Coherent4.2.10/include/kernel/strmlib.h

/* (-lgl
 *	Coherent 386 release 4.2
 *	Copyright (c) 1982, 1993 by Mark Williams Company.
 *	All rights reserved. May not be copied without permission.
 *	For copying permission and licensing info, write licensing@mwc.com
 -lgl) */

#ifndef __KERNEL_STRMLIB_H__
#define __KERNEL_STRMLIB_H__

/*
 * This ^^^^^^^^^^^^^^^^^ symbol is used in the DDI/DKI header <sys/ddi.h> to
 * determine which #undef directives it is required to perform, on the basis
 * that it should avoid touching namespaces unless they have been reserved
 * by the inclusion of a header that reserves such classes of names.
 */

/*
 * This file contains definitions that are be used for the implementation of
 * the STREAMS standard library routines and scheduling code. Some details
 * of the interfaces between STREAMS and the rest of the system will also
 * be dealt with here so as to insulate the STREAMS implementation from the
 * details of that interface.
 *
 * Note that as the contents of this header are for private, system internal
 * use only, names do not begin with underscores.
 */

#include <common/ccompat.h>
#include <common/xdebug.h>
#include <common/__size.h>
#include <common/__clock.h>
#include <common/__pid.h>
#include <common/__cred.h>
#include <common/__types.h>
#include <kernel/__pl.h>
#include <kernel/st_alloc.h>
#include <kernel/defer.h>
#include <sys/inline.h>
#include <sys/ksynch.h>
#include <sys/uio.h>

#include <common/_stream.h>
#include <kernel/ddi_lock.h>
#include <kernel/ddi_glob.h>

/*
 * To be able to declare prototypes that refer to structures that are
 * declared in other headers, we supply incomplete declarations at top-level
 * to avoid some scoping problems.
 */

struct stroptions;
struct strbuf;

/*
 * For freezestr () to work as defined in the Multiprocessor DDI/DKI,
 * it cannot simply be implemented in terms of the high-level locking
 * operations defined in <sys/ksynch.h>.  This is because it is specified
 * as raising the processor priority level, whereas the "pl" parameter to the
 * high-level locking functions is specified as setting the level, with a
 * caution that it not cause the level to be lowered.
 *
 * Stream-queue freezing cannot really be implemented in terms of high-level
 * basic locks because of the hierarchy mechanism; the relative priority of
 * stream head locks is determined by relative position in a stream so that
 * following the "q_next" member yields increasing (virtual) hierarchy values,
 * with the additional constraint that a given context may only hold a lock on
 * one side of a stream at a time.
 */

#define	SFREEZE_INIT(q)		((void) ATOMIC_CLEAR_UCHAR ((q)->q_locked))
#define	SFREEZE_DESTROY(q)	((void) 0)
#define	SFREEZE_LOCK(q,name)	TEST_AND_SET_LOCK ((q)->q_locked, plstr, \
						   (name))
#define	SFREEZE_UNLOCK(q,pl)	(ATOMIC_CLEAR_UCHAR ((q)->q_locked), \
				 (void) splx (pl))
#define	SFREEZE_ASSERT_FROZEN(q) ASSERT (ATOMIC_FETCH_UCHAR ((q)->q_locked))


/*
 * The implementation of allocb () and freeb () deals in terms of "triples"
 * consisting of an mblk_t, a dblk_t, and the actual data being managed.
 * (Of course, under esballoc () the data belongs to another subsystem, but
 * such a double should also be managed by the same system).
 *
 * Be aware that changing the definition of any of these things may
 * invalidate assumptions made by the code in allocb (), freeb (), dupb ()
 * and esballoc ().
 */

/*
 * Various ways to map between the components of a triple.
 */

#define	MB_TO_DB(mp)		((dblk_t *) (mp + 1))
#define	DB_TO_MB(db)		(((mblk_t *) db) - 1)
#define	DB_TO_DATA(db)		((unsigned char *) (db + 1))
#define	DATA_TO_DB(d)		(((dblk_t *) d)) - 1)


