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