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 #include <sys/types.h>
  28 #include <inet/common.h>
  29 #include <sys/stropts.h>
  30 #include <sys/modctl.h>
  31 #include <sys/dld.h>
  32 #include <sys/softmac_impl.h>
  33 
  34 dev_info_t              *softmac_dip = NULL;
  35 static kmem_cache_t     *softmac_upper_cachep;
  36 
  37 /*
  38  * This function is a generic open(9E) entry point into the softmac for
  39  * both the softmac module and the softmac driver.
  40  */
  41 static int softmac_cmn_open(queue_t *, dev_t *, int, int, cred_t *);
  42 
  43 /*
  44  * The following softmac_mod_xxx() functions are (9E) entry point functions for
  45  * the softmac module.
  46  */
  47 static int softmac_mod_close(queue_t *, int, cred_t *);
  48 static int softmac_mod_rput(queue_t *, mblk_t *);
  49 static int softmac_mod_wput(queue_t *, mblk_t *);
  50 static int softmac_mod_wsrv(queue_t *);
  51 
  52 /*
  53  * The following softmac_drv_xxx() functions are (9E) entry point functions for
  54  * the softmac driver.
  55  */
  56 static int softmac_drv_open(queue_t *, dev_t *, int, int, cred_t *);
  57 static int softmac_drv_close(queue_t *, int, cred_t *);
  58 static int softmac_drv_wput(queue_t *, mblk_t *);
  59 static int softmac_drv_wsrv(queue_t *);
  60 
  61 static int softmac_attach(dev_info_t *, ddi_attach_cmd_t);
  62 static int softmac_detach(dev_info_t *, ddi_detach_cmd_t);
  63 static int softmac_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
  64 
  65 static struct module_info softmac_modinfo = {
  66         0,
  67         SOFTMAC_DEV_NAME,
  68         0,
  69         INFPSZ,
  70         65536,
  71         1024
  72 };
  73 
  74 /*
  75  * hi-water mark is 1 because of the flow control mechanism implemented in
  76  * dld.  Refer to the comments in dld_str.c for details.
  77  */
  78 static struct module_info softmac_dld_modinfo = {
  79         0,
  80         SOFTMAC_DEV_NAME,
  81         0,
  82         INFPSZ,
  83         1,
  84         0
  85 };
  86 
  87 static struct qinit softmac_urinit = {
  88         softmac_mod_rput,               /* qi_putp */
  89         NULL,                           /* qi_srvp */
  90         softmac_cmn_open,               /* qi_qopen */
  91         softmac_mod_close,              /* qi_qclose */
  92         NULL,                           /* qi_qadmin */
  93         &softmac_modinfo            /* qi_minfo */
  94 };
  95 
  96 static struct qinit softmac_uwinit = {
  97         softmac_mod_wput,               /* qi_putp */
  98         softmac_mod_wsrv,               /* qi_srvp */
  99         NULL,                           /* qi_qopen */
 100         NULL,                           /* qi_qclose */
 101         NULL,                           /* qi_qadmin */
 102         &softmac_modinfo            /* qi_minfo */
 103 };
 104 
 105 static struct streamtab softmac_tab = {
 106         &softmac_urinit,    /* st_rdinit */
 107         &softmac_uwinit             /* st_wrinit */
 108 };
 109 
 110 DDI_DEFINE_STREAM_OPS(softmac_ops, nulldev, nulldev, softmac_attach,
 111     softmac_detach, nodev, softmac_info, D_MP, &softmac_tab,
 112     ddi_quiesce_not_supported);
 113 
 114 static struct qinit softmac_dld_r_qinit = {
 115         NULL, NULL, softmac_drv_open, softmac_drv_close, NULL,
 116         &softmac_dld_modinfo
 117 };
 118 
 119 static struct qinit softmac_dld_w_qinit = {
 120         softmac_drv_wput, softmac_drv_wsrv, NULL, NULL, NULL,
 121         &softmac_dld_modinfo
 122 };
 123 
 124 static struct fmodsw softmac_fmodsw = {
 125         SOFTMAC_DEV_NAME,
 126         &softmac_tab,
 127         D_MP
 128 };
 129 
 130 static struct modldrv softmac_modldrv = {
 131         &mod_driverops,
 132         "softmac driver",
 133         &softmac_ops
 134 };
 135 
 136 static struct modlstrmod softmac_modlstrmod = {
 137         &mod_strmodops,
 138         "softmac module",
 139         &softmac_fmodsw
 140 };
 141 
 142 static struct modlinkage softmac_modlinkage = {
 143         MODREV_1,
 144         &softmac_modlstrmod,
 145         &softmac_modldrv,
 146         NULL
 147 };
 148 
 149 static void softmac_dedicated_rx(void *, mac_resource_handle_t, mblk_t *,
 150     mac_header_info_t *);
 151 
 152 /*ARGSUSED*/
 153 static int
 154 softmac_upper_constructor(void *buf, void *arg, int kmflag)
 155 {
 156         softmac_upper_t *sup = buf;
 157 
 158         bzero(buf, sizeof (softmac_upper_t));
 159 
 160         mutex_init(&sup->su_mutex, NULL, MUTEX_DEFAULT, NULL);
 161         cv_init(&sup->su_cv, NULL, CV_DEFAULT, NULL);
 162         mutex_init(&sup->su_disp_mutex, NULL, MUTEX_DEFAULT, NULL);
 163         cv_init(&sup->su_disp_cv, NULL, CV_DEFAULT, NULL);
 164         list_create(&sup->su_req_list, sizeof (softmac_switch_req_t),
 165             offsetof(softmac_switch_req_t, ssq_req_list_node));
 166         return (0);
 167 }
 168 
 169 /*ARGSUSED*/
 170 static void
 171 softmac_upper_destructor(void *buf, void *arg)
 172 {
 173         softmac_upper_t *sup = buf;
 174 
 175         ASSERT(sup->su_slp == NULL);
 176         ASSERT(sup->su_pending_head == NULL && sup->su_pending_tail == NULL);
 177         ASSERT(!sup->su_dlpi_pending);
 178         ASSERT(!sup->su_active);
 179         ASSERT(!sup->su_closing);
 180         ASSERT(sup->su_tx_flow_mp == NULL);
 181         ASSERT(sup->su_tx_inprocess == 0);
 182         ASSERT(sup->su_mode == SOFTMAC_UNKNOWN);
 183         ASSERT(!sup->su_tx_busy);
 184         ASSERT(!sup->su_bound);
 185         ASSERT(!sup->su_taskq_scheduled);
 186         ASSERT(sup->su_tx_notify_func == NULL);
 187         ASSERT(sup->su_tx_notify_arg == NULL);
 188         ASSERT(list_is_empty(&sup->su_req_list));
 189 
 190         list_destroy(&sup->su_req_list);
 191         mutex_destroy(&sup->su_mutex);
 192         cv_destroy(&sup->su_cv);
 193         mutex_destroy(&sup->su_disp_mutex);
 194         cv_destroy(&sup->su_disp_cv);
 195 }
 196 
 197 int
 198 _init(void)
 199 {
 200         int     err;
 201 
 202         mac_init_ops(NULL, SOFTMAC_DEV_NAME);
 203         softmac_init();
 204 
 205         softmac_upper_cachep = kmem_cache_create("softmac_upper_cache",
 206             sizeof (softmac_upper_t), 0, softmac_upper_constructor,
 207             softmac_upper_destructor, NULL, NULL, NULL, 0);
 208         ASSERT(softmac_upper_cachep != NULL);
 209 
 210         if ((err = mod_install(&softmac_modlinkage)) != 0) {
 211                 softmac_fini();
 212                 return (err);
 213         }
 214 
 215         return (0);
 216 }
 217 
 218 int
 219 _fini(void)
 220 {
 221         int err;
 222 
 223         if (softmac_busy())
 224                 return (EBUSY);
 225 
 226         if ((err = mod_remove(&softmac_modlinkage)) != 0)
 227                 return (err);
 228 
 229         kmem_cache_destroy(softmac_upper_cachep);
 230         softmac_fini();
 231 
 232         return (0);
 233 }
 234 
 235 int
 236 _info(struct modinfo *modinfop)
 237 {
 238         return (mod_info(&softmac_modlinkage, modinfop));
 239 }
 240 
 241 static int
 242 softmac_cmn_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp)
 243 {
 244         softmac_lower_t *slp;
 245         /*
 246          * This is a self-cloning driver so that each queue should only
 247          * get opened once.
 248          */
 249         if (rq->q_ptr != NULL)
 250                 return (EBUSY);
 251 
 252         if (sflag == MODOPEN) {
 253                 /*
 254                  * This is the softmac module pushed over an underlying
 255                  * legacy device.  Initialize the lower structure.
 256                  */
 257                 if ((slp = kmem_zalloc(sizeof (*slp), KM_NOSLEEP)) == NULL)
 258                         return (ENOMEM);
 259 
 260                 slp->sl_wq = WR(rq);
 261                 cv_init(&slp->sl_cv, NULL, CV_DRIVER, NULL);
 262                 mutex_init(&slp->sl_mutex, NULL, MUTEX_DRIVER, NULL);
 263                 slp->sl_pending_prim = DL_PRIM_INVAL;
 264                 rq->q_ptr = WR(rq)->q_ptr = slp;
 265                 qprocson(rq);
 266                 return (0);
 267         }
 268 
 269         /*
 270          * Regular device open of a softmac DLPI node.  We modify
 271          * the queues' q_qinfo pointer such that all future STREAMS
 272          * operations will go through another set of entry points
 273          */
 274         rq->q_qinfo = &softmac_dld_r_qinit;
 275         WR(rq)->q_qinfo = &softmac_dld_w_qinit;
 276         return (softmac_drv_open(rq, devp, flag, sflag, credp));
 277 }
 278 
 279 /* ARGSUSED */
 280 static int
 281 softmac_mod_close(queue_t *rq, int flags __unused, cred_t *credp __unused)
 282 {
 283         softmac_lower_t *slp = rq->q_ptr;
 284 
 285         /*
 286          * Call the appropriate delete routine depending on whether this is
 287          * a module or device.
 288          */
 289         ASSERT(WR(rq)->q_next != NULL);
 290 
 291         qprocsoff(rq);
 292 
 293         slp->sl_softmac = NULL;
 294         slp->sl_lh = NULL;
 295 
 296         ASSERT(slp->sl_ack_mp == NULL);
 297         ASSERT(slp->sl_pending_prim == DL_PRIM_INVAL);
 298         ASSERT(slp->sl_pending_ioctl == B_FALSE);
 299 
 300         cv_destroy(&slp->sl_cv);
 301         mutex_destroy(&slp->sl_mutex);
 302 
 303         kmem_free(slp, sizeof (*slp));
 304         return (0);
 305 }
 306 
 307 static int
 308 softmac_mod_rput(queue_t *rq, mblk_t *mp)
 309 {
 310         softmac_lower_t         *slp = rq->q_ptr;
 311         softmac_lower_rxinfo_t  *rxinfo;
 312         union DL_primitives     *dlp;
 313 
 314         /*
 315          * This is the softmac module.
 316          */
 317         ASSERT(WR(rq)->q_next != NULL);
 318         ASSERT((mp->b_next == NULL) && (mp->b_prev == NULL));
 319 
 320         switch (DB_TYPE(mp)) {
 321         case M_DATA: {
 322 
 323                 /*
 324                  * If sl_rxinfo is non-NULL. This is dedicated-lower-stream
 325                  * created for fastpath. Directly call the rx callback.
 326                  */
 327                 if ((rxinfo = slp->sl_rxinfo) != NULL) {
 328                         rxinfo->slr_rx(rxinfo->slr_arg, NULL, mp, NULL);
 329                         break;
 330                 }
 331 
 332                 /*
 333                  * A shared-lower-stream. Some driver starts to send up
 334                  * packets even it not in the DL_IDLE state, where
 335                  * sl_softmac is not set yet. Drop the packet in this case.
 336                  */
 337                 if (slp->sl_softmac == NULL) {
 338                         freemsg(mp);
 339                         return (0);
 340                 }
 341 
 342                 /*
 343                  * If this message is looped back from the legacy devices,
 344                  * drop it as the Nemo framework will be responsible for
 345                  * looping it back by the mac_txloop() function.
 346                  */
 347                 if (mp->b_flag & MSGNOLOOP) {
 348                         freemsg(mp);
 349                         return (0);
 350                 }
 351 
 352                 /*
 353                  * This is the most common case.
 354                  */
 355                 if (DB_REF(mp) == 1) {
 356                         ASSERT(slp->sl_softmac != NULL);
 357                         mac_rx(slp->sl_softmac->smac_mh, NULL, mp);
 358                         return (0);
 359                 } else {
 360                         softmac_rput_process_data(slp, mp);
 361                 }
 362                 break;
 363         }
 364         case M_PROTO:
 365         case M_PCPROTO:
 366                 if (MBLKL(mp) < sizeof (dlp->dl_primitive)) {
 367                         freemsg(mp);
 368                         break;
 369                 }
 370                 dlp = (union DL_primitives *)mp->b_rptr;
 371                 if (dlp->dl_primitive == DL_UNITDATA_IND) {
 372 
 373                         if ((rxinfo = slp->sl_rxinfo) != NULL) {
 374                                 softmac_dedicated_rx(slp->sl_sup, NULL, mp,
 375                                     NULL);
 376                                 break;
 377                         }
 378 
 379                         cmn_err(CE_WARN, "got unexpected %s message",
 380                             dl_primstr(DL_UNITDATA_IND));
 381                         freemsg(mp);
 382                         break;
 383                 }
 384                 /*FALLTHROUGH*/
 385         default:
 386                 softmac_rput_process_notdata(rq, slp->sl_sup, mp);
 387                 break;
 388         }
 389         return (0);
 390 }
 391 
 392 static int
 393 softmac_mod_wput(queue_t *wq, mblk_t *mp)
 394 {
 395         /*
 396          * This is the softmac module
 397          */
 398         ASSERT(wq->q_next != NULL);
 399 
 400         switch (DB_TYPE(mp)) {
 401         case M_IOCTL: {
 402                 struct iocblk           *ioc = (struct iocblk *)mp->b_rptr;
 403 
 404                 switch (ioc->ioc_cmd) {
 405                 case SMAC_IOC_START: {
 406                         softmac_lower_t         *slp = wq->q_ptr;
 407                         smac_ioc_start_t        *arg;
 408 
 409                         if (ioc->ioc_count != sizeof (*arg)) {
 410                                 miocnak(wq, mp, 0, EINVAL);
 411                                 break;
 412                         }
 413 
 414                         /*
 415                          * Assign the devname and perstream handle of the
 416                          * specific lower stream and return it as a part
 417                          * of the ioctl.
 418                          */
 419                         arg = (smac_ioc_start_t *)mp->b_cont->b_rptr;
 420                         arg->si_slp = slp;
 421                         miocack(wq, mp, sizeof (*arg), 0);
 422                         break;
 423                 }
 424                 default:
 425                         miocnak(wq, mp, 0, EINVAL);
 426                         break;
 427                 }
 428                 break;
 429         }
 430         default:
 431                 freemsg(mp);
 432                 break;
 433         }
 434         return (0);
 435 }
 436 
 437 static int
 438 softmac_mod_wsrv(queue_t *wq)
 439 {
 440         softmac_lower_t *slp = wq->q_ptr;
 441 
 442         /*
 443          * This is the softmac module
 444          */
 445         ASSERT(wq->q_next != NULL);
 446 
 447         /*
 448          * Inform that the tx resource is available; mac_tx_update() will
 449          * inform all the upper streams sharing this lower stream.
 450          */
 451         if (slp->sl_sup != NULL)
 452                 qenable(slp->sl_sup->su_wq);
 453         else if (slp->sl_softmac != NULL)
 454                 mac_tx_update(slp->sl_softmac->smac_mh);
 455         return (0);
 456 }
 457 
 458 static int
 459 softmac_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 460 {
 461         ASSERT(ddi_get_instance(dip) == 0);
 462 
 463         if (cmd != DDI_ATTACH)
 464                 return (DDI_FAILURE);
 465 
 466         softmac_dip = dip;
 467 
 468         return (DDI_SUCCESS);
 469 }
 470 
 471 /* ARGSUSED */
 472 static int
 473 softmac_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 474 {
 475         if (cmd != DDI_DETACH)
 476                 return (DDI_FAILURE);
 477 
 478         softmac_dip = NULL;
 479         return (DDI_SUCCESS);
 480 }
 481 
 482 /* ARGSUSED */
 483 static int
 484 softmac_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
 485 {
 486         switch (infocmd) {
 487         case DDI_INFO_DEVT2DEVINFO:
 488                 if (softmac_dip != NULL) {
 489                         *result = softmac_dip;
 490                         return (DDI_SUCCESS);
 491                 }
 492                 break;
 493 
 494         case DDI_INFO_DEVT2INSTANCE:
 495                 *result = NULL;
 496                 return (DDI_SUCCESS);
 497 
 498         }
 499 
 500         return (DDI_FAILURE);
 501 }
 502 
 503 /*ARGSUSED*/
 504 static void
 505 softmac_dedicated_rx(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
 506     mac_header_info_t *mhip)
 507 {
 508         queue_t *rq = ((softmac_upper_t *)arg)->su_rq;
 509 
 510         if (canputnext(rq))
 511                 putnext(rq, mp);
 512         else
 513                 freemsg(mp);
 514 }
 515 
 516 /*ARGSUSED*/
 517 static int
 518 softmac_drv_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp)
 519 {
 520         softmac_upper_t *sup = NULL;
 521         softmac_t       *softmac;
 522         int             err = 0;
 523 
 524         /*
 525          * This is a softmac device created for a legacy device, find the
 526          * associated softmac and initialize the softmac_upper_t structure.
 527          */
 528         if ((err = softmac_hold(*devp, &softmac)) != 0)
 529                 return (err);
 530 
 531         sup = kmem_cache_alloc(softmac_upper_cachep, KM_NOSLEEP);
 532         if (sup == NULL) {
 533                 err = ENOMEM;
 534                 goto fail;
 535         }
 536 
 537         ASSERT(list_is_empty(&sup->su_req_list));
 538 
 539         if ((sup->su_tx_flow_mp = allocb(1, BPRI_HI)) == NULL) {
 540                 err = ENOMEM;
 541                 goto fail;
 542         }
 543 
 544         sup->su_rq = rq;
 545         sup->su_wq = WR(rq);
 546         sup->su_softmac = softmac;
 547         sup->su_mode = SOFTMAC_UNKNOWN;
 548 
 549         sup->su_rxinfo.slr_arg = sup;
 550         sup->su_rxinfo.slr_rx = softmac_dedicated_rx;
 551         sup->su_direct_rxinfo.slr_arg = sup;
 552         sup->su_direct_rxinfo.slr_rx = softmac_dedicated_rx;
 553 
 554         if ((err = dld_str_open(rq, devp, sup)) != 0) {
 555                 freeb(sup->su_tx_flow_mp);
 556                 sup->su_tx_flow_mp = NULL;
 557                 goto fail;
 558         }
 559 
 560         return (0);
 561 
 562 fail:
 563         if (sup != NULL)
 564                 kmem_cache_free(softmac_upper_cachep, sup);
 565         softmac_rele(softmac);
 566         return (err);
 567 }
 568 
 569 /* ARGSUSED */
 570 static int
 571 softmac_drv_close(queue_t *rq, int flags __unused, cred_t *credp __unused)
 572 {
 573         softmac_upper_t *sup = dld_str_private(rq);
 574         softmac_t       *softmac = sup->su_softmac;
 575 
 576         ASSERT(WR(rq)->q_next == NULL);
 577 
 578         qprocsoff(rq);
 579 
 580         ASSERT(sup->su_tx_inprocess == 0);
 581 
 582         /*
 583          * Wait until the pending request are processed by the worker thread.
 584          */
 585         mutex_enter(&sup->su_disp_mutex);
 586         sup->su_closing = B_TRUE;
 587         while (sup->su_dlpi_pending)
 588                 cv_wait(&sup->su_disp_cv, &sup->su_disp_mutex);
 589         mutex_exit(&sup->su_disp_mutex);
 590 
 591         softmac_upperstream_close(sup);
 592 
 593         if (sup->su_tx_flow_mp != NULL) {
 594                 freeb(sup->su_tx_flow_mp);
 595                 sup->su_tx_flow_mp = NULL;
 596         }
 597 
 598         if (sup->su_active) {
 599                 mutex_enter(&softmac->smac_active_mutex);
 600                 softmac->smac_nactive--;
 601                 mutex_exit(&softmac->smac_active_mutex);
 602                 sup->su_active = B_FALSE;
 603         }
 604 
 605         sup->su_bound = B_FALSE;
 606         sup->su_softmac = NULL;
 607         sup->su_closing = B_FALSE;
 608 
 609         kmem_cache_free(softmac_upper_cachep, sup);
 610 
 611         softmac_rele(softmac);
 612         return (dld_str_close(rq));
 613 }
 614 
 615 static int
 616 softmac_drv_wput(queue_t *wq, mblk_t *mp)
 617 {
 618         softmac_upper_t *sup = dld_str_private(wq);
 619         t_uscalar_t     prim;
 620 
 621         ASSERT(wq->q_next == NULL);
 622 
 623         switch (DB_TYPE(mp)) {
 624         case M_DATA:
 625         case M_MULTIDATA:
 626                 softmac_wput_data(sup, mp);
 627                 break;
 628         case M_PROTO:
 629         case M_PCPROTO:
 630 
 631                 if (MBLKL(mp) < sizeof (t_uscalar_t)) {
 632                         freemsg(mp);
 633                         return (0);
 634                 }
 635 
 636                 prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive;
 637                 if (prim == DL_UNITDATA_REQ) {
 638                         softmac_wput_data(sup, mp);
 639                         return (0);
 640                 }
 641 
 642                 softmac_wput_nondata(sup, mp);
 643                 break;
 644         default:
 645                 softmac_wput_nondata(sup, mp);
 646                 break;
 647         }
 648         return (0);
 649 }
 650 
 651 static int
 652 softmac_drv_wsrv(queue_t *wq)
 653 {
 654         softmac_upper_t *sup = dld_str_private(wq);
 655 
 656         ASSERT(wq->q_next == NULL);
 657 
 658         mutex_enter(&sup->su_mutex);
 659         if (sup->su_mode != SOFTMAC_FASTPATH) {
 660                 /*
 661                  * Bump su_tx_inprocess so that su_mode won't change.
 662                  */
 663                 sup->su_tx_inprocess++;
 664                 mutex_exit(&sup->su_mutex);
 665                 dld_wsrv(wq);
 666                 mutex_enter(&sup->su_mutex);
 667                 if (--sup->su_tx_inprocess == 0)
 668                         cv_signal(&sup->su_cv);
 669         } else if (sup->su_tx_busy && SOFTMAC_CANPUTNEXT(sup->su_slp->sl_wq)) {
 670                 /*
 671                  * The flow-conctol of the dedicated-lower-stream is
 672                  * relieved. If DLD_CAPAB_DIRECT is enabled, call tx_notify
 673                  * callback to relieve the flow-control of the specific client,
 674                  * otherwise relieve the flow-control of all the upper-stream
 675                  * using the traditional STREAM mechanism.
 676                  */
 677                 if (sup->su_tx_notify_func != NULL) {
 678                         sup->su_tx_inprocess++;
 679                         mutex_exit(&sup->su_mutex);
 680                         sup->su_tx_notify_func(sup->su_tx_notify_arg,
 681                             (mac_tx_cookie_t)sup);
 682                         mutex_enter(&sup->su_mutex);
 683                         if (--sup->su_tx_inprocess == 0)
 684                                 cv_signal(&sup->su_cv);
 685                 }
 686                 ASSERT(sup->su_tx_flow_mp == NULL);
 687                 VERIFY((sup->su_tx_flow_mp = getq(wq)) != NULL);
 688                 sup->su_tx_busy = B_FALSE;
 689         }
 690         mutex_exit(&sup->su_mutex);
 691         return (0);
 692 }