xref: /titanic_41/usr/src/cmd/rcm_daemon/common/vnic_rcm.c (revision 93c20f2609342fd05f6625f16dfcb9348e7977f2)
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 2008 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 VNIC links
28  */
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <sys/types.h>
35 #include <synch.h>
36 #include <assert.h>
37 #include <strings.h>
38 #include "rcm_module.h"
39 #include <libintl.h>
40 #include <libdllink.h>
41 #include <libdlvnic.h>
42 #include <libdlpi.h>
43 
44 /*
45  * Definitions
46  */
47 #ifndef lint
48 #define	_(x)	gettext(x)
49 #else
50 #define	_(x)	x
51 #endif
52 
53 /* Some generic well-knowns and defaults used in this module */
54 #define	RCM_LINK_PREFIX		"SUNW_datalink"	/* RCM datalink name prefix */
55 #define	RCM_LINK_RESOURCE_MAX	(13 + LINKID_STR_WIDTH)
56 
57 /* VNIC link flags */
58 typedef enum {
59 	VNIC_OFFLINED		= 0x1,
60 	VNIC_CONSUMER_OFFLINED	= 0x2,
61 	VNIC_STALE		= 0x4
62 } vnic_flag_t;
63 
64 /* link representation */
65 typedef struct dl_vnic {
66 	struct dl_vnic	*dlv_next;		/* next VNIC on the same link */
67 	struct dl_vnic	*dlv_prev;		/* prev VNIC on the same link */
68 	datalink_id_t	dlv_vnic_id;
69 	vnic_flag_t	dlv_flags;		/* VNIC link flags */
70 } dl_vnic_t;
71 
72 /* VNIC Cache state flags */
73 typedef enum {
74 	CACHE_NODE_STALE	= 0x1,		/* stale cached data */
75 	CACHE_NODE_NEW		= 0x2,		/* new cached nodes */
76 	CACHE_NODE_OFFLINED	= 0x4		/* nodes offlined */
77 } cache_node_state_t;
78 
79 /* Network Cache lookup options */
80 #define	CACHE_NO_REFRESH	0x1		/* cache refresh not needed */
81 #define	CACHE_REFRESH		0x2		/* refresh cache */
82 
83 /* Cache element */
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_vnic_t		*vc_vnic;	/* VNIC list on this link */
90 	cache_node_state_t	vc_state;	/* cache state flags */
91 } link_cache_t;
92 
93 /*
94  * Global cache for network VNICs
95  */
96 static link_cache_t	cache_head;
97 static link_cache_t	cache_tail;
98 static mutex_t		cache_lock;
99 static int		events_registered = 0;
100 
101 /*
102  * RCM module interface prototypes
103  */
104 static int		vnic_register(rcm_handle_t *);
105 static int		vnic_unregister(rcm_handle_t *);
106 static int		vnic_get_info(rcm_handle_t *, char *, id_t, uint_t,
107 			    char **, char **, nvlist_t *, rcm_info_t **);
108 static int		vnic_suspend(rcm_handle_t *, char *, id_t,
109 			    timespec_t *, uint_t, char **, rcm_info_t **);
110 static int		vnic_resume(rcm_handle_t *, char *, id_t, uint_t,
111 			    char **, rcm_info_t **);
112 static int		vnic_offline(rcm_handle_t *, char *, id_t, uint_t,
113 			    char **, rcm_info_t **);
114 static int		vnic_undo_offline(rcm_handle_t *, char *, id_t, uint_t,
115 			    char **, rcm_info_t **);
116 static int		vnic_remove(rcm_handle_t *, char *, id_t, uint_t,
117 			    char **, rcm_info_t **);
118 static int		vnic_notify_event(rcm_handle_t *, char *, id_t, uint_t,
119 			    char **, nvlist_t *, rcm_info_t **);
120 static int		vnic_configure(rcm_handle_t *, datalink_id_t);
121 
122 /* Module private routines */
123 static void 		cache_free();
124 static int 		cache_update(rcm_handle_t *);
125 static void 		cache_remove(link_cache_t *);
126 static void 		node_free(link_cache_t *);
127 static void 		cache_insert(link_cache_t *);
128 static link_cache_t	*cache_lookup(rcm_handle_t *, char *, char);
129 static int		vnic_consumer_offline(rcm_handle_t *, link_cache_t *,
130 			    char **, uint_t, rcm_info_t **);
131 static void		vnic_consumer_online(rcm_handle_t *, link_cache_t *,
132 			    char **, uint_t, rcm_info_t **);
133 static int		vnic_offline_vnic(link_cache_t *, uint32_t,
134 			    cache_node_state_t);
135 static void		vnic_online_vnic(link_cache_t *);
136 static char 		*vnic_usage(link_cache_t *);
137 static void 		vnic_log_err(datalink_id_t, char **, char *);
138 static int		vnic_consumer_notify(rcm_handle_t *, datalink_id_t,
139 			    char **, uint_t, rcm_info_t **);
140 
141 /* Module-Private data */
142 static struct rcm_mod_ops vnic_ops =
143 {
144 	RCM_MOD_OPS_VERSION,
145 	vnic_register,
146 	vnic_unregister,
147 	vnic_get_info,
148 	vnic_suspend,
149 	vnic_resume,
150 	vnic_offline,
151 	vnic_undo_offline,
152 	vnic_remove,
153 	NULL,
154 	NULL,
155 	vnic_notify_event
156 };
157 
158 /*
159  * rcm_mod_init() - Update registrations, and return the ops structure.
160  */
161 struct rcm_mod_ops *
162 rcm_mod_init(void)
163 {
164 	rcm_log_message(RCM_TRACE1, "VNIC: mod_init\n");
165 
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);
171 
172 	/* Return the ops vectors */
173 	return (&vnic_ops);
174 }
175 
176 /*
177  * rcm_mod_info() - Return a string describing this module.
178  */
179 const char *
180 rcm_mod_info(void)
181 {
182 	rcm_log_message(RCM_TRACE1, "VNIC: mod_info\n");
183 
184 	return ("VNIC module");
185 }
186 
187 /*
188  * rcm_mod_fini() - Destroy the network VNIC cache.
189  */
190 int
191 rcm_mod_fini(void)
192 {
193 	rcm_log_message(RCM_TRACE1, "VNIC: mod_fini\n");
194 
195 	/*
196 	 * Note that vnic_unregister() does not seem to be called anywhere,
197 	 * therefore we free the cache nodes here. In theory we should call
198 	 * rcm_register_interest() for each node before we free it, the
199 	 * framework does not provide the rcm_handle to allow us to do so.
200 	 */
201 	cache_free();
202 	(void) mutex_destroy(&cache_lock);
203 	return (RCM_SUCCESS);
204 }
205 
206 /*
207  * vnic_register() - Make sure the cache is properly sync'ed, and its
208  *		 registrations are in order.
209  */
210 static int
211 vnic_register(rcm_handle_t *hd)
212 {
213 	rcm_log_message(RCM_TRACE1, "VNIC: register\n");
214 
215 	if (cache_update(hd) < 0)
216 		return (RCM_FAILURE);
217 
218 	/*
219 	 * Need to register interest in all new resources
220 	 * getting attached, so we get attach event notifications
221 	 */
222 	if (!events_registered) {
223 		if (rcm_register_event(hd, RCM_RESOURCE_LINK_NEW, 0, NULL)
224 		    != RCM_SUCCESS) {
225 			rcm_log_message(RCM_ERROR,
226 			    _("VNIC: failed to register %s\n"),
227 			    RCM_RESOURCE_LINK_NEW);
228 			return (RCM_FAILURE);
229 		} else {
230 			rcm_log_message(RCM_DEBUG, "VNIC: registered %s\n",
231 			    RCM_RESOURCE_LINK_NEW);
232 			events_registered++;
233 		}
234 	}
235 
236 	return (RCM_SUCCESS);
237 }
238 
239 /*
240  * vnic_unregister() - Walk the cache, unregistering all the networks.
241  */
242 static int
243 vnic_unregister(rcm_handle_t *hd)
244 {
245 	link_cache_t *node;
246 
247 	rcm_log_message(RCM_TRACE1, "VNIC: unregister\n");
248 
249 	/* Walk the cache, unregistering everything */
250 	(void) mutex_lock(&cache_lock);
251 	node = cache_head.vc_next;
252 	while (node != &cache_tail) {
253 		if (rcm_unregister_interest(hd, node->vc_resource, 0)
254 		    != RCM_SUCCESS) {
255 			rcm_log_message(RCM_ERROR,
256 			    _("VNIC: failed to unregister %s\n"),
257 			    node->vc_resource);
258 			(void) mutex_unlock(&cache_lock);
259 			return (RCM_FAILURE);
260 		}
261 		cache_remove(node);
262 		node_free(node);
263 		node = cache_head.vc_next;
264 	}
265 	(void) mutex_unlock(&cache_lock);
266 
267 	/*
268 	 * Unregister interest in all new resources
269 	 */
270 	if (events_registered) {
271 		if (rcm_unregister_event(hd, RCM_RESOURCE_LINK_NEW, 0)
272 		    != RCM_SUCCESS) {
273 			rcm_log_message(RCM_ERROR,
274 			    _("VNIC: failed to unregister %s\n"),
275 			    RCM_RESOURCE_LINK_NEW);
276 			return (RCM_FAILURE);
277 		} else {
278 			rcm_log_message(RCM_DEBUG, "VNIC: unregistered %s\n",
279 			    RCM_RESOURCE_LINK_NEW);
280 			events_registered--;
281 		}
282 	}
283 
284 	return (RCM_SUCCESS);
285 }
286 
287 /*
288  * vnic_offline() - Offline VNICs on a specific node.
289  */
290 static int
291 vnic_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
292     char **errorp, rcm_info_t **info)
293 {
294 	link_cache_t *node;
295 
296 	rcm_log_message(RCM_TRACE1, "VNIC: offline(%s)\n", rsrc);
297 
298 	/* Lock the cache and lookup the resource */
299 	(void) mutex_lock(&cache_lock);
300 	node = cache_lookup(hd, rsrc, CACHE_REFRESH);
301 	if (node == NULL) {
302 		/* should not happen because the resource is registered. */
303 		vnic_log_err(node->vc_linkid, errorp, "unrecognized resource");
304 		(void) mutex_unlock(&cache_lock);
305 		return (RCM_SUCCESS);
306 	}
307 
308 	/*
309 	 * Inform consumers (IP interfaces) of associated VNICs to be offlined
310 	 */
311 	if (vnic_consumer_offline(hd, node, errorp, flags, info) ==
312 	    RCM_SUCCESS) {
313 		rcm_log_message(RCM_DEBUG,
314 		    "VNIC: consumers agreed on offline\n");
315 	} else {
316 		vnic_log_err(node->vc_linkid, errorp,
317 		    "consumers failed to offline");
318 		(void) mutex_unlock(&cache_lock);
319 		return (RCM_FAILURE);
320 	}
321 
322 	/* Check if it's a query */
323 	if (flags & RCM_QUERY) {
324 		rcm_log_message(RCM_TRACE1,
325 		    "VNIC: offline query succeeded(%s)\n", rsrc);
326 		(void) mutex_unlock(&cache_lock);
327 		return (RCM_SUCCESS);
328 	}
329 
330 	if (vnic_offline_vnic(node, VNIC_OFFLINED, CACHE_NODE_OFFLINED) !=
331 	    RCM_SUCCESS) {
332 		vnic_online_vnic(node);
333 		vnic_log_err(node->vc_linkid, errorp, "offline failed");
334 		(void) mutex_unlock(&cache_lock);
335 		return (RCM_FAILURE);
336 	}
337 
338 	rcm_log_message(RCM_TRACE1, "VNIC: Offline succeeded(%s)\n", rsrc);
339 	(void) mutex_unlock(&cache_lock);
340 	return (RCM_SUCCESS);
341 }
342 
343 /*
344  * vnic_undo_offline() - Undo offline of a previously offlined node.
345  */
346 /*ARGSUSED*/
347 static int
348 vnic_undo_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
349     char **errorp, rcm_info_t **info)
350 {
351 	link_cache_t *node;
352 
353 	rcm_log_message(RCM_TRACE1, "VNIC: online(%s)\n", rsrc);
354 
355 	(void) mutex_lock(&cache_lock);
356 	node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH);
357 	if (node == NULL) {
358 		vnic_log_err(DATALINK_INVALID_LINKID, errorp, "no such link");
359 		(void) mutex_unlock(&cache_lock);
360 		errno = ENOENT;
361 		return (RCM_FAILURE);
362 	}
363 
364 	/* Check if no attempt should be made to online the link here */
365 	if (!(node->vc_state & CACHE_NODE_OFFLINED)) {
366 		vnic_log_err(node->vc_linkid, errorp, "link not offlined");
367 		(void) mutex_unlock(&cache_lock);
368 		errno = ENOTSUP;
369 		return (RCM_SUCCESS);
370 	}
371 
372 	vnic_online_vnic(node);
373 
374 	/*
375 	 * Inform IP interfaces on associated VNICs to be onlined
376 	 */
377 	vnic_consumer_online(hd, node, errorp, flags, info);
378 
379 	node->vc_state &= ~CACHE_NODE_OFFLINED;
380 	rcm_log_message(RCM_TRACE1, "VNIC: online succeeded(%s)\n", rsrc);
381 	(void) mutex_unlock(&cache_lock);
382 	return (RCM_SUCCESS);
383 }
384 
385 static void
386 vnic_online_vnic(link_cache_t *node)
387 {
388 	dl_vnic_t *vnic;
389 	dladm_status_t status;
390 	char errmsg[DLADM_STRSIZE];
391 
392 	/*
393 	 * Try to bring on all offlined VNICs
394 	 */
395 	for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
396 		if (!(vnic->dlv_flags & VNIC_OFFLINED))
397 			continue;
398 
399 		if ((status = dladm_vnic_up(vnic->dlv_vnic_id, 0)) !=
400 		    DLADM_STATUS_OK) {
401 			/*
402 			 * Print a warning message and continue to online
403 			 * other VNICs.
404 			 */
405 			rcm_log_message(RCM_WARNING,
406 			    _("VNIC: VNIC online failed (%u): %s\n"),
407 			    vnic->dlv_vnic_id,
408 			    dladm_status2str(status, errmsg));
409 		} else {
410 			vnic->dlv_flags &= ~VNIC_OFFLINED;
411 		}
412 	}
413 }
414 
415 static int
416 vnic_offline_vnic(link_cache_t *node, uint32_t flags, cache_node_state_t state)
417 {
418 	dl_vnic_t *vnic;
419 	dladm_status_t status;
420 	char errmsg[DLADM_STRSIZE];
421 
422 	rcm_log_message(RCM_TRACE2, "VNIC: vnic_offline_vnic (%s %u %u)\n",
423 	    node->vc_resource, flags, state);
424 
425 	/*
426 	 * Try to delete all explicit created VNIC
427 	 */
428 	for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
429 
430 		if ((status = dladm_vnic_delete(vnic->dlv_vnic_id,
431 		    DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) {
432 			rcm_log_message(RCM_WARNING,
433 			    _("VNIC: VNIC offline failed (%u): %s\n"),
434 			    vnic->dlv_vnic_id,
435 			    dladm_status2str(status, errmsg));
436 			return (RCM_FAILURE);
437 		} else {
438 			rcm_log_message(RCM_TRACE1,
439 			    "VNIC: VNIC offline succeeded(%u)\n",
440 			    vnic->dlv_vnic_id);
441 			vnic->dlv_flags |= flags;
442 		}
443 	}
444 
445 	node->vc_state |= state;
446 	return (RCM_SUCCESS);
447 }
448 
449 /*
450  * vnic_get_info() - Gather usage information for this resource.
451  */
452 /*ARGSUSED*/
453 int
454 vnic_get_info(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
455     char **usagep, char **errorp, nvlist_t *props, rcm_info_t **info)
456 {
457 	link_cache_t *node;
458 
459 	rcm_log_message(RCM_TRACE1, "VNIC: get_info(%s)\n", rsrc);
460 
461 	(void) mutex_lock(&cache_lock);
462 	node = cache_lookup(hd, rsrc, CACHE_REFRESH);
463 	if (node == NULL) {
464 		rcm_log_message(RCM_INFO,
465 		    _("VNIC: get_info(%s) unrecognized resource\n"), rsrc);
466 		(void) mutex_unlock(&cache_lock);
467 		errno = ENOENT;
468 		return (RCM_FAILURE);
469 	}
470 
471 	*usagep = vnic_usage(node);
472 	(void) mutex_unlock(&cache_lock);
473 	if (*usagep == NULL) {
474 		/* most likely malloc failure */
475 		rcm_log_message(RCM_ERROR,
476 		    _("VNIC: get_info(%s) malloc failure\n"), rsrc);
477 		(void) mutex_unlock(&cache_lock);
478 		errno = ENOMEM;
479 		return (RCM_FAILURE);
480 	}
481 
482 	/* Set client/role properties */
483 	(void) nvlist_add_string(props, RCM_CLIENT_NAME, "VNIC");
484 
485 	rcm_log_message(RCM_TRACE1, "VNIC: get_info(%s) info = %s\n",
486 	    rsrc, *usagep);
487 	return (RCM_SUCCESS);
488 }
489 
490 /*
491  * vnic_suspend() - Nothing to do, always okay
492  */
493 /*ARGSUSED*/
494 static int
495 vnic_suspend(rcm_handle_t *hd, char *rsrc, id_t id, timespec_t *interval,
496     uint_t flags, char **errorp, rcm_info_t **info)
497 {
498 	rcm_log_message(RCM_TRACE1, "VNIC: suspend(%s)\n", rsrc);
499 	return (RCM_SUCCESS);
500 }
501 
502 /*
503  * vnic_resume() - Nothing to do, always okay
504  */
505 /*ARGSUSED*/
506 static int
507 vnic_resume(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
508     char **errorp, rcm_info_t **info)
509 {
510 	rcm_log_message(RCM_TRACE1, "VNIC: resume(%s)\n", rsrc);
511 	return (RCM_SUCCESS);
512 }
513 
514 /*
515  * vnic_consumer_remove()
516  *
517  *	Notify VNIC consumers to remove cache.
518  */
519 static int
520 vnic_consumer_remove(rcm_handle_t *hd, link_cache_t *node, uint_t flags,
521     rcm_info_t **info)
522 {
523 	dl_vnic_t *vnic = NULL;
524 	char rsrc[RCM_LINK_RESOURCE_MAX];
525 	int ret = RCM_SUCCESS;
526 
527 	rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_remove (%s)\n",
528 	    node->vc_resource);
529 
530 	for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
531 
532 		/*
533 		 * This will only be called when the offline operation
534 		 * succeeds, so the VNIC consumers must have been offlined
535 		 * at this point.
536 		 */
537 		assert(vnic->dlv_flags & VNIC_CONSUMER_OFFLINED);
538 
539 		(void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u",
540 		    RCM_LINK_PREFIX, vnic->dlv_vnic_id);
541 
542 		ret = rcm_notify_remove(hd, rsrc, flags, info);
543 		if (ret != RCM_SUCCESS) {
544 			rcm_log_message(RCM_WARNING,
545 			    _("VNIC: notify remove failed (%s)\n"), rsrc);
546 			break;
547 		}
548 	}
549 
550 	rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_remove done\n");
551 	return (ret);
552 }
553 
554 /*
555  * vnic_remove() - remove a resource from cache
556  */
557 /*ARGSUSED*/
558 static int
559 vnic_remove(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
560     char **errorp, rcm_info_t **info)
561 {
562 	link_cache_t *node;
563 	int rv;
564 
565 	rcm_log_message(RCM_TRACE1, "VNIC: remove(%s)\n", rsrc);
566 
567 	(void) mutex_lock(&cache_lock);
568 	node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH);
569 	if (node == NULL) {
570 		rcm_log_message(RCM_INFO,
571 		    _("VNIC: remove(%s) unrecognized resource\n"), rsrc);
572 		(void) mutex_unlock(&cache_lock);
573 		errno = ENOENT;
574 		return (RCM_FAILURE);
575 	}
576 
577 	/* remove the cached entry for the resource */
578 	cache_remove(node);
579 	(void) mutex_unlock(&cache_lock);
580 
581 	rv = vnic_consumer_remove(hd, node, flags, info);
582 	node_free(node);
583 	return (rv);
584 }
585 
586 /*
587  * vnic_notify_event - Project private implementation to receive new resource
588  *		   events. It intercepts all new resource events. If the
589  *		   new resource is a network resource, pass up a notify
590  *		   for it too. The new resource need not be cached, since
591  *		   it is done at register again.
592  */
593 /*ARGSUSED*/
594 static int
595 vnic_notify_event(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
596     char **errorp, nvlist_t *nvl, rcm_info_t **info)
597 {
598 	nvpair_t	*nvp = NULL;
599 	datalink_id_t	linkid;
600 	uint64_t	id64;
601 	int		rv = RCM_SUCCESS;
602 
603 	rcm_log_message(RCM_TRACE1, "VNIC: notify_event(%s)\n", rsrc);
604 
605 	if (strcmp(rsrc, RCM_RESOURCE_LINK_NEW) != 0) {
606 		vnic_log_err(DATALINK_INVALID_LINKID, errorp,
607 		    "unrecognized event");
608 		errno = EINVAL;
609 		return (RCM_FAILURE);
610 	}
611 
612 	/* Update cache to reflect latest VNICs */
613 	if (cache_update(hd) < 0) {
614 		vnic_log_err(DATALINK_INVALID_LINKID, errorp,
615 		    "private Cache update failed");
616 		return (RCM_FAILURE);
617 	}
618 
619 	/*
620 	 * Try best to recover all configuration.
621 	 */
622 	rcm_log_message(RCM_DEBUG, "VNIC: process_nvlist\n");
623 	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
624 		if (strcmp(nvpair_name(nvp), RCM_NV_LINKID) != 0)
625 			continue;
626 
627 		if (nvpair_value_uint64(nvp, &id64) != 0) {
628 			vnic_log_err(DATALINK_INVALID_LINKID, errorp,
629 			    "cannot get linkid");
630 			rv = RCM_FAILURE;
631 			continue;
632 		}
633 
634 		linkid = (datalink_id_t)id64;
635 		if (vnic_configure(hd, linkid) != 0) {
636 			vnic_log_err(linkid, errorp, "configuring failed");
637 			rv = RCM_FAILURE;
638 			continue;
639 		}
640 
641 		/* Notify all VNIC consumers */
642 		if (vnic_consumer_notify(hd, linkid, errorp, flags,
643 		    info) != 0) {
644 			vnic_log_err(linkid, errorp, "consumer notify failed");
645 			rv = RCM_FAILURE;
646 		}
647 	}
648 
649 	rcm_log_message(RCM_TRACE1,
650 	    "VNIC: notify_event: link configuration complete\n");
651 	return (rv);
652 }
653 
654 /*
655  * vnic_usage - Determine the usage of a link.
656  *	    The returned buffer is owned by caller, and the caller
657  *	    must free it up when done.
658  */
659 static char *
660 vnic_usage(link_cache_t *node)
661 {
662 	dl_vnic_t *vnic;
663 	int nvnic;
664 	char *buf;
665 	const char *fmt;
666 	char *sep;
667 	char errmsg[DLADM_STRSIZE];
668 	char name[MAXLINKNAMELEN];
669 	dladm_status_t status;
670 	size_t bufsz;
671 
672 	rcm_log_message(RCM_TRACE2, "VNIC: usage(%s)\n", node->vc_resource);
673 
674 	assert(MUTEX_HELD(&cache_lock));
675 	if ((status = dladm_datalink_id2info(node->vc_linkid, NULL, NULL, NULL,
676 	    name, sizeof (name))) != DLADM_STATUS_OK) {
677 		rcm_log_message(RCM_ERROR,
678 		    _("VNIC: usage(%s) get link name failure(%s)\n"),
679 		    node->vc_resource, dladm_status2str(status, errmsg));
680 		return (NULL);
681 	}
682 
683 	if (node->vc_state & CACHE_NODE_OFFLINED)
684 		fmt = _("%1$s offlined");
685 	else
686 		fmt = _("%1$s VNICs: ");
687 
688 	/* TRANSLATION_NOTE: separator used between VNIC linkids */
689 	sep = _(", ");
690 
691 	nvnic = 0;
692 	for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next)
693 		nvnic++;
694 
695 	/* space for VNICs and separators, plus message */
696 	bufsz = nvnic * (MAXLINKNAMELEN + strlen(sep)) +
697 	    strlen(fmt) + MAXLINKNAMELEN + 1;
698 	if ((buf = malloc(bufsz)) == NULL) {
699 		rcm_log_message(RCM_ERROR,
700 		    _("VNIC: usage(%s) malloc failure(%s)\n"),
701 		    node->vc_resource, strerror(errno));
702 		return (NULL);
703 	}
704 	(void) snprintf(buf, bufsz, fmt, name);
705 
706 	if (node->vc_state & CACHE_NODE_OFFLINED) {
707 		/* Nothing else to do */
708 		rcm_log_message(RCM_TRACE2, "VNIC: usage (%s) info = %s\n",
709 		    node->vc_resource, buf);
710 		return (buf);
711 	}
712 
713 	for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
714 		rcm_log_message(RCM_DEBUG, "VNIC:= %u\n", vnic->dlv_vnic_id);
715 
716 		if ((status = dladm_datalink_id2info(vnic->dlv_vnic_id, NULL,
717 		    NULL, NULL, name, sizeof (name))) != DLADM_STATUS_OK) {
718 			rcm_log_message(RCM_ERROR,
719 			    _("VNIC: usage(%s) get vnic %u name failure(%s)\n"),
720 			    node->vc_resource, vnic->dlv_vnic_id,
721 			    dladm_status2str(status, errmsg));
722 			free(buf);
723 			return (NULL);
724 		}
725 
726 		(void) strlcat(buf, name, bufsz);
727 		if (vnic->dlv_next != NULL)
728 			(void) strlcat(buf, sep, bufsz);
729 	}
730 
731 	rcm_log_message(RCM_TRACE2, "VNIC: usage (%s) info = %s\n",
732 	    node->vc_resource, buf);
733 
734 	return (buf);
735 }
736 
737 /*
738  * Cache management routines, all cache management functions should be
739  * be called with cache_lock held.
740  */
741 
742 /*
743  * cache_lookup() - Get a cache node for a resource.
744  *		  Call with cache lock held.
745  *
746  * This ensures that the cache is consistent with the system state and
747  * returns a pointer to the cache element corresponding to the resource.
748  */
749 static link_cache_t *
750 cache_lookup(rcm_handle_t *hd, char *rsrc, char options)
751 {
752 	link_cache_t *node;
753 
754 	rcm_log_message(RCM_TRACE2, "VNIC: cache lookup(%s)\n", rsrc);
755 
756 	assert(MUTEX_HELD(&cache_lock));
757 	if (options & CACHE_REFRESH) {
758 		/* drop lock since update locks cache again */
759 		(void) mutex_unlock(&cache_lock);
760 		(void) cache_update(hd);
761 		(void) mutex_lock(&cache_lock);
762 	}
763 
764 	node = cache_head.vc_next;
765 	for (; node != &cache_tail; node = node->vc_next) {
766 		if (strcmp(rsrc, node->vc_resource) == 0) {
767 			rcm_log_message(RCM_TRACE2,
768 			    "VNIC: cache lookup succeeded(%s)\n", rsrc);
769 			return (node);
770 		}
771 	}
772 	return (NULL);
773 }
774 
775 /*
776  * node_free - Free a node from the cache
777  */
778 static void
779 node_free(link_cache_t *node)
780 {
781 	dl_vnic_t *vnic, *next;
782 
783 	if (node != NULL) {
784 		free(node->vc_resource);
785 
786 		/* free the VNIC list */
787 		for (vnic = node->vc_vnic; vnic != NULL; vnic = next) {
788 			next = vnic->dlv_next;
789 			free(vnic);
790 		}
791 		free(node);
792 	}
793 }
794 
795 /*
796  * cache_insert - Insert a resource node in cache
797  */
798 static void
799 cache_insert(link_cache_t *node)
800 {
801 	assert(MUTEX_HELD(&cache_lock));
802 
803 	/* insert at the head for best performance */
804 	node->vc_next = cache_head.vc_next;
805 	node->vc_prev = &cache_head;
806 
807 	node->vc_next->vc_prev = node;
808 	node->vc_prev->vc_next = node;
809 }
810 
811 /*
812  * cache_remove() - Remove a resource node from cache.
813  */
814 static void
815 cache_remove(link_cache_t *node)
816 {
817 	assert(MUTEX_HELD(&cache_lock));
818 	node->vc_next->vc_prev = node->vc_prev;
819 	node->vc_prev->vc_next = node->vc_next;
820 	node->vc_next = NULL;
821 	node->vc_prev = NULL;
822 }
823 
824 typedef struct vnic_update_arg_s {
825 	rcm_handle_t	*hd;
826 	int		retval;
827 } vnic_update_arg_t;
828 
829 /*
830  * vnic_update() - Update physical interface properties
831  */
832 static int
833 vnic_update(datalink_id_t vnicid, void *arg)
834 {
835 	vnic_update_arg_t *vnic_update_argp = arg;
836 	rcm_handle_t *hd = vnic_update_argp->hd;
837 	link_cache_t *node;
838 	dl_vnic_t *vnic;
839 	char *rsrc;
840 	dladm_vnic_attr_t vnic_attr;
841 	dladm_status_t status;
842 	char errmsg[DLADM_STRSIZE];
843 	boolean_t newnode = B_FALSE;
844 	int ret = -1;
845 
846 	rcm_log_message(RCM_TRACE2, "VNIC: vnic_update(%u)\n", vnicid);
847 
848 	assert(MUTEX_HELD(&cache_lock));
849 	status = dladm_vnic_info(vnicid, &vnic_attr, DLADM_OPT_ACTIVE);
850 	if (status != DLADM_STATUS_OK) {
851 		rcm_log_message(RCM_TRACE1,
852 		    "VNIC: vnic_update() cannot get vnic information for "
853 		    "%u(%s)\n", vnicid, dladm_status2str(status, errmsg));
854 		return (DLADM_WALK_CONTINUE);
855 	}
856 
857 	if (vnic_attr.va_link_id == DATALINK_INVALID_LINKID) {
858 		/*
859 		 * Skip the etherstubs.
860 		 */
861 		rcm_log_message(RCM_TRACE1,
862 		    "VNIC: vnic_update(): skip the etherstub %u\n", vnicid);
863 		return (DLADM_WALK_CONTINUE);
864 	}
865 
866 	rsrc = malloc(RCM_LINK_RESOURCE_MAX);
867 	if (rsrc == NULL) {
868 		rcm_log_message(RCM_ERROR, _("VNIC: malloc error(%s): %u\n"),
869 		    strerror(errno), vnicid);
870 		goto done;
871 	}
872 
873 	(void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u",
874 	    RCM_LINK_PREFIX, vnic_attr.va_link_id);
875 
876 	node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH);
877 	if (node != NULL) {
878 		rcm_log_message(RCM_DEBUG,
879 		    "VNIC: %s already registered (vnicid:%d)\n",
880 		    rsrc, vnic_attr.va_vnic_id);
881 		free(rsrc);
882 	} else {
883 		rcm_log_message(RCM_DEBUG,
884 		    "VNIC: %s is a new resource (vnicid:%d)\n",
885 		    rsrc, vnic_attr.va_vnic_id);
886 		if ((node = calloc(1, sizeof (link_cache_t))) == NULL) {
887 			free(rsrc);
888 			rcm_log_message(RCM_ERROR, _("VNIC: calloc: %s\n"),
889 			    strerror(errno));
890 			goto done;
891 		}
892 
893 		node->vc_resource = rsrc;
894 		node->vc_vnic = NULL;
895 		node->vc_linkid = vnic_attr.va_link_id;
896 		node->vc_state |= CACHE_NODE_NEW;
897 		newnode = B_TRUE;
898 	}
899 
900 	for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
901 		if (vnic->dlv_vnic_id == vnicid) {
902 			vnic->dlv_flags &= ~VNIC_STALE;
903 			break;
904 		}
905 	}
906 
907 	if (vnic == NULL) {
908 		if ((vnic = calloc(1, sizeof (dl_vnic_t))) == NULL) {
909 			rcm_log_message(RCM_ERROR, _("VNIC: malloc: %s\n"),
910 			    strerror(errno));
911 			if (newnode) {
912 				free(rsrc);
913 				free(node);
914 			}
915 			goto done;
916 		}
917 		vnic->dlv_vnic_id = vnicid;
918 		vnic->dlv_next = node->vc_vnic;
919 		vnic->dlv_prev = NULL;
920 		if (node->vc_vnic != NULL)
921 			node->vc_vnic->dlv_prev = vnic;
922 		node->vc_vnic = vnic;
923 	}
924 
925 	node->vc_state &= ~CACHE_NODE_STALE;
926 
927 	if (newnode)
928 		cache_insert(node);
929 
930 	rcm_log_message(RCM_TRACE3, "VNIC: vnic_update: succeeded(%u)\n",
931 	    vnicid);
932 	ret = 0;
933 done:
934 	vnic_update_argp->retval = ret;
935 	return (ret == 0 ? DLADM_WALK_CONTINUE : DLADM_WALK_TERMINATE);
936 }
937 
938 /*
939  * vnic_update_all() - Determine all VNIC links in the system
940  */
941 static int
942 vnic_update_all(rcm_handle_t *hd)
943 {
944 	vnic_update_arg_t arg = {NULL, 0};
945 
946 	rcm_log_message(RCM_TRACE2, "VNIC: vnic_update_all\n");
947 
948 	assert(MUTEX_HELD(&cache_lock));
949 	arg.hd = hd;
950 	(void) dladm_walk_datalink_id(vnic_update, &arg, DATALINK_CLASS_VNIC,
951 	    DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
952 	return (arg.retval);
953 }
954 
955 /*
956  * cache_update() - Update cache with latest interface info
957  */
958 static int
959 cache_update(rcm_handle_t *hd)
960 {
961 	link_cache_t *node, *nnode;
962 	dl_vnic_t *vnic;
963 	int rv;
964 
965 	rcm_log_message(RCM_TRACE2, "VNIC: cache_update\n");
966 
967 	(void) mutex_lock(&cache_lock);
968 
969 	/* first we walk the entire cache, marking each entry stale */
970 	node = cache_head.vc_next;
971 	for (; node != &cache_tail; node = node->vc_next) {
972 		node->vc_state |= CACHE_NODE_STALE;
973 		for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next)
974 			vnic->dlv_flags |= VNIC_STALE;
975 	}
976 
977 	rv = vnic_update_all(hd);
978 
979 	/*
980 	 * Continue to delete all stale nodes from the cache even
981 	 * vnic_update_all() failed. Unregister link that are not offlined
982 	 * and still in cache
983 	 */
984 	for (node = cache_head.vc_next; node != &cache_tail; node = nnode) {
985 		dl_vnic_t *vnic, *next;
986 
987 		for (vnic = node->vc_vnic; vnic != NULL; vnic = next) {
988 			next = vnic->dlv_next;
989 
990 			/* clear stale VNICs */
991 			if (vnic->dlv_flags & VNIC_STALE) {
992 				if (vnic->dlv_prev != NULL)
993 					vnic->dlv_prev->dlv_next = next;
994 				else
995 					node->vc_vnic = next;
996 
997 				if (next != NULL)
998 					next->dlv_prev = vnic->dlv_prev;
999 				free(vnic);
1000 			}
1001 		}
1002 
1003 		nnode = node->vc_next;
1004 		if (node->vc_state & CACHE_NODE_STALE) {
1005 			(void) rcm_unregister_interest(hd, node->vc_resource,
1006 			    0);
1007 			rcm_log_message(RCM_DEBUG, "VNIC: unregistered %s\n",
1008 			    node->vc_resource);
1009 			assert(node->vc_vnic == NULL);
1010 			cache_remove(node);
1011 			node_free(node);
1012 			continue;
1013 		}
1014 
1015 		if (!(node->vc_state & CACHE_NODE_NEW))
1016 			continue;
1017 
1018 		if (rcm_register_interest(hd, node->vc_resource, 0, NULL) !=
1019 		    RCM_SUCCESS) {
1020 			rcm_log_message(RCM_ERROR,
1021 			    _("VNIC: failed to register %s\n"),
1022 			    node->vc_resource);
1023 			rv = -1;
1024 		} else {
1025 			rcm_log_message(RCM_DEBUG, "VNIC: registered %s\n",
1026 			    node->vc_resource);
1027 			node->vc_state &= ~CACHE_NODE_NEW;
1028 		}
1029 	}
1030 
1031 	(void) mutex_unlock(&cache_lock);
1032 	return (rv);
1033 }
1034 
1035 /*
1036  * cache_free() - Empty the cache
1037  */
1038 static void
1039 cache_free()
1040 {
1041 	link_cache_t *node;
1042 
1043 	rcm_log_message(RCM_TRACE2, "VNIC: cache_free\n");
1044 
1045 	(void) mutex_lock(&cache_lock);
1046 	node = cache_head.vc_next;
1047 	while (node != &cache_tail) {
1048 		cache_remove(node);
1049 		node_free(node);
1050 		node = cache_head.vc_next;
1051 	}
1052 	(void) mutex_unlock(&cache_lock);
1053 }
1054 
1055 /*
1056  * vnic_log_err() - RCM error log wrapper
1057  */
1058 static void
1059 vnic_log_err(datalink_id_t linkid, char **errorp, char *errmsg)
1060 {
1061 	char link[MAXLINKNAMELEN];
1062 	char errstr[DLADM_STRSIZE];
1063 	dladm_status_t status;
1064 	int len;
1065 	const char *errfmt;
1066 	char *error;
1067 
1068 	link[0] = '\0';
1069 	if (linkid != DATALINK_INVALID_LINKID) {
1070 		char rsrc[RCM_LINK_RESOURCE_MAX];
1071 
1072 		(void) snprintf(rsrc, sizeof (rsrc), "%s/%u",
1073 		    RCM_LINK_PREFIX, linkid);
1074 
1075 		rcm_log_message(RCM_ERROR, _("VNIC: %s(%s)\n"), errmsg, rsrc);
1076 		if ((status = dladm_datalink_id2info(linkid, NULL, NULL,
1077 		    NULL, link, sizeof (link))) != DLADM_STATUS_OK) {
1078 			rcm_log_message(RCM_WARNING,
1079 			    _("VNIC: cannot get link name for (%s) %s\n"),
1080 			    rsrc, dladm_status2str(status, errstr));
1081 		}
1082 	} else {
1083 		rcm_log_message(RCM_ERROR, _("VNIC: %s\n"), errmsg);
1084 	}
1085 
1086 	errfmt = strlen(link) > 0 ? _("VNIC: %s(%s)") : _("VNIC: %s");
1087 	len = strlen(errfmt) + strlen(errmsg) + MAXLINKNAMELEN + 1;
1088 	if ((error = malloc(len)) != NULL) {
1089 		if (strlen(link) > 0)
1090 			(void) snprintf(error, len, errfmt, errmsg, link);
1091 		else
1092 			(void) snprintf(error, len, errfmt, errmsg);
1093 	}
1094 
1095 	if (errorp != NULL)
1096 		*errorp = error;
1097 }
1098 
1099 /*
1100  * vnic_consumer_online()
1101  *
1102  *	Notify online to VNIC consumers.
1103  */
1104 /* ARGSUSED */
1105 static void
1106 vnic_consumer_online(rcm_handle_t *hd, link_cache_t *node, char **errorp,
1107     uint_t flags, rcm_info_t **info)
1108 {
1109 	dl_vnic_t *vnic;
1110 	char rsrc[RCM_LINK_RESOURCE_MAX];
1111 
1112 	rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_online (%s)\n",
1113 	    node->vc_resource);
1114 
1115 	for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
1116 		if (!(vnic->dlv_flags & VNIC_CONSUMER_OFFLINED))
1117 			continue;
1118 
1119 		(void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u",
1120 		    RCM_LINK_PREFIX, vnic->dlv_vnic_id);
1121 
1122 		if (rcm_notify_online(hd, rsrc, flags, info) == RCM_SUCCESS)
1123 			vnic->dlv_flags &= ~VNIC_CONSUMER_OFFLINED;
1124 	}
1125 
1126 	rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_online done\n");
1127 }
1128 
1129 /*
1130  * vnic_consumer_offline()
1131  *
1132  *	Offline VNIC consumers.
1133  */
1134 static int
1135 vnic_consumer_offline(rcm_handle_t *hd, link_cache_t *node, char **errorp,
1136     uint_t flags, rcm_info_t **info)
1137 {
1138 	dl_vnic_t *vnic;
1139 	char rsrc[RCM_LINK_RESOURCE_MAX];
1140 	int ret = RCM_SUCCESS;
1141 
1142 	rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_offline (%s)\n",
1143 	    node->vc_resource);
1144 
1145 	for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
1146 		(void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u",
1147 		    RCM_LINK_PREFIX, vnic->dlv_vnic_id);
1148 
1149 		ret = rcm_request_offline(hd, rsrc, flags, info);
1150 		if (ret != RCM_SUCCESS)
1151 			break;
1152 
1153 		vnic->dlv_flags |= VNIC_CONSUMER_OFFLINED;
1154 	}
1155 
1156 	if (vnic != NULL)
1157 		vnic_consumer_online(hd, node, errorp, flags, info);
1158 
1159 	rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_offline done\n");
1160 	return (ret);
1161 }
1162 
1163 /*
1164  * Send RCM_RESOURCE_LINK_NEW events to other modules about new VNICs.
1165  * Return 0 on success, -1 on failure.
1166  */
1167 static int
1168 vnic_notify_new_vnic(rcm_handle_t *hd, char *rsrc)
1169 {
1170 	link_cache_t *node;
1171 	dl_vnic_t *vnic;
1172 	nvlist_t *nvl = NULL;
1173 	uint64_t id;
1174 	int ret = -1;
1175 
1176 	rcm_log_message(RCM_TRACE2, "VNIC: vnic_notify_new_vnic (%s)\n", rsrc);
1177 
1178 	(void) mutex_lock(&cache_lock);
1179 	if ((node = cache_lookup(hd, rsrc, CACHE_REFRESH)) == NULL) {
1180 		(void) mutex_unlock(&cache_lock);
1181 		return (0);
1182 	}
1183 
1184 	if (nvlist_alloc(&nvl, 0, 0) != 0) {
1185 		(void) mutex_unlock(&cache_lock);
1186 		rcm_log_message(RCM_WARNING,
1187 		    _("VNIC: failed to allocate nvlist\n"));
1188 		goto done;
1189 	}
1190 
1191 	for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
1192 		rcm_log_message(RCM_TRACE2,
1193 		    "VNIC: vnic_notify_new_vnic add (%u)\n", vnic->dlv_vnic_id);
1194 
1195 		id = vnic->dlv_vnic_id;
1196 		if (nvlist_add_uint64(nvl, RCM_NV_LINKID, id) != 0) {
1197 			rcm_log_message(RCM_ERROR,
1198 			    _("VNIC: failed to construct nvlist\n"));
1199 			(void) mutex_unlock(&cache_lock);
1200 			goto done;
1201 		}
1202 	}
1203 	(void) mutex_unlock(&cache_lock);
1204 
1205 	if (rcm_notify_event(hd, RCM_RESOURCE_LINK_NEW, 0, nvl, NULL) !=
1206 	    RCM_SUCCESS) {
1207 		rcm_log_message(RCM_ERROR,
1208 		    _("VNIC: failed to notify %s event for %s\n"),
1209 		    RCM_RESOURCE_LINK_NEW, node->vc_resource);
1210 		goto done;
1211 	}
1212 
1213 	ret = 0;
1214 done:
1215 	if (nvl != NULL)
1216 		nvlist_free(nvl);
1217 	return (ret);
1218 }
1219 
1220 /*
1221  * vnic_consumer_notify() - Notify consumers of VNICs coming back online.
1222  */
1223 static int
1224 vnic_consumer_notify(rcm_handle_t *hd, datalink_id_t linkid, char **errorp,
1225     uint_t flags, rcm_info_t **info)
1226 {
1227 	char rsrc[RCM_LINK_RESOURCE_MAX];
1228 	link_cache_t *node;
1229 
1230 	/* Check for the interface in the cache */
1231 	(void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u", RCM_LINK_PREFIX,
1232 	    linkid);
1233 
1234 	rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_notify(%s)\n", rsrc);
1235 
1236 	/*
1237 	 * Inform IP consumers of the new link.
1238 	 */
1239 	if (vnic_notify_new_vnic(hd, rsrc) != 0) {
1240 		(void) mutex_lock(&cache_lock);
1241 		if ((node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH)) != NULL) {
1242 			(void) vnic_offline_vnic(node, VNIC_STALE,
1243 			    CACHE_NODE_STALE);
1244 		}
1245 		(void) mutex_unlock(&cache_lock);
1246 		rcm_log_message(RCM_TRACE2,
1247 		    "VNIC: vnic_notify_new_vnic failed(%s)\n", rsrc);
1248 		return (-1);
1249 	}
1250 
1251 	rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_notify succeeded\n");
1252 	return (0);
1253 }
1254 
1255 typedef struct vnic_up_arg_s {
1256 	datalink_id_t	linkid;
1257 	int		retval;
1258 } vnic_up_arg_t;
1259 
1260 static int
1261 vnic_up(datalink_id_t vnicid, void *arg)
1262 {
1263 	vnic_up_arg_t *vnic_up_argp = arg;
1264 	dladm_status_t status;
1265 	dladm_vnic_attr_t vnic_attr;
1266 	char errmsg[DLADM_STRSIZE];
1267 
1268 	status = dladm_vnic_info(vnicid, &vnic_attr, DLADM_OPT_PERSIST);
1269 	if (status != DLADM_STATUS_OK) {
1270 		rcm_log_message(RCM_TRACE1,
1271 		    "VNIC: vnic_up(): cannot get information for VNIC %u "
1272 		    "(%s)\n", vnicid, dladm_status2str(status, errmsg));
1273 		return (DLADM_WALK_CONTINUE);
1274 	}
1275 
1276 	if (vnic_attr.va_link_id != vnic_up_argp->linkid)
1277 		return (DLADM_WALK_CONTINUE);
1278 
1279 	rcm_log_message(RCM_TRACE3, "VNIC: vnic_up(%u)\n", vnicid);
1280 	if ((status = dladm_vnic_up(vnicid, 0)) == DLADM_STATUS_OK)
1281 		return (DLADM_WALK_CONTINUE);
1282 
1283 	/*
1284 	 * Prompt the warning message and continue to UP other VNICs.
1285 	 */
1286 	rcm_log_message(RCM_WARNING,
1287 	    _("VNIC: VNIC up failed (%u): %s\n"),
1288 	    vnicid, dladm_status2str(status, errmsg));
1289 
1290 	vnic_up_argp->retval = -1;
1291 	return (DLADM_WALK_CONTINUE);
1292 }
1293 
1294 /*
1295  * vnic_configure() - Configure VNICs over a physical link after it attaches
1296  */
1297 static int
1298 vnic_configure(rcm_handle_t *hd, datalink_id_t linkid)
1299 {
1300 	char rsrc[RCM_LINK_RESOURCE_MAX];
1301 	link_cache_t *node;
1302 	vnic_up_arg_t arg = {DATALINK_INVALID_LINKID, 0};
1303 
1304 	/* Check for the VNICs in the cache */
1305 	(void) snprintf(rsrc, sizeof (rsrc), "%s/%u", RCM_LINK_PREFIX, linkid);
1306 
1307 	rcm_log_message(RCM_TRACE2, "VNIC: vnic_configure(%s)\n", rsrc);
1308 
1309 	/* Check if the link is new or was previously offlined */
1310 	(void) mutex_lock(&cache_lock);
1311 	if (((node = cache_lookup(hd, rsrc, CACHE_REFRESH)) != NULL) &&
1312 	    (!(node->vc_state & CACHE_NODE_OFFLINED))) {
1313 		rcm_log_message(RCM_TRACE2,
1314 		    "VNIC: Skipping configured interface(%s)\n", rsrc);
1315 		(void) mutex_unlock(&cache_lock);
1316 		return (0);
1317 	}
1318 	(void) mutex_unlock(&cache_lock);
1319 
1320 	arg.linkid = linkid;
1321 	(void) dladm_walk_datalink_id(vnic_up, &arg, DATALINK_CLASS_VNIC,
1322 	    DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
1323 
1324 	if (arg.retval == 0) {
1325 		rcm_log_message(RCM_TRACE2,
1326 		    "VNIC: vnic_configure succeeded(%s)\n", rsrc);
1327 	}
1328 	return (arg.retval);
1329 }
1330