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 */
59 typedef struct dl_aggr {
60 struct dl_aggr *da_next; /* next AGGR on the system */
61 struct dl_aggr *da_prev; /* prev AGGR on the system */
62 boolean_t da_stale; /* AGGR link is stale? */
63 datalink_id_t da_aggrid;
64 datalink_id_t da_lastport;
65 } dl_aggr_t;
66
67 /* AGGR Cache state flags */
68 typedef enum {
69 CACHE_NODE_STALE = 0x01, /* stale cached data */
70 CACHE_NODE_NEW = 0x02, /* new cached nodes */
71 CACHE_NODE_OFFLINED = 0x04, /* node offlined */
72 CACHE_AGGR_PORT_OFFLINED = 0x08, /* aggr port offlined */
73 CACHE_AGGR_CONSUMER_OFFLINED = 0x10 /* consumers offlined */
74 } cache_node_state_t;
75
76 /* Network Cache lookup options */
77 #define CACHE_NO_REFRESH 0x1 /* cache refresh not needed */
78 #define CACHE_REFRESH 0x2 /* refresh cache */
79
80 /*
81 * Cache element. It is used to keep a list of links on the system and
82 * their associated aggregations.
83 */
84 typedef struct link_cache {
85 struct link_cache *vc_next; /* next cached resource */
86 struct link_cache *vc_prev; /* prev cached resource */
87 char *vc_resource; /* resource name */
88 datalink_id_t vc_linkid; /* linkid */
89 dl_aggr_t *vc_aggr; /* AGGR on this link */
90 cache_node_state_t vc_state; /* cache state flags */
91 } link_cache_t;
92
93 /*
94 * Global cache for network AGGRs
95 */
96 static link_cache_t cache_head;
97 static link_cache_t cache_tail;
98 static mutex_t cache_lock;
99 static dl_aggr_t aggr_head;
100 static dl_aggr_t aggr_tail;
101 static mutex_t aggr_list_lock;
102 static int events_registered = 0;
103
104 static dladm_handle_t dld_handle = NULL;
105
106 /*
107 * RCM module interface prototypes
108 */
109 static int aggr_register(rcm_handle_t *);
110 static int aggr_unregister(rcm_handle_t *);
111 static int aggr_get_info(rcm_handle_t *, char *, id_t, uint_t,
112 char **, char **, nvlist_t *, rcm_info_t **);
113 static int aggr_suspend(rcm_handle_t *, char *, id_t,
114 timespec_t *, uint_t, char **, rcm_info_t **);
115 static int aggr_resume(rcm_handle_t *, char *, id_t, uint_t,
116 char **, rcm_info_t **);
117 static int aggr_offline(rcm_handle_t *, char *, id_t, uint_t,
118 char **, rcm_info_t **);
119 static int aggr_undo_offline(rcm_handle_t *, char *, id_t, uint_t,
120 char **, rcm_info_t **);
121 static int aggr_remove(rcm_handle_t *, char *, id_t, uint_t,
122 char **, rcm_info_t **);
123 static int aggr_notify_event(rcm_handle_t *, char *, id_t, uint_t,
124 char **, nvlist_t *, rcm_info_t **);
125 static int aggr_configure_all(rcm_handle_t *, datalink_id_t,
126 boolean_t *);
127
128 /* Module private routines */
129 static int cache_update(rcm_handle_t *);
130 static void cache_remove(link_cache_t *);
131 static void cache_insert(link_cache_t *);
132 static void node_free(link_cache_t *);
133 static void aggr_list_remove(dl_aggr_t *);
134 static void aggr_list_insert(dl_aggr_t *);
135 static void aggr_list_free();
136 static link_cache_t *cache_lookup(rcm_handle_t *, char *, char);
137 static int aggr_consumer_offline(rcm_handle_t *, link_cache_t *,
138 char **, uint_t, rcm_info_t **);
139 static int aggr_consumer_online(rcm_handle_t *, link_cache_t *,
140 char **, uint_t, rcm_info_t **);
141 static int aggr_offline_port(link_cache_t *, cache_node_state_t);
142 static int aggr_online_port(link_cache_t *, boolean_t *);
143 static char *aggr_usage(link_cache_t *);
144 static void aggr_log_err(datalink_id_t, char **, char *);
145 static int aggr_consumer_notify(rcm_handle_t *, datalink_id_t,
146 char **, uint_t, rcm_info_t **);
147
148 /* Module-Private data */
149 static struct rcm_mod_ops aggr_ops =
150 {
151 RCM_MOD_OPS_VERSION,
152 aggr_register,
153 aggr_unregister,
154 aggr_get_info,
155 aggr_suspend,
156 aggr_resume,
157 aggr_offline,
158 aggr_undo_offline,
159 aggr_remove,
160 NULL,
161 NULL,
162 aggr_notify_event
163 };
164
165 /*
166 * rcm_mod_init() - Update registrations, and return the ops structure.
167 */
168 struct rcm_mod_ops *
rcm_mod_init(void)169 rcm_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 */
201 const char *
rcm_mod_info(void)202 rcm_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 */
212 int
rcm_mod_fini(void)213 rcm_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 */
245 static void
aggr_list_insert(dl_aggr_t * aggr)246 aggr_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 */
261 static void
aggr_list_remove(dl_aggr_t * aggr)262 aggr_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
271 static void
aggr_list_free()272 aggr_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 */
290 static int
aggr_register(rcm_handle_t * hd)291 aggr_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 */
322 static int
aggr_unregister(rcm_handle_t * hd)323 aggr_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 */
373 static int
aggr_offline(rcm_handle_t * hd,char * rsrc,id_t id,uint_t flags,char ** errorp,rcm_info_t ** depend_info)374 aggr_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*/
432 static int
aggr_undo_offline(rcm_handle_t * hd,char * rsrc,id_t id,uint_t flags,char ** errorp,rcm_info_t ** depend_info)433 aggr_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
479 done:
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
486 static int
aggr_offline_port(link_cache_t * node,cache_node_state_t state)487 aggr_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
529 static int
aggr_online_port(link_cache_t * node,boolean_t * up)530 aggr_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*/
577 int
aggr_get_info(rcm_handle_t * hd,char * rsrc,id_t id,uint_t flags,char ** usagep,char ** errorp,nvlist_t * props,rcm_info_t ** depend_info)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)
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*/
621 static int
aggr_suspend(rcm_handle_t * hd,char * rsrc,id_t id,timespec_t * interval,uint_t flags,char ** errorp,rcm_info_t ** depend_info)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)
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*/
633 static int
aggr_resume(rcm_handle_t * hd,char * rsrc,id_t id,uint_t flags,char ** errorp,rcm_info_t ** depend_info)634 aggr_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*/
645 static int
aggr_remove(rcm_handle_t * hd,char * rsrc,id_t id,uint_t flags,char ** errorp,rcm_info_t ** depend_info)646 aggr_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*/
699 static int
aggr_notify_event(rcm_handle_t * hd,char * rsrc,id_t id,uint_t flags,char ** errorp,nvlist_t * nvl,rcm_info_t ** depend_info)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)
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 */
763 static char *
aggr_usage(link_cache_t * node)764 aggr_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 */
836 static link_cache_t *
cache_lookup(rcm_handle_t * hd,char * rsrc,char options)837 cache_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 */
865 static void
node_free(link_cache_t * node)866 node_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 */
875 static void
cache_insert(link_cache_t * node)876 cache_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 */
892 static void
cache_remove(link_cache_t * node)893 cache_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
902 static int
aggr_port_update(rcm_handle_t * hd,dl_aggr_t * aggr,datalink_id_t portid)903 aggr_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;
962 done:
963 return (ret);
964 }
965
966 typedef 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 */
974 static int
aggr_update(dladm_handle_t handle,datalink_id_t aggrid,void * arg)975 aggr_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;
1038 done:
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 */
1049 static int
aggr_update_all(rcm_handle_t * hd)1050 aggr_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 */
1066 static int
cache_update(rcm_handle_t * hd)1067 cache_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
1136 done:
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 */
1145 static void
aggr_log_err(datalink_id_t linkid,char ** errorp,char * errmsg)1146 aggr_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 */
1192 static int
aggr_consumer_offline(rcm_handle_t * hd,link_cache_t * node,char ** errorp,uint_t flags,rcm_info_t ** depend_info)1193 aggr_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 */
1225 static int
aggr_consumer_online(rcm_handle_t * hd,link_cache_t * node,char ** errorp,uint_t flags,rcm_info_t ** depend_info)1226 aggr_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 */
1260 static int
aggr_notify_new_aggr(rcm_handle_t * hd,char * rsrc)1261 aggr_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;
1321 done:
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 */
1330 static int
aggr_consumer_notify(rcm_handle_t * hd,datalink_id_t linkid,char ** errorp,uint_t flags,rcm_info_t ** depend_info)1331 aggr_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
1359 typedef struct aggr_configure_arg {
1360 datalink_id_t portid;
1361 int retval;
1362 boolean_t up;
1363 } aggr_configure_arg_t;
1364
1365 static int
aggr_configure(dladm_handle_t handle,datalink_id_t aggrid,void * arg)1366 aggr_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 */
1435 static int
aggr_configure_all(rcm_handle_t * hd,datalink_id_t linkid,boolean_t * up)1436 aggr_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