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