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