|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.