|
flushband(9F) is discussed in Flushing Priority Band. bcanputnext(9F) is discussed in Flow Control in Service Procedures, and the other two routines are described in the following section. Appendix B, Kernel Utility Interface Summary also has a description of these routines.
Message Processing Procedures
Typically, put procedures are required in pushable modules, but service procedures are optional. If the put routine queues messages, a corresponding service routine must be present to handle the queued messages. If the put routine does not queue messages, the service routine is not required.
Figure 7-10 shows typical processing flow for a put procedure which works as follows:
A message is received by the put procedure associated with the queue, where some processing can be performed on the message.
The put procedure determines if the message can be sent to the next module by the use of canput(9F) or canputnext(9F).
If the next module is flow controlled, the put procedure queues the message using putq(9F).
putq(9F) places the message in the queue based on its priority.
Then, putq(9F) makes the queue ready for execution by the STREAMS scheduler, following all other queues currently scheduled.
If the next module is not flow controlled, the put procedure does any processing needed on the message and sends it to the next module using putnext(9F). Note that if the module does not have a service procedure it cannot queue the message, and must process and send the message to the next module.
Figure 7-11 shows typical processing flow for a service procedure that works as follows:
When the system goes from kernel mode to user mode, the STREAMS scheduler calls the service procedure.
The service procedure gets the first message (q_first) from the message queue using the getq(9F)
utility.
The put procedure determines if the message can be sent to the next module using canput(9F) or canputnext(9F).
If the next module is flow controlled, the put procedure requeues the message with putbq(9F), and then returns.
If the next module is not flow controlled, the service procedure processes the message and passes it to the put procedure of the next queue with putnext(9F).
The service procedure gets the next message and processes it. This processing continues until the queue is empty or flow control blocks further processing. The service procedure returns to the caller.
Caution - A service or put procedure must never block since it has no user context. It must always return to its caller.
If no processing is required in the put procedure, the procedure does not have to be explicitly declared. Rather, putq(9F)
can be placed in the qinit(9S) structure declaration for the appropriate queue side to queue the message for the service procedure.
For example:
static struct qinit winit = { putq, modwsrv, ...... };
|
More typically, put procedures process high-priority messages to avoid queueing them.
Device drivers associated with hardware are examples of STREAMS devices that might not have a put procedure. Since there are no queues below the hardware level, another module does not call the module's put procedure. Data comes into the stream from an interrupt
routine, and is either processed or queued for the service procedure.
A STREAMS filter is an example of a module without a service procedure--messages passed to it are either passed or filtered. Flow control is described in Flow Control in Service Procedures.
The key attribute of a service procedure in the STREAMS architecture is delayed processing. When a service procedure is used in a module, the module developer is implying that there are other, more time-sensitive activities to be performed elsewhere in this
stream, in other streams, or in the system in general.
Note - The presence of a service procedure is mandatory if the flow control mechanism is to be utilized by the queue. If you do not implement flow control, queues can overflow and hang the system.
Flow Control in Service Procedures
The STREAMS flow control mechanism is voluntary and operates between the two nearest queues in a stream containing service procedures (see Figure 7-13). Messages are held on a queue only if a service procedure is present
in the associated queue.
Messages accumulate on a queue when the queue's service procedure processing does not keep pace with the message arrival rate, or when the procedure is blocked from placing its messages on the following STREAMS component by the flow control mechanism. Pushable modules can contain independent upstream
and downstream limits. The stream head contains a preset upstream limit (which can be modified by a special message sent from downstream) and a driver can contain a downstream limit. See M_SETOPTS for more information.
Flow control operates as follows:
Each time a STREAMS message-handling routine (for example, putq(9F)) adds or removes a message from a message queue, the limits are
checked. STREAMS calculates the total size of all message blocks (bp->b_wptr - bp->b_rptr) on the message queue.
Note - bp is a pointer to the buffer header structure allocated by bp_mapin(), b_wptr is the first unwritten byte in the buffer, and b_rptr is the first unread byte in the buffer. See msgb(9S) STREAMS message block structure.
The total is compared to the queue high and low watermark values. If the total exceeds the high watermark value, an internal full indicator is set for the queue. The operation of the service procedure in this queue is not affected if the indicator is set, and the service procedure continues
to be scheduled.
The next part of flow control processing occurs in the nearest preceding queue that contains a service procedure. In the following figure, if D is full and C has no service procedure, then B is the nearest preceding queue.
Figure 7-13 Flow Control Mechanism
 The service procedure in B uses canputnext(9F) to check if a queue ahead is marked full. If messages cannot be sent, the scheduler
blocks the service procedure in B from further execution. B remains blocked until the low watermark of the full queue, D, is reached.
While B is blocked, any messages except high-priority messages arriving at B accumulate on its message queue. High-priority messages are not subject to flow control. Eventually, B can reach a full state and the full condition propagates back to the preceding module in the stream.
When the service procedure processing on D causes the message block total to fall below the low watermark, the full indicator is turned off. STREAMS then schedules the nearest preceding blocked queue (B in this case). This automatic scheduling is called back-enabling a queue.
Modules and drivers need to observe the message priority. High-priority messages, determined by the type of the first block in the message,
mp->b_datap->db_type >= QPCTL
|
are not subject to flow control. They should be processed immediately and forwarded, as appropriate.
For ordinary messages, flow control must be tested before any processing is performed. canputnext(9F) determines if the forward path from the queue is blocked by flow control.
This is the general flow control processing of ordinary messages:
Retrieve the message at the head of the queue with getq(9F).
Determine if the message type is high priority and not to be processed here.
If so, pass the message to the put procedure of the following queue with putnext(9F).
Use canputnext(9F) to determine if messages can be sent onward.
If messages cannot be forwarded, put the message back in the queue with putbq(9F) and return from the procedure.
Caution - High-priority messages must be processed and not placed back on the queue.
The canonical representation of this processing within a service procedure is:
while (getq() != NULL)
if (high priority message || no flow control) {
process message
putnext()
} else {
putbq()
return
}
|
|