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

9.  STREAMS Drivers

10.  STREAMS Modules

11.  Configuring STREAMS Drivers and Modules

12.  Multithreaded STREAMS

Multithreaded (MT) STREAMS Overview

MT STREAMS Framework

STREAMS Framework Integrity

Message Ordering

MT STREAMS Perimeters

Inner Perimeters

Outer Perimeters

PERMOD Perimeter

Hot Perimeters

Defining Perimeter Types

Choosing a Perimeter Type

MT SAFE Modules and Drivers

MT SAFE Module

MT SAFE Driver

Routines Used Inside a Perimeter

qprocson/qprocsoff

qtimeout/qunbufcall

qwriter

qwait

Asynchronous Callback Functions

close() Race Conditions

Unloading a Module that Uses esballoc

Use of the q_next Field

MT SAFE Modules Using Explicit Locks

Constraints When Using Locks

Preserving Message Ordering

Preparing to Port

Porting to the SunOS 5 System

Sample Multithreaded Device Driver Using a Per Module Inner Perimeter

Sample Multithreaded Module With Outer Perimeter

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

Sample Multithreaded Module With Outer Perimeter

Example 12-2 is a sample multithreaded, loadable STREAMS module. The module MT design is a relatively simple one, based on a per queue-pair inner perimeter plus an outer perimeter. The inner perimeter protects per-instance data structure (accessed through the q_ptr field) and the module global data is protected by the outer perimeter. The outer perimeter is configured so that the open and close routines have exclusive access to the outer perimeter. This is necessary because they both modify the global-linked list of instances. Other routines that modify global data are run as qwriter(9F) callbacks, giving them exclusive access to the whole module.

Example 12-2 Multithread Module with Outer Perimeter

/*
 * Example SunOS 5 multi-threaded STREAMS module.
 * Using a per-queue-pair inner perimeter plus an outer perimeter.
 */

#include        <sys/types.h>
#include        <sys/errno.h>
#include        <sys/stropts.h>
#include        <sys/stream.h>
#include        <sys/strlog.h>
#include        <sys/cmn_err.h>
#include        <sys/kmem.h>
#include        <sys/conf.h>
#include        <sys/ksynch.h>
#include        <sys/modctl.h>
#include        <sys/stat.h>
#include        <sys/ddi.h>
#include        <sys/sunddi.h>

/*
 * Function prototypes.
 */
static            int xxopen(queue_t *, dev_t *, int, int, cred_t *);
static            int xxclose(queue_t *, int, cred_t *);
static            int xxwput(queue_t *, mblk_t *);
static            int xxwsrv(queue_t *);
static            void xxwput_ioctl(queue_t *, mblk_t *);
static            int xxrput(queue_t *, mblk_t *);
static            void xxtick(caddr_t);

/*
 * Streams Declarations
 */
static struct module_info xxm_info = {
   99,                    /* mi_idnum */
   “xx”,            /* mi_idname */
   0,                    /* mi_minpsz */
   INFPSZ,            /* mi_maxpsz */
   0,                    /* mi_hiwat */
   0                    /* mi_lowat */
};
/*
 * Define the read-side qinit structure
 */
static struct qinit xxrinit = {
        xxrput,         /* qi_putp */
        NULL,           /* qi_srvp */
        xxopen,         /* qi_qopen */
        xxclose,        /* qi_qclose */
        NULL,           /* qi_qadmin */
        &xxm_info,      /* qi_minfo */
        NULL            /* qi_mstat */
};
/*
 * Define the write-side qinit structure
 */
static struct qinit xxwinit = {
        xxwput,         /* qi_putp */
        xxwsr,          /* qi_srvp */
        NULL,           /* qi_qopen */
        NULL,           /* qi_qclose */
        NULL,           /* qi_qadmin */
        &xxm_info,      /* qi_minfo */
        NULL            /* qi_mstat */
};

static struct streamtab xxstrtab = {
        &xxrini,        /* st_rdinit */
        &xxwini,        /* st_wrinit */
        NULL,           /* st_muxrinit */
        NULL            /* st_muxwrinit */
};

/*
 * define the fmodsw structure.
 */

static struct fmodsw xx_fsw = {
        “xx”,         /* f_name */
        &xxstrtab,      /* f_str */
        (D_NEW|D_MP|D_MTQPAIR|D_MTOUTPERIM|D_MTOCEXCL) /* f_flag */
};

/*
 * Module linkage information for the kernel.
 */
static struct modlstrmod modlstrmod = {
        &mod_strmodops,    /* Type of module; a STREAMS module */
        “xx module”,        /* Module name */
        &xx_fsw,                /* fmodsw */
};

static struct modlinkage modlinkage = {
        MODREV_1,
        &modlstrmod,
        NULL
};

/*
 * Module private data structure. One is allocated per stream.
 */
struct xxstr {
        struct        xxstr *xx_next;    /* pointer to next in list */
        queue_t        *xx_rq;                /* read side queue pointer */
        int            xx_timeoutid;        /* id returned from timeout() */
};

/*
 * Linked list of opened stream xxstr structures and other module
 * global data. Protected by the outer perimeter.
 */
static struct xxstr                        *xxup = NULL;
static int some_module_global_data;


/*
 * Module Config entry points
 */
int
_init(void)
{
        return (mod_install(&modlinkage));
}
int
_fini(void)
{
        return (mod_remove(&modlinkage));
}
int
_info(struct modinfo *modinfop)
{
        return (mod_info(&modlinkage, modinfop));
}


static int
xxopen(queue_t *rq,dev_t *devp,int flag,int sflag, cred_t *credp)
{
        struct xxstr *xxp;
        /* If this stream already open - we're done. */
        if (rq->q_ptr)
            return (0);
        /* We must be a module */
        if (sflag != MODOPEN)
            return (EINVAL);

        /*
         * The perimeter flag D_MTOCEXCL implies that the open and
         * close routines have exclusive access to the module global
         * data structures.
         *
         * Allocate our private per-stream data structure.
         */
         xxp = kmem_alloc(sizeof (struct xxstr),KM_SLEEP);

        /* Point q_ptr at it. */
        rq->q_ptr = WR(rq)->q_ptr = (char *) xxp;

        /* Initialize it. */
        xxp->xx_rq = rq;
        xxp->xx_timeoutid = 0;

        /* Link new entry into the list of active entries. */
        xxp->xx_next = xxup;
        xxup = xxp;

        /* Enable xxput() and xxsrv() procedures on this queue. */
        qprocson(rq);
        /* Return success */
        return (0);
}

static int
xxclose(queue_t,*rq, int flag,cred_t *credp)
{
        struct            xxstr                *xxp;
        struct            xxstr                **prevxxp;

        /* Disable xxput() and xxsrv() procedures on this queue. */
        qprocsoff(rq);
        /* Cancel any pending timeout. */
         xxp = (struct xxstr *) rq->q_ptr;
         if (xxp->xx_timeoutid != 0) {
             (void) quntimeout(WR(rq), xxp->xx_timeoutid);
              xxp->xx_timeoutid = 0;
         }
        /*
         * D_MTOCEXCL implies that the open and close routines have
         * exclusive access to the module global data structures.
         *
         * Unlink per-stream entry from the active list and free it.
         */
        for (prevxxp = &xxup; (xxp = *prevxxp) != NULL; 
                prevxxp = &xxp->xx_next) {
            if (xxp == (struct xxstr *) rq->q_ptr)
                break;
        }
        *prevxxp = xxp->xx_next;
        kmem_free (xxp, sizeof (struct xxstr));
        rq->q_ptr = WR(rq)->q_ptr = NULL;
        return (0);
}

static int
xxrput(queue_t, *wq, mblk_t *mp)
{
        struct xxstr    *xxp = (struct xxstr *)wq->q_ptr;
    
        /*
         * Write your code here. Can read “some_module_global_data”
         * since we have shared access at the outer perimeter.
         */
        putnext(wq, mp);
}

/* qwriter callback function for handling M_IOCTL messages */
static void
xxwput_ioctl(queue_t, *wq, mblk_t *mp)
{
        struct xxstr                *xxp = (struct xxstr *)wq->q_ptr;

        /*
         * Write your code here. Can modify “some_module_global_data”
         * since we have exclusive access at the outer perimeter.
         */
        mp->b_datap->db_type = M_IOCNAK;
        qreply(wq, mp);
}

static
xxwput(queue_t *wq, mblk_t *mp)
{
        struct xxstr                *xxp = (struct xxstr *)wq->q_ptr;

        if (mp->b_datap->db_type == M_IOCTL) {
            /* M_IOCTL will modify the module global data */
            qwriter(wq, mp, xxwput_ioctl, PERIM_OUTER);
            return;
        }
        /*
         * Write your code here. Can read “some_module_global_data”
         * since we have exclusive access at the outer perimeter.
         */
        putnext(wq, mp);
}

static
xxwsrv(queue_t wq)
{
        mblk_t            *mp;
        struct xxstr    *xxp= (struct xxstr *) wq->q_ptr;

        while (mp = getq(wq)) {
        /*
         * Write your code here. Can read “some_module_global_data”
         * since we have exclusive access at the outer perimeter.
         */
            freemsg(mp);

            /* for example, start a timeout */
            if (xxp->xx_timeoutid != 0) {
                /* cancel running timeout */
                (void) quntimeout(wq, xxp->xx_timeoutid);
            }
            xxp->xx_timeoutid = qtimeout(wq, xxtick, (char *)xxp, 10);
        }
}

static void
xxtick(arg)
        caddr_t arg;
{
        struct xxstr *xxp = (struct xxstr *)arg;

        xxp->xx_timeoutid = 0;      /* timeout has run */
        /*
         * Write your code here. Can read “some_module_global_data”
         * since we have shared access at the outer perimeter.
         */
}