xref: /illumos-gate/usr/src/cmd/rcm_daemon/common/aggr_rcm.c (revision 69a119caa6570c7077699161b7c28b6ee9f8b0f4)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * This RCM module adds support to the RCM framework for AGGR links
28  */
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <alloca.h>
35 #include <sys/types.h>
36 #include <sys/aggr.h>
37 #include <synch.h>
38 #include <assert.h>
39 #include <strings.h>
40 #include "rcm_module.h"
41 #include <libintl.h>
42 #include <libdllink.h>
43 #include <libdlaggr.h>
44 
45 /*
46  * Definitions
47  */
48 #ifndef lint
49 #define	_(x)	gettext(x)
50 #else
51 #define	_(x)	x
52 #endif
53 
54 /* Some generic well-knowns and defaults used in this module */
55 #define	RCM_LINK_PREFIX		"SUNW_datalink"	/* RCM datalink name prefix */
56 #define	RCM_LINK_RESOURCE_MAX	(13 + LINKID_STR_WIDTH)
57 
58 /* AGGR link representation */
59 typedef struct dl_aggr {
60 	struct dl_aggr		*da_next;	/* next AGGR on the system */
61 	struct dl_aggr		*da_prev;	/* prev AGGR on the system */
62 	boolean_t		da_stale;	/* AGGR link is stale? */
63 	datalink_id_t		da_aggrid;
64 	datalink_id_t		da_lastport;
65 } dl_aggr_t;
66 
67 /* AGGR Cache state flags */
68 typedef enum {
69 	CACHE_NODE_STALE		= 0x01,	/* stale cached data */
70 	CACHE_NODE_NEW			= 0x02,	/* new cached nodes */
71 	CACHE_NODE_OFFLINED		= 0x04,	/* node offlined */
72 	CACHE_AGGR_PORT_OFFLINED	= 0x08,	/* aggr port offlined */
73 	CACHE_AGGR_CONSUMER_OFFLINED	= 0x10	/* consumers offlined */
74 } cache_node_state_t;
75 
76 /* Network Cache lookup options */
77 #define	CACHE_NO_REFRESH	0x1		/* cache refresh not needed */
78 #define	CACHE_REFRESH		0x2		/* refresh cache */
79 
80 /*
81  * Cache element. It is used to keep a list of links on the system and
82  * their associated aggregations.
83  */
84 typedef struct link_cache {
85 	struct link_cache	*vc_next;	/* next cached resource */
86 	struct link_cache	*vc_prev;	/* prev cached resource */
87 	char			*vc_resource;	/* resource name */
88 	datalink_id_t		vc_linkid;	/* linkid */
89 	dl_aggr_t		*vc_aggr;	/* AGGR on this link */
90 	cache_node_state_t	vc_state;	/* cache state flags */
91 } link_cache_t;
92 
93 /*
94  * Global cache for network AGGRs
95  */
96 static link_cache_t	cache_head;
97 static link_cache_t	cache_tail;
98 static mutex_t		cache_lock;
99 static dl_aggr_t	aggr_head;
100 static dl_aggr_t	aggr_tail;
101 static mutex_t		aggr_list_lock;
102 static int		events_registered = 0;
103 
104 static dladm_handle_t	dld_handle = NULL;
105 
106 /*
107  * RCM module interface prototypes
108  */
109 static int		aggr_register(rcm_handle_t *);
110 static int		aggr_unregister(rcm_handle_t *);
111 static int		aggr_get_info(rcm_handle_t *, char *, id_t, uint_t,
112 			    char **, char **, nvlist_t *, rcm_info_t **);
113 static int		aggr_suspend(rcm_handle_t *, char *, id_t,
114 			    timespec_t *, uint_t, char **, rcm_info_t **);
115 static int		aggr_resume(rcm_handle_t *, char *, id_t, uint_t,
116 			    char **, rcm_info_t **);
117 static int		aggr_offline(rcm_handle_t *, char *, id_t, uint_t,
118 			    char **, rcm_info_t **);
119 static int		aggr_undo_offline(rcm_handle_t *, char *, id_t, uint_t,
120 			    char **, rcm_info_t **);
121 static int		aggr_remove(rcm_handle_t *, char *, id_t, uint_t,
122 			    char **, rcm_info_t **);
123 static int		aggr_notify_event(rcm_handle_t *, char *, id_t, uint_t,
124 			    char **, nvlist_t *, rcm_info_t **);
125 static int		aggr_configure_all(rcm_handle_t *, datalink_id_t,
126 			    boolean_t *);
127 
128 /* Module private routines */
129 static int 		cache_update(rcm_handle_t *);
130 static void 		cache_remove(link_cache_t *);
131 static void 		cache_insert(link_cache_t *);
132 static void 		node_free(link_cache_t *);
133 static void 		aggr_list_remove(dl_aggr_t *);
134 static void 		aggr_list_insert(dl_aggr_t *);
135 static void 		aggr_list_free();
136 static link_cache_t	*cache_lookup(rcm_handle_t *, char *, char);
137 static int		aggr_consumer_offline(rcm_handle_t *, link_cache_t *,
138 			    char **, uint_t, rcm_info_t **);
139 static int		aggr_consumer_online(rcm_handle_t *, link_cache_t *,
140 			    char **, uint_t, rcm_info_t **);
141 static int		aggr_offline_port(link_cache_t *, cache_node_state_t);
142 static int		aggr_online_port(link_cache_t *, boolean_t *);
143 static char 		*aggr_usage(link_cache_t *);
144 static void 		aggr_log_err(datalink_id_t, char **, char *);
145 static int		aggr_consumer_notify(rcm_handle_t *, datalink_id_t,
146 			    char **, uint_t, rcm_info_t **);
147 
148 /* Module-Private data */
149 static struct rcm_mod_ops aggr_ops =
150 {
151 	RCM_MOD_OPS_VERSION,
152 	aggr_register,
153 	aggr_unregister,
154 	aggr_get_info,
155 	aggr_suspend,
156 	aggr_resume,
157 	aggr_offline,
158 	aggr_undo_offline,
159 	aggr_remove,
160 	NULL,
161 	NULL,
162 	aggr_notify_event
163 };
164 
165 /*
166  * rcm_mod_init() - Update registrations, and return the ops structure.
167  */
168 struct rcm_mod_ops *
169 rcm_mod_init(void)
170 {
171 	dladm_status_t status;
172 	char errmsg[DLADM_STRSIZE];
173 
174 	rcm_log_message(RCM_TRACE1, "AGGR: mod_init\n");
175 
176 	cache_head.vc_next = &cache_tail;
177 	cache_head.vc_prev = NULL;
178 	cache_tail.vc_prev = &cache_head;
179 	cache_tail.vc_next = NULL;
180 	(void) mutex_init(&cache_lock, 0, NULL);
181 	aggr_head.da_next = &aggr_tail;
182 	aggr_head.da_prev = NULL;
183 	aggr_tail.da_prev = &aggr_head;
184 	aggr_tail.da_next = NULL;
185 	(void) mutex_init(&aggr_list_lock, NULL, NULL);
186 
187 	if ((status = dladm_open(&dld_handle)) != DLADM_STATUS_OK) {
188 		rcm_log_message(RCM_WARNING,
189 		    "AGGR: mod_init failed: cannot open datalink handle: %s\n",
190 		    dladm_status2str(status, errmsg));
191 		return (NULL);
192 	}
193 
194 	/* Return the ops vectors */
195 	return (&aggr_ops);
196 }
197 
198 /*
199  * rcm_mod_info() - Return a string describing this module.
200  */
201 const char *
202 rcm_mod_info(void)
203 {
204 	rcm_log_message(RCM_TRACE1, "AGGR: mod_info\n");
205 
206 	return ("AGGR module version 1.1");
207 }
208 
209 /*
210  * rcm_mod_fini() - Destroy the network AGGR cache.
211  */
212 int
213 rcm_mod_fini(void)
214 {
215 	link_cache_t *node;
216 
217 	rcm_log_message(RCM_TRACE1, "AGGR: mod_fini\n");
218 
219 	/*
220 	 * Note that aggr_unregister() does not seem to be called anywhere,
221 	 * therefore we free the cache nodes here. In theory we should call
222 	 * rcm_register_interest() for each node before we free it, the
223 	 * framework does not provide the rcm_handle to allow us to do so.
224 	 */
225 	(void) mutex_lock(&cache_lock);
226 	node = cache_head.vc_next;
227 	while (node != &cache_tail) {
228 		cache_remove(node);
229 		node_free(node);
230 		node = cache_head.vc_next;
231 	}
232 	(void) mutex_unlock(&cache_lock);
233 	(void) mutex_destroy(&cache_lock);
234 
235 	aggr_list_free();
236 	(void) mutex_destroy(&aggr_list_lock);
237 
238 	dladm_close(dld_handle);
239 	return (RCM_SUCCESS);
240 }
241 
242 /*
243  * aggr_list_insert - Insert an aggr in the global aggr list
244  */
245 static void
246 aggr_list_insert(dl_aggr_t *aggr)
247 {
248 	assert(MUTEX_HELD(&aggr_list_lock));
249 
250 	/* insert at the head for best performance */
251 	aggr->da_next = aggr_head.da_next;
252 	aggr->da_prev = &aggr_head;
253 
254 	aggr->da_next->da_prev = aggr;
255 	aggr->da_prev->da_next = aggr;
256 }
257 
258 /*
259  * aggr_list_remove - Remove an aggr from the global aggr list
260  */
261 static void
262 aggr_list_remove(dl_aggr_t *aggr)
263 {
264 	assert(MUTEX_HELD(&aggr_list_lock));
265 	aggr->da_next->da_prev = aggr->da_prev;
266 	aggr->da_prev->da_next = aggr->da_next;
267 	aggr->da_next = NULL;
268 	aggr->da_prev = NULL;
269 }
270 
271 static void
272 aggr_list_free()
273 {
274 	dl_aggr_t *aggr;
275 
276 	(void) mutex_lock(&aggr_list_lock);
277 	aggr = aggr_head.da_next;
278 	while (aggr != &aggr_tail) {
279 		aggr_list_remove(aggr);
280 		free(aggr);
281 		aggr = aggr_head.da_next;
282 	}
283 	(void) mutex_unlock(&aggr_list_lock);
284 }
285 
286 /*
287  * aggr_register() - Make sure the cache is properly sync'ed, and its
288  *		 registrations are in order.
289  */
290 static int
291 aggr_register(rcm_handle_t *hd)
292 {
293 	rcm_log_message(RCM_TRACE1, "AGGR: register\n");
294 
295 	if (cache_update(hd) < 0)
296 		return (RCM_FAILURE);
297 
298 	/*
299 	 * Need to register interest in all new resources
300 	 * getting attached, so we get attach event notifications
301 	 */
302 	if (!events_registered) {
303 		if (rcm_register_event(hd, RCM_RESOURCE_LINK_NEW, 0, NULL)
304 		    != RCM_SUCCESS) {
305 			rcm_log_message(RCM_ERROR,
306 			    _("AGGR: failed to register %s\n"),
307 			    RCM_RESOURCE_LINK_NEW);
308 			return (RCM_FAILURE);
309 		} else {
310 			rcm_log_message(RCM_DEBUG, "AGGR: registered %s\n",
311 			    RCM_RESOURCE_LINK_NEW);
312 			events_registered++;
313 		}
314 	}
315 
316 	return (RCM_SUCCESS);
317 }
318 
319 /*
320  * aggr_unregister() - Walk the cache, unregistering all the networks.
321  */
322 static int
323 aggr_unregister(rcm_handle_t *hd)
324 {
325 	link_cache_t *node;
326 
327 	rcm_log_message(RCM_TRACE1, "AGGR: unregister\n");
328 
329 	/* Walk the cache, unregistering everything */
330 	(void) mutex_lock(&cache_lock);
331 	node = cache_head.vc_next;
332 	while (node != &cache_tail) {
333 		if (rcm_unregister_interest(hd, node->vc_resource, 0)
334 		    != RCM_SUCCESS) {
335 			/* unregister failed for whatever reason */
336 			rcm_log_message(RCM_ERROR,
337 			    _("AGGR: failed to unregister %s\n"),
338 			    node->vc_resource);
339 			(void) mutex_unlock(&cache_lock);
340 			return (RCM_FAILURE);
341 		}
342 		cache_remove(node);
343 		node_free(node);
344 		node = cache_head.vc_next;
345 	}
346 	(void) mutex_unlock(&cache_lock);
347 
348 	aggr_list_free();
349 
350 	/*
351 	 * Unregister interest in all new resources
352 	 */
353 	if (events_registered) {
354 		if (rcm_unregister_event(hd, RCM_RESOURCE_LINK_NEW, 0)
355 		    != RCM_SUCCESS) {
356 			rcm_log_message(RCM_ERROR,
357 			    _("AGGR: failed to unregister %s\n"),
358 			    RCM_RESOURCE_LINK_NEW);
359 			return (RCM_FAILURE);
360 		} else {
361 			rcm_log_message(RCM_DEBUG, "AGGR: unregistered %s\n",
362 			    RCM_RESOURCE_LINK_NEW);
363 			events_registered--;
364 		}
365 	}
366 
367 	return (RCM_SUCCESS);
368 }
369 
370 /*
371  * aggr_offline() - Offline AGGRs on a specific link.
372  */
373 static int
374 aggr_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
375     char **errorp, rcm_info_t **depend_info)
376 {
377 	link_cache_t *node;
378 
379 	rcm_log_message(RCM_TRACE1, "AGGR: offline(%s)\n", rsrc);
380 
381 	/* Lock the cache and lookup the resource */
382 	(void) mutex_lock(&cache_lock);
383 	node = cache_lookup(hd, rsrc, CACHE_REFRESH);
384 	if (node == NULL) {
385 		/* should not happen because the resource is registered. */
386 		aggr_log_err(DATALINK_INVALID_LINKID, errorp,
387 		    "offline, unrecognized resource");
388 		(void) mutex_unlock(&cache_lock);
389 		return (RCM_SUCCESS);
390 	}
391 
392 	/*
393 	 * If this given link is the only port in the aggregation, inform
394 	 * VLANs and IP interfaces on associated AGGRs to be offlined
395 	 */
396 	if (node->vc_aggr->da_lastport == node->vc_linkid) {
397 		if (aggr_consumer_offline(hd, node, errorp, flags,
398 		    depend_info) == RCM_SUCCESS) {
399 			rcm_log_message(RCM_DEBUG,
400 			    "AGGR: consumers agreed on offline\n");
401 		} else {
402 			aggr_log_err(node->vc_linkid, errorp,
403 			    "consumers offline failed");
404 			(void) mutex_unlock(&cache_lock);
405 			return (RCM_FAILURE);
406 		}
407 	}
408 
409 	/* Check if it's a query */
410 	if (flags & RCM_QUERY) {
411 		rcm_log_message(RCM_TRACE1,
412 		    "AGGR: offline query succeeded(%s)\n", rsrc);
413 		(void) mutex_unlock(&cache_lock);
414 		return (RCM_SUCCESS);
415 	}
416 
417 	if (aggr_offline_port(node, CACHE_NODE_OFFLINED) != RCM_SUCCESS) {
418 		aggr_log_err(node->vc_linkid, errorp, "offline port failed");
419 		(void) mutex_unlock(&cache_lock);
420 		return (RCM_FAILURE);
421 	}
422 
423 	rcm_log_message(RCM_TRACE1, "AGGR: Offline succeeded(%s)\n", rsrc);
424 	(void) mutex_unlock(&cache_lock);
425 	return (RCM_SUCCESS);
426 }
427 
428 /*
429  * aggr_undo_offline() - Undo offline of a previously offlined link.
430  */
431 /*ARGSUSED*/
432 static int
433 aggr_undo_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
434     char **errorp, rcm_info_t **depend_info)
435 {
436 	link_cache_t *node;
437 	boolean_t up;
438 
439 	rcm_log_message(RCM_TRACE1, "AGGR: online(%s)\n", rsrc);
440 
441 	(void) mutex_lock(&cache_lock);
442 	node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH);
443 	if (node == NULL) {
444 		aggr_log_err(DATALINK_INVALID_LINKID, errorp,
445 		    "undo offline, unrecognized resource");
446 		(void) mutex_unlock(&cache_lock);
447 		errno = ENOENT;
448 		return (RCM_FAILURE);
449 	}
450 
451 	/* Check if no attempt should be made to online the link here */
452 	if (!(node->vc_state & CACHE_NODE_OFFLINED)) {
453 		aggr_log_err(node->vc_linkid, errorp, "resource not offlined");
454 		(void) mutex_unlock(&cache_lock);
455 		errno = ENOTSUP;
456 		return (RCM_SUCCESS);
457 	}
458 
459 	if (aggr_online_port(node, &up) != RCM_SUCCESS) {
460 		aggr_log_err(node->vc_linkid, errorp, "online failed");
461 		(void) mutex_unlock(&cache_lock);
462 		return (RCM_FAILURE);
463 	}
464 
465 	/*
466 	 * Inform VLANs and IP interfaces on associated AGGRs to be online
467 	 */
468 	if (!up)
469 		goto done;
470 
471 	if (aggr_consumer_online(hd, node, errorp, flags, depend_info) ==
472 	    RCM_SUCCESS) {
473 		rcm_log_message(RCM_DEBUG, "AGGR: Consumers agree on online");
474 	} else {
475 		rcm_log_message(RCM_WARNING,
476 		    _("AGGR: Consumers online failed (%s)\n"), rsrc);
477 	}
478 
479 done:
480 	node->vc_state &= ~CACHE_NODE_OFFLINED;
481 	rcm_log_message(RCM_TRACE1, "AGGR: online succeeded(%s)\n", rsrc);
482 	(void) mutex_unlock(&cache_lock);
483 	return (RCM_SUCCESS);
484 }
485 
486 static int
487 aggr_offline_port(link_cache_t *node, cache_node_state_t state)
488 {
489 	dl_aggr_t *aggr;
490 	dladm_status_t status;
491 	char errmsg[DLADM_STRSIZE];
492 	dladm_aggr_port_attr_db_t port;
493 
494 	rcm_log_message(RCM_TRACE2, "AGGR: aggr_offline_port %s\n",
495 	    node->vc_resource);
496 
497 	aggr = node->vc_aggr;
498 
499 	/*
500 	 * Try to remove the given port from the AGGR or delete the AGGR
501 	 */
502 	if (aggr->da_lastport == node->vc_linkid) {
503 		rcm_log_message(RCM_TRACE2, "AGGR: delete aggregation %u\n",
504 		    aggr->da_aggrid);
505 		status = dladm_aggr_delete(dld_handle, aggr->da_aggrid,
506 		    DLADM_OPT_ACTIVE);
507 	} else {
508 		rcm_log_message(RCM_TRACE2,
509 		    "AGGR: remove port (%s) from aggregation %u\n",
510 		    node->vc_resource, aggr->da_aggrid);
511 		port.lp_linkid = node->vc_linkid;
512 		status = dladm_aggr_remove(dld_handle, aggr->da_aggrid, 1,
513 		    &port, DLADM_OPT_ACTIVE);
514 	}
515 	if (status != DLADM_STATUS_OK) {
516 		rcm_log_message(RCM_WARNING,
517 		    _("AGGR: AGGR offline port failed (%u): %s\n"),
518 		    aggr->da_aggrid, dladm_status2str(status, errmsg));
519 		return (RCM_FAILURE);
520 	} else {
521 		rcm_log_message(RCM_TRACE1,
522 		    "AGGR: AGGR offline port succeeded (%u)\n",
523 		    aggr->da_aggrid);
524 		node->vc_state |= (CACHE_AGGR_PORT_OFFLINED | state);
525 		return (RCM_SUCCESS);
526 	}
527 }
528 
529 static int
530 aggr_online_port(link_cache_t *node, boolean_t *up)
531 {
532 	dl_aggr_t *aggr;
533 	dladm_status_t status;
534 	char errmsg[DLADM_STRSIZE];
535 	dladm_aggr_port_attr_db_t port;
536 
537 	rcm_log_message(RCM_TRACE2, "AGGR: aggr_online_port %s\n",
538 	    node->vc_resource);
539 
540 	*up = B_FALSE;
541 	if (!(node->vc_state & CACHE_AGGR_PORT_OFFLINED))
542 		return (RCM_SUCCESS);
543 
544 	/*
545 	 * Either add the port into the AGGR or recreate specific AGGR
546 	 * depending on whether this link is the only port in the aggregation.
547 	 */
548 	aggr = node->vc_aggr;
549 	if (aggr->da_lastport == node->vc_linkid) {
550 		rcm_log_message(RCM_TRACE2, "AGGR: delete aggregation %u\n",
551 		    aggr->da_aggrid);
552 		status = dladm_aggr_up(dld_handle, aggr->da_aggrid);
553 		*up = B_TRUE;
554 	} else {
555 		rcm_log_message(RCM_TRACE2,
556 		    "AGGR: add port (%s) to aggregation %u\n",
557 		    node->vc_resource, aggr->da_aggrid);
558 		port.lp_linkid = node->vc_linkid;
559 		status = dladm_aggr_add(dld_handle, aggr->da_aggrid, 1, &port,
560 		    DLADM_OPT_ACTIVE);
561 	}
562 	if (status != DLADM_STATUS_OK) {
563 		rcm_log_message(RCM_WARNING,
564 		    _("AGGR: AGGR online failed (%u): %s\n"),
565 		    aggr->da_aggrid, dladm_status2str(status, errmsg));
566 		*up = B_FALSE;
567 		return (RCM_FAILURE);
568 	}
569 	node->vc_state &= ~CACHE_AGGR_PORT_OFFLINED;
570 	return (RCM_SUCCESS);
571 }
572 
573 /*
574  * aggr_get_info() - Gather usage information for this resource.
575  */
576 /*ARGSUSED*/
577 int
578 aggr_get_info(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
579     char **usagep, char **errorp, nvlist_t *props, rcm_info_t **depend_info)
580 {
581 	link_cache_t *node;
582 
583 	rcm_log_message(RCM_TRACE1, "AGGR: get_info(%s)\n", rsrc);
584 
585 	(void) mutex_lock(&cache_lock);
586 	node = cache_lookup(hd, rsrc, CACHE_REFRESH);
587 	if (node == NULL) {
588 		rcm_log_message(RCM_INFO,
589 		    _("AGGR: get_info(%s) unrecognized resource\n"), rsrc);
590 		(void) mutex_unlock(&cache_lock);
591 		errno = ENOENT;
592 		return (RCM_FAILURE);
593 	}
594 
595 	/*
596 	 * *usagep will be freed by the caller.
597 	 */
598 	*usagep = aggr_usage(node);
599 	(void) mutex_unlock(&cache_lock);
600 
601 	if (*usagep == NULL) {
602 		/* most likely malloc failure */
603 		rcm_log_message(RCM_ERROR,
604 		    _("AGGR: get_info(%s) malloc failure\n"), rsrc);
605 		(void) mutex_unlock(&cache_lock);
606 		errno = ENOMEM;
607 		return (RCM_FAILURE);
608 	}
609 
610 	/* Set client/role properties */
611 	(void) nvlist_add_string(props, RCM_CLIENT_NAME, "AGGR");
612 	rcm_log_message(RCM_TRACE1, "AGGR: get_info(%s) info = %s\n",
613 	    rsrc, *usagep);
614 	return (RCM_SUCCESS);
615 }
616 
617 /*
618  * aggr_suspend() - Nothing to do, always okay
619  */
620 /*ARGSUSED*/
621 static int
622 aggr_suspend(rcm_handle_t *hd, char *rsrc, id_t id, timespec_t *interval,
623     uint_t flags, char **errorp, rcm_info_t **depend_info)
624 {
625 	rcm_log_message(RCM_TRACE1, "AGGR: suspend(%s)\n", rsrc);
626 	return (RCM_SUCCESS);
627 }
628 
629 /*
630  * aggr_resume() - Nothing to do, always okay
631  */
632 /*ARGSUSED*/
633 static int
634 aggr_resume(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
635     char **errorp, rcm_info_t **depend_info)
636 {
637 	rcm_log_message(RCM_TRACE1, "AGGR: resume(%s)\n", rsrc);
638 	return (RCM_SUCCESS);
639 }
640 
641 /*
642  * aggr_remove() - remove a resource from cache
643  */
644 /*ARGSUSED*/
645 static int
646 aggr_remove(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
647     char **errorp, rcm_info_t **depend_info)
648 {
649 	link_cache_t *node;
650 	char *exported;
651 	dl_aggr_t *aggr;
652 	int rv = RCM_SUCCESS;
653 
654 	rcm_log_message(RCM_TRACE1, "AGGR: remove(%s)\n", rsrc);
655 
656 	(void) mutex_lock(&cache_lock);
657 	node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH);
658 	if (node == NULL) {
659 		rcm_log_message(RCM_INFO,
660 		    _("AGGR: remove(%s) unrecognized resource\n"), rsrc);
661 		(void) mutex_unlock(&cache_lock);
662 		errno = ENOENT;
663 		return (RCM_FAILURE);
664 	}
665 
666 	/* remove the cached entry for the resource */
667 	cache_remove(node);
668 	(void) mutex_unlock(&cache_lock);
669 
670 	/*
671 	 * If this link is not the only port in the associated aggregation,
672 	 * the CACHE_AGGR_CONSUMER_OFFLINED flags won't be set.
673 	 */
674 	if (node->vc_state & CACHE_AGGR_CONSUMER_OFFLINED) {
675 		aggr = node->vc_aggr;
676 		exported = alloca(RCM_LINK_RESOURCE_MAX);
677 		(void) snprintf(exported, RCM_LINK_RESOURCE_MAX, "%s/%u",
678 		    RCM_LINK_PREFIX, aggr->da_aggrid);
679 		rv = rcm_notify_remove(hd, exported, flags, depend_info);
680 		if (rv != RCM_SUCCESS) {
681 			rcm_log_message(RCM_WARNING,
682 			    _("AGGR: failed to notify remove dependent %s\n"),
683 			    exported);
684 		}
685 	}
686 
687 	node_free(node);
688 	return (rv);
689 }
690 
691 /*
692  * aggr_notify_event - Project private implementation to receive new resource
693  *		   events. It intercepts all new resource events. If the
694  *		   new resource is a network resource, pass up a notify
695  *		   for it too. The new resource need not be cached, since
696  *		   it is done at register again.
697  */
698 /*ARGSUSED*/
699 static int
700 aggr_notify_event(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
701     char **errorp, nvlist_t *nvl, rcm_info_t **depend_info)
702 {
703 	nvpair_t	*nvp = NULL;
704 	datalink_id_t	linkid;
705 	uint64_t	id64;
706 	boolean_t	up;
707 	int		rv = RCM_SUCCESS;
708 
709 	rcm_log_message(RCM_TRACE1, "AGGR: notify_event(%s)\n", rsrc);
710 
711 	if (strcmp(rsrc, RCM_RESOURCE_LINK_NEW) != 0) {
712 		aggr_log_err(DATALINK_INVALID_LINKID, errorp,
713 		    "unrecognized event");
714 		errno = EINVAL;
715 		return (RCM_FAILURE);
716 	}
717 
718 	/* Update cache to reflect latest AGGRs */
719 	if (cache_update(hd) < 0) {
720 		aggr_log_err(DATALINK_INVALID_LINKID, errorp,
721 		    "private Cache update failed");
722 		return (RCM_FAILURE);
723 	}
724 
725 	/* Process the nvlist for the event */
726 	rcm_log_message(RCM_TRACE1, "AGGR: process_nvlist\n");
727 	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
728 
729 		if (strcmp(nvpair_name(nvp), RCM_NV_LINKID) != 0)
730 			continue;
731 
732 		if (nvpair_value_uint64(nvp, &id64) != 0) {
733 			aggr_log_err(DATALINK_INVALID_LINKID, errorp,
734 			    "cannot get linkid");
735 			return (RCM_FAILURE);
736 		}
737 
738 		linkid = (datalink_id_t)id64;
739 		if (aggr_configure_all(hd, linkid, &up) != 0) {
740 			aggr_log_err(linkid, errorp,
741 			    "failed configuring AGGR links");
742 			rv = RCM_FAILURE;
743 		}
744 
745 		/* Notify all VLAN and IP AGGR consumers */
746 		if (up && aggr_consumer_notify(hd, linkid, errorp, flags,
747 		    depend_info) != 0) {
748 			aggr_log_err(linkid, errorp, "consumer notify failed");
749 			rv = RCM_FAILURE;
750 		}
751 	}
752 
753 	rcm_log_message(RCM_TRACE1,
754 	    "AGGR: notify_event: link configuration complete\n");
755 	return (rv);
756 }
757 
758 /*
759  * aggr_usage - Determine the usage of a link.
760  *	    The returned buffer is owned by caller, and the caller
761  *	    must free it up when done.
762  */
763 static char *
764 aggr_usage(link_cache_t *node)
765 {
766 	char *buf;
767 	const char *fmt;
768 	char errmsg[DLADM_STRSIZE];
769 	char name[MAXLINKNAMELEN];
770 	dladm_status_t status;
771 	size_t bufsz;
772 
773 	rcm_log_message(RCM_TRACE2, "AGGR: usage(%s)\n", node->vc_resource);
774 	assert(MUTEX_HELD(&cache_lock));
775 
776 	if (node->vc_state & CACHE_NODE_OFFLINED)
777 		fmt = _("%s offlined");
778 	else
779 		fmt = _("%s is part of AGGR ");
780 
781 	if ((status = dladm_datalink_id2info(dld_handle, node->vc_linkid, NULL,
782 	    NULL, NULL, name, sizeof (name))) != DLADM_STATUS_OK) {
783 		rcm_log_message(RCM_ERROR,
784 		    _("AGGR: usage(%s) get port name failure(%s)\n"),
785 		    node->vc_resource, dladm_status2str(status, errmsg));
786 		return (NULL);
787 	}
788 
789 	/* space for resources and message */
790 	bufsz = MAXLINKNAMELEN + strlen(fmt) + strlen(name) + 1;
791 	if ((buf = malloc(bufsz)) == NULL) {
792 		rcm_log_message(RCM_ERROR,
793 		    _("AGGR: usage(%s) malloc failure(%s)\n"),
794 		    node->vc_resource, strerror(errno));
795 		return (NULL);
796 	}
797 	(void) snprintf(buf, bufsz, fmt, name);
798 
799 	if (node->vc_state & CACHE_NODE_OFFLINED) {
800 		/* Nothing else to do */
801 		rcm_log_message(RCM_TRACE2, "AGGR: usage (%s) info = %s\n",
802 		    node->vc_resource, buf);
803 		return (buf);
804 	}
805 
806 	if ((status = dladm_datalink_id2info(dld_handle,
807 	    node->vc_aggr->da_aggrid, NULL, NULL, NULL, name,
808 	    sizeof (name))) != DLADM_STATUS_OK) {
809 		rcm_log_message(RCM_ERROR,
810 		    _("AGGR: usage(%s) get aggr %u name failure(%s)\n"),
811 		    node->vc_resource, node->vc_aggr->da_aggrid,
812 		    dladm_status2str(status, errmsg));
813 		(void) free(buf);
814 		return (NULL);
815 	}
816 
817 	(void) strlcat(buf, name, bufsz);
818 
819 	rcm_log_message(RCM_TRACE2, "AGGR: usage (%s) info = %s\n",
820 	    node->vc_resource, buf);
821 	return (buf);
822 }
823 
824 /*
825  * Cache management routines, all cache management functions should be
826  * be called with cache_lock held.
827  */
828 
829 /*
830  * cache_lookup() - Get a cache node for a resource.
831  *		  Call with cache lock held.
832  *
833  * This ensures that the cache is consistent with the system state and
834  * returns a pointer to the cache element corresponding to the resource.
835  */
836 static link_cache_t *
837 cache_lookup(rcm_handle_t *hd, char *rsrc, char options)
838 {
839 	link_cache_t *node;
840 
841 	rcm_log_message(RCM_TRACE2, "AGGR: cache lookup(%s)\n", rsrc);
842 	assert(MUTEX_HELD(&cache_lock));
843 
844 	if (options & CACHE_REFRESH) {
845 		/* drop lock since update locks cache again */
846 		(void) mutex_unlock(&cache_lock);
847 		(void) cache_update(hd);
848 		(void) mutex_lock(&cache_lock);
849 	}
850 
851 	node = cache_head.vc_next;
852 	for (; node != &cache_tail; node = node->vc_next) {
853 		if (strcmp(rsrc, node->vc_resource) == 0) {
854 			rcm_log_message(RCM_TRACE2,
855 			    "AGGR: cache lookup succeeded(%s)\n", rsrc);
856 			return (node);
857 		}
858 	}
859 	return (NULL);
860 }
861 
862 /*
863  * node_free - Free a node from the cache
864  */
865 static void
866 node_free(link_cache_t *node)
867 {
868 	free(node->vc_resource);
869 	free(node);
870 }
871 
872 /*
873  * cache_insert - Insert a resource node in cache
874  */
875 static void
876 cache_insert(link_cache_t *node)
877 {
878 	assert(MUTEX_HELD(&cache_lock));
879 
880 	/* insert at the head for best performance */
881 	node->vc_next = cache_head.vc_next;
882 	node->vc_prev = &cache_head;
883 
884 	node->vc_next->vc_prev = node;
885 	node->vc_prev->vc_next = node;
886 }
887 
888 /*
889  * cache_remove() - Remove a resource node from cache.
890  *		  Call with the cache_lock held.
891  */
892 static void
893 cache_remove(link_cache_t *node)
894 {
895 	assert(MUTEX_HELD(&cache_lock));
896 	node->vc_next->vc_prev = node->vc_prev;
897 	node->vc_prev->vc_next = node->vc_next;
898 	node->vc_next = NULL;
899 	node->vc_prev = NULL;
900 }
901 
902 static int
903 aggr_port_update(rcm_handle_t *hd, dl_aggr_t *aggr, datalink_id_t portid)
904 {
905 	link_cache_t *node;
906 	char *rsrc;
907 	int ret = -1;
908 
909 	rcm_log_message(RCM_TRACE1,
910 	    "AGGR: aggr_port_update aggr:%u port:%u\n",
911 	    aggr->da_aggrid, portid);
912 	assert(MUTEX_HELD(&cache_lock));
913 
914 	rsrc = malloc(RCM_LINK_RESOURCE_MAX);
915 	if (rsrc == NULL) {
916 		rcm_log_message(RCM_ERROR,
917 		    _("AGGR: resource malloc error(%s)\n"), strerror(errno));
918 		goto done;
919 	}
920 
921 	(void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u",
922 	    RCM_LINK_PREFIX, portid);
923 
924 	node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH);
925 	if (node != NULL) {
926 		rcm_log_message(RCM_DEBUG,
927 		    "AGGR: %s already registered (aggrid:%u)\n",
928 		    rsrc, aggr->da_aggrid);
929 
930 		free(rsrc);
931 		node->vc_state &= ~CACHE_NODE_STALE;
932 
933 		assert(node->vc_linkid == portid);
934 		/*
935 		 * Update vc_aggr directly as only one aggregation can be
936 		 * created on one port.
937 		 */
938 		node->vc_aggr = aggr;
939 	} else {
940 		rcm_log_message(RCM_DEBUG,
941 		    "AGGR: %s is a new resource (aggrid:%u)\n",
942 		    rsrc, aggr->da_aggrid);
943 
944 		node = calloc(1, sizeof (link_cache_t));
945 		if (node == NULL) {
946 			free(rsrc);
947 			rcm_log_message(RCM_ERROR,
948 			    _("AGGR: calloc: %s\n"), strerror(errno));
949 			return (ret);
950 		}
951 
952 		node->vc_resource = rsrc;
953 		node->vc_aggr = aggr;
954 		node->vc_linkid = portid;
955 		node->vc_state |= CACHE_NODE_NEW;
956 
957 
958 		cache_insert(node);
959 	}
960 
961 	ret = 0;
962 done:
963 	return (ret);
964 }
965 
966 typedef struct aggr_update_arg_s {
967 	rcm_handle_t	*hd;
968 	int		retval;
969 } aggr_update_arg_t;
970 
971 /*
972  * aggr_update() - Update physical interface properties
973  */
974 static int
975 aggr_update(dladm_handle_t handle, datalink_id_t aggrid, void *arg)
976 {
977 	aggr_update_arg_t *aggr_update_argp = arg;
978 	rcm_handle_t *hd = aggr_update_argp->hd;
979 	dladm_aggr_grp_attr_t aggr_attr;
980 	dl_aggr_t *aggr;
981 	dladm_status_t status;
982 	char errmsg[DLADM_STRSIZE];
983 	boolean_t exist = B_FALSE;
984 	uint32_t i;
985 	int ret = -1;
986 
987 	rcm_log_message(RCM_TRACE1, "AGGR: aggr_update(%u)\n", aggrid);
988 
989 	assert(MUTEX_HELD(&aggr_list_lock));
990 	status = dladm_aggr_info(handle, aggrid, &aggr_attr,
991 	    DLADM_OPT_ACTIVE);
992 	if (status != DLADM_STATUS_OK) {
993 		rcm_log_message(RCM_TRACE1,
994 		    "AGGR: cannot get aggr information for %u error(%s)\n",
995 		    aggrid, dladm_status2str(status, errmsg));
996 		return (DLADM_WALK_CONTINUE);
997 	}
998 
999 	/*
1000 	 * Try to find the aggr from the aggr list.
1001 	 */
1002 	for (aggr = aggr_head.da_next; aggr != &aggr_tail; aggr = aggr->da_next)
1003 		if (aggr->da_aggrid == aggr_attr.lg_linkid)
1004 			break;
1005 
1006 	if (aggr != NULL) {
1007 		exist = B_TRUE;
1008 	} else {
1009 		if ((aggr = calloc(1, sizeof (dl_aggr_t))) == NULL) {
1010 			rcm_log_message(RCM_ERROR, _("AGGR: malloc: %s\n"),
1011 			    strerror(errno));
1012 			goto done;
1013 		}
1014 	}
1015 
1016 	/* Update aggregation information. */
1017 	if (aggr_attr.lg_nports == 1)
1018 		aggr->da_lastport = aggr_attr.lg_ports[0].lp_linkid;
1019 	else
1020 		aggr->da_lastport = DATALINK_INVALID_LINKID;
1021 	aggr->da_aggrid = aggr_attr.lg_linkid;
1022 
1023 	for (i = 0; i < aggr_attr.lg_nports; i++) {
1024 		datalink_id_t portid = (aggr_attr.lg_ports[i]).lp_linkid;
1025 
1026 		if (aggr_port_update(hd, aggr, portid) != 0)
1027 			goto done;
1028 	}
1029 
1030 	if (!exist)
1031 		aggr_list_insert(aggr);
1032 
1033 	aggr->da_stale = B_FALSE;
1034 	rcm_log_message(RCM_TRACE3,
1035 	    "AGGR: aggr_update: succeeded(%u)\n", aggrid);
1036 
1037 	ret = 0;
1038 done:
1039 	if (!exist && ret != 0)
1040 		free(aggr);
1041 	free(aggr_attr.lg_ports);
1042 	aggr_update_argp->retval = ret;
1043 	return (ret == 0 ? DLADM_WALK_CONTINUE : DLADM_WALK_TERMINATE);
1044 }
1045 
1046 /*
1047  * aggr_update_all() - Determine all AGGR links in the system
1048  */
1049 static int
1050 aggr_update_all(rcm_handle_t *hd)
1051 {
1052 	aggr_update_arg_t arg = {NULL, 0};
1053 
1054 	rcm_log_message(RCM_TRACE2, "AGGR: aggr_update_all\n");
1055 	assert(MUTEX_HELD(&cache_lock));
1056 
1057 	arg.hd = hd;
1058 	(void) dladm_walk_datalink_id(aggr_update, dld_handle, &arg,
1059 	    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
1060 	return (arg.retval);
1061 }
1062 
1063 /*
1064  * cache_update() - Update cache with latest interface info
1065  */
1066 static int
1067 cache_update(rcm_handle_t *hd)
1068 {
1069 	link_cache_t *node, *next;
1070 	dl_aggr_t *aggr;
1071 	int ret = 0;
1072 
1073 	rcm_log_message(RCM_TRACE2, "AGGR: cache_update\n");
1074 	(void) mutex_lock(&aggr_list_lock);
1075 	(void) mutex_lock(&cache_lock);
1076 
1077 	/* first we walk the entire aggr list, marking each entry stale */
1078 	for (aggr = aggr_head.da_next; aggr != &aggr_tail; aggr = aggr->da_next)
1079 		aggr->da_stale = B_TRUE;
1080 
1081 	/* then we walk the entire cache, marking each entry stale */
1082 	node = cache_head.vc_next;
1083 	for (; node != &cache_tail; node = node->vc_next)
1084 		node->vc_state |= CACHE_NODE_STALE;
1085 
1086 	ret = aggr_update_all(hd);
1087 
1088 	/*
1089 	 * Even aggr_update_all() fails, continue to delete all the stale
1090 	 * resources. First, unregister links that are not offlined and
1091 	 * still in cache.
1092 	 */
1093 	for (node = cache_head.vc_next; node != &cache_tail; node = next) {
1094 
1095 		next = node->vc_next;
1096 		if (node->vc_state & CACHE_NODE_STALE) {
1097 			(void) rcm_unregister_interest(hd, node->vc_resource,
1098 			    0);
1099 			rcm_log_message(RCM_DEBUG,
1100 			    "AGGR: unregistered %s\n", node->vc_resource);
1101 			cache_remove(node);
1102 			node_free(node);
1103 			continue;
1104 		}
1105 
1106 		if (!(node->vc_state & CACHE_NODE_NEW))
1107 			continue;
1108 
1109 		if (rcm_register_interest(hd, node->vc_resource, 0,
1110 
1111 		    NULL) != RCM_SUCCESS) {
1112 			rcm_log_message(RCM_ERROR,
1113 			    _("AGGR: failed to register %s\n"),
1114 			    node->vc_resource);
1115 			ret = -1;
1116 		} else {
1117 			rcm_log_message(RCM_DEBUG, "AGGR: registered %s\n",
1118 			    node->vc_resource);
1119 
1120 			node->vc_state &= ~CACHE_NODE_NEW;
1121 		}
1122 	}
1123 
1124 	aggr = aggr_head.da_next;
1125 	while (aggr != &aggr_tail) {
1126 		dl_aggr_t *next = aggr->da_next;
1127 
1128 		/* delete stale AGGRs */
1129 		if (aggr->da_stale) {
1130 			aggr_list_remove(aggr);
1131 			free(aggr);
1132 		}
1133 		aggr = next;
1134 	}
1135 
1136 done:
1137 	(void) mutex_unlock(&cache_lock);
1138 	(void) mutex_unlock(&aggr_list_lock);
1139 	return (ret);
1140 }
1141 
1142 /*
1143  * aggr_log_err() - RCM error log wrapper
1144  */
1145 static void
1146 aggr_log_err(datalink_id_t linkid, char **errorp, char *errmsg)
1147 {
1148 	char link[MAXLINKNAMELEN];
1149 	char errstr[DLADM_STRSIZE];
1150 	dladm_status_t status;
1151 	int len;
1152 	const char *errfmt;
1153 	char *error;
1154 
1155 	link[0] = '\0';
1156 	if (linkid != DATALINK_INVALID_LINKID) {
1157 		char rsrc[RCM_LINK_RESOURCE_MAX];
1158 
1159 		(void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u",
1160 		    RCM_LINK_PREFIX, linkid);
1161 
1162 		rcm_log_message(RCM_ERROR, _("AGGR: %s(%s)\n"), errmsg, rsrc);
1163 
1164 		if ((status = dladm_datalink_id2info(dld_handle, linkid, NULL,
1165 		    NULL, NULL, link, sizeof (link))) != DLADM_STATUS_OK) {
1166 			rcm_log_message(RCM_WARNING,
1167 			    _("AGGR: cannot get link name of (%s) %s\n"),
1168 			    rsrc, dladm_status2str(status, errstr));
1169 		}
1170 	} else {
1171 		rcm_log_message(RCM_ERROR, _("AGGR: %s\n"), errmsg);
1172 	}
1173 
1174 	errfmt = strlen(link) > 0 ? _("AGGR: %s(%s)") : _("AGGR: %s");
1175 	len = strlen(errfmt) + strlen(errmsg) + MAXLINKNAMELEN + 1;
1176 	if ((error = malloc(len)) != NULL) {
1177 		if (strlen(link) > 0)
1178 			(void) sprintf(error, errfmt, errmsg, link);
1179 		else
1180 			(void) sprintf(error, errfmt, errmsg);
1181 	}
1182 
1183 	if (errorp != NULL)
1184 		*errorp = error;
1185 }
1186 
1187 /*
1188  * aggr_consumer_offline()
1189  *
1190  *	Offline AGGR consumers.
1191  */
1192 static int
1193 aggr_consumer_offline(rcm_handle_t *hd, link_cache_t *node, char **errorp,
1194     uint_t flags, rcm_info_t **depend_info)
1195 {
1196 	char rsrc[RCM_LINK_RESOURCE_MAX];
1197 	int ret;
1198 
1199 	rcm_log_message(RCM_TRACE2, "AGGR: aggr_consumer_offline %s\n",
1200 	    node->vc_resource);
1201 
1202 	(void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u",
1203 	    RCM_LINK_PREFIX, node->vc_aggr->da_aggrid);
1204 
1205 	/*
1206 	 * Inform associated VLANs and IP interfaces to be offlined
1207 	 */
1208 	ret = rcm_request_offline(hd, rsrc, flags, depend_info);
1209 	if (ret != RCM_SUCCESS) {
1210 		rcm_log_message(RCM_DEBUG,
1211 		    "AGGR: rcm_request_offline failed (%s)\n", rsrc);
1212 		return (ret);
1213 	}
1214 
1215 	node->vc_state |= CACHE_AGGR_CONSUMER_OFFLINED;
1216 	rcm_log_message(RCM_TRACE2, "AGGR: aggr_consumer_offline done\n");
1217 	return (ret);
1218 }
1219 
1220 /*
1221  * aggr_consumer_online()
1222  *
1223  *	online AGGR consumers.
1224  */
1225 static int
1226 aggr_consumer_online(rcm_handle_t *hd, link_cache_t *node, char **errorp,
1227     uint_t flags, rcm_info_t **depend_info)
1228 {
1229 	char rsrc[RCM_LINK_RESOURCE_MAX];
1230 	int ret;
1231 
1232 	rcm_log_message(RCM_TRACE2, "AGGR: aggr_consumer_online %s\n",
1233 	    node->vc_resource);
1234 
1235 	if (!(node->vc_state & CACHE_AGGR_CONSUMER_OFFLINED)) {
1236 		rcm_log_message(RCM_DEBUG,
1237 		    "AGGR: no consumers offlined (%s)\n", node->vc_resource);
1238 		return (RCM_SUCCESS);
1239 	}
1240 
1241 	(void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u",
1242 	    RCM_LINK_PREFIX, node->vc_aggr->da_aggrid);
1243 
1244 	ret = rcm_notify_online(hd, rsrc, flags, depend_info);
1245 	if (ret != RCM_SUCCESS) {
1246 		rcm_log_message(RCM_DEBUG,
1247 		    "AGGR: rcm_notify_online failed (%s)\n", rsrc);
1248 		return (ret);
1249 	}
1250 
1251 	node->vc_state &= ~CACHE_AGGR_CONSUMER_OFFLINED;
1252 	rcm_log_message(RCM_TRACE2, "AGGR: aggr_consumer_online done\n");
1253 	return (ret);
1254 }
1255 
1256 /*
1257  * Send RCM_RESOURCE_LINK_NEW events to other modules about new aggregations.
1258  * Return 0 on success, -1 on failure.
1259  */
1260 static int
1261 aggr_notify_new_aggr(rcm_handle_t *hd, char *rsrc)
1262 {
1263 	link_cache_t *node;
1264 	dl_aggr_t *aggr;
1265 	nvlist_t *nvl = NULL;
1266 	uint64_t id;
1267 	boolean_t is_only_port;
1268 	int ret = -1;
1269 
1270 	rcm_log_message(RCM_TRACE2, "AGGR: aggr_notify_new_aggr (%s)\n", rsrc);
1271 
1272 	/* Check for the interface in the cache */
1273 	(void) mutex_lock(&cache_lock);
1274 	if ((node = cache_lookup(hd, rsrc, CACHE_REFRESH)) == NULL) {
1275 		rcm_log_message(RCM_TRACE1,
1276 		    "AGGR: aggr_notify_new_aggr() unrecognized resource (%s)\n",
1277 		    rsrc);
1278 		(void) mutex_unlock(&cache_lock);
1279 		return (0);
1280 	}
1281 
1282 	if (nvlist_alloc(&nvl, 0, 0) != 0) {
1283 		rcm_log_message(RCM_WARNING,
1284 		    _("AGGR: failed to allocate nvlist\n"));
1285 		(void) mutex_unlock(&cache_lock);
1286 		goto done;
1287 	}
1288 
1289 	aggr = node->vc_aggr;
1290 	is_only_port = (aggr->da_lastport == node->vc_linkid);
1291 
1292 	if (is_only_port) {
1293 		rcm_log_message(RCM_TRACE2,
1294 		    "AGGR: aggr_notify_new_aggr add (%u)\n",
1295 		    aggr->da_aggrid);
1296 
1297 		id = aggr->da_aggrid;
1298 		if (nvlist_add_uint64(nvl, RCM_NV_LINKID, id) != 0) {
1299 			rcm_log_message(RCM_ERROR,
1300 			    _("AGGR: failed to construct nvlist\n"));
1301 			(void) mutex_unlock(&cache_lock);
1302 			goto done;
1303 		}
1304 	}
1305 
1306 	(void) mutex_unlock(&cache_lock);
1307 
1308 	/*
1309 	 * If this link is not the only port in the aggregation, the aggregation
1310 	 * is not new. No need to inform other consumers in that case.
1311 	 */
1312 	if (is_only_port && rcm_notify_event(hd, RCM_RESOURCE_LINK_NEW,
1313 	    0, nvl, NULL) != RCM_SUCCESS) {
1314 		rcm_log_message(RCM_ERROR,
1315 		    _("AGGR: failed to notify %s event for %s\n"),
1316 		    RCM_RESOURCE_LINK_NEW, node->vc_resource);
1317 		goto done;
1318 	}
1319 
1320 	ret = 0;
1321 done:
1322 	if (nvl != NULL)
1323 		nvlist_free(nvl);
1324 	return (ret);
1325 }
1326 
1327 /*
1328  * aggr_consumer_notify() - Notify consumers of AGGRs coming back online.
1329  */
1330 static int
1331 aggr_consumer_notify(rcm_handle_t *hd, datalink_id_t linkid, char **errorp,
1332     uint_t flags, rcm_info_t **depend_info)
1333 {
1334 	char rsrc[RCM_LINK_RESOURCE_MAX];
1335 	link_cache_t *node;
1336 
1337 	(void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u",
1338 	    RCM_LINK_PREFIX, linkid);
1339 
1340 	rcm_log_message(RCM_TRACE1, "AGGR: aggr_consumer_notify(%s)\n", rsrc);
1341 
1342 	/*
1343 	 * Inform IP and VLAN consumers to be online.
1344 	 */
1345 	if (aggr_notify_new_aggr(hd, rsrc) != 0) {
1346 		(void) mutex_lock(&cache_lock);
1347 		if ((node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH)) != NULL)
1348 			(void) aggr_offline_port(node, CACHE_NODE_STALE);
1349 		(void) mutex_unlock(&cache_lock);
1350 		rcm_log_message(RCM_TRACE1,
1351 		    "AGGR: aggr_notify_new_aggr failed(%s)\n", rsrc);
1352 		return (-1);
1353 	}
1354 
1355 	rcm_log_message(RCM_TRACE2, "AGGR: aggr_consumer_notify succeeded\n");
1356 	return (0);
1357 }
1358 
1359 typedef struct aggr_configure_arg {
1360 	datalink_id_t	portid;
1361 	int		retval;
1362 	boolean_t	up;
1363 } aggr_configure_arg_t;
1364 
1365 static int
1366 aggr_configure(dladm_handle_t handle, datalink_id_t aggrid, void *arg)
1367 {
1368 	aggr_configure_arg_t *aggr_configure_argp = arg;
1369 	datalink_id_t portid;
1370 	dladm_aggr_grp_attr_t aggr_attr;
1371 	dladm_aggr_port_attr_db_t port_attr;
1372 	dladm_status_t status;
1373 	uint32_t flags;
1374 	char errmsg[DLADM_STRSIZE];
1375 	int i;
1376 
1377 	status = dladm_datalink_id2info(handle, aggrid, &flags, NULL, NULL,
1378 	    NULL, 0);
1379 	if (status != DLADM_STATUS_OK)
1380 		return (DLADM_WALK_CONTINUE);
1381 
1382 	status = dladm_aggr_info(handle, aggrid, &aggr_attr, DLADM_OPT_PERSIST);
1383 	if (status != DLADM_STATUS_OK)
1384 		return (DLADM_WALK_CONTINUE);
1385 
1386 	portid = aggr_configure_argp->portid;
1387 	for (i = 0; i < aggr_attr.lg_nports; i++)
1388 		if (aggr_attr.lg_ports[i].lp_linkid == portid)
1389 			break;
1390 
1391 	if (i == aggr_attr.lg_nports) {
1392 		/*
1393 		 * The aggregation doesn't contain this port.
1394 		 */
1395 		free(aggr_attr.lg_ports);
1396 		return (DLADM_WALK_CONTINUE);
1397 	}
1398 
1399 	/*
1400 	 * If this aggregation already exists, add this port to this
1401 	 * aggregation, otherwise, bring up this aggregation.
1402 	 */
1403 	if (flags & DLADM_OPT_ACTIVE) {
1404 		rcm_log_message(RCM_TRACE3,
1405 		    "AGGR: aggr_configure dladm_aggr_add port %u (%u)\n",
1406 		    portid, aggrid);
1407 		port_attr.lp_linkid = portid;
1408 		status = dladm_aggr_add(handle, aggrid, 1, &port_attr,
1409 		    DLADM_OPT_ACTIVE);
1410 	} else {
1411 		rcm_log_message(RCM_TRACE3,
1412 		    "AGGR: aggr_configure dladm_aggr_up (%u)\n", aggrid);
1413 		status = dladm_aggr_up(handle, aggrid);
1414 	}
1415 
1416 	if (status != DLADM_STATUS_OK) {
1417 		/*
1418 		 * Print a warning message and continue to UP other AGGRs.
1419 		 */
1420 		rcm_log_message(RCM_WARNING,
1421 		    _("AGGR: AGGR online failed (%u): %s\n"),
1422 		    aggrid, dladm_status2str(status, errmsg));
1423 		aggr_configure_argp->retval = -1;
1424 	} else if (!(flags & DLADM_OPT_ACTIVE)) {
1425 		aggr_configure_argp->up = B_TRUE;
1426 	}
1427 
1428 	free(aggr_attr.lg_ports);
1429 	return (DLADM_WALK_TERMINATE);
1430 }
1431 
1432 /*
1433  * aggr_configure_all() - Configure AGGRs over a physical link after it attaches
1434  */
1435 static int
1436 aggr_configure_all(rcm_handle_t *hd, datalink_id_t linkid, boolean_t *up)
1437 {
1438 	char rsrc[RCM_LINK_RESOURCE_MAX];
1439 	link_cache_t *node;
1440 	aggr_configure_arg_t arg = {DATALINK_INVALID_LINKID, 0, B_FALSE};
1441 
1442 	*up = B_FALSE;
1443 
1444 	/* Check for the AGGRs in the cache */
1445 	(void) snprintf(rsrc, sizeof (rsrc), "%s/%u", RCM_LINK_PREFIX, linkid);
1446 
1447 	rcm_log_message(RCM_TRACE1, "AGGR: aggr_configure_all(%s)\n", rsrc);
1448 
1449 	/* Check if the link is new or was previously offlined */
1450 	(void) mutex_lock(&cache_lock);
1451 	if (((node = cache_lookup(hd, rsrc, CACHE_REFRESH)) != NULL) &&
1452 	    (!(node->vc_state & CACHE_NODE_OFFLINED))) {
1453 		rcm_log_message(RCM_TRACE1,
1454 		    "AGGR: Skipping configured link(%s)\n", rsrc);
1455 		(void) mutex_unlock(&cache_lock);
1456 		return (0);
1457 	}
1458 	(void) mutex_unlock(&cache_lock);
1459 
1460 	arg.portid = linkid;
1461 	(void) dladm_walk_datalink_id(aggr_configure, dld_handle, &arg,
1462 	    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
1463 
1464 	if (arg.retval == 0) {
1465 		*up = arg.up;
1466 		rcm_log_message(RCM_TRACE1,
1467 		    "AGGR: aggr_configure_all succeeded(%s)\n", rsrc);
1468 	}
1469 	return (arg.retval);
1470 }
1471