xref: /illumos-gate/usr/src/uts/common/io/mac/mac_bcast.c (revision 47842382d52f28aa3173aa6b511781c322ccb6a2)
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 #include <sys/types.h>
27 #include <sys/sysmacros.h>
28 #include <sys/conf.h>
29 #include <sys/cmn_err.h>
30 #include <sys/list.h>
31 #include <sys/kmem.h>
32 #include <sys/stream.h>
33 #include <sys/modctl.h>
34 #include <sys/ddi.h>
35 #include <sys/sunddi.h>
36 #include <sys/atomic.h>
37 #include <sys/stat.h>
38 #include <sys/modhash.h>
39 #include <sys/strsubr.h>
40 #include <sys/strsun.h>
41 #include <sys/sdt.h>
42 #include <sys/mac.h>
43 #include <sys/mac_impl.h>
44 #include <sys/mac_client_impl.h>
45 #include <sys/mac_client_priv.h>
46 #include <sys/mac_flow_impl.h>
47 
48 /*
49  * Broadcast and multicast traffic must be distributed to the MAC clients
50  * that are defined on top of the same MAC. The set of
51  * destinations to which a multicast packet must be sent is a subset
52  * of all MAC clients defined on top of the MAC. A MAC client can be member
53  * of more than one such subset.
54  *
55  * To accomodate these requirements, we introduce broadcast groups.
56  * A broadcast group is associated with a broadcast or multicast
57  * address. The members of a broadcast group consist of the MAC clients
58  * that should received copies of packets sent to the address
59  * associated with the group, and are defined on top of the
60  * same MAC.
61  *
62  * The broadcast groups defined on top of a MAC are chained,
63  * hanging off the mac_impl_t. The broadcast group id's are
64  * unique globally (tracked by mac_bcast_id).
65  */
66 
67 /*
68  * The same MAC client may be added for different <addr,vid> tuple,
69  * we maintain a ref count for the number of times it has been added
70  * to account for deleting the MAC client from the group.
71  */
72 typedef struct mac_bcast_grp_mcip_s {
73 	mac_client_impl_t	*mgb_client;
74 	int			mgb_client_ref;
75 } mac_bcast_grp_mcip_t;
76 
77 typedef struct mac_bcast_grp_s {			/* Protected by */
78 	struct mac_bcast_grp_s	*mbg_next;		/* SL */
79 	void			*mbg_addr;		/* SL */
80 	uint16_t		mbg_vid;		/* SL */
81 	mac_impl_t		*mbg_mac_impl;		/* WO */
82 	mac_addrtype_t		mbg_addrtype;		/* WO */
83 	flow_entry_t		*mbg_flow_ent;		/* WO */
84 	mac_bcast_grp_mcip_t	*mbg_clients;		/* mi_rw_lock */
85 	uint_t			mbg_nclients;		/* mi_rw_lock */
86 	uint_t			mbg_nclients_alloc;	/* SL */
87 	uint64_t		mbg_clients_gen;	/* mi_rw_lock */
88 	uint32_t		mbg_id;			/* atomic */
89 } mac_bcast_grp_t;
90 
91 static kmem_cache_t *mac_bcast_grp_cache;
92 static uint32_t mac_bcast_id = 0;
93 
94 void
95 mac_bcast_init(void)
96 {
97 	mac_bcast_grp_cache = kmem_cache_create("mac_bcast_grp_cache",
98 	    sizeof (mac_bcast_grp_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
99 }
100 
101 void
102 mac_bcast_fini(void)
103 {
104 	kmem_cache_destroy(mac_bcast_grp_cache);
105 }
106 
107 mac_impl_t *
108 mac_bcast_grp_mip(void *grp)
109 {
110 	mac_bcast_grp_t *bcast_grp = grp;
111 
112 	return (bcast_grp->mbg_mac_impl);
113 }
114 
115 /*
116  * Free the specific broadcast group. Invoked when the last reference
117  * to the group is released.
118  */
119 void
120 mac_bcast_grp_free(void *bcast_grp)
121 {
122 	mac_bcast_grp_t	*grp = bcast_grp;
123 	mac_impl_t *mip = grp->mbg_mac_impl;
124 
125 	ASSERT(MAC_PERIM_HELD((mac_handle_t)mip));
126 
127 	if (grp->mbg_addrtype == MAC_ADDRTYPE_MULTICAST) {
128 		/*
129 		 * The address is a multicast address, have the
130 		 * underlying NIC leave the multicast group.
131 		 */
132 		(void) mip->mi_multicst(mip->mi_driver, B_FALSE, grp->mbg_addr);
133 	}
134 
135 	ASSERT(grp->mbg_addr != NULL);
136 	kmem_free(grp->mbg_addr, mip->mi_type->mt_addr_length);
137 	kmem_free(grp->mbg_clients,
138 	    grp->mbg_nclients_alloc * sizeof (mac_bcast_grp_mcip_t));
139 	mip->mi_bcast_ngrps--;
140 	kmem_cache_free(mac_bcast_grp_cache, grp);
141 }
142 
143 /*
144  * arg1: broadcast group
145  * arg2: sender MAC client if it is being sent by a MAC client,
146  * NULL if it was received from the wire.
147  */
148 void
149 mac_bcast_send(void *arg1, void *arg2, mblk_t *mp_chain, boolean_t is_loopback)
150 {
151 	mac_bcast_grp_t *grp = arg1;
152 	mac_client_impl_t *src_mcip = arg2, *dst_mcip;
153 	mac_impl_t *mip = grp->mbg_mac_impl;
154 	uint64_t gen;
155 	uint_t i;
156 	mblk_t *mp_chain1;
157 	flow_entry_t	*flent;
158 	int err;
159 
160 	rw_enter(&mip->mi_rw_lock, RW_READER);
161 
162 	/*
163 	 * Pass a copy of the mp chain to every MAC client except the sender
164 	 * MAC client, if the packet was not received from the underlying NIC.
165 	 *
166 	 * The broadcast group lock should not be held across calls to
167 	 * the flow's callback function, since the same group could
168 	 * potentially be accessed from the same context. When the lock
169 	 * is reacquired, changes to the broadcast group while the lock
170 	 * was released are caught using a generation counter incremented
171 	 * each time the list of MAC clients associated with the broadcast
172 	 * group is changed.
173 	 */
174 	for (i = 0; i < grp->mbg_nclients_alloc; i++) {
175 		dst_mcip = grp->mbg_clients[i].mgb_client;
176 		if (dst_mcip == NULL)
177 			continue;
178 		flent = dst_mcip->mci_flent;
179 		if (flent == NULL || dst_mcip == src_mcip) {
180 			/*
181 			 * Don't send a copy of the packet back to
182 			 * its sender.
183 			 */
184 			continue;
185 		}
186 
187 		/*
188 		 * It is important to hold a reference on the
189 		 * flow_ent here.
190 		 */
191 		if ((mp_chain1 = mac_copymsgchain_cksum(mp_chain)) == NULL)
192 			break;
193 		/*
194 		 * Fix the checksum for packets originating
195 		 * from the local machine.
196 		 */
197 		if ((src_mcip != NULL) &&
198 		    (mp_chain1 = mac_fix_cksum(mp_chain1)) == NULL)
199 			break;
200 
201 		FLOW_TRY_REFHOLD(flent, err);
202 		if (err != 0) {
203 			freemsgchain(mp_chain1);
204 			continue;
205 		}
206 
207 		gen = grp->mbg_clients_gen;
208 
209 		rw_exit(&mip->mi_rw_lock);
210 
211 		DTRACE_PROBE4(mac__bcast__send__to, mac_client_impl_t *,
212 		    src_mcip, flow_fn_t, dst_mcip->mci_flent->fe_cb_fn,
213 		    void *, dst_mcip->mci_flent->fe_cb_arg1,
214 		    void *, dst_mcip->mci_flent->fe_cb_arg2);
215 
216 		(dst_mcip->mci_flent->fe_cb_fn)(dst_mcip->mci_flent->fe_cb_arg1,
217 		    dst_mcip->mci_flent->fe_cb_arg2, mp_chain1, is_loopback);
218 		FLOW_REFRELE(flent);
219 
220 		rw_enter(&mip->mi_rw_lock, RW_READER);
221 
222 		/* update stats */
223 		if (grp->mbg_addrtype == MAC_ADDRTYPE_MULTICAST)
224 			dst_mcip->mci_stat_multircv++;
225 		else
226 			dst_mcip->mci_stat_brdcstrcv++;
227 
228 		if (grp->mbg_clients_gen != gen) {
229 			/*
230 			 * The list of MAC clients associated with the group
231 			 * was changed while the lock was released.
232 			 * Give up on the current packet.
233 			 */
234 			rw_exit(&mip->mi_rw_lock);
235 			freemsgchain(mp_chain);
236 			return;
237 		}
238 	}
239 	rw_exit(&mip->mi_rw_lock);
240 
241 	if (src_mcip != NULL) {
242 		/*
243 		 * The packet was sent from one of the MAC clients,
244 		 * so we need to send a copy of the packet to the
245 		 * underlying NIC so that it can be sent on the wire.
246 		 */
247 		mblk_t *rest;
248 
249 		src_mcip->mci_stat_multixmt++;
250 		src_mcip->mci_stat_brdcstxmt++;
251 
252 		rest = MAC_RING_TX_DEFAULT(mip, mp_chain);
253 		if (rest != NULL)
254 			freemsgchain(rest);
255 	} else {
256 		freemsgchain(mp_chain);
257 	}
258 }
259 
260 /*
261  * Add the specified MAC client to the group corresponding to the specified
262  * broadcast or multicast address.
263  * Return 0 on success, or an errno value on failure.
264  */
265 int
266 mac_bcast_add(mac_client_impl_t *mcip, const uint8_t *addr, uint16_t vid,
267     mac_addrtype_t addrtype)
268 {
269 	mac_impl_t 		*mip = mcip->mci_mip;
270 	mac_bcast_grp_t		*grp = NULL, **last_grp;
271 	size_t			addr_len = mip->mi_type->mt_addr_length;
272 	int			rc = 0;
273 	int			i, index = -1;
274 	mac_mcast_addrs_t	*mci_maddr = NULL;
275 	mac_mcast_addrs_t	*mi_maddr = NULL;
276 	mac_mcast_addrs_t	**last_maddr;
277 
278 	ASSERT(MAC_PERIM_HELD((mac_handle_t)mip));
279 
280 	ASSERT(addrtype == MAC_ADDRTYPE_MULTICAST ||
281 	    addrtype == MAC_ADDRTYPE_BROADCAST);
282 
283 	/* The list is protected by the perimeter */
284 	last_grp = &mip->mi_bcast_grp;
285 	for (grp = *last_grp; grp != NULL;
286 	    last_grp = &grp->mbg_next, grp = grp->mbg_next) {
287 		if (bcmp(grp->mbg_addr, addr, addr_len) == 0 &&
288 		    grp->mbg_vid == vid)
289 			break;
290 	}
291 
292 	if (grp == NULL) {
293 		/*
294 		 * The group does not yet exist, create it.
295 		 */
296 		flow_desc_t flow_desc;
297 		char flow_name[MAXFLOWNAMELEN];
298 
299 		grp = kmem_cache_alloc(mac_bcast_grp_cache, KM_SLEEP);
300 		bzero(grp, sizeof (mac_bcast_grp_t));
301 		grp->mbg_next = NULL;
302 		grp->mbg_mac_impl = mip;
303 
304 		DTRACE_PROBE1(mac__bcast__add__new__group, mac_bcast_grp_t *,
305 		    grp);
306 
307 		grp->mbg_addr = kmem_zalloc(addr_len, KM_SLEEP);
308 		bcopy(addr, grp->mbg_addr, addr_len);
309 		grp->mbg_addrtype = addrtype;
310 		grp->mbg_vid = vid;
311 
312 		/*
313 		 * Add a new flow to the underlying MAC.
314 		 */
315 		bzero(&flow_desc, sizeof (flow_desc));
316 		bcopy(addr, &flow_desc.fd_dst_mac, addr_len);
317 		flow_desc.fd_mac_len = (uint32_t)addr_len;
318 
319 		flow_desc.fd_mask = FLOW_LINK_DST;
320 		if (vid != 0) {
321 			flow_desc.fd_vid = vid;
322 			flow_desc.fd_mask |= FLOW_LINK_VID;
323 		}
324 
325 		grp->mbg_id = atomic_add_32_nv(&mac_bcast_id, 1);
326 		(void) sprintf(flow_name,
327 		    "mac/%s/mcast%d", mip->mi_name, grp->mbg_id);
328 
329 		rc = mac_flow_create(&flow_desc, NULL, flow_name,
330 		    grp, FLOW_MCAST, &grp->mbg_flow_ent);
331 		if (rc != 0) {
332 			kmem_free(grp->mbg_addr, addr_len);
333 			kmem_cache_free(mac_bcast_grp_cache, grp);
334 			return (rc);
335 		}
336 		grp->mbg_flow_ent->fe_mbg = grp;
337 		mip->mi_bcast_ngrps++;
338 
339 		/*
340 		 * Initial creation reference on the flow. This is released
341 		 * in the corresponding delete action i_mac_bcast_delete()
342 		 */
343 		FLOW_REFHOLD(grp->mbg_flow_ent);
344 
345 		/*
346 		 * When the multicast and broadcast packet is received
347 		 * by the underlying NIC, mac_rx_classify() will invoke
348 		 * mac_bcast_send() with arg2=NULL, which will cause
349 		 * mac_bcast_send() to send a copy of the packet(s)
350 		 * to every MAC client opened on top of the underlying MAC.
351 		 *
352 		 * When the mac_bcast_send() function is invoked from
353 		 * the transmit path of a MAC client, it will specify the
354 		 * transmitting MAC client as the arg2 value, which will
355 		 * allow mac_bcast_send() to skip that MAC client and not
356 		 * send it a copy of the packet.
357 		 *
358 		 * We program the classifier to dispatch matching broadcast
359 		 * packets to mac_bcast_send().
360 		 */
361 
362 		grp->mbg_flow_ent->fe_cb_fn = mac_bcast_send;
363 		grp->mbg_flow_ent->fe_cb_arg1 = grp;
364 		grp->mbg_flow_ent->fe_cb_arg2 = NULL;
365 
366 		rc = mac_flow_add(mip->mi_flow_tab, grp->mbg_flow_ent);
367 		if (rc != 0) {
368 			FLOW_FINAL_REFRELE(grp->mbg_flow_ent);
369 			return (rc);
370 		}
371 
372 		/*
373 		 * For multicast addresses, have the underlying MAC
374 		 * join the corresponsing multicast group.
375 		 */
376 		if (addrtype == MAC_ADDRTYPE_MULTICAST) {
377 			rc = mip->mi_multicst(mip->mi_driver, B_TRUE, addr);
378 			if (rc != 0) {
379 				mac_flow_remove(mip->mi_flow_tab,
380 				    grp->mbg_flow_ent, B_FALSE);
381 				mac_flow_wait(grp->mbg_flow_ent,
382 				    FLOW_DRIVER_UPCALL);
383 				FLOW_FINAL_REFRELE(grp->mbg_flow_ent);
384 				return (rc);
385 			}
386 		}
387 
388 		*last_grp = grp;
389 	}
390 
391 	ASSERT(grp->mbg_addrtype == addrtype);
392 
393 	/*
394 	 * Add the MAC client to the list of MAC clients associated
395 	 * with the group.
396 	 */
397 	rw_enter(&mip->mi_rw_lock, RW_WRITER);
398 	if (addrtype == MAC_ADDRTYPE_MULTICAST) {
399 		/*
400 		 * We maintain a separate list for each MAC client. Get
401 		 * the entry or add, if it is not present.
402 		 */
403 		last_maddr = &mcip->mci_mcast_addrs;
404 		for (mci_maddr = *last_maddr; mci_maddr != NULL;
405 		    last_maddr = &mci_maddr->mma_next,
406 		    mci_maddr = mci_maddr->mma_next) {
407 			if (bcmp(mci_maddr->mma_addr, addr, addr_len) == 0)
408 				break;
409 		}
410 		if (mci_maddr == NULL) {
411 			mci_maddr = kmem_zalloc(sizeof (mac_mcast_addrs_t),
412 			    KM_SLEEP);
413 			bcopy(addr, mci_maddr->mma_addr, addr_len);
414 			*last_maddr = mci_maddr;
415 		}
416 		mci_maddr->mma_ref++;
417 
418 		/*
419 		 * In case of a driver (say aggr), we also need this
420 		 * information on a per MAC instance basis.
421 		 */
422 		last_maddr = &mip->mi_mcast_addrs;
423 		for (mi_maddr = *last_maddr; mi_maddr != NULL;
424 		    last_maddr = &mi_maddr->mma_next,
425 		    mi_maddr = mi_maddr->mma_next) {
426 			if (bcmp(mi_maddr->mma_addr, addr, addr_len) == 0)
427 				break;
428 		}
429 		if (mi_maddr == NULL) {
430 			mi_maddr = kmem_zalloc(sizeof (mac_mcast_addrs_t),
431 			    KM_SLEEP);
432 			bcopy(addr, mi_maddr->mma_addr, addr_len);
433 			*last_maddr = mi_maddr;
434 		}
435 		mi_maddr->mma_ref++;
436 	}
437 	for (i = 0; i < grp->mbg_nclients_alloc; i++) {
438 		/*
439 		 * The MAC client was already added, say when we have
440 		 * different unicast addresses with the same vid.
441 		 * Just increment the ref and we are done.
442 		 */
443 		if (grp->mbg_clients[i].mgb_client == mcip) {
444 			grp->mbg_clients[i].mgb_client_ref++;
445 			goto add_done;
446 		} else if (grp->mbg_clients[i].mgb_client == NULL &&
447 		    index == -1) {
448 			index = i;
449 		}
450 	}
451 	if (grp->mbg_nclients_alloc == grp->mbg_nclients) {
452 		mac_bcast_grp_mcip_t	*new_clients;
453 		uint_t			new_size = grp->mbg_nclients+1;
454 
455 		new_clients = kmem_zalloc(new_size *
456 		    sizeof (mac_bcast_grp_mcip_t), KM_SLEEP);
457 
458 		if (grp->mbg_nclients > 0) {
459 			ASSERT(grp->mbg_clients != NULL);
460 			bcopy(grp->mbg_clients, new_clients, grp->mbg_nclients *
461 			    sizeof (mac_bcast_grp_mcip_t));
462 			kmem_free(grp->mbg_clients, grp->mbg_nclients *
463 			    sizeof (mac_bcast_grp_mcip_t));
464 		}
465 
466 		grp->mbg_clients = new_clients;
467 		grp->mbg_nclients_alloc = new_size;
468 		index = new_size - 1;
469 	}
470 
471 	ASSERT(index != -1);
472 	grp->mbg_clients[index].mgb_client = mcip;
473 	grp->mbg_clients[index].mgb_client_ref = 1;
474 	grp->mbg_nclients++;
475 	/*
476 	 * Since we're adding to the list of MAC clients using that group,
477 	 * kick the generation count, which will allow mac_bcast_send()
478 	 * to detect that condition after re-acquiring the lock.
479 	 */
480 	grp->mbg_clients_gen++;
481 add_done:
482 	rw_exit(&mip->mi_rw_lock);
483 
484 	return (0);
485 }
486 
487 /*
488  * Remove the specified MAC client from the group corresponding to
489  * the specific broadcast or multicast address.
490  *
491  * Note: mac_bcast_delete() calls  mac_remove_flow() which
492  * will call cv_wait for fe_refcnt to drop to 0. So this function
493  * should not be called from interrupt or STREAMS context.
494  */
495 void
496 mac_bcast_delete(mac_client_impl_t *mcip, const uint8_t *addr, uint16_t vid)
497 {
498 	mac_impl_t *mip = mcip->mci_mip;
499 	mac_bcast_grp_t *grp = NULL, **prev;
500 	size_t addr_len = mip->mi_type->mt_addr_length;
501 	flow_entry_t *flent;
502 	uint_t i;
503 	mac_mcast_addrs_t	*maddr = NULL;
504 	mac_mcast_addrs_t	**mprev;
505 
506 	ASSERT(MAC_PERIM_HELD((mac_handle_t)mip));
507 
508 	/* find the broadcast group. The list is protected by the perimeter */
509 	prev = &mip->mi_bcast_grp;
510 	for (grp = mip->mi_bcast_grp; grp != NULL; prev = &grp->mbg_next,
511 	    grp = grp->mbg_next) {
512 		if (bcmp(grp->mbg_addr, addr, addr_len) == 0 &&
513 		    grp->mbg_vid == vid)
514 			break;
515 	}
516 	ASSERT(grp != NULL);
517 
518 	/*
519 	 * Remove the MAC client from the list of MAC clients associated
520 	 * with that broadcast group.
521 	 *
522 	 * We mark the mbg_clients[] location corresponding to the removed MAC
523 	 * client NULL and reuse that location when we add a new MAC client.
524 	 */
525 
526 	rw_enter(&mip->mi_rw_lock, RW_WRITER);
527 
528 	for (i = 0; i < grp->mbg_nclients_alloc; i++) {
529 		if (grp->mbg_clients[i].mgb_client == mcip)
530 			break;
531 	}
532 
533 	ASSERT(i < grp->mbg_nclients_alloc);
534 	/*
535 	 * If there are more references to this MAC client, then we let
536 	 * it remain till it goes to 0.
537 	 */
538 	if (--grp->mbg_clients[i].mgb_client_ref > 0)
539 		goto update_maddr;
540 
541 	grp->mbg_clients[i].mgb_client = NULL;
542 	grp->mbg_clients[i].mgb_client_ref = 0;
543 
544 	/*
545 	 * Since we're removing from the list of MAC clients using that group,
546 	 * kick the generation count, which will allow mac_bcast_send()
547 	 * to detect that condition.
548 	 */
549 	grp->mbg_clients_gen++;
550 
551 	if (--grp->mbg_nclients == 0) {
552 		/*
553 		 * The last MAC client of the group was just removed.
554 		 * Unlink the current group from the list of groups
555 		 * defined on top of the underlying NIC. The group
556 		 * structure will stay around until the last reference
557 		 * is dropped.
558 		 */
559 		*prev = grp->mbg_next;
560 	}
561 update_maddr:
562 	if (grp->mbg_addrtype == MAC_ADDRTYPE_MULTICAST) {
563 		mprev = &mcip->mci_mcast_addrs;
564 		for (maddr = mcip->mci_mcast_addrs; maddr != NULL;
565 		    mprev = &maddr->mma_next, maddr = maddr->mma_next) {
566 			if (bcmp(grp->mbg_addr, maddr->mma_addr,
567 			    mip->mi_type->mt_addr_length) == 0)
568 				break;
569 		}
570 		ASSERT(maddr != NULL);
571 		if (--maddr->mma_ref == 0) {
572 			*mprev = maddr->mma_next;
573 			maddr->mma_next = NULL;
574 			kmem_free(maddr, sizeof (mac_mcast_addrs_t));
575 		}
576 
577 		mprev = &mip->mi_mcast_addrs;
578 		for (maddr = mip->mi_mcast_addrs; maddr != NULL;
579 		    mprev = &maddr->mma_next, maddr = maddr->mma_next) {
580 			if (bcmp(grp->mbg_addr, maddr->mma_addr,
581 			    mip->mi_type->mt_addr_length) == 0)
582 				break;
583 		}
584 		ASSERT(maddr != NULL);
585 		if (--maddr->mma_ref == 0) {
586 			*mprev = maddr->mma_next;
587 			maddr->mma_next = NULL;
588 			kmem_free(maddr, sizeof (mac_mcast_addrs_t));
589 		}
590 	}
591 	rw_exit(&mip->mi_rw_lock);
592 
593 	/*
594 	 * If the group itself is being removed, remove the
595 	 * corresponding flow from the underlying NIC.
596 	 */
597 	flent = grp->mbg_flow_ent;
598 	if (grp->mbg_nclients == 0) {
599 		mac_flow_remove(mip->mi_flow_tab, flent, B_FALSE);
600 		mac_flow_wait(flent, FLOW_DRIVER_UPCALL);
601 		FLOW_FINAL_REFRELE(flent);
602 	}
603 }
604 
605 /*
606  * This will be called by a driver, such as aggr, when a port is added/removed
607  * to add/remove the port to/from all the multcast addresses for that aggr.
608  */
609 void
610 mac_bcast_refresh(mac_impl_t *mip, mac_multicst_t refresh_fn, void *arg,
611     boolean_t add)
612 {
613 	mac_mcast_addrs_t *grp, *next;
614 
615 	ASSERT(refresh_fn != NULL);
616 
617 	ASSERT(MAC_PERIM_HELD((mac_handle_t)mip));
618 
619 	/*
620 	 * Walk the multicast address list and call the refresh function for
621 	 * each address.
622 	 */
623 
624 	for (grp = mip->mi_mcast_addrs; grp != NULL; grp = next) {
625 		/*
626 		 * Save the next pointer just in case the refresh
627 		 * function's action causes the group entry to be
628 		 * freed.
629 		 * We won't be adding to this list as part of the
630 		 * refresh.
631 		 */
632 		next = grp->mma_next;
633 		refresh_fn(arg, add, grp->mma_addr);
634 	}
635 }
636 
637 /*
638  * Walk the MAC client's multicast address list and add/remove the addr/vid
639  * ('arg' is 'flent') to all the addresses.
640  */
641 void
642 mac_client_bcast_refresh(mac_client_impl_t *mcip, mac_multicst_t refresh_fn,
643     void *arg, boolean_t add)
644 {
645 	mac_mcast_addrs_t *grp, *next;
646 	mac_impl_t		*mip = mcip->mci_mip;
647 
648 	ASSERT(refresh_fn != NULL);
649 
650 	ASSERT(MAC_PERIM_HELD((mac_handle_t)mip));
651 	/*
652 	 * Walk the multicast address list and call the refresh function for
653 	 * each address.
654 	 * Broadcast addresses are not added or removed through the multicast
655 	 * entry points, so don't include them as part of the refresh.
656 	 */
657 	for (grp = mcip->mci_mcast_addrs; grp != NULL; grp = next) {
658 		/*
659 		 * Save the next pointer just in case the refresh
660 		 * function's action causes the group entry to be
661 		 * freed.
662 		 * We won't be adding to this list as part of the
663 		 * refresh.
664 		 */
665 		next = grp->mma_next;
666 		refresh_fn(arg, add, grp->mma_addr);
667 	}
668 }
669