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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * This RCM module adds support to the RCM framework for AGGR links
35 #include <sys/types.h>
40 #include "rcm_module.h"
42 #include <libdllink.h>
43 #include <libdlaggr.h>
49 #define _(x) gettext(x)
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)
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
;
67 /* AGGR Cache state flags */
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 */
76 /* Network Cache lookup options */
77 #define CACHE_NO_REFRESH 0x1 /* cache refresh not needed */
78 #define CACHE_REFRESH 0x2 /* refresh cache */
81 * Cache element. It is used to keep a list of links on the system and
82 * their associated aggregations.
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 */
94 * Global cache for network AGGRs
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;
104 static dladm_handle_t dld_handle
= NULL
;
107 * RCM module interface prototypes
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
,
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
**);
148 /* Module-Private data */
149 static struct rcm_mod_ops aggr_ops
=
166 * rcm_mod_init() - Update registrations, and return the ops structure.
171 dladm_status_t status
;
172 char errmsg
[DLADM_STRSIZE
];
174 rcm_log_message(RCM_TRACE1
, "AGGR: mod_init\n");
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
);
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
));
194 /* Return the ops vectors */
199 * rcm_mod_info() - Return a string describing this module.
204 rcm_log_message(RCM_TRACE1
, "AGGR: mod_info\n");
206 return ("AGGR module version 1.1");
210 * rcm_mod_fini() - Destroy the network AGGR cache.
217 rcm_log_message(RCM_TRACE1
, "AGGR: mod_fini\n");
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.
225 (void) mutex_lock(&cache_lock
);
226 node
= cache_head
.vc_next
;
227 while (node
!= &cache_tail
) {
230 node
= cache_head
.vc_next
;
232 (void) mutex_unlock(&cache_lock
);
233 (void) mutex_destroy(&cache_lock
);
236 (void) mutex_destroy(&aggr_list_lock
);
238 dladm_close(dld_handle
);
239 return (RCM_SUCCESS
);
243 * aggr_list_insert - Insert an aggr in the global aggr list
246 aggr_list_insert(dl_aggr_t
*aggr
)
248 assert(MUTEX_HELD(&aggr_list_lock
));
250 /* insert at the head for best performance */
251 aggr
->da_next
= aggr_head
.da_next
;
252 aggr
->da_prev
= &aggr_head
;
254 aggr
->da_next
->da_prev
= aggr
;
255 aggr
->da_prev
->da_next
= aggr
;
259 * aggr_list_remove - Remove an aggr from the global aggr list
262 aggr_list_remove(dl_aggr_t
*aggr
)
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
;
276 (void) mutex_lock(&aggr_list_lock
);
277 aggr
= aggr_head
.da_next
;
278 while (aggr
!= &aggr_tail
) {
279 aggr_list_remove(aggr
);
281 aggr
= aggr_head
.da_next
;
283 (void) mutex_unlock(&aggr_list_lock
);
287 * aggr_register() - Make sure the cache is properly sync'ed, and its
288 * registrations are in order.
291 aggr_register(rcm_handle_t
*hd
)
293 rcm_log_message(RCM_TRACE1
, "AGGR: register\n");
295 if (cache_update(hd
) < 0)
296 return (RCM_FAILURE
);
299 * Need to register interest in all new resources
300 * getting attached, so we get attach event notifications
302 if (!events_registered
) {
303 if (rcm_register_event(hd
, RCM_RESOURCE_LINK_NEW
, 0, NULL
)
305 rcm_log_message(RCM_ERROR
,
306 _("AGGR: failed to register %s\n"),
307 RCM_RESOURCE_LINK_NEW
);
308 return (RCM_FAILURE
);
310 rcm_log_message(RCM_DEBUG
, "AGGR: registered %s\n",
311 RCM_RESOURCE_LINK_NEW
);
316 return (RCM_SUCCESS
);
320 * aggr_unregister() - Walk the cache, unregistering all the networks.
323 aggr_unregister(rcm_handle_t
*hd
)
327 rcm_log_message(RCM_TRACE1
, "AGGR: unregister\n");
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)
335 /* unregister failed for whatever reason */
336 rcm_log_message(RCM_ERROR
,
337 _("AGGR: failed to unregister %s\n"),
339 (void) mutex_unlock(&cache_lock
);
340 return (RCM_FAILURE
);
344 node
= cache_head
.vc_next
;
346 (void) mutex_unlock(&cache_lock
);
351 * Unregister interest in all new resources
353 if (events_registered
) {
354 if (rcm_unregister_event(hd
, RCM_RESOURCE_LINK_NEW
, 0)
356 rcm_log_message(RCM_ERROR
,
357 _("AGGR: failed to unregister %s\n"),
358 RCM_RESOURCE_LINK_NEW
);
359 return (RCM_FAILURE
);
361 rcm_log_message(RCM_DEBUG
, "AGGR: unregistered %s\n",
362 RCM_RESOURCE_LINK_NEW
);
367 return (RCM_SUCCESS
);
371 * aggr_offline() - Offline AGGRs on a specific link.
374 aggr_offline(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flags
,
375 char **errorp
, rcm_info_t
**depend_info
)
379 rcm_log_message(RCM_TRACE1
, "AGGR: offline(%s)\n", rsrc
);
381 /* Lock the cache and lookup the resource */
382 (void) mutex_lock(&cache_lock
);
383 node
= cache_lookup(hd
, rsrc
, CACHE_REFRESH
);
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
);
393 * If this given link is the only port in the aggregation, inform
394 * VLANs and IP interfaces on associated AGGRs to be offlined
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");
402 aggr_log_err(node
->vc_linkid
, errorp
,
403 "consumers offline failed");
404 (void) mutex_unlock(&cache_lock
);
405 return (RCM_FAILURE
);
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
);
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
);
423 rcm_log_message(RCM_TRACE1
, "AGGR: Offline succeeded(%s)\n", rsrc
);
424 (void) mutex_unlock(&cache_lock
);
425 return (RCM_SUCCESS
);
429 * aggr_undo_offline() - Undo offline of a previously offlined link.
433 aggr_undo_offline(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flags
,
434 char **errorp
, rcm_info_t
**depend_info
)
439 rcm_log_message(RCM_TRACE1
, "AGGR: online(%s)\n", rsrc
);
441 (void) mutex_lock(&cache_lock
);
442 node
= cache_lookup(hd
, rsrc
, CACHE_NO_REFRESH
);
444 aggr_log_err(DATALINK_INVALID_LINKID
, errorp
,
445 "undo offline, unrecognized resource");
446 (void) mutex_unlock(&cache_lock
);
448 return (RCM_FAILURE
);
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
);
456 return (RCM_SUCCESS
);
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
);
466 * Inform VLANs and IP interfaces on associated AGGRs to be online
471 if (aggr_consumer_online(hd
, node
, errorp
, flags
, depend_info
) ==
473 rcm_log_message(RCM_DEBUG
, "AGGR: Consumers agree on online");
475 rcm_log_message(RCM_WARNING
,
476 _("AGGR: Consumers online failed (%s)\n"), rsrc
);
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
);
487 aggr_offline_port(link_cache_t
*node
, cache_node_state_t state
)
490 dladm_status_t status
;
491 char errmsg
[DLADM_STRSIZE
];
492 dladm_aggr_port_attr_db_t port
;
494 rcm_log_message(RCM_TRACE2
, "AGGR: aggr_offline_port %s\n",
497 aggr
= node
->vc_aggr
;
500 * Try to remove the given port from the AGGR or delete the AGGR
502 if (aggr
->da_lastport
== node
->vc_linkid
) {
503 rcm_log_message(RCM_TRACE2
, "AGGR: delete aggregation %u\n",
505 status
= dladm_aggr_delete(dld_handle
, aggr
->da_aggrid
,
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
);
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
);
521 rcm_log_message(RCM_TRACE1
,
522 "AGGR: AGGR offline port succeeded (%u)\n",
524 node
->vc_state
|= (CACHE_AGGR_PORT_OFFLINED
| state
);
525 return (RCM_SUCCESS
);
530 aggr_online_port(link_cache_t
*node
, boolean_t
*up
)
533 dladm_status_t status
;
534 char errmsg
[DLADM_STRSIZE
];
535 dladm_aggr_port_attr_db_t port
;
537 rcm_log_message(RCM_TRACE2
, "AGGR: aggr_online_port %s\n",
541 if (!(node
->vc_state
& CACHE_AGGR_PORT_OFFLINED
))
542 return (RCM_SUCCESS
);
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.
548 aggr
= node
->vc_aggr
;
549 if (aggr
->da_lastport
== node
->vc_linkid
) {
550 rcm_log_message(RCM_TRACE2
, "AGGR: delete aggregation %u\n",
552 status
= dladm_aggr_up(dld_handle
, aggr
->da_aggrid
);
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
,
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
));
567 return (RCM_FAILURE
);
569 node
->vc_state
&= ~CACHE_AGGR_PORT_OFFLINED
;
570 return (RCM_SUCCESS
);
574 * aggr_get_info() - Gather usage information for this resource.
578 aggr_get_info(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flags
,
579 char **usagep
, char **errorp
, nvlist_t
*props
, rcm_info_t
**depend_info
)
583 rcm_log_message(RCM_TRACE1
, "AGGR: get_info(%s)\n", rsrc
);
585 (void) mutex_lock(&cache_lock
);
586 node
= cache_lookup(hd
, rsrc
, CACHE_REFRESH
);
588 rcm_log_message(RCM_INFO
,
589 _("AGGR: get_info(%s) unrecognized resource\n"), rsrc
);
590 (void) mutex_unlock(&cache_lock
);
592 return (RCM_FAILURE
);
596 * *usagep will be freed by the caller.
598 *usagep
= aggr_usage(node
);
599 (void) mutex_unlock(&cache_lock
);
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
);
607 return (RCM_FAILURE
);
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",
614 return (RCM_SUCCESS
);
618 * aggr_suspend() - Nothing to do, always okay
622 aggr_suspend(rcm_handle_t
*hd
, char *rsrc
, id_t id
, timespec_t
*interval
,
623 uint_t flags
, char **errorp
, rcm_info_t
**depend_info
)
625 rcm_log_message(RCM_TRACE1
, "AGGR: suspend(%s)\n", rsrc
);
626 return (RCM_SUCCESS
);
630 * aggr_resume() - Nothing to do, always okay
634 aggr_resume(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flags
,
635 char **errorp
, rcm_info_t
**depend_info
)
637 rcm_log_message(RCM_TRACE1
, "AGGR: resume(%s)\n", rsrc
);
638 return (RCM_SUCCESS
);
642 * aggr_remove() - remove a resource from cache
646 aggr_remove(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flags
,
647 char **errorp
, rcm_info_t
**depend_info
)
652 int rv
= RCM_SUCCESS
;
654 rcm_log_message(RCM_TRACE1
, "AGGR: remove(%s)\n", rsrc
);
656 (void) mutex_lock(&cache_lock
);
657 node
= cache_lookup(hd
, rsrc
, CACHE_NO_REFRESH
);
659 rcm_log_message(RCM_INFO
,
660 _("AGGR: remove(%s) unrecognized resource\n"), rsrc
);
661 (void) mutex_unlock(&cache_lock
);
663 return (RCM_FAILURE
);
666 /* remove the cached entry for the resource */
668 (void) mutex_unlock(&cache_lock
);
671 * If this link is not the only port in the associated aggregation,
672 * the CACHE_AGGR_CONSUMER_OFFLINED flags won't be set.
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"),
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.
700 aggr_notify_event(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flags
,
701 char **errorp
, nvlist_t
*nvl
, rcm_info_t
**depend_info
)
703 nvpair_t
*nvp
= NULL
;
704 datalink_id_t linkid
;
707 int rv
= RCM_SUCCESS
;
709 rcm_log_message(RCM_TRACE1
, "AGGR: notify_event(%s)\n", rsrc
);
711 if (strcmp(rsrc
, RCM_RESOURCE_LINK_NEW
) != 0) {
712 aggr_log_err(DATALINK_INVALID_LINKID
, errorp
,
713 "unrecognized event");
715 return (RCM_FAILURE
);
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
);
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
) {
729 if (strcmp(nvpair_name(nvp
), RCM_NV_LINKID
) != 0)
732 if (nvpair_value_uint64(nvp
, &id64
) != 0) {
733 aggr_log_err(DATALINK_INVALID_LINKID
, errorp
,
734 "cannot get linkid");
735 return (RCM_FAILURE
);
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");
745 /* Notify all VLAN and IP AGGR consumers */
746 if (up
&& aggr_consumer_notify(hd
, linkid
, errorp
, flags
,
748 aggr_log_err(linkid
, errorp
, "consumer notify failed");
753 rcm_log_message(RCM_TRACE1
,
754 "AGGR: notify_event: link configuration complete\n");
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.
764 aggr_usage(link_cache_t
*node
)
768 char errmsg
[DLADM_STRSIZE
];
769 char name
[MAXLINKNAMELEN
];
770 dladm_status_t status
;
773 rcm_log_message(RCM_TRACE2
, "AGGR: usage(%s)\n", node
->vc_resource
);
774 assert(MUTEX_HELD(&cache_lock
));
776 if (node
->vc_state
& CACHE_NODE_OFFLINED
)
777 fmt
= _("%s offlined");
779 fmt
= _("%s is part of AGGR ");
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
));
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
));
797 (void) snprintf(buf
, bufsz
, fmt
, name
);
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
);
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
));
817 (void) strlcat(buf
, name
, bufsz
);
819 rcm_log_message(RCM_TRACE2
, "AGGR: usage (%s) info = %s\n",
820 node
->vc_resource
, buf
);
825 * Cache management routines, all cache management functions should be
826 * be called with cache_lock held.
830 * cache_lookup() - Get a cache node for a resource.
831 * Call with cache lock held.
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.
836 static link_cache_t
*
837 cache_lookup(rcm_handle_t
*hd
, char *rsrc
, char options
)
841 rcm_log_message(RCM_TRACE2
, "AGGR: cache lookup(%s)\n", rsrc
);
842 assert(MUTEX_HELD(&cache_lock
));
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
);
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
);
863 * node_free - Free a node from the cache
866 node_free(link_cache_t
*node
)
868 free(node
->vc_resource
);
873 * cache_insert - Insert a resource node in cache
876 cache_insert(link_cache_t
*node
)
878 assert(MUTEX_HELD(&cache_lock
));
880 /* insert at the head for best performance */
881 node
->vc_next
= cache_head
.vc_next
;
882 node
->vc_prev
= &cache_head
;
884 node
->vc_next
->vc_prev
= node
;
885 node
->vc_prev
->vc_next
= node
;
889 * cache_remove() - Remove a resource node from cache.
890 * Call with the cache_lock held.
893 cache_remove(link_cache_t
*node
)
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
;
903 aggr_port_update(rcm_handle_t
*hd
, dl_aggr_t
*aggr
, datalink_id_t portid
)
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
));
914 rsrc
= malloc(RCM_LINK_RESOURCE_MAX
);
916 rcm_log_message(RCM_ERROR
,
917 _("AGGR: resource malloc error(%s)\n"), strerror(errno
));
921 (void) snprintf(rsrc
, RCM_LINK_RESOURCE_MAX
, "%s/%u",
922 RCM_LINK_PREFIX
, portid
);
924 node
= cache_lookup(hd
, rsrc
, CACHE_NO_REFRESH
);
926 rcm_log_message(RCM_DEBUG
,
927 "AGGR: %s already registered (aggrid:%u)\n",
928 rsrc
, aggr
->da_aggrid
);
931 node
->vc_state
&= ~CACHE_NODE_STALE
;
933 assert(node
->vc_linkid
== portid
);
935 * Update vc_aggr directly as only one aggregation can be
936 * created on one port.
938 node
->vc_aggr
= aggr
;
940 rcm_log_message(RCM_DEBUG
,
941 "AGGR: %s is a new resource (aggrid:%u)\n",
942 rsrc
, aggr
->da_aggrid
);
944 node
= calloc(1, sizeof (link_cache_t
));
947 rcm_log_message(RCM_ERROR
,
948 _("AGGR: calloc: %s\n"), strerror(errno
));
952 node
->vc_resource
= rsrc
;
953 node
->vc_aggr
= aggr
;
954 node
->vc_linkid
= portid
;
955 node
->vc_state
|= CACHE_NODE_NEW
;
966 typedef struct aggr_update_arg_s
{
972 * aggr_update() - Update physical interface properties
975 aggr_update(dladm_handle_t handle
, datalink_id_t aggrid
, void *arg
)
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
;
981 dladm_status_t status
;
982 char errmsg
[DLADM_STRSIZE
];
983 boolean_t exist
= B_FALSE
;
987 rcm_log_message(RCM_TRACE1
, "AGGR: aggr_update(%u)\n", aggrid
);
989 assert(MUTEX_HELD(&aggr_list_lock
));
990 status
= dladm_aggr_info(handle
, aggrid
, &aggr_attr
,
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
);
1000 * Try to find the aggr from the aggr list.
1002 for (aggr
= aggr_head
.da_next
; aggr
!= &aggr_tail
; aggr
= aggr
->da_next
)
1003 if (aggr
->da_aggrid
== aggr_attr
.lg_linkid
)
1009 if ((aggr
= calloc(1, sizeof (dl_aggr_t
))) == NULL
) {
1010 rcm_log_message(RCM_ERROR
, _("AGGR: malloc: %s\n"),
1016 /* Update aggregation information. */
1017 if (aggr_attr
.lg_nports
== 1)
1018 aggr
->da_lastport
= aggr_attr
.lg_ports
[0].lp_linkid
;
1020 aggr
->da_lastport
= DATALINK_INVALID_LINKID
;
1021 aggr
->da_aggrid
= aggr_attr
.lg_linkid
;
1023 for (i
= 0; i
< aggr_attr
.lg_nports
; i
++) {
1024 datalink_id_t portid
= (aggr_attr
.lg_ports
[i
]).lp_linkid
;
1026 if (aggr_port_update(hd
, aggr
, portid
) != 0)
1031 aggr_list_insert(aggr
);
1033 aggr
->da_stale
= B_FALSE
;
1034 rcm_log_message(RCM_TRACE3
,
1035 "AGGR: aggr_update: succeeded(%u)\n", aggrid
);
1039 if (!exist
&& ret
!= 0)
1041 free(aggr_attr
.lg_ports
);
1042 aggr_update_argp
->retval
= ret
;
1043 return (ret
== 0 ? DLADM_WALK_CONTINUE
: DLADM_WALK_TERMINATE
);
1047 * aggr_update_all() - Determine all AGGR links in the system
1050 aggr_update_all(rcm_handle_t
*hd
)
1052 aggr_update_arg_t arg
= {NULL
, 0};
1054 rcm_log_message(RCM_TRACE2
, "AGGR: aggr_update_all\n");
1055 assert(MUTEX_HELD(&cache_lock
));
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
);
1064 * cache_update() - Update cache with latest interface info
1067 cache_update(rcm_handle_t
*hd
)
1069 link_cache_t
*node
, *next
;
1073 rcm_log_message(RCM_TRACE2
, "AGGR: cache_update\n");
1074 (void) mutex_lock(&aggr_list_lock
);
1075 (void) mutex_lock(&cache_lock
);
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
;
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
;
1086 ret
= aggr_update_all(hd
);
1089 * Even aggr_update_all() fails, continue to delete all the stale
1090 * resources. First, unregister links that are not offlined and
1093 for (node
= cache_head
.vc_next
; node
!= &cache_tail
; node
= next
) {
1095 next
= node
->vc_next
;
1096 if (node
->vc_state
& CACHE_NODE_STALE
) {
1097 (void) rcm_unregister_interest(hd
, node
->vc_resource
,
1099 rcm_log_message(RCM_DEBUG
,
1100 "AGGR: unregistered %s\n", node
->vc_resource
);
1106 if (!(node
->vc_state
& CACHE_NODE_NEW
))
1109 if (rcm_register_interest(hd
, node
->vc_resource
, 0,
1111 NULL
) != RCM_SUCCESS
) {
1112 rcm_log_message(RCM_ERROR
,
1113 _("AGGR: failed to register %s\n"),
1117 rcm_log_message(RCM_DEBUG
, "AGGR: registered %s\n",
1120 node
->vc_state
&= ~CACHE_NODE_NEW
;
1124 aggr
= aggr_head
.da_next
;
1125 while (aggr
!= &aggr_tail
) {
1126 dl_aggr_t
*next
= aggr
->da_next
;
1128 /* delete stale AGGRs */
1129 if (aggr
->da_stale
) {
1130 aggr_list_remove(aggr
);
1137 (void) mutex_unlock(&cache_lock
);
1138 (void) mutex_unlock(&aggr_list_lock
);
1143 * aggr_log_err() - RCM error log wrapper
1146 aggr_log_err(datalink_id_t linkid
, char **errorp
, char *errmsg
)
1148 char link
[MAXLINKNAMELEN
];
1149 char errstr
[DLADM_STRSIZE
];
1150 dladm_status_t status
;
1156 if (linkid
!= DATALINK_INVALID_LINKID
) {
1157 char rsrc
[RCM_LINK_RESOURCE_MAX
];
1159 (void) snprintf(rsrc
, RCM_LINK_RESOURCE_MAX
, "%s/%u",
1160 RCM_LINK_PREFIX
, linkid
);
1162 rcm_log_message(RCM_ERROR
, _("AGGR: %s(%s)\n"), errmsg
, rsrc
);
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
));
1171 rcm_log_message(RCM_ERROR
, _("AGGR: %s\n"), errmsg
);
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
);
1180 (void) sprintf(error
, errfmt
, errmsg
);
1188 * aggr_consumer_offline()
1190 * Offline AGGR consumers.
1193 aggr_consumer_offline(rcm_handle_t
*hd
, link_cache_t
*node
, char **errorp
,
1194 uint_t flags
, rcm_info_t
**depend_info
)
1196 char rsrc
[RCM_LINK_RESOURCE_MAX
];
1199 rcm_log_message(RCM_TRACE2
, "AGGR: aggr_consumer_offline %s\n",
1202 (void) snprintf(rsrc
, RCM_LINK_RESOURCE_MAX
, "%s/%u",
1203 RCM_LINK_PREFIX
, node
->vc_aggr
->da_aggrid
);
1206 * Inform associated VLANs and IP interfaces to be offlined
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
);
1215 node
->vc_state
|= CACHE_AGGR_CONSUMER_OFFLINED
;
1216 rcm_log_message(RCM_TRACE2
, "AGGR: aggr_consumer_offline done\n");
1221 * aggr_consumer_online()
1223 * online AGGR consumers.
1226 aggr_consumer_online(rcm_handle_t
*hd
, link_cache_t
*node
, char **errorp
,
1227 uint_t flags
, rcm_info_t
**depend_info
)
1229 char rsrc
[RCM_LINK_RESOURCE_MAX
];
1232 rcm_log_message(RCM_TRACE2
, "AGGR: aggr_consumer_online %s\n",
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
);
1241 (void) snprintf(rsrc
, RCM_LINK_RESOURCE_MAX
, "%s/%u",
1242 RCM_LINK_PREFIX
, node
->vc_aggr
->da_aggrid
);
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
);
1251 node
->vc_state
&= ~CACHE_AGGR_CONSUMER_OFFLINED
;
1252 rcm_log_message(RCM_TRACE2
, "AGGR: aggr_consumer_online done\n");
1257 * Send RCM_RESOURCE_LINK_NEW events to other modules about new aggregations.
1258 * Return 0 on success, -1 on failure.
1261 aggr_notify_new_aggr(rcm_handle_t
*hd
, char *rsrc
)
1265 nvlist_t
*nvl
= NULL
;
1267 boolean_t is_only_port
;
1270 rcm_log_message(RCM_TRACE2
, "AGGR: aggr_notify_new_aggr (%s)\n", rsrc
);
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",
1278 (void) mutex_unlock(&cache_lock
);
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
);
1289 aggr
= node
->vc_aggr
;
1290 is_only_port
= (aggr
->da_lastport
== node
->vc_linkid
);
1293 rcm_log_message(RCM_TRACE2
,
1294 "AGGR: aggr_notify_new_aggr add (%u)\n",
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
);
1306 (void) mutex_unlock(&cache_lock
);
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.
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
);
1327 * aggr_consumer_notify() - Notify consumers of AGGRs coming back online.
1330 aggr_consumer_notify(rcm_handle_t
*hd
, datalink_id_t linkid
, char **errorp
,
1331 uint_t flags
, rcm_info_t
**depend_info
)
1333 char rsrc
[RCM_LINK_RESOURCE_MAX
];
1336 (void) snprintf(rsrc
, RCM_LINK_RESOURCE_MAX
, "%s/%u",
1337 RCM_LINK_PREFIX
, linkid
);
1339 rcm_log_message(RCM_TRACE1
, "AGGR: aggr_consumer_notify(%s)\n", rsrc
);
1342 * Inform IP and VLAN consumers to be online.
1344 if (aggr_notify_new_aggr(hd
, rsrc
) != 0) {
1345 (void) mutex_lock(&cache_lock
);
1346 if ((node
= cache_lookup(hd
, rsrc
, CACHE_NO_REFRESH
)) != NULL
)
1347 (void) aggr_offline_port(node
, CACHE_NODE_STALE
);
1348 (void) mutex_unlock(&cache_lock
);
1349 rcm_log_message(RCM_TRACE1
,
1350 "AGGR: aggr_notify_new_aggr failed(%s)\n", rsrc
);
1354 rcm_log_message(RCM_TRACE2
, "AGGR: aggr_consumer_notify succeeded\n");
1358 typedef struct aggr_configure_arg
{
1359 datalink_id_t portid
;
1362 } aggr_configure_arg_t
;
1365 aggr_configure(dladm_handle_t handle
, datalink_id_t aggrid
, void *arg
)
1367 aggr_configure_arg_t
*aggr_configure_argp
= arg
;
1368 datalink_id_t portid
;
1369 dladm_aggr_grp_attr_t aggr_attr
;
1370 dladm_aggr_port_attr_db_t port_attr
;
1371 dladm_status_t status
;
1373 char errmsg
[DLADM_STRSIZE
];
1376 status
= dladm_datalink_id2info(handle
, aggrid
, &flags
, NULL
, NULL
,
1378 if (status
!= DLADM_STATUS_OK
)
1379 return (DLADM_WALK_CONTINUE
);
1381 status
= dladm_aggr_info(handle
, aggrid
, &aggr_attr
, DLADM_OPT_PERSIST
);
1382 if (status
!= DLADM_STATUS_OK
)
1383 return (DLADM_WALK_CONTINUE
);
1385 portid
= aggr_configure_argp
->portid
;
1386 for (i
= 0; i
< aggr_attr
.lg_nports
; i
++)
1387 if (aggr_attr
.lg_ports
[i
].lp_linkid
== portid
)
1390 if (i
== aggr_attr
.lg_nports
) {
1392 * The aggregation doesn't contain this port.
1394 free(aggr_attr
.lg_ports
);
1395 return (DLADM_WALK_CONTINUE
);
1399 * If this aggregation already exists, add this port to this
1400 * aggregation, otherwise, bring up this aggregation.
1402 if (flags
& DLADM_OPT_ACTIVE
) {
1403 rcm_log_message(RCM_TRACE3
,
1404 "AGGR: aggr_configure dladm_aggr_add port %u (%u)\n",
1406 port_attr
.lp_linkid
= portid
;
1407 status
= dladm_aggr_add(handle
, aggrid
, 1, &port_attr
,
1410 rcm_log_message(RCM_TRACE3
,
1411 "AGGR: aggr_configure dladm_aggr_up (%u)\n", aggrid
);
1412 status
= dladm_aggr_up(handle
, aggrid
);
1415 if (status
!= DLADM_STATUS_OK
) {
1417 * Print a warning message and continue to UP other AGGRs.
1419 rcm_log_message(RCM_WARNING
,
1420 _("AGGR: AGGR online failed (%u): %s\n"),
1421 aggrid
, dladm_status2str(status
, errmsg
));
1422 aggr_configure_argp
->retval
= -1;
1423 } else if (!(flags
& DLADM_OPT_ACTIVE
)) {
1424 aggr_configure_argp
->up
= B_TRUE
;
1427 free(aggr_attr
.lg_ports
);
1428 return (DLADM_WALK_TERMINATE
);
1432 * aggr_configure_all() - Configure AGGRs over a physical link after it attaches
1435 aggr_configure_all(rcm_handle_t
*hd
, datalink_id_t linkid
, boolean_t
*up
)
1437 char rsrc
[RCM_LINK_RESOURCE_MAX
];
1439 aggr_configure_arg_t arg
= {DATALINK_INVALID_LINKID
, 0, B_FALSE
};
1443 /* Check for the AGGRs in the cache */
1444 (void) snprintf(rsrc
, sizeof (rsrc
), "%s/%u", RCM_LINK_PREFIX
, linkid
);
1446 rcm_log_message(RCM_TRACE1
, "AGGR: aggr_configure_all(%s)\n", rsrc
);
1448 /* Check if the link is new or was previously offlined */
1449 (void) mutex_lock(&cache_lock
);
1450 if (((node
= cache_lookup(hd
, rsrc
, CACHE_REFRESH
)) != NULL
) &&
1451 (!(node
->vc_state
& CACHE_NODE_OFFLINED
))) {
1452 rcm_log_message(RCM_TRACE1
,
1453 "AGGR: Skipping configured link(%s)\n", rsrc
);
1454 (void) mutex_unlock(&cache_lock
);
1457 (void) mutex_unlock(&cache_lock
);
1459 arg
.portid
= linkid
;
1460 (void) dladm_walk_datalink_id(aggr_configure
, dld_handle
, &arg
,
1461 DATALINK_CLASS_AGGR
, DATALINK_ANY_MEDIATYPE
, DLADM_OPT_PERSIST
);
1463 if (arg
.retval
== 0) {
1465 rcm_log_message(RCM_TRACE1
,
1466 "AGGR: aggr_configure_all succeeded(%s)\n", rsrc
);
1468 return (arg
.retval
);