STREAMS Programming Guide

Exit Print View

Updated: July 2014
 
 

Module Procedures

STREAMS module procedures (open, close, put, service) have already been described in the previous chapters. This section shows some examples and further describes attributes common to module put and service procedures.

A module's put procedure is called by the preceding module, driver, or stream head, and always before that queue's service procedure. The put procedure does any immediate processing (for example, high-priority messages), while the corresponding service procedure performs deferred processing.

The service procedure is used primarily for performing deferred processing, with a secondary task to implement flow control. Once the service procedure is enabled, it can start but not finish before running user-level code. The put and service procedures must not block because there is no thread synchronization being done.

Example 10–1 shows a STREAMS module read-side put procedure.

Example 10-1  Read-side put Procedure
static int
modrput (queue_t *q, mblk_t *mp)
{
		struct mod_prv *modptr;

		modptr = (struct mod_prv *) q->q_ptr;  /*state info*/

		if (mp->b_datap->db_type >= QPCTL){	/*proc pri msg*/
			putnext(q, mp); 						/* and pass it on */
			return (0);
		}

		switch(mp->b_datap->db_type) {
			case M_DATA:				/* can process message data */
				putq(q, mp);	/* queue msg for service procedure */
			return (0);

			case M_PROTO:			/* handle protocol control message */
					.
					.
					.

			default:
					putnext(q, mp);		
					return (0);
		}
}

The preceding code does the following:

  • A pointer to a queue defining an instance of the module and a pointer to a message are passed to the put procedure.

  • The put procedure performs a switch on the type of the message. For each message type, the put procedure either enqueues the message for further processing by the module service procedure, or passes the message to the next module in the stream.

  • High-priority messages are typically processed immediately. Immediate processing is not required by the put procedure and the message is passed to the next module.

  • Ordinary (or normal) messages are either queued or passed along the stream.

Example 10–2 shows a module write-side put procedure.

Example 10-2  Write-side put Procedure
static int
modwput (queue_t *q, mblk_t *mp)
{
 	struct mod_prv *modptr;

 modptr = (struct mod_prv *) q->q_ptr;		/*state info*/

 if (mp->b_datap->db_type >= QPCTL){	/* proc pri msg and pass it on */
			putnext(q, mp);
			return (0);
		}

 switch(mp->b_datap->db_type) {
		case M_DATA:			/* can process message data queue msg */
			putq(q, mp);		/* for service procedure or pass message */
								/* along with putnext(q,mp) */
			return (0);

		case M_PROTO:
				.
				.
				.

		case M_IOCTL:		/* if cmd in msg is recognized */
							/* process message and send reply back */
							/* else pass message downstream */

		default:
			putnext(q, mp);
			return (0);
	 }
}

The write-side put procedure, unlike the read side, can be passed M_IOCTL messages. The module must recognize and process the ioctl(2) command, or pass the message downstream if it does not recognize the command.

Example 10–3 shows a general scenario employed by the module's service procedure.

Example 10-3  STREAMS Module Service Procedure
static int
modrsrv (queue_t *q)
{
		mblk_t *mp;

		while ((mp = getq(q)) != NULL) {
			/* flow control check */
			if (!(mp->b_datap->db_type >= QPCTL) && !canputnext(q)) {	
				putbq(q, mp);						/* return message */
				return (0);
			}
			/* process the message */
				.
				.
				.
			putnext(q, mp); /* pass the result */
		}
		return (0);
}

The steps are:

  1. Retrieve the first message from the queue using getq(9F).

  2. If the message is high priority, process it immediately and pass it along the stream.

    Otherwise, the service procedure should use canputnext(9F) to determine if the next module or driver that enqueues messages is within acceptable flow-control limits. canputnext(9F) searches the stream for the next module, driver, or the stream head with a service procedure. When it finds one, it looks at the total message space currently allocated to the queue for messages. If the amount of space currently used at that queue reaches the high-water mark, canputnext(9F) returns false (zero). If the next queue with a service procedure is within acceptable flow-control limits, canputnext(9F) returns true (nonzero).

  3. If canputnext(9F) returns false, the service procedure returns the message to its own queue with putbq(9F). The service procedure can do no further processing at this time, and it returns to the caller.

    If canputnext(9F) returns true, the service procedure completes any processing of the message. This can involve retrieving more messages from the queue, allocating and deallocating header and trailer information, and performing control functions for the module.

  4. When the service procedure is finished processing the message, it calls putnext(9F) to pass the resulting message to the next queue.

These steps are repeated until getq(9F) returns NULL (the queue is empty) or canputnext(9F) returns false.