Sun Microsystems, Inc.
spacerspacer
spacer www.sun.com docs.sun.com |
spacer
black dot
 
 
7.  STREAMS Framework - Kernel Level Message Queues queue(9S)Structure   Previous   Contents   Next 
   
 

Using Queue Information

The q_first, q_last, q_count, and q_flags components must not be modified by the module, and should be accessed using strqget(9F). The values of q_minpsz, q_maxpsz, q_hiwat, and q_lowat are accessed through strqget(9F), and are modified by strqset(9F). q_ptr can be accessed and modified by the module and contains data private to the module.

All other accesses to fields in the queue(9S) structure should be made through STREAMS utility routines (see Appendix B, "STREAMS Utilities"). Modules and drivers should not change any fields not explicitly listed previously.

strqget(9F) enables modules and drivers to get information about a queue or particular band of the queue. This insulates the STREAMS data structures from the modules and drivers. The prototype for the strqget(9F) routine is:

int
strqget(queue_t *q, qfields_t what, unsigned char pri, void *valp)

q specifies from which queue the information is to be retrieved; what defines the queue_t field value to obtain (see the following structure fields). pri identifies a specific priority band. The value of the field is returned in valp. The fields that can be obtained are defined in <sys/stream.h> and shown here as:

QHIWAT              /* high-water mark */
QLOWAT              /* low-water mark */
QMAXPSZ             /* largest packet accepted */
QMINPSZ             /* smallest packet accepted */
QCOUNT              /* approx. size (in bytes) of data */
QFIRST              /* first message */
QLAST               /* last message */
QFLAG               /* status */

strqset(9F) enables modules and drivers to change information about a queue or a band of the queue. This also insulates the STREAMS data structures from the modules and drivers. Its prototype is:

int 
strqset(queue_t *q. qfields_t what, unsigned char pri, intptr_t val)

The q, what, and pri fields are the same as in strqget(9F), but the information to be updated is provided in val instead of through a pointer. If the field is read-only, EPERM is returned and the field is left unchanged. The following fields are read-only: QCOUNT, QFIRST, QLAST, and QFLAG.


Note - Hardening Information. Access queue structure information, through strqget() and strqset() only. Do not access the queue structure directly.


Entry Points

The q_qinfo component points to a qinit structure. This structure defines the module's entry point procedures for each queue, which include the following:

int        (*qi_putp)();       /* put procedure */
int        (*qi_srvp)();       /* service procedure */
int        (*qi_qopen)();      /* open procedure */
int        (*qi_qclose)();     /* close procedure */
struct module_info *qi_minfo;  /* module information structure */

There is generally a unique q_init structure associated with the read queue and the write queue. qi_putp identifies the put procedure for the module. qi_srvp identifies the optional service procedure for the module.

The open and close entry points are required for the read-side queue. The put procedure is generally required on both queues and the service procedure is optional.


Note - Hardening Information. If the put procedure is not defined and a subsequent put is done to the module, a panic occurs. As a precaution, putnext should be declared as the module's put procedure.

If a module only requires a service procedure, putq(9F) can be used as the module's put procedure. If the service procedure is not defined, the module's put procedure must not queue data (putq).


The qi_qopen member of the read-side qinit structure identifies the open(9E) entry point of the module. The qi_qclose member of the read-side qinit structure identifies the close(9E) entry point of the module.

The qi_minfo member points to the module_info(9S) structure.

struct module_info {
		ushort   mi_idnum;          /* module id number */
		char     *mi_idname;        /* module name */
		ssize_t  mi_minpsz;         /* min packet size accepted */
		ssize_t  mi_maxpsz;         /* max packet size accepted */
		size_t   mi_hiwat;          /* hi-water mark */
		size_t   mi_lowat;          /* lo-water mark */
};

mi_idnum is the module's unique identifier defined by the developer and used in strlog(9F). mi_idname is an ASCII string containing the name of the module. mi_minpsz is the initial minimum packet size of the queue. mi_maxpsz is the initial maximum packet size of the queue. mi_hiwat is the initial high-water mark of the queue. mi_lowat is the initial low-water mark of the queue.

open Routine

The open(9E) routine of a device is called once for the initial open of the device, then is called again on subsequent reopens of the stream. Module open routines are called once for the initial push onto the stream and again on subsequent reopens of the stream.

Figure 7-8 Order of a Module's open Procedure

The stream is analogous to a stack. Initially the driver is opened and as modules are pushed onto the stream, their open routines are invoked. Once the stream is built, this order reverses if a reopen of the stream occurs. For example, while building the stream shown in Figure 7-8, device A's open routine is called, followed by B's and C's when they are pushed onto the stream. If the stream is reopened, Module C's open routine is called first, followed by B's, and finally by A's.

Usually the module or driver does not check this, but the issue is raised so that dependencies on the order of open routines are not introduced by the programmer. Note that although an open can happen more than once, close is only called once. See the next section on the close routine for more details. If a file is duplicated (dup(2)) the stream is not reopened.

The prototype for the open entry point is:

int prefix_open(queue_t *q, dev_t *devp, int oflag, int sflag, 
						cred_t *cred_p)
