Home | History | Annotate | Download | only in softmac
      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 *);
     48 static void softmac_mod_rput(queue_t *, mblk_t *);
     49 static void softmac_mod_wput(queue_t *, mblk_t *);
     50 static void 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 *);
     58 static void softmac_drv_wput(queue_t *, mblk_t *);
     59 static void 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 	(pfi_t)softmac_mod_rput,	/* qi_putp */
     89 	(pfi_t)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 	(pfi_t)softmac_mod_wput,	/* qi_putp */
     98 	(pfi_t)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 	(pfi_t)softmac_drv_wput, (pfi_t)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 static int
    280 softmac_mod_close(queue_t *rq)
    281 {
    282 	softmac_lower_t	*slp = rq->q_ptr;
    283 
    284 	/*
    285 	 * Call the appropriate delete routine depending on whether this is
    286 	 * a module or device.
    287 	 */
    288 	ASSERT(WR(rq)->q_next != NULL);
    289 
    290 	qprocsoff(rq);
    291 
    292 	slp->sl_softmac = NULL;
    293 	slp->sl_lh = NULL;
    294 
    295 	ASSERT(slp->sl_ack_mp == NULL);
    296 	ASSERT(slp->sl_pending_prim == DL_PRIM_INVAL);
    297 	ASSERT(slp->sl_pending_ioctl == B_FALSE);
    298 
    299 	cv_destroy(&slp->sl_cv);
    300 	mutex_destroy(&slp->sl_mutex);
    301 
    302 	kmem_free(slp, sizeof (*slp));
    303 	return (0);
    304 }
    305 
    306 static void
    307 softmac_mod_rput(queue_t *rq, mblk_t *mp)
    308 {
    309 	softmac_lower_t		*slp = rq->q_ptr;
    310 	softmac_lower_rxinfo_t	*rxinfo;
    311 	union DL_primitives	*dlp;
    312 
    313 	/*
    314 	 * This is the softmac module.
    315 	 */
    316 	ASSERT(WR(rq)->q_next != NULL);
    317 	ASSERT((mp->b_next == NULL) && (mp->b_prev == NULL));
    318 
    319 	switch (DB_TYPE(mp)) {
    320 	case M_DATA: {
    321 
    322 		/*
    323 		 * If sl_rxinfo is non-NULL. This is dedicated-lower-stream
    324 		 * created for fastpath. Directly call the rx callback.
    325 		 */
    326 		if ((rxinfo = slp->sl_rxinfo) != NULL) {
    327 			rxinfo->slr_rx(rxinfo->slr_arg, NULL, mp, NULL);
    328 			break;
    329 		}
    330 
    331 		/*
    332 		 * A shared-lower-stream. Some driver starts to send up
    333 		 * packets even it not in the DL_IDLE state, where
    334 		 * sl_softmac is not set yet. Drop the packet in this case.
    335 		 */
    336 		if (slp->sl_softmac == NULL) {
    337 			freemsg(mp);
    338 			return;
    339 		}
    340 
    341 		/*
    342 		 * If this message is looped back from the legacy devices,
    343 		 * drop it as the Nemo framework will be responsible for
    344 		 * looping it back by the mac_txloop() function.
    345 		 */
    346 		if (mp->b_flag & MSGNOLOOP) {
    347 			freemsg(mp);
    348 			return;
    349 		}
    350 
    351 		/*
    352 		 * This is the most common case.
    353 		 */
    354 		if (DB_REF(mp) == 1) {
    355 			ASSERT(slp->sl_softmac != NULL);
    356 			mac_rx(slp->sl_softmac->smac_mh, NULL, mp);
    357 			return;
    358 		} else {
    359 			softmac_rput_process_data(slp, mp);
    360 		}
    361 		break;
    362 	}
    363 	case M_PROTO:
    364 	case M_PCPROTO:
    365 		if (MBLKL(mp) < sizeof (dlp->dl_primitive)) {
    366 			freemsg(mp);
    367 			break;
    368 		}
    369 		dlp = (union DL_primitives *)mp->b_rptr;
    370 		if (dlp->dl_primitive == DL_UNITDATA_IND) {
    371 
    372 			if ((rxinfo = slp->sl_rxinfo) != NULL) {
    373 				softmac_dedicated_rx(slp->sl_sup, NULL, mp,
    374 				    NULL);
    375 				break;
    376 			}
    377 
    378 			cmn_err(CE_WARN, "got unexpected %s message",
    379 			    dl_primstr(DL_UNITDATA_IND));
    380 			freemsg(mp);
    381 			break;
    382 		}
    383 		/*FALLTHROUGH*/
    384 	default:
    385 		softmac_rput_process_notdata(rq, slp->sl_sup, mp);
    386 		break;
    387 	}
    388 }
    389 
    390 static void
    391 softmac_mod_wput(queue_t *wq, mblk_t *mp)
    392 {
    393 	/*
    394 	 * This is the softmac module
    395 	 */
    396 	ASSERT(wq->q_next != NULL);
    397 
    398 	switch (DB_TYPE(mp)) {
    399 	case M_IOCTL: {
    400 		struct iocblk		*ioc = (struct iocblk *)mp->b_rptr;
    401 
    402 		switch (ioc->ioc_cmd) {
    403 		case SMAC_IOC_START: {
    404 			softmac_lower_t		*slp = wq->q_ptr;
    405 			smac_ioc_start_t	*arg;
    406 
    407 			if (ioc->ioc_count != sizeof (*arg)) {
    408 				miocnak(wq, mp, 0, EINVAL);
    409 				break;
    410 			}
    411 
    412 			/*
    413 			 * Assign the devname and perstream handle of the
    414 			 * specific lower stream and return it as a part
    415 			 * of the ioctl.
    416 			 */
    417 			arg = (smac_ioc_start_t *)mp->b_cont->b_rptr;
    418 			arg->si_slp = slp;
    419 			miocack(wq, mp, sizeof (*arg), 0);
    420 			break;
    421 		}
    422 		default:
    423 			miocnak(wq, mp, 0, EINVAL);
    424 			break;
    425 		}
    426 		break;
    427 	}
    428 	default:
    429 		freemsg(mp);
    430 		break;
    431 	}
    432 }
    433 
    434 static void
    435 softmac_mod_wsrv(queue_t *wq)
    436 {
    437 	softmac_lower_t *slp = wq->q_ptr;
    438 
    439 	/*
    440 	 * This is the softmac module
    441 	 */
    442 	ASSERT(wq->q_next != NULL);
    443 
    444 	/*
    445 	 * Inform that the tx resource is available; mac_tx_update() will
    446 	 * inform all the upper streams sharing this lower stream.
    447 	 */
    448 	if (slp->sl_sup != NULL)
    449 		qenable(slp->sl_sup->su_wq);
    450 	else if (slp->sl_softmac != NULL)
    451 		mac_tx_update(slp->sl_softmac->smac_mh);
    452 }
    453 
    454 static int
    455 softmac_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
    456 {
    457 	ASSERT(ddi_get_instance(dip) == 0);
    458 
    459 	if (cmd != DDI_ATTACH)
    460 		return (DDI_FAILURE);
    461 
    462 	softmac_dip = dip;
    463 
    464 	return (DDI_SUCCESS);
    465 }
    466 
    467 /* ARGSUSED */
    468 static int
    469 softmac_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
    470 {
    471 	if (cmd != DDI_DETACH)
    472 		return (DDI_FAILURE);
    473 
    474 	softmac_dip = NULL;
    475 	return (DDI_SUCCESS);
    476 }
    477 
    478 /* ARGSUSED */
    479 static int
    480 softmac_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
    481 {
    482 	switch (infocmd) {
    483 	case DDI_INFO_DEVT2DEVINFO:
    484 		if (softmac_dip != NULL) {
    485 			*result = softmac_dip;
    486 			return (DDI_SUCCESS);
    487 		}
    488 		break;
    489 
    490 	case DDI_INFO_DEVT2INSTANCE:
    491 		*result = NULL;
    492 		return (DDI_SUCCESS);
    493 
    494 	}
    495 
    496 	return (DDI_FAILURE);
    497 }
    498 
    499 /*ARGSUSED*/
    500 static void
    501 softmac_dedicated_rx(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
    502     mac_header_info_t *mhip)
    503 {
    504 	queue_t *rq = ((softmac_upper_t *)arg)->su_rq;
    505 
    506 	if (canputnext(rq))
    507 		putnext(rq, mp);
    508 	else
    509 		freemsg(mp);
    510 }
    511 
    512 /*ARGSUSED*/
    513 static int
    514 softmac_drv_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp)
    515 {
    516 	softmac_upper_t	*sup = NULL;
    517 	softmac_t	*softmac;
    518 	int		err = 0;
    519 
    520 	/*
    521 	 * This is a softmac device created for a legacy device, find the
    522 	 * associated softmac and initialize the softmac_upper_t structure.
    523 	 */
    524 	if ((err = softmac_hold(*devp, &softmac)) != 0)
    525 		return (err);
    526 
    527 	sup = kmem_cache_alloc(softmac_upper_cachep, KM_NOSLEEP);
    528 	if (sup == NULL) {
    529 		err = ENOMEM;
    530 		goto fail;
    531 	}
    532 
    533 	ASSERT(list_is_empty(&sup->su_req_list));
    534 
    535 	if ((sup->su_tx_flow_mp = allocb(1, BPRI_HI)) == NULL) {
    536 		err = ENOMEM;
    537 		goto fail;
    538 	}
    539 
    540 	sup->su_rq = rq;
    541 	sup->su_wq = WR(rq);
    542 	sup->su_softmac = softmac;
    543 	sup->su_mode = SOFTMAC_UNKNOWN;
    544 
    545 	sup->su_rxinfo.slr_arg = sup;
    546 	sup->su_rxinfo.slr_rx = softmac_dedicated_rx;
    547 	sup->su_direct_rxinfo.slr_arg = sup;
    548 	sup->su_direct_rxinfo.slr_rx = softmac_dedicated_rx;
    549 
    550 	if ((err = dld_str_open(rq, devp, sup)) != 0) {
    551 		freeb(sup->su_tx_flow_mp);
    552 		sup->su_tx_flow_mp = NULL;
    553 		goto fail;
    554 	}
    555 
    556 	return (0);
    557 
    558 fail:
    559 	if (sup != NULL)
    560 		kmem_cache_free(softmac_upper_cachep, sup);
    561 	softmac_rele(softmac);
    562 	return (err);
    563 }
    564 
    565 static int
    566 softmac_drv_close(queue_t *rq)
    567 {
    568 	softmac_upper_t	*sup = dld_str_private(rq);
    569 	softmac_t	*softmac = sup->su_softmac;
    570 
    571 	ASSERT(WR(rq)->q_next == NULL);
    572 
    573 	qprocsoff(rq);
    574 
    575 	ASSERT(sup->su_tx_inprocess == 0);
    576 
    577 	/*
    578 	 * Wait until the pending request are processed by the worker thread.
    579 	 */
    580 	mutex_enter(&sup->su_disp_mutex);
    581 	sup->su_closing = B_TRUE;
    582 	while (sup->su_dlpi_pending)
    583 		cv_wait(&sup->su_disp_cv, &sup->su_disp_mutex);
    584 	mutex_exit(&sup->su_disp_mutex);
    585 
    586 	softmac_upperstream_close(sup);
    587 
    588 	if (sup->su_tx_flow_mp != NULL) {
    589 		freeb(sup->su_tx_flow_mp);
    590 		sup->su_tx_flow_mp = NULL;
    591 	}
    592 
    593 	if (sup->su_active) {
    594 		mutex_enter(&softmac->smac_active_mutex);
    595 		softmac->smac_nactive--;
    596 		mutex_exit(&softmac->smac_active_mutex);
    597 		sup->su_active = B_FALSE;
    598 	}
    599 
    600 	sup->su_bound = B_FALSE;
    601 	sup->su_softmac = NULL;
    602 	sup->su_closing = B_FALSE;
    603 
    604 	kmem_cache_free(softmac_upper_cachep, sup);
    605 
    606 	softmac_rele(softmac);
    607 	return (dld_str_close(rq));
    608 }
    609 
    610 static void
    611 softmac_drv_wput(queue_t *wq, mblk_t *mp)
    612 {
    613 	softmac_upper_t	*sup = dld_str_private(wq);
    614 	t_uscalar_t	prim;
    615 
    616 	ASSERT(wq->q_next == NULL);
    617 
    618 	switch (DB_TYPE(mp)) {
    619 	case M_DATA:
    620 	case M_MULTIDATA:
    621 		softmac_wput_data(sup, mp);
    622 		break;
    623 	case M_PROTO:
    624 	case M_PCPROTO:
    625 
    626 		if (MBLKL(mp) < sizeof (t_uscalar_t)) {
    627 			freemsg(mp);
    628 			return;
    629 		}
    630 
    631 		prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive;
    632 		if (prim == DL_UNITDATA_REQ) {
    633 			softmac_wput_data(sup, mp);
    634 			return;
    635 		}
    636 
    637 		softmac_wput_nondata(sup, mp);
    638 		break;
    639 	default:
    640 		softmac_wput_nondata(sup, mp);
    641 		break;
    642 	}
    643 }
    644 
    645 static void
    646 softmac_drv_wsrv(queue_t *wq)
    647 {
    648 	softmac_upper_t	*sup = dld_str_private(wq);
    649 
    650 	ASSERT(wq->q_next == NULL);
    651 
    652 	mutex_enter(&sup->su_mutex);
    653 	if (sup->su_mode != SOFTMAC_FASTPATH) {
    654 		/*
    655 		 * Bump su_tx_inprocess so that su_mode won't change.
    656 		 */
    657 		sup->su_tx_inprocess++;
    658 		mutex_exit(&sup->su_mutex);
    659 		dld_wsrv(wq);
    660 		mutex_enter(&sup->su_mutex);
    661 		if (--sup->su_tx_inprocess == 0)
    662 			cv_signal(&sup->su_cv);
    663 	} else if (sup->su_tx_busy && SOFTMAC_CANPUTNEXT(sup->su_slp->sl_wq)) {
    664 		/*
    665 		 * The flow-conctol of the dedicated-lower-stream is
    666 		 * relieved. If DLD_CAPAB_DIRECT is enabled, call tx_notify
    667 		 * callback to relieve the flow-control of the specific client,
    668 		 * otherwise relieve the flow-control of all the upper-stream
    669 		 * using the traditional STREAM mechanism.
    670 		 */
    671 		if (sup->su_tx_notify_func != NULL) {
    672 			sup->su_tx_inprocess++;
    673 			mutex_exit(&sup->su_mutex);
    674 			sup->su_tx_notify_func(sup->su_tx_notify_arg,
    675 			    (mac_tx_cookie_t)sup);
    676 			mutex_enter(&sup->su_mutex);
    677 			if (--sup->su_tx_inprocess == 0)
    678 				cv_signal(&sup->su_cv);
    679 		}
    680 		ASSERT(sup->su_tx_flow_mp == NULL);
    681 		VERIFY((sup->su_tx_flow_mp = getq(wq)) != NULL);
    682 		sup->su_tx_busy = B_FALSE;
    683 	}
    684 	mutex_exit(&sup->su_mutex);
    685 }
    686