/*
 * Some useful predicates for determining whether an item is
 * part of a triples, as there are exceptional cases in which the
 * elements of a triple have been scattered to the four winds.
 *
 * Note that the SET_MB_TRIPLE () and SET_MB_FREE () exist as a pair, because
 * they relate only to message blocks that are part of a triple.
 * SET_MB_TRIPLE () is used when a message block is instantiated, and
 * SET_MB_FREE () is used when a message block is deallocated but the rest
 * of the triple is still in use.
 */

#define	IS_DB_USER_DATA(db)	(db->db_base != DB_TO_DATA (db))
						/*
						 * Test to see whether the
						 * data referenced by this
						 * block is user-controlled.
						 */

#define	IS_TRIPLE_TOGETHER(mp)	(mp->b_datap == MB_TO_DB (mp))
						/*
						 * Test to see whether the
						 * data block referenced by
						 * this message block forms
						 * part of the same triple.
						 */

#define	SET_MB_TRIPLE(mp)	(void) ((mp)->b_flags =\
				 ((mp)->b_flags | MSGTRIPLE) & ~ MSGFREE)
						/*
						 * Mark the message block as
						 * part of a triple.
						 */

#define	IS_MB_TRIPLE(mp)	(((mp)->b_flags & MSGTRIPLE) != 0)
						/*
						 * Test whether the message
						 * block is part of a triple
						 * or is a floating block.
						 */

#define	SET_MB_FREE(mp)		(void) ((mp)->b_flags |= MSGFREE)
						/*
						 * Indicate that the message
						 * block is no longer in
						 * use. Only for message
						 * blocks in triples.
						 */

#define	IS_MB_FREE(mp)		((mp->b_flags & MSGFREE) != 0)
						/*
						 * Test whether the message
						 * block is free or not.
						 */

/*
 * STREAMS Local extension: scheduling
 */

/*
 * The methods used in the multiplexing examples in the STREAMS Programmer's
 * Guide for System V Release 2 are quite inefficient. The following general
 * STREAMS queue scheduling structures and routines were defined in a visible
 * manner because:
 *
 *   (i) It better documents the way in which queues are serviced
 *	 within this STREAMS implementation,
 *
 *  (ii) Multiplexing drivers (and device drivers which multiplex several
 *	 minor numbers on a single I/O bus, such as an Ethernet or SCSI
 *	 driver) need effcient mechanisms for managing multiple request
 *	 channels,
 *
 * (iii) It allows drivers to leverage the multiprocessor locking that is
 *	 built into these primitives.
 *
 * Note that allowing drivers access to this facility causes a significant
 * departure from the standard version of STREAMS, but that driver use of the
 * macros and data structures below permits other implementations.
 *
 * As this system is used internally by this STREAMS implementation, it can
 * use the "q_link" member of a queue to hold a link to the next STREAM on
 * a schedule.  In addition, some "q_flag" bits can be used to control
 * whether or not a queue is currently threaded on any schedule.  A portable
 * implementation that does not depend on any reserved parts of STREAMS
 * data structures may not use these fields, and so may operate under a
 * different set of constraints. However, since in this case the drivers and
 * STREAMS itself share common scheduling code, the following restrictions
 * must be observed:
 *
 *   (i) A queue may be requested to be scheduled more than once with no
 *	 effect. If a queue is requested to be scheduled on different
 *	 schedules, then this is probably an error, most likely to occur
 *	 if you define both a STREAMS service routine and a schedule for
 *	 a driver queue; this should not be done under the common scheme.
 *
 *  (ii) A queue which has noenable () set on it will still be scheduled.
 *	 The manner in which qenable ()/noenable () work has no bearing on
 *	 this mechanism.
 *
 * (iii) Schedules should only be created or destroyed with the schedule
 *	 allocation/deallocation routines in order to guarantee that the
 *	 schedule is multi-processor addressable and is the correct size for
 *	 the selected run-time environment.
 *
 *  (iv) When a driver is closed, the queue must not be on any schedule in
 *	 order to prevent list corruption and the dire effects that can
 *	 result from such corruption. A portable implementation of scheduling
 *	 has no way to do this automatically for the driver since the
 *	 close () call is managed by STREAMS.
 *
 * Note that in this common implementation, conditions (i), (iii), and (iv)
 * can be checked by the scheduling system if compiled such that debugging
 * code is inserted.
 */

