/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

/* @(#)snode.h	1.2 86/12/23 NFSSRC */
/*      @(#)snode.h 1.1 86/09/25 SMI      */
 
/*
 * Copyright (c) 1986 by Sun Microsystems, Inc.
 */

#ident "$Header: snode.h 1.16 90/01/06 $"

#ifndef	_SYS_SNODE_H_

/* $Log:	snode.h,v $
 */

/*
 * The SNODE represents a special file in any filesystem. There is
 * one snode for each active special file.  Filesystems which support
 * special files use specvp() to convert a normal vnode to a special 
 * vnode in the ops create, mkdir, and lookup.
 *
 * The basic idea is that an snode is allocated for each active inode which
 * represents a VBLK or VCHR device (a fifonode, which is an snode
 * variant, is allocated for each VFIFO).  This snode points to the
 * inode (via s_realvp).  Note that, because there can exist multiple
 * inodes for a given dev_t, there can correspondingly be multiple
 * snodes.  The allocation of this snode is done when VOP_LOOKUP()
 * calls specvp().
 * 
 * For the block buffer cache, a single unique vnode is needed for
 * a given dev_t VBLK device.  To support this, whenever a VBLK snode
 * is allocated for an inode, specvp() calls bdevvp()
 * to allocate a another snode for the VBLK device.
 * The first snode points to the second via s_bdevvp.  A subsequent
 * specvp() or bdevvp() for a VBLK inode with the same dev_t will
 * use the already allocated second snode.  This second snode is
 * identified as type VBLK but with a null s_realvp.  It is this
 * snode (vnode) which is used by the buffer cache to hash to identify
 * blocks coming from the same device.
 * 
 * For STREAMS clone opens, since there is no inode for the VCHR dev_t
 * being opened (at least you don't know what it is), a special VCHR
 * snode is allocated with a null s_realvp.  However, unlike the
 * special VBLK snodes, the same VCHR snodes are not returned for
 * subsequent specvp()'s with the same dev_t; different snodes are
 * always returned.  It is the job of the I/O system (devops or STREAMS)
 * to associate together different VCHR vnodes for the same dev_t.
 * This type of snode is allocated when cloneopen() calls cdevvp().
 * 
 * Thus the "typical" snode which is allocated by specvp() for ufs_lookup()
 * points to the inode (via s_realvp) and, if VBLK, also points to the
 * common secondary VBLK snode for that dev_t (via s_bdevvp).
 * 
 * Code which needs to refer to the common secondary VBLK snode directly
 * (e.g., ufs_mount()) calls bdevvp() supplying the dev_t.
 *
 * Once created, the snode is used instead of the normal vnode.  The 
 * snode's VOPs perform I/O by calling the underlying character/block 
 * driver subsystem.  In some cases, these VOPs invoke the VOPs of the 
 * normal vnode associated with the snode.  The snode's normal vnode,
 * if any, is pointed at by s_realvp in the snode.  The snode holds
 * a reference to the normal vnode (bumps it's v_count).  The normal
 * vnode's VOP_INACTIVE() is typically called only as a result of calling 
 * the snode's VOP_INACTIVE().
 *
 * Thus, there are four types of regular snodes:
 *
 *	v_type	s_realvp	      b_devvp	   made by	called by, e.g.
 *	------	--------	      -------	   -------	---------------
 *	VCHR	NULL		      NULL	   cdevvp()	cloneopen()
 *	VCHR	ptr to normal vnode   NULL	   specvp()	VOP_LOOKUP()
 *
 *	VBLK	NULL		      ptr to self  bdevvp()	specvp()
 *	VBLK	ptr to normal vnode   ptr to ^	   specvp()	VOP_LOOKUP()
 *
 * In addition to these types, there are fifonodes which are variants of snodes.
 * A fifonode contains an snode.  It is created for VFIFO files.  See fifonode.h
 * for more information.
 */


struct snode {
	struct	snode	*s_next;	/* must be first */
	struct	vnode	 s_vnode;	/* vnode associated with this snode */
	struct	vnode	*s_realvp;	/* the vnode for the filesystem entry */
	struct	vnode	*s_bdevvp;	/* blk device vnode (shared with fs) */
	ushort		 s_flag;	/* flags, see below */
	dev_t		 s_dev;		/* device the snode represents */
	struct	dev	*s_devp;	/* device object for special files */
	struct stdata	*s_sptr;	/* stream assoc with this snode */
	daddr_t		 s_lastr;	/* last read (read-ahead) */

	timeval_t	 s_atime;	/* time of last access */
	timeval_t	 s_mtime;	/* time of last modification */
	timeval_t	 s_ctime;	/* time of last attributes change */

	/* The following are to satisfy spec_getattr() when no s_realvp: */
	uid_t		 s_uid;
	gid_t		 s_gid;
	mode_t		 s_mode;	/* ufs-style mode */
	long		 s_nodeid;	/* "inode" number */
};

