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