/*
 * STREAMS scheduler private per-schedule global data structure.
 */

struct __streams_schedule {
	queue_t	      *	ss_head;	/* head of list of scheduled queues */
	queue_t	      *	ss_tail;	/* tail of list of scheduled queues */

	lock_t	      *	ss_locked;	/* lock for schedule */
};


extern lkinfo_t	__stream_schedule_lkinfo;

#define	SCHLOCK_INIT(s,flag) \
	((s)->ss_locked = LOCK_ALLOC (stream_schedule_hierarchy, plstr, \
				      & __stream_schedule_lkinfo, (flag)))

#define	SCHLOCK_DESTROY(s)	LOCK_DEALLOC ((s)->ss_locked)

#define	SCHLOCK_LOCK(s,n)	LOCK ((s)->ss_locked, plstr)

#define	SCHLOCK_UNLOCK(s,p)	UNLOCK ((s)->ss_locked, p)

#define	SCHLOCK_ASSERT_LOCKED(s) \
		ASSERT (TRYLOCK ((s)->ss_locked, plstr) == invpl)

#define SCHED_INIT(s,flag)	((s)->ss_head = (s)->ss_tail = NULL, \
				 SCHLOCK_INIT (s, flag))


/*
 * I use these everywhere that I know which queue side I've got. These
 * macros duplicate the old-style RD () and WR () functionality, which has
 * been changed along the path from System V Release 3.2 to System V Release
 * 4 Multi-Processor. When compiling under the DDI/DKI, these macros are also
 * converted to functions.
 */

#define W(q)    	((q) + 1)
#define R(q)    	((q) - 1)


/*
 * A utility function, for use with STRMEM_ALLOC (), to calculate the size
 * required for a message triple with "n" bytes of associated data buffer.
 */

#define	MSGB_SIZE(n)	((n) + sizeof (mblk_t) + sizeof (dblk_t))


/*
 * The following data type is used to hold registration information for the
 * SIGPOLL signal. Every process that registers for SIGPOLL signals allocates
 * one of these structures and threads it on the stream head.
 */

typedef struct sigpoll	sigpoll_t;


/*
 * The following is the internal structure (an "event cell" in STREAMS)
 * used by the bufcall ()/esbbcall () mechanism to record the necessary
 * information for calling the user back.
 *
 * Note that the real System V bufcall ()/esbbcall () takes a a pointer to
 * a function with undefined parameters, yet also accepts a parameter which
 * it will then pass on to the callback. The only way to implement this at
 * all portably is to use some version of the ISO <stdarg.h> mechanism to
 * allow bufcall () to take any argument in its "natural" form and then
 * pass a maximum-sized chunk of stack into the callback in order to capture
 * all of the information that the client wanted to pass.
 *
 * Of course, doing this is opening a door to all kinds of problems:
 * although there are some sleazy ways to restrict the size of the extra
 * argument to something reasonable, there is no way that we can make sure
 * that the shapes of the argument and the callback parameter really do
 * match (eg, the callback takes a long but bufcall () is given an int).
 *
 * Of course, this is all no worse than the situation AT&T have under the
 * DDI/DKI, where bufcall () is required to take the callback parameter as
 * a "long" and clients assume that pointers have the same shape as longs.
 * (If you look at the example in bufcall (D3DK) that is exactly what A&T
 * seem to be suggesting.)
 *
 * There is no entirely satisfactory way to get around this in plain C,
 * although we can get at least a partial solution through supplying a range
 * of supplementary definitions that (at least in an ISO environment) ensure
 * that the function argument types and argument types will be coerced to
 * something reasonable.
 *
 * Event management is made a little more complex by the availability of the
 * unbufcall () procedure, which requires us to assign a numeric ID code to
 * each event cell. It would be nice if this ID code also included some kind
 * of generation field, and mapped instantly to the desired value.
 *
 * Note that this is exactly the same problem faced by the timeout () routine
 * in implementing untimeout (), and there are numerous possible solutions,
 * each of which maximizes some desirable property at the cost of losing some
 * other property.
 */