/* flags */
#define SUPD		0x02		/* update device modification time*/
#define SACC		0x04		/* update device access time */
#define SCHG		0x40		/* update device change time */

/*
 * Convert between vnode and snode
 */
#define	VTOS(vp)	((struct snode *)((vp)->v_data))
#define	STOV(sp)	(&(sp)->s_vnode)

/*
 *	SN_INIT does basic snode initialization.
 *
 *	Assumes the following vnode fields are already initialized:
 *		v_op, v_nodemutex, v_mutex.
 *	(Note: v_nodemutex's count is set to 1 here.)
 *
 *	See also SN_VATTR_INIT().
 */
#define SN_INIT(sp, vfsp, type, dev)	{ \
	VN_INIT(STOV(sp), vfsp, type, dev); \
	STOV(sp)->v_data = (caddr_t)sp; \
	RWSEMA_SETWRBUSY(&STOV(sp)->v_nodemutex);	/* lock the snode */ \
	sp->s_flag = 0; \
	sp->s_dev = dev; \
	sp->s_realvp = NULL;	/* default value */ \
	sp->s_bdevvp = NULL;	/* default value */ \
}

/*
 *	SN_VATTR_INIT initializes optional snode attributes useful if the
 *		snode has no s_realvp for spec_getattr() to retrieve from.
 *		This includes s_nodeid which must be a unique number on
 *		the specfs filesystem device.
 *
 *      Assumes SN_INIT() already called.
 */

extern lock_t spec_nodeid_mutex;
extern spl_t  spec_nodeid_ipl;
extern long   spec_lastnodeid;		/* last s_nodeid number allocated */

#define SN_VATTR_INIT(sp, mode, uid, gid)	{ \
	sp->s_mode = mode; \
	sp->s_uid = uid; \
	sp->s_gid = gid; \
	sp->s_ctime = time; \
	sp->s_mtime = sp->s_ctime; \
	sp->s_atime = sp->s_ctime; \
	spec_nodeid_ipl = p_lock(&spec_nodeid_mutex, SPLFS); \
	sp->s_nodeid = ++spec_lastnodeid; \
	v_lock(&spec_nodeid_mutex, spec_nodeid_ipl); \
}


/*
 * construct a spec vnode for a given device
 */
int specvp();			/* make snode for supplied device vnode */
struct vnode *bdevvp();		/* make "common" VBLK snode for given dev_t */
struct vnode *cdevvp();		/* make anonymous VCHR snode for given dev_t */


/*
 * macro form of smar().   Also, improved to not
 * call uniqtime().
 */
#ifdef	lint
#define	SMARK(sp, flag)
#else
#define SMARK(sp, flag) { \
	timeval_t ltime; \
\
	ltime = time; \
	(sp)->s_flag |= (flag); \
	if ((flag) & SUPD) { \
		if ((sp)->s_mtime.tv_sec == ltime.tv_sec && \
 		    (sp)->s_mtime.tv_usec >= ltime.tv_usec) { \
			ltime.tv_usec = (sp)->s_mtime.tv_usec + 1; \
			if (ltime.tv_usec >= 1000000) { \
				ltime.tv_usec = 0; \
				ltime.tv_sec++; \
			} \
 		} \
		(sp)->s_mtime = ltime; \
	} \
	if ((flag) & SACC) \
		(sp)->s_atime = ltime; \
	if ((flag) & SCHG) { \
		(sp)->s_ctime = ltime; \
	} \
}
#endif	/* lint */

/*
 * Debug printf for specfs.
 *
 * Level 0 - No messages.
 * Level 1 - Add: one line summaries of major operations
 * Level 3 - Add: one line summaries of minor operations
 * Level 5 - Add: detailed info on internals of operations
 * Level 9 - Add: VERY detailed info on internals of operations
 */

#ifdef DEBUG

extern spec_debug_level;

#define SPECPRINTF1 if (spec_debug_level > 0) printf
#define SPECPRINTF3 if (spec_debug_level > 2) printf
#define SPECPRINTF5 if (spec_debug_level > 4) printf
#define SPECPRINTF9 if (spec_debug_level > 8) printf

#else	/* !DEBUG */

/*
 * Even though the optimizer tosses null-effect expressions,
 * lint doesn't like them.  So to shut up lint...
 */
#ifdef lint
#define SPECPRINTF1 printf
#define SPECPRINTF3 printf
#define SPECPRINTF5 printf
#define SPECPRINTF9 printf
#else
#define SPECPRINTF1 (void)
#define SPECPRINTF3 (void)
#define SPECPRINTF5 (void)
#define SPECPRINTF9 (void)
#endif

#endif	/* !DEBUG */

#define	_SYS_SNODE_H_
#endif	/* _SYS_SNODE_H_ */