q

Pointer to the read queue of this module.

devp

Pointer to a device number that is always associated with the device at the end of the stream. Modules cannot modify this value, but drivers can, as described in Chapter 9, STREAMS Drivers.

oflag

For devices, oflag can contain the following bit mask values: FEXCL, FNDELAY, FREAD, and FWRITE. See Chapter 9, STREAMS Drivers for more information on drivers.

sflag

When the open is associated with a driver, sflag is set to 0 or CLONEOPEN, see Chapter 9, STREAMS Drivers, "Cloning STREAMS Drivers" for more details. If the open is associated with a module, sflag contains the value MODOPEN.

cred_p

Pointer to the user credentials structure.

The open routines to devices are serialized . If more than one process attempts to open the device, only one proceeds and the others wait until the first finishes. Interrupts are not blocked during an open. The driver's interrupt routine must continue to handle interrupts when multiple processes are opening the same device. See Chapter 9, STREAMS Drivers for more information.

The open routines for both drivers and modules have user context. For example, they can do blocking operations, but the blocking routine should return in the event of a signal. In other words, q_wait_sig is allowed, but q_wait is not.

If the module or driver is to allocate a controlling terminal, it should send an M_SETOPTS message with SO_ISTTY set to the stream head.

The open routine usually initializes the q_ptr member of the queue. q_ptr is generally initialized to some private data structure that contains various state information private to the module or driver. The module's close routine is responsible for freeing resources allocated by the module including q_ptr. Example 7-1 shows a simple open routine.


Example 7-1 A Simple open Routine

/* example of a module open */
int xx_open(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *crp)
{
	struct xxstr *xx_ptr;

	xx_ptr = kmemzalloc(sizeof(struct xxstr), KM_SLEEP);
	xx_ptr->xx_foo = 1;
	q->q_ptr = WR(q)->q_ptr = xx_ptr;
	qprocson(q);
	return (0);
}

In a multithreaded environment, data can flow up the stream during the open. A module receiving this data before its open routine finishes initialization can panic. To eliminate this problem, modules and drivers are not linked into the stream until qprocson(9F) is called (messages flow around the module). Figure 7-9 illustrates this process. See Chapter 12, Multithreaded STREAMS for more information on the multithreaded environment and the use of perimeters.

Figure 7-9 Messages Flowing Around the Module Before qprocson

The module or driver instance is guaranteed to be single-threaded before qprocson(9F) is called, except for interrupts or callbacks that must be handled separately.


Note - Hardening Information. qprocson must be called before calling qbufcall(9F), qtimeout(9F), qwait(9F), or qwait_sig(9F).

Before a module calls qprocson, it must be ready to accept data via the module's put procedure so all data structures must be fully initialized (see "put Procedure"). The most common method for calling qprocson is to call this function just before returning from a successful open (see Example 7-1).



Note - For a multithreaded environment, verify you are using the correct perimeter before accessing data. See Chapter 12, Multithreaded STREAMS for more information on the multithreaded environment and the use of perimeters.


close Routine

The close routine of devices is called only during the last close of the device. Module close routines are called during the last close or if the module is popped.

The prototype for the close entry point is:

int prefix_close (queue *q, int flag, cred_t * cred_p)
q

is a pointer to the read queue of the module.

flag

is analogous to the oflag parameter of the open entry point. If FNBLOCK or FNDELAY is set, then the module should attempt to avoid blocking during the close.

cred_p

is a pointer to the user credential structure.

Like open, the close entry point has user context and can block. Likewise, the blocking routines should return in the event of a signal. Device drivers must take into consideration that interrupts are not blocked during close. The close routine must cancel all pending and qbufcall callbacks, and process any remaining messages on its service queue.

The open and close procedures are only used on the read side of the queue and can be set to NULL in the write-side qinit structure initialization. Example 7-2 shows an example of a module close routine.


Example 7-2 Example of a Module Close

/* example of a module close */
static int
xx_close(queue_t *, *rq, int flag, cred_t *credp)
{
		struct xxstr   *xxp;
  
   /*
    * Disable xxput() and xxsrv() procedures on this queue.
    */
		qprocsoff(rq);
		xxp = (struct xxstr *) rq->q_ptr;

   /*
    * Cancel any pending timeout.
    * This example assumes that the timeout was issued
    * against the write queue.
    */

		if (xxp->xx_timeoutid != 0) {
			(void) quntimeout(WR(rq), xxp->xx_timeoutid);
			xxp->xx_timeoutid=0;
    }
   /*
    * Cancel any pending bufcalls.
    * This example assumes that the bufcall was issued
    * against the write queue.
    */
		if (xxp->xx_bufcallid !=0) {
			(void) qunbufcall(WR(rq), xxp->xx_bufcallid);
 		xxp->xx_bufcallid = 0;
		}
		rq->q_ptr = WR(rq)->q_ptr = NULL;

   /*
    * Free resources allocated during open
    */
		kmem_free (xxp, sizeof (struct xxstr));
		return (0);
}

 
 
 
  Previous   Contents   Next