#define	_TOID_MEMBER	1

typedef void (* se_funcptr_t)	__PROTO ((long _arg));

typedef struct __stream_event sevent_t;

struct __stream_event {
	sevent_t      *	se_next;     	/* next event in chain */
	sevent_t      *	se_prev;	/* previous event in chain */
	long    	se_arg;		/* argument for function */
	se_funcptr_t	se_func;	/* function to call */
	unsigned int	se_size;	/* optional size information */
#if _TOID_MEMBER
	toid_t		se_id;		/* timeout id for item */
#endif
};


/*
 * Of course, we have to thread the above events on a list. We'll create a
 * structure for this to abstract the details of the locking scheme we'll use
 * for maintaining the consistency of the event lists.
 *
 * We store a generator for the timeout ID codes in the list header so that
 * the ID generation step can be protected by the same lock as the list
 * manipulation code. This means that at initialisation time each list header
 * should have the initial ID code set to its number and that the increment
 * value used to step between codes is one greater than the total number of
 * list headers (so that code 0 never appears).
 *
 * We have a wide range of possible policies for managing event cells. We need
 * to get some performance data on each to make a final decision.
 */

#define	_FIFO_BUFCALL	1

typedef struct __stream_event_list selist_t;

struct __stream_event_list {
	lock_t	      *	sl_locked;
	sevent_t      *	sl_head;
#if _FIFO_BUFCALL
	sevent_t      *	sl_tail;
#endif
#if _TOID_MEMBER
	toid_t		sl_id;
#endif
};


extern lkinfo_t	__stream_event_lkinfo;

#define	SELIST_INIT(s,flag) \
	((s)->sl_head = NULL, \
	 (s)->sl_locked = LOCK_ALLOC (stream_event_hierarchy, plstr, \
				      & __stream_event_lkinfo, (flag)))

#define SELIST_DESTROY(s)	LOCK_DEALLOC ((s)->sl_locked)

#define	SELIST_LOCK(s)		LOCK ((s)->sl_locked, plstr)

#define	SELIST_UNLOCK(s,pl)	UNLOCK ((s)->sl_locked, (pl))

#define	SELIST_ASSERT_LOCKED(s)	\
		ASSERT (TRYLOCK ((s)->sl_locked, plstr) == invpl)


/*
 * Some handy requirements for timeout ID generation. We have to define these
 * numbers such that each list above generates a non-overlapping sequence that
 * overflows into the same sequence, eg.
 *
 *	1, k + 1, 2k + 1, ... ik + 1, 1, ...
 *	2, k + 2, 2k + 2, ... ik + 2, 2, ...
 *
 * Since each list generates a distinct and identifiable sequence we can map
 * from the ID to the list in one step, which somewhat ameliorates the cost
 * of having to search the list to find the given event.
 */

#define	TOID_INCREMENT		(N_PRI_LEVELS + 1)
#define	TOID_MODULUS		(TOID_MAX - TOID_MAX % TOID_INCREMENT)
#define	TOID_TO_PRI(id)		((id) % TOID_INCREMENT - 1)


/*
 * Watermark control structures:
 *
 * We record here the amount of memory held by each allocation band and the
 * maximum amount we will permit it to hold. In addition, we record a thread
 * of structures used by bufcall () and esbbcall () to record information
 * for callbacks to drivers when memory becomes available.
 */
