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 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 
  27 /*
  28  * Description: logindmux.c
  29  *
  30  * The logindmux driver is used with login modules (like telmod/rlmod).
  31  * This is a 1x1 cloning mux and two of these muxes are used. The lower link
  32  * of one of the muxes receives input from net and the lower link of the
  33  * other mux receives input from pseudo terminal subsystem.
  34  *
  35  * The logdmux_qexch_lock mutex manages the race between LOGDMX_IOC_QEXCHANGE,
  36  * logdmuxunlink() and logdmuxclose(), so that the instance selected as a peer
  37  * in LOGDMX_IOC_QEXCHANGE cannot be unlinked or closed until the qexchange
  38  * is complete; see the inline comments in the code for details.
  39  *
  40  * The logdmux_peerq_lock mutex manages the race between logdmuxlwsrv() and
  41  * logdmuxlrput() (when null'ing tmxp->peerq during LOGDMUX_UNLINK_REQ
  42  * processing).
  43  *
  44  * The logdmux_minor_lock mutex serializes the growth of logdmux_minor_arena
  45  * (the arena is grown gradually rather than allocated all at once so that
  46  * minor numbers are recycled sooner; for simplicity it is never shrunk).
  47  *
  48  * The unlink operation is implemented using protocol messages that flow
  49  * between the two logindmux peer instances. The instance processing the
  50  * I_UNLINK ioctl will send a LOGDMUX_UNLINK_REQ protocol message to its
  51  * peer to indicate that it wishes to unlink; the peer will process this
  52  * message in its lrput, null its tmxp->peerq and then send a
  53  * LOGDMUX_UNLINK_RESP protocol message in reply to indicate that the
  54  * unlink can proceed; having received the reply in its lrput, the
  55  * instance processing the I_UNLINK can then continue. To ensure that only
  56  * one of the peer instances will be actively processing an I_UNLINK at
  57  * any one time, a single structure (an unlinkinfo_t containing a mutex,
  58  * state variable and pointer to an M_CTL mblk) is allocated during
  59  * the processing of the LOGDMX_IOC_QEXCHANGE ioctl. The two instances, if
  60  * trying to unlink simultaneously, will race to get control of this
  61  * structure which contains the resources necessary to process the
  62  * I_UNLINK. The instance that wins this race will be able to continue
  63  * with the unlink whilst the other instance will be obliged to wait.
  64  */
  65 
  66 #include <sys/types.h>
  67 #include <sys/param.h>
  68 #include <sys/errno.h>
  69 #include <sys/debug.h>
  70 #include <sys/stropts.h>
  71 #include <sys/stream.h>
  72 #include <sys/logindmux.h>
  73 #include <sys/logindmux_impl.h>
  74 #include <sys/stat.h>
  75 #include <sys/kmem.h>
  76 #include <sys/vmem.h>
  77 #include <sys/strsun.h>
  78 #include <sys/sysmacros.h>
  79 #include <sys/mkdev.h>
  80 #include <sys/ddi.h>
  81 #include <sys/sunddi.h>
  82 #include <sys/modctl.h>
  83 #include <sys/termios.h>
  84 #include <sys/cmn_err.h>
  85 
  86 static int logdmuxopen(queue_t *, dev_t *, int, int, cred_t *);
  87 static int logdmuxclose(queue_t *, int, cred_t *);
  88 static int logdmuxursrv(queue_t *);
  89 static int logdmuxuwput(queue_t *, mblk_t *);
  90 static int logdmuxlrput(queue_t *, mblk_t *);
  91 static int logdmuxlrsrv(queue_t *);
  92 static int logdmuxlwsrv(queue_t *);
  93 static int logdmuxuwsrv(queue_t *);
  94 static int logdmux_alloc_unlinkinfo(struct tmx *, struct tmx *);
  95 
  96 static void logdmuxlink(queue_t *, mblk_t *);
  97 static void logdmuxunlink(queue_t *, mblk_t *);
  98 static void logdmux_finish_unlink(queue_t *, mblk_t *);
  99 static void logdmux_unlink_timer(void *arg);
 100 static void recover(queue_t *, mblk_t *, size_t);
 101 static void flushq_dataonly(queue_t *);
 102 
 103 static kmutex_t logdmux_qexch_lock;
 104 static kmutex_t logdmux_peerq_lock;
 105 static kmutex_t logdmux_minor_lock;
 106 static minor_t  logdmux_maxminor = 256; /* grown as necessary */
 107 static vmem_t   *logdmux_minor_arena;
 108 static void     *logdmux_statep;
 109 
 110 static struct module_info logdmuxm_info = {
 111         LOGDMX_ID,
 112         "logindmux",
 113         0,
 114         256,
 115         512,
 116         256
 117 };
 118 
 119 static struct qinit logdmuxurinit = {
 120         NULL,
 121         logdmuxursrv,
 122         logdmuxopen,
 123         logdmuxclose,
 124         NULL,
 125         &logdmuxm_info
 126 };
 127 
 128 static struct qinit logdmuxuwinit = {
 129         logdmuxuwput,
 130         logdmuxuwsrv,
 131         NULL,
 132         NULL,
 133         NULL,
 134         &logdmuxm_info
 135 };
 136 
 137 static struct qinit logdmuxlrinit = {
 138         logdmuxlrput,
 139         logdmuxlrsrv,
 140         NULL,
 141         NULL,
 142         NULL,
 143         &logdmuxm_info
 144 };
 145 
 146 static struct qinit logdmuxlwinit = {
 147         NULL,
 148         logdmuxlwsrv,
 149         NULL,
 150         NULL,
 151         NULL,
 152         &logdmuxm_info
 153 };
 154 
 155 struct streamtab logdmuxinfo = {
 156         &logdmuxurinit,
 157         &logdmuxuwinit,
 158         &logdmuxlrinit,
 159         &logdmuxlwinit
 160 };
 161 
 162 static int logdmux_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
 163 static int logdmux_attach(dev_info_t *, ddi_attach_cmd_t);
 164 static int logdmux_detach(dev_info_t *, ddi_detach_cmd_t);
 165 static dev_info_t *logdmux_dip;
 166 
 167 DDI_DEFINE_STREAM_OPS(logdmux_ops, nulldev, nulldev, logdmux_attach,
 168     logdmux_detach, nulldev, logdmux_info, D_MP | D_MTPERQ, &logdmuxinfo,
 169     ddi_quiesce_not_needed);
 170 
 171 static struct modldrv modldrv = {
 172         &mod_driverops,
 173         "logindmux driver",
 174         &logdmux_ops
 175 };
 176 
 177 static struct modlinkage modlinkage = {
 178         MODREV_1, { &modldrv, NULL }
 179 };
 180 
 181 int
 182 _init(void)
 183 {
 184         int     ret;
 185 
 186         mutex_init(&logdmux_peerq_lock, NULL, MUTEX_DRIVER, NULL);
 187         mutex_init(&logdmux_qexch_lock, NULL, MUTEX_DRIVER, NULL);
 188 
 189         if ((ret = mod_install(&modlinkage)) != 0) {
 190                 mutex_destroy(&logdmux_peerq_lock);
 191                 mutex_destroy(&logdmux_qexch_lock);
 192                 return (ret);
 193         }
 194 
 195         logdmux_minor_arena = vmem_create("logdmux_minor", (void *)1,
 196             logdmux_maxminor, 1, NULL, NULL, NULL, 0,
 197             VM_SLEEP | VMC_IDENTIFIER);
 198         (void) ddi_soft_state_init(&logdmux_statep, sizeof (struct tmx), 1);
 199 
 200         return (0);
 201 }
 202 
 203 int
 204 _fini(void)
 205 {
 206         int     ret;
 207 
 208         if ((ret = mod_remove(&modlinkage)) == 0) {
 209                 mutex_destroy(&logdmux_peerq_lock);
 210                 mutex_destroy(&logdmux_qexch_lock);
 211                 ddi_soft_state_fini(&logdmux_statep);
 212                 vmem_destroy(logdmux_minor_arena);
 213                 logdmux_minor_arena = NULL;
 214         }
 215 
 216         return (ret);
 217 }
 218 
 219 int
 220 _info(struct modinfo *modinfop)
 221 {
 222         return (mod_info(&modlinkage, modinfop));
 223 }
 224 
 225 static int
 226 logdmux_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
 227 {
 228         if (cmd != DDI_ATTACH)
 229                 return (DDI_FAILURE);
 230 
 231         if (ddi_create_minor_node(devi, "logindmux", S_IFCHR, 0, DDI_PSEUDO,
 232             CLONE_DEV) == DDI_FAILURE)
 233                 return (DDI_FAILURE);
 234 
 235         logdmux_dip = devi;
 236         return (DDI_SUCCESS);
 237 }
 238 
 239 static int
 240 logdmux_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
 241 {
 242         if (cmd != DDI_DETACH)
 243                 return (DDI_FAILURE);
 244 
 245         ddi_remove_minor_node(devi, NULL);
 246         return (DDI_SUCCESS);
 247 }
 248 
 249 /* ARGSUSED */
 250 static int
 251 logdmux_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
 252 {
 253         int error;
 254 
 255         switch (infocmd) {
 256         case DDI_INFO_DEVT2DEVINFO:
 257                 if (logdmux_dip == NULL) {
 258                         error = DDI_FAILURE;
 259                 } else {
 260                         *result = logdmux_dip;
 261                         error = DDI_SUCCESS;
 262                 }
 263                 break;
 264         case DDI_INFO_DEVT2INSTANCE:
 265                 *result = (void *)0;
 266                 error = DDI_SUCCESS;
 267                 break;
 268         default:
 269                 error = DDI_FAILURE;
 270         }
 271         return (error);
 272 }
 273 
 274 /*
 275  * Logindmux open routine
 276  */
 277 /*ARGSUSED*/
 278 static int
 279 logdmuxopen(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
 280 {
 281         struct  tmx *tmxp;
 282         minor_t minor, omaxminor;
 283 
 284         if (sflag != CLONEOPEN)
 285                 return (EINVAL);
 286 
 287         mutex_enter(&logdmux_minor_lock);
 288         if (vmem_size(logdmux_minor_arena, VMEM_FREE) == 0) {
 289                 /*
 290                  * The arena has been exhausted; grow by powers of two
 291                  * up to MAXMIN; bail if we've run out of minors.
 292                  */
 293                 if (logdmux_maxminor == MAXMIN) {
 294                         mutex_exit(&logdmux_minor_lock);
 295                         return (ENOMEM);
 296                 }
 297 
 298                 omaxminor = logdmux_maxminor;
 299                 logdmux_maxminor = MIN(logdmux_maxminor << 1, MAXMIN);
 300 
 301                 (void) vmem_add(logdmux_minor_arena,
 302                     (void *)(uintptr_t)(omaxminor + 1),
 303                     logdmux_maxminor - omaxminor, VM_SLEEP);
 304         }
 305         minor = (minor_t)(uintptr_t)
 306             vmem_alloc(logdmux_minor_arena, 1, VM_SLEEP);
 307         mutex_exit(&logdmux_minor_lock);
 308 
 309         if (ddi_soft_state_zalloc(logdmux_statep, minor) == DDI_FAILURE) {
 310                 vmem_free(logdmux_minor_arena, (void *)(uintptr_t)minor, 1);
 311                 return (ENOMEM);
 312         }
 313 
 314         tmxp = ddi_get_soft_state(logdmux_statep, minor);
 315         tmxp->rdq = q;
 316         tmxp->muxq = NULL;
 317         tmxp->peerq = NULL;
 318         tmxp->unlinkinfop = NULL;
 319         tmxp->dev0 = minor;
 320 
 321         *devp = makedevice(getmajor(*devp), tmxp->dev0);
 322         q->q_ptr = tmxp;
 323         WR(q)->q_ptr = tmxp;
 324 
 325         qprocson(q);
 326         return (0);
 327 }
 328 
 329 /*
 330  * Logindmux close routine gets called when telnet connection is closed
 331  */
 332 /*ARGSUSED*/
 333 static int
 334 logdmuxclose(queue_t *q, int flag, cred_t *crp)
 335 {
 336         struct tmx      *tmxp = q->q_ptr;
 337         minor_t         minor = tmxp->dev0;
 338 
 339         ASSERT(tmxp->muxq == NULL);
 340         ASSERT(tmxp->peerq == NULL);
 341 
 342         qprocsoff(q);
 343         if (tmxp->wbufcid != 0) {
 344                 qunbufcall(q, tmxp->wbufcid);
 345                 tmxp->wbufcid = 0;
 346         }
 347         if (tmxp->rbufcid != 0) {
 348                 qunbufcall(q, tmxp->rbufcid);
 349                 tmxp->rbufcid = 0;
 350         }
 351         if (tmxp->rtimoutid != 0) {
 352                 (void) quntimeout(q, tmxp->rtimoutid);
 353                 tmxp->rtimoutid = 0;
 354         }
 355         if (tmxp->wtimoutid != 0) {
 356                 (void) quntimeout(q, tmxp->wtimoutid);
 357                 tmxp->wtimoutid = 0;
 358         }
 359         if (tmxp->utimoutid != 0) {
 360                 (void) quntimeout(q, tmxp->utimoutid);
 361                 tmxp->utimoutid = 0;
 362         }
 363 
 364         /*
 365          * Hold logdmux_qexch_lock to prevent another thread that might be
 366          * in LOGDMX_IOC_QEXCHANGE from looking up our state while we're
 367          * disposing of it.
 368          */
 369         mutex_enter(&logdmux_qexch_lock);
 370         ddi_soft_state_free(logdmux_statep, minor);
 371         vmem_free(logdmux_minor_arena, (void *)(uintptr_t)minor, 1);
 372         mutex_exit(&logdmux_qexch_lock);
 373 
 374         q->q_ptr = NULL;
 375         WR(q)->q_ptr = NULL;
 376 
 377         return (0);
 378 }
 379 
 380 /*
 381  * Upper read service routine
 382  */
 383 static int
 384 logdmuxursrv(queue_t *q)
 385 {
 386         struct tmx *tmxp = q->q_ptr;
 387 
 388         if (tmxp->muxq != NULL)
 389                 qenable(RD(tmxp->muxq));
 390         return (0);
 391 }
 392 
 393 /*
 394  * This routine gets called when telnet daemon sends data or ioctl messages
 395  * to upper mux queue.
 396  */
 397 static int
 398 logdmuxuwput(queue_t *q, mblk_t *mp)
 399 {
 400         queue_t         *qp;
 401         mblk_t          *newmp;
 402         struct iocblk   *ioc;
 403         minor_t         minor;
 404         STRUCT_HANDLE(protocol_arg, protoh);
 405         struct tmx      *tmxp, *tmxpeerp;
 406         int             error;
 407 
 408         tmxp = q->q_ptr;
 409 
 410         switch (mp->b_datap->db_type) {
 411 
 412         case M_IOCTL:
 413                 ASSERT(MBLKL(mp) == sizeof (struct iocblk));
 414 
 415                 ioc = (struct iocblk *)mp->b_rptr;
 416                 switch (ioc->ioc_cmd) {
 417                 /*
 418                  * This is a special ioctl which exchanges q info
 419                  * of the two peers, connected to netf and ptmx.
 420                  */
 421                 case LOGDMX_IOC_QEXCHANGE:
 422                         error = miocpullup(mp,
 423                             SIZEOF_STRUCT(protocol_arg, ioc->ioc_flag));
 424                         if (error != 0) {
 425                                 miocnak(q, mp, 0, error);
 426                                 break;
 427                         }
 428                         STRUCT_SET_HANDLE(protoh, ioc->ioc_flag,
 429                             (struct protocol_arg *)mp->b_cont->b_rptr);
 430 #ifdef _SYSCALL32_IMPL
 431                         if ((ioc->ioc_flag & DATAMODEL_MASK) ==
 432                             DATAMODEL_ILP32) {
 433                                 minor = getminor(expldev(
 434                                     STRUCT_FGET(protoh, dev)));
 435                         } else
 436 #endif
 437                         {
 438                                 minor = getminor(STRUCT_FGET(protoh, dev));
 439                         }
 440 
 441                         /*
 442                          * The second argument to ddi_get_soft_state() is
 443                          * interpreted as an `int', so prohibit negative
 444                          * values.
 445                          */
 446                         if ((int)minor < 0) {
 447                                 miocnak(q, mp, 0, EINVAL);
 448                                 break;
 449                         }
 450 
 451                         /*
 452                          * We must hold logdmux_qexch_lock while looking up
 453                          * the proposed peer to prevent another thread from
 454                          * simultaneously I_UNLINKing or closing it.
 455                          */
 456                         mutex_enter(&logdmux_qexch_lock);
 457 
 458                         /*
 459                          * For LOGDMX_IOC_QEXCHANGE to succeed, our peer must
 460                          * exist (and not be us), and both we and our peer
 461                          * must be I_LINKed (i.e., muxq must not be NULL) and
 462                          * not already have a peer.
 463                          */
 464                         tmxpeerp = ddi_get_soft_state(logdmux_statep, minor);
 465                         if (tmxpeerp == NULL || tmxpeerp == tmxp ||
 466                             tmxpeerp->muxq == NULL || tmxpeerp->peerq != NULL ||
 467                             tmxp->muxq == NULL || tmxp->peerq != NULL) {
 468                                 mutex_exit(&logdmux_qexch_lock);
 469                                 miocnak(q, mp, 0, EINVAL);
 470                                 break;
 471                         }
 472 
 473                         /*
 474                          * If `flag' is set then exchange queues and assume
 475                          * tmxp refers to the ptmx stream.
 476                          */
 477                         if (STRUCT_FGET(protoh, flag)) {
 478                                 /*
 479                                  * Allocate and populate the structure we
 480                                  * need when processing an I_UNLINK ioctl.
 481                                  * Give both logindmux instances a pointer
 482                                  * to it from their tmx structure.
 483                                  */
 484                                 if ((error = logdmux_alloc_unlinkinfo(
 485                                     tmxp, tmxpeerp)) != 0) {
 486                                         mutex_exit(&logdmux_qexch_lock);
 487                                         miocnak(q, mp, 0, error);
 488                                         break;
 489                                 }
 490                                 tmxp->peerq = tmxpeerp->muxq;
 491                                 tmxpeerp->peerq = tmxp->muxq;
 492                                 tmxp->isptm = B_TRUE;
 493                         }
 494                         mutex_exit(&logdmux_qexch_lock);
 495                         miocack(q, mp, 0, 0);
 496                         break;
 497 
 498                 case I_LINK:
 499                         ASSERT(MBLKL(mp->b_cont) == sizeof (struct linkblk));
 500                         logdmuxlink(q, mp);
 501                         break;
 502 
 503                 case I_UNLINK:
 504                         ASSERT(MBLKL(mp->b_cont) == sizeof (struct linkblk));
 505                         logdmuxunlink(q, mp);
 506                         break;
 507 
 508                 default:
 509                         if (tmxp->muxq == NULL) {
 510                                 miocnak(q, mp, 0, EINVAL);
 511                                 return (0);
 512                         }
 513                         putnext(tmxp->muxq, mp);
 514                         break;
 515                 }
 516 
 517                 break;
 518 
 519         case M_DATA:
 520                 if (!tmxp->isptm) {
 521                         if ((newmp = allocb(sizeof (char), BPRI_MED)) == NULL) {
 522                                 recover(q, mp, sizeof (char));
 523                                 return (0);
 524                         }
 525                         newmp->b_datap->db_type = M_CTL;
 526                         *newmp->b_wptr++ = M_CTL_MAGIC_NUMBER;
 527                         newmp->b_cont = mp;
 528                         mp = newmp;
 529                 }
 530                 /* FALLTHRU */
 531 
 532         case M_PROTO:
 533         case M_PCPROTO:
 534                 qp = tmxp->muxq;
 535                 if (qp == NULL) {
 536                         merror(q, mp, EINVAL);
 537                         return (0);
 538                 }
 539 
 540                 if (queclass(mp) < QPCTL) {
 541                         if (q->q_first != NULL || !canputnext(qp)) {
 542                                 (void) putq(q, mp);
 543                                 return (0);
 544                         }
 545                 }
 546                 putnext(qp, mp);
 547                 break;
 548 
 549         case M_FLUSH:
 550                 if (*mp->b_rptr & FLUSHW)
 551                         flushq(q, FLUSHALL);
 552 
 553                 if (tmxp->muxq != NULL) {
 554                         putnext(tmxp->muxq, mp);
 555                         return (0);
 556                 }
 557 
 558                 *mp->b_rptr &= ~FLUSHW;
 559                 if (*mp->b_rptr & FLUSHR)
 560                         qreply(q, mp);
 561                 else
 562                         freemsg(mp);
 563                 break;
 564 
 565         default:
 566                 cmn_err(CE_NOTE, "logdmuxuwput: received unexpected message"
 567                     " of type 0x%x", mp->b_datap->db_type);
 568                 freemsg(mp);
 569         }
 570         return (0);
 571 }
 572 
 573 /*
 574  * Upper write service routine
 575  */
 576 static int
 577 logdmuxuwsrv(queue_t *q)
 578 {
 579         mblk_t          *mp, *newmp;
 580         queue_t         *qp;
 581         struct tmx      *tmxp = q->q_ptr;
 582 
 583         while ((mp = getq(q)) != NULL) {
 584                 switch (mp->b_datap->db_type) {
 585                 case M_DATA:
 586                         if (!tmxp->isptm) {
 587                                 if ((newmp = allocb(sizeof (char), BPRI_MED)) ==
 588                                     NULL) {
 589                                         recover(q, mp, sizeof (char));
 590                                         return (0);
 591                                 }
 592                                 newmp->b_datap->db_type = M_CTL;
 593                                 *newmp->b_wptr++ = M_CTL_MAGIC_NUMBER;
 594                                 newmp->b_cont = mp;
 595                                 mp = newmp;
 596                         }
 597                         /* FALLTHRU */
 598 
 599                 case M_CTL:
 600                 case M_PROTO:
 601                         if (tmxp->muxq == NULL) {
 602                                 merror(q, mp, EIO);
 603                                 break;
 604                         }
 605                         qp = tmxp->muxq;
 606                         if (!canputnext(qp)) {
 607                                 (void) putbq(q, mp);
 608                                 return (0);
 609                         }
 610                         putnext(qp, mp);
 611                         break;
 612 
 613 
 614                 default:
 615                         cmn_err(CE_NOTE, "logdmuxuwsrv: received unexpected"
 616                             " message of type 0x%x", mp->b_datap->db_type);
 617                         freemsg(mp);
 618                 }
 619         }
 620         return (0);
 621 }
 622 
 623 /*
 624  * Logindmux lower put routine detects from which of the two lower queues
 625  * the data needs to be read from and writes it out to its peer queue.
 626  * For protocol, it detects M_CTL and sends its data to the daemon. Also,
 627  * for ioctl and other types of messages, it lets the daemon handle it.
 628  */
 629 static int
 630 logdmuxlrput(queue_t *q, mblk_t *mp)
 631 {
 632         mblk_t          *savemp;
 633         queue_t         *qp;
 634         struct iocblk   *ioc;
 635         struct tmx      *tmxp = q->q_ptr;
 636         uchar_t         flush;
 637         uint_t          *messagep;
 638         unlinkinfo_t    *unlinkinfop = tmxp->unlinkinfop;
 639 
 640         if (tmxp->muxq == NULL || tmxp->peerq == NULL) {
 641                 freemsg(mp);
 642                 return (0);
 643         }
 644 
 645         /*
 646          * If there's already a message on our queue and the incoming
 647          * message is not of a high-priority, enqueue the message --
 648          * but not if it's a logindmux protocol message.
 649          */
 650         if ((q->q_first != NULL) && (queclass(mp) < QPCTL) &&
 651             (!LOGDMUX_PROTO_MBLK(mp))) {
 652                 (void) putq(q, mp);
 653                 return (0);
 654         }
 655 
 656         switch (mp->b_datap->db_type) {
 657 
 658         case M_IOCTL:
 659                 ioc = (struct iocblk *)mp->b_rptr;
 660                 switch (ioc->ioc_cmd) {
 661 
 662                 case TIOCSWINSZ:
 663                 case TCSETAF:
 664                 case TCSETSF:
 665                 case TCSETA:
 666                 case TCSETAW:
 667                 case TCSETS:
 668                 case TCSETSW:
 669                 case TCSBRK:
 670                 case TIOCSTI:
 671                         qp = tmxp->peerq;
 672                         break;
 673 
 674                 default:
 675                         cmn_err(CE_NOTE, "logdmuxlrput: received unexpected"
 676                             " request for ioctl 0x%x", ioc->ioc_cmd);
 677 
 678                         /* NAK unrecognized ioctl's. */
 679                         miocnak(q, mp, 0, 0);
 680                         return (0);
 681                 }
 682                 break;
 683 
 684         case M_DATA:
 685         case M_HANGUP:
 686                 qp = tmxp->peerq;
 687                 break;
 688 
 689         case M_CTL:
 690                 /*
 691                  * The protocol messages that flow between the peers
 692                  * to implement the unlink functionality are M_CTLs
 693                  * which have the M_IOCTL/I_UNLINK mblk of the ioctl
 694                  * attached via b_cont.  LOGDMUX_PROTO_MBLK() uses
 695                  * this to determine whether a particular M_CTL is a
 696                  * peer protocol message.
 697                  */
 698                 if (LOGDMUX_PROTO_MBLK(mp)) {
 699                         messagep = (uint_t *)mp->b_rptr;
 700 
 701                         switch (*messagep) {
 702 
 703                         case LOGDMUX_UNLINK_REQ:
 704                                 /*
 705                                  * We've received a message from our
 706                                  * peer indicating that it wants to
 707                                  * unlink.
 708                                  */
 709                                 *messagep = LOGDMUX_UNLINK_RESP;
 710                                 qp = tmxp->peerq;
 711 
 712                                 mutex_enter(&logdmux_peerq_lock);
 713                                 tmxp->peerq = NULL;
 714                                 mutex_exit(&logdmux_peerq_lock);
 715 
 716                                 put(RD(qp), mp);
 717                                 return (0);
 718 
 719                         case LOGDMUX_UNLINK_RESP:
 720                                 /*
 721                                  * We've received a positive response
 722                                  * from our peer to an earlier
 723                                  * LOGDMUX_UNLINK_REQ that we sent.
 724                                  * We can now carry on with the unlink.
 725                                  */
 726                                 qp = tmxp->rdq;
 727                                 mutex_enter(&unlinkinfop->state_lock);
 728                                 ASSERT(unlinkinfop->state ==
 729                                     LOGDMUX_UNLINK_PENDING);
 730                                 unlinkinfop->state = LOGDMUX_UNLINKED;
 731                                 mutex_exit(&unlinkinfop->state_lock);
 732                                 logdmux_finish_unlink(WR(qp), mp->b_cont);
 733                                 return (0);
 734                         }
 735                 }
 736 
 737                 qp = tmxp->rdq;
 738                 if (q->q_first != NULL || !canputnext(qp)) {
 739                         (void) putq(q, mp);
 740                         return (0);
 741                 }
 742                 if ((MBLKL(mp) == 1) && (*mp->b_rptr == M_CTL_MAGIC_NUMBER)) {
 743                         savemp = mp->b_cont;
 744                         freeb(mp);
 745                         mp = savemp;
 746                 }
 747                 putnext(qp, mp);
 748                 return (0);
 749 
 750         case M_IOCACK:
 751         case M_IOCNAK:
 752         case M_PROTO:
 753         case M_PCPROTO:
 754         case M_PCSIG:
 755         case M_SETOPTS:
 756                 qp = tmxp->rdq;
 757                 break;
 758 
 759         case M_ERROR:
 760                 if (tmxp->isptm) {
 761                         /*
 762                          * This error is from ptm.  We could tell TCP to
 763                          * shutdown the connection, but it's easier to just
 764                          * wait for the daemon to get SIGCHLD and close from
 765                          * above.
 766                          */
 767                         freemsg(mp);
 768                         return (0);
 769                 }
 770                 /*
 771                  * This is from TCP.  Don't really know why we'd
 772                  * get this, but we have a pretty good idea what
 773                  * to do:  Send M_HANGUP to the pty.
 774                  */
 775                 mp->b_datap->db_type = M_HANGUP;
 776                 mp->b_wptr = mp->b_rptr;
 777                 qp = tmxp->peerq;
 778                 break;
 779 
 780         case M_FLUSH:
 781                 if (*mp->b_rptr & FLUSHR)
 782                         flushq_dataonly(q);
 783 
 784                 if (mp->b_flag & MSGMARK) {
 785                         /*
 786                          * This M_FLUSH has been marked by the module
 787                          * below as intended for the upper queue,
 788                          * not the peer queue.
 789                          */
 790                         qp = tmxp->rdq;
 791                         mp->b_flag &= ~MSGMARK;
 792                 } else {
 793                         /*
 794                          * Wrap this M_FLUSH through the mux.
 795                          * The FLUSHR and FLUSHW bits must be
 796                          * reversed.
 797                          */
 798                         qp = tmxp->peerq;
 799                         flush = *mp->b_rptr;
 800                         *mp->b_rptr &= ~(FLUSHR | FLUSHW);
 801                         if (flush & FLUSHW)
 802                                 *mp->b_rptr |= FLUSHR;
 803                         if (flush & FLUSHR)
 804                                 *mp->b_rptr |= FLUSHW;
 805                 }
 806                 break;
 807 
 808         case M_START:
 809         case M_STOP:
 810         case M_STARTI:
 811         case M_STOPI:
 812                 freemsg(mp);
 813                 return (0);
 814 
 815         default:
 816                 cmn_err(CE_NOTE, "logdmuxlrput: received unexpected "
 817                     "message of type 0x%x", mp->b_datap->db_type);
 818                 freemsg(mp);
 819                 return (0);
 820         }
 821         if (queclass(mp) < QPCTL) {
 822                 if (q->q_first != NULL || !canputnext(qp)) {
 823                         (void) putq(q, mp);
 824                         return (0);
 825                 }
 826         }
 827         putnext(qp, mp);
 828         return (0);
 829 }
 830 
 831 /*
 832  * Lower read service routine
 833  */
 834 static int
 835 logdmuxlrsrv(queue_t *q)
 836 {
 837         mblk_t          *mp, *savemp;
 838         queue_t         *qp;
 839         struct iocblk   *ioc;
 840         struct tmx      *tmxp = q->q_ptr;
 841 
 842         while ((mp = getq(q)) != NULL) {
 843                 if (tmxp->muxq == NULL || tmxp->peerq == NULL) {
 844                         freemsg(mp);
 845                         continue;
 846                 }
 847 
 848                 switch (mp->b_datap->db_type) {
 849 
 850                 case M_IOCTL:
 851                         ioc = (struct iocblk *)mp->b_rptr;
 852 
 853                         switch (ioc->ioc_cmd) {
 854 
 855                         case TIOCSWINSZ:
 856                         case TCSETAF:
 857                         case TCSETSF:
 858                         case TCSETA:
 859                         case TCSETAW:
 860                         case TCSETS:
 861                         case TCSETSW:
 862                         case TCSBRK:
 863                         case TIOCSTI:
 864                                 qp = tmxp->peerq;
 865                                 break;
 866 
 867                         default:
 868                                 cmn_err(CE_NOTE, "logdmuxlrsrv: received "
 869                                     "unexpected request for ioctl 0x%x",
 870                                     ioc->ioc_cmd);
 871 
 872                                 /* NAK unrecognized ioctl's. */
 873                                 miocnak(q, mp, 0, 0);
 874                                 continue;
 875                         }
 876                         break;
 877 
 878                 case M_DATA:
 879                 case M_HANGUP:
 880                         qp = tmxp->peerq;
 881                         break;
 882 
 883                 case M_CTL:
 884                         qp = tmxp->rdq;
 885                         if (!canputnext(qp)) {
 886                                 (void) putbq(q, mp);
 887                                 return (0);
 888                         }
 889                         if (MBLKL(mp) == 1 &&
 890                             (*mp->b_rptr == M_CTL_MAGIC_NUMBER)) {
 891                                 savemp = mp->b_cont;
 892                                 freeb(mp);
 893                                 mp = savemp;
 894                         }
 895                         putnext(qp, mp);
 896                         continue;
 897 
 898                 case M_PROTO:
 899                 case M_SETOPTS:
 900                         qp = tmxp->rdq;
 901                         break;
 902 
 903                 default:
 904                         cmn_err(CE_NOTE, "logdmuxlrsrv: received unexpected "
 905                             "message of type 0x%x", mp->b_datap->db_type);
 906                         freemsg(mp);
 907                         continue;
 908                 }
 909                 ASSERT(queclass(mp) < QPCTL);
 910                 if (!canputnext(qp)) {
 911                         (void) putbq(q, mp);
 912                         return (0);
 913                 }
 914                 putnext(qp, mp);
 915         }
 916         return (0);
 917 }
 918 
 919 /*
 920  * Lower side write service procedure.  No messages are ever placed on
 921  * the write queue here, this just back-enables all of the upper side
 922  * write service procedures.
 923  */
 924 static int
 925 logdmuxlwsrv(queue_t *q)
 926 {
 927         struct tmx *tmxp = q->q_ptr;
 928 
 929         /*
 930          * Qenable upper write queue and find out which lower
 931          * queue needs to be restarted with flow control.
 932          * Qenable the peer queue so canputnext will
 933          * succeed on next call to logdmuxlrput.
 934          */
 935         qenable(WR(tmxp->rdq));
 936 
 937         mutex_enter(&logdmux_peerq_lock);
 938         if (tmxp->peerq != NULL)
 939                 qenable(RD(tmxp->peerq));
 940         mutex_exit(&logdmux_peerq_lock);
 941 
 942         return (0);
 943 }
 944 
 945 /*
 946  * This routine does I_LINK operation.
 947  */
 948 static void
 949 logdmuxlink(queue_t *q, mblk_t *mp)
 950 {
 951         struct tmx      *tmxp = q->q_ptr;
 952         struct linkblk  *lp = (struct linkblk *)mp->b_cont->b_rptr;
 953 
 954         /*
 955          * Fail if we're already linked.
 956          */
 957         if (tmxp->muxq != NULL) {
 958                 miocnak(q, mp, 0, EINVAL);
 959                 return;
 960         }
 961 
 962         tmxp->muxq = lp->l_qbot;
 963         tmxp->muxq->q_ptr = tmxp;
 964         RD(tmxp->muxq)->q_ptr = tmxp;
 965 
 966         miocack(q, mp, 0, 0);
 967 }
 968 
 969 /*
 970  * logdmuxunlink() is called from logdmuxuwput() and is the first of two
 971  * functions which process an I_UNLINK ioctl. logdmuxunlink() will determine
 972  * the state of logindmux peer linkage and, based on this, control when the
 973  * second function, logdmux_finish_unlink(), is called.  It's
 974  * logdmux_finish_unlink() that's sending the M_IOCACK upstream and
 975  * resetting the link state.
 976  */
 977 static void
 978 logdmuxunlink(queue_t *q, mblk_t *mp)
 979 {
 980         struct tmx      *tmxp = q->q_ptr;
 981         unlinkinfo_t    *unlinkinfop;
 982 
 983         /*
 984          * If we don't have a peer, just unlink.  Note that this check needs
 985          * to be done under logdmux_qexch_lock to prevent racing with
 986          * LOGDMX_IOC_QEXCHANGE, and we *must* set muxq to NULL prior to
 987          * releasing the lock so that LOGDMX_IOC_QEXCHANGE will not consider
 988          * us as a possible peer anymore (if it already considers us to be a
 989          * peer, then unlinkinfop will not be NULL) -- NULLing muxq precludes
 990          * use of logdmux_finish_unlink() here.
 991          */
 992         mutex_enter(&logdmux_qexch_lock);
 993         unlinkinfop = tmxp->unlinkinfop;
 994         if (unlinkinfop == NULL) {
 995                 ASSERT(tmxp->peerq == NULL);
 996                 tmxp->muxq = NULL;
 997                 mutex_exit(&logdmux_qexch_lock);
 998                 miocack(q, mp, 0, 0);
 999                 return;
1000         }
1001         mutex_exit(&logdmux_qexch_lock);
1002 
1003         mutex_enter(&unlinkinfop->state_lock);
1004 
1005         switch (unlinkinfop->state) {
1006 
1007         case LOGDMUX_LINKED:
1008                 /*
1009                  * We're the first instance to process an I_UNLINK --
1010                  * ie, the peer instance is still there. We'll change
1011                  * the state so that only one instance is executing an
1012                  * I_UNLINK at any one time.
1013                  */
1014                 unlinkinfop->state = LOGDMUX_UNLINK_PENDING;
1015                 mutex_exit(&unlinkinfop->state_lock);
1016                 /*
1017                  * Attach the original M_IOCTL message to a
1018                  * LOGDMUX_UNLINK_REQ message and send it to our peer to
1019                  * tell it to unlink from us. When it has completed the
1020                  * task, it will send us a LOGDMUX_UNLINK_RESP message
1021                  * with the original M_IOCTL still attached, which will be
1022                  * processed in our logdmuxlrput(). At that point, we will
1023                  * call logdmux_finish_unlink() to complete the unlink
1024                  * operation using the attached M_IOCTL.
1025                  */
1026                 unlinkinfop->prot_mp->b_cont = mp;
1027                 /*
1028                  * Put the M_CTL directly to the peer's lower RQ.
1029                  */
1030                 put(RD(tmxp->peerq), unlinkinfop->prot_mp);
1031                 break;
1032 
1033         case LOGDMUX_UNLINK_PENDING:
1034                 mutex_exit(&unlinkinfop->state_lock);
1035                 /*
1036                  * Our peer is actively processing an I_UNLINK itself.
1037                  * We have to wait for the peer to complete and we use
1038                  * qtimeout as a way to poll for its completion.
1039                  * We save a reference to our mblk so that we can send
1040                  * it upstream once our peer is done.
1041                  */
1042                 tmxp->unlink_mp = mp;
1043                 tmxp->utimoutid = qtimeout(q, logdmux_unlink_timer, q,
1044                     drv_usectohz(LOGDMUX_POLL_WAIT));
1045                 break;
1046 
1047         case LOGDMUX_UNLINKED:
1048                 /*
1049                  * Our peer is no longer linked so we can proceed.
1050                  */
1051                 mutex_exit(&unlinkinfop->state_lock);
1052                 mutex_destroy(&unlinkinfop->state_lock);
1053                 freeb(unlinkinfop->prot_mp);
1054                 kmem_free(unlinkinfop, sizeof (unlinkinfo_t));
1055                 logdmux_finish_unlink(q, mp);
1056                 break;
1057 
1058         default:
1059                 mutex_exit(&unlinkinfop->state_lock);
1060                 cmn_err(CE_PANIC,
1061                     "logdmuxunlink: peer linkage is in an unrecognized state");
1062                 break;
1063         }
1064 }
1065 
1066 /*
1067  * Finish the unlink operation.  Note that no locks should be held since
1068  * this routine calls into other queues.
1069  */
1070 static void
1071 logdmux_finish_unlink(queue_t *q, mblk_t *unlink_mp)
1072 {
1073         struct tmx *tmxp = q->q_ptr;
1074         mblk_t *mp;
1075 
1076         /*
1077          * Flush any write side data downstream.
1078          */
1079         while ((mp = getq(WR(q))) != NULL)
1080                 putnext(tmxp->muxq, mp);
1081 
1082         /*
1083          * Note that we do not NULL out q_ptr since another thread (e.g., a
1084          * STREAMS service thread) might call logdmuxlrput() between the time
1085          * we exit the logindmux perimeter and the time the STREAMS framework
1086          * resets q_ptr to stdata (since muxq is set to NULL, any messages
1087          * will just be discarded).
1088          */
1089         tmxp->muxq = NULL;
1090         tmxp->unlinkinfop = NULL;
1091         tmxp->peerq = NULL;
1092         miocack(q, unlink_mp, 0, 0);
1093 }
1094 
1095 /*
1096  * logdmux_unlink_timer() is executed by qtimeout(). This function will
1097  * check unlinkinfop->state to determine whether the peer has completed
1098  * its I_UNLINK. If it hasn't, we use qtimeout() to initiate another poll.
1099  */
1100 static void
1101 logdmux_unlink_timer(void *arg)
1102 {
1103         queue_t         *q = arg;
1104         struct  tmx     *tmxp = q->q_ptr;
1105         unlinkinfo_t    *unlinkinfop = tmxp->unlinkinfop;
1106 
1107         tmxp->utimoutid = 0;
1108 
1109         mutex_enter(&unlinkinfop->state_lock);
1110 
1111         if (unlinkinfop->state != LOGDMUX_UNLINKED) {
1112                 ASSERT(unlinkinfop->state == LOGDMUX_UNLINK_PENDING);
1113                 mutex_exit(&unlinkinfop->state_lock);
1114                 /*
1115                  * We need to wait longer for our peer to complete.
1116                  */
1117                 tmxp->utimoutid = qtimeout(q, logdmux_unlink_timer, q,
1118                     drv_usectohz(LOGDMUX_POLL_WAIT));
1119         } else {
1120                 /*
1121                  * Our peer is no longer linked so we can proceed with
1122                  * the cleanup.
1123                  */
1124                 mutex_exit(&unlinkinfop->state_lock);
1125                 mutex_destroy(&unlinkinfop->state_lock);
1126                 freeb(unlinkinfop->prot_mp);
1127                 kmem_free(unlinkinfop, sizeof (unlinkinfo_t));
1128                 logdmux_finish_unlink(q, tmxp->unlink_mp);
1129         }
1130 }
1131 
1132 static void
1133 logdmux_timer(void *arg)
1134 {
1135         queue_t         *q = arg;
1136         struct tmx      *tmxp = q->q_ptr;
1137 
1138         ASSERT(tmxp != NULL);
1139 
1140         if (q->q_flag & QREADR) {
1141                 ASSERT(tmxp->rtimoutid != 0);
1142                 tmxp->rtimoutid = 0;
1143         } else {
1144                 ASSERT(tmxp->wtimoutid != 0);
1145                 tmxp->wtimoutid = 0;
1146         }
1147         enableok(q);
1148         qenable(q);
1149 }
1150 
1151 static void
1152 logdmux_buffer(void *arg)
1153 {
1154         queue_t         *q = arg;
1155         struct tmx      *tmxp = q->q_ptr;
1156 
1157         ASSERT(tmxp != NULL);
1158 
1159         if (q->q_flag & QREADR) {
1160                 ASSERT(tmxp->rbufcid != 0);
1161                 tmxp->rbufcid = 0;
1162         } else {
1163                 ASSERT(tmxp->wbufcid != 0);
1164                 tmxp->wbufcid = 0;
1165         }
1166         enableok(q);
1167         qenable(q);
1168 }
1169 
1170 static void
1171 recover(queue_t *q, mblk_t *mp, size_t size)
1172 {
1173         timeout_id_t    tid;
1174         bufcall_id_t    bid;
1175         struct  tmx     *tmxp = q->q_ptr;
1176 
1177         /*
1178          * Avoid re-enabling the queue.
1179          */
1180         ASSERT(queclass(mp) < QPCTL);
1181         ASSERT(WR(q)->q_next == NULL); /* Called from upper queue only */
1182         noenable(q);
1183         (void) putbq(q, mp);
1184 
1185         /*
1186          * Make sure there is at most one outstanding request per queue.
1187          */
1188         if (q->q_flag & QREADR) {
1189                 if (tmxp->rtimoutid != 0 || tmxp->rbufcid != 0)
1190                         return;
1191         } else {
1192                 if (tmxp->wtimoutid != 0 || tmxp->wbufcid != 0)
1193                         return;
1194         }
1195         if (!(bid = qbufcall(RD(q), size, BPRI_MED, logdmux_buffer, q))) {
1196                 tid = qtimeout(RD(q), logdmux_timer, q, drv_usectohz(SIMWAIT));
1197                 if (q->q_flag & QREADR)
1198                         tmxp->rtimoutid = tid;
1199                 else
1200                         tmxp->wtimoutid = tid;
1201         } else  {
1202                 if (q->q_flag & QREADR)
1203                         tmxp->rbufcid = bid;
1204                 else
1205                         tmxp->wbufcid = bid;
1206         }
1207 }
1208 
1209 static void
1210 flushq_dataonly(queue_t *q)
1211 {
1212         mblk_t *mp, *nmp;
1213 
1214         /*
1215          * Since we are already in the perimeter, and we are not a put-shared
1216          * perimeter, we don't need to freeze the stream or anything to
1217          * be ensured of exclusivity.
1218          */
1219         mp = q->q_first;
1220         while (mp != NULL) {
1221                 if (mp->b_datap->db_type == M_DATA) {
1222                         nmp = mp->b_next;
1223                         rmvq(q, mp);
1224                         freemsg(mp);
1225                         mp = nmp;
1226                 } else {
1227                         mp = mp->b_next;
1228                 }
1229         }
1230 }
1231 
1232 /*
1233  * logdmux_alloc_unlinkinfo() is called from logdmuxuwput() during the
1234  * processing of a LOGDMX_IOC_QEXCHANGE ioctl() to allocate the
1235  * unlinkinfo_t which is needed during the processing of an I_UNLINK.
1236  */
1237 static int
1238 logdmux_alloc_unlinkinfo(struct tmx *t0, struct tmx *t1)
1239 {
1240         unlinkinfo_t    *p;
1241         uint_t          *messagep;
1242 
1243         if ((p = kmem_zalloc(sizeof (unlinkinfo_t), KM_NOSLEEP)) == NULL)
1244                 return (ENOSR);
1245 
1246         if ((p->prot_mp = allocb(sizeof (uint_t), BPRI_MED)) == NULL) {
1247                 kmem_free(p, sizeof (unlinkinfo_t));
1248                 return (ENOSR);
1249         }
1250 
1251         DB_TYPE(p->prot_mp) = M_CTL;
1252         messagep = (uint_t *)p->prot_mp->b_wptr;
1253         *messagep = LOGDMUX_UNLINK_REQ;
1254         p->prot_mp->b_wptr += sizeof (*messagep);
1255         p->state = LOGDMUX_LINKED;
1256         mutex_init(&p->state_lock, NULL, MUTEX_DRIVER, NULL);
1257 
1258         t0->unlinkinfop = t1->unlinkinfop = p;
1259 
1260         return (0);
1261 }