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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 /* 28 * Zone Console Driver. 29 * 30 * This driver, derived from the pts/ptm drivers, is the pseudo console driver 31 * for system zones. Its implementation is straightforward. Each instance 32 * of the driver represents a global-zone/local-zone pair (this maps in a 33 * straightforward way to the commonly used terminal notion of "master side" 34 * and "slave side", and we use that terminology throughout). 35 * 36 * Instances of zcons are onlined as children of /pseudo/zconsnex@1/ 37 * by zoneadmd in userland, using the devctl framework; thus the driver 38 * does not need to maintain any sort of "admin" node. 39 * 40 * The driver shuttles I/O from master side to slave side and back. In a break 41 * from the pts/ptm semantics, if one side is not open, I/O directed towards 42 * it will simply be discarded. This is so that if zoneadmd is not holding 43 * the master side console open (i.e. it has died somehow), processes in 44 * the zone do not experience any errors and I/O to the console does not 45 * hang. 46 * 47 * TODO: we may want to revisit the other direction; i.e. we may want 48 * zoneadmd to be able to detect whether no zone processes are holding the 49 * console open, an unusual situation. 50 * 51 * 52 * 53 * MASTER SIDE IOCTLS 54 * 55 * The ZC_HOLDSLAVE and ZC_RELEASESLAVE ioctls instruct the master side of the 56 * console to hold and release a reference to the slave side's vnode. They are 57 * meant to be issued by zoneadmd after the console device node is created and 58 * before it is destroyed so that the slave's STREAMS anchor, ptem, is 59 * preserved when ttymon starts popping STREAMS modules from within the 60 * associated zone. This guarantees that the zone console will always have 61 * terminal semantics while the zone is running. 62 * 63 * Here is the issue: the ptem module is anchored in the zone console 64 * (slave side) so that processes within the associated non-global zone will 65 * fail to pop it off, thus ensuring that the slave will retain terminal 66 * semantics. When a process attempts to pop the anchor off of a stream, the 67 * STREAMS subsystem checks whether the calling process' zone is the same as 68 * that of the process that pushed the anchor onto the stream and cancels the 69 * pop if they differ. zoneadmd used to hold an open file descriptor for the 70 * slave while the associated non-global zone ran, thus ensuring that the 71 * slave's STREAMS anchor would never be popped from within the non-global zone 72 * (because zoneadmd runs in the global zone). However, this file descriptor 73 * was removed to make zone console management more robust. sad(7D) is now 74 * used to automatically set up the slave's STREAMS modules when the zone 75 * console is freshly opened within the associated non-global zone. However, 76 * when a process within the non-global zone freshly opens the zone console, the 77 * anchor is pushed from within the non-global zone, making it possible for 78 * processes within the non-global zone (e.g., ttymon) to pop the anchor and 79 * destroy the zone console's terminal semantics. 80 * 81 * One solution is to make the zcons device hold the slave open while the 82 * associated non-global zone runs so that the STREAMS anchor will always be 83 * associated with the global zone. Unfortunately, the slave cannot be opened 84 * from within the zcons driver because the driver is not reentrant: it has 85 * an outer STREAMS perimeter. Therefore, the next best option is for zcons to 86 * provide an ioctl interface to zoneadmd to manage holding and releasing 87 * the slave side of the console. It is sufficient to hold the slave side's 88 * vnode and bump the associated snode's reference count to preserve the slave's 89 * STREAMS configuration while the associated zone runs, so that's what the 90 * ioctls do. 91 * 92 * 93 * ZC_HOLDSLAVE 94 * 95 * This ioctl takes a file descriptor as an argument. It effectively gets a 96 * reference to the slave side's minor node's vnode and bumps the associated 97 * snode's reference count. The vnode reference is stored in the zcons device 98 * node's soft state. This ioctl succeeds if the given file descriptor refers 99 * to the slave side's minor node or if there is already a reference to the 100 * slave side's minor node's vnode in the device's soft state. 101 * 102 * 103 * ZC_RELEASESLAVE 104 * 105 * This ioctl takes a file descriptor as an argument. It effectively releases 106 * the vnode reference stored in the zcons device node's soft state (which was 107 * previously acquired via ZC_HOLDSLAVE) and decrements the reference count of 108 * the snode associated with the vnode. This ioctl succeeds if the given file 109 * descriptor refers to the slave side's minor node or if no reference to the 110 * slave side's minor node's vnode is stored in the device's soft state. 111 * 112 * 113 * Note that the file descriptor arguments for both ioctls must be cast to 114 * integers of pointer width. 115 * 116 * Here's how the dance between zcons and zoneadmd works: 117 * 118 * Zone boot: 119 * 1. While booting the zone, zoneadmd creates an instance of zcons. 120 * 2. zoneadmd opens the master and slave sides of the new zone console 121 * and issues the ZC_HOLDSLAVE ioctl on the master side, passing its 122 * file descriptor for the slave side as the ioctl argument. 123 * 3. zcons holds the slave side's vnode, bumps the snode's reference 124 * count, and stores a pointer to the vnode in the device's soft 125 * state. 126 * 4. zoneadmd closes the master and slave sides and continues to boot 127 * the zone. 128 * 129 * Zone halt: 130 * 1. While halting the zone, zoneadmd opens the master and slave sides 131 * of the zone's console and issues the ZC_RELEASESLAVE ioctl on the 132 * master side, passing its file descriptor for the slave side as the 133 * ioctl argument. 134 * 2. zcons decrements the slave side's snode's reference count, releases 135 * the slave's vnode, and eliminates its reference to the vnode in the 136 * device's soft state. 137 * 3. zoneadmd closes the master and slave sides. 138 * 4. zoneadmd destroys the zcons device and continues to halt the zone. 139 * 140 * It is necessary for zoneadmd to hold the slave open while issuing 141 * ZC_RELEASESLAVE because zcons might otherwise release the last reference to 142 * the slave's vnode. If it does, then specfs will panic because it will expect 143 * that the STREAMS configuration for the vnode was destroyed, which VN_RELE 144 * doesn't do. Forcing zoneadmd to hold the slave open guarantees that zcons 145 * won't release the vnode's last reference. zoneadmd will properly destroy the 146 * vnode and the snode when it closes the file descriptor. 147 * 148 * Technically, any process that can access the master side can issue these 149 * ioctls, but they should be treated as private interfaces for zoneadmd. 150 */ 151 152 #include <sys/types.h> 153 #include <sys/cmn_err.h> 154 #include <sys/conf.h> 155 #include <sys/cred.h> 156 #include <sys/ddi.h> 157 #include <sys/debug.h> 158 #include <sys/devops.h> 159 #include <sys/errno.h> 160 #include <sys/file.h> 161 #include <sys/kstr.h> 162 #include <sys/modctl.h> 163 #include <sys/param.h> 164 #include <sys/stat.h> 165 #include <sys/stream.h> 166 #include <sys/stropts.h> 167 #include <sys/strsun.h> 168 #include <sys/sunddi.h> 169 #include <sys/sysmacros.h> 170 #include <sys/systm.h> 171 #include <sys/types.h> 172 #include <sys/zcons.h> 173 #include <sys/vnode.h> 174 #include <sys/fs/snode.h> 175 176 static int zc_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 177 static int zc_attach(dev_info_t *, ddi_attach_cmd_t); 178 static int zc_detach(dev_info_t *, ddi_detach_cmd_t); 179 180 static int zc_open(queue_t *, dev_t *, int, int, cred_t *); 181 static int zc_close(queue_t *, int, cred_t *); 182 static void zc_wput(queue_t *, mblk_t *); 183 static void zc_rsrv(queue_t *); 184 static void zc_wsrv(queue_t *); 185 186 /* 187 * The instance number is encoded in the dev_t in the minor number; the lowest 188 * bit of the minor number is used to track the master vs. slave side of the 189 * virtual console. The rest of the bits in the minor number are the instance. 190 */ 191 #define ZC_MASTER_MINOR 0 192 #define ZC_SLAVE_MINOR 1 193 194 #define ZC_INSTANCE(x) (getminor((x)) >> 1) 195 #define ZC_NODE(x) (getminor((x)) & 0x01) 196 197 /* 198 * This macro converts a zc_state_t pointer to the associated slave minor node's 199 * dev_t. 200 */ 201 #define ZC_STATE_TO_SLAVEDEV(x) (makedevice(ddi_driver_major((x)->zc_devinfo), \ 202 (minor_t)(ddi_get_instance((x)->zc_devinfo) << 1 | ZC_SLAVE_MINOR))) 203 204 int zcons_debug = 0; 205 #define DBG(a) if (zcons_debug) cmn_err(CE_NOTE, a) 206 #define DBG1(a, b) if (zcons_debug) cmn_err(CE_NOTE, a, b) 207 208 209 /* 210 * Zone Console Pseudo Terminal Module: stream data structure definitions 211 */ 212 static struct module_info zc_info = { 213 31337, /* c0z we r hAx0rs */ 214 "zcons", 215 0, 216 INFPSZ, 217 2048, 218 128 219 }; 220 221 static struct qinit zc_rinit = { 222 NULL, 223 (int (*)()) zc_rsrv, 224 zc_open, 225 zc_close, 226 NULL, 227 &zc_info, 228 NULL 229 }; 230 231 static struct qinit zc_winit = { 232 (int (*)()) zc_wput, 233 (int (*)()) zc_wsrv, 234 NULL, 235 NULL, 236 NULL, 237 &zc_info, 238 NULL 239 }; 240 241 static struct streamtab zc_tab_info = { 242 &zc_rinit, 243 &zc_winit, 244 NULL, 245 NULL 246 }; 247 248 #define ZC_CONF_FLAG (D_MP | D_MTQPAIR | D_MTOUTPERIM | D_MTOCEXCL) 249 250 /* 251 * this will define (struct cb_ops cb_zc_ops) and (struct dev_ops zc_ops) 252 */ 253 DDI_DEFINE_STREAM_OPS(zc_ops, nulldev, nulldev, zc_attach, zc_detach, nodev, \ 254 zc_getinfo, ZC_CONF_FLAG, &zc_tab_info, ddi_quiesce_not_needed); 255 256 /* 257 * Module linkage information for the kernel. 258 */ 259 260 static struct modldrv modldrv = { 261 &mod_driverops, /* Type of module (this is a pseudo driver) */ 262 "Zone console driver", /* description of module */ 263 &zc_ops /* driver ops */ 264 }; 265 266 static struct modlinkage modlinkage = { 267 MODREV_1, 268 &modldrv, 269 NULL 270 }; 271 272 typedef struct zc_state { 273 dev_info_t *zc_devinfo; 274 queue_t *zc_master_rdq; 275 queue_t *zc_slave_rdq; 276 vnode_t *zc_slave_vnode; 277 int zc_state; 278 } zc_state_t; 279 280 #define ZC_STATE_MOPEN 0x01 281 #define ZC_STATE_SOPEN 0x02 282 283 static void *zc_soft_state; 284 285 /* 286 * List of STREAMS modules that should be pushed onto every slave instance. 287 */ 288 static char *zcons_mods[] = { 289 "ptem", 290 "ldterm", 291 "ttcompat", 292 NULL 293 }; 294 295 int 296 _init(void) 297 { 298 int err; 299 300 if ((err = ddi_soft_state_init(&zc_soft_state, 301 sizeof (zc_state_t), 0)) != 0) { 302 return (err); 303 } 304 305 if ((err = mod_install(&modlinkage)) != 0) 306 ddi_soft_state_fini(zc_soft_state); 307 308 return (err); 309 } 310 311 312 int 313 _fini(void) 314 { 315 int err; 316 317 if ((err = mod_remove(&modlinkage)) != 0) { 318 return (err); 319 } 320 321 ddi_soft_state_fini(&zc_soft_state); 322 return (0); 323 } 324 325 int 326 _info(struct modinfo *modinfop) 327 { 328 return (mod_info(&modlinkage, modinfop)); 329 } 330 331 /* 332 * This is a convenience function that clears a device's autopush configuration. 333 * It is meant to be used on the slave side of the console. Unlike calling 334 * kstr_autopush() directly, this function outputs a warning via cmn_err() if 335 * kstr_autopush() fails. 'dip' must be non-NULL in debug builds. Both 336 * 'major' and 'minor' must be valid. 337 */ 338 static void 339 zc_clearautopush(dev_info_t *dip, major_t major, minor_t minor) 340 { 341 char *devicepathp; 342 343 if (kstr_autopush(CLR_AUTOPUSH, &major, &minor, NULL, NULL, NULL) != 344 0 && zcons_debug != 0) { 345 devicepathp = (char *)kmem_alloc(MAXPATHLEN * sizeof (char), 346 KM_SLEEP); 347 (void) ddi_pathname(dip, devicepathp); 348 cmn_err(CE_NOTE, "zc_detach: could not clear sad configuration " 349 "for device %s\n", devicepathp); 350 kmem_free(devicepathp, MAXPATHLEN * sizeof (char)); 351 } 352 } 353 354 static int 355 zc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 356 { 357 zc_state_t *zcs; 358 int instance; 359 major_t major; 360 minor_t minor; 361 minor_t lastminor; 362 uint_t anchorindex; 363 364 if (cmd != DDI_ATTACH) 365 return (DDI_FAILURE); 366 367 instance = ddi_get_instance(dip); 368 if (ddi_soft_state_zalloc(zc_soft_state, instance) != DDI_SUCCESS) 369 return (DDI_FAILURE); 370 371 /* 372 * Set up sad(7D) so that the necessary STREAMS modules will be in place 373 * when the slave is opened. A wrinkle is that 'ptem' must be anchored 374 * in place (see streamio(7i)) because we always want the console to 375 * have terminal semantics. 376 */ 377 minor = instance << 1 | ZC_SLAVE_MINOR; 378 lastminor = 0; 379 major = ddi_driver_major(dip); 380 anchorindex = 1; 381 if (kstr_autopush(SET_AUTOPUSH, &major, &minor, &lastminor, 382 &anchorindex, zcons_mods) != 0) 383 goto commonfail; 384 385 /* 386 * Create the master and slave minor nodes. 387 */ 388 if ((ddi_create_minor_node(dip, ZCONS_SLAVE_NAME, S_IFCHR, minor, 389 DDI_PSEUDO, 0) == DDI_FAILURE) || 390 (ddi_create_minor_node(dip, ZCONS_MASTER_NAME, S_IFCHR, 391 instance << 1 | ZC_MASTER_MINOR, DDI_PSEUDO, 0) == DDI_FAILURE)) 392 goto failwithautopush; 393 394 VERIFY((zcs = ddi_get_soft_state(zc_soft_state, instance)) != NULL); 395 zcs->zc_devinfo = dip; 396 return (DDI_SUCCESS); 397 398 failwithautopush: 399 zc_clearautopush(dip, major, minor); 400 ddi_remove_minor_node(dip, NULL); 401 commonfail: 402 ddi_soft_state_free(zc_soft_state, instance); 403 return (DDI_FAILURE); 404 } 405 406 static int 407 zc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 408 { 409 zc_state_t *zcs; 410 int instance; 411 412 if (cmd != DDI_DETACH) 413 return (DDI_FAILURE); 414 415 instance = ddi_get_instance(dip); 416 if ((zcs = ddi_get_soft_state(zc_soft_state, instance)) == NULL) 417 return (DDI_FAILURE); 418 419 if ((zcs->zc_state & ZC_STATE_MOPEN) || 420 (zcs->zc_state & ZC_STATE_SOPEN)) { 421 DBG1("zc_detach: device (dip=%p) still open\n", (void *)dip); 422 return (DDI_FAILURE); 423 } 424 425 /* 426 * Clear the sad configuration so that reattaching doesn't fail to 427 * set up sad configuration. 428 */ 429 zc_clearautopush(dip, ddi_driver_major(dip), instance << 1 | 430 ZC_SLAVE_MINOR); 431 432 ddi_remove_minor_node(dip, NULL); 433 ddi_soft_state_free(zc_soft_state, instance); 434 435 return (DDI_SUCCESS); 436 } 437 438 /* 439 * zc_getinfo() 440 * getinfo(9e) entrypoint. 441 */ 442 /*ARGSUSED*/ 443 static int 444 zc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 445 { 446 zc_state_t *zcs; 447 int instance = ZC_INSTANCE((dev_t)arg); 448 449 switch (infocmd) { 450 case DDI_INFO_DEVT2DEVINFO: 451 if ((zcs = ddi_get_soft_state(zc_soft_state, instance)) == NULL) 452 return (DDI_FAILURE); 453 *result = zcs->zc_devinfo; 454 return (DDI_SUCCESS); 455 case DDI_INFO_DEVT2INSTANCE: 456 *result = (void *)(uintptr_t)instance; 457 return (DDI_SUCCESS); 458 } 459 return (DDI_FAILURE); 460 } 461 462 /* 463 * Return the equivalent queue from the other side of the relationship. 464 * e.g.: given the slave's write queue, return the master's write queue. 465 */ 466 static queue_t * 467 zc_switch(queue_t *qp) 468 { 469 zc_state_t *zcs = qp->q_ptr; 470 ASSERT(zcs != NULL); 471 472 if (qp == zcs->zc_master_rdq) 473 return (zcs->zc_slave_rdq); 474 else if (OTHERQ(qp) == zcs->zc_master_rdq && zcs->zc_slave_rdq != NULL) 475 return (OTHERQ(zcs->zc_slave_rdq)); 476 else if (qp == zcs->zc_slave_rdq) 477 return (zcs->zc_master_rdq); 478 else if (OTHERQ(qp) == zcs->zc_slave_rdq && zcs->zc_master_rdq != NULL) 479 return (OTHERQ(zcs->zc_master_rdq)); 480 else 481 return (NULL); 482 } 483 484 /* 485 * For debugging and outputting messages. Returns the name of the side of 486 * the relationship associated with this queue. 487 */ 488 static const char * 489 zc_side(queue_t *qp) 490 { 491 zc_state_t *zcs = qp->q_ptr; 492 ASSERT(zcs != NULL); 493 494 if (qp == zcs->zc_master_rdq || 495 OTHERQ(qp) == zcs->zc_master_rdq) { 496 return ("master"); 497 } 498 ASSERT(qp == zcs->zc_slave_rdq || OTHERQ(qp) == zcs->zc_slave_rdq); 499 return ("slave"); 500 } 501 502 /*ARGSUSED*/ 503 static int 504 zc_master_open(zc_state_t *zcs, 505 queue_t *rqp, /* pointer to the read side queue */ 506 dev_t *devp, /* pointer to stream tail's dev */ 507 int oflag, /* the user open(2) supplied flags */ 508 int sflag, /* open state flag */ 509 cred_t *credp) /* credentials */ 510 { 511 mblk_t *mop; 512 struct stroptions *sop; 513 514 /* 515 * Enforce exclusivity on the master side; the only consumer should 516 * be the zoneadmd for the zone. 517 */ 518 if ((zcs->zc_state & ZC_STATE_MOPEN) != 0) 519 return (EBUSY); 520 521 if ((mop = allocb(sizeof (struct stroptions), BPRI_MED)) == NULL) { 522 DBG("zc_master_open(): mop allocation failed\n"); 523 return (ENOMEM); 524 } 525 526 zcs->zc_state |= ZC_STATE_MOPEN; 527 528 /* 529 * q_ptr stores driver private data; stash the soft state data on both 530 * read and write sides of the queue. 531 */ 532 WR(rqp)->q_ptr = rqp->q_ptr = zcs; 533 qprocson(rqp); 534 535 /* 536 * Following qprocson(), the master side is fully plumbed into the 537 * STREAM and may send/receive messages. Setting zcs->zc_master_rdq 538 * will allow the slave to send messages to us (the master). 539 * This cannot occur before qprocson() because the master is not 540 * ready to process them until that point. 541 */ 542 zcs->zc_master_rdq = rqp; 543 544 /* 545 * set up hi/lo water marks on stream head read queue and add 546 * controlling tty as needed. 547 */ 548 mop->b_datap->db_type = M_SETOPTS; 549 mop->b_wptr += sizeof (struct stroptions); 550 sop = (struct stroptions *)(void *)mop->b_rptr; 551 if (oflag & FNOCTTY) 552 sop->so_flags = SO_HIWAT | SO_LOWAT; 553 else 554 sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY; 555 sop->so_hiwat = 512; 556 sop->so_lowat = 256; 557 putnext(rqp, mop); 558 559 return (0); 560 } 561 562 /*ARGSUSED*/ 563 static int 564 zc_slave_open(zc_state_t *zcs, 565 queue_t *rqp, /* pointer to the read side queue */ 566 dev_t *devp, /* pointer to stream tail's dev */ 567 int oflag, /* the user open(2) supplied flags */ 568 int sflag, /* open state flag */ 569 cred_t *credp) /* credentials */ 570 { 571 mblk_t *mop; 572 struct stroptions *sop; 573 574 /* 575 * The slave side can be opened as many times as needed. 576 */ 577 if ((zcs->zc_state & ZC_STATE_SOPEN) != 0) { 578 ASSERT((rqp != NULL) && (WR(rqp)->q_ptr == zcs)); 579 return (0); 580 } 581 582 if ((mop = allocb(sizeof (struct stroptions), BPRI_MED)) == NULL) { 583 DBG("zc_slave_open(): mop allocation failed\n"); 584 return (ENOMEM); 585 } 586 587 zcs->zc_state |= ZC_STATE_SOPEN; 588 589 /* 590 * q_ptr stores driver private data; stash the soft state data on both 591 * read and write sides of the queue. 592 */ 593 WR(rqp)->q_ptr = rqp->q_ptr = zcs; 594 595 qprocson(rqp); 596 597 /* 598 * Must follow qprocson(), since we aren't ready to process until then. 599 */ 600 zcs->zc_slave_rdq = rqp; 601 602 /* 603 * set up hi/lo water marks on stream head read queue and add 604 * controlling tty as needed. 605 */ 606 mop->b_datap->db_type = M_SETOPTS; 607 mop->b_wptr += sizeof (struct stroptions); 608 sop = (struct stroptions *)(void *)mop->b_rptr; 609 sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY; 610 sop->so_hiwat = 512; 611 sop->so_lowat = 256; 612 putnext(rqp, mop); 613 614 return (0); 615 } 616 617 /* 618 * open(9e) entrypoint; checks sflag, and rejects anything unordinary. 619 */ 620 static int 621 zc_open(queue_t *rqp, /* pointer to the read side queue */ 622 dev_t *devp, /* pointer to stream tail's dev */ 623 int oflag, /* the user open(2) supplied flags */ 624 int sflag, /* open state flag */ 625 cred_t *credp) /* credentials */ 626 { 627 int instance = ZC_INSTANCE(*devp); 628 int ret; 629 zc_state_t *zcs; 630 631 if (sflag != 0) 632 return (EINVAL); 633 634 if ((zcs = ddi_get_soft_state(zc_soft_state, instance)) == NULL) 635 return (ENXIO); 636 637 switch (ZC_NODE(*devp)) { 638 case ZC_MASTER_MINOR: 639 ret = zc_master_open(zcs, rqp, devp, oflag, sflag, credp); 640 break; 641 case ZC_SLAVE_MINOR: 642 ret = zc_slave_open(zcs, rqp, devp, oflag, sflag, credp); 643 break; 644 default: 645 ret = ENXIO; 646 break; 647 } 648 649 return (ret); 650 } 651 652 /* 653 * close(9e) entrypoint. 654 */ 655 /*ARGSUSED1*/ 656 static int 657 zc_close(queue_t *rqp, int flag, cred_t *credp) 658 { 659 queue_t *wqp; 660 mblk_t *bp; 661 zc_state_t *zcs; 662 663 zcs = (zc_state_t *)rqp->q_ptr; 664 665 if (rqp == zcs->zc_master_rdq) { 666 DBG("Closing master side"); 667 668 zcs->zc_master_rdq = NULL; 669 zcs->zc_state &= ~ZC_STATE_MOPEN; 670 671 /* 672 * qenable slave side write queue so that it can flush 673 * its messages as master's read queue is going away 674 */ 675 if (zcs->zc_slave_rdq != NULL) { 676 qenable(WR(zcs->zc_slave_rdq)); 677 } 678 679 qprocsoff(rqp); 680 WR(rqp)->q_ptr = rqp->q_ptr = NULL; 681 682 } else if (rqp == zcs->zc_slave_rdq) { 683 684 DBG("Closing slave side"); 685 zcs->zc_state &= ~ZC_STATE_SOPEN; 686 zcs->zc_slave_rdq = NULL; 687 688 wqp = WR(rqp); 689 while ((bp = getq(wqp)) != NULL) { 690 if (zcs->zc_master_rdq != NULL) 691 putnext(zcs->zc_master_rdq, bp); 692 else if (bp->b_datap->db_type == M_IOCTL) 693 miocnak(wqp, bp, 0, 0); 694 else 695 freemsg(bp); 696 } 697 698 /* 699 * Qenable master side write queue so that it can flush its 700 * messages as slaves's read queue is going away. 701 */ 702 if (zcs->zc_master_rdq != NULL) 703 qenable(WR(zcs->zc_master_rdq)); 704 705 qprocsoff(rqp); 706 WR(rqp)->q_ptr = rqp->q_ptr = NULL; 707 } 708 709 return (0); 710 } 711 712 static void 713 handle_mflush(queue_t *qp, mblk_t *mp) 714 { 715 mblk_t *nmp; 716 DBG1("M_FLUSH on %s side", zc_side(qp)); 717 718 if (*mp->b_rptr & FLUSHW) { 719 DBG1("M_FLUSH, FLUSHW, %s side", zc_side(qp)); 720 flushq(qp, FLUSHDATA); 721 *mp->b_rptr &= ~FLUSHW; 722 if ((*mp->b_rptr & FLUSHR) == 0) { 723 /* 724 * FLUSHW only. Change to FLUSHR and putnext other side, 725 * then we are done. 726 */ 727 *mp->b_rptr |= FLUSHR; 728 if (zc_switch(RD(qp)) != NULL) { 729 putnext(zc_switch(RD(qp)), mp); 730 return; 731 } 732 } else if ((zc_switch(RD(qp)) != NULL) && 733 (nmp = copyb(mp)) != NULL) { 734 /* 735 * It is a FLUSHRW; we copy the mblk and send 736 * it to the other side, since we still need to use 737 * the mblk in FLUSHR processing, below. 738 */ 739 putnext(zc_switch(RD(qp)), nmp); 740 } 741 } 742 743 if (*mp->b_rptr & FLUSHR) { 744 DBG("qreply(qp) turning FLUSHR around\n"); 745 qreply(qp, mp); 746 return; 747 } 748 freemsg(mp); 749 } 750 751 /* 752 * wput(9E) is symmetric for master and slave sides, so this handles both 753 * without splitting the codepath. (The only exception to this is the 754 * processing of zcons ioctls, which is restricted to the master side.) 755 * 756 * zc_wput() looks at the other side; if there is no process holding that 757 * side open, it frees the message. This prevents processes from hanging 758 * if no one is holding open the console. Otherwise, it putnext's high 759 * priority messages, putnext's normal messages if possible, and otherwise 760 * enqueues the messages; in the case that something is enqueued, wsrv(9E) 761 * will take care of eventually shuttling I/O to the other side. 762 */ 763 static void 764 zc_wput(queue_t *qp, mblk_t *mp) 765 { 766 unsigned char type = mp->b_datap->db_type; 767 zc_state_t *zcs; 768 struct iocblk *iocbp; 769 file_t *slave_filep; 770 struct snode *slave_snodep; 771 int slave_fd; 772 773 ASSERT(qp->q_ptr); 774 775 DBG1("entering zc_wput, %s side", zc_side(qp)); 776 777 /* 778 * Process zcons ioctl messages if qp is the master console's write 779 * queue. 780 */ 781 zcs = (zc_state_t *)qp->q_ptr; 782 if (zcs->zc_master_rdq != NULL && qp == WR(zcs->zc_master_rdq) && 783 type == M_IOCTL) { 784 iocbp = (struct iocblk *)(void *)mp->b_rptr; 785 switch (iocbp->ioc_cmd) { 786 case ZC_HOLDSLAVE: 787 /* 788 * Hold the slave's vnode and increment the refcount 789 * of the snode. If the vnode is already held, then 790 * indicate success. 791 */ 792 if (iocbp->ioc_count != TRANSPARENT) { 793 miocack(qp, mp, 0, EINVAL); 794 return; 795 } 796 if (zcs->zc_slave_vnode != NULL) { 797 miocack(qp, mp, 0, 0); 798 return; 799 } 800 801 /* 802 * The calling process must pass a file descriptor for 803 * the slave device. 804 */ 805 slave_fd = 806 (int)(intptr_t)*(caddr_t *)(void *)mp->b_cont-> 807 b_rptr; 808 slave_filep = getf(slave_fd); 809 if (slave_filep == NULL) { 810 miocack(qp, mp, 0, EINVAL); 811 return; 812 } 813 if (ZC_STATE_TO_SLAVEDEV(zcs) != 814 slave_filep->f_vnode->v_rdev) { 815 releasef(slave_fd); 816 miocack(qp, mp, 0, EINVAL); 817 return; 818 } 819 820 /* 821 * Get a reference to the slave's vnode. Also bump the 822 * reference count on the associated snode. 823 */ 824 ASSERT(vn_matchops(slave_filep->f_vnode, 825 spec_getvnodeops())); 826 zcs->zc_slave_vnode = slave_filep->f_vnode; 827 VN_HOLD(zcs->zc_slave_vnode); 828 slave_snodep = VTOCS(zcs->zc_slave_vnode); 829 mutex_enter(&slave_snodep->s_lock); 830 ++slave_snodep->s_count; 831 mutex_exit(&slave_snodep->s_lock); 832 releasef(slave_fd); 833 miocack(qp, mp, 0, 0); 834 return; 835 case ZC_RELEASESLAVE: 836 /* 837 * Release the master's handle on the slave's vnode. 838 * If there isn't a handle for the vnode, then indicate 839 * success. 840 */ 841 if (iocbp->ioc_count != TRANSPARENT) { 842 miocack(qp, mp, 0, EINVAL); 843 return; 844 } 845 if (zcs->zc_slave_vnode == NULL) { 846 miocack(qp, mp, 0, 0); 847 return; 848 } 849 850 /* 851 * The process that passed the ioctl must have provided 852 * a file descriptor for the slave device. Make sure 853 * this is correct. 854 */ 855 slave_fd = 856 (int)(intptr_t)*(caddr_t *)(void *)mp->b_cont-> 857 b_rptr; 858 slave_filep = getf(slave_fd); 859 if (slave_filep == NULL) { 860 miocack(qp, mp, 0, EINVAL); 861 return; 862 } 863 if (zcs->zc_slave_vnode->v_rdev != 864 slave_filep->f_vnode->v_rdev) { 865 releasef(slave_fd); 866 miocack(qp, mp, 0, EINVAL); 867 return; 868 } 869 870 /* 871 * Decrement the snode's reference count and release the 872 * vnode. 873 */ 874 ASSERT(vn_matchops(slave_filep->f_vnode, 875 spec_getvnodeops())); 876 slave_snodep = VTOCS(zcs->zc_slave_vnode); 877 mutex_enter(&slave_snodep->s_lock); 878 --slave_snodep->s_count; 879 mutex_exit(&slave_snodep->s_lock); 880 VN_RELE(zcs->zc_slave_vnode); 881 zcs->zc_slave_vnode = NULL; 882 releasef(slave_fd); 883 miocack(qp, mp, 0, 0); 884 return; 885 default: 886 break; 887 } 888 } 889 890 if (zc_switch(RD(qp)) == NULL) { 891 DBG1("wput to %s side (no one listening)", zc_side(qp)); 892 switch (type) { 893 case M_FLUSH: 894 handle_mflush(qp, mp); 895 break; 896 case M_IOCTL: 897 miocnak(qp, mp, 0, 0); 898 break; 899 default: 900 freemsg(mp); 901 break; 902 } 903 return; 904 } 905 906 if (type >= QPCTL) { 907 DBG1("(hipri) wput, %s side", zc_side(qp)); 908 switch (type) { 909 case M_READ: /* supposedly from ldterm? */ 910 DBG("zc_wput: tossing M_READ\n"); 911 freemsg(mp); 912 break; 913 case M_FLUSH: 914 handle_mflush(qp, mp); 915 break; 916 default: 917 /* 918 * Put this to the other side. 919 */ 920 ASSERT(zc_switch(RD(qp)) != NULL); 921 putnext(zc_switch(RD(qp)), mp); 922 break; 923 } 924 DBG1("done (hipri) wput, %s side", zc_side(qp)); 925 return; 926 } 927 928 /* 929 * Only putnext if there isn't already something in the queue. 930 * otherwise things would wind up out of order. 931 */ 932 if (qp->q_first == NULL && bcanputnext(RD(zc_switch(qp)), mp->b_band)) { 933 DBG("wput: putting message to other side\n"); 934 putnext(RD(zc_switch(qp)), mp); 935 } else { 936 DBG("wput: putting msg onto queue\n"); 937 (void) putq(qp, mp); 938 } 939 DBG1("done wput, %s side", zc_side(qp)); 940 } 941 942 /* 943 * rsrv(9E) is symmetric for master and slave, so zc_rsrv() handles both 944 * without splitting up the codepath. 945 * 946 * Enable the write side of the partner. This triggers the partner to send 947 * messages queued on its write side to this queue's read side. 948 */ 949 static void 950 zc_rsrv(queue_t *qp) 951 { 952 zc_state_t *zcs; 953 zcs = (zc_state_t *)qp->q_ptr; 954 955 /* 956 * Care must be taken here, as either of the master or slave side 957 * qptr could be NULL. 958 */ 959 ASSERT(qp == zcs->zc_master_rdq || qp == zcs->zc_slave_rdq); 960 if (zc_switch(qp) == NULL) { 961 DBG("zc_rsrv: other side isn't listening\n"); 962 return; 963 } 964 qenable(WR(zc_switch(qp))); 965 } 966 967 /* 968 * This routine is symmetric for master and slave, so it handles both without 969 * splitting up the codepath. 970 * 971 * If there are messages on this queue that can be sent to the other, send 972 * them via putnext(). Else, if queued messages cannot be sent, leave them 973 * on this queue. 974 */ 975 static void 976 zc_wsrv(queue_t *qp) 977 { 978 mblk_t *mp; 979 980 DBG1("zc_wsrv master (%s) side", zc_side(qp)); 981 982 /* 983 * Partner has no read queue, so take the data, and throw it away. 984 */ 985 if (zc_switch(RD(qp)) == NULL) { 986 DBG("zc_wsrv: other side isn't listening"); 987 while ((mp = getq(qp)) != NULL) { 988 if (mp->b_datap->db_type == M_IOCTL) 989 miocnak(qp, mp, 0, 0); 990 else 991 freemsg(mp); 992 } 993 flushq(qp, FLUSHALL); 994 return; 995 } 996 997 /* 998 * while there are messages on this write queue... 999 */ 1000 while ((mp = getq(qp)) != NULL) { 1001 /* 1002 * Due to the way zc_wput is implemented, we should never 1003 * see a control message here. 1004 */ 1005 ASSERT(mp->b_datap->db_type < QPCTL); 1006 1007 if (bcanputnext(RD(zc_switch(qp)), mp->b_band)) { 1008 DBG("wsrv: send message to other side\n"); 1009 putnext(RD(zc_switch(qp)), mp); 1010 } else { 1011 DBG("wsrv: putting msg back on queue\n"); 1012 (void) putbq(qp, mp); 1013 break; 1014 } 1015 } 1016 } 1017