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 */
59typedef 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 */
68typedef 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 */
84typedef 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 */
96static link_cache_t	cache_head;
97static link_cache_t	cache_tail;
98static mutex_t		cache_lock;
99static dl_aggr_t	aggr_head;
100static dl_aggr_t	aggr_tail;
101static mutex_t		aggr_list_lock;
102static int		events_registered = 0;
103
104static dladm_handle_t	dld_handle = NULL;
105
106/*
107 * RCM module interface prototypes
108 */
109static int		aggr_register(rcm_handle_t *);
110static int		aggr_unregister(rcm_handle_t *);
111static int		aggr_get_info(rcm_handle_t *, char *, id_t, uint_t,
112			    char **, char **, nvlist_t *, rcm_info_t **);
113static int		aggr_suspend(rcm_handle_t *, char *, id_t,
114			    timespec_t *, uint_t, char **, rcm_info_t **);
115static int		aggr_resume(rcm_handle_t *, char *, id_t, uint_t,
116			    char **, rcm_info_t **);
117static int		aggr_offline(rcm_handle_t *, char *, id_t, uint_t,
118			    char **, rcm_info_t **);
119static int		aggr_undo_offline(rcm_handle_t *, char *, id_t, uint_t,
120			    char **, rcm_info_t **);
121static int		aggr_remove(rcm_handle_t *, char *, id_t, uint_t,
122			    char **, rcm_info_t **);
123static int		aggr_notify_event(rcm_handle_t *, char *, id_t, uint_t,
124			    char **, nvlist_t *, rcm_info_t **);
125static int		aggr_configure_all(rcm_handle_t *, datalink_id_t,
126			    boolean_t *);
127
128/* Module private routines */
129static int 		cache_update(rcm_handle_t *);
130static void 		cache_remove(link_cache_t *);
131static void 		cache_insert(link_cache_t *);
132static void 		node_free(link_cache_t *);
133static void 		aggr_list_remove(dl_aggr_t *);
134static void 		aggr_list_insert(dl_aggr_t *);
135static void 		aggr_list_free();
136static link_cache_t	*cache_lookup(rcm_handle_t *, char *, char);
137static int		aggr_consumer_offline(rcm_handle_t *, link_cache_t *,
138			    char **, uint_t, rcm_info_t **);
139static int		aggr_consumer_online(rcm_handle_t *, link_cache_t *,
140			    char **, uint_t, rcm_info_t **);
141static int		aggr_offline_port(link_cache_t *, cache_node_state_t);
142static int		aggr_online_port(link_cache_t *, boolean_t *);
143static char 		*aggr_usage(link_cache_t *);
144static void 		aggr_log_err(datalink_id_t, char **, char *);
145static int		aggr_consumer_notify(rcm_handle_t *, datalink_id_t,
146			    char **, uint_t, rcm_info_t **);
147
148/* Module-Private data */
149static 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 */
168struct rcm_mod_ops *
169rcm_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 */
201const char *
202rcm_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 */
212int
213rcm_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 */
245static void
246aggr_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 */
261static void
262aggr_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
271static void
272aggr_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 */
290static int
291aggr_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 */
322static int
323aggr_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 */
373static int
374aggr_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*/
432static int
433aggr_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
479done:
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
486static int
487aggr_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
529static int
530aggr_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*/
577int
578aggr_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*/
621static int
622aggr_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*/
633static int
634aggr_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*/
645static int
646aggr_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*/
699static int
700aggr_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 */
763static char *
764aggr_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 */
836static link_cache_t *
837cache_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 */
865static void
866node_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 */
875static void
876cache_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 */
892static void
893cache_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
902static int
903aggr_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;
962done:
963	return (ret);
964}
965
966typedef 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 */
974static int
975aggr_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;
1038done:
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 */
1049static int
1050aggr_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 */
1066static int
1067cache_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
1136done:
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 */
1145static void
1146aggr_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 */
1192static int
1193aggr_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 */
1225static int
1226aggr_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 */
1260static int
1261aggr_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;
1321done:
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 */
1330static int
1331aggr_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
1359typedef struct aggr_configure_arg {
1360	datalink_id_t	portid;
1361	int		retval;
1362	boolean_t	up;
1363} aggr_configure_arg_t;
1364
1365static int
1366aggr_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 */
1435static int
1436aggr_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