JavaScript is required to for searching.
Skip Navigation Links
Exit Print View
STREAMS Programming Guide     Oracle Solaris 11.1 Information Library
search filter icon
search icon

Document Information

Preface

Part I Application Programming Interface

1.  Overview of STREAMS

2.  STREAMS Application-Level Components

3.  STREAMS Application-Level Mechanisms

4.  Application Access to the STREAMS Driver and Module Interfaces

5.  STREAMS Administration

6.  Pipes and Queues

Part II Kernel Interface

7.  STREAMS Framework - Kernel Level

8.  STREAMS Kernel-Level Mechanisms

ioctl Processing

Message Allocation and Freeing

Recovering From No Buffers

Read Device Interrupt Handler

Write Service Procedure

Releasing Callback Requests

Extended STREAMS Buffers

esballoc(9F) Example

General ioctl Processing

STREAMS ioctl Issues

I_STR ioctl Processing

Transparent ioctl

Transparent ioctl Messages

Transparent ioctl Examples

M_COPYIN Example

M_COPYOUT Example

Bidirectional Data Transfer Example

I_LIST ioctl(2)Example

M_FLUSH Message Handling

Flushing According to Priority Bands

Flushing Priority Band

Driver and Module Service Interfaces

Service Interface Library Example

Module Service Interface Example

Message Type Change Rules

Common ioctl Interfaces

FIORDCHK

FIONREAD

I_NREAD

signal Message

9.  STREAMS Drivers

10.  STREAMS Modules

11.  Configuring STREAMS Drivers and Modules

12.  Multithreaded STREAMS

13.  STREAMS Multiplex Drivers

Part III Advanced Topics

14.  Debugging STREAMS-based Applications

Part IV Appendixes

A.  Message Types

B.  Kernel Utility Interface Summary

C.  STREAMS-Based Terminal Subsystem

D.  STREAMS FAQ

Glossary

Index

Message Allocation and Freeing

The allocb(9F) utility routine allocates a message and the space to hold the data for the message. allocb(9F) returns a pointer to a message block containing a data buffer of at least the size requested, providing there is enough memory available. The routine returns NULL on failure. allocb(9F) always returns a message of type M_DATA. The type can then be changed if required. b_rptr and b_wptr are set to db_base (see msgb(9S) and datab(9S)), which is the start of the memory location for the message buffer.


Note - STREAMS often provides buffers that are bit aligned, but there is no guarantee that db_base or db_lim reside on bit-aligned boundaries. If bit or page alignment is required on module-supplied buffers use esballoc For more information about esballoc see Extended STREAMS Buffers.


allocb(9F) can return a buffer larger than the size requested. If allocb(9F) indicates that buffers are not available (allocb(9F) fails), the put or service procedure cannot block to wait for a buffer to become available. Instead, bufcall(9F) defers processing in the module or the driver until a buffer becomes available.

If message space allocation is done by the put procedure and allocb(9F) fails, the message is usually discarded. If the allocation fails in the service routine, the message is returned to the queue. bufcall(9F) is called to set a call to the service routine when a message buffer becomes available, and the service routine returns.

freeb(9F) releases the message block descriptor and the corresponding data block, if the reference count (see datab(9S)) is equal to 1. If the reference count exceeds 1, the data block is not released.

freemsg(9F) releases all message blocks in a message. It uses freeb(9F) to free all message blocks and corresponding data blocks.

In Example 8-1, allocb(9F) is used by the bappend subroutine that appends a character to a message block.

Example 8-1 Use of allocb

/*
 * Append a character to a message block.
 * If (*bpp) is null, it will allocate a new block
 * Returns 0 when the message block is full, 1 otherwise
 */
#define MODBLKSZ        128            /* size of message blocks */

static int bappend(mblk_t **bpp, int ch)
{
     mblk_t *bp;

     if ((bp = *bpp) != NULL) {
             if (bp->b_wptr >= bp->b_datap->db_lim)
                 return (0);
     } else {
             if ((*bpp = bp = allocb(MODBLKSZ, BPRI_MED)) == NULL)
                 return (1);
     }
     *bp->b_wptr++ = ch;
     return 1;
}

bappend receives a pointer to a message block and a character as arguments. If a message block is supplied (*bpp != NULL), bappend checks if there is room for more data in the block. If not, it fails. If there is no message block, a block of at least MODBLKSZ is allocated through allocb(9F).

If allocb(9F) fails, bappend returns success and discards the character. If the original message block is not full or the allocb(9F) is successful, bappend stores the character in the block.

Example 8-2 shows the processing of all the message blocks in any downstream data (type M_DATA) messages. freemsg(9F) frees messages.

Example 8-2 Subroutine modwput

/* Write side put procedure */
static int modwput(queue_t *q, mblk_t *mp)
{
     switch (mp->b_datap->db_type) {
     default:
             putnext(q, mp);            /* Don't do these, pass along */
             break;

    case M_DATA: {
             mblk_t *bp;
            struct mblk_t *nmp = NULL, *nbp = NULL;

            for (bp = mp; bp != NULL; bp = bp->b_cont) {
                 while (bp->b_rptr < bp->b_wptr) {
                         if (*bp->b_rptr == '\n')
                                 if (!bappend(&nbp, '\r'))
                                     goto newblk;
                         if (!bappend(&nbp, *bp->b_rptr))
                                 goto newblk;

                        bp->b_rptr++;
                         continue;

                newblk:
                         if (nmp == NULL)
                                 nmp = nbp;
                         else { /* link msg blk to tail of nmp */
                                 linkb(nmp, nbp);
                                 nbp = NULL;
                         }
                 }
             }
            if (nmp == NULL)
                 nmp = nbp;
             else
                 linkb(nmp, nbp);
             freemsg(mp); /* de-allocate message */
             if (nmp)
                 putnext(q, nmp);
             break;
          }
     }
}