/*
 * A note on bufcall ()/esbbcall () structures;
 *
 * I cannot conceive a comprehensive strategy for dealing with
 * these things that works well under all circumstances. For now, it seems
 * that keeping a few around in preallocated event cells is a good idea for
 * when we run out of memory. However, as long as possible the system will
 * always attempt to satisfy a request for an event cell with a newly
 * allocated cell, since we expect the usual reason for being unable to
 * satisfy a request is that memory is merely fragmented rather than fully
 * occupied.
 *
 * Of course, allocating bufcall cells within the heap may increase the
 * level of fragmentation - which is why we prefer to allocate them from
 * the "long-term" heap (and indeed, why we have two heaps at all).
 */

#define	N_PRI_LEVELS		3	/*
					 * Number of buffer priority levels
					 */
#define MAP_PRI_LEVEL(p)	p	/*
					 * Map from BPRI_xxx to index.
					 */

/*
 * We keep several lists of stream heads according to the category of the
 * stream; device, pipe, and possibly FIFO. Various general operations require
 * iterating over several of the lists, and so we define an array of list
 * head pointers to simplify things since we don't have the C++ "pointer to
 * member" types and operations available.
 */

typedef	struct __stream_head shead_t;

typedef enum slist_id {
	DEV_SLIST,
	PIPE_SLIST,
	SLIST_MAX
} slist_id_t;

typedef	int		muxid_t;


/*
 * This structure grew to its present form before the <kernel/ddi_glob.h>
 * header took shape. Most of it should eventually be moved there, with
 * appropriate thought given to what should be fixed and what should be
 * variable allocations.
 */


struct streams_mem {
	atomic_uchar_t	sm_init;	/* primitive lock for startup */

	__size_t	sm_used;	/* memory used in the message heap */
	__size_t	sm_max [N_PRI_LEVELS];
					/*
					 * Maximum memory we let a level use
					 */

	toid_t		sm_bcid;	/* id generator for bufcall events */
	selist_t	sm_bcevents [N_PRI_LEVELS];
					/*
					 * Bufcall event linked-list head.
					 */
	ssched_t	sm_sched [1];	/* List of enabled queues. */

	lock_t	      *	sm_seq_lock;	/* Lock sequence number registers. */
	unsigned long	sm_err_seq;	/* error log sequence number */
	unsigned long	sm_trc_seq;	/* trace log sequence number */
	unsigned long	sm_con_seq;	/* console log sequence number */

	queue_t	      *	sm_log_rq;	/* STREAMS logger device */

	lock_t	      *	sm_proc_lock;	/* Basic lock for qprocsoff (). */
	sv_t	      *	sm_proc_sv;	/*
					 * Synchronization variable for
					 * qprocsoff ().
					 */
	queue_t	      *	sm_log_drvr;	/* Log-driver read side. */

	rwlock_t      *	sm_head_lock;	/* Lock for stream head list */
	shead_t	      *	sm_streams [SLIST_MAX];
					/* Chains of stream heads */

	long		sm_maxctlsize;	/* max. size of control message */
	long		sm_maxdatasize;	/* max. size of data message part */


	/*---------- Locked by sm_msg_lock ------------*/
	lock_t	      *	sm_msg_lock;	/* basic lock for message heap */
	_ST_HEAP_CONTROL_P sm_msg_heap;	/* Memory heap for STREAMS messages */
	sv_t	      *	sm_msg_sv;	/*
					 * synchronization variable for
					 * waiting for free memory.
					 */
	__size_t	sm_msg_needed;	/* Level of free memory required */
};


/*
 * These should be the standard priorities to be used with the locks for
 * controlling access to the various memory pools.
 */

#define	str_msg_pl	plstr


/*
 * When someone thinks it might be a good idea to see about running some of
 * the bufcall routines, we defer a routine to deal with the checking. A
 * global flag bit protects
 */

#define SCHEDULE_BUFCALLS() \
   (ATOMIC_FETCH_AND_STORE_UCHAR (ddi_global_data ()->dg_run_bufcalls, 1) \
			== 1 ? (void) 0 : (void) defer_int_any (RUN_BUFCALLS))

