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>
48 #define _(x) gettext(x)
50 /* Some generic well-knowns and defaults used in this module */
51 #define RCM_LINK_PREFIX "SUNW_datalink" /* RCM datalink name prefix */
52 #define RCM_LINK_RESOURCE_MAX (13 + LINKID_STR_WIDTH)
54 /* AGGR link representation */
55 typedef struct dl_aggr
{
56 struct dl_aggr
*da_next
; /* next AGGR on the system */
57 struct dl_aggr
*da_prev
; /* prev AGGR on the system */
58 boolean_t da_stale
; /* AGGR link is stale? */
59 datalink_id_t da_aggrid
;
60 datalink_id_t da_lastport
;
63 /* AGGR Cache state flags */
65 CACHE_NODE_STALE
= 0x01, /* stale cached data */
66 CACHE_NODE_NEW
= 0x02, /* new cached nodes */
67 CACHE_NODE_OFFLINED
= 0x04, /* node offlined */
68 CACHE_AGGR_PORT_OFFLINED
= 0x08, /* aggr port offlined */
69 CACHE_AGGR_CONSUMER_OFFLINED
= 0x10 /* consumers offlined */
72 /* Network Cache lookup options */
73 #define CACHE_NO_REFRESH 0x1 /* cache refresh not needed */
74 #define CACHE_REFRESH 0x2 /* refresh cache */
77 * Cache element. It is used to keep a list of links on the system and
78 * their associated aggregations.
80 typedef struct link_cache
{
81 struct link_cache
*vc_next
; /* next cached resource */
82 struct link_cache
*vc_prev
; /* prev cached resource */
83 char *vc_resource
; /* resource name */
84 datalink_id_t vc_linkid
; /* linkid */
85 dl_aggr_t
*vc_aggr
; /* AGGR on this link */
86 cache_node_state_t vc_state
; /* cache state flags */
90 * Global cache for network AGGRs
92 static link_cache_t cache_head
;
93 static link_cache_t cache_tail
;
94 static mutex_t cache_lock
;
95 static dl_aggr_t aggr_head
;
96 static dl_aggr_t aggr_tail
;
97 static mutex_t aggr_list_lock
;
98 static int events_registered
= 0;
100 static dladm_handle_t dld_handle
= NULL
;
103 * RCM module interface prototypes
105 static int aggr_register(rcm_handle_t
*);
106 static int aggr_unregister(rcm_handle_t
*);
107 static int aggr_get_info(rcm_handle_t
*, char *, id_t
, uint_t
,
108 char **, char **, nvlist_t
*, rcm_info_t
**);
109 static int aggr_suspend(rcm_handle_t
*, char *, id_t
,
110 timespec_t
*, uint_t
, char **, rcm_info_t
**);
111 static int aggr_resume(rcm_handle_t
*, char *, id_t
, uint_t
,
112 char **, rcm_info_t
**);
113 static int aggr_offline(rcm_handle_t
*, char *, id_t
, uint_t
,
114 char **, rcm_info_t
**);
115 static int aggr_undo_offline(rcm_handle_t
*, char *, id_t
, uint_t
,
116 char **, rcm_info_t
**);
117 static int aggr_remove(rcm_handle_t
*, char *, id_t
, uint_t
,
118 char **, rcm_info_t
**);
119 static int aggr_notify_event(rcm_handle_t
*, char *, id_t
, uint_t
,
120 char **, nvlist_t
*, rcm_info_t
**);
121 static int aggr_configure_all(rcm_handle_t
*, datalink_id_t
,
124 /* Module private routines */
125 static int cache_update(rcm_handle_t
*);
126 static void cache_remove(link_cache_t
*);
127 static void cache_insert(link_cache_t
*);
128 static void node_free(link_cache_t
*);
129 static void aggr_list_remove(dl_aggr_t
*);
130 static void aggr_list_insert(dl_aggr_t
*);
131 static void aggr_list_free();
132 static link_cache_t
*cache_lookup(rcm_handle_t
*, char *, char);
133 static int aggr_consumer_offline(rcm_handle_t
*, link_cache_t
*,
134 char **, uint_t
, rcm_info_t
**);
135 static int aggr_consumer_online(rcm_handle_t
*, link_cache_t
*,
136 char **, uint_t
, rcm_info_t
**);
137 static int aggr_offline_port(link_cache_t
*, cache_node_state_t
);
138 static int aggr_online_port(link_cache_t
*, boolean_t
*);
139 static char *aggr_usage(link_cache_t
*);
140 static void aggr_log_err(datalink_id_t
, char **, char *);
141 static int aggr_consumer_notify(rcm_handle_t
*, datalink_id_t
,
142 char **, uint_t
, rcm_info_t
**);
144 /* Module-Private data */
145 static struct rcm_mod_ops aggr_ops
=
162 * rcm_mod_init() - Update registrations, and return the ops structure.
167 dladm_status_t status
;
168 char errmsg
[DLADM_STRSIZE
];
170 rcm_log_message(RCM_TRACE1
, "AGGR: mod_init\n");
172 cache_head
.vc_next
= &cache_tail
;
173 cache_head
.vc_prev
= NULL
;
174 cache_tail
.vc_prev
= &cache_head
;
175 cache_tail
.vc_next
= NULL
;
176 (void) mutex_init(&cache_lock
, 0, NULL
);
177 aggr_head
.da_next
= &aggr_tail
;
178 aggr_head
.da_prev
= NULL
;
179 aggr_tail
.da_prev
= &aggr_head
;
180 aggr_tail
.da_next
= NULL
;
181 (void) mutex_init(&aggr_list_lock
, 0, NULL
);
183 if ((status
= dladm_open(&dld_handle
)) != DLADM_STATUS_OK
) {
184 rcm_log_message(RCM_WARNING
,
185 "AGGR: mod_init failed: cannot open datalink handle: %s\n",
186 dladm_status2str(status
, errmsg
));
190 /* Return the ops vectors */
195 * rcm_mod_info() - Return a string describing this module.
200 rcm_log_message(RCM_TRACE1
, "AGGR: mod_info\n");
202 return ("AGGR module version 1.1");
206 * rcm_mod_fini() - Destroy the network AGGR cache.
213 rcm_log_message(RCM_TRACE1
, "AGGR: mod_fini\n");
216 * Note that aggr_unregister() does not seem to be called anywhere,
217 * therefore we free the cache nodes here. In theory we should call
218 * rcm_register_interest() for each node before we free it, the
219 * framework does not provide the rcm_handle to allow us to do so.
221 (void) mutex_lock(&cache_lock
);
222 node
= cache_head
.vc_next
;
223 while (node
!= &cache_tail
) {
226 node
= cache_head
.vc_next
;
228 (void) mutex_unlock(&cache_lock
);
229 (void) mutex_destroy(&cache_lock
);
232 (void) mutex_destroy(&aggr_list_lock
);
234 dladm_close(dld_handle
);
235 return (RCM_SUCCESS
);
239 * aggr_list_insert - Insert an aggr in the global aggr list
242 aggr_list_insert(dl_aggr_t
*aggr
)
244 assert(MUTEX_HELD(&aggr_list_lock
));
246 /* insert at the head for best performance */
247 aggr
->da_next
= aggr_head
.da_next
;
248 aggr
->da_prev
= &aggr_head
;
250 aggr
->da_next
->da_prev
= aggr
;
251 aggr
->da_prev
->da_next
= aggr
;
255 * aggr_list_remove - Remove an aggr from the global aggr list
258 aggr_list_remove(dl_aggr_t
*aggr
)
260 assert(MUTEX_HELD(&aggr_list_lock
));
261 aggr
->da_next
->da_prev
= aggr
->da_prev
;
262 aggr
->da_prev
->da_next
= aggr
->da_next
;
263 aggr
->da_next
= NULL
;
264 aggr
->da_prev
= NULL
;
272 (void) mutex_lock(&aggr_list_lock
);
273 aggr
= aggr_head
.da_next
;
274 while (aggr
!= &aggr_tail
) {
275 aggr_list_remove(aggr
);
277 aggr
= aggr_head
.da_next
;
279 (void) mutex_unlock(&aggr_list_lock
);
283 * aggr_register() - Make sure the cache is properly sync'ed, and its
284 * registrations are in order.
287 aggr_register(rcm_handle_t
*hd
)
289 rcm_log_message(RCM_TRACE1
, "AGGR: register\n");
291 if (cache_update(hd
) < 0)
292 return (RCM_FAILURE
);
295 * Need to register interest in all new resources
296 * getting attached, so we get attach event notifications
298 if (!events_registered
) {
299 if (rcm_register_event(hd
, RCM_RESOURCE_LINK_NEW
, 0, NULL
)
301 rcm_log_message(RCM_ERROR
,
302 _("AGGR: failed to register %s\n"),
303 RCM_RESOURCE_LINK_NEW
);
304 return (RCM_FAILURE
);
306 rcm_log_message(RCM_DEBUG
, "AGGR: registered %s\n",
307 RCM_RESOURCE_LINK_NEW
);
312 return (RCM_SUCCESS
);
316 * aggr_unregister() - Walk the cache, unregistering all the networks.
319 aggr_unregister(rcm_handle_t
*hd
)
323 rcm_log_message(RCM_TRACE1
, "AGGR: unregister\n");
325 /* Walk the cache, unregistering everything */
326 (void) mutex_lock(&cache_lock
);
327 node
= cache_head
.vc_next
;
328 while (node
!= &cache_tail
) {
329 if (rcm_unregister_interest(hd
, node
->vc_resource
, 0)
331 /* unregister failed for whatever reason */
332 rcm_log_message(RCM_ERROR
,
333 _("AGGR: failed to unregister %s\n"),
335 (void) mutex_unlock(&cache_lock
);
336 return (RCM_FAILURE
);
340 node
= cache_head
.vc_next
;
342 (void) mutex_unlock(&cache_lock
);
347 * Unregister interest in all new resources
349 if (events_registered
) {
350 if (rcm_unregister_event(hd
, RCM_RESOURCE_LINK_NEW
, 0)
352 rcm_log_message(RCM_ERROR
,
353 _("AGGR: failed to unregister %s\n"),
354 RCM_RESOURCE_LINK_NEW
);
355 return (RCM_FAILURE
);
357 rcm_log_message(RCM_DEBUG
, "AGGR: unregistered %s\n",
358 RCM_RESOURCE_LINK_NEW
);
363 return (RCM_SUCCESS
);
367 * aggr_offline() - Offline AGGRs on a specific link.
370 aggr_offline(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flags
,
371 char **errorp
, rcm_info_t
**depend_info
)
375 rcm_log_message(RCM_TRACE1
, "AGGR: offline(%s)\n", rsrc
);
377 /* Lock the cache and lookup the resource */
378 (void) mutex_lock(&cache_lock
);
379 node
= cache_lookup(hd
, rsrc
, CACHE_REFRESH
);
381 /* should not happen because the resource is registered. */
382 aggr_log_err(DATALINK_INVALID_LINKID
, errorp
,
383 "offline, unrecognized resource");
384 (void) mutex_unlock(&cache_lock
);
385 return (RCM_SUCCESS
);
389 * If this given link is the only port in the aggregation, inform
390 * VLANs and IP interfaces on associated AGGRs to be offlined
392 if (node
->vc_aggr
->da_lastport
== node
->vc_linkid
) {
393 if (aggr_consumer_offline(hd
, node
, errorp
, flags
,
394 depend_info
) == RCM_SUCCESS
) {
395 rcm_log_message(RCM_DEBUG
,
396 "AGGR: consumers agreed on offline\n");
398 aggr_log_err(node
->vc_linkid
, errorp
,
399 "consumers offline failed");
400 (void) mutex_unlock(&cache_lock
);
401 return (RCM_FAILURE
);
405 /* Check if it's a query */
406 if (flags
& RCM_QUERY
) {
407 rcm_log_message(RCM_TRACE1
,
408 "AGGR: offline query succeeded(%s)\n", rsrc
);
409 (void) mutex_unlock(&cache_lock
);
410 return (RCM_SUCCESS
);
413 if (aggr_offline_port(node
, CACHE_NODE_OFFLINED
) != RCM_SUCCESS
) {
414 aggr_log_err(node
->vc_linkid
, errorp
, "offline port failed");
415 (void) mutex_unlock(&cache_lock
);
416 return (RCM_FAILURE
);
419 rcm_log_message(RCM_TRACE1
, "AGGR: Offline succeeded(%s)\n", rsrc
);
420 (void) mutex_unlock(&cache_lock
);
421 return (RCM_SUCCESS
);
425 * aggr_undo_offline() - Undo offline of a previously offlined link.
429 aggr_undo_offline(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flags
,
430 char **errorp
, rcm_info_t
**depend_info
)
435 rcm_log_message(RCM_TRACE1
, "AGGR: online(%s)\n", rsrc
);
437 (void) mutex_lock(&cache_lock
);
438 node
= cache_lookup(hd
, rsrc
, CACHE_NO_REFRESH
);
440 aggr_log_err(DATALINK_INVALID_LINKID
, errorp
,
441 "undo offline, unrecognized resource");
442 (void) mutex_unlock(&cache_lock
);
444 return (RCM_FAILURE
);
447 /* Check if no attempt should be made to online the link here */
448 if (!(node
->vc_state
& CACHE_NODE_OFFLINED
)) {
449 aggr_log_err(node
->vc_linkid
, errorp
, "resource not offlined");
450 (void) mutex_unlock(&cache_lock
);
452 return (RCM_SUCCESS
);
455 if (aggr_online_port(node
, &up
) != RCM_SUCCESS
) {
456 aggr_log_err(node
->vc_linkid
, errorp
, "online failed");
457 (void) mutex_unlock(&cache_lock
);
458 return (RCM_FAILURE
);
462 * Inform VLANs and IP interfaces on associated AGGRs to be online
467 if (aggr_consumer_online(hd
, node
, errorp
, flags
, depend_info
) ==
469 rcm_log_message(RCM_DEBUG
, "AGGR: Consumers agree on online");
471 rcm_log_message(RCM_WARNING
,
472 _("AGGR: Consumers online failed (%s)\n"), rsrc
);
476 node
->vc_state
&= ~CACHE_NODE_OFFLINED
;
477 rcm_log_message(RCM_TRACE1
, "AGGR: online succeeded(%s)\n", rsrc
);
478 (void) mutex_unlock(&cache_lock
);
479 return (RCM_SUCCESS
);
483 aggr_offline_port(link_cache_t
*node
, cache_node_state_t state
)
486 dladm_status_t status
;
487 char errmsg
[DLADM_STRSIZE
];
488 dladm_aggr_port_attr_db_t port
;
490 rcm_log_message(RCM_TRACE2
, "AGGR: aggr_offline_port %s\n",
493 aggr
= node
->vc_aggr
;
496 * Try to remove the given port from the AGGR or delete the AGGR
498 if (aggr
->da_lastport
== node
->vc_linkid
) {
499 rcm_log_message(RCM_TRACE2
, "AGGR: delete aggregation %u\n",
501 status
= dladm_aggr_delete(dld_handle
, aggr
->da_aggrid
,
504 rcm_log_message(RCM_TRACE2
,
505 "AGGR: remove port (%s) from aggregation %u\n",
506 node
->vc_resource
, aggr
->da_aggrid
);
507 port
.lp_linkid
= node
->vc_linkid
;
508 status
= dladm_aggr_remove(dld_handle
, aggr
->da_aggrid
, 1,
509 &port
, DLADM_OPT_ACTIVE
);
511 if (status
!= DLADM_STATUS_OK
) {
512 rcm_log_message(RCM_WARNING
,
513 _("AGGR: AGGR offline port failed (%u): %s\n"),
514 aggr
->da_aggrid
, dladm_status2str(status
, errmsg
));
515 return (RCM_FAILURE
);
517 rcm_log_message(RCM_TRACE1
,
518 "AGGR: AGGR offline port succeeded (%u)\n",
520 node
->vc_state
|= (CACHE_AGGR_PORT_OFFLINED
| state
);
521 return (RCM_SUCCESS
);
526 aggr_online_port(link_cache_t
*node
, boolean_t
*up
)
529 dladm_status_t status
;
530 char errmsg
[DLADM_STRSIZE
];
531 dladm_aggr_port_attr_db_t port
;
533 rcm_log_message(RCM_TRACE2
, "AGGR: aggr_online_port %s\n",
537 if (!(node
->vc_state
& CACHE_AGGR_PORT_OFFLINED
))
538 return (RCM_SUCCESS
);
541 * Either add the port into the AGGR or recreate specific AGGR
542 * depending on whether this link is the only port in the aggregation.
544 aggr
= node
->vc_aggr
;
545 if (aggr
->da_lastport
== node
->vc_linkid
) {
546 rcm_log_message(RCM_TRACE2
, "AGGR: delete aggregation %u\n",
548 status
= dladm_aggr_up(dld_handle
, aggr
->da_aggrid
);
551 rcm_log_message(RCM_TRACE2
,
552 "AGGR: add port (%s) to aggregation %u\n",
553 node
->vc_resource
, aggr
->da_aggrid
);
554 port
.lp_linkid
= node
->vc_linkid
;
555 status
= dladm_aggr_add(dld_handle
, aggr
->da_aggrid
, 1, &port
,
558 if (status
!= DLADM_STATUS_OK
) {
559 rcm_log_message(RCM_WARNING
,
560 _("AGGR: AGGR online failed (%u): %s\n"),
561 aggr
->da_aggrid
, dladm_status2str(status
, errmsg
));
563 return (RCM_FAILURE
);
565 node
->vc_state
&= ~CACHE_AGGR_PORT_OFFLINED
;
566 return (RCM_SUCCESS
);
570 * aggr_get_info() - Gather usage information for this resource.
574 aggr_get_info(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flags
,
575 char **usagep
, char **errorp
, nvlist_t
*props
, rcm_info_t
**depend_info
)
579 rcm_log_message(RCM_TRACE1
, "AGGR: get_info(%s)\n", rsrc
);
581 (void) mutex_lock(&cache_lock
);
582 node
= cache_lookup(hd
, rsrc
, CACHE_REFRESH
);
584 rcm_log_message(RCM_INFO
,
585 _("AGGR: get_info(%s) unrecognized resource\n"), rsrc
);
586 (void) mutex_unlock(&cache_lock
);
588 return (RCM_FAILURE
);
592 * *usagep will be freed by the caller.
594 *usagep
= aggr_usage(node
);
595 (void) mutex_unlock(&cache_lock
);
597 if (*usagep
== NULL
) {
598 /* most likely malloc failure */
599 rcm_log_message(RCM_ERROR
,
600 _("AGGR: get_info(%s) malloc failure\n"), rsrc
);
601 (void) mutex_unlock(&cache_lock
);
603 return (RCM_FAILURE
);
606 /* Set client/role properties */
607 (void) nvlist_add_string(props
, RCM_CLIENT_NAME
, "AGGR");
608 rcm_log_message(RCM_TRACE1
, "AGGR: get_info(%s) info = %s\n",
610 return (RCM_SUCCESS
);
614 * aggr_suspend() - Nothing to do, always okay
618 aggr_suspend(rcm_handle_t
*hd
, char *rsrc
, id_t id
, timespec_t
*interval
,
619 uint_t flags
, char **errorp
, rcm_info_t
**depend_info
)
621 rcm_log_message(RCM_TRACE1
, "AGGR: suspend(%s)\n", rsrc
);
622 return (RCM_SUCCESS
);
626 * aggr_resume() - Nothing to do, always okay
630 aggr_resume(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flags
,
631 char **errorp
, rcm_info_t
**depend_info
)
633 rcm_log_message(RCM_TRACE1
, "AGGR: resume(%s)\n", rsrc
);
634 return (RCM_SUCCESS
);
638 * aggr_remove() - remove a resource from cache
642 aggr_remove(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flags
,
643 char **errorp
, rcm_info_t
**depend_info
)
648 int rv
= RCM_SUCCESS
;
650 rcm_log_message(RCM_TRACE1
, "AGGR: remove(%s)\n", rsrc
);
652 (void) mutex_lock(&cache_lock
);
653 node
= cache_lookup(hd
, rsrc
, CACHE_NO_REFRESH
);
655 rcm_log_message(RCM_INFO
,
656 _("AGGR: remove(%s) unrecognized resource\n"), rsrc
);
657 (void) mutex_unlock(&cache_lock
);
659 return (RCM_FAILURE
);
662 /* remove the cached entry for the resource */
664 (void) mutex_unlock(&cache_lock
);
667 * If this link is not the only port in the associated aggregation,
668 * the CACHE_AGGR_CONSUMER_OFFLINED flags won't be set.
670 if (node
->vc_state
& CACHE_AGGR_CONSUMER_OFFLINED
) {
671 aggr
= node
->vc_aggr
;
672 exported
= alloca(RCM_LINK_RESOURCE_MAX
);
673 (void) snprintf(exported
, RCM_LINK_RESOURCE_MAX
, "%s/%u",
674 RCM_LINK_PREFIX
, aggr
->da_aggrid
);
675 rv
= rcm_notify_remove(hd
, exported
, flags
, depend_info
);
676 if (rv
!= RCM_SUCCESS
) {
677 rcm_log_message(RCM_WARNING
,
678 _("AGGR: failed to notify remove dependent %s\n"),
688 * aggr_notify_event - Project private implementation to receive new resource
689 * events. It intercepts all new resource events. If the
690 * new resource is a network resource, pass up a notify
691 * for it too. The new resource need not be cached, since
692 * it is done at register again.
696 aggr_notify_event(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flags
,
697 char **errorp
, nvlist_t
*nvl
, rcm_info_t
**depend_info
)
699 nvpair_t
*nvp
= NULL
;
700 datalink_id_t linkid
;
703 int rv
= RCM_SUCCESS
;
705 rcm_log_message(RCM_TRACE1
, "AGGR: notify_event(%s)\n", rsrc
);
707 if (strcmp(rsrc
, RCM_RESOURCE_LINK_NEW
) != 0) {
708 aggr_log_err(DATALINK_INVALID_LINKID
, errorp
,
709 "unrecognized event");
711 return (RCM_FAILURE
);
714 /* Update cache to reflect latest AGGRs */
715 if (cache_update(hd
) < 0) {
716 aggr_log_err(DATALINK_INVALID_LINKID
, errorp
,
717 "private Cache update failed");
718 return (RCM_FAILURE
);
721 /* Process the nvlist for the event */
722 rcm_log_message(RCM_TRACE1
, "AGGR: process_nvlist\n");
723 while ((nvp
= nvlist_next_nvpair(nvl
, nvp
)) != NULL
) {
725 if (strcmp(nvpair_name(nvp
), RCM_NV_LINKID
) != 0)
728 if (nvpair_value_uint64(nvp
, &id64
) != 0) {
729 aggr_log_err(DATALINK_INVALID_LINKID
, errorp
,
730 "cannot get linkid");
731 return (RCM_FAILURE
);
734 linkid
= (datalink_id_t
)id64
;
735 if (aggr_configure_all(hd
, linkid
, &up
) != 0) {
736 aggr_log_err(linkid
, errorp
,
737 "failed configuring AGGR links");
741 /* Notify all VLAN and IP AGGR consumers */
742 if (up
&& aggr_consumer_notify(hd
, linkid
, errorp
, flags
,
744 aggr_log_err(linkid
, errorp
, "consumer notify failed");
749 rcm_log_message(RCM_TRACE1
,
750 "AGGR: notify_event: link configuration complete\n");
755 * aggr_usage - Determine the usage of a link.
756 * The returned buffer is owned by caller, and the caller
757 * must free it up when done.
760 aggr_usage(link_cache_t
*node
)
764 char errmsg
[DLADM_STRSIZE
];
765 char name
[MAXLINKNAMELEN
];
766 dladm_status_t status
;
769 rcm_log_message(RCM_TRACE2
, "AGGR: usage(%s)\n", node
->vc_resource
);
770 assert(MUTEX_HELD(&cache_lock
));
772 if (node
->vc_state
& CACHE_NODE_OFFLINED
)
773 fmt
= _("%s offlined");
775 fmt
= _("%s is part of AGGR ");
777 if ((status
= dladm_datalink_id2info(dld_handle
, node
->vc_linkid
, NULL
,
778 NULL
, NULL
, name
, sizeof (name
))) != DLADM_STATUS_OK
) {
779 rcm_log_message(RCM_ERROR
,
780 _("AGGR: usage(%s) get port name failure(%s)\n"),
781 node
->vc_resource
, dladm_status2str(status
, errmsg
));
785 /* space for resources and message */
786 bufsz
= MAXLINKNAMELEN
+ strlen(fmt
) + strlen(name
) + 1;
787 if ((buf
= malloc(bufsz
)) == NULL
) {
788 rcm_log_message(RCM_ERROR
,
789 _("AGGR: usage(%s) malloc failure(%s)\n"),
790 node
->vc_resource
, strerror(errno
));
793 (void) snprintf(buf
, bufsz
, fmt
, name
);
795 if (node
->vc_state
& CACHE_NODE_OFFLINED
) {
796 /* Nothing else to do */
797 rcm_log_message(RCM_TRACE2
, "AGGR: usage (%s) info = %s\n",
798 node
->vc_resource
, buf
);
802 if ((status
= dladm_datalink_id2info(dld_handle
,
803 node
->vc_aggr
->da_aggrid
, NULL
, NULL
, NULL
, name
,
804 sizeof (name
))) != DLADM_STATUS_OK
) {
805 rcm_log_message(RCM_ERROR
,
806 _("AGGR: usage(%s) get aggr %u name failure(%s)\n"),
807 node
->vc_resource
, node
->vc_aggr
->da_aggrid
,
808 dladm_status2str(status
, errmsg
));
813 (void) strlcat(buf
, name
, bufsz
);
815 rcm_log_message(RCM_TRACE2
, "AGGR: usage (%s) info = %s\n",
816 node
->vc_resource
, buf
);
821 * Cache management routines, all cache management functions should be
822 * be called with cache_lock held.
826 * cache_lookup() - Get a cache node for a resource.
827 * Call with cache lock held.
829 * This ensures that the cache is consistent with the system state and
830 * returns a pointer to the cache element corresponding to the resource.
832 static link_cache_t
*
833 cache_lookup(rcm_handle_t
*hd
, char *rsrc
, char options
)
837 rcm_log_message(RCM_TRACE2
, "AGGR: cache lookup(%s)\n", rsrc
);
838 assert(MUTEX_HELD(&cache_lock
));
840 if (options
& CACHE_REFRESH
) {
841 /* drop lock since update locks cache again */
842 (void) mutex_unlock(&cache_lock
);
843 (void) cache_update(hd
);
844 (void) mutex_lock(&cache_lock
);
847 node
= cache_head
.vc_next
;
848 for (; node
!= &cache_tail
; node
= node
->vc_next
) {
849 if (strcmp(rsrc
, node
->vc_resource
) == 0) {
850 rcm_log_message(RCM_TRACE2
,
851 "AGGR: cache lookup succeeded(%s)\n", rsrc
);
859 * node_free - Free a node from the cache
862 node_free(link_cache_t
*node
)
864 free(node
->vc_resource
);
869 * cache_insert - Insert a resource node in cache
872 cache_insert(link_cache_t
*node
)
874 assert(MUTEX_HELD(&cache_lock
));
876 /* insert at the head for best performance */
877 node
->vc_next
= cache_head
.vc_next
;
878 node
->vc_prev
= &cache_head
;
880 node
->vc_next
->vc_prev
= node
;
881 node
->vc_prev
->vc_next
= node
;
885 * cache_remove() - Remove a resource node from cache.
886 * Call with the cache_lock held.
889 cache_remove(link_cache_t
*node
)
891 assert(MUTEX_HELD(&cache_lock
));
892 node
->vc_next
->vc_prev
= node
->vc_prev
;
893 node
->vc_prev
->vc_next
= node
->vc_next
;
894 node
->vc_next
= NULL
;
895 node
->vc_prev
= NULL
;
899 aggr_port_update(rcm_handle_t
*hd
, dl_aggr_t
*aggr
, datalink_id_t portid
)
905 rcm_log_message(RCM_TRACE1
,
906 "AGGR: aggr_port_update aggr:%u port:%u\n",
907 aggr
->da_aggrid
, portid
);
908 assert(MUTEX_HELD(&cache_lock
));
910 rsrc
= malloc(RCM_LINK_RESOURCE_MAX
);
912 rcm_log_message(RCM_ERROR
,
913 _("AGGR: resource malloc error(%s)\n"), strerror(errno
));
917 (void) snprintf(rsrc
, RCM_LINK_RESOURCE_MAX
, "%s/%u",
918 RCM_LINK_PREFIX
, portid
);
920 node
= cache_lookup(hd
, rsrc
, CACHE_NO_REFRESH
);
922 rcm_log_message(RCM_DEBUG
,
923 "AGGR: %s already registered (aggrid:%u)\n",
924 rsrc
, aggr
->da_aggrid
);
927 node
->vc_state
&= ~CACHE_NODE_STALE
;
929 assert(node
->vc_linkid
== portid
);
931 * Update vc_aggr directly as only one aggregation can be
932 * created on one port.
934 node
->vc_aggr
= aggr
;
936 rcm_log_message(RCM_DEBUG
,
937 "AGGR: %s is a new resource (aggrid:%u)\n",
938 rsrc
, aggr
->da_aggrid
);
940 node
= calloc(1, sizeof (link_cache_t
));
943 rcm_log_message(RCM_ERROR
,
944 _("AGGR: calloc: %s\n"), strerror(errno
));
948 node
->vc_resource
= rsrc
;
949 node
->vc_aggr
= aggr
;
950 node
->vc_linkid
= portid
;
951 node
->vc_state
|= CACHE_NODE_NEW
;
962 typedef struct aggr_update_arg_s
{
968 * aggr_update() - Update physical interface properties
971 aggr_update(dladm_handle_t handle
, datalink_id_t aggrid
, void *arg
)
973 aggr_update_arg_t
*aggr_update_argp
= arg
;
974 rcm_handle_t
*hd
= aggr_update_argp
->hd
;
975 dladm_aggr_grp_attr_t aggr_attr
;
977 dladm_status_t status
;
978 char errmsg
[DLADM_STRSIZE
];
979 boolean_t exist
= B_FALSE
;
983 rcm_log_message(RCM_TRACE1
, "AGGR: aggr_update(%u)\n", aggrid
);
985 assert(MUTEX_HELD(&aggr_list_lock
));
986 status
= dladm_aggr_info(handle
, aggrid
, &aggr_attr
,
988 if (status
!= DLADM_STATUS_OK
) {
989 rcm_log_message(RCM_TRACE1
,
990 "AGGR: cannot get aggr information for %u error(%s)\n",
991 aggrid
, dladm_status2str(status
, errmsg
));
992 return (DLADM_WALK_CONTINUE
);
996 * Try to find the aggr from the aggr list.
998 for (aggr
= aggr_head
.da_next
; aggr
!= &aggr_tail
; aggr
= aggr
->da_next
)
999 if (aggr
->da_aggrid
== aggr_attr
.lg_linkid
)
1005 if ((aggr
= calloc(1, sizeof (dl_aggr_t
))) == NULL
) {
1006 rcm_log_message(RCM_ERROR
, _("AGGR: malloc: %s\n"),
1012 /* Update aggregation information. */
1013 if (aggr_attr
.lg_nports
== 1)
1014 aggr
->da_lastport
= aggr_attr
.lg_ports
[0].lp_linkid
;
1016 aggr
->da_lastport
= DATALINK_INVALID_LINKID
;
1017 aggr
->da_aggrid
= aggr_attr
.lg_linkid
;
1019 for (i
= 0; i
< aggr_attr
.lg_nports
; i
++) {
1020 datalink_id_t portid
= (aggr_attr
.lg_ports
[i
]).lp_linkid
;
1022 if (aggr_port_update(hd
, aggr
, portid
) != 0)
1027 aggr_list_insert(aggr
);
1029 aggr
->da_stale
= B_FALSE
;
1030 rcm_log_message(RCM_TRACE3
,
1031 "AGGR: aggr_update: succeeded(%u)\n", aggrid
);
1035 if (!exist
&& ret
!= 0)
1037 free(aggr_attr
.lg_ports
);
1038 aggr_update_argp
->retval
= ret
;
1039 return (ret
== 0 ? DLADM_WALK_CONTINUE
: DLADM_WALK_TERMINATE
);
1043 * aggr_update_all() - Determine all AGGR links in the system
1046 aggr_update_all(rcm_handle_t
*hd
)
1048 aggr_update_arg_t arg
= {NULL
, 0};
1050 rcm_log_message(RCM_TRACE2
, "AGGR: aggr_update_all\n");
1051 assert(MUTEX_HELD(&cache_lock
));
1054 (void) dladm_walk_datalink_id(aggr_update
, dld_handle
, &arg
,
1055 DATALINK_CLASS_AGGR
, DATALINK_ANY_MEDIATYPE
, DLADM_OPT_ACTIVE
);
1056 return (arg
.retval
);
1060 * cache_update() - Update cache with latest interface info
1063 cache_update(rcm_handle_t
*hd
)
1065 link_cache_t
*node
, *next
;
1069 rcm_log_message(RCM_TRACE2
, "AGGR: cache_update\n");
1070 (void) mutex_lock(&aggr_list_lock
);
1071 (void) mutex_lock(&cache_lock
);
1073 /* first we walk the entire aggr list, marking each entry stale */
1074 for (aggr
= aggr_head
.da_next
; aggr
!= &aggr_tail
; aggr
= aggr
->da_next
)
1075 aggr
->da_stale
= B_TRUE
;
1077 /* then we walk the entire cache, marking each entry stale */
1078 node
= cache_head
.vc_next
;
1079 for (; node
!= &cache_tail
; node
= node
->vc_next
)
1080 node
->vc_state
|= CACHE_NODE_STALE
;
1082 ret
= aggr_update_all(hd
);
1085 * Even aggr_update_all() fails, continue to delete all the stale
1086 * resources. First, unregister links that are not offlined and
1089 for (node
= cache_head
.vc_next
; node
!= &cache_tail
; node
= next
) {
1091 next
= node
->vc_next
;
1092 if (node
->vc_state
& CACHE_NODE_STALE
) {
1093 (void) rcm_unregister_interest(hd
, node
->vc_resource
,
1095 rcm_log_message(RCM_DEBUG
,
1096 "AGGR: unregistered %s\n", node
->vc_resource
);
1102 if (!(node
->vc_state
& CACHE_NODE_NEW
))
1105 if (rcm_register_interest(hd
, node
->vc_resource
, 0,
1107 NULL
) != RCM_SUCCESS
) {
1108 rcm_log_message(RCM_ERROR
,
1109 _("AGGR: failed to register %s\n"),
1113 rcm_log_message(RCM_DEBUG
, "AGGR: registered %s\n",
1116 node
->vc_state
&= ~CACHE_NODE_NEW
;
1120 aggr
= aggr_head
.da_next
;
1121 while (aggr
!= &aggr_tail
) {
1122 dl_aggr_t
*next
= aggr
->da_next
;
1124 /* delete stale AGGRs */
1125 if (aggr
->da_stale
) {
1126 aggr_list_remove(aggr
);
1133 (void) mutex_unlock(&cache_lock
);
1134 (void) mutex_unlock(&aggr_list_lock
);
1139 * aggr_log_err() - RCM error log wrapper
1142 aggr_log_err(datalink_id_t linkid
, char **errorp
, char *errmsg
)
1144 char link
[MAXLINKNAMELEN
];
1145 char errstr
[DLADM_STRSIZE
];
1146 dladm_status_t status
;
1152 if (linkid
!= DATALINK_INVALID_LINKID
) {
1153 char rsrc
[RCM_LINK_RESOURCE_MAX
];
1155 (void) snprintf(rsrc
, RCM_LINK_RESOURCE_MAX
, "%s/%u",
1156 RCM_LINK_PREFIX
, linkid
);
1158 rcm_log_message(RCM_ERROR
, _("AGGR: %s(%s)\n"), errmsg
, rsrc
);
1160 if ((status
= dladm_datalink_id2info(dld_handle
, linkid
, NULL
,
1161 NULL
, NULL
, link
, sizeof (link
))) != DLADM_STATUS_OK
) {
1162 rcm_log_message(RCM_WARNING
,
1163 _("AGGR: cannot get link name of (%s) %s\n"),
1164 rsrc
, dladm_status2str(status
, errstr
));
1167 rcm_log_message(RCM_ERROR
, _("AGGR: %s\n"), errmsg
);
1170 errfmt
= strlen(link
) > 0 ? _("AGGR: %s(%s)") : _("AGGR: %s");
1171 len
= strlen(errfmt
) + strlen(errmsg
) + MAXLINKNAMELEN
+ 1;
1172 if ((error
= malloc(len
)) != NULL
) {
1173 if (strlen(link
) > 0)
1174 (void) sprintf(error
, errfmt
, errmsg
, link
);
1176 (void) sprintf(error
, errfmt
, errmsg
);
1184 * aggr_consumer_offline()
1186 * Offline AGGR consumers.
1189 aggr_consumer_offline(rcm_handle_t
*hd
, link_cache_t
*node
, char **errorp
,
1190 uint_t flags
, rcm_info_t
**depend_info
)
1192 char rsrc
[RCM_LINK_RESOURCE_MAX
];
1195 rcm_log_message(RCM_TRACE2
, "AGGR: aggr_consumer_offline %s\n",
1198 (void) snprintf(rsrc
, RCM_LINK_RESOURCE_MAX
, "%s/%u",
1199 RCM_LINK_PREFIX
, node
->vc_aggr
->da_aggrid
);
1202 * Inform associated VLANs and IP interfaces to be offlined
1204 ret
= rcm_request_offline(hd
, rsrc
, flags
, depend_info
);
1205 if (ret
!= RCM_SUCCESS
) {
1206 rcm_log_message(RCM_DEBUG
,
1207 "AGGR: rcm_request_offline failed (%s)\n", rsrc
);
1211 node
->vc_state
|= CACHE_AGGR_CONSUMER_OFFLINED
;
1212 rcm_log_message(RCM_TRACE2
, "AGGR: aggr_consumer_offline done\n");
1217 * aggr_consumer_online()
1219 * online AGGR consumers.
1222 aggr_consumer_online(rcm_handle_t
*hd
, link_cache_t
*node
, char **errorp
,
1223 uint_t flags
, rcm_info_t
**depend_info
)
1225 char rsrc
[RCM_LINK_RESOURCE_MAX
];
1228 rcm_log_message(RCM_TRACE2
, "AGGR: aggr_consumer_online %s\n",
1231 if (!(node
->vc_state
& CACHE_AGGR_CONSUMER_OFFLINED
)) {
1232 rcm_log_message(RCM_DEBUG
,
1233 "AGGR: no consumers offlined (%s)\n", node
->vc_resource
);
1234 return (RCM_SUCCESS
);
1237 (void) snprintf(rsrc
, RCM_LINK_RESOURCE_MAX
, "%s/%u",
1238 RCM_LINK_PREFIX
, node
->vc_aggr
->da_aggrid
);
1240 ret
= rcm_notify_online(hd
, rsrc
, flags
, depend_info
);
1241 if (ret
!= RCM_SUCCESS
) {
1242 rcm_log_message(RCM_DEBUG
,
1243 "AGGR: rcm_notify_online failed (%s)\n", rsrc
);
1247 node
->vc_state
&= ~CACHE_AGGR_CONSUMER_OFFLINED
;
1248 rcm_log_message(RCM_TRACE2
, "AGGR: aggr_consumer_online done\n");
1253 * Send RCM_RESOURCE_LINK_NEW events to other modules about new aggregations.
1254 * Return 0 on success, -1 on failure.
1257 aggr_notify_new_aggr(rcm_handle_t
*hd
, char *rsrc
)
1261 nvlist_t
*nvl
= NULL
;
1263 boolean_t is_only_port
;
1266 rcm_log_message(RCM_TRACE2
, "AGGR: aggr_notify_new_aggr (%s)\n", rsrc
);
1268 /* Check for the interface in the cache */
1269 (void) mutex_lock(&cache_lock
);
1270 if ((node
= cache_lookup(hd
, rsrc
, CACHE_REFRESH
)) == NULL
) {
1271 rcm_log_message(RCM_TRACE1
,
1272 "AGGR: aggr_notify_new_aggr() unrecognized resource (%s)\n",
1274 (void) mutex_unlock(&cache_lock
);
1278 if (nvlist_alloc(&nvl
, 0, 0) != 0) {
1279 rcm_log_message(RCM_WARNING
,
1280 _("AGGR: failed to allocate nvlist\n"));
1281 (void) mutex_unlock(&cache_lock
);
1285 aggr
= node
->vc_aggr
;
1286 is_only_port
= (aggr
->da_lastport
== node
->vc_linkid
);
1289 rcm_log_message(RCM_TRACE2
,
1290 "AGGR: aggr_notify_new_aggr add (%u)\n",
1293 id
= aggr
->da_aggrid
;
1294 if (nvlist_add_uint64(nvl
, RCM_NV_LINKID
, id
) != 0) {
1295 rcm_log_message(RCM_ERROR
,
1296 _("AGGR: failed to construct nvlist\n"));
1297 (void) mutex_unlock(&cache_lock
);
1302 (void) mutex_unlock(&cache_lock
);
1305 * If this link is not the only port in the aggregation, the aggregation
1306 * is not new. No need to inform other consumers in that case.
1308 if (is_only_port
&& rcm_notify_event(hd
, RCM_RESOURCE_LINK_NEW
,
1309 0, nvl
, NULL
) != RCM_SUCCESS
) {
1310 rcm_log_message(RCM_ERROR
,
1311 _("AGGR: failed to notify %s event for %s\n"),
1312 RCM_RESOURCE_LINK_NEW
, node
->vc_resource
);
1323 * aggr_consumer_notify() - Notify consumers of AGGRs coming back online.
1326 aggr_consumer_notify(rcm_handle_t
*hd
, datalink_id_t linkid
, char **errorp
,
1327 uint_t flags
, rcm_info_t
**depend_info
)
1329 char rsrc
[RCM_LINK_RESOURCE_MAX
];
1332 (void) snprintf(rsrc
, RCM_LINK_RESOURCE_MAX
, "%s/%u",
1333 RCM_LINK_PREFIX
, linkid
);
1335 rcm_log_message(RCM_TRACE1
, "AGGR: aggr_consumer_notify(%s)\n", rsrc
);
1338 * Inform IP and VLAN consumers to be online.
1340 if (aggr_notify_new_aggr(hd
, rsrc
) != 0) {
1341 (void) mutex_lock(&cache_lock
);
1342 if ((node
= cache_lookup(hd
, rsrc
, CACHE_NO_REFRESH
)) != NULL
)
1343 (void) aggr_offline_port(node
, CACHE_NODE_STALE
);
1344 (void) mutex_unlock(&cache_lock
);
1345 rcm_log_message(RCM_TRACE1
,
1346 "AGGR: aggr_notify_new_aggr failed(%s)\n", rsrc
);
1350 rcm_log_message(RCM_TRACE2
, "AGGR: aggr_consumer_notify succeeded\n");
1354 typedef struct aggr_configure_arg
{
1355 datalink_id_t portid
;
1358 } aggr_configure_arg_t
;
1361 aggr_configure(dladm_handle_t handle
, datalink_id_t aggrid
, void *arg
)
1363 aggr_configure_arg_t
*aggr_configure_argp
= arg
;
1364 datalink_id_t portid
;
1365 dladm_aggr_grp_attr_t aggr_attr
;
1366 dladm_aggr_port_attr_db_t port_attr
;
1367 dladm_status_t status
;
1369 char errmsg
[DLADM_STRSIZE
];
1372 status
= dladm_datalink_id2info(handle
, aggrid
, &flags
, NULL
, NULL
,
1374 if (status
!= DLADM_STATUS_OK
)
1375 return (DLADM_WALK_CONTINUE
);
1377 status
= dladm_aggr_info(handle
, aggrid
, &aggr_attr
, DLADM_OPT_PERSIST
);
1378 if (status
!= DLADM_STATUS_OK
)
1379 return (DLADM_WALK_CONTINUE
);
1381 portid
= aggr_configure_argp
->portid
;
1382 for (i
= 0; i
< aggr_attr
.lg_nports
; i
++)
1383 if (aggr_attr
.lg_ports
[i
].lp_linkid
== portid
)
1386 if (i
== aggr_attr
.lg_nports
) {
1388 * The aggregation doesn't contain this port.
1390 free(aggr_attr
.lg_ports
);
1391 return (DLADM_WALK_CONTINUE
);
1395 * If this aggregation already exists, add this port to this
1396 * aggregation, otherwise, bring up this aggregation.
1398 if (flags
& DLADM_OPT_ACTIVE
) {
1399 rcm_log_message(RCM_TRACE3
,
1400 "AGGR: aggr_configure dladm_aggr_add port %u (%u)\n",
1402 port_attr
.lp_linkid
= portid
;
1403 status
= dladm_aggr_add(handle
, aggrid
, 1, &port_attr
,
1406 rcm_log_message(RCM_TRACE3
,
1407 "AGGR: aggr_configure dladm_aggr_up (%u)\n", aggrid
);
1408 status
= dladm_aggr_up(handle
, aggrid
);
1411 if (status
!= DLADM_STATUS_OK
) {
1413 * Print a warning message and continue to UP other AGGRs.
1415 rcm_log_message(RCM_WARNING
,
1416 _("AGGR: AGGR online failed (%u): %s\n"),
1417 aggrid
, dladm_status2str(status
, errmsg
));
1418 aggr_configure_argp
->retval
= -1;
1419 } else if (!(flags
& DLADM_OPT_ACTIVE
)) {
1420 aggr_configure_argp
->up
= B_TRUE
;
1423 free(aggr_attr
.lg_ports
);
1424 return (DLADM_WALK_TERMINATE
);
1428 * aggr_configure_all() - Configure AGGRs over a physical link after it attaches
1431 aggr_configure_all(rcm_handle_t
*hd
, datalink_id_t linkid
, boolean_t
*up
)
1433 char rsrc
[RCM_LINK_RESOURCE_MAX
];
1435 aggr_configure_arg_t arg
= {DATALINK_INVALID_LINKID
, 0, B_FALSE
};
1439 /* Check for the AGGRs in the cache */
1440 (void) snprintf(rsrc
, sizeof (rsrc
), "%s/%u", RCM_LINK_PREFIX
, linkid
);
1442 rcm_log_message(RCM_TRACE1
, "AGGR: aggr_configure_all(%s)\n", rsrc
);
1444 /* Check if the link is new or was previously offlined */
1445 (void) mutex_lock(&cache_lock
);
1446 if (((node
= cache_lookup(hd
, rsrc
, CACHE_REFRESH
)) != NULL
) &&
1447 (!(node
->vc_state
& CACHE_NODE_OFFLINED
))) {
1448 rcm_log_message(RCM_TRACE1
,
1449 "AGGR: Skipping configured link(%s)\n", rsrc
);
1450 (void) mutex_unlock(&cache_lock
);
1453 (void) mutex_unlock(&cache_lock
);
1455 arg
.portid
= linkid
;
1456 (void) dladm_walk_datalink_id(aggr_configure
, dld_handle
, &arg
,
1457 DATALINK_CLASS_AGGR
, DATALINK_ANY_MEDIATYPE
, DLADM_OPT_PERSIST
);
1459 if (arg
.retval
== 0) {
1461 rcm_log_message(RCM_TRACE1
,
1462 "AGGR: aggr_configure_all succeeded(%s)\n", rsrc
);
1464 return (arg
.retval
);