Data messages are scanned and filtered. modwput copies the original message into new blocks, modifying as it copies. nbp points to the current new message block. nmp points to the new message being formed as multiple M_DATA message blocks. The outer for loop goes through each message block of the original message. The inner while loop goes through each byte. bappend is used to add characters to the current or new block. If bappend fails, the current new block is full. If nmp is NULL, nmp is pointed at the new block. If nmp is not NULL, the new block is linked to the end of nmp by use of linkb(9F).

At the end of the loops, the final new block is linked to nmp. The original message (all message blocks) is returned to the pool by freemsg(9F). If a new message exists, it is sent downstream.

Recovering From No Buffers

bufcall(9F) can be used to recover from an allocb(9F) failure. The call syntax is as follows:

bufcall_id_t bufcall(int size, int pri, void(*func)(), long arg);

Note - qbufcall(9F) and qunbufcall(9F) must be used with perimeters.


bufcall(9F) calls (*func)(arg) when a buffer of size bytes is available. When func is called, it has no user context and must return without blocking. Also, there is no guarantee that when func is called, a buffer will still be available.

On success, bufcall returns a nonzero identifier that can be used as a parameter to unbufcall(9F) to cancel the request later. On failure, 0 is returned and the requested function is never called.


Caution

Caution - Care must be taken to avoid deadlock when holding resources while waiting for bufcall to call (*func(arg). bufcall should be used sparingly.


Read Device Interrupt Handler

Example 8-3 is an example of a read device interrupt handler.

Example 8-3 Device Interrupt Handler

#include <sys/types.h>
#include <sys/param.h>
#include <sys/stream.h>
buffcall_id_t id;                        /* hold id val for unbufcall */

dev_rintr(dev)
{
     /* process incoming message ... */
     /* allocate new buffer for device */
     dev_re_load(dev);
}

/*
 * Reload device with a new receive buffer
 */
dev_re_load(dev)
{
     mblk_t *bp;
     id = 0;                        /* begin with no waiting for buffers */
     if ((bp = allocb(DEVBLKSZ, BPRI_MED)) == NULL) {
             cmn_err(CE_WARN,"dev:allocbfailure(size%d)\n",
                  DEVBLKSZ);
             /*
              * Allocation failed. Use bufcall to
              * schedule a call to ourselves.
              */
             id = bufcall(DEVBLKSZ,BPRI_MED,dev_re_load,dev);
             return;
     }

     /* pass buffer to device ... */
}

See Chapter 12, Multithreaded STREAMS for more information on the uses of unbufcall(9F). These references to unbufcall are protected by MT locks.

Because bufcall(9F) can fail, there is still a chance that the device will hang. A better strategy if bufcall(9F) fails is to discard the current input message and resubmit that buffer to the device. Losing input data is preferable to the device hanging.

Write Service Procedure

Example 8-4 is an example of a write service procedure.

Example 8-4 Write Service Procedure

static int mod_wsrv(queue_t *q)
{
     extern int qenable();
     mblk_t *mp, *bp;
        while (mp = getq(q)) {
            /* check for priority messages and canput ... */

            /* Allocate a header to prepend to the message.
          * If the allocb fails, use bufcall to reschedule.
          */
         if ((bp = allocb(HDRSZ, BPRI_MED)) == NULL) {
             if (!(id=bufcall(HDRSZ,BPRI_MED,qenable, q))) {
                  timeout(qenable, (caddr_t)q,
                    drv_usectohz());
                 /*
                  * Put the msg back and exit, we will be
                  * re-enabled later
                     */
                 putbq(q, mp);
                 return;
             }
             /* process message .... */
         }
        }
    }

mod_wsrv prefixes each output message with a header.

In this example, mod_wsrv illustrates a potential deadlock case. If allocb(9F) fails, mod_wsrv tends to recover without loss of data and calls bufcall(9F). In this case, the routine passed to bufcall(9F) is qenable(9F). When a buffer is available, the service procedure is automatically re-enabled. Before exiting, the current message is put back in the queue. Example 8-4 deals with bufcall(9F) failure by calling timeout(9F).

timeout(9F) schedules the given function to be run with the given argument in the given number of clock cycles. In this example, if bufcall(9F) fails, the system runs qenable(9F) after two seconds have passed.

Releasing Callback Requests

When allocb(9F) fails and bufcall(9F) is called, a callback is pending until a buffer is actually returned. Because this callback is asynchronous, it must be released before all processing is complete. To release this queued event, use unbufcall(9F).

Pass the id returned by bufcall(9F) to unbufcall(9F). Then close the driver in the normal way. If this sequence of unbufcall(9F) and xxclose is not followed, the callback can occur when the driver is already closed. This is one of the most difficult types of problems to find and debug.


Caution

Caution - All bufcall(9F) and timeouts must be canceled in the close routine.