/*
 * We need access to a global instance of the above for managing STREAMS
 * memory in a consistent manner.
 */

extern struct streams_mem str_mem [];


/*
 * Category flags for the sh_lock_mask member. This is a collection of flag
 * bits which correspond to operations on the "sh_wait_sv" synchronization
 * variable.
 */

typedef enum category {
	SH_NONE		= 0,		/* use for paranoid checking */
	SH_OPENCLOSE	= 1,
	SH_IOCTL_LOCK	= 2,
	SH_READ_LOCK	= 4,		/* NOT NORMALLY USED */
	SH_WRITE_LOCK	= 8,		/* NOT NORMALLY USED */
	SH_PEEK_LOCK	= 16,

	SH_LOCK_MASK	= 31,		/* Mask for lock bits */


	SH_READ_WAIT	= 32,
	SH_WRITE_WAIT	= 64,
	SH_IOCTL_WAIT	= 128,
	SH_DRAIN_WAIT	= 256,
	SH_PEEK_WAIT	= 512,

	SH_WAIT_MASK	= 992,		/* Mask for wait bits */


	/*
	 * Not a lock mask; just use the extra flag space.
	 */

	SH_TIMEFLAG	= 8192
} cat_t;


/*
 * Stream head control structure.
 *
 * Be careful with locking the stream head; for many operations, it may be
 * sufficient to freeze the stream head.
 */

struct __stream_head {
	n_dev_t		sh_dev;		/* key for lookup */
	shead_t	      *	sh_next;	/* next stream head on chain */

	queue_t       *	sh_head;	/* read queue of stream head */
	struct streamtab
		      *	sh_tab;		/* initialisation data for driver */

	__clock_t	sh_cltime;	/* time to wait for close to drain */

	struct pollhead
		      *	sh_pollhead;	/* for polling support */

	__pid_t		sh_pgrp;	/* foreground TTY process group */
	__VOID__      *	sh_controller;	/* reference to controlling process */

	int		sh_rerrcode;	/* error code from M_ERROR */
	int		sh_werrcode;	/* error code from M_ERROR */

	unsigned short	sh_flags;	/* miscellaneous flags */

	short		sh_readopt;	/* read options */
	short		sh_wropt;	/* write options */

	unsigned short	sh_wroff;	/* write offset */

	sigpoll_t     *	sh_sigs;	/* processes registered for SIGPOLL */

	toid_t		sh_read_bufcall;/* ID for read bufcall () */

	shead_t	      *	sh_linked;	/* head of stream we are linked to */
	muxid_t		sh_muxid;	/* our ID below that stream */


	lock_t	      *	sh_basic_lockp;	/* */

	/*---- For process-level locking and timeout operations ----*/

	sv_t	      *	sh_wait_sv;	/* for process lock operations */

	int		sh_lock_mask;	/* which categories are locked */

	short		sh_ref_count;	/* references to stream memory */
	short		sh_open_count;	/* successful open count */
	short		sh_attach_count;/* attachments to stream */
	short		sh_lock_count;	/* processes queued or locked */
	short		sh_time_count;	/* count of timeout checks */

	__clock_t	sh_timeout_tick;/* current soonest timeout */
	toid_t		sh_timeout_id;	/* ID of current active timeout */

	int		sh_ioc_seq;	/* ioctl () sequence number */
	mblk_t	      *	sh_ioc_msg;	/* ioctl () messages */
};


/*
 * A collection of functions for dealing with the stream head basic lock.
 */

#define	SHEAD_LOCK(sheadp)	LOCK ((sheadp)->sh_basic_lockp, plstr)
#define	SHEAD_UNLOCK(sheadp,pl)	UNLOCK ((sheadp)->sh_basic_lockp, pl)
#define	SHEAD_ASSERT_LOCKED(sheadp) \
		ASSERT ((sheadp) != NULL && \
			TRYLOCK ((sheadp)->sh_basic_lockp, plstr) == invpl)


