dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / rcm_daemon / common / vnic_rcm.c
blobcc29596297131c74bc7320b3149d808117a21fe2
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
26 * This RCM module adds support to the RCM framework for VNIC links
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <sys/types.h>
34 #include <synch.h>
35 #include <assert.h>
36 #include <strings.h>
37 #include "rcm_module.h"
38 #include <libintl.h>
39 #include <libdllink.h>
40 #include <libdlvnic.h>
41 #include <libdlpi.h>
44 * Definitions
46 #define _(x) gettext(x)
48 /* Some generic well-knowns and defaults used in this module */
49 #define RCM_LINK_PREFIX "SUNW_datalink" /* RCM datalink name prefix */
50 #define RCM_LINK_RESOURCE_MAX (13 + LINKID_STR_WIDTH)
52 /* VNIC link flags */
53 typedef enum {
54 VNIC_OFFLINED = 0x1,
55 VNIC_CONSUMER_OFFLINED = 0x2,
56 VNIC_STALE = 0x4
57 } vnic_flag_t;
59 /* link representation */
60 typedef struct dl_vnic {
61 struct dl_vnic *dlv_next; /* next VNIC on the same link */
62 struct dl_vnic *dlv_prev; /* prev VNIC on the same link */
63 datalink_id_t dlv_vnic_id;
64 vnic_flag_t dlv_flags; /* VNIC link flags */
65 } dl_vnic_t;
67 /* VNIC Cache state flags */
68 typedef enum {
69 CACHE_NODE_STALE = 0x1, /* stale cached data */
70 CACHE_NODE_NEW = 0x2, /* new cached nodes */
71 CACHE_NODE_OFFLINED = 0x4 /* nodes offlined */
72 } cache_node_state_t;
74 /* Network Cache lookup options */
75 #define CACHE_NO_REFRESH 0x1 /* cache refresh not needed */
76 #define CACHE_REFRESH 0x2 /* refresh cache */
78 /* Cache element */
79 typedef struct link_cache {
80 struct link_cache *vc_next; /* next cached resource */
81 struct link_cache *vc_prev; /* prev cached resource */
82 char *vc_resource; /* resource name */
83 datalink_id_t vc_linkid; /* linkid */
84 dl_vnic_t *vc_vnic; /* VNIC list on this link */
85 cache_node_state_t vc_state; /* cache state flags */
86 } link_cache_t;
89 * Global cache for network VNICs
91 static link_cache_t cache_head;
92 static link_cache_t cache_tail;
93 static mutex_t cache_lock;
94 static int events_registered = 0;
96 static dladm_handle_t dld_handle = NULL;
99 * RCM module interface prototypes
101 static int vnic_register(rcm_handle_t *);
102 static int vnic_unregister(rcm_handle_t *);
103 static int vnic_get_info(rcm_handle_t *, char *, id_t, uint_t,
104 char **, char **, nvlist_t *, rcm_info_t **);
105 static int vnic_suspend(rcm_handle_t *, char *, id_t,
106 timespec_t *, uint_t, char **, rcm_info_t **);
107 static int vnic_resume(rcm_handle_t *, char *, id_t, uint_t,
108 char **, rcm_info_t **);
109 static int vnic_offline(rcm_handle_t *, char *, id_t, uint_t,
110 char **, rcm_info_t **);
111 static int vnic_undo_offline(rcm_handle_t *, char *, id_t, uint_t,
112 char **, rcm_info_t **);
113 static int vnic_remove(rcm_handle_t *, char *, id_t, uint_t,
114 char **, rcm_info_t **);
115 static int vnic_notify_event(rcm_handle_t *, char *, id_t, uint_t,
116 char **, nvlist_t *, rcm_info_t **);
117 static int vnic_configure(rcm_handle_t *, datalink_id_t);
119 /* Module private routines */
120 static void cache_free();
121 static int cache_update(rcm_handle_t *);
122 static void cache_remove(link_cache_t *);
123 static void node_free(link_cache_t *);
124 static void cache_insert(link_cache_t *);
125 static link_cache_t *cache_lookup(rcm_handle_t *, char *, char);
126 static int vnic_consumer_offline(rcm_handle_t *, link_cache_t *,
127 char **, uint_t, rcm_info_t **);
128 static void vnic_consumer_online(rcm_handle_t *, link_cache_t *,
129 char **, uint_t, rcm_info_t **);
130 static int vnic_offline_vnic(link_cache_t *, uint32_t,
131 cache_node_state_t);
132 static void vnic_online_vnic(link_cache_t *);
133 static char *vnic_usage(link_cache_t *);
134 static void vnic_log_err(datalink_id_t, char **, char *);
135 static int vnic_consumer_notify(rcm_handle_t *, datalink_id_t,
136 char **, uint_t, rcm_info_t **);
138 /* Module-Private data */
139 static struct rcm_mod_ops vnic_ops =
141 RCM_MOD_OPS_VERSION,
142 vnic_register,
143 vnic_unregister,
144 vnic_get_info,
145 vnic_suspend,
146 vnic_resume,
147 vnic_offline,
148 vnic_undo_offline,
149 vnic_remove,
150 NULL,
151 NULL,
152 vnic_notify_event
156 * rcm_mod_init() - Update registrations, and return the ops structure.
158 struct rcm_mod_ops *
159 rcm_mod_init(void)
161 char errmsg[DLADM_STRSIZE];
162 dladm_status_t status;
164 rcm_log_message(RCM_TRACE1, "VNIC: mod_init\n");
166 cache_head.vc_next = &cache_tail;
167 cache_head.vc_prev = NULL;
168 cache_tail.vc_prev = &cache_head;
169 cache_tail.vc_next = NULL;
170 (void) mutex_init(&cache_lock, 0, NULL);
172 if ((status = dladm_open(&dld_handle)) != DLADM_STATUS_OK) {
173 rcm_log_message(RCM_WARNING,
174 "VNIC: mod_init failed: cannot open datalink handle: %s\n",
175 dladm_status2str(status, errmsg));
176 return (NULL);
179 /* Return the ops vectors */
180 return (&vnic_ops);
184 * rcm_mod_info() - Return a string describing this module.
186 const char *
187 rcm_mod_info(void)
189 rcm_log_message(RCM_TRACE1, "VNIC: mod_info\n");
191 return ("VNIC module");
195 * rcm_mod_fini() - Destroy the network VNIC cache.
198 rcm_mod_fini(void)
200 rcm_log_message(RCM_TRACE1, "VNIC: mod_fini\n");
203 * Note that vnic_unregister() does not seem to be called anywhere,
204 * therefore we free the cache nodes here. In theory we should call
205 * rcm_register_interest() for each node before we free it, the
206 * framework does not provide the rcm_handle to allow us to do so.
208 cache_free();
209 (void) mutex_destroy(&cache_lock);
211 dladm_close(dld_handle);
212 return (RCM_SUCCESS);
216 * vnic_register() - Make sure the cache is properly sync'ed, and its
217 * registrations are in order.
219 static int
220 vnic_register(rcm_handle_t *hd)
222 rcm_log_message(RCM_TRACE1, "VNIC: register\n");
224 if (cache_update(hd) < 0)
225 return (RCM_FAILURE);
228 * Need to register interest in all new resources
229 * getting attached, so we get attach event notifications
231 if (!events_registered) {
232 if (rcm_register_event(hd, RCM_RESOURCE_LINK_NEW, 0, NULL)
233 != RCM_SUCCESS) {
234 rcm_log_message(RCM_ERROR,
235 _("VNIC: failed to register %s\n"),
236 RCM_RESOURCE_LINK_NEW);
237 return (RCM_FAILURE);
238 } else {
239 rcm_log_message(RCM_DEBUG, "VNIC: registered %s\n",
240 RCM_RESOURCE_LINK_NEW);
241 events_registered++;
245 return (RCM_SUCCESS);
249 * vnic_unregister() - Walk the cache, unregistering all the networks.
251 static int
252 vnic_unregister(rcm_handle_t *hd)
254 link_cache_t *node;
256 rcm_log_message(RCM_TRACE1, "VNIC: unregister\n");
258 /* Walk the cache, unregistering everything */
259 (void) mutex_lock(&cache_lock);
260 node = cache_head.vc_next;
261 while (node != &cache_tail) {
262 if (rcm_unregister_interest(hd, node->vc_resource, 0)
263 != RCM_SUCCESS) {
264 rcm_log_message(RCM_ERROR,
265 _("VNIC: failed to unregister %s\n"),
266 node->vc_resource);
267 (void) mutex_unlock(&cache_lock);
268 return (RCM_FAILURE);
270 cache_remove(node);
271 node_free(node);
272 node = cache_head.vc_next;
274 (void) mutex_unlock(&cache_lock);
277 * Unregister interest in all new resources
279 if (events_registered) {
280 if (rcm_unregister_event(hd, RCM_RESOURCE_LINK_NEW, 0)
281 != RCM_SUCCESS) {
282 rcm_log_message(RCM_ERROR,
283 _("VNIC: failed to unregister %s\n"),
284 RCM_RESOURCE_LINK_NEW);
285 return (RCM_FAILURE);
286 } else {
287 rcm_log_message(RCM_DEBUG, "VNIC: unregistered %s\n",
288 RCM_RESOURCE_LINK_NEW);
289 events_registered--;
293 return (RCM_SUCCESS);
297 * vnic_offline() - Offline VNICs on a specific node.
299 static int
300 vnic_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
301 char **errorp, rcm_info_t **info)
303 link_cache_t *node;
305 rcm_log_message(RCM_TRACE1, "VNIC: offline(%s)\n", rsrc);
307 /* Lock the cache and lookup the resource */
308 (void) mutex_lock(&cache_lock);
309 node = cache_lookup(hd, rsrc, CACHE_REFRESH);
310 if (node == NULL) {
311 /* should not happen because the resource is registered. */
312 vnic_log_err(DATALINK_INVALID_LINKID, errorp,
313 "unrecognized resource");
314 (void) mutex_unlock(&cache_lock);
315 return (RCM_SUCCESS);
319 * Inform consumers (IP interfaces) of associated VNICs to be offlined
321 if (vnic_consumer_offline(hd, node, errorp, flags, info) ==
322 RCM_SUCCESS) {
323 rcm_log_message(RCM_DEBUG,
324 "VNIC: consumers agreed on offline\n");
325 } else {
326 vnic_log_err(node->vc_linkid, errorp,
327 "consumers failed to offline");
328 (void) mutex_unlock(&cache_lock);
329 return (RCM_FAILURE);
332 /* Check if it's a query */
333 if (flags & RCM_QUERY) {
334 rcm_log_message(RCM_TRACE1,
335 "VNIC: offline query succeeded(%s)\n", rsrc);
336 (void) mutex_unlock(&cache_lock);
337 return (RCM_SUCCESS);
340 if (vnic_offline_vnic(node, VNIC_OFFLINED, CACHE_NODE_OFFLINED) !=
341 RCM_SUCCESS) {
342 vnic_online_vnic(node);
343 vnic_log_err(node->vc_linkid, errorp, "offline failed");
344 (void) mutex_unlock(&cache_lock);
345 return (RCM_FAILURE);
348 rcm_log_message(RCM_TRACE1, "VNIC: Offline succeeded(%s)\n", rsrc);
349 (void) mutex_unlock(&cache_lock);
350 return (RCM_SUCCESS);
354 * vnic_undo_offline() - Undo offline of a previously offlined node.
356 /*ARGSUSED*/
357 static int
358 vnic_undo_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
359 char **errorp, rcm_info_t **info)
361 link_cache_t *node;
363 rcm_log_message(RCM_TRACE1, "VNIC: online(%s)\n", rsrc);
365 (void) mutex_lock(&cache_lock);
366 node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH);
367 if (node == NULL) {
368 vnic_log_err(DATALINK_INVALID_LINKID, errorp, "no such link");
369 (void) mutex_unlock(&cache_lock);
370 errno = ENOENT;
371 return (RCM_FAILURE);
374 /* Check if no attempt should be made to online the link here */
375 if (!(node->vc_state & CACHE_NODE_OFFLINED)) {
376 vnic_log_err(node->vc_linkid, errorp, "link not offlined");
377 (void) mutex_unlock(&cache_lock);
378 errno = ENOTSUP;
379 return (RCM_SUCCESS);
382 vnic_online_vnic(node);
385 * Inform IP interfaces on associated VNICs to be onlined
387 vnic_consumer_online(hd, node, errorp, flags, info);
389 node->vc_state &= ~CACHE_NODE_OFFLINED;
390 rcm_log_message(RCM_TRACE1, "VNIC: online succeeded(%s)\n", rsrc);
391 (void) mutex_unlock(&cache_lock);
392 return (RCM_SUCCESS);
395 static void
396 vnic_online_vnic(link_cache_t *node)
398 dl_vnic_t *vnic;
399 dladm_status_t status;
400 char errmsg[DLADM_STRSIZE];
403 * Try to bring on all offlined VNICs
405 for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
406 if (!(vnic->dlv_flags & VNIC_OFFLINED))
407 continue;
409 if ((status = dladm_vnic_up(dld_handle, vnic->dlv_vnic_id, 0))
410 != DLADM_STATUS_OK) {
412 * Print a warning message and continue to online
413 * other VNICs.
415 rcm_log_message(RCM_WARNING,
416 _("VNIC: VNIC online failed (%u): %s\n"),
417 vnic->dlv_vnic_id,
418 dladm_status2str(status, errmsg));
419 } else {
420 vnic->dlv_flags &= ~VNIC_OFFLINED;
425 static int
426 vnic_offline_vnic(link_cache_t *node, uint32_t flags, cache_node_state_t state)
428 dl_vnic_t *vnic;
429 dladm_status_t status;
430 char errmsg[DLADM_STRSIZE];
432 rcm_log_message(RCM_TRACE2, "VNIC: vnic_offline_vnic (%s %u %u)\n",
433 node->vc_resource, flags, state);
436 * Try to delete all explicit created VNIC
438 for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
440 if ((status = dladm_vnic_delete(dld_handle, vnic->dlv_vnic_id,
441 DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) {
442 rcm_log_message(RCM_WARNING,
443 _("VNIC: VNIC offline failed (%u): %s\n"),
444 vnic->dlv_vnic_id,
445 dladm_status2str(status, errmsg));
446 return (RCM_FAILURE);
447 } else {
448 rcm_log_message(RCM_TRACE1,
449 "VNIC: VNIC offline succeeded(%u)\n",
450 vnic->dlv_vnic_id);
451 vnic->dlv_flags |= flags;
455 node->vc_state |= state;
456 return (RCM_SUCCESS);
460 * vnic_get_info() - Gather usage information for this resource.
462 /*ARGSUSED*/
464 vnic_get_info(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
465 char **usagep, char **errorp, nvlist_t *props, rcm_info_t **info)
467 link_cache_t *node;
469 rcm_log_message(RCM_TRACE1, "VNIC: get_info(%s)\n", rsrc);
471 (void) mutex_lock(&cache_lock);
472 node = cache_lookup(hd, rsrc, CACHE_REFRESH);
473 if (node == NULL) {
474 rcm_log_message(RCM_INFO,
475 _("VNIC: get_info(%s) unrecognized resource\n"), rsrc);
476 (void) mutex_unlock(&cache_lock);
477 errno = ENOENT;
478 return (RCM_FAILURE);
481 *usagep = vnic_usage(node);
482 (void) mutex_unlock(&cache_lock);
483 if (*usagep == NULL) {
484 /* most likely malloc failure */
485 rcm_log_message(RCM_ERROR,
486 _("VNIC: get_info(%s) malloc failure\n"), rsrc);
487 (void) mutex_unlock(&cache_lock);
488 errno = ENOMEM;
489 return (RCM_FAILURE);
492 /* Set client/role properties */
493 (void) nvlist_add_string(props, RCM_CLIENT_NAME, "VNIC");
495 rcm_log_message(RCM_TRACE1, "VNIC: get_info(%s) info = %s\n",
496 rsrc, *usagep);
497 return (RCM_SUCCESS);
501 * vnic_suspend() - Nothing to do, always okay
503 /*ARGSUSED*/
504 static int
505 vnic_suspend(rcm_handle_t *hd, char *rsrc, id_t id, timespec_t *interval,
506 uint_t flags, char **errorp, rcm_info_t **info)
508 rcm_log_message(RCM_TRACE1, "VNIC: suspend(%s)\n", rsrc);
509 return (RCM_SUCCESS);
513 * vnic_resume() - Nothing to do, always okay
515 /*ARGSUSED*/
516 static int
517 vnic_resume(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
518 char **errorp, rcm_info_t **info)
520 rcm_log_message(RCM_TRACE1, "VNIC: resume(%s)\n", rsrc);
521 return (RCM_SUCCESS);
525 * vnic_consumer_remove()
527 * Notify VNIC consumers to remove cache.
529 static int
530 vnic_consumer_remove(rcm_handle_t *hd, link_cache_t *node, uint_t flags,
531 rcm_info_t **info)
533 dl_vnic_t *vnic = NULL;
534 char rsrc[RCM_LINK_RESOURCE_MAX];
535 int ret = RCM_SUCCESS;
537 rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_remove (%s)\n",
538 node->vc_resource);
540 for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
543 * This will only be called when the offline operation
544 * succeeds, so the VNIC consumers must have been offlined
545 * at this point.
547 assert(vnic->dlv_flags & VNIC_CONSUMER_OFFLINED);
549 (void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u",
550 RCM_LINK_PREFIX, vnic->dlv_vnic_id);
552 ret = rcm_notify_remove(hd, rsrc, flags, info);
553 if (ret != RCM_SUCCESS) {
554 rcm_log_message(RCM_WARNING,
555 _("VNIC: notify remove failed (%s)\n"), rsrc);
556 break;
560 rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_remove done\n");
561 return (ret);
565 * vnic_remove() - remove a resource from cache
567 /*ARGSUSED*/
568 static int
569 vnic_remove(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
570 char **errorp, rcm_info_t **info)
572 link_cache_t *node;
573 int rv;
575 rcm_log_message(RCM_TRACE1, "VNIC: remove(%s)\n", rsrc);
577 (void) mutex_lock(&cache_lock);
578 node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH);
579 if (node == NULL) {
580 rcm_log_message(RCM_INFO,
581 _("VNIC: remove(%s) unrecognized resource\n"), rsrc);
582 (void) mutex_unlock(&cache_lock);
583 errno = ENOENT;
584 return (RCM_FAILURE);
587 /* remove the cached entry for the resource */
588 cache_remove(node);
589 (void) mutex_unlock(&cache_lock);
591 rv = vnic_consumer_remove(hd, node, flags, info);
592 node_free(node);
593 return (rv);
597 * vnic_notify_event - Project private implementation to receive new resource
598 * events. It intercepts all new resource events. If the
599 * new resource is a network resource, pass up a notify
600 * for it too. The new resource need not be cached, since
601 * it is done at register again.
603 /*ARGSUSED*/
604 static int
605 vnic_notify_event(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
606 char **errorp, nvlist_t *nvl, rcm_info_t **info)
608 nvpair_t *nvp = NULL;
609 datalink_id_t linkid;
610 uint64_t id64;
611 int rv = RCM_SUCCESS;
613 rcm_log_message(RCM_TRACE1, "VNIC: notify_event(%s)\n", rsrc);
615 if (strcmp(rsrc, RCM_RESOURCE_LINK_NEW) != 0) {
616 vnic_log_err(DATALINK_INVALID_LINKID, errorp,
617 "unrecognized event");
618 errno = EINVAL;
619 return (RCM_FAILURE);
622 /* Update cache to reflect latest VNICs */
623 if (cache_update(hd) < 0) {
624 vnic_log_err(DATALINK_INVALID_LINKID, errorp,
625 "private Cache update failed");
626 return (RCM_FAILURE);
630 * Try best to recover all configuration.
632 rcm_log_message(RCM_DEBUG, "VNIC: process_nvlist\n");
633 while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
634 if (strcmp(nvpair_name(nvp), RCM_NV_LINKID) != 0)
635 continue;
637 if (nvpair_value_uint64(nvp, &id64) != 0) {
638 vnic_log_err(DATALINK_INVALID_LINKID, errorp,
639 "cannot get linkid");
640 rv = RCM_FAILURE;
641 continue;
644 linkid = (datalink_id_t)id64;
645 if (vnic_configure(hd, linkid) != 0) {
646 vnic_log_err(linkid, errorp, "configuring failed");
647 rv = RCM_FAILURE;
648 continue;
651 /* Notify all VNIC consumers */
652 if (vnic_consumer_notify(hd, linkid, errorp, flags,
653 info) != 0) {
654 vnic_log_err(linkid, errorp, "consumer notify failed");
655 rv = RCM_FAILURE;
659 rcm_log_message(RCM_TRACE1,
660 "VNIC: notify_event: link configuration complete\n");
661 return (rv);
665 * vnic_usage - Determine the usage of a link.
666 * The returned buffer is owned by caller, and the caller
667 * must free it up when done.
669 static char *
670 vnic_usage(link_cache_t *node)
672 dl_vnic_t *vnic;
673 int nvnic;
674 char *buf;
675 const char *fmt;
676 char *sep;
677 char errmsg[DLADM_STRSIZE];
678 char name[MAXLINKNAMELEN];
679 dladm_status_t status;
680 size_t bufsz;
682 rcm_log_message(RCM_TRACE2, "VNIC: usage(%s)\n", node->vc_resource);
684 assert(MUTEX_HELD(&cache_lock));
685 if ((status = dladm_datalink_id2info(dld_handle, node->vc_linkid, NULL,
686 NULL, NULL, name, sizeof (name))) != DLADM_STATUS_OK) {
687 rcm_log_message(RCM_ERROR,
688 _("VNIC: usage(%s) get link name failure(%s)\n"),
689 node->vc_resource, dladm_status2str(status, errmsg));
690 return (NULL);
693 if (node->vc_state & CACHE_NODE_OFFLINED)
694 fmt = _("%1$s offlined");
695 else
696 fmt = _("%1$s VNICs: ");
698 /* TRANSLATION_NOTE: separator used between VNIC linkids */
699 sep = _(", ");
701 nvnic = 0;
702 for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next)
703 nvnic++;
705 /* space for VNICs and separators, plus message */
706 bufsz = nvnic * (MAXLINKNAMELEN + strlen(sep)) +
707 strlen(fmt) + MAXLINKNAMELEN + 1;
708 if ((buf = malloc(bufsz)) == NULL) {
709 rcm_log_message(RCM_ERROR,
710 _("VNIC: usage(%s) malloc failure(%s)\n"),
711 node->vc_resource, strerror(errno));
712 return (NULL);
714 (void) snprintf(buf, bufsz, fmt, name);
716 if (node->vc_state & CACHE_NODE_OFFLINED) {
717 /* Nothing else to do */
718 rcm_log_message(RCM_TRACE2, "VNIC: usage (%s) info = %s\n",
719 node->vc_resource, buf);
720 return (buf);
723 for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
724 rcm_log_message(RCM_DEBUG, "VNIC:= %u\n", vnic->dlv_vnic_id);
726 if ((status = dladm_datalink_id2info(dld_handle,
727 vnic->dlv_vnic_id, NULL, NULL, NULL, name, sizeof (name)))
728 != DLADM_STATUS_OK) {
729 rcm_log_message(RCM_ERROR,
730 _("VNIC: usage(%s) get vnic %u name failure(%s)\n"),
731 node->vc_resource, vnic->dlv_vnic_id,
732 dladm_status2str(status, errmsg));
733 free(buf);
734 return (NULL);
737 (void) strlcat(buf, name, bufsz);
738 if (vnic->dlv_next != NULL)
739 (void) strlcat(buf, sep, bufsz);
742 rcm_log_message(RCM_TRACE2, "VNIC: usage (%s) info = %s\n",
743 node->vc_resource, buf);
745 return (buf);
749 * Cache management routines, all cache management functions should be
750 * be called with cache_lock held.
754 * cache_lookup() - Get a cache node for a resource.
755 * Call with cache lock held.
757 * This ensures that the cache is consistent with the system state and
758 * returns a pointer to the cache element corresponding to the resource.
760 static link_cache_t *
761 cache_lookup(rcm_handle_t *hd, char *rsrc, char options)
763 link_cache_t *node;
765 rcm_log_message(RCM_TRACE2, "VNIC: cache lookup(%s)\n", rsrc);
767 assert(MUTEX_HELD(&cache_lock));
768 if (options & CACHE_REFRESH) {
769 /* drop lock since update locks cache again */
770 (void) mutex_unlock(&cache_lock);
771 (void) cache_update(hd);
772 (void) mutex_lock(&cache_lock);
775 node = cache_head.vc_next;
776 for (; node != &cache_tail; node = node->vc_next) {
777 if (strcmp(rsrc, node->vc_resource) == 0) {
778 rcm_log_message(RCM_TRACE2,
779 "VNIC: cache lookup succeeded(%s)\n", rsrc);
780 return (node);
783 return (NULL);
787 * node_free - Free a node from the cache
789 static void
790 node_free(link_cache_t *node)
792 dl_vnic_t *vnic, *next;
794 if (node != NULL) {
795 free(node->vc_resource);
797 /* free the VNIC list */
798 for (vnic = node->vc_vnic; vnic != NULL; vnic = next) {
799 next = vnic->dlv_next;
800 free(vnic);
802 free(node);
807 * cache_insert - Insert a resource node in cache
809 static void
810 cache_insert(link_cache_t *node)
812 assert(MUTEX_HELD(&cache_lock));
814 /* insert at the head for best performance */
815 node->vc_next = cache_head.vc_next;
816 node->vc_prev = &cache_head;
818 node->vc_next->vc_prev = node;
819 node->vc_prev->vc_next = node;
823 * cache_remove() - Remove a resource node from cache.
825 static void
826 cache_remove(link_cache_t *node)
828 assert(MUTEX_HELD(&cache_lock));
829 node->vc_next->vc_prev = node->vc_prev;
830 node->vc_prev->vc_next = node->vc_next;
831 node->vc_next = NULL;
832 node->vc_prev = NULL;
835 typedef struct vnic_update_arg_s {
836 rcm_handle_t *hd;
837 int retval;
838 } vnic_update_arg_t;
841 * vnic_update() - Update physical interface properties
843 static int
844 vnic_update(dladm_handle_t handle, datalink_id_t vnicid, void *arg)
846 vnic_update_arg_t *vnic_update_argp = arg;
847 rcm_handle_t *hd = vnic_update_argp->hd;
848 link_cache_t *node;
849 dl_vnic_t *vnic;
850 char *rsrc;
851 dladm_vnic_attr_t vnic_attr;
852 dladm_status_t status;
853 char errmsg[DLADM_STRSIZE];
854 boolean_t newnode = B_FALSE;
855 int ret = -1;
857 rcm_log_message(RCM_TRACE2, "VNIC: vnic_update(%u)\n", vnicid);
859 assert(MUTEX_HELD(&cache_lock));
860 status = dladm_vnic_info(handle, vnicid, &vnic_attr, DLADM_OPT_ACTIVE);
861 if (status != DLADM_STATUS_OK) {
862 rcm_log_message(RCM_TRACE1,
863 "VNIC: vnic_update() cannot get vnic information for "
864 "%u(%s)\n", vnicid, dladm_status2str(status, errmsg));
865 return (DLADM_WALK_CONTINUE);
868 if (vnic_attr.va_link_id == DATALINK_INVALID_LINKID) {
870 * Skip the etherstubs.
872 rcm_log_message(RCM_TRACE1,
873 "VNIC: vnic_update(): skip the etherstub %u\n", vnicid);
874 return (DLADM_WALK_CONTINUE);
877 rsrc = malloc(RCM_LINK_RESOURCE_MAX);
878 if (rsrc == NULL) {
879 rcm_log_message(RCM_ERROR, _("VNIC: malloc error(%s): %u\n"),
880 strerror(errno), vnicid);
881 goto done;
884 (void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u",
885 RCM_LINK_PREFIX, vnic_attr.va_link_id);
887 node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH);
888 if (node != NULL) {
889 rcm_log_message(RCM_DEBUG,
890 "VNIC: %s already registered (vnicid:%d)\n",
891 rsrc, vnic_attr.va_vnic_id);
892 free(rsrc);
893 } else {
894 rcm_log_message(RCM_DEBUG,
895 "VNIC: %s is a new resource (vnicid:%d)\n",
896 rsrc, vnic_attr.va_vnic_id);
897 if ((node = calloc(1, sizeof (link_cache_t))) == NULL) {
898 free(rsrc);
899 rcm_log_message(RCM_ERROR, _("VNIC: calloc: %s\n"),
900 strerror(errno));
901 goto done;
904 node->vc_resource = rsrc;
905 node->vc_vnic = NULL;
906 node->vc_linkid = vnic_attr.va_link_id;
907 node->vc_state |= CACHE_NODE_NEW;
908 newnode = B_TRUE;
911 for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
912 if (vnic->dlv_vnic_id == vnicid) {
913 vnic->dlv_flags &= ~VNIC_STALE;
914 break;
918 if (vnic == NULL) {
919 if ((vnic = calloc(1, sizeof (dl_vnic_t))) == NULL) {
920 rcm_log_message(RCM_ERROR, _("VNIC: malloc: %s\n"),
921 strerror(errno));
922 if (newnode) {
923 free(rsrc);
924 free(node);
926 goto done;
928 vnic->dlv_vnic_id = vnicid;
929 vnic->dlv_next = node->vc_vnic;
930 vnic->dlv_prev = NULL;
931 if (node->vc_vnic != NULL)
932 node->vc_vnic->dlv_prev = vnic;
933 node->vc_vnic = vnic;
936 node->vc_state &= ~CACHE_NODE_STALE;
938 if (newnode)
939 cache_insert(node);
941 rcm_log_message(RCM_TRACE3, "VNIC: vnic_update: succeeded(%u)\n",
942 vnicid);
943 ret = 0;
944 done:
945 vnic_update_argp->retval = ret;
946 return (ret == 0 ? DLADM_WALK_CONTINUE : DLADM_WALK_TERMINATE);
950 * vnic_update_all() - Determine all VNIC links in the system
952 static int
953 vnic_update_all(rcm_handle_t *hd)
955 vnic_update_arg_t arg = {NULL, 0};
957 rcm_log_message(RCM_TRACE2, "VNIC: vnic_update_all\n");
959 assert(MUTEX_HELD(&cache_lock));
960 arg.hd = hd;
961 (void) dladm_walk_datalink_id(vnic_update, dld_handle, &arg,
962 DATALINK_CLASS_VNIC, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
963 return (arg.retval);
967 * cache_update() - Update cache with latest interface info
969 static int
970 cache_update(rcm_handle_t *hd)
972 link_cache_t *node, *nnode;
973 dl_vnic_t *vnic;
974 int rv;
976 rcm_log_message(RCM_TRACE2, "VNIC: cache_update\n");
978 (void) mutex_lock(&cache_lock);
980 /* first we walk the entire cache, marking each entry stale */
981 node = cache_head.vc_next;
982 for (; node != &cache_tail; node = node->vc_next) {
983 node->vc_state |= CACHE_NODE_STALE;
984 for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next)
985 vnic->dlv_flags |= VNIC_STALE;
988 rv = vnic_update_all(hd);
991 * Continue to delete all stale nodes from the cache even
992 * vnic_update_all() failed. Unregister link that are not offlined
993 * and still in cache
995 for (node = cache_head.vc_next; node != &cache_tail; node = nnode) {
996 dl_vnic_t *vnic, *next;
998 for (vnic = node->vc_vnic; vnic != NULL; vnic = next) {
999 next = vnic->dlv_next;
1001 /* clear stale VNICs */
1002 if (vnic->dlv_flags & VNIC_STALE) {
1003 if (vnic->dlv_prev != NULL)
1004 vnic->dlv_prev->dlv_next = next;
1005 else
1006 node->vc_vnic = next;
1008 if (next != NULL)
1009 next->dlv_prev = vnic->dlv_prev;
1010 free(vnic);
1014 nnode = node->vc_next;
1015 if (node->vc_state & CACHE_NODE_STALE) {
1016 (void) rcm_unregister_interest(hd, node->vc_resource,
1018 rcm_log_message(RCM_DEBUG, "VNIC: unregistered %s\n",
1019 node->vc_resource);
1020 assert(node->vc_vnic == NULL);
1021 cache_remove(node);
1022 node_free(node);
1023 continue;
1026 if (!(node->vc_state & CACHE_NODE_NEW))
1027 continue;
1029 if (rcm_register_interest(hd, node->vc_resource, 0, NULL) !=
1030 RCM_SUCCESS) {
1031 rcm_log_message(RCM_ERROR,
1032 _("VNIC: failed to register %s\n"),
1033 node->vc_resource);
1034 rv = -1;
1035 } else {
1036 rcm_log_message(RCM_DEBUG, "VNIC: registered %s\n",
1037 node->vc_resource);
1038 node->vc_state &= ~CACHE_NODE_NEW;
1042 (void) mutex_unlock(&cache_lock);
1043 return (rv);
1047 * cache_free() - Empty the cache
1049 static void
1050 cache_free()
1052 link_cache_t *node;
1054 rcm_log_message(RCM_TRACE2, "VNIC: cache_free\n");
1056 (void) mutex_lock(&cache_lock);
1057 node = cache_head.vc_next;
1058 while (node != &cache_tail) {
1059 cache_remove(node);
1060 node_free(node);
1061 node = cache_head.vc_next;
1063 (void) mutex_unlock(&cache_lock);
1067 * vnic_log_err() - RCM error log wrapper
1069 static void
1070 vnic_log_err(datalink_id_t linkid, char **errorp, char *errmsg)
1072 char link[MAXLINKNAMELEN];
1073 char errstr[DLADM_STRSIZE];
1074 dladm_status_t status;
1075 int len;
1076 const char *errfmt;
1077 char *error;
1079 link[0] = '\0';
1080 if (linkid != DATALINK_INVALID_LINKID) {
1081 char rsrc[RCM_LINK_RESOURCE_MAX];
1083 (void) snprintf(rsrc, sizeof (rsrc), "%s/%u",
1084 RCM_LINK_PREFIX, linkid);
1086 rcm_log_message(RCM_ERROR, _("VNIC: %s(%s)\n"), errmsg, rsrc);
1087 if ((status = dladm_datalink_id2info(dld_handle, linkid, NULL,
1088 NULL, NULL, link, sizeof (link))) != DLADM_STATUS_OK) {
1089 rcm_log_message(RCM_WARNING,
1090 _("VNIC: cannot get link name for (%s) %s\n"),
1091 rsrc, dladm_status2str(status, errstr));
1093 } else {
1094 rcm_log_message(RCM_ERROR, _("VNIC: %s\n"), errmsg);
1097 errfmt = strlen(link) > 0 ? _("VNIC: %s(%s)") : _("VNIC: %s");
1098 len = strlen(errfmt) + strlen(errmsg) + MAXLINKNAMELEN + 1;
1099 if ((error = malloc(len)) != NULL) {
1100 if (strlen(link) > 0)
1101 (void) snprintf(error, len, errfmt, errmsg, link);
1102 else
1103 (void) snprintf(error, len, errfmt, errmsg);
1106 if (errorp != NULL)
1107 *errorp = error;
1111 * vnic_consumer_online()
1113 * Notify online to VNIC consumers.
1115 /* ARGSUSED */
1116 static void
1117 vnic_consumer_online(rcm_handle_t *hd, link_cache_t *node, char **errorp,
1118 uint_t flags, rcm_info_t **info)
1120 dl_vnic_t *vnic;
1121 char rsrc[RCM_LINK_RESOURCE_MAX];
1123 rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_online (%s)\n",
1124 node->vc_resource);
1126 for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
1127 if (!(vnic->dlv_flags & VNIC_CONSUMER_OFFLINED))
1128 continue;
1130 (void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u",
1131 RCM_LINK_PREFIX, vnic->dlv_vnic_id);
1133 if (rcm_notify_online(hd, rsrc, flags, info) == RCM_SUCCESS)
1134 vnic->dlv_flags &= ~VNIC_CONSUMER_OFFLINED;
1137 rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_online done\n");
1141 * vnic_consumer_offline()
1143 * Offline VNIC consumers.
1145 static int
1146 vnic_consumer_offline(rcm_handle_t *hd, link_cache_t *node, char **errorp,
1147 uint_t flags, rcm_info_t **info)
1149 dl_vnic_t *vnic;
1150 char rsrc[RCM_LINK_RESOURCE_MAX];
1151 int ret = RCM_SUCCESS;
1153 rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_offline (%s)\n",
1154 node->vc_resource);
1156 for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
1157 (void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u",
1158 RCM_LINK_PREFIX, vnic->dlv_vnic_id);
1160 ret = rcm_request_offline(hd, rsrc, flags, info);
1161 if (ret != RCM_SUCCESS)
1162 break;
1164 vnic->dlv_flags |= VNIC_CONSUMER_OFFLINED;
1167 if (vnic != NULL)
1168 vnic_consumer_online(hd, node, errorp, flags, info);
1170 rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_offline done\n");
1171 return (ret);
1175 * Send RCM_RESOURCE_LINK_NEW events to other modules about new VNICs.
1176 * Return 0 on success, -1 on failure.
1178 static int
1179 vnic_notify_new_vnic(rcm_handle_t *hd, char *rsrc)
1181 link_cache_t *node;
1182 dl_vnic_t *vnic;
1183 nvlist_t *nvl = NULL;
1184 uint64_t id;
1185 int ret = -1;
1187 rcm_log_message(RCM_TRACE2, "VNIC: vnic_notify_new_vnic (%s)\n", rsrc);
1189 (void) mutex_lock(&cache_lock);
1190 if ((node = cache_lookup(hd, rsrc, CACHE_REFRESH)) == NULL) {
1191 (void) mutex_unlock(&cache_lock);
1192 return (0);
1195 if (nvlist_alloc(&nvl, 0, 0) != 0) {
1196 (void) mutex_unlock(&cache_lock);
1197 rcm_log_message(RCM_WARNING,
1198 _("VNIC: failed to allocate nvlist\n"));
1199 goto done;
1202 for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
1203 rcm_log_message(RCM_TRACE2,
1204 "VNIC: vnic_notify_new_vnic add (%u)\n", vnic->dlv_vnic_id);
1206 id = vnic->dlv_vnic_id;
1207 if (nvlist_add_uint64(nvl, RCM_NV_LINKID, id) != 0) {
1208 rcm_log_message(RCM_ERROR,
1209 _("VNIC: failed to construct nvlist\n"));
1210 (void) mutex_unlock(&cache_lock);
1211 goto done;
1214 (void) mutex_unlock(&cache_lock);
1216 if (rcm_notify_event(hd, RCM_RESOURCE_LINK_NEW, 0, nvl, NULL) !=
1217 RCM_SUCCESS) {
1218 rcm_log_message(RCM_ERROR,
1219 _("VNIC: failed to notify %s event for %s\n"),
1220 RCM_RESOURCE_LINK_NEW, node->vc_resource);
1221 goto done;
1224 ret = 0;
1225 done:
1226 nvlist_free(nvl);
1227 return (ret);
1231 * vnic_consumer_notify() - Notify consumers of VNICs coming back online.
1233 static int
1234 vnic_consumer_notify(rcm_handle_t *hd, datalink_id_t linkid, char **errorp,
1235 uint_t flags, rcm_info_t **info)
1237 char rsrc[RCM_LINK_RESOURCE_MAX];
1238 link_cache_t *node;
1240 /* Check for the interface in the cache */
1241 (void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u", RCM_LINK_PREFIX,
1242 linkid);
1244 rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_notify(%s)\n", rsrc);
1247 * Inform IP consumers of the new link.
1249 if (vnic_notify_new_vnic(hd, rsrc) != 0) {
1250 (void) mutex_lock(&cache_lock);
1251 if ((node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH)) != NULL) {
1252 (void) vnic_offline_vnic(node, VNIC_STALE,
1253 CACHE_NODE_STALE);
1255 (void) mutex_unlock(&cache_lock);
1256 rcm_log_message(RCM_TRACE2,
1257 "VNIC: vnic_notify_new_vnic failed(%s)\n", rsrc);
1258 return (-1);
1261 rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_notify succeeded\n");
1262 return (0);
1265 typedef struct vnic_up_arg_s {
1266 datalink_id_t linkid;
1267 int retval;
1268 } vnic_up_arg_t;
1270 static int
1271 vnic_up(dladm_handle_t handle, datalink_id_t vnicid, void *arg)
1273 vnic_up_arg_t *vnic_up_argp = arg;
1274 dladm_status_t status;
1275 dladm_vnic_attr_t vnic_attr;
1276 char errmsg[DLADM_STRSIZE];
1278 status = dladm_vnic_info(handle, vnicid, &vnic_attr, DLADM_OPT_PERSIST);
1279 if (status != DLADM_STATUS_OK) {
1280 rcm_log_message(RCM_TRACE1,
1281 "VNIC: vnic_up(): cannot get information for VNIC %u "
1282 "(%s)\n", vnicid, dladm_status2str(status, errmsg));
1283 return (DLADM_WALK_CONTINUE);
1286 if (vnic_attr.va_link_id != vnic_up_argp->linkid)
1287 return (DLADM_WALK_CONTINUE);
1289 rcm_log_message(RCM_TRACE3, "VNIC: vnic_up(%u)\n", vnicid);
1290 if ((status = dladm_vnic_up(handle, vnicid, 0)) == DLADM_STATUS_OK)
1291 return (DLADM_WALK_CONTINUE);
1294 * Prompt the warning message and continue to UP other VNICs.
1296 rcm_log_message(RCM_WARNING,
1297 _("VNIC: VNIC up failed (%u): %s\n"),
1298 vnicid, dladm_status2str(status, errmsg));
1300 vnic_up_argp->retval = -1;
1301 return (DLADM_WALK_CONTINUE);
1305 * vnic_configure() - Configure VNICs over a physical link after it attaches
1307 static int
1308 vnic_configure(rcm_handle_t *hd, datalink_id_t linkid)
1310 char rsrc[RCM_LINK_RESOURCE_MAX];
1311 link_cache_t *node;
1312 vnic_up_arg_t arg = {DATALINK_INVALID_LINKID, 0};
1314 /* Check for the VNICs in the cache */
1315 (void) snprintf(rsrc, sizeof (rsrc), "%s/%u", RCM_LINK_PREFIX, linkid);
1317 rcm_log_message(RCM_TRACE2, "VNIC: vnic_configure(%s)\n", rsrc);
1319 /* Check if the link is new or was previously offlined */
1320 (void) mutex_lock(&cache_lock);
1321 if (((node = cache_lookup(hd, rsrc, CACHE_REFRESH)) != NULL) &&
1322 (!(node->vc_state & CACHE_NODE_OFFLINED))) {
1323 rcm_log_message(RCM_TRACE2,
1324 "VNIC: Skipping configured interface(%s)\n", rsrc);
1325 (void) mutex_unlock(&cache_lock);
1326 return (0);
1328 (void) mutex_unlock(&cache_lock);
1330 arg.linkid = linkid;
1331 (void) dladm_walk_datalink_id(vnic_up, dld_handle, &arg,
1332 DATALINK_CLASS_VNIC, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
1334 if (arg.retval == 0) {
1335 rcm_log_message(RCM_TRACE2,
1336 "VNIC: vnic_configure succeeded(%s)\n", rsrc);
1338 return (arg.retval);