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