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