|
|
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.