Example 60, Multithreaded, Loadable, STREAMS Pseudo-Driver 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 synchronous callback routine is used. The driver cancels an outstanding qtimeout by calling quntimeout in the close routine. For more information, see close Race Conditions. For more information, see the quntimeout(9F) and qtimeout(9F) man page.
Example 60 Multithreaded, Loadable, STREAMS Pseudo-Driver/* * Example Oracle Solaris 11 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 */ }