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