Home | History | Annotate | Download | only in dls
      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  * Data-Link Services Module
     28  */
     29 
     30 #include	<sys/strsun.h>
     31 #include	<sys/vlan.h>
     32 #include	<sys/dld_impl.h>
     33 #include	<sys/mac_client_priv.h>
     34 
     35 int
     36 dls_open(dls_link_t *dlp, dls_dl_handle_t ddh, dld_str_t *dsp)
     37 {
     38 	zoneid_t	zid = getzoneid();
     39 	boolean_t	local;
     40 	int		err;
     41 
     42 	/*
     43 	 * Check whether this client belongs to the zone of this dlp. Note that
     44 	 * a global zone client is allowed to open a local zone dlp.
     45 	 */
     46 	if (zid != GLOBAL_ZONEID && dlp->dl_zid != zid)
     47 		return (ENOENT);
     48 
     49 	/*
     50 	 * mac_start() is required for non-legacy MACs to show accurate
     51 	 * kstats even before the interface is brought up. For legacy
     52 	 * drivers, this is not needed. Further, calling mac_start() for
     53 	 * legacy drivers would make the shared-lower-stream to stay in
     54 	 * the DL_IDLE state, which in turn causes performance regression.
     55 	 */
     56 	if (!mac_capab_get(dlp->dl_mh, MAC_CAPAB_LEGACY, NULL) &&
     57 	    ((err = mac_start(dlp->dl_mh)) != 0)) {
     58 		return (err);
     59 	}
     60 
     61 	local = (zid == dlp->dl_zid);
     62 	dlp->dl_zone_ref += (local ? 1 : 0);
     63 
     64 	/*
     65 	 * Cache a copy of the MAC interface handle, a pointer to the
     66 	 * immutable MAC info.
     67 	 */
     68 	dsp->ds_dlp = dlp;
     69 	dsp->ds_mh = dlp->dl_mh;
     70 	dsp->ds_mch = dlp->dl_mch;
     71 	dsp->ds_mip = dlp->dl_mip;
     72 	dsp->ds_ddh = ddh;
     73 	dsp->ds_local = local;
     74 
     75 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
     76 	return (0);
     77 }
     78 
     79 void
     80 dls_close(dld_str_t *dsp)
     81 {
     82 	dls_link_t		*dlp = dsp->ds_dlp;
     83 	dls_multicst_addr_t	*p;
     84 	dls_multicst_addr_t	*nextp;
     85 	uint32_t		old_flags;
     86 
     87 	ASSERT(dsp->ds_datathr_cnt == 0);
     88 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
     89 
     90 	if (dsp->ds_local)
     91 		dlp->dl_zone_ref--;
     92 	dsp->ds_local = B_FALSE;
     93 
     94 	/*
     95 	 * Walk the list of multicast addresses, disabling each at the MAC.
     96 	 * Note that we must remove multicast address before
     97 	 * mac_unicast_remove() (called by dls_active_clear()) because
     98 	 * mac_multicast_remove() relies on the unicast flows on the mac
     99 	 * client.
    100 	 */
    101 	for (p = dsp->ds_dmap; p != NULL; p = nextp) {
    102 		(void) mac_multicast_remove(dsp->ds_mch, p->dma_addr);
    103 		nextp = p->dma_nextp;
    104 		kmem_free(p, sizeof (dls_multicst_addr_t));
    105 	}
    106 	dsp->ds_dmap = NULL;
    107 
    108 	dls_active_clear(dsp, B_TRUE);
    109 
    110 	/*
    111 	 * If the dld_str_t is bound then unbind it.
    112 	 */
    113 	if (dsp->ds_dlstate == DL_IDLE) {
    114 		dls_unbind(dsp);
    115 		dsp->ds_dlstate = DL_UNBOUND;
    116 	}
    117 
    118 	/*
    119 	 * If the MAC has been set in promiscuous mode then disable it.
    120 	 * This needs to be done before resetting ds_rx.
    121 	 */
    122 	old_flags = dsp->ds_promisc;
    123 	dsp->ds_promisc = 0;
    124 	(void) dls_promisc(dsp, old_flags);
    125 
    126 	/*
    127 	 * At this point we have cutoff inbound packet flow from the mac
    128 	 * for this 'dsp'. The dls_link_remove above cut off packets meant
    129 	 * for us and waited for upcalls to finish. Similarly the dls_promisc
    130 	 * reset above waited for promisc callbacks to finish. Now we can
    131 	 * safely reset ds_rx to NULL
    132 	 */
    133 	dsp->ds_rx = NULL;
    134 	dsp->ds_rx_arg = NULL;
    135 
    136 	dsp->ds_dlp = NULL;
    137 
    138 	if (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_LEGACY, NULL))
    139 		mac_stop(dsp->ds_mh);
    140 
    141 	/*
    142 	 * Release our reference to the dls_link_t allowing that to be
    143 	 * destroyed if there are no more dls_impl_t.
    144 	 */
    145 	dls_link_rele(dlp);
    146 }
    147 
    148 int
    149 dls_bind(dld_str_t *dsp, uint32_t sap)
    150 {
    151 	uint32_t	dls_sap;
    152 
    153 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
    154 
    155 	/*
    156 	 * Check to see the value is legal for the media type.
    157 	 */
    158 	if (!mac_sap_verify(dsp->ds_mh, sap, &dls_sap))
    159 		return (EINVAL);
    160 
    161 	if (dsp->ds_promisc & DLS_PROMISC_SAP)
    162 		dls_sap = DLS_SAP_PROMISC;
    163 
    164 	/*
    165 	 * Set up the dld_str_t to mark it as able to receive packets.
    166 	 */
    167 	dsp->ds_sap = sap;
    168 
    169 	/*
    170 	 * The MAC layer does the VLAN demultiplexing and will only pass up
    171 	 * untagged packets to non-promiscuous primary MAC clients. In order to
    172 	 * support the binding to the VLAN SAP which is required by DLPI, dls
    173 	 * needs to get a copy of all tagged packets when the client binds to
    174 	 * the VLAN SAP. We do this by registering a separate promiscuous
    175 	 * callback for each dls client binding to that SAP.
    176 	 *
    177 	 * Note: even though there are two promiscuous handles in dld_str_t,
    178 	 * ds_mph is for the regular promiscuous mode, ds_vlan_mph is the handle
    179 	 * to receive VLAN pkt when promiscuous mode is not on. Only one of
    180 	 * them can be non-NULL at the same time, to avoid receiving dup copies
    181 	 * of pkts.
    182 	 */
    183 	if (sap == ETHERTYPE_VLAN && dsp->ds_promisc == 0) {
    184 		int err;
    185 
    186 		if (dsp->ds_vlan_mph != NULL)
    187 			return (EINVAL);
    188 		err = mac_promisc_add(dsp->ds_mch,
    189 		    MAC_CLIENT_PROMISC_ALL, dls_rx_vlan_promisc, dsp,
    190 		    &dsp->ds_vlan_mph, MAC_PROMISC_FLAGS_NO_PHYS);
    191 		return (err);
    192 	}
    193 
    194 	/*
    195 	 * Now bind the dld_str_t by adding it into the hash table in the
    196 	 * dls_link_t.
    197 	 */
    198 	dls_link_add(dsp->ds_dlp, dls_sap, dsp);
    199 	return (0);
    200 }
    201 
    202 void
    203 dls_unbind(dld_str_t *dsp)
    204 {
    205 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
    206 
    207 	/*
    208 	 * For VLAN SAP, there was a promisc handle registered when dls_bind.
    209 	 * When unbind this dls link, we need to remove the promisc handle.
    210 	 * See comments in dls_bind().
    211 	 */
    212 	if (dsp->ds_vlan_mph != NULL) {
    213 		mac_promisc_remove(dsp->ds_vlan_mph);
    214 		dsp->ds_vlan_mph = NULL;
    215 		return;
    216 	}
    217 
    218 	/*
    219 	 * Unbind the dld_str_t by removing it from the hash table in the
    220 	 * dls_link_t.
    221 	 */
    222 	dls_link_remove(dsp->ds_dlp, dsp);
    223 	dsp->ds_sap = 0;
    224 }
    225 
    226 int
    227 dls_promisc(dld_str_t *dsp, uint32_t old_flags)
    228 {
    229 	int		err = 0;
    230 
    231 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
    232 	ASSERT(!(dsp->ds_promisc & ~(DLS_PROMISC_SAP | DLS_PROMISC_MULTI |
    233 	    DLS_PROMISC_PHYS)));
    234 
    235 	if (old_flags == 0 && dsp->ds_promisc != 0) {
    236 		/*
    237 		 * If only DLS_PROMISC_SAP, we don't turn on the
    238 		 * physical promisc mode
    239 		 */
    240 		err = mac_promisc_add(dsp->ds_mch, MAC_CLIENT_PROMISC_ALL,
    241 		    dls_rx_promisc, dsp, &dsp->ds_mph,
    242 		    (dsp->ds_promisc != DLS_PROMISC_SAP) ? 0 :
    243 		    MAC_PROMISC_FLAGS_NO_PHYS);
    244 		if (err != 0)
    245 			return (err);
    246 
    247 		/* Remove vlan promisc handle to avoid sending dup copy up */
    248 		if (dsp->ds_vlan_mph != NULL) {
    249 			mac_promisc_remove(dsp->ds_vlan_mph);
    250 			dsp->ds_vlan_mph = NULL;
    251 		}
    252 	} else if (old_flags != 0 && dsp->ds_promisc == 0) {
    253 		ASSERT(dsp->ds_mph != NULL);
    254 
    255 		mac_promisc_remove(dsp->ds_mph);
    256 		dsp->ds_mph = NULL;
    257 
    258 		if (dsp->ds_sap == ETHERTYPE_VLAN &&
    259 		    dsp->ds_dlstate != DL_UNBOUND) {
    260 			int err;
    261 
    262 			if (dsp->ds_vlan_mph != NULL)
    263 				return (EINVAL);
    264 			err = mac_promisc_add(dsp->ds_mch,
    265 			    MAC_CLIENT_PROMISC_ALL, dls_rx_vlan_promisc, dsp,
    266 			    &dsp->ds_vlan_mph, MAC_PROMISC_FLAGS_NO_PHYS);
    267 			return (err);
    268 		}
    269 	} else if (old_flags == DLS_PROMISC_SAP && dsp->ds_promisc != 0 &&
    270 	    dsp->ds_promisc != old_flags) {
    271 		/*
    272 		 * If the old flag is PROMISC_SAP, but the current flag has
    273 		 * changed to some new non-zero value, we need to turn the
    274 		 * physical promiscuous mode.
    275 		 */
    276 		ASSERT(dsp->ds_mph != NULL);
    277 		mac_promisc_remove(dsp->ds_mph);
    278 		err = mac_promisc_add(dsp->ds_mch, MAC_CLIENT_PROMISC_ALL,
    279 		    dls_rx_promisc, dsp, &dsp->ds_mph, 0);
    280 	}
    281 
    282 	return (err);
    283 }
    284 
    285 int
    286 dls_multicst_add(dld_str_t *dsp, const uint8_t *addr)
    287 {
    288 	int			err;
    289 	dls_multicst_addr_t	**pp;
    290 	dls_multicst_addr_t	*p;
    291 	uint_t			addr_length;
    292 
    293 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
    294 
    295 	/*
    296 	 * Check whether the address is in the list of enabled addresses for
    297 	 * this dld_str_t.
    298 	 */
    299 	addr_length = dsp->ds_mip->mi_addr_length;
    300 
    301 	/*
    302 	 * Protect against concurrent access of ds_dmap by data threads using
    303 	 * ds_rw_lock. The mac perimeter serializes the dls_multicst_add and
    304 	 * remove operations. Dropping the ds_rw_lock across mac calls is thus
    305 	 * ok and is also required by the locking protocol.
    306 	 */
    307 	rw_enter(&dsp->ds_rw_lock, RW_WRITER);
    308 	for (pp = &(dsp->ds_dmap); (p = *pp) != NULL; pp = &(p->dma_nextp)) {
    309 		if (bcmp(addr, p->dma_addr, addr_length) == 0) {
    310 			/*
    311 			 * It is there so there's nothing to do.
    312 			 */
    313 			err = 0;
    314 			goto done;
    315 		}
    316 	}
    317 
    318 	/*
    319 	 * Allocate a new list item and add it to the list.
    320 	 */
    321 	p = kmem_zalloc(sizeof (dls_multicst_addr_t), KM_SLEEP);
    322 	bcopy(addr, p->dma_addr, addr_length);
    323 	*pp = p;
    324 	rw_exit(&dsp->ds_rw_lock);
    325 
    326 	/*
    327 	 * Enable the address at the MAC.
    328 	 */
    329 	err = mac_multicast_add(dsp->ds_mch, addr);
    330 	if (err == 0)
    331 		return (0);
    332 
    333 	/* Undo the operation as it has failed */
    334 	rw_enter(&dsp->ds_rw_lock, RW_WRITER);
    335 	ASSERT(*pp == p && p->dma_nextp == NULL);
    336 	*pp = NULL;
    337 	kmem_free(p, sizeof (dls_multicst_addr_t));
    338 done:
    339 	rw_exit(&dsp->ds_rw_lock);
    340 	return (err);
    341 }
    342 
    343 int
    344 dls_multicst_remove(dld_str_t *dsp, const uint8_t *addr)
    345 {
    346 	dls_multicst_addr_t	**pp;
    347 	dls_multicst_addr_t	*p;
    348 	uint_t			addr_length;
    349 
    350 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
    351 
    352 	/*
    353 	 * Find the address in the list of enabled addresses for this
    354 	 * dld_str_t.
    355 	 */
    356 	addr_length = dsp->ds_mip->mi_addr_length;
    357 
    358 	/*
    359 	 * Protect against concurrent access to ds_dmap by data threads using
    360 	 * ds_rw_lock. The mac perimeter serializes the dls_multicst_add and
    361 	 * remove operations. Dropping the ds_rw_lock across mac calls is thus
    362 	 * ok and is also required by the locking protocol.
    363 	 */
    364 	rw_enter(&dsp->ds_rw_lock, RW_WRITER);
    365 	for (pp = &(dsp->ds_dmap); (p = *pp) != NULL; pp = &(p->dma_nextp)) {
    366 		if (bcmp(addr, p->dma_addr, addr_length) == 0)
    367 			break;
    368 	}
    369 
    370 	/*
    371 	 * If we walked to the end of the list then the given address is
    372 	 * not currently enabled for this dld_str_t.
    373 	 */
    374 	if (p == NULL) {
    375 		rw_exit(&dsp->ds_rw_lock);
    376 		return (ENOENT);
    377 	}
    378 
    379 	/*
    380 	 * Remove the address from the list.
    381 	 */
    382 	*pp = p->dma_nextp;
    383 	rw_exit(&dsp->ds_rw_lock);
    384 
    385 	/*
    386 	 * Disable the address at the MAC.
    387 	 */
    388 	mac_multicast_remove(dsp->ds_mch, addr);
    389 	kmem_free(p, sizeof (dls_multicst_addr_t));
    390 	return (0);
    391 }
    392 
    393 mblk_t *
    394 dls_header(dld_str_t *dsp, const uint8_t *addr, uint16_t sap, uint_t pri,
    395     mblk_t **payloadp)
    396 {
    397 	uint16_t vid;
    398 	size_t extra_len;
    399 	uint16_t mac_sap;
    400 	mblk_t *mp, *payload;
    401 	boolean_t is_ethernet = (dsp->ds_mip->mi_media == DL_ETHER);
    402 	struct ether_vlan_header *evhp;
    403 
    404 	vid = mac_client_vid(dsp->ds_mch);
    405 	payload = (payloadp == NULL) ? NULL : (*payloadp);
    406 
    407 	/*
    408 	 * In the case of Ethernet, we need to tell mac_header() if we need
    409 	 * extra room beyond the Ethernet header for a VLAN header.  We'll
    410 	 * need to add a VLAN header if this isn't an ETHERTYPE_VLAN listener
    411 	 * (because such streams will be handling VLAN headers on their own)
    412 	 * and one of the following conditions is satisfied:
    413 	 *
    414 	 * - This is a VLAN stream
    415 	 * - This is a physical stream, the priority is not 0, and user
    416 	 *   priority tagging is allowed.
    417 	 */
    418 	if (is_ethernet && sap != ETHERTYPE_VLAN &&
    419 	    (vid != VLAN_ID_NONE ||
    420 	    (pri != 0 && dsp->ds_dlp->dl_tagmode != LINK_TAGMODE_VLANONLY))) {
    421 		extra_len = sizeof (struct ether_vlan_header) -
    422 		    sizeof (struct ether_header);
    423 		mac_sap = ETHERTYPE_VLAN;
    424 	} else {
    425 		extra_len = 0;
    426 		mac_sap = sap;
    427 	}
    428 
    429 	mp = mac_header(dsp->ds_mh, addr, mac_sap, payload, extra_len);
    430 	if (mp == NULL)
    431 		return (NULL);
    432 
    433 	if ((vid == VLAN_ID_NONE && (pri == 0 ||
    434 	    dsp->ds_dlp->dl_tagmode == LINK_TAGMODE_VLANONLY)) || !is_ethernet)
    435 		return (mp);
    436 
    437 	/*
    438 	 * Fill in the tag information.
    439 	 */
    440 	ASSERT(MBLKL(mp) == sizeof (struct ether_header));
    441 	if (extra_len != 0) {
    442 		mp->b_wptr += extra_len;
    443 		evhp = (struct ether_vlan_header *)mp->b_rptr;
    444 		evhp->ether_tci = htons(VLAN_TCI(pri, ETHER_CFI, vid));
    445 		evhp->ether_type = htons(sap);
    446 	} else {
    447 		/*
    448 		 * The stream is ETHERTYPE_VLAN listener, so its VLAN tag is
    449 		 * in the payload. Update the priority.
    450 		 */
    451 		struct ether_vlan_extinfo *extinfo;
    452 		size_t len = sizeof (struct ether_vlan_extinfo);
    453 
    454 		ASSERT(sap == ETHERTYPE_VLAN);
    455 		ASSERT(payload != NULL);
    456 
    457 		if ((DB_REF(payload) > 1) || (MBLKL(payload) < len)) {
    458 			mblk_t *newmp;
    459 
    460 			/*
    461 			 * Because some DLS consumers only check the db_ref
    462 			 * count of the first mblk, we pullup 'payload' into
    463 			 * a single mblk.
    464 			 */
    465 			newmp = msgpullup(payload, -1);
    466 			if ((newmp == NULL) || (MBLKL(newmp) < len)) {
    467 				freemsg(newmp);
    468 				freemsg(mp);
    469 				return (NULL);
    470 			} else {
    471 				freemsg(payload);
    472 				*payloadp = payload = newmp;
    473 			}
    474 		}
    475 
    476 		extinfo = (struct ether_vlan_extinfo *)payload->b_rptr;
    477 		extinfo->ether_tci = htons(VLAN_TCI(pri, ETHER_CFI,
    478 		    VLAN_ID(ntohs(extinfo->ether_tci))));
    479 	}
    480 	return (mp);
    481 }
    482 
    483 void
    484 dls_rx_set(dld_str_t *dsp, dls_rx_t rx, void *arg)
    485 {
    486 	mutex_enter(&dsp->ds_lock);
    487 	dsp->ds_rx = rx;
    488 	dsp->ds_rx_arg = arg;
    489 	mutex_exit(&dsp->ds_lock);
    490 }
    491 
    492 static boolean_t
    493 dls_accept_common(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
    494     void **ds_rx_arg, boolean_t promisc, boolean_t promisc_loopback)
    495 {
    496 	dls_multicst_addr_t	*dmap;
    497 	size_t			addr_length = dsp->ds_mip->mi_addr_length;
    498 
    499 	/*
    500 	 * We must not accept packets if the dld_str_t is not marked as bound
    501 	 * or is being removed.
    502 	 */
    503 	if (dsp->ds_dlstate != DL_IDLE)
    504 		goto refuse;
    505 
    506 	if (dsp->ds_promisc != 0) {
    507 		/*
    508 		 * Filter out packets that arrived from the data path
    509 		 * (i_dls_link_rx) when promisc mode is on.
    510 		 */
    511 		if (!promisc)
    512 			goto refuse;
    513 		/*
    514 		 * If the dls_impl_t is in 'all physical' mode then
    515 		 * always accept.
    516 		 */
    517 		if (dsp->ds_promisc & DLS_PROMISC_PHYS)
    518 			goto accept;
    519 
    520 		/*
    521 		 * Loopback packets i.e. packets sent out by DLS on a given
    522 		 * mac end point, will be accepted back by DLS on loopback
    523 		 * from the mac, only in the 'all physical' mode which has been
    524 		 * covered by the previous check above
    525 		 */
    526 		if (promisc_loopback)
    527 			goto refuse;
    528 	}
    529 
    530 	switch (mhip->mhi_dsttype) {
    531 	case MAC_ADDRTYPE_UNICAST:
    532 	case MAC_ADDRTYPE_BROADCAST:
    533 		/*
    534 		 * We can accept unicast and broadcast packets because
    535 		 * filtering is already done by the mac layer.
    536 		 */
    537 		goto accept;
    538 	case MAC_ADDRTYPE_MULTICAST:
    539 		/*
    540 		 * Additional filtering is needed for multicast addresses
    541 		 * because different streams may be interested in different
    542 		 * addresses.
    543 		 */
    544 		if (dsp->ds_promisc & DLS_PROMISC_MULTI)
    545 			goto accept;
    546 
    547 		rw_enter(&dsp->ds_rw_lock, RW_READER);
    548 		for (dmap = dsp->ds_dmap; dmap != NULL;
    549 		    dmap = dmap->dma_nextp) {
    550 			if (memcmp(mhip->mhi_daddr, dmap->dma_addr,
    551 			    addr_length) == 0) {
    552 				rw_exit(&dsp->ds_rw_lock);
    553 				goto accept;
    554 			}
    555 		}
    556 		rw_exit(&dsp->ds_rw_lock);
    557 		break;
    558 	}
    559 
    560 refuse:
    561 	return (B_FALSE);
    562 
    563 accept:
    564 	/*
    565 	 * the returned ds_rx and ds_rx_arg will always be in sync.
    566 	 */
    567 	mutex_enter(&dsp->ds_lock);
    568 	*ds_rx = dsp->ds_rx;
    569 	*ds_rx_arg = dsp->ds_rx_arg;
    570 	mutex_exit(&dsp->ds_lock);
    571 
    572 	return (B_TRUE);
    573 }
    574 
    575 /* ARGSUSED */
    576 boolean_t
    577 dls_accept(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
    578     void **ds_rx_arg)
    579 {
    580 	return (dls_accept_common(dsp, mhip, ds_rx, ds_rx_arg, B_FALSE,
    581 	    B_FALSE));
    582 }
    583 
    584 boolean_t
    585 dls_accept_promisc(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
    586     void **ds_rx_arg, boolean_t loopback)
    587 {
    588 	return (dls_accept_common(dsp, mhip, ds_rx, ds_rx_arg, B_TRUE,
    589 	    loopback));
    590 }
    591 
    592 int
    593 dls_mac_active_set(dls_link_t *dlp)
    594 {
    595 	int err = 0;
    596 
    597 	/*
    598 	 * First client; add the primary unicast address.
    599 	 */
    600 	if (dlp->dl_nactive == 0) {
    601 		/*
    602 		 * First client; add the primary unicast address.
    603 		 */
    604 		mac_diag_t diag;
    605 
    606 		/* request the primary MAC address */
    607 		if ((err = mac_unicast_add(dlp->dl_mch, NULL,
    608 		    MAC_UNICAST_PRIMARY | MAC_UNICAST_TAG_DISABLE |
    609 		    MAC_UNICAST_DISABLE_TX_VID_CHECK, &dlp->dl_mah, 0,
    610 		    &diag)) != 0) {
    611 			return (err);
    612 		}
    613 
    614 		/*
    615 		 * Set the function to start receiving packets.
    616 		 */
    617 		mac_rx_set(dlp->dl_mch, i_dls_link_rx, dlp);
    618 	}
    619 	dlp->dl_nactive++;
    620 	return (0);
    621 }
    622 
    623 void
    624 dls_mac_active_clear(dls_link_t *dlp)
    625 {
    626 	if (--dlp->dl_nactive == 0) {
    627 		ASSERT(dlp->dl_mah != NULL);
    628 		(void) mac_unicast_remove(dlp->dl_mch, dlp->dl_mah);
    629 		dlp->dl_mah = NULL;
    630 		mac_rx_clear(dlp->dl_mch);
    631 	}
    632 }
    633 
    634 int
    635 dls_active_set(dld_str_t *dsp)
    636 {
    637 	int err = 0;
    638 
    639 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
    640 
    641 	if (dsp->ds_passivestate == DLD_PASSIVE)
    642 		return (0);
    643 
    644 	/* If we're already active, then there's nothing more to do. */
    645 	if ((dsp->ds_nactive == 0) &&
    646 	    ((err = dls_mac_active_set(dsp->ds_dlp)) != 0)) {
    647 		/* except for ENXIO all other errors are mapped to EBUSY */
    648 		if (err != ENXIO)
    649 			return (EBUSY);
    650 		return (err);
    651 	}
    652 
    653 	dsp->ds_passivestate = DLD_ACTIVE;
    654 	dsp->ds_nactive++;
    655 	return (0);
    656 }
    657 
    658 /*
    659  * Note that dls_active_set() is called whenever an active operation
    660  * (DL_BIND_REQ, DL_ENABMULTI_REQ ...) is processed and
    661  * dls_active_clear(dsp, B_FALSE) is called whenever the active operation
    662  * is being undone (DL_UNBIND_REQ, DL_DISABMULTI_REQ ...). In some cases,
    663  * a stream is closed without every active operation being undone and we
    664  * need to clear all the "active" states by calling
    665  * dls_active_clear(dsp, B_TRUE).
    666  */
    667 void
    668 dls_active_clear(dld_str_t *dsp, boolean_t all)
    669 {
    670 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
    671 
    672 	if (dsp->ds_passivestate == DLD_PASSIVE)
    673 		return;
    674 
    675 	if (all && dsp->ds_nactive == 0)
    676 		return;
    677 
    678 	ASSERT(dsp->ds_nactive > 0);
    679 
    680 	dsp->ds_nactive -= (all ? dsp->ds_nactive : 1);
    681 	if (dsp->ds_nactive != 0)
    682 		return;
    683 
    684 	ASSERT(dsp->ds_passivestate == DLD_ACTIVE);
    685 	dls_mac_active_clear(dsp->ds_dlp);
    686 	dsp->ds_passivestate = DLD_UNINITIALIZED;
    687 }
    688