qprocsoff does the inverse operation shown in Figure 7-9. This supports the need for cancelling callbacks before a qprocsoff.
qprocsoff is typically called at the begining of the close routine. The module can no longer receive messages from adjoining modules. The queue, however, still has pointers to it's adjoining modules and can putnext. However, as the queue is no longer inserted into the stream, these messages may be out of order from other messages in the stream, so it is best to process these messages before qprocsoff.
qwait is used because a module needs to get some response from another module or driver in the STREAM (i.e. a DLPI disconnect message sent downstream). qwait and qwait_sig must also be called before qprocsoff because once the queue is removed from the stream, there will be no way for the reply message to reach the queue.
put Procedure
The put procedure is the mechanism that other modules use to pass messages into this module. This procedure is called via the putor putnext routines on behalf of other modules. The queue's put procedure is invoked by the preceding module to process a message immediately (see put(9F) and putnext(9F)). Most modules will have a put routine. The common exception is on the read-side of drivers because there will not typically be a module downstream to the driver.
Note - Hardening Information. putnext is used by adjoining modules to ensure that the next module's queue is intact. Use of put cannot guarantee that the queue being called is currently valid and inserted into a stream; you must ensure that the queue is valid when using put.
A driver's put procedure must do one of the following:
Process and free the message.
Process and route the message back upstream.
Queue the message to be processed by the driver's service procedure.
All M_IOCTL type messages must be acknowledged through M_IOACK or rejected through M_IOCNACK. M_IOCTL messages should not be freed. Drivers must free any unrecognized message.
A module's put procedure must do one of the following as shown in Figure 7-10:
Process and free the message.
Process the message and pass it to the next module or driver.
Queue the message to be processed later by the module's service procedure.
Unrecognized messages are passed to the next module or driver. The stream operates more efficiently when messages are processed in the put procedure. Processing a message with the service procedure imposes some latency on the message.
Figure 7-10 Flow of put Procedure
If the next module is flow controlled (see canput(9F) and canputnext(9F)), the put procedure can queue the message for processing by the next service procedure (see putq(9F)). The put routine is always called before the component's corresponding srv(9E) service routine, so always use put for immediate message processing.
Note - Hardening Information. canput and canputnext operate similar to put and putnext; that is the next functions verify the integrity of the next queue. Not using the next functions can cause panics as the queue being referenced might have already been closed.
The preferred naming convention for a put procedure reflects the direction of the message flow. The read put procedure is suffixed by r (rput), and the write procedure by w (wput). For example, the read-side put procedure for module xx is declared as int xxrput (queue_t *q, mblk_t *mp). The write-side put procedure is declared as int xxwput(queue_t *q, mblk_t *mp), where q points to the corresponding read or write queue and mp points to the message to be processed.
Although high-priority messages can be placed on the service queue, processing them immediately in the put procedure is better. (See the stub code in Example 7-3.) Place ordinary or priority-band messages on the service queue (putq(9F)) if:
The stream has been flow controlled; that is, canput fails.
There are already messages on the service queue, that is, q_first is not NULL.
Deferred processing is desired.
If other messages already exist on the queue and the put procedure does not queue new messages (provided they are not high-priority), messages are reordered. If the next module is flow controlled (see canput(9F) and canputnext(9F)), the put procedure can queue the message for processing by the service procedure (see putq(9F)).
Example 7-3 Example of a Module put Procedure
Put procedures must never call putq, putbq, or qenable if the module does not have a service procedure.
Note - Hardening Information. Once a message is passed using a putq, put, putnext, as well as the perimeter function qwriter, it cannot be accessed again because the use of this message has been given to the new routine. If a reference needs to be retained by the module, it should copy it by using copyb, copymsg, dupb, or dupmsg.
A module need not process the message immediately, and can queue it for later processing by the service procedure (see putq(9F)).
The SunOS STREAMS framework is multithreaded. Unsafe (nonmultithreaded) modules are not supported. To make multithreading of modules easier, the SunOS STREAMS framework uses perimeters (see "MT STREAMS Perimeters" for more information).
Caution - Mutex locks must not be held across a call to put(9F), putnext(9F), or qreply(9F).
Because of the asynchronous nature of STREAMS, do not assume that a module's put procedure has been called just because put(9F), putnext(9F), or qreply(9F) has returned.
Queue service Procedure
A queue's service procedure is invoked to process messages on the queue. It removes successive messages from the queue, processes them, and calls the put procedure of the next module in the stream to give the processed message to the next queue.
The service procedure is optional. A module or driver can use a service procedure for the following reasons:
Streams flow control is implemented by service procedures. If the next component on the stream has been flow controlled, the put procedure can queue the message. (See "Flow Control in Service Procedures" in Chapter 7, STREAMS Framework - Kernel Level for more on flow control.)
Resource allocation recovery. If a put or service procedure cannot allocate a resource, such as memory, the message is usually queued to process later.
A device driver can queue a message and get out of interrupt context.
To combine multiple messages into larger messages.
The service procedure is invoked by the STREAMS scheduler. A STREAMS service procedure is scheduled to run if:
The queue is not disabled (noenable(9F)) and the message being queued is either the first message on the queue, or a priority band message.
The message being queued (putq(9F) or putbq(9F)) is a high-priority message,
The queue has been back-enabled because flow control has been relieved,
The queue has been explicitly enabled (qenable(9F)).