1 /* 2 * (MPSAFE) 3 * 4 * Copyright (c) 1995 Ugen J.S.Antsilevich 5 * 6 * Redistribution and use in source forms, with and without modification, 7 * are permitted provided that this entire comment appears intact. 8 * 9 * Redistribution in binary form may occur without any restrictions. 10 * Obviously, it would be nice if you gave credit where credit is due 11 * but requiring it would be too onerous. 12 * 13 * This software is provided ``AS IS'' without any warranties of any kind. 14 * 15 * Snoop stuff. 16 * 17 * $FreeBSD: src/sys/dev/snp/snp.c,v 1.69.2.2 2002/05/06 07:30:02 dd Exp $ 18 */ 19 20 #include "use_snp.h" 21 #include <sys/param.h> 22 #include <sys/systm.h> 23 #include <sys/filio.h> 24 #include <sys/malloc.h> 25 #include <sys/tty.h> 26 #include <sys/conf.h> 27 #include <sys/event.h> 28 #include <sys/kernel.h> 29 #include <sys/queue.h> 30 #include <sys/snoop.h> 31 #include <sys/thread2.h> 32 #include <sys/vnode.h> 33 #include <sys/device.h> 34 #include <sys/devfs.h> 35 36 static l_close_t snplclose; 37 static l_write_t snplwrite; 38 static d_open_t snpopen; 39 static d_close_t snpclose; 40 static d_read_t snpread; 41 static d_write_t snpwrite; 42 static d_ioctl_t snpioctl; 43 static d_kqfilter_t snpkqfilter; 44 static d_clone_t snpclone; 45 DEVFS_DEFINE_CLONE_BITMAP(snp); 46 47 static void snpfilter_detach(struct knote *); 48 static int snpfilter_rd(struct knote *, long); 49 static int snpfilter_wr(struct knote *, long); 50 51 #if NSNP <= 1 52 #define SNP_PREALLOCATED_UNITS 4 53 #else 54 #define SNP_PREALLOCATED_UNITS NSNP 55 #endif 56 57 static struct dev_ops snp_ops = { 58 { "snp", 0, 0 }, 59 .d_open = snpopen, 60 .d_close = snpclose, 61 .d_read = snpread, 62 .d_write = snpwrite, 63 .d_ioctl = snpioctl, 64 .d_kqfilter = snpkqfilter 65 }; 66 67 static struct linesw snpdisc = { 68 ttyopen, snplclose, ttread, snplwrite, 69 l_nullioctl, ttyinput, ttstart, ttymodem 70 }; 71 72 /* 73 * This is the main snoop per-device structure. 74 */ 75 struct snoop { 76 LIST_ENTRY(snoop) snp_list; /* List glue. */ 77 cdev_t snp_target; /* Target tty device. */ 78 struct tty *snp_tty; /* Target tty pointer. */ 79 u_long snp_len; /* Possible length. */ 80 u_long snp_base; /* Data base. */ 81 u_long snp_blen; /* Used length. */ 82 caddr_t snp_buf; /* Allocation pointer. */ 83 int snp_flags; /* Flags. */ 84 struct kqinfo snp_kq; /* Kqueue info. */ 85 int snp_olddisc; /* Old line discipline. */ 86 }; 87 88 /* 89 * Possible flags. 90 */ 91 #define SNOOP_ASYNC 0x0002 92 #define SNOOP_OPEN 0x0004 93 #define SNOOP_RWAIT 0x0008 94 #define SNOOP_OFLOW 0x0010 95 #define SNOOP_DOWN 0x0020 96 97 /* 98 * Other constants. 99 */ 100 #define SNOOP_MINLEN (4*1024) /* This should be power of 2. 101 * 4K tested to be the minimum 102 * for which on normal tty 103 * usage there is no need to 104 * allocate more. 105 */ 106 #define SNOOP_MAXLEN (64*1024) /* This one also,64K enough 107 * If we grow more,something 108 * really bad in this world.. 109 */ 110 111 static MALLOC_DEFINE(M_SNP, "snp", "Snoop device data"); 112 /* 113 * The number of the "snoop" line discipline. This gets determined at 114 * module load time. 115 */ 116 static int snooplinedisc; 117 118 119 static LIST_HEAD(, snoop) snp_sclist = LIST_HEAD_INITIALIZER(&snp_sclist); 120 static struct lwkt_token snp_token = LWKT_TOKEN_INITIALIZER(snp_token); 121 122 static struct tty *snpdevtotty (cdev_t dev); 123 static int snp_detach (struct snoop *snp); 124 static int snp_down (struct snoop *snp); 125 static int snp_in (struct snoop *snp, char *buf, int n); 126 static int snp_modevent (module_t mod, int what, void *arg); 127 128 static int 129 snplclose(struct tty *tp, int flag) 130 { 131 struct snoop *snp; 132 int error; 133 134 lwkt_gettoken(&snp_token); 135 snp = tp->t_sc; 136 error = snp_down(snp); 137 if (error != 0) { 138 lwkt_reltoken(&snp_token); 139 return (error); 140 } 141 lwkt_gettoken(&tp->t_token); 142 error = ttylclose(tp, flag); 143 lwkt_reltoken(&tp->t_token); 144 lwkt_reltoken(&snp_token); 145 146 return (error); 147 } 148 149 static int 150 snplwrite(struct tty *tp, struct uio *uio, int flag) 151 { 152 struct iovec iov; 153 struct uio uio2; 154 struct snoop *snp; 155 int error, ilen; 156 char *ibuf; 157 158 lwkt_gettoken(&tp->t_token); 159 error = 0; 160 ibuf = NULL; 161 snp = tp->t_sc; 162 while (uio->uio_resid > 0) { 163 ilen = (int)szmin(512, uio->uio_resid); 164 ibuf = kmalloc(ilen, M_SNP, M_WAITOK); 165 error = uiomove(ibuf, (size_t)ilen, uio); 166 if (error != 0) 167 break; 168 snp_in(snp, ibuf, ilen); 169 /* Hackish, but probably the least of all evils. */ 170 iov.iov_base = ibuf; 171 iov.iov_len = ilen; 172 uio2.uio_iov = &iov; 173 uio2.uio_iovcnt = 1; 174 uio2.uio_offset = 0; 175 uio2.uio_resid = ilen; 176 uio2.uio_segflg = UIO_SYSSPACE; 177 uio2.uio_rw = UIO_WRITE; 178 uio2.uio_td = uio->uio_td; 179 error = ttwrite(tp, &uio2, flag); 180 if (error != 0) 181 break; 182 kfree(ibuf, M_SNP); 183 ibuf = NULL; 184 } 185 if (ibuf != NULL) 186 kfree(ibuf, M_SNP); 187 lwkt_reltoken(&tp->t_token); 188 return (error); 189 } 190 191 static struct tty * 192 snpdevtotty(cdev_t dev) 193 { 194 if ((dev_dflags(dev) & D_TTY) == 0) 195 return (NULL); 196 return (dev->si_tty); 197 } 198 199 #define SNP_INPUT_BUF 5 /* This is even too much, the maximal 200 * interactive mode write is 3 bytes 201 * length for function keys... 202 */ 203 204 static int 205 snpwrite(struct dev_write_args *ap) 206 { 207 cdev_t dev = ap->a_head.a_dev; 208 struct uio *uio = ap->a_uio; 209 struct snoop *snp; 210 struct tty *tp; 211 int error, i, len; 212 unsigned char c[SNP_INPUT_BUF]; 213 214 snp = dev->si_drv1; 215 tp = snp->snp_tty; 216 if (tp == NULL) 217 return (EIO); 218 lwkt_gettoken(&tp->t_token); 219 if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) && 220 tp->t_line == snooplinedisc) 221 goto tty_input; 222 223 kprintf("Snoop: attempt to write to bad tty.\n"); 224 lwkt_reltoken(&tp->t_token); 225 return (EIO); 226 227 tty_input: 228 if (!(tp->t_state & TS_ISOPEN)) { 229 lwkt_reltoken(&tp->t_token); 230 return (EIO); 231 } 232 233 while (uio->uio_resid > 0) { 234 len = (int)szmin(uio->uio_resid, SNP_INPUT_BUF); 235 if ((error = uiomove(c, (size_t)len, uio)) != 0) { 236 lwkt_reltoken(&tp->t_token); 237 return (error); 238 } 239 for (i=0; i < len; i++) { 240 if (ttyinput(c[i], tp)) { 241 lwkt_reltoken(&tp->t_token); 242 return (EIO); 243 } 244 } 245 } 246 lwkt_reltoken(&tp->t_token); 247 return (0); 248 } 249 250 251 static int 252 snpread(struct dev_read_args *ap) 253 { 254 cdev_t dev = ap->a_head.a_dev; 255 struct uio *uio = ap->a_uio; 256 struct snoop *snp; 257 struct tty *tp; 258 int error, len, n, nblen; 259 caddr_t from; 260 char *nbuf; 261 262 snp = dev->si_drv1; 263 tp = snp->snp_tty; 264 lwkt_gettoken(&tp->t_token); 265 KASSERT(snp->snp_len + snp->snp_base <= snp->snp_blen, 266 ("snoop buffer error")); 267 268 if (snp->snp_tty == NULL) { 269 lwkt_reltoken(&tp->t_token); 270 return (EIO); 271 } 272 273 snp->snp_flags &= ~SNOOP_RWAIT; 274 275 do { 276 if (snp->snp_len == 0) { 277 if (ap->a_ioflag & IO_NDELAY) { 278 lwkt_reltoken(&tp->t_token); 279 return (EWOULDBLOCK); 280 } 281 snp->snp_flags |= SNOOP_RWAIT; 282 error = tsleep((caddr_t)snp, PCATCH, "snprd", 0); 283 if (error != 0) { 284 lwkt_reltoken(&tp->t_token); 285 return (error); 286 } 287 } 288 } while (snp->snp_len == 0); 289 290 n = snp->snp_len; 291 292 error = 0; 293 while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) { 294 len = (int)szmin(uio->uio_resid, snp->snp_len); 295 from = (caddr_t)(snp->snp_buf + snp->snp_base); 296 if (len == 0) 297 break; 298 299 error = uiomove(from, (size_t)len, uio); 300 snp->snp_base += len; 301 snp->snp_len -= len; 302 } 303 if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) { 304 snp->snp_flags &= ~SNOOP_OFLOW; 305 } 306 nblen = snp->snp_blen; 307 if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) { 308 while (nblen / 2 >= snp->snp_len && nblen / 2 >= SNOOP_MINLEN) 309 nblen = nblen / 2; 310 if ((nbuf = kmalloc(nblen, M_SNP, M_NOWAIT)) != NULL) { 311 bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 312 kfree(snp->snp_buf, M_SNP); 313 snp->snp_buf = nbuf; 314 snp->snp_blen = nblen; 315 snp->snp_base = 0; 316 } 317 } 318 lwkt_reltoken(&tp->t_token); 319 320 return (error); 321 } 322 323 /* 324 * NOTE: Must be called with tp->t_token held 325 */ 326 static int 327 snp_in(struct snoop *snp, char *buf, int n) 328 { 329 int s_free, s_tail; 330 int len, nblen; 331 caddr_t from, to; 332 char *nbuf; 333 334 KASSERT(n >= 0, ("negative snoop char count")); 335 336 if (n == 0) 337 return (0); 338 339 if (snp->snp_flags & SNOOP_DOWN) { 340 kprintf("Snoop: more data to down interface.\n"); 341 return (0); 342 } 343 344 if (snp->snp_flags & SNOOP_OFLOW) { 345 kprintf("Snoop: buffer overflow.\n"); 346 /* 347 * On overflow we just repeat the standart close 348 * procedure...yes , this is waste of space but.. Then next 349 * read from device will fail if one would recall he is 350 * snooping and retry... 351 */ 352 353 return (snp_down(snp)); 354 } 355 s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base); 356 s_free = snp->snp_blen - snp->snp_len; 357 358 359 if (n > s_free) { 360 nblen = snp->snp_blen; 361 while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) { 362 nblen = snp->snp_blen * 2; 363 s_free = nblen - (snp->snp_len + snp->snp_base); 364 } 365 if ((n <= s_free) && (nbuf = kmalloc(nblen, M_SNP, M_NOWAIT))) { 366 bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len); 367 kfree(snp->snp_buf, M_SNP); 368 snp->snp_buf = nbuf; 369 snp->snp_blen = nblen; 370 snp->snp_base = 0; 371 } else { 372 snp->snp_flags |= SNOOP_OFLOW; 373 if (snp->snp_flags & SNOOP_RWAIT) { 374 snp->snp_flags &= ~SNOOP_RWAIT; 375 wakeup((caddr_t)snp); 376 } 377 return (0); 378 } 379 } 380 if (n > s_tail) { 381 from = (caddr_t)(snp->snp_buf + snp->snp_base); 382 to = (caddr_t)(snp->snp_buf); 383 len = snp->snp_len; 384 bcopy(from, to, len); 385 snp->snp_base = 0; 386 } 387 to = (caddr_t)(snp->snp_buf + snp->snp_base + snp->snp_len); 388 bcopy(buf, to, n); 389 snp->snp_len += n; 390 391 if (snp->snp_flags & SNOOP_RWAIT) { 392 snp->snp_flags &= ~SNOOP_RWAIT; 393 wakeup((caddr_t)snp); 394 } 395 KNOTE(&snp->snp_kq.ki_note, 0); 396 397 return (n); 398 } 399 400 static int 401 snpopen(struct dev_open_args *ap) 402 { 403 cdev_t dev = ap->a_head.a_dev; 404 struct snoop *snp; 405 406 lwkt_gettoken(&snp_token); 407 if (dev->si_drv1 == NULL) { 408 #if 0 409 make_dev(&snp_ops, minor(dev), UID_ROOT, GID_WHEEL, 410 0600, "snp%d", minor(dev)); 411 #endif 412 dev->si_drv1 = snp = kmalloc(sizeof(*snp), M_SNP, 413 M_WAITOK | M_ZERO); 414 } else { 415 lwkt_reltoken(&snp_token); 416 return (EBUSY); 417 } 418 419 /* 420 * We intentionally do not OR flags with SNOOP_OPEN, but set them so 421 * all previous settings (especially SNOOP_OFLOW) will be cleared. 422 */ 423 snp->snp_flags = SNOOP_OPEN; 424 425 snp->snp_buf = kmalloc(SNOOP_MINLEN, M_SNP, M_WAITOK); 426 snp->snp_blen = SNOOP_MINLEN; 427 snp->snp_base = 0; 428 snp->snp_len = 0; 429 430 /* 431 * snp_tty == NULL is for inactive snoop devices. 432 */ 433 snp->snp_tty = NULL; 434 snp->snp_target = NULL; 435 436 LIST_INSERT_HEAD(&snp_sclist, snp, snp_list); 437 lwkt_reltoken(&snp_token); 438 return (0); 439 } 440 441 /* 442 * NOTE: Must be called with snp_token held 443 */ 444 static int 445 snp_detach(struct snoop *snp) 446 { 447 struct tty *tp; 448 449 ASSERT_LWKT_TOKEN_HELD(&snp_token); 450 snp->snp_base = 0; 451 snp->snp_len = 0; 452 453 /* 454 * If line disc. changed we do not touch this pointer, SLIP/PPP will 455 * change it anyway. 456 */ 457 tp = snp->snp_tty; 458 if (tp == NULL) 459 goto detach_notty; 460 461 lwkt_gettoken(&tp->t_token); 462 if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) && 463 tp->t_line == snooplinedisc) { 464 tp->t_sc = NULL; 465 tp->t_state &= ~TS_SNOOP; 466 tp->t_line = snp->snp_olddisc; 467 } else { 468 kprintf("Snoop: bad attached tty data.\n"); 469 } 470 lwkt_reltoken(&tp->t_token); 471 472 snp->snp_tty = NULL; 473 snp->snp_target = NULL; 474 475 detach_notty: 476 KNOTE(&snp->snp_kq.ki_note, 0); 477 if ((snp->snp_flags & SNOOP_OPEN) == 0) 478 kfree(snp, M_SNP); 479 480 return (0); 481 } 482 483 static int 484 snpclose(struct dev_close_args *ap) 485 { 486 cdev_t dev = ap->a_head.a_dev; 487 struct snoop *snp; 488 int unit; 489 int ret; 490 491 lwkt_gettoken(&snp_token); 492 snp = dev->si_drv1; 493 snp->snp_blen = 0; 494 LIST_REMOVE(snp, snp_list); 495 kfree(snp->snp_buf, M_SNP); 496 snp->snp_flags &= ~SNOOP_OPEN; 497 dev->si_drv1 = NULL; 498 unit = dev->si_uminor; 499 if (unit >= SNP_PREALLOCATED_UNITS) { 500 destroy_dev(dev); 501 devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(snp), unit); 502 } 503 ret = snp_detach(snp); 504 lwkt_reltoken(&snp_token); 505 506 return ret; 507 } 508 509 /* 510 * NOTE: Must be called with snp_token held 511 */ 512 static int 513 snp_down(struct snoop *snp) 514 { 515 ASSERT_LWKT_TOKEN_HELD(&snp_token); 516 if (snp->snp_blen != SNOOP_MINLEN) { 517 kfree(snp->snp_buf, M_SNP); 518 snp->snp_buf = kmalloc(SNOOP_MINLEN, M_SNP, M_WAITOK); 519 snp->snp_blen = SNOOP_MINLEN; 520 } 521 snp->snp_flags |= SNOOP_DOWN; 522 523 return (snp_detach(snp)); 524 } 525 526 static int 527 snpioctl(struct dev_ioctl_args *ap) 528 { 529 cdev_t dev = ap->a_head.a_dev; 530 struct snoop *snp; 531 struct tty *tp, *tpo; 532 cdev_t tdev; 533 int ret; 534 535 lwkt_gettoken(&snp_token); 536 snp = dev->si_drv1; 537 538 switch (ap->a_cmd) { 539 case SNPSTTY: 540 tdev = udev2dev(*((udev_t *)ap->a_data), 0); 541 if (tdev == NULL) { 542 lwkt_reltoken(&snp_token); 543 ret = snp_down(snp); 544 return ret; 545 } 546 547 tp = snpdevtotty(tdev); 548 if (!tp) { 549 lwkt_reltoken(&snp_token); 550 return (EINVAL); 551 } 552 lwkt_gettoken(&tp->t_token); 553 if (tp->t_state & TS_SNOOP) { 554 lwkt_reltoken(&tp->t_token); 555 lwkt_reltoken(&snp_token); 556 return (EBUSY); 557 } 558 559 if (snp->snp_target == NULL) { 560 tpo = snp->snp_tty; 561 if (tpo) 562 tpo->t_state &= ~TS_SNOOP; 563 } 564 565 tp->t_sc = (caddr_t)snp; 566 tp->t_state |= TS_SNOOP; 567 snp->snp_olddisc = tp->t_line; 568 tp->t_line = snooplinedisc; 569 snp->snp_tty = tp; 570 snp->snp_target = tdev; 571 572 /* 573 * Clean overflow and down flags - 574 * we'll have a chance to get them in the future :))) 575 */ 576 snp->snp_flags &= ~SNOOP_OFLOW; 577 snp->snp_flags &= ~SNOOP_DOWN; 578 lwkt_reltoken(&tp->t_token); 579 break; 580 581 case SNPGTTY: 582 /* 583 * We keep snp_target field specially to make 584 * SNPGTTY happy, else we can't know what is device 585 * major/minor for tty. 586 */ 587 *((cdev_t *)ap->a_data) = snp->snp_target; 588 break; 589 590 case FIOASYNC: 591 if (*(int *)ap->a_data) 592 snp->snp_flags |= SNOOP_ASYNC; 593 else 594 snp->snp_flags &= ~SNOOP_ASYNC; 595 break; 596 597 case FIONREAD: 598 if (snp->snp_tty != NULL) { 599 *(int *)ap->a_data = snp->snp_len; 600 } else { 601 if (snp->snp_flags & SNOOP_DOWN) { 602 if (snp->snp_flags & SNOOP_OFLOW) 603 *(int *)ap->a_data = SNP_OFLOW; 604 else 605 *(int *)ap->a_data = SNP_TTYCLOSE; 606 } else { 607 *(int *)ap->a_data = SNP_DETACH; 608 } 609 } 610 break; 611 612 default: 613 lwkt_reltoken(&snp_token); 614 return (ENOTTY); 615 } 616 lwkt_reltoken(&snp_token); 617 618 return (0); 619 } 620 621 static struct filterops snpfiltops_rd = 622 { FILTEROP_ISFD, NULL, snpfilter_detach, snpfilter_rd }; 623 static struct filterops snpfiltops_wr = 624 { FILTEROP_ISFD, NULL, snpfilter_detach, snpfilter_wr }; 625 626 static int 627 snpkqfilter(struct dev_kqfilter_args *ap) 628 { 629 cdev_t dev = ap->a_head.a_dev; 630 struct snoop *snp = dev->si_drv1; 631 struct knote *kn = ap->a_kn; 632 struct klist *klist; 633 struct tty *tp = snp->snp_tty; 634 635 lwkt_gettoken(&tp->t_token); 636 ap->a_result = 0; 637 638 switch (kn->kn_filter) { 639 case EVFILT_READ: 640 kn->kn_fop = &snpfiltops_rd; 641 kn->kn_hook = (caddr_t)snp; 642 break; 643 case EVFILT_WRITE: 644 kn->kn_fop = &snpfiltops_wr; 645 kn->kn_hook = (caddr_t)snp; 646 break; 647 default: 648 ap->a_result = EOPNOTSUPP; 649 lwkt_reltoken(&tp->t_token); 650 return (0); 651 } 652 653 klist = &snp->snp_kq.ki_note; 654 knote_insert(klist, kn); 655 lwkt_reltoken(&tp->t_token); 656 657 return (0); 658 } 659 660 static void 661 snpfilter_detach(struct knote *kn) 662 { 663 struct snoop *snp = (struct snoop *)kn->kn_hook; 664 struct klist *klist; 665 666 klist = &snp->snp_kq.ki_note; 667 knote_remove(klist, kn); 668 } 669 670 static int 671 snpfilter_rd(struct knote *kn, long hint) 672 { 673 struct snoop *snp = (struct snoop *)kn->kn_hook; 674 struct tty *tp = snp->snp_tty; 675 int ready = 0; 676 677 lwkt_gettoken(&tp->t_token); 678 /* 679 * If snoop is down, we don't want to poll forever so we return 1. 680 * Caller should see if we down via FIONREAD ioctl(). The last should 681 * return -1 to indicate down state. 682 */ 683 if (snp->snp_flags & SNOOP_DOWN || snp->snp_len > 0) 684 ready = 1; 685 lwkt_reltoken(&tp->t_token); 686 687 return (ready); 688 } 689 690 static int 691 snpfilter_wr(struct knote *kn, long hint) 692 { 693 /* Writing is always OK */ 694 return (1); 695 } 696 697 static int 698 snpclone(struct dev_clone_args *ap) 699 { 700 int unit; 701 702 lwkt_gettoken(&snp_token); 703 unit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(snp), 0); 704 ap->a_dev = make_only_dev(&snp_ops, unit, UID_ROOT, GID_WHEEL, 0600, 705 "snp%d", unit); 706 lwkt_reltoken(&snp_token); 707 708 return 0; 709 } 710 711 static int 712 snp_modevent(module_t mod, int type, void *data) 713 { 714 int i; 715 716 lwkt_gettoken(&snp_token); 717 718 switch (type) { 719 case MOD_LOAD: 720 snooplinedisc = ldisc_register(LDISC_LOAD, &snpdisc); 721 make_autoclone_dev(&snp_ops, &DEVFS_CLONE_BITMAP(snp), 722 snpclone, UID_ROOT, GID_WHEEL, 0600, "snp"); 723 724 for (i = 0; i < SNP_PREALLOCATED_UNITS; i++) { 725 make_dev(&snp_ops, i, UID_ROOT, GID_WHEEL, 0600, 726 "snp%d", i); 727 devfs_clone_bitmap_set(&DEVFS_CLONE_BITMAP(snp), i); 728 } 729 break; 730 case MOD_UNLOAD: 731 if (!LIST_EMPTY(&snp_sclist)) { 732 lwkt_reltoken(&snp_token); 733 return (EBUSY); 734 } 735 ldisc_deregister(snooplinedisc); 736 devfs_clone_handler_del("snp"); 737 dev_ops_remove_all(&snp_ops); 738 devfs_clone_bitmap_uninit(&DEVFS_CLONE_BITMAP(snp)); 739 break; 740 default: 741 break; 742 } 743 lwkt_reltoken(&snp_token); 744 745 return (0); 746 } 747 748 static moduledata_t snp_mod = { 749 "snp", 750 snp_modevent, 751 NULL 752 }; 753 DECLARE_MODULE(snp, snp_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 754