Annotation of coherent/b/STREAMS/conf/loop/src/loop.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * This loop-around driver has been transcribed verbatim from the System V,
                      3:  * Release 4 Multiprocessor "Programmer's Guide : STREAMS" manual, from
                      4:  * Chapter 4. This functions as a simple test for compatibility.
                      5:  *
                      6:  * Some minor editing has proved necessary due to this driver's use of header
                      7:  * files not documented in the DDI/DKI. The following files were either
                      8:  * #included by the original source, but are not present in this system, or
                      9:  * were never used in the example. Additional imports not present in the
                     10:  * original code are documented below.
                     11:  *
                     12:  * It may well be that the example is correct in importing these headers, and
                     13:  * the "synopsis" lines in the DDI/DKI documentation are incorrect.
                     14:  */
                     15: 
                     16: #define        _DDI_DKI        1
                     17: #define        _SYSV4          1
                     18: 
                     19: /*
                     20:  *-IMPORTS:
                     21:  *     <common/ccompat.h>
                     22:  *             __USE_PROTO__
                     23:  *             __ARGS ()
                     24:  *             __PROTO ()
                     25:  *     <sys/kmem.h>
                     26:  *             KM_NOSLEEP
                     27:  *     <sys/ksynch.h>
                     28:  *             lkinfo_t
                     29:  *             lock_t
                     30:  *             LOCK_ALLOC ()
                     31:  *             LOCK ()
                     32:  *             UNLOCK ()
                     33:  *     <sys/cmn_err.h>
                     34:  *             CE_PANIC
                     35:  *             CE_WARN
                     36:  *             cmn_err ()
                     37:  *     <sys/errno.h>
                     38:  *             ENXIO
                     39:  *             EBUSY
                     40:  *             EINVAL
                     41:  *     <stddef.h>
                     42:  *             NULL
                     43:  */
                     44: 
                     45: #include <common/ccompat.h>
                     46: #include <sys/kmem.h>
                     47: #include <sys/ksynch.h>
                     48: #include <sys/cmn_err.h>
                     49: #include <sys/errno.h>
                     50: #include <stddef.h>
                     51: 
                     52: /* #include "sys/param.h" */
                     53: /* #include "sys/sysmacros.h" */
                     54: /* #include "sys/dir.h" */
                     55: /* #include "sys/signal.h" */
                     56: /* #include "sys/user.h" */
                     57: /* #include "sys/cred.h" */
                     58: 
                     59: #include "sys/types.h"
                     60: #include "sys/stream.h"
                     61: #include "sys/stropts.h"
                     62: #include "sys/ddi.h"
                     63: 
                     64: 
                     65: static struct module_info minfo = {
                     66:        0xEE12, "loop", 0, INFPSZ, 512, 128
                     67: };
                     68: 
                     69: 
                     70: /*
                     71:  * The original example code contained declarations for the loop... ()
                     72:  * routines that did not even declare them as functions, but as integers. This
                     73:  * has been corrected. The original code would not have compiled with an ISO
                     74:  * C compiler.
                     75:  */
                     76: 
                     77: static int     loopopen        __PROTO ((queue_t * q, dev_t * devp, int flag,
                     78:                                          int sflag, cred_t * credp));
                     79: static int     loopclose       __PROTO ((queue_t * q, int flag,
                     80:                                          cred_t * credp));
                     81: static void    loopwput        __PROTO ((queue_t * q, mblk_t * mp));
                     82: static void    loopwsrv        __PROTO ((queue_t * q));
                     83: static void    looprsrv        __PROTO ((queue_t * q));
                     84: 
                     85: static struct qinit rinit = {
                     86:        NULL, looprsrv, loopopen, loopclose, NULL, & minfo, NULL
                     87: };
                     88: 
                     89: static struct qinit winit = {
                     90:        loopwput, loopwsrv, NULL, NULL, NULL, & minfo, NULL
                     91: };
                     92: 
                     93: struct streamtab loopinfo = { & rinit, & winit, NULL, NULL };
                     94: 
                     95: 
                     96: /*
                     97:  * Import definitions from the "Space.c" file.
                     98:  */
                     99: 
                    100: #include "loop.h"
                    101: 
                    102: int    loopdevflag = 0;
                    103: 
                    104: 
                    105: /*
                    106:  * The example code did not declare this, ever.
                    107:  */
                    108: 
                    109: static lkinfo_t        loop_lkinfo = {
                    110:        "Loopback driver lock"
                    111: };
                    112: 
                    113: 
                    114: /*
                    115:  * Extensions to the original code; as dicussed, there are limitations in the
                    116:  * multiprocessor code in the original example. We have extracted the code
                    117:  * that deals with the lock hierarchy into separate functions to make it
                    118:  * simpler to experiment with different multiprocessor lock strategies.
                    119:  */
                    120: 
                    121: #define        LOOP_HIER(idx)          (hier + 1)
                    122: 
                    123: 
                    124: /*
                    125:  * Lock a pair of loop entries.
                    126:  */
                    127: 
                    128: #if    __USE_PROTO__
                    129: static pl_t (LOOP_LOCK) (struct loop * loop, struct loop * other)
                    130: #else
                    131: static pl_t
                    132: LOOP_LOCK __ARGS ((loop, other))
                    133: struct loop   *        loop;
                    134: struct loop   *        other;
                    135: #endif
                    136: {
                    137:        pl_t            prev_pl;
                    138: 
                    139:        /*
                    140:         * Since the entries are part of a single array object, we are
                    141:         * guaranteed that the relational comparison of the pointers will
                    142:         * yield valid results.
                    143:         *
                    144:         * The comparison ensures that we lock the entries in hierarchy order;
                    145:         * the canonical ordering is needed to avoid deadlock.
                    146:         */
                    147: 
                    148:        if (loop < other) {
                    149: 
                    150:                prev_pl = LOCK (loop->lck, plstr);
                    151:                (void) LOCK (other->lck, plstr);
                    152:        } else {
                    153: 
                    154:                prev_pl = LOCK (other->lck, plstr);
                    155:                (void) LOCK (loop->lck, plstr);
                    156:        }
                    157: 
                    158:         return prev_pl;
                    159: }
                    160: 
                    161: 
                    162: /*
                    163:  * Unlock a pair of loop entries.
                    164:  */
                    165: 
                    166: #if    __USE_PROTO__
                    167: static void (LOOP_UNLOCK) (struct loop * loop, struct loop * other,
                    168:                           pl_t prev_pl)
                    169: #else
                    170: static void
                    171: LOOP_UNLOCK __ARGS ((loop, other, prev_pl))
                    172: struct loop   *        loop;
                    173: struct loop   *        other;
                    174: pl_t           prev_pl;
                    175: #endif
                    176: {
                    177:        /*
                    178:         * The original example always unlocked the minor devices in reverse
                    179:         * order to the lock order. This isn't necessary; whether it's a good
                    180:         * idea or not cannot be determined without forbidden knowledge of the
                    181:         * implementation of the lock primitives, or rigorous profiling.
                    182:         */
                    183: 
                    184:        UNLOCK (loop->lck, plstr);
                    185:        UNLOCK (other->lck, prev_pl);
                    186: }
                    187: 
                    188: 
                    189: /*
                    190:  * Init routine for the driver, called at boot time before system services are
                    191:  * initialized. This function may not call any DDI/DKI routines other than
                    192:  * those listed on the init(2D2K) manual page, because only those services
                    193:  * have been initialized.
                    194:  */
                    195: 
                    196: __EXTERN_C__   
                    197: #if    __USE_PROTO__
                    198: void loopinit (void)
                    199: #else
                    200: void
                    201: loopinit ()
                    202: #endif
                    203: {
                    204:        int             hier;
                    205: 
                    206:        /*
                    207:         * Allocate a multiprocessor lock for each minor device.
                    208:         *
                    209:         * An arbitrary hierarchy is defined based on position in the
                    210:         * "loop_loop" table, such that to lock two entries at the same time,
                    211:         * always lock "loop_loop [n].lck" before "loop_loop [n+m].lck".
                    212:         */
                    213: 
                    214:        /*
                    215:         * This version of the loopback driver will keep the above scheme from
                    216:         * the original code, but there are some important restrictions.
                    217:         *
                    218:         * This scheme restricts the number of minor devices to the range of
                    219:         * valid hierarchy values that LOCK_ALLOC () will accept, which is a
                    220:         * total of 32 entries. This restriction can be lifted by using a
                    221:         * single hierarchy level and using TRYLOCK () when trying to acquire
                    222:         * multiple locks, or simply using a single global lock.
                    223:         *
                    224:         * Note that the original code allocated the locks without testing the
                    225:         * return value from LOCK_ALLOC (). Furthermore, the code passed a
                    226:         * flag value of KM_SLEEP, which is not legal for an init routine,
                    227:         * according to the init(D2DK) manual page.
                    228:         */
                    229: 
                    230:        if (loop_cnt > 32) {
                    231: 
                    232:                cmn_err (CE_WARN, "Only 32 loopback entries can be provided");
                    233:                loop_cnt = 32;
                    234:        }
                    235: 
                    236:        for (hier = 0 ; hier < loop_cnt ; hier ++) {
                    237: 
                    238:                if ((loop_loop [hier].lck =
                    239:                                LOCK_ALLOC (hier + 1, plstr, & loop_lkinfo,
                    240:                                            KM_NOSLEEP)) ==  NULL)
                    241:                        cmn_err (CE_PANIC, "Could not allocate lock in "
                    242:                                           "loopinit ()");
                    243:        }
                    244: }
                    245: 
                    246: 
                    247: #if    __USE_PROTO__
                    248: int loopopen (queue_t * q, dev_t * devp, int __NOTUSED (flag), int sflag,
                    249:              cred_t * __NOTUSED (credp))
                    250: #else
                    251: int
                    252: loopopen (q, devp, flag, sflag, credp)
                    253: queue_t              * q;
                    254: dev_t        * devp;
                    255: int            flag;
                    256: int            sflag;
                    257: cred_t       * credp;
                    258: #endif
                    259: {
                    260:        struct loop   * loop;
                    261:        dev_t           newminor;
                    262: 
                    263:        /*
                    264:         * If CLONEOPEN, pick a minor device number to use. Otherwise, check
                    265:         * the minor device range.
                    266:         */
                    267: 
                    268:        if (sflag == CLONEOPEN) {
                    269: 
                    270:                for (newminor = 0 ; newminor < loop_cnt ; newminor ++) {
                    271: 
                    272:                        if (loop_loop [newminor].qptr == NULL)
                    273:                                break;
                    274:                }
                    275:        } else
                    276:                newminor = geteminor (* devp);
                    277: 
                    278:        if (newminor >= loop_cnt)
                    279:                return ENXIO;
                    280: 
                    281:        /*
                    282:         * Construct new device number if this is the first open.
                    283:         */
                    284: 
                    285:        if (q->q_ptr != NULL)
                    286:                return 0;
                    287: 
                    288:        * devp = makedevice (getemajor (* devp), newminor);
                    289: 
                    290:        loop = & loop_loop [newminor];
                    291: 
                    292:        q->q_ptr = WR (q)->q_ptr = (char *) loop;
                    293:        loop->qptr = WR (q);
                    294:        loop->oqptr = NULL;
                    295: 
                    296: 
                    297:        /*
                    298:         * Enable put and service routines for this queue pair.
                    299:         */
                    300: 
                    301:        qprocson (q);
                    302:        return 0;
                    303: }
                    304: 
                    305: 
                    306: #if    __USE_PROTO__
                    307: void loopwput (queue_t * q, mblk_t * mp)
                    308: #else
                    309: void
                    310: loopwput (q, mp)
                    311: queue_t              * q;
                    312: mblk_t       * mp;
                    313: #endif
                    314: {
                    315:        struct loop   * loop;
                    316:        pl_t            prev_pl;
                    317:        int             error;
                    318: 
                    319:        loop = (struct loop *) q->q_ptr;
                    320: 
                    321:        switch (mp->b_datap->db_type) {
                    322: 
                    323:        case M_IOCTL: {
                    324:                struct iocblk * iocp;
                    325: 
                    326:                iocp = (struct iocblk *) mp->b_rptr;
                    327: 
                    328:                switch (iocp->ioc_cmd) {
                    329: 
                    330:                case LOOP_SET: {
                    331:                        int             to;     /* other minor device */
                    332:                        struct loop   * other;
                    333: 
                    334:                        /*
                    335:                         * Sanity check: "ioc_count" contains the amount of
                    336:                         * user-supplied data, and must equal the size of an
                    337:                         * int.
                    338:                         */
                    339: 
                    340:                        if (iocp->ioc_count != sizeof (int)) {
                    341: 
                    342:                                error = EINVAL;
                    343:                                goto iocnak;
                    344:                        }
                    345: 
                    346: 
                    347:                        /*
                    348:                         * Fetch other device from 2nd message block.
                    349:                         */
                    350: 
                    351:                        to = * (int *) mp->b_cont->b_rptr;
                    352: 
                    353: 
                    354:                        /*
                    355:                         * More sanity checks: the minor must be in range,
                    356:                         * open already, and both devices must be
                    357:                         * disconnected.
                    358:                         *
                    359:                         * Note that the original code contained a serious
                    360:                         * flaw in the test logic where the lock on the other
                    361:                         * minor device was taken out before the range check.
                    362:                         * In addition, the lock was
                    363:                         *      oldpri = LOCK (loop_loop [to].qptr, plstr);
                    364:                         * but it isn't legal to try locking a "queue_t *".
                    365:                         *
                    366:                         * The original code allowed a connect attempt to the
                    367:                         * same minor as the "loop" entry, which will cause
                    368:                         * deadlock.
                    369:                         */
                    370: 
                    371:                        if (to >= loop_cnt || to < 0 ||
                    372:                            (other = & loop_loop [to]) == loop) {
                    373: 
                    374:                                error = ENXIO;
                    375:                                goto iocnak;
                    376:                        }
                    377: 
                    378:                        prev_pl = LOOP_LOCK (loop, other);
                    379: 
                    380:                        if (other->qptr == NULL || loop->oqptr != NULL ||
                    381:                            other->oqptr != NULL) {
                    382: 
                    383:                                LOOP_UNLOCK (loop, other, prev_pl);
                    384:                                error = EBUSY;
                    385:                                goto iocnak;
                    386:                        }
                    387: 
                    388: 
                    389:                        /*
                    390:                         * Cross-connect streams via the loop structures.
                    391:                         */
                    392: 
                    393:                        loop->oqptr = RD (other->qptr);
                    394:                        other->oqptr = RD (q);
                    395: 
                    396:                        LOOP_UNLOCK (loop, other, prev_pl);
                    397: 
                    398: 
                    399:                        /*
                    400:                         * Return a successful ioctl (). Set "ioc_count" to
                    401:                         * zero, since no data is returned.
                    402:                         */
                    403: 
                    404:                        mp->b_datap->db_type = M_IOCACK;
                    405:                        iocp->ioc_count = 0;
                    406:                        break;
                    407:                    }
                    408: 
                    409:                default:
                    410:                        error = EINVAL;
                    411: iocnak:
                    412:                        /*
                    413:                         * Invalid ioctl (). Setting "ioc_error" causes the
                    414:                         * ioctl () call to return that particular errno. By
                    415:                         * default, ioctl () will return EINVAL on failure.
                    416:                         */
                    417: 
                    418:                        mp->b_datap->db_type = M_IOCNAK;
                    419:                        iocp->ioc_error = error;
                    420:                }
                    421: 
                    422:                qreply (q, mp);
                    423:                break;
                    424:            }
                    425: 
                    426:        case M_FLUSH: {
                    427:                queue_t       * tempqptr;
                    428: 
                    429:                prev_pl = LOCK (loop->lck, plstr);
                    430: 
                    431:                /*
                    432:                 * The original example code flushed the read queues of this
                    433:                 * minor and any connected minor, despite the fact that there
                    434:                 * are never any messages queued there. We'll skip that part.
                    435:                 */
                    436: 
                    437:                if ((* mp->b_rptr & FLUSHW) != 0)
                    438:                        flushq (q, FLUSHALL);
                    439: 
                    440:                if ((* mp->b_rptr & FLUSHR) != 0)
                    441:                        if (loop->oqptr != NULL)
                    442:                                flushq (WR (loop->oqptr), FLUSHALL);
                    443: 
                    444:                /*
                    445:                 * Now change around the flush message in a similar fashion to
                    446:                 * the midpoint of a STREAMS FIFO. The logic in the original
                    447:                 * example was broken in that it did not cope correctly with
                    448:                 * the situation where the device was not connected, it did
                    449:                 * not have a declaration for "tempqptr", and it just fell
                    450:                 * through into the data message case.
                    451:                 */
                    452: 
                    453:                if ((tempqptr = loop->oqptr) != NULL) {
                    454: 
                    455:                        switch (* mp->b_rptr & ~ FLUSHBAND) {
                    456: 
                    457:                        case FLUSHW:
                    458:                        case FLUSHR:
                    459:                                * mp->b_rptr ^= FLUSHR | FLUSHW;
                    460:                                break;
                    461: 
                    462:                        default:
                    463:                                break;
                    464:                        }
                    465:                } else {
                    466:                        /*
                    467:                         * Canonical driver flush processing. We use a NULL
                    468:                         * value in "tempqptr" to flag that the message should
                    469:                         * be discarded.
                    470:                         */
                    471: 
                    472:                        * mp->b_rptr &= ~ FLUSHW;
                    473: 
                    474:                        tempqptr = (* mp->b_rptr & FLUSHR) != 0 ? RD (q) :
                    475:                                                NULL;
                    476:                }
                    477: 
                    478:                UNLOCK (loop->lck, prev_pl);
                    479: 
                    480:                if (tempqptr != NULL)
                    481:                        putnext (tempqptr, mp);
                    482:                else
                    483:                        freemsg (mp);
                    484: 
                    485:                /*
                    486:                 * The original example fell through into the data-message
                    487:                 * code, which is clearly incorrect.
                    488:                 */
                    489: 
                    490:                break;
                    491:            }
                    492: 
                    493:        case M_DATA:
                    494:        case M_PROTO:
                    495:        case M_PCPROTO:
                    496:                /*
                    497:                 * If this stream is not connected, register an error with an
                    498:                 * M_ERROR message. Otherwise, queue the message for
                    499:                 * forwarding by the write service procedure.
                    500:                 */
                    501: 
                    502:                prev_pl = LOCK (loop->lck, plstr);
                    503: 
                    504:                error = loop->oqptr == NULL ? ENXIO : 0;
                    505: 
                    506:                UNLOCK (loop->lck, prev_pl);
                    507: 
                    508:                if (error != 0) {
                    509: 
                    510:                        freemsg (mp);
                    511:                        putnextctl1 (RD (q), M_ERROR, error);
                    512:                } else
                    513:                        putq (q, mp);
                    514:                break;
                    515: 
                    516:        default:
                    517:                /*
                    518:                 * Discard unrecognized messages.
                    519:                 */
                    520: 
                    521:                freemsg (mp);
                    522:                break;
                    523:        }
                    524: }
                    525: 
                    526: 
                    527: #if    __USE_PROTO__
                    528: void loopwsrv (queue_t * q)
                    529: #else
                    530: void
                    531: loopwsrv (q)
                    532: queue_t              * q;
                    533: #endif
                    534: {
                    535:        mblk_t        * mp;
                    536:        struct loop   * loop;
                    537:        queue_t       * tmpqptr;
                    538:        pl_t            prev_pl;
                    539: 
                    540:        loop = (struct loop *) q->q_ptr;
                    541: 
                    542:        while ((mp = getq (q)) != NULL) {
                    543: 
                    544:                prev_pl = LOCK (loop->lck, plstr);
                    545: 
                    546:                tmpqptr = loop->oqptr;
                    547: 
                    548:                UNLOCK (loop->lck, prev_pl);
                    549: 
                    550:                /*
                    551:                 * The original example code implicitly assumed that the loop
                    552:                 * was connected at this point. It also would never pass a
                    553:                 * priority message to the other side, and on flow control or
                    554:                 * a priority message would never unlock the minor device.
                    555:                 *
                    556:                 * Note that the disconnect handling in the original example
                    557:                 * is broken, and this is only a partial fix.
                    558:                 */
                    559: 
                    560:                if (tmpqptr == NULL) {
                    561: 
                    562:                        freemsg (mp);
                    563:                        putnextctl1 (RD (q), M_ERROR, EINVAL);
                    564:                        continue;
                    565:                }
                    566: 
                    567:                if (! pcmsg (mp->b_datap->db_type) &&
                    568:                    ! canputnext (tmpqptr)) {
                    569: 
                    570:                        putbq (q, mp);
                    571:                        break;
                    572:                }
                    573: 
                    574:                putnext (tmpqptr, mp);
                    575:        }
                    576: }
                    577: 
                    578: 
                    579: #if    __USE_PROTO__
                    580: void looprsrv (queue_t * q)
                    581: #else
                    582: void
                    583: looprsrv (q)
                    584: queue_t              * q;
                    585: #endif
                    586: {
                    587:        struct loop   * loop;
                    588:        pl_t            prev_pl;
                    589: 
                    590:        /*
                    591:         * Deal with being back-enabled by flow control by enabling the write
                    592:         * side service procedure to retry sending the messages backed up
                    593:         * there.
                    594:         */
                    595: 
                    596:        loop = (struct loop *) q->q_ptr;
                    597: 
                    598:        prev_pl = LOCK (loop->lck, plstr);
                    599: 
                    600:        if (loop->oqptr != NULL)
                    601:                qenable (WR (loop->oqptr));
                    602: 
                    603:        UNLOCK (loop->lck, prev_pl);
                    604: }
                    605: 
                    606: 
                    607: #if    __USE_PROTO__
                    608: int loopclose (queue_t * q, int __NOTUSED (flag), cred_t * __NOTUSED (credp))
                    609: #else
                    610: int
                    611: loopclose (q, flag, credp)
                    612: queue_t              * q;
                    613: int            flag;
                    614: cred_t       * credp;
                    615: #endif
                    616: {
                    617:        struct loop   * loop;
                    618: 
                    619:        /*
                    620:         * Disable put and service routines for the queue pair. This, plus the
                    621:         * fact that open and close are single-threaded, makes any further
                    622:         * locking of this "loop_loop []" entry unnecessary.
                    623:         */
                    624: 
                    625:        qprocsoff (q);
                    626: 
                    627:        loop = (struct loop *) q->q_ptr;
                    628:        loop->qptr = NULL;
                    629: 
                    630: 
                    631:        /*
                    632:         * If we are connected to another stream, break the linkage and send a
                    633:         * hangup message. The original example didn't bother performing any
                    634:         * locking for the disconnect, which is incorrect, because put and
                    635:         * service routines for the other minor device could still be running.
                    636:         */
                    637: 
                    638:        if (loop->oqptr != NULL) {
                    639:                struct loop   * other = (struct loop *) loop->oqptr->q_ptr;
                    640:                pl_t            prev_pl;
                    641: 
                    642:                prev_pl = LOCK (other->lck, plstr);
                    643: 
                    644:                if (other->oqptr == WR (q)) {
                    645: 
                    646:                        putnextctl (loop->oqptr, M_HANGUP);
                    647:                        other->oqptr = loop->oqptr = NULL;
                    648:                }
                    649: 
                    650:                UNLOCK (other->lck, prev_pl);
                    651:        }
                    652: 
                    653:         return 0;
                    654: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.