/*
 * Masks for use with the "sh_flags" member.
 */

enum {
	SH_READMSG	= 0x0001,	/* generate M_READ messages */
	SH_NDELAY	= 0x0002,	/* non-STREAMS O_NDELAY semantics */
	SH_TTY		= 0x0004,	/* stream is acting as a tty */
	SH_TOSTOP	= 0x0008,	/* job control on background writes */


	SH_HANGUP	= 0x0010,	/* stream has been hung up */

	SH_PLINK	= 0x0020,	/* stream is permanently linked */

	/*
	 * Permanent flags set when the stream is created. We do not need a
	 * flag to indicate a pipe, because pipes have a NULL "sh_tab" entry
	 * to mark them.
	 */

	SH_MASTER	= 0x0100,	/* master end of stream pipe */


	/*
	 * This flag enables serialization of reads and writes. This is a
	 * local, experimental extension to STREAMS.
	 */

	SH_RWLOCKING	= 0x0200
};


/*
 * For stream pipes there is a master/slave relationship between the stream
 * heads at each end. The following macros define some handy relationships
 * between them to allow quick mapping from one to another.
 */

#define	SHEAD_M2SLAVE(m)	((m) + 1)
#define	SHEAD_SLAVE2M(s)	((s) - 1)
#define	SHEAD_MASTER(sheadp)	(((sheadp)->sh_flags & SH_MASTER) == 0 ? \
				 SHEAD_SLAVE2M (sheadp) : (sheadp))
#define	SHEAD_OTHER(sheadp)	(((sheadp)->sh_flags & SH_MASTER) == 0 ? \
				 SHEAD_SLAVE2M (sheadp) : \
				 SHEAD_M2SLAVE (sheadp))

#define	SHEAD_IS_PIPE(sheadp)	((sheadp)->sh_tab == NULL)

#define	SHEAD_HANGUP(sheadp)	(((sheadp)->sh_flags & SH_HANGUP) != 0)

#define	SHEAD_READMSG(sheadp)	(((sheadp)->sh_flags & SH_READMSG) != 0)


/*
 * For working in with the abstract filesystem layer, we define a format for
 * our opaque information.
 */

typedef	struct {
	shead_t	      *	sheadp;
} scookie_t;



__EXTERN_C_BEGIN__

ssched_t      *	QSCHED_ALLOC	__PROTO ((void));
void		SSCHED_FREE	__PROTO ((ssched_t * _sched));
int		QSCHED_SCHEDULE	__PROTO ((queue_t * _q, ssched_t * _sched));
void		QSCHED_UNSCHEDULE
				__PROTO ((queue_t * _q, ssched_t * _sched));
queue_t	      *	QSCHED_GETFIRST	__PROTO ((ssched_t * _sched));

__pl_t		QFREEZE_TRACE	__PROTO ((queue_t * _q,
					  __CONST__ char * _name));
void		QFROZEN_TRACE	__PROTO ((queue_t * _q,
					  __CONST__ char * _name));
void		QUEUE_TRACE	__PROTO ((queue_t * _q,
					  __CONST__ char * _name));
void		QUNFREEZE_TRACE	__PROTO ((queue_t * _q, __pl_t _pl));

void		QUEUE_BACKENAB	__PROTO ((queue_t * _q));
queue_t	      *	QUEUE_NEXT	__PROTO ((queue_t * _q));

mblk_t	      *	STRMEM_ALLOC	__PROTO ((__size_t _size, int _pri,
					  int _flag));
void		STRMEM_FREE	__PROTO ((mblk_t * _bp, __size_t _size));

mblk_t	      *	MSGB_ALLOC	__PROTO ((__size_t _size, int _pri,
					  int _flag));

void		SHEAD_WAKE	__PROTO ((shead_t * _sheadp, cat_t _flag));
void		SHEAD_SIGNAL	__PROTO ((shead_t * _sheadp, __uchar_t _sig));
int		SHEAD_SRDOPT	__PROTO ((shead_t * _sheadp, int _flag));
void		QBAND_SETOPT	__PROTO ((queue_t * _q,
					  struct stroptions * _so));

qband_t	      *	QUEUE_BAND	__PROTO ((queue_t * _q, __uchar_t _pri));
qband_t	      *	QBAND_PREV	__PROTO ((queue_t * _q, qband_t * _qbandp));

void		RUN_BUFCALLS	__PROTO ((void));
void		RUN_STREAMS	__PROTO ((void));

shead_t	      *	SHEAD_FIND	__PROTO ((n_dev_t _dev, slist_id_t _id));

int		STREAMS_OPEN	__PROTO ((n_dev_t * _devp,
					  struct streamtab * _stabp,
					  int _mode, __cred_t * _credp,
					  int _cloneflag));
int		STREAMS_CLOSE	__PROTO ((shead_t * _sheadp, int _mode,
					  __cred_t * _credp));
int		STREAMS_READ	__PROTO ((shead_t * _sheadp, uio_t * _uiop,
					  __cred_t * credp));
int		STREAMS_WRITE	__PROTO ((shead_t * _sheadp, uio_t * _uiop,
					  __cred_t * credp));
int		STREAMS_IOCTL	__PROTO ((shead_t * _sheadp, int _cmd,
					  __VOID__ * _arg, int _mode,
					  __cred_t * _credp, int * _rvalp));

int		STREAMS_CHPOLL	__PROTO ((shead_t * _sheadp, short _events,
					  int _anyyet, short * _reventsp,
					  struct pollhead ** _phpp));

int		STREAMS_GETPMSG	__PROTO ((shead_t * _sheadp,
					  struct strbuf * _ctlbuf,
					  struct strbuf * _databuf,
					  int * _bandp, int * _flagsp,
					  int _mode, __cred_t * credp,
					  int * _rvalp));
int		STREAMS_PUTPMSG	__PROTO ((shead_t * _sheadp,
					  __CONST__ struct strbuf * _ctlbuf,
					  __CONST__ struct strbuf * _databuf,
					  int _band, int _flags, int _mode,
					  __cred_t * credp, int * _rvalp));

__EXTERN_C_END__


/*
 * This internal definition is the dual to QFREEZE_TRACE (), being the
 * internal implementation of unfreezestr ().
 */

#define	QUNFREEZE_TRACE(q,pl)	SFREEZE_UNLOCK (q, pl)


/*
 * Dealing with queue priority bands can be a little tricky; in particular,
 * the definition of the linked-list structure of priority bands has some
 * characteristics that may be unsuitable for some implementations.
 * Specifically, the "qband" structure apparently is designed to function as
 * a member of a singly-linked list rooted in the "queue" structure. This is
 * flexible, but if there are several priority bands the time spent in
 * dereferencing the link pointers might outweigh any extra cost involved in
 * keeping the "band" structures in a vector [While the allocation cost is
 * neglible in terms of time, there is a potentially high cost in terms of
 * increasing the likelihood of a failure to allocate a band structure].
 *
 * The following internal functions have been defined to abstract away the
 * details of how band structures are accessed from the queue, allowing a
 * more time-efficient vector scheme to be used, or even a hybrid scheme
 * where vectors are preferred but a linked-list fallback is available.
 *
 * The SVR4 DDI/DKI used to document the band structure, but that structure is
 * no longer documented as of the SVR4 MP DDI/DKI.
 */

#define	_VECTOR_BANDS		0
#define	_VECTOR_BANDS_TEST	0	/* test mode for VECTOR_BANDS */

#if	_VECTOR_BANDS

#define	QUEUE_BAND(q,pri)	((pri) > (q)->q_nband ? NULL : \
					& (q)->q_bandp [pri - 1])

#define	QBAND_PREV(q,bandp)	((bandp) > (q)->q_bandp ? (bandp) - 1 : NULL)

#endif	/* _VECTOR_BANDS */


#endif /* ! __KERNEL_STRMLIB_H__ */