Skip Navigation Links | |
Exit Print View | |
STREAMS Programming Guide Oracle Solaris 11.1 Information Library |
Part I Application Programming Interface
2. STREAMS Application-Level Components
3. STREAMS Application-Level Mechanisms
4. Application Access to the STREAMS Driver and Module Interfaces
7. STREAMS Framework - Kernel Level
8. STREAMS Kernel-Level Mechanisms
11. Configuring STREAMS Drivers and Modules
Multithreaded (MT) STREAMS Overview
Routines Used Inside a Perimeter
Asynchronous Callback Functions
Unloading a Module that Uses esballoc
MT SAFE Modules Using Explicit Locks
Sample Multithreaded Module With Outer Perimeter
14. Debugging STREAMS-based Applications
B. Kernel Utility Interface Summary
Example 12-1 is a sample multithreaded, loadable, STREAMS pseudo-driver. The driver MT design is the simplest possible based on using a per module inner perimeter. Thus, only one thread can execute in the driver at any time. In addition, a quntimeout(9F) synchronous callback routine is used. The driver cancels an outstanding qtimeout(9F) by calling quntimeout(9F) in the close routine. See close() Race Conditions.
Example 12-1 Multithreaded, Loadable, STREAMS Pseudo-Driver
/* * Example SunOS 5 multithreaded STREAMS pseudo device driver. * Using a D_MTPERMOD inner 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/modctl.h> #include <sys/kmem.h> #include <sys/conf.h> #include <sys/ksynch.h> #include <sys/stat.h> #include <sys/ddi.h> #include <sys/sunddi.h> /* * Function prototypes. */ static int xxidentify(dev_info_t *); static int xxattach(dev_info_t *, ddi_attach_cmd_t); static int xxdetach(dev_info_t *, ddi_detach_cmd_t); static int xxgetinfo(dev_info_t *,ddi_info_cmd_t,void *,void**); 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 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 */ }; static struct qinit xxrinit = { NULL, /* qi_putp */ NULL, /* qi_srvp */ xxopen, /* qi_qopen */ xxclose, /* qi_qclose */ NULL, /* qi_qadmin */ &xxm_info, /* qi_minfo */ NULL /* qi_mstat */ }; static struct qinit xxwinit = { xxwput, /* qi_putp */ xxwsrv, /* qi_srvp */ NULL, /* qi_qopen */ NULL, /* qi_qclose */ NULL, /* qi_qadmin */ &xxm_info, /* qi_minfo */ NULL /* qi_mstat */ }; static struct streamtab xxstrtab = { &xxrinit, /* st_rdinit */ &xxwinit, /* st_wrinit */ NULL, /* st_muxrinit */ NULL /* st_muxwrinit */ }; /* * define the xx_ops structure. */ static struct cb_ops cb_xx_ops = { nodev, /* cb_open */ nodev, /* cb_close */ nodev, /* cb_strategy */ nodev, /* cb_print */ nodev, /* cb_dump */ nodev, /* cb_read */ nodev, /* cb_write */ nodev, /* cb_ioctl */ nodev, /* cb_devmap */ nodev, /* cb_mmap */ nodev, /* cb_segmap */ nochpoll, /* cb_chpoll */ ddi_prop_op, /* cb_prop_op */ &xxstrtab, /* cb_stream */ (D_NEW|D_MP|D_MTPERMOD) /* cb_flag */ }; static struct dev_ops xx_ops = { DEVO_REV, /* devo_rev */ 0, /* devo_refcnt */ xxgetinfo, /* devo_getinfo */ xxidentify, /* devo_identify */ nodev, /* devo_probe */ xxattach, /* devo_attach */ xxdetach, /* devo_detach */ nodev, /* devo_reset */ &cb_xx_ops, /* devo_cb_ops */ (struct bus_ops *)NULL /* devo_bus_ops */ }; /* * Module linkage information for the kernel. */ static struct modldrv modldrv = { &mod_driverops, /* Type of module. This one is a driver */ "xx", /* Driver name */ &xx_ops, /* driver ops */ }; static struct modlinkage modlinkage = { MODREV_1, &modldrv, NULL }; /* * Driver 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 */ minor_t xx_minor; /* minor device # (for clone) */ int xx_timeoutid; /* id returned from timeout() */ }; /* * Linked list of opened Stream xxstr structures. * No need for locks protecting it since the whole module is * single threaded using the D_MTPERMOD perimeter. */ static struct xxstr *xxup = NULL; /* * Module Config entry points */ _init(void) { return (mod_install(&modlinkage)); } _fini(void) { return (mod_remove(&modlinkage)); } _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } /* * Auto Configuration entry points */ /* Identify device. */ static int xxidentify(dev_info_t *dip) { if (strcmp(ddi_get_name(dip), "xx") == 0) return (DDI_IDENTIFIED); else return (DDI_NOT_IDENTIFIED); } /* Attach device. */ static int xxattach(dev_info_t *dip, ddi_attach_cmd_t cmd) { /* This creates the device node. */ if (ddi_create_minor_node(dip, "xx", S_IFCHR, ddi_get_instance(dip), DDI_PSEUDO, CLONE_DEV) == DDI_FAILURE) { return (DDI_FAILURE); } ddi_report_dev(dip); return (DDI_SUCCESS); } /* Detach device. */ static int xxdetach(dev_info_t *dip, ddi_detach_cmd_t cmd) { ddi_remove_minor_node(dip, NULL); return (DDI_SUCCESS); } /* ARGSUSED */ static int xxgetinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **resultp) { dev_t dev = (dev_t) arg; int instance, ret = DDI_FAILURE; devstate_t *sp; state *statep; instance = getminor(dev); switch (infocmd) { case DDI_INFO_DEVT2DEVINFO: if ((sp = ddi_get_soft_state(statep, getminor((dev_t) arg))) != NULL) { *resultp = sp->devi; ret = DDI_SUCCESS; } else *result = NULL; break; case DDI_INFO_DEVT2INSTANCE: *resultp = (void *)instance; ret = DDI_SUCCESS; break; default: break; } return (ret); } static xxopen(rq, devp, flag, sflag, credp) queue_t *rq; dev_t *devp; int flag; int sflag; cred_t *credp; { struct xxstr *xxp; struct xxstr **prevxxp; minor_t minordev; /* If this stream already open - we're done. */ if (rq->q_ptr) return (0); /* Determine minor device number. */ prevxxp = & xxup; if (sflag == CLONEOPEN) { minordev = 0; while ((xxp = *prevxxp) != NULL) { if (minordev < xxp->xx_minor) break; minordev++; prevxxp = &xxp->xx_next; } *devp = makedevice(getmajor(*devp), minordev) } else minordev = getminor(*devp); /* Allocate our private per-Stream data structure. */ if ((xxp = kmem_alloc(sizeof (struct xxstr), KM_SLEEP)) == NULL) return (ENOMEM); /* Point q_ptr at it. */ rq->q_ptr = WR(rq)->q_ptr = (char *) xxp; /* Initialize it. */ xxp->xx_minor = minordev; xxp->xx_timeoutid = 0; xxp->xx_rq = rq; /* Link new entry into the list of active entries. */ xxp->xx_next = *prevxxp; *prevxxp = xxp; /* Enable xxput() and xxsrv() procedures on this queue. */ qprocson(rq); return (0); } static xxclose(rq, flag, credp) 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(rq, xxp->xx_timeoutid); xxp->xx_timeoutid = 0; } /* 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 xxwput(wq, mp) queue_t *wq; mblk_t *mp; { struct xxstr *xxp = (struct xxstr *)wq->q_ptr; /* write your code here */ /* *** Sacha's Comments *** broken */ freemsg(mp); mp = NULL; if (mp != NULL) putnext(wq, mp); } static xxwsrv(wq) queue_t *wq; { mblk_t *mp; struct xxstr *xxp; xxp = (struct xxstr *) wq->q_ptr; while (mp = getq(wq)) { /* write your code here */ 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 */ }