xref: /illumos-gate/usr/src/uts/common/io/aggr/aggr_grp.c (revision da604a3e)
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 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * IEEE 802.3ad Link Aggregation -- Link Aggregation Groups.
30  *
31  * An instance of the structure aggr_grp_t is allocated for each
32  * link aggregation group. When created, aggr_grp_t objects are
33  * entered into the aggr_grp_hash hash table maintained by the modhash
34  * module. The hash key is the port number associated with the link
35  * aggregation group. The port number associated with a group corresponds
36  * the key associated with the group.
37  *
38  * A set of MAC ports are associated with each association group.
39  */
40 
41 #include <sys/types.h>
42 #include <sys/sysmacros.h>
43 #include <sys/conf.h>
44 #include <sys/cmn_err.h>
45 #include <sys/list.h>
46 #include <sys/ksynch.h>
47 #include <sys/kmem.h>
48 #include <sys/stream.h>
49 #include <sys/modctl.h>
50 #include <sys/ddi.h>
51 #include <sys/sunddi.h>
52 #include <sys/atomic.h>
53 #include <sys/stat.h>
54 #include <sys/modhash.h>
55 #include <sys/strsun.h>
56 #include <sys/dlpi.h>
57 
58 #include <sys/aggr.h>
59 #include <sys/aggr_impl.h>
60 
61 static int aggr_m_start(void *);
62 static void aggr_m_stop(void *);
63 static int aggr_m_promisc(void *, boolean_t);
64 static int aggr_m_multicst(void *, boolean_t, const uint8_t *);
65 static int aggr_m_unicst(void *, const uint8_t *);
66 static int aggr_m_stat(void *, uint_t, uint64_t *);
67 static void aggr_m_resources(void *);
68 static void aggr_m_ioctl(void *, queue_t *, mblk_t *);
69 static boolean_t aggr_m_capab_get(void *, mac_capab_t, void *);
70 
71 static aggr_port_t *aggr_grp_port_lookup(aggr_grp_t *, const char *);
72 static int aggr_grp_rem_port(aggr_grp_t *, aggr_port_t *, boolean_t *,
73     boolean_t *);
74 static void aggr_grp_capab_set(aggr_grp_t *);
75 static boolean_t aggr_grp_capab_check(aggr_grp_t *, aggr_port_t *);
76 
77 static kmem_cache_t	*aggr_grp_cache;
78 static mod_hash_t	*aggr_grp_hash;
79 static krwlock_t	aggr_grp_lock;
80 static uint_t		aggr_grp_cnt;
81 
82 #define	GRP_HASHSZ		64
83 #define	GRP_HASH_KEY(key)	((mod_hash_key_t)(uintptr_t)key)
84 
85 static uchar_t aggr_zero_mac[] = {0, 0, 0, 0, 0, 0};
86 
87 /* used by grp_info_walker */
88 typedef struct aggr_grp_info_state {
89 	uint32_t	ls_group_key;
90 	boolean_t	ls_group_found;
91 	aggr_grp_info_new_grp_fn_t ls_new_grp_fn;
92 	aggr_grp_info_new_port_fn_t ls_new_port_fn;
93 	void		*ls_fn_arg;
94 	int		ls_rc;
95 } aggr_grp_info_state_t;
96 
97 #define	AGGR_M_CALLBACK_FLAGS	(MC_RESOURCES | MC_IOCTL | MC_GETCAPAB)
98 
99 static mac_callbacks_t aggr_m_callbacks = {
100 	AGGR_M_CALLBACK_FLAGS,
101 	aggr_m_stat,
102 	aggr_m_start,
103 	aggr_m_stop,
104 	aggr_m_promisc,
105 	aggr_m_multicst,
106 	aggr_m_unicst,
107 	aggr_m_tx,
108 	aggr_m_resources,
109 	aggr_m_ioctl,
110 	aggr_m_capab_get
111 };
112 
113 /*ARGSUSED*/
114 static int
115 aggr_grp_constructor(void *buf, void *arg, int kmflag)
116 {
117 	aggr_grp_t *grp = buf;
118 
119 	bzero(grp, sizeof (*grp));
120 	rw_init(&grp->lg_lock, NULL, RW_DRIVER, NULL);
121 	mutex_init(&grp->aggr.gl_lock, NULL, MUTEX_DEFAULT, NULL);
122 
123 	grp->lg_link_state = LINK_STATE_UNKNOWN;
124 
125 	return (0);
126 }
127 
128 /*ARGSUSED*/
129 static void
130 aggr_grp_destructor(void *buf, void *arg)
131 {
132 	aggr_grp_t *grp = buf;
133 
134 	if (grp->lg_tx_ports != NULL) {
135 		kmem_free(grp->lg_tx_ports,
136 		    grp->lg_tx_ports_size * sizeof (aggr_port_t *));
137 	}
138 
139 	mutex_destroy(&grp->aggr.gl_lock);
140 	rw_destroy(&grp->lg_lock);
141 }
142 
143 void
144 aggr_grp_init(void)
145 {
146 	aggr_grp_cache = kmem_cache_create("aggr_grp_cache",
147 	    sizeof (aggr_grp_t), 0, aggr_grp_constructor,
148 	    aggr_grp_destructor, NULL, NULL, NULL, 0);
149 
150 	aggr_grp_hash = mod_hash_create_idhash("aggr_grp_hash",
151 	    GRP_HASHSZ, mod_hash_null_valdtor);
152 	rw_init(&aggr_grp_lock, NULL, RW_DEFAULT, NULL);
153 	aggr_grp_cnt = 0;
154 }
155 
156 void
157 aggr_grp_fini(void)
158 {
159 	rw_destroy(&aggr_grp_lock);
160 	mod_hash_destroy_idhash(aggr_grp_hash);
161 	kmem_cache_destroy(aggr_grp_cache);
162 }
163 
164 uint_t
165 aggr_grp_count(void)
166 {
167 	uint_t	count;
168 
169 	rw_enter(&aggr_grp_lock, RW_READER);
170 	count = aggr_grp_cnt;
171 	rw_exit(&aggr_grp_lock);
172 	return (count);
173 }
174 
175 /*
176  * Attach a port to a link aggregation group.
177  *
178  * A port is attached to a link aggregation group once its speed
179  * and link state have been verified.
180  *
181  * Returns B_TRUE if the group link state or speed has changed. If
182  * it's the case, the caller must notify the MAC layer via a call
183  * to mac_link().
184  */
185 boolean_t
186 aggr_grp_attach_port(aggr_grp_t *grp, aggr_port_t *port)
187 {
188 	boolean_t link_state_changed = B_FALSE;
189 
190 	ASSERT(AGGR_LACP_LOCK_HELD(grp));
191 	ASSERT(RW_WRITE_HELD(&grp->lg_lock));
192 	ASSERT(RW_WRITE_HELD(&port->lp_lock));
193 
194 	if (port->lp_state == AGGR_PORT_STATE_ATTACHED)
195 		return (B_FALSE);
196 
197 	/*
198 	 * Validate the MAC port link speed and update the group
199 	 * link speed if needed.
200 	 */
201 	if (port->lp_ifspeed == 0 ||
202 	    port->lp_link_state != LINK_STATE_UP ||
203 	    port->lp_link_duplex != LINK_DUPLEX_FULL) {
204 		/*
205 		 * Can't attach a MAC port with unknown link speed,
206 		 * down link, or not in full duplex mode.
207 		 */
208 		return (B_FALSE);
209 	}
210 
211 	if (grp->lg_ifspeed == 0) {
212 		/*
213 		 * The group inherits the speed of the first link being
214 		 * attached.
215 		 */
216 		grp->lg_ifspeed = port->lp_ifspeed;
217 		link_state_changed = B_TRUE;
218 	} else if (grp->lg_ifspeed != port->lp_ifspeed) {
219 		/*
220 		 * The link speed of the MAC port must be the same as
221 		 * the group link speed, as per 802.3ad. Since it is
222 		 * not, the attach is cancelled.
223 		 */
224 		return (B_FALSE);
225 	}
226 
227 	grp->lg_nattached_ports++;
228 
229 	/*
230 	 * Update the group link state.
231 	 */
232 	if (grp->lg_link_state != LINK_STATE_UP) {
233 		grp->lg_link_state = LINK_STATE_UP;
234 		grp->lg_link_duplex = LINK_DUPLEX_FULL;
235 		link_state_changed = B_TRUE;
236 	}
237 
238 	aggr_grp_multicst_port(port, B_TRUE);
239 
240 	/*
241 	 * Update port's state.
242 	 */
243 	port->lp_state = AGGR_PORT_STATE_ATTACHED;
244 
245 	/*
246 	 * Set port's receive callback
247 	 */
248 	port->lp_mrh = mac_rx_add(port->lp_mh, aggr_recv_cb, (void *)port);
249 
250 	/*
251 	 * If LACP is OFF, the port can be used to send data as soon
252 	 * as its link is up and verified to be compatible with the
253 	 * aggregation.
254 	 *
255 	 * If LACP is active or passive, notify the LACP subsystem, which
256 	 * will enable sending on the port following the LACP protocol.
257 	 */
258 	if (grp->lg_lacp_mode == AGGR_LACP_OFF)
259 		aggr_send_port_enable(port);
260 	else
261 		aggr_lacp_port_attached(port);
262 
263 	return (link_state_changed);
264 }
265 
266 boolean_t
267 aggr_grp_detach_port(aggr_grp_t *grp, aggr_port_t *port)
268 {
269 	boolean_t link_state_changed = B_FALSE;
270 
271 	ASSERT(RW_WRITE_HELD(&grp->lg_lock));
272 	ASSERT(RW_WRITE_HELD(&port->lp_lock));
273 	ASSERT(AGGR_LACP_LOCK_HELD(grp));
274 
275 	/* update state */
276 	if (port->lp_state != AGGR_PORT_STATE_ATTACHED)
277 		return (B_FALSE);
278 
279 	mac_rx_remove(port->lp_mh, port->lp_mrh);
280 	port->lp_state = AGGR_PORT_STATE_STANDBY;
281 
282 	aggr_grp_multicst_port(port, B_FALSE);
283 
284 	if (grp->lg_lacp_mode == AGGR_LACP_OFF)
285 		aggr_send_port_disable(port);
286 	else
287 		aggr_lacp_port_detached(port);
288 
289 	grp->lg_nattached_ports--;
290 	if (grp->lg_nattached_ports == 0) {
291 		/* the last attached MAC port of the group is being detached */
292 		grp->lg_ifspeed = 0;
293 		grp->lg_link_state = LINK_STATE_DOWN;
294 		grp->lg_link_duplex = LINK_DUPLEX_UNKNOWN;
295 		link_state_changed = B_TRUE;
296 	}
297 
298 	return (link_state_changed);
299 }
300 
301 /*
302  * Update the MAC addresses of the constituent ports of the specified
303  * group. This function is invoked:
304  * - after creating a new aggregation group.
305  * - after adding new ports to an aggregation group.
306  * - after removing a port from a group when the MAC address of
307  *   that port was used for the MAC address of the group.
308  * - after the MAC address of a port changed when the MAC address
309  *   of that port was used for the MAC address of the group.
310  *
311  * Return true if the link state of the aggregation changed, for example
312  * as a result of a failure changing the MAC address of one of the
313  * constituent ports.
314  */
315 boolean_t
316 aggr_grp_update_ports_mac(aggr_grp_t *grp)
317 {
318 	aggr_port_t *cport;
319 	boolean_t link_state_changed = B_FALSE;
320 
321 	ASSERT(RW_WRITE_HELD(&grp->lg_lock));
322 
323 	if (grp->lg_closing)
324 		return (link_state_changed);
325 
326 	for (cport = grp->lg_ports; cport != NULL;
327 	    cport = cport->lp_next) {
328 		rw_enter(&cport->lp_lock, RW_WRITER);
329 		if (aggr_port_unicst(cport, grp->lg_addr) != 0) {
330 			link_state_changed = link_state_changed ||
331 			    aggr_grp_detach_port(grp, cport);
332 		} else {
333 			/*
334 			 * If a port was detached because of a previous
335 			 * failure changing the MAC address, the port is
336 			 * reattached when it successfully changes the MAC
337 			 * address now, and this might cause the link state
338 			 * of the aggregation to change.
339 			 */
340 			link_state_changed = link_state_changed ||
341 			    aggr_grp_attach_port(grp, cport);
342 		}
343 		rw_exit(&cport->lp_lock);
344 	}
345 	return (link_state_changed);
346 }
347 
348 /*
349  * Invoked when the MAC address of a port has changed. If the port's
350  * MAC address was used for the group MAC address, set mac_addr_changedp
351  * to B_TRUE to indicate to the caller that it should send a MAC_NOTE_UNICST
352  * notification. If the link state changes due to detach/attach of
353  * the constituent port, set link_state_changedp to B_TRUE to indicate
354  * to the caller that it should send a MAC_NOTE_LINK notification. In both
355  * cases, it is the responsibility of the caller to invoke notification
356  * functions after releasing the the port lock.
357  */
358 void
359 aggr_grp_port_mac_changed(aggr_grp_t *grp, aggr_port_t *port,
360     boolean_t *mac_addr_changedp, boolean_t *link_state_changedp)
361 {
362 	ASSERT(AGGR_LACP_LOCK_HELD(grp));
363 	ASSERT(RW_WRITE_HELD(&grp->lg_lock));
364 	ASSERT(RW_WRITE_HELD(&port->lp_lock));
365 	ASSERT(mac_addr_changedp != NULL);
366 	ASSERT(link_state_changedp != NULL);
367 
368 	*mac_addr_changedp = B_FALSE;
369 	*link_state_changedp = B_FALSE;
370 
371 	if (grp->lg_addr_fixed) {
372 		/*
373 		 * The group is using a fixed MAC address or an automatic
374 		 * MAC address has not been set.
375 		 */
376 		return;
377 	}
378 
379 	if (grp->lg_mac_addr_port == port) {
380 		/*
381 		 * The MAC address of the port was assigned to the group
382 		 * MAC address. Update the group MAC address.
383 		 */
384 		bcopy(port->lp_addr, grp->lg_addr, ETHERADDRL);
385 		*mac_addr_changedp = B_TRUE;
386 	} else {
387 		/*
388 		 * Update the actual port MAC address to the MAC address
389 		 * of the group.
390 		 */
391 		if (aggr_port_unicst(port, grp->lg_addr) != 0) {
392 			*link_state_changedp = aggr_grp_detach_port(grp, port);
393 		} else {
394 			/*
395 			 * If a port was detached because of a previous
396 			 * failure changing the MAC address, the port is
397 			 * reattached when it successfully changes the MAC
398 			 * address now, and this might cause the link state
399 			 * of the aggregation to change.
400 			 */
401 			*link_state_changedp = aggr_grp_attach_port(grp, port);
402 		}
403 	}
404 }
405 
406 /*
407  * Add a port to a link aggregation group.
408  */
409 static int
410 aggr_grp_add_port(aggr_grp_t *grp, const char *name, aggr_port_t **pp)
411 {
412 	aggr_port_t *port, **cport;
413 	int err;
414 
415 	ASSERT(AGGR_LACP_LOCK_HELD(grp));
416 	ASSERT(RW_WRITE_HELD(&grp->lg_lock));
417 
418 	/* create new port */
419 	err = aggr_port_create(name, &port);
420 	if (err != 0)
421 		return (err);
422 
423 	rw_enter(&port->lp_lock, RW_WRITER);
424 
425 	/* add port to list of group constituent ports */
426 	cport = &grp->lg_ports;
427 	while (*cport != NULL)
428 		cport = &((*cport)->lp_next);
429 	*cport = port;
430 
431 	/*
432 	 * Back reference to the group it is member of. A port always
433 	 * holds a reference to its group to ensure that the back
434 	 * reference is always valid.
435 	 */
436 	port->lp_grp = grp;
437 	AGGR_GRP_REFHOLD(grp);
438 	grp->lg_nports++;
439 
440 	aggr_lacp_init_port(port);
441 
442 	/*
443 	 * Initialize the callback functions for this port. Note that this
444 	 * can only be done after the lp_grp field is set.
445 	 */
446 	aggr_port_init_callbacks(port);
447 
448 	rw_exit(&port->lp_lock);
449 
450 	if (pp != NULL)
451 		*pp = port;
452 
453 	return (0);
454 }
455 
456 /*
457  * Add one or more ports to an existing link aggregation group.
458  */
459 int
460 aggr_grp_add_ports(uint32_t key, uint_t nports, laioc_port_t *ports)
461 {
462 	int rc, i, nadded = 0;
463 	aggr_grp_t *grp = NULL;
464 	aggr_port_t *port;
465 	boolean_t link_state_changed = B_FALSE;
466 
467 	/* get group corresponding to key */
468 	rw_enter(&aggr_grp_lock, RW_READER);
469 	if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(key),
470 	    (mod_hash_val_t *)&grp) != 0) {
471 		rw_exit(&aggr_grp_lock);
472 		return (ENOENT);
473 	}
474 	AGGR_GRP_REFHOLD(grp);
475 	rw_exit(&aggr_grp_lock);
476 
477 	AGGR_LACP_LOCK(grp);
478 	rw_enter(&grp->lg_lock, RW_WRITER);
479 
480 	/* add the specified ports to group */
481 	for (i = 0; i < nports; i++) {
482 		/* add port to group */
483 		if ((rc = aggr_grp_add_port(grp, ports[i].lp_devname, &port)) !=
484 		    0) {
485 			goto bail;
486 		}
487 		ASSERT(port != NULL);
488 		nadded++;
489 
490 		/* check capabilities */
491 		if (!aggr_grp_capab_check(grp, port)) {
492 			rc = ENOTSUP;
493 			goto bail;
494 		}
495 
496 		/* start port if group has already been started */
497 		if (grp->lg_started) {
498 			rw_enter(&port->lp_lock, RW_WRITER);
499 			rc = aggr_port_start(port);
500 			if (rc != 0) {
501 				rw_exit(&port->lp_lock);
502 				goto bail;
503 			}
504 
505 			/* set port promiscuous mode */
506 			rc = aggr_port_promisc(port, grp->lg_promisc);
507 			if (rc != 0) {
508 				rw_exit(&port->lp_lock);
509 				goto bail;
510 			}
511 			rw_exit(&port->lp_lock);
512 		}
513 
514 		/*
515 		 * Attach each port if necessary.
516 		 */
517 		link_state_changed = link_state_changed ||
518 		    aggr_port_notify_link(grp, port, B_FALSE);
519 	}
520 
521 	/* update the MAC address of the constituent ports */
522 	link_state_changed = link_state_changed ||
523 	    aggr_grp_update_ports_mac(grp);
524 
525 	if (link_state_changed)
526 		mac_link_update(grp->lg_mh, grp->lg_link_state);
527 
528 bail:
529 	if (rc != 0) {
530 		/* stop and remove ports that have been added */
531 		for (i = 0; i < nadded && !grp->lg_closing; i++) {
532 			port = aggr_grp_port_lookup(grp, ports[i].lp_devname);
533 			ASSERT(port != NULL);
534 			if (grp->lg_started) {
535 				rw_enter(&port->lp_lock, RW_WRITER);
536 				aggr_port_stop(port);
537 				rw_exit(&port->lp_lock);
538 			}
539 			(void) aggr_grp_rem_port(grp, port, NULL, NULL);
540 		}
541 	}
542 
543 	rw_exit(&grp->lg_lock);
544 	AGGR_LACP_UNLOCK(grp);
545 	if (rc == 0 && !grp->lg_closing)
546 		mac_resource_update(grp->lg_mh);
547 	AGGR_GRP_REFRELE(grp);
548 	return (rc);
549 }
550 
551 /*
552  * Update properties of an existing link aggregation group.
553  */
554 int
555 aggr_grp_modify(uint32_t key, aggr_grp_t *grp_arg, uint8_t update_mask,
556     uint32_t policy, boolean_t mac_fixed, const uchar_t *mac_addr,
557     aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer)
558 {
559 	int rc = 0;
560 	aggr_grp_t *grp = NULL;
561 	boolean_t mac_addr_changed = B_FALSE;
562 	boolean_t link_state_changed = B_FALSE;
563 
564 	if (grp_arg == NULL) {
565 		/* get group corresponding to key */
566 		rw_enter(&aggr_grp_lock, RW_READER);
567 		if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(key),
568 		    (mod_hash_val_t *)&grp) != 0) {
569 			rc = ENOENT;
570 			goto bail;
571 		}
572 		AGGR_LACP_LOCK(grp);
573 		rw_enter(&grp->lg_lock, RW_WRITER);
574 	} else {
575 		grp = grp_arg;
576 		ASSERT(AGGR_LACP_LOCK_HELD(grp));
577 		ASSERT(RW_WRITE_HELD(&grp->lg_lock));
578 	}
579 
580 	ASSERT(RW_WRITE_HELD(&grp->lg_lock) || RW_READ_HELD(&grp->lg_lock));
581 	AGGR_GRP_REFHOLD(grp);
582 
583 	/* validate fixed address if specified */
584 	if ((update_mask & AGGR_MODIFY_MAC) && mac_fixed &&
585 	    ((bcmp(aggr_zero_mac, mac_addr, ETHERADDRL) == 0) ||
586 	    (mac_addr[0] & 0x01))) {
587 		rc = EINVAL;
588 		goto bail;
589 	}
590 
591 	/* update policy if requested */
592 	if (update_mask & AGGR_MODIFY_POLICY)
593 		aggr_send_update_policy(grp, policy);
594 
595 	/* update unicast MAC address if requested */
596 	if (update_mask & AGGR_MODIFY_MAC) {
597 		if (mac_fixed) {
598 			/* user-supplied MAC address */
599 			grp->lg_mac_addr_port = NULL;
600 			if (bcmp(mac_addr, grp->lg_addr, ETHERADDRL) != 0) {
601 				bcopy(mac_addr, grp->lg_addr, ETHERADDRL);
602 				mac_addr_changed = B_TRUE;
603 			}
604 		} else if (grp->lg_addr_fixed) {
605 			/* switch from user-supplied to automatic */
606 			aggr_port_t *port = grp->lg_ports;
607 
608 			rw_enter(&port->lp_lock, RW_WRITER);
609 			bcopy(port->lp_addr, grp->lg_addr, ETHERADDRL);
610 			grp->lg_mac_addr_port = port;
611 			mac_addr_changed = B_TRUE;
612 			rw_exit(&port->lp_lock);
613 		}
614 		grp->lg_addr_fixed = mac_fixed;
615 	}
616 
617 	if (mac_addr_changed)
618 		link_state_changed = aggr_grp_update_ports_mac(grp);
619 
620 	if (update_mask & AGGR_MODIFY_LACP_MODE)
621 		aggr_lacp_update_mode(grp, lacp_mode);
622 
623 	if ((update_mask & AGGR_MODIFY_LACP_TIMER) && !grp->lg_closing)
624 		aggr_lacp_update_timer(grp, lacp_timer);
625 
626 bail:
627 	if (grp != NULL && !grp->lg_closing) {
628 		/*
629 		 * If grp_arg is non-NULL, this function is called from
630 		 * mac_unicst_set(), and the MAC_NOTE_UNICST notification
631 		 * will be sent there.
632 		 */
633 		if ((grp_arg == NULL) && mac_addr_changed)
634 			mac_unicst_update(grp->lg_mh, grp->lg_addr);
635 
636 		if (link_state_changed)
637 			mac_link_update(grp->lg_mh, grp->lg_link_state);
638 
639 	}
640 
641 	if (grp_arg == NULL) {
642 		if (grp != NULL) {
643 			rw_exit(&grp->lg_lock);
644 			AGGR_LACP_UNLOCK(grp);
645 		}
646 		rw_exit(&aggr_grp_lock);
647 	}
648 
649 	if (grp != NULL)
650 		AGGR_GRP_REFRELE(grp);
651 
652 	return (rc);
653 }
654 
655 /*
656  * Create a new link aggregation group upon request from administrator.
657  * Returns 0 on success, an errno on failure.
658  */
659 int
660 aggr_grp_create(uint32_t key, uint_t nports, laioc_port_t *ports,
661     uint32_t policy, boolean_t mac_fixed, uchar_t *mac_addr,
662     aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer)
663 {
664 	aggr_grp_t *grp = NULL;
665 	aggr_port_t *port;
666 	mac_register_t *mac;
667 	boolean_t link_state_changed;
668 	int err;
669 	int i;
670 
671 	/* need at least one port */
672 	if (nports == 0)
673 		return (EINVAL);
674 
675 	rw_enter(&aggr_grp_lock, RW_WRITER);
676 
677 	/* does a group with the same key already exist? */
678 	err = mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(key),
679 	    (mod_hash_val_t *)&grp);
680 	if (err == 0) {
681 		rw_exit(&aggr_grp_lock);
682 		return (EEXIST);
683 	}
684 
685 	grp = kmem_cache_alloc(aggr_grp_cache, KM_SLEEP);
686 
687 	AGGR_LACP_LOCK(grp);
688 	rw_enter(&grp->lg_lock, RW_WRITER);
689 
690 	grp->lg_refs = 1;
691 	grp->lg_closing = B_FALSE;
692 	grp->lg_key = key;
693 
694 	grp->lg_ifspeed = 0;
695 	grp->lg_link_state = LINK_STATE_UNKNOWN;
696 	grp->lg_link_duplex = LINK_DUPLEX_UNKNOWN;
697 	grp->lg_started = B_FALSE;
698 	grp->lg_promisc = B_FALSE;
699 	aggr_lacp_init_grp(grp);
700 
701 	/* add MAC ports to group */
702 	grp->lg_ports = NULL;
703 	grp->lg_nports = 0;
704 	grp->lg_nattached_ports = 0;
705 	grp->lg_ntx_ports = 0;
706 
707 	for (i = 0; i < nports; i++) {
708 		err = aggr_grp_add_port(grp, ports[i].lp_devname, NULL);
709 		if (err != 0)
710 			goto bail;
711 	}
712 
713 	/*
714 	 * If no explicit MAC address was specified by the administrator,
715 	 * set it to the MAC address of the first port.
716 	 */
717 	grp->lg_addr_fixed = mac_fixed;
718 	if (grp->lg_addr_fixed) {
719 		/* validate specified address */
720 		if (bcmp(aggr_zero_mac, mac_addr, ETHERADDRL) == 0) {
721 			err = EINVAL;
722 			goto bail;
723 		}
724 		bcopy(mac_addr, grp->lg_addr, ETHERADDRL);
725 	} else {
726 		bcopy(grp->lg_ports->lp_addr, grp->lg_addr, ETHERADDRL);
727 		grp->lg_mac_addr_port = grp->lg_ports;
728 	}
729 
730 	/*
731 	 * Update the MAC address of the constituent ports.
732 	 * None of the port is attached at this time, the link state of the
733 	 * aggregation will not change.
734 	 */
735 	link_state_changed = aggr_grp_update_ports_mac(grp);
736 	ASSERT(!link_state_changed);
737 
738 	/* update outbound load balancing policy */
739 	aggr_send_update_policy(grp, policy);
740 
741 	/* set the initial group capabilities */
742 	aggr_grp_capab_set(grp);
743 
744 	if ((mac = mac_alloc(MAC_VERSION)) == NULL)
745 		goto bail;
746 	mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
747 	mac->m_driver = grp;
748 	mac->m_dip = aggr_dip;
749 	mac->m_instance = key;
750 	mac->m_src_addr = grp->lg_addr;
751 	mac->m_callbacks = &aggr_m_callbacks;
752 	mac->m_min_sdu = 0;
753 	mac->m_max_sdu = ETHERMTU;
754 	err = mac_register(mac, &grp->lg_mh);
755 	mac_free(mac);
756 	if (err != 0)
757 		goto bail;
758 
759 	/* set LACP mode */
760 	aggr_lacp_set_mode(grp, lacp_mode, lacp_timer);
761 
762 	/*
763 	 * Attach each port if necessary.
764 	 */
765 	for (port = grp->lg_ports; port != NULL; port = port->lp_next)
766 		(void) aggr_port_notify_link(grp, port, B_FALSE);
767 
768 	/* add new group to hash table */
769 	err = mod_hash_insert(aggr_grp_hash, GRP_HASH_KEY(key),
770 	    (mod_hash_val_t)grp);
771 	ASSERT(err == 0);
772 	aggr_grp_cnt++;
773 
774 	rw_exit(&grp->lg_lock);
775 	AGGR_LACP_UNLOCK(grp);
776 	rw_exit(&aggr_grp_lock);
777 	return (0);
778 
779 bail:
780 	if (grp != NULL) {
781 		aggr_port_t *cport;
782 
783 		grp->lg_closing = B_TRUE;
784 
785 		port = grp->lg_ports;
786 		while (port != NULL) {
787 			cport = port->lp_next;
788 			aggr_port_delete(port);
789 			port = cport;
790 		}
791 
792 		rw_exit(&grp->lg_lock);
793 		AGGR_LACP_UNLOCK(grp);
794 
795 		kmem_cache_free(aggr_grp_cache, grp);
796 	}
797 
798 	rw_exit(&aggr_grp_lock);
799 	return (err);
800 }
801 
802 /*
803  * Return a pointer to the member of a group with specified device name
804  * and port number.
805  */
806 static aggr_port_t *
807 aggr_grp_port_lookup(aggr_grp_t *grp, const char *devname)
808 {
809 	aggr_port_t *port;
810 
811 	ASSERT(RW_WRITE_HELD(&grp->lg_lock) || RW_READ_HELD(&grp->lg_lock));
812 
813 	for (port = grp->lg_ports; port != NULL; port = port->lp_next) {
814 		if (strcmp(port->lp_devname, devname) == 0)
815 			break;
816 	}
817 
818 	return (port);
819 }
820 
821 /*
822  * Stop, detach and remove a port from a link aggregation group.
823  */
824 static int
825 aggr_grp_rem_port(aggr_grp_t *grp, aggr_port_t *port,
826     boolean_t *mac_addr_changedp, boolean_t *link_state_changedp)
827 {
828 	int rc = 0;
829 	aggr_port_t **pport;
830 	boolean_t mac_addr_changed = B_FALSE;
831 	boolean_t link_state_changed = B_FALSE;
832 	uint64_t val;
833 	uint_t i;
834 	uint_t stat;
835 
836 	ASSERT(AGGR_LACP_LOCK_HELD(grp));
837 	ASSERT(RW_WRITE_HELD(&grp->lg_lock));
838 	ASSERT(grp->lg_nports > 1);
839 	ASSERT(!grp->lg_closing);
840 
841 	/* unlink port */
842 	for (pport = &grp->lg_ports; *pport != port;
843 	    pport = &(*pport)->lp_next) {
844 		if (*pport == NULL) {
845 			rc = ENOENT;
846 			goto done;
847 		}
848 	}
849 	*pport = port->lp_next;
850 
851 	atomic_add_32(&port->lp_closing, 1);
852 
853 	rw_enter(&port->lp_lock, RW_WRITER);
854 
855 	/*
856 	 * If the MAC address of the port being removed was assigned
857 	 * to the group, update the group MAC address
858 	 * using the MAC address of a different port.
859 	 */
860 	if (!grp->lg_addr_fixed && grp->lg_mac_addr_port == port) {
861 		/*
862 		 * Set the MAC address of the group to the
863 		 * MAC address of its first port.
864 		 */
865 		bcopy(grp->lg_ports->lp_addr, grp->lg_addr, ETHERADDRL);
866 		grp->lg_mac_addr_port = grp->lg_ports;
867 		mac_addr_changed = B_TRUE;
868 	}
869 
870 	link_state_changed = aggr_grp_detach_port(grp, port);
871 
872 	/*
873 	 * Add the counter statistics of the ports while it was aggregated
874 	 * to the group's residual statistics.  This is done by obtaining
875 	 * the current counter from the underlying MAC then subtracting the
876 	 * value of the counter at the moment it was added to the
877 	 * aggregation.
878 	 */
879 	for (i = 0; i < MAC_NSTAT && !grp->lg_closing; i++) {
880 		stat = i + MAC_STAT_MIN;
881 		if (!MAC_STAT_ISACOUNTER(stat))
882 			continue;
883 		val = aggr_port_stat(port, stat);
884 		val -= port->lp_stat[i];
885 		grp->lg_stat[i] += val;
886 	}
887 	for (i = 0; i < ETHER_NSTAT && !grp->lg_closing; i++) {
888 		stat = i + MACTYPE_STAT_MIN;
889 		if (!ETHER_STAT_ISACOUNTER(stat))
890 		    continue;
891 		val = aggr_port_stat(port, stat);
892 		val -= port->lp_ether_stat[i];
893 		grp->lg_ether_stat[i] += val;
894 	}
895 
896 	grp->lg_nports--;
897 
898 	rw_exit(&port->lp_lock);
899 
900 	aggr_port_delete(port);
901 
902 	/*
903 	 * If the group MAC address has changed, update the MAC address of
904 	 * the remaining consistuent ports according to the new MAC
905 	 * address of the group.
906 	 */
907 	if (mac_addr_changed)
908 		link_state_changed = link_state_changed ||
909 		    aggr_grp_update_ports_mac(grp);
910 
911 done:
912 	if (mac_addr_changedp != NULL)
913 		*mac_addr_changedp = mac_addr_changed;
914 	if (link_state_changedp != NULL)
915 		*link_state_changedp = link_state_changed;
916 
917 	return (rc);
918 }
919 
920 /*
921  * Remove one or more ports from an existing link aggregation group.
922  */
923 int
924 aggr_grp_rem_ports(uint32_t key, uint_t nports, laioc_port_t *ports)
925 {
926 	int rc = 0, i;
927 	aggr_grp_t *grp = NULL;
928 	aggr_port_t *port;
929 	boolean_t mac_addr_update = B_FALSE, mac_addr_changed;
930 	boolean_t link_state_update = B_FALSE, link_state_changed;
931 
932 	/* get group corresponding to key */
933 	rw_enter(&aggr_grp_lock, RW_READER);
934 	if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(key),
935 	    (mod_hash_val_t *)&grp) != 0) {
936 		rw_exit(&aggr_grp_lock);
937 		return (ENOENT);
938 	}
939 	AGGR_GRP_REFHOLD(grp);
940 	rw_exit(&aggr_grp_lock);
941 
942 	AGGR_LACP_LOCK(grp);
943 	rw_enter(&grp->lg_lock, RW_WRITER);
944 
945 	/* we need to keep at least one port per group */
946 	if (nports >= grp->lg_nports) {
947 		rc = EINVAL;
948 		goto bail;
949 	}
950 
951 	/* first verify that all the groups are valid */
952 	for (i = 0; i < nports; i++) {
953 		if (aggr_grp_port_lookup(grp, ports[i].lp_devname) == NULL) {
954 			/* port not found */
955 			rc = ENOENT;
956 			goto bail;
957 		}
958 	}
959 
960 	/* remove the specified ports from group */
961 	for (i = 0; i < nports && !grp->lg_closing; i++) {
962 		/* lookup port */
963 		port = aggr_grp_port_lookup(grp, ports[i].lp_devname);
964 		ASSERT(port != NULL);
965 
966 		/* stop port if group has already been started */
967 		if (grp->lg_started) {
968 			rw_enter(&port->lp_lock, RW_WRITER);
969 			aggr_port_stop(port);
970 			rw_exit(&port->lp_lock);
971 		}
972 
973 		/* remove port from group */
974 		rc = aggr_grp_rem_port(grp, port, &mac_addr_changed,
975 		    &link_state_changed);
976 		ASSERT(rc == 0);
977 		mac_addr_update = mac_addr_update || mac_addr_changed;
978 		link_state_update = link_state_update || link_state_changed;
979 	}
980 
981 bail:
982 	rw_exit(&grp->lg_lock);
983 	AGGR_LACP_UNLOCK(grp);
984 	if (!grp->lg_closing) {
985 		if (mac_addr_update)
986 			mac_unicst_update(grp->lg_mh, grp->lg_addr);
987 		if (link_state_update)
988 			mac_link_update(grp->lg_mh, grp->lg_link_state);
989 		if (rc == 0)
990 			mac_resource_update(grp->lg_mh);
991 	}
992 	AGGR_GRP_REFRELE(grp);
993 
994 	return (rc);
995 }
996 
997 int
998 aggr_grp_delete(uint32_t key)
999 {
1000 	aggr_grp_t *grp = NULL;
1001 	aggr_port_t *port, *cport;
1002 	mod_hash_val_t val;
1003 
1004 	rw_enter(&aggr_grp_lock, RW_WRITER);
1005 
1006 	if (mod_hash_find(aggr_grp_hash, GRP_HASH_KEY(key),
1007 	    (mod_hash_val_t *)&grp) != 0) {
1008 		rw_exit(&aggr_grp_lock);
1009 		return (ENOENT);
1010 	}
1011 
1012 	AGGR_LACP_LOCK(grp);
1013 	rw_enter(&grp->lg_lock, RW_WRITER);
1014 
1015 	grp->lg_closing = B_TRUE;
1016 
1017 	/*
1018 	 * Unregister from the MAC service module. Since this can
1019 	 * fail if a client hasn't closed the MAC port, we gracefully
1020 	 * fail the operation.
1021 	 */
1022 	if (mac_unregister(grp->lg_mh)) {
1023 		grp->lg_closing = B_FALSE;
1024 		rw_exit(&grp->lg_lock);
1025 		AGGR_LACP_UNLOCK(grp);
1026 		rw_exit(&aggr_grp_lock);
1027 		return (EBUSY);
1028 	}
1029 
1030 	/* detach and free MAC ports associated with group */
1031 	port = grp->lg_ports;
1032 	while (port != NULL) {
1033 		cport = port->lp_next;
1034 		rw_enter(&port->lp_lock, RW_WRITER);
1035 		if (grp->lg_started)
1036 			aggr_port_stop(port);
1037 		(void) aggr_grp_detach_port(grp, port);
1038 		rw_exit(&port->lp_lock);
1039 		aggr_port_delete(port);
1040 		port = cport;
1041 	}
1042 
1043 	rw_exit(&grp->lg_lock);
1044 	AGGR_LACP_UNLOCK(grp);
1045 
1046 	(void) mod_hash_remove(aggr_grp_hash, GRP_HASH_KEY(key), &val);
1047 	ASSERT(grp == (aggr_grp_t *)val);
1048 
1049 	ASSERT(aggr_grp_cnt > 0);
1050 	aggr_grp_cnt--;
1051 
1052 	rw_exit(&aggr_grp_lock);
1053 	AGGR_GRP_REFRELE(grp);
1054 
1055 	return (0);
1056 }
1057 
1058 void
1059 aggr_grp_free(aggr_grp_t *grp)
1060 {
1061 	ASSERT(grp->lg_refs == 0);
1062 	kmem_cache_free(aggr_grp_cache, grp);
1063 }
1064 
1065 /*
1066  * Walker invoked when building the list of configured groups and
1067  * their ports that must be passed up to user-space.
1068  */
1069 
1070 /*ARGSUSED*/
1071 static uint_t
1072 aggr_grp_info_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
1073 {
1074 	aggr_grp_t *grp;
1075 	aggr_port_t *port;
1076 	aggr_grp_info_state_t *state = arg;
1077 
1078 	if (state->ls_rc != 0)
1079 		return (MH_WALK_TERMINATE);	/* terminate walk */
1080 
1081 	grp = (aggr_grp_t *)val;
1082 
1083 	rw_enter(&grp->lg_lock, RW_READER);
1084 
1085 	if (state->ls_group_key != 0 && grp->lg_key != state->ls_group_key)
1086 		goto bail;
1087 
1088 	state->ls_group_found = B_TRUE;
1089 
1090 	state->ls_rc = state->ls_new_grp_fn(state->ls_fn_arg, grp->lg_key,
1091 	    grp->lg_addr, grp->lg_addr_fixed, grp->lg_tx_policy,
1092 	    grp->lg_nports, grp->lg_lacp_mode, grp->aggr.PeriodicTimer);
1093 
1094 	if (state->ls_rc != 0)
1095 		goto bail;
1096 
1097 	for (port = grp->lg_ports; port != NULL; port = port->lp_next) {
1098 
1099 		rw_enter(&port->lp_lock, RW_READER);
1100 
1101 		state->ls_rc = state->ls_new_port_fn(state->ls_fn_arg,
1102 		    port->lp_devname, port->lp_addr, port->lp_state,
1103 		    &port->lp_lacp.ActorOperPortState);
1104 
1105 		rw_exit(&port->lp_lock);
1106 
1107 		if (state->ls_rc != 0)
1108 			goto bail;
1109 	}
1110 
1111 bail:
1112 	rw_exit(&grp->lg_lock);
1113 	return ((state->ls_rc == 0) ? MH_WALK_CONTINUE : MH_WALK_TERMINATE);
1114 }
1115 
1116 int
1117 aggr_grp_info(uint_t *ngroups, uint32_t group_key, void *fn_arg,
1118     aggr_grp_info_new_grp_fn_t new_grp_fn,
1119     aggr_grp_info_new_port_fn_t new_port_fn)
1120 {
1121 	aggr_grp_info_state_t state;
1122 	int rc = 0;
1123 
1124 	rw_enter(&aggr_grp_lock, RW_READER);
1125 
1126 	*ngroups = aggr_grp_cnt;
1127 
1128 	bzero(&state, sizeof (state));
1129 	state.ls_group_key = group_key;
1130 	state.ls_new_grp_fn = new_grp_fn;
1131 	state.ls_new_port_fn = new_port_fn;
1132 	state.ls_fn_arg = fn_arg;
1133 
1134 	mod_hash_walk(aggr_grp_hash, aggr_grp_info_walker, &state);
1135 
1136 	if ((rc = state.ls_rc) == 0 && group_key != 0 &&
1137 	    !state.ls_group_found)
1138 		rc = ENOENT;
1139 
1140 	rw_exit(&aggr_grp_lock);
1141 	return (rc);
1142 }
1143 
1144 static void
1145 aggr_m_resources(void *arg)
1146 {
1147 	aggr_grp_t *grp = arg;
1148 	aggr_port_t *port;
1149 
1150 	/* Call each port's m_resources function */
1151 	for (port = grp->lg_ports; port != NULL; port = port->lp_next)
1152 		mac_resources(port->lp_mh);
1153 }
1154 
1155 /*ARGSUSED*/
1156 static void
1157 aggr_m_ioctl(void *arg, queue_t *q, mblk_t *mp)
1158 {
1159 	miocnak(q, mp, 0, ENOTSUP);
1160 }
1161 
1162 static int
1163 aggr_grp_stat(aggr_grp_t *grp, uint_t stat, uint64_t *val)
1164 {
1165 	aggr_port_t	*port;
1166 	uint_t		stat_index;
1167 
1168 	/* We only aggregate counter statistics. */
1169 	if (IS_MAC_STAT(stat) && !MAC_STAT_ISACOUNTER(stat) ||
1170 	    IS_MACTYPE_STAT(stat) && !ETHER_STAT_ISACOUNTER(stat)) {
1171 		return (ENOTSUP);
1172 	}
1173 
1174 	/*
1175 	 * Counter statistics for a group are computed by aggregating the
1176 	 * counters of the members MACs while they were aggregated, plus
1177 	 * the residual counter of the group itself, which is updated each
1178 	 * time a MAC is removed from the group.
1179 	 */
1180 	*val = 0;
1181 	for (port = grp->lg_ports; port != NULL; port = port->lp_next) {
1182 		/* actual port statistic */
1183 		*val += aggr_port_stat(port, stat);
1184 		/*
1185 		 * minus the port stat when it was added, plus any residual
1186 		 * ammount for the group.
1187 		 */
1188 		if (IS_MAC_STAT(stat)) {
1189 			stat_index = stat - MAC_STAT_MIN;
1190 			*val -= port->lp_stat[stat_index];
1191 			*val += grp->lg_stat[stat_index];
1192 		} else if (IS_MACTYPE_STAT(stat)) {
1193 			stat_index = stat - MACTYPE_STAT_MIN;
1194 			*val -= port->lp_ether_stat[stat_index];
1195 			*val += grp->lg_ether_stat[stat_index];
1196 		}
1197 	}
1198 	return (0);
1199 }
1200 
1201 static int
1202 aggr_m_stat(void *arg, uint_t stat, uint64_t *val)
1203 {
1204 	aggr_grp_t	*grp = arg;
1205 	int		rval = 0;
1206 
1207 	rw_enter(&grp->lg_lock, RW_READER);
1208 
1209 	switch (stat) {
1210 	case MAC_STAT_IFSPEED:
1211 		*val = grp->lg_ifspeed;
1212 		break;
1213 
1214 	case ETHER_STAT_LINK_DUPLEX:
1215 		*val = grp->lg_link_duplex;
1216 		break;
1217 
1218 	default:
1219 		/*
1220 		 * For all other statistics, we return the aggregated stat
1221 		 * from the underlying ports.  aggr_grp_stat() will set
1222 		 * rval appropriately if the statistic isn't a counter.
1223 		 */
1224 		rval = aggr_grp_stat(grp, stat, val);
1225 	}
1226 
1227 	rw_exit(&grp->lg_lock);
1228 	return (rval);
1229 }
1230 
1231 static int
1232 aggr_m_start(void *arg)
1233 {
1234 	aggr_grp_t *grp = arg;
1235 	aggr_port_t *port;
1236 
1237 	AGGR_LACP_LOCK(grp);
1238 	rw_enter(&grp->lg_lock, RW_WRITER);
1239 
1240 	/*
1241 	 * Attempts to start all configured members of the group.
1242 	 * Group members will be attached when their link-up notification
1243 	 * is received.
1244 	 */
1245 	for (port = grp->lg_ports; port != NULL; port = port->lp_next) {
1246 		rw_enter(&port->lp_lock, RW_WRITER);
1247 		if (aggr_port_start(port) != 0) {
1248 			rw_exit(&port->lp_lock);
1249 			continue;
1250 		}
1251 
1252 		/* set port promiscuous mode */
1253 		if (aggr_port_promisc(port, grp->lg_promisc) != 0)
1254 			aggr_port_stop(port);
1255 		rw_exit(&port->lp_lock);
1256 	}
1257 
1258 	grp->lg_started = B_TRUE;
1259 
1260 	rw_exit(&grp->lg_lock);
1261 	AGGR_LACP_UNLOCK(grp);
1262 
1263 	return (0);
1264 }
1265 
1266 static void
1267 aggr_m_stop(void *arg)
1268 {
1269 	aggr_grp_t *grp = arg;
1270 	aggr_port_t *port;
1271 
1272 	rw_enter(&grp->lg_lock, RW_WRITER);
1273 
1274 	for (port = grp->lg_ports; port != NULL; port = port->lp_next) {
1275 		rw_enter(&port->lp_lock, RW_WRITER);
1276 		aggr_port_stop(port);
1277 		rw_exit(&port->lp_lock);
1278 	}
1279 
1280 	grp->lg_started = B_FALSE;
1281 
1282 	rw_exit(&grp->lg_lock);
1283 }
1284 
1285 static int
1286 aggr_m_promisc(void *arg, boolean_t on)
1287 {
1288 	aggr_grp_t *grp = arg;
1289 	aggr_port_t *port;
1290 	boolean_t link_state_changed = B_FALSE;
1291 
1292 	AGGR_LACP_LOCK(grp);
1293 	rw_enter(&grp->lg_lock, RW_WRITER);
1294 	AGGR_GRP_REFHOLD(grp);
1295 
1296 	ASSERT(!grp->lg_closing);
1297 
1298 	if (on == grp->lg_promisc)
1299 		goto bail;
1300 
1301 	for (port = grp->lg_ports; port != NULL; port = port->lp_next) {
1302 		rw_enter(&port->lp_lock, RW_WRITER);
1303 		AGGR_PORT_REFHOLD(port);
1304 		if (port->lp_started) {
1305 			if (aggr_port_promisc(port, on) != 0) {
1306 				link_state_changed = link_state_changed ||
1307 				    aggr_grp_detach_port(grp, port);
1308 			} else {
1309 				/*
1310 				 * If a port was detached because of a previous
1311 				 * failure changing the promiscuity, the port
1312 				 * is reattached when it successfully changes
1313 				 * the promiscuity now, and this might cause
1314 				 * the link state of the aggregation to change.
1315 				 */
1316 				link_state_changed = link_state_changed ||
1317 				    aggr_grp_attach_port(grp, port);
1318 			}
1319 		}
1320 		rw_exit(&port->lp_lock);
1321 		AGGR_PORT_REFRELE(port);
1322 	}
1323 
1324 	grp->lg_promisc = on;
1325 
1326 	if (link_state_changed)
1327 		mac_link_update(grp->lg_mh, grp->lg_link_state);
1328 
1329 bail:
1330 	rw_exit(&grp->lg_lock);
1331 	AGGR_LACP_UNLOCK(grp);
1332 	AGGR_GRP_REFRELE(grp);
1333 
1334 	return (0);
1335 }
1336 
1337 /*
1338  * Initialize the capabilities that are advertised for the group
1339  * according to the capabilities of the constituent ports.
1340  */
1341 static boolean_t
1342 aggr_m_capab_get(void *arg, mac_capab_t cap, void *cap_data)
1343 {
1344 	aggr_grp_t *grp = arg;
1345 
1346 	switch (cap) {
1347 	case MAC_CAPAB_HCKSUM: {
1348 		uint32_t *hcksum_txflags = cap_data;
1349 		*hcksum_txflags = grp->lg_hcksum_txflags;
1350 		break;
1351 	}
1352 	case MAC_CAPAB_POLL:
1353 		/*
1354 		 * There's nothing for us to fill in, we simply return
1355 		 * B_TRUE or B_FALSE to represent the group's support
1356 		 * status for this capability.
1357 		 */
1358 		return (grp->lg_gldv3_polling);
1359 	default:
1360 		return (B_FALSE);
1361 	}
1362 	return (B_TRUE);
1363 }
1364 
1365 /*
1366  * Add or remove the multicast addresses that are defined for the group
1367  * to or from the specified port.
1368  * This function is called before stopping a port, before a port
1369  * is detached from a group, and when attaching a port to a group.
1370  */
1371 void
1372 aggr_grp_multicst_port(aggr_port_t *port, boolean_t add)
1373 {
1374 	aggr_grp_t *grp = port->lp_grp;
1375 
1376 	ASSERT(RW_WRITE_HELD(&port->lp_lock));
1377 	ASSERT(RW_WRITE_HELD(&grp->lg_lock) || RW_READ_HELD(&grp->lg_lock));
1378 
1379 	if (!port->lp_started)
1380 		return;
1381 
1382 	mac_multicst_refresh(grp->lg_mh, aggr_port_multicst, port, add);
1383 }
1384 
1385 static int
1386 aggr_m_multicst(void *arg, boolean_t add, const uint8_t *addrp)
1387 {
1388 	aggr_grp_t *grp = arg;
1389 	aggr_port_t *port = NULL;
1390 	int err = 0, cerr;
1391 
1392 	rw_enter(&grp->lg_lock, RW_WRITER);
1393 	for (port = grp->lg_ports; port != NULL; port = port->lp_next) {
1394 		if (port->lp_state != AGGR_PORT_STATE_ATTACHED)
1395 			continue;
1396 		cerr = aggr_port_multicst(port, add, addrp);
1397 		if (cerr != 0 && err == 0)
1398 			err = cerr;
1399 	}
1400 	rw_exit(&grp->lg_lock);
1401 	return (err);
1402 }
1403 
1404 static int
1405 aggr_m_unicst(void *arg, const uint8_t *macaddr)
1406 {
1407 	aggr_grp_t *grp = arg;
1408 	int rc;
1409 
1410 	AGGR_LACP_LOCK(grp);
1411 	rw_enter(&grp->lg_lock, RW_WRITER);
1412 	rc = aggr_grp_modify(0, grp, AGGR_MODIFY_MAC, 0, B_TRUE, macaddr,
1413 	    0, 0);
1414 	rw_exit(&grp->lg_lock);
1415 	AGGR_LACP_UNLOCK(grp);
1416 
1417 	return (rc);
1418 }
1419 
1420 /*
1421  * Initialize the capabilities that are advertised for the group
1422  * according to the capabilities of the constituent ports.
1423  */
1424 static void
1425 aggr_grp_capab_set(aggr_grp_t *grp)
1426 {
1427 	uint32_t cksum;
1428 	aggr_port_t *port;
1429 
1430 	ASSERT(RW_WRITE_HELD(&grp->lg_lock));
1431 	ASSERT(grp->lg_ports != NULL);
1432 
1433 	grp->lg_hcksum_txflags = (uint32_t)-1;
1434 	grp->lg_gldv3_polling = B_TRUE;
1435 
1436 	for (port = grp->lg_ports; port != NULL; port = port->lp_next) {
1437 		if (!mac_capab_get(port->lp_mh, MAC_CAPAB_HCKSUM, &cksum))
1438 			cksum = 0;
1439 		grp->lg_hcksum_txflags &= cksum;
1440 
1441 		grp->lg_gldv3_polling &=
1442 		    mac_capab_get(port->lp_mh, MAC_CAPAB_POLL, NULL);
1443 	}
1444 }
1445 
1446 
1447 /*
1448  * Checks whether the capabilities of the port being added are compatible
1449  * with the current capabilities of the aggregation.
1450  */
1451 static boolean_t
1452 aggr_grp_capab_check(aggr_grp_t *grp, aggr_port_t *port)
1453 {
1454 	uint32_t	hcksum_txflags;
1455 
1456 	ASSERT(grp->lg_ports != NULL);
1457 
1458 	if (!mac_capab_get(port->lp_mh, MAC_CAPAB_HCKSUM, &hcksum_txflags)) {
1459 		if (grp->lg_hcksum_txflags != 0)
1460 			return (B_FALSE);
1461 	} else if ((hcksum_txflags & grp->lg_hcksum_txflags) !=
1462 	    grp->lg_hcksum_txflags) {
1463 		return (B_FALSE);
1464 	}
1465 
1466 	if (mac_capab_get(port->lp_mh, MAC_CAPAB_POLL, NULL) !=
1467 	    grp->lg_gldv3_polling) {
1468 		return (B_FALSE);
1469 	}
1470 
1471 	return (B_TRUE);
1472 }
1473