1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Redirecting driver; used to handle workstation console redirection. 31 * 32 * Redirects all I/O through a given device instance to the device designated 33 * as the current target, as given by the vnode associated with the first 34 * entry in the list of redirections for the given device instance. The 35 * implementation assumes that this vnode denotes a STREAMS device; this is 36 * perhaps a bug. 37 * 38 * Supports the SRIOCSREDIR ioctl for designating a new redirection target. 39 * The new target is added to the front of a list of potentially active 40 * designees. Should the device at the front of this list be closed, the new 41 * front entry assumes active duty. (Stated differently, redirection targets 42 * stack, except that it's possible for entries in the interior of the stack 43 * to go away.) 44 * 45 * Supports the SRIOCISREDIR ioctl for inquiring whether the descriptor given 46 * as argument is the current front of the redirection list associated with 47 * the descriptor on which the ioctl was issued. 48 * 49 * Every open instance of this driver corresponds to an instance of the 50 * underlying client driver. If the redirection stack would otherwise become 51 * empty, this device (designated by the wd_vp field of the wcd_data 52 * structure) is implicitly opened and added to the front of the list. Thus, 53 * there's always an active device for handling i/o through an open instance 54 * of this driver. 55 * 56 * XXX: Names -- many of the names in this driver and its companion STREAMS 57 * module still reflect its origins as the workstation console 58 * redirection driver. Ultimately, they should be changed to reflect the 59 * fact that this driver is potentially a general purpose redirection 60 * driver. In the meantime, the driver is still specialized to have a 61 * single client -- the workstation console driver -- and its file name 62 * remains iwscons.c to reflect that specialization. 63 * 64 * Proposed change: "iwscn" becomes either "dr" (for "streams redirecting 65 * driver") or "srm" (for "streams redirecting module"), as appropriate. 66 * 67 * XXX: Add mechanism for notifying a redirectee that it's no longer the 68 * current redirectee? (This in contrast to the current facility for 69 * letting it ask.) 70 */ 71 72 #include <sys/types.h> 73 #include <sys/sysmacros.h> 74 #include <sys/open.h> 75 #include <sys/param.h> 76 #include <sys/systm.h> 77 #include <sys/signal.h> 78 #include <sys/cred.h> 79 #include <sys/user.h> 80 #include <sys/proc.h> 81 #include <sys/vnode.h> 82 #include <sys/uio.h> 83 #include <sys/file.h> 84 #include <sys/kmem.h> 85 #include <sys/stat.h> 86 87 #include <sys/stream.h> 88 #include <sys/stropts.h> 89 #include <sys/strsubr.h> 90 #include <sys/poll.h> 91 92 #include <sys/debug.h> 93 94 #include <sys/strredir.h> 95 96 #include <sys/conf.h> 97 #include <sys/ddi.h> 98 #include <sys/sunddi.h> 99 100 static int iwscninfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 101 static int iwscnattach(dev_info_t *, ddi_attach_cmd_t); 102 static int iwscnopen(dev_t *, int, int, cred_t *); 103 static int iwscnclose(dev_t, int, int, cred_t *); 104 static int iwscnread(dev_t, struct uio *, cred_t *); 105 static int iwscnwrite(dev_t, struct uio *, cred_t *); 106 static int iwscnioctl(dev_t, int, intptr_t, int, cred_t *, int *); 107 static int iwscnpoll(dev_t, short, int, short *, struct pollhead **); 108 109 /* 110 * Private copy of devinfo pointer; iwscninfo uses it. 111 */ 112 static dev_info_t *iwscn_dip; 113 114 struct cb_ops iwscn_cb_ops = { 115 iwscnopen, /* open */ 116 iwscnclose, /* close */ 117 nodev, /* strategy */ 118 nodev, /* print */ 119 nodev, /* dump */ 120 iwscnread, /* read */ 121 iwscnwrite, /* write */ 122 iwscnioctl, /* ioctl */ 123 nodev, /* devmap */ 124 nodev, /* mmap */ 125 nodev, /* segmap */ 126 iwscnpoll, /* poll */ 127 ddi_prop_op, /* cb_prop_op */ 128 0, /* streamtab */ 129 D_NEW|D_MP /* Driver compatibility flag */ 130 }; 131 132 struct dev_ops iwscn_ops = { 133 DEVO_REV, /* devo_rev, */ 134 0, /* refcnt */ 135 iwscninfo, /* info */ 136 nulldev, /* identify */ 137 nulldev, /* probe */ 138 iwscnattach, /* attach */ 139 nodev, /* detach */ 140 nodev, /* reset */ 141 &iwscn_cb_ops, /* driver operations */ 142 NULL /* bus operations */ 143 }; 144 145 static krwlock_t iwscn_lock; /* lock proecting almost everything here */ 146 147 /* 148 * A read/write lock was used to serialize reads,writes/opens,closes. 149 * Sometime the open would hang due to a pending read. The new lock 150 * iwscn_open_lock and the read lock are held in open to assure a single 151 * instance, while letting concurrent reads/writes to proceed. 152 */ 153 static kmutex_t iwscn_open_lock; /* Serializes opens */ 154 155 /* 156 * These next two fields, protected by iwscn_lock, pass the data to wcmopen() 157 * from the ioctl SRIOCSREDIR. wcmopen() uses the data only if the thread 158 * matches. This keeps other threads from interfering. 159 */ 160 extern kthread_id_t iwscn_thread; /* thread that is allowed to */ 161 /* push redirm */ 162 extern wcm_data_t *iwscn_wcm_data; /* allocated data for redirm */ 163 164 /* 165 * Forward declarations of private routines. 166 */ 167 static int srreset(wcd_data_t *, int, cred_t *); 168 static wcrlist_t *srrm(wcrlist_t **, vnode_t *, int); 169 static wcd_data_t *srilookup(minor_t); 170 static wcd_data_t *srialloc(minor_t); 171 static void sridealloc(wcd_data_t *); 172 static wcrlist_t *srpush(wcrlist_t **, vnode_t *); 173 174 /* 175 * The head of the list of open instances. 176 */ 177 static wcd_data_t *wcddata; 178 179 /* 180 * Currently, the only client of this driver is the workstation console 181 * driver. Thus, we can get away with hard-wiring a reference to it here. 182 * 183 * To handle multiple clients, the driver must be revised as follows. 184 * 1) Add a registration routine that clients can call to announce 185 * themselves to this driver. The routine should take as arguments the 186 * major device number of the corresponding instantiation of the 187 * redirecting driver and a pointer to its dedvnops ops vector. 188 * 2) Maintain a list (or perhaps hash array) or registered clients, 189 * recording for each the srvnops ops vector and a pointer to the list 190 * of open instances for that client. 191 * 3) Modify the driver entry points to use their dev argument to look up 192 * the proper instantiation, get the list of open instances, and then use 193 * that as they currently use the open instance list. 194 * 4) To allow clients to unload themselves, we probably need an unregister 195 * routine. This routine would have to cope with active open instances. 196 */ 197 extern srvnops_t wscons_srvnops; 198 199 #include <sys/types.h> 200 #include <sys/conf.h> 201 #include <sys/param.h> 202 #include <sys/systm.h> 203 #include <sys/errno.h> 204 #include <sys/modctl.h> 205 206 /* 207 * Module linkage information for the kernel. 208 */ 209 210 static struct modldrv modldrv = { 211 &mod_driverops, /* Type of module. This one is a pseudo driver */ 212 "Workstation Redirection driver 'iwscn' 1.42", 213 &iwscn_ops, /* driver ops */ 214 }; 215 216 static struct modlinkage modlinkage = { 217 MODREV_1, 218 &modldrv, 219 NULL 220 }; 221 222 223 int 224 _init(void) 225 { 226 return (mod_install(&modlinkage)); 227 } 228 229 int 230 _fini(void) 231 { 232 return (EBUSY); 233 } 234 235 int 236 _info(struct modinfo *modinfop) 237 { 238 return (mod_info(&modlinkage, modinfop)); 239 } 240 241 /* 242 * DDI glue routines. 243 */ 244 245 /*ARGSUSED*/ 246 static int 247 iwscnattach(dev_info_t *devi, ddi_attach_cmd_t cmd) 248 { 249 static char been_here; 250 251 if (!been_here) { 252 been_here = 1; 253 rw_init(&iwscn_lock, NULL, RW_DEFAULT, NULL); 254 } 255 if (ddi_create_minor_node(devi, "iwscn", S_IFCHR, 256 0, DDI_PSEUDO, NULL) == DDI_FAILURE) { 257 ddi_remove_minor_node(devi, NULL); 258 return (-1); 259 } 260 iwscn_dip = devi; 261 return (DDI_SUCCESS); 262 } 263 264 /* ARGSUSED */ 265 static int 266 iwscninfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 267 { 268 int error; 269 270 switch (infocmd) { 271 case DDI_INFO_DEVT2DEVINFO: 272 if (iwscn_dip == NULL) { 273 error = DDI_FAILURE; 274 } else { 275 *result = (void *)iwscn_dip; 276 error = DDI_SUCCESS; 277 } 278 break; 279 case DDI_INFO_DEVT2INSTANCE: 280 *result = (void *)0; 281 error = DDI_SUCCESS; 282 break; 283 default: 284 error = DDI_FAILURE; 285 } 286 return (error); 287 } 288 289 290 /* ARGSUSED */ 291 static int 292 iwscnopen( 293 dev_t *devp, 294 int flag, 295 int state, /* should be OTYP_CHR */ 296 cred_t *cred) 297 { 298 minor_t unit = getminor(*devp); 299 wcd_data_t *wd; 300 int err = 0; 301 struct wcrlist *wwd; 302 303 if (state != OTYP_CHR) 304 return (ENXIO); 305 rw_enter(&iwscn_lock, RW_READER); 306 mutex_enter(&iwscn_open_lock); 307 if ((wd = srilookup(unit)) == NULL) { 308 vnode_t *vp; 309 310 /* 311 * First open for this instance; get a state structure for it. 312 */ 313 wd = srialloc(unit); 314 315 /* 316 * Call the client driver to obtain a held vnode for the 317 * underlying "real" device instance. 318 * 319 * XXX: There's wired in knowledge of the client driver here. 320 */ 321 err = wscons_srvnops.svn_get(unit, &vp); 322 if (err != 0) { 323 sridealloc(wd); 324 mutex_exit(&iwscn_open_lock); 325 rw_exit(&iwscn_lock); 326 return (err); 327 } 328 wd->wd_vp = vp; 329 } 330 331 /* 332 * Reinitalize the list if necessary. 333 * 334 * XXX: Is it possible for the list to empty completely while this 335 * instance is still open? If not, this if should be coalesced 336 * with the previous one. 337 */ 338 if (wd->wd_list == NULL) { 339 wcrlist_t *e = srpush(&wd->wd_list, wd->wd_vp); 340 341 /* 342 * There's no corresponding redirecting module instance for 343 * the underlying device. 344 */ 345 e->wl_data = NULL; 346 } 347 348 err = srreset(wd, flag, cred); 349 /* 350 * XXX cleanup the sig list. Hook for console driver. 351 */ 352 for (wwd = wd->wd_list; wwd != NULL; wwd = wwd->wl_next) { 353 ASSERT(wwd->wl_vp->v_stream != NULL); 354 str_cn_clean(wwd->wl_vp); 355 } 356 mutex_exit(&iwscn_open_lock); 357 rw_exit(&iwscn_lock); 358 return (err); 359 } 360 361 /* ARGSUSED */ 362 static int 363 iwscnclose( 364 dev_t dev, 365 int flag, 366 int state, /* should be OTYP_CHR */ 367 cred_t *cred) 368 { 369 wcd_data_t *wd; 370 int err = 0; 371 372 if (state != OTYP_CHR) 373 return (ENXIO); 374 rw_enter(&iwscn_lock, RW_WRITER); 375 wd = srilookup(getminor(dev)); 376 /* 377 * Remove all outstanding redirections for this instance. 378 */ 379 while (wd->wd_list != NULL) 380 (void) srrm(&wd->wd_list, wd->wd_list->wl_vp, 1); 381 382 /* 383 * Since this is the _last_ close, it's our last chance to close the 384 * underlying device. (Note that if someone else has the underlying 385 * workstation console device open, we won't get here, since 386 * spec_close will see s_count > 1.) 387 */ 388 while ((wd->wd_wsconsopen != 0) && (!err)) { 389 err = VOP_CLOSE(wd->wd_vp, flag, 1, (offset_t)0, cred); 390 if (!err) 391 wd->wd_wsconsopen--; 392 } 393 if (!err) 394 wd->wd_vp->v_stream = NULL; 395 396 /* 397 * We don't need the vnode that the client driver gave us any more. 398 * 399 * XXX: There's wired in knowledge of the client driver here. 400 */ 401 wscons_srvnops.svn_rele(wd->wd_unit, wd->wd_vp); 402 sridealloc(wd); 403 404 rw_exit(&iwscn_lock); 405 return (err); 406 } 407 408 static int 409 iwscnread(dev_t dev, uio_t *uio, cred_t *cred) 410 { 411 wcd_data_t *wd; 412 int error; 413 vnode_t *vp; 414 415 rw_enter(&iwscn_lock, RW_READER); 416 wd = srilookup(getminor(dev)); 417 /* 418 * We don't need to hold iwscn_lock while waiting for the read to 419 * complete, but the vnode must not be destroyed. 420 */ 421 vp = wd->wd_list->wl_vp; 422 VN_HOLD(vp); 423 rw_exit(&iwscn_lock); 424 error = strread(vp, uio, cred); 425 VN_RELE(vp); 426 return (error); 427 } 428 429 static int 430 iwscnwrite(dev_t dev, uio_t *uio, cred_t *cred) 431 { 432 wcd_data_t *wd; 433 int error; 434 435 rw_enter(&iwscn_lock, RW_READER); 436 wd = srilookup(getminor(dev)); 437 error = strwrite(wd->wd_list->wl_vp, uio, cred); 438 rw_exit(&iwscn_lock); 439 return (error); 440 } 441 442 static int 443 iwscnioctl(dev_t dev, int cmd, intptr_t arg, int flag, 444 cred_t *cred, int *rvalp) 445 { 446 wcd_data_t *wd; 447 int err = 0; 448 file_t *f; 449 450 switch (cmd) { 451 case SRIOCSREDIR: { 452 wcrlist_t *wlp; 453 wcm_data_t *mdp; 454 455 if (!rw_tryenter(&iwscn_lock, RW_WRITER)) { 456 return (EBUSY); 457 } 458 wd = srilookup(getminor(dev)); 459 /* 460 * Find the vnode corresponding to the file descriptor 461 * argument and verify that it names a stream. 462 */ 463 if ((f = getf((int)arg)) == NULL) { 464 err = EBADF; 465 break; 466 } 467 if (f->f_vnode->v_stream == NULL) { 468 err = ENOSTR; 469 releasef((int)arg); 470 break; 471 } 472 /* 473 * allocate the private data for redirmod, and pass it through 474 * a global to wcmopen(). This is all protected by iwscn_lock. 475 */ 476 mdp = kmem_alloc(sizeof (*mdp), KM_SLEEP); 477 iwscn_wcm_data = mdp; 478 iwscn_thread = curthread; 479 480 /* 481 * Push a new instance of the redirecting module onto the 482 * stream, so that its close routine can notify us when the 483 * overall stream is closed. (In turn, we'll then remove it 484 * from the redirection list.) 485 */ 486 if ((err = VOP_IOCTL(f->f_vnode, I_PUSH, (intptr_t)"redirmod", 487 (FREAD | FKIOCTL), cred, rvalp)) != 0) { 488 iwscn_thread = NULL; 489 kmem_free(mdp, sizeof (*mdp)); 490 releasef((int)arg); 491 break; 492 } 493 iwscn_thread = NULL; /* clear authorization for wcmopen() */ 494 495 /* 496 * Push it onto the redirection stack. 497 */ 498 wlp = srpush(&wd->wd_list, f->f_vnode); 499 /* 500 * Fill in the redirecting module instance's private data with 501 * information to let it get to our redirection list when its 502 * close routine is called. Cross-link it with the 503 * redirection list entry. 504 */ 505 mdp->wm_wd = wd; 506 mdp->wm_entry = wlp; 507 wlp->wl_data = mdp; 508 releasef((int)arg); 509 510 break; 511 } 512 513 case SRIOCISREDIR: 514 rw_enter(&iwscn_lock, RW_READER); 515 wd = srilookup(getminor(dev)); 516 if ((f = getf((int)arg)) == NULL) { 517 err = EBADF; 518 break; 519 } 520 /* 521 * Return value is 1 if the argument descriptor is the current 522 * redirection target, and 0 otherwise. 523 */ 524 *rvalp = (f->f_vnode == wd->wd_list->wl_vp) ? 1 : 0; 525 releasef((int)arg); 526 break; 527 528 case I_POP: { 529 /* 530 * XXX - This is a big kludge the handles a deadlock case 531 * when we are trying to pop off the redirection 532 * module. Since this should only happen on a close 533 * of the device, and since it hangs the system, just 534 * do not allow a pop of the redirection module to happen. 535 * Popping other modules is allowed. 536 */ 537 struct stdata *stp; 538 char modname[FMNAMESZ + 1] = " "; 539 540 rw_enter(&iwscn_lock, RW_READER); 541 wd = srilookup(getminor(dev)); 542 (void) strioctl(wd->wd_list->wl_vp, I_LOOK, (intptr_t)modname, 543 flag, K_TO_K, cred, rvalp); 544 if (strcmp("redirmod", modname) == 0) { 545 if ((f = getf((int)arg)) == NULL) { 546 err = EBADF; 547 break; 548 } 549 if ((stp = f->f_vnode->v_stream) == NULL) { 550 err = ENOSTR; 551 releasef((int)arg); 552 break; 553 } 554 if (!(stp->sd_flag & STRCLOSE)) { 555 releasef((int)arg); 556 rw_exit(&iwscn_lock); 557 cmn_err(CE_WARN, "Popping of redirection " 558 "module not allowed"); 559 return (EINVAL); 560 } 561 562 releasef((int)arg); 563 } 564 565 /* Process ioctl normally */ 566 err = strioctl(wd->wd_list->wl_vp, cmd, arg, flag, U_TO_K, 567 cred, rvalp); 568 break; 569 } 570 571 default: 572 rw_enter(&iwscn_lock, RW_READER); 573 wd = srilookup(getminor(dev)); 574 err = strioctl(wd->wd_list->wl_vp, cmd, arg, flag, U_TO_K, 575 cred, rvalp); 576 break; 577 } 578 579 rw_exit(&iwscn_lock); 580 return (err); 581 } 582 583 static int 584 iwscnpoll( 585 dev_t dev, 586 short events, 587 int anyyet, 588 short *reventsp, 589 struct pollhead **phpp) 590 { 591 wcd_data_t *wd; 592 int error; 593 594 rw_enter(&iwscn_lock, RW_READER); 595 wd = srilookup(getminor(dev)); 596 error = strpoll(wd->wd_list->wl_vp->v_stream, events, anyyet, 597 reventsp, phpp); 598 rw_exit(&iwscn_lock); 599 return (error); 600 } 601 602 603 /* 604 * Auxiliary routines. 605 */ 606 607 /* 608 * Additional public interfaces. 609 */ 610 611 /* 612 * Reset the current workstation console designee to the device denoted by the 613 * wl_vp field of the first entry in the redirection list. Called from 614 * iwscnopen and from the SRIOCSREDIR case of iwscnioctl, in both cases after 615 * the target vp has been set to its new value. 616 */ 617 static int 618 srreset(wcd_data_t *wd, int flag, cred_t *cred) 619 { 620 wcrlist_t *wlp; 621 int err = 0; 622 623 ASSERT(RW_WRITE_HELD(&iwscn_lock) || MUTEX_HELD(&iwscn_open_lock)); 624 wlp = wd->wd_list; /* first entry */ 625 626 /* 627 * If we're reverting back to the workstation console, make sure it's 628 * open. 629 */ 630 if (wlp != NULL && wlp->wl_vp == wd->wd_vp) { 631 vnode_t *vp = wd->wd_vp; /* underlying device's vp */ 632 633 err = VOP_OPEN(&vp, flag, cred); 634 /* 635 * The underlying driver is not allowed to have cloned itself 636 * for this open. 637 */ 638 if (vp != wd->wd_vp) { 639 panic("srreset: Illegal clone"); 640 /*NOTREACHED*/ 641 } 642 if (!err) 643 wd->wd_wsconsopen++; 644 } 645 return (err); 646 } 647 648 /* 649 * Remove vp from the redirection list rooted at *rwlp, should it be there. 650 * If zap is nonzero, deallocate the entry and remove dangling references to 651 * the it from the corresponding redirecting module instance's wcm_data 652 * structure. 653 * 654 * If the entry doesn't exist upon completion, return NULL; otherwise return a 655 * pointer to it. 656 */ 657 static wcrlist_t * 658 srrm(wcrlist_t **rwlp, vnode_t *vp, int zap) 659 { 660 wcrlist_t **delwlp; 661 wcrlist_t *wlp; 662 wcm_data_t *mdp; 663 664 ASSERT(RW_WRITE_HELD(&iwscn_lock) || MUTEX_HELD(&iwscn_open_lock)); 665 for (delwlp = rwlp; (wlp = *delwlp) != NULL; delwlp = &wlp->wl_next) 666 if (wlp->wl_vp == vp) 667 break; 668 if (wlp == NULL) 669 return (NULL); 670 *delwlp = wlp->wl_next; 671 672 if (zap == 0) 673 return (wlp); 674 675 if (wlp->wl_vp == vp) 676 VN_RELE(vp); 677 /* 678 * Make sure there are no dangling references leading to the entry 679 * from the corresponding redirecting module instance. 680 */ 681 if ((mdp = wlp->wl_data) != NULL) { 682 mdp->wm_wd = NULL; 683 mdp->wm_entry = NULL; 684 } 685 686 kmem_free(wlp, sizeof (*wlp)); 687 return (NULL); 688 } 689 690 /* 691 * srpop - remove redirection because the target stream is being closed. 692 * Called from wcmclose(). 693 */ 694 void 695 srpop(wcm_data_t *mdp, int flag, cred_t *cred) 696 { 697 wcd_data_t *ddp; 698 699 rw_enter(&iwscn_lock, RW_WRITER); 700 if ((ddp = mdp->wm_wd) != NULL) { 701 ASSERT(mdp->wm_entry != NULL); 702 (void) srrm(&ddp->wd_list, mdp->wm_entry->wl_vp, 1); 703 (void) srreset(ddp, flag, cred); 704 } 705 rw_exit(&iwscn_lock); 706 } 707 708 /* 709 * Routines for allocating, deallocating, and finding wcd_data structures. 710 * 711 * For a given instantiation of the driver, its open instance structures are 712 * linked together into a list, on the assumption that there will never be 713 * enough open instances to make search efficiency a serious concern. 714 */ 715 716 /* 717 * Look up the instance structure denoted by unit. 718 */ 719 static wcd_data_t * 720 srilookup(minor_t unit) 721 { 722 wcd_data_t *wd = wcddata; 723 724 ASSERT(RW_LOCK_HELD(&iwscn_lock)); 725 for (; wd != NULL && wd->wd_unit != unit; wd = wd->wd_next) 726 continue; 727 728 return (wd); 729 } 730 731 /* 732 * Allocate a wcd_data structure for the instance denoted by unit, link it in 733 * place, and return a pointer to it. If it's already allocated, simply 734 * return a pointer to it. 735 */ 736 static wcd_data_t * 737 srialloc(minor_t unit) 738 { 739 wcd_data_t *wdp; 740 wcd_data_t **wdpp; 741 742 ASSERT(MUTEX_HELD(&iwscn_open_lock)); 743 for (wdpp = &wcddata; (wdp = *wdpp) != NULL; wdpp = &wdp->wd_next) { 744 if (unit < wdp->wd_unit) 745 break; 746 if (unit == wdp->wd_unit) { 747 /* Already allocated and in place. */ 748 return (wdp); 749 } 750 } 751 /* 752 * wdpp now points to the proper insertion point for unit's 753 * per-instance structure. 754 */ 755 wdp = kmem_zalloc(sizeof (*wdp), KM_SLEEP); 756 wdp->wd_unit = unit; 757 wdp->wd_next = *wdpp; 758 *wdpp = wdp; 759 760 return (wdp); 761 } 762 763 /* 764 * Deallocate the wcd_data structure denoted by wd and unlink it from the 765 * list of open instances. 766 */ 767 static void 768 sridealloc(wcd_data_t *wd) 769 { 770 wcd_data_t *wdp; 771 wcd_data_t **wdpp; 772 773 ASSERT(RW_WRITE_HELD(&iwscn_lock) || MUTEX_HELD(&iwscn_open_lock)); 774 for (wdpp = &wcddata; (wdp = *wdpp) != NULL; wdpp = &wdp->wd_next) 775 if (wd == wdp) 776 break; 777 if (wdp == NULL) { 778 /* 779 * Not there. This should probably be a panic. 780 */ 781 return; 782 } 783 *wdpp = wdp->wd_next; 784 kmem_free(wdp, sizeof (*wdp)); 785 } 786 787 788 /* 789 * Push vp onto the redirection list rooted at *wlpp. If it's already there, 790 * move it to the front position. Return a pointer to its list entry. 791 * 792 * N.B.: It is the caller's responsibility to initialize all fields in the 793 * entry other than the wl_next and wl_vp fields. 794 */ 795 static wcrlist_t * 796 srpush(wcrlist_t **wlpp, vnode_t *vp) 797 { 798 wcrlist_t *nwlp; 799 800 ASSERT(RW_WRITE_HELD(&iwscn_lock) || MUTEX_HELD(&iwscn_open_lock)); 801 if ((nwlp = srrm(wlpp, vp, 0)) == NULL) { 802 nwlp = kmem_zalloc(sizeof (*nwlp), KM_SLEEP); 803 nwlp->wl_vp = vp; 804 /* 805 * The hold will prevent underlying device from closing 806 * while this vnode is still on the redirection list. 807 */ 808 VN_HOLD(vp); 809 } 810 nwlp->wl_next = *wlpp; 811 *wlpp = nwlp; 812 813 return (nwlp); 814 } 815