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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25/*
26 * This RCM module adds support to the RCM framework for VNIC links
27 */
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <errno.h>
33#include <sys/types.h>
34#include <synch.h>
35#include <assert.h>
36#include <strings.h>
37#include "rcm_module.h"
38#include <libintl.h>
39#include <libdllink.h>
40#include <libdlvnic.h>
41#include <libdlpi.h>
42
43/*
44 * Definitions
45 */
46#ifndef lint
47#define	_(x)	gettext(x)
48#else
49#define	_(x)	x
50#endif
51
52/* Some generic well-knowns and defaults used in this module */
53#define	RCM_LINK_PREFIX		"SUNW_datalink"	/* RCM datalink name prefix */
54#define	RCM_LINK_RESOURCE_MAX	(13 + LINKID_STR_WIDTH)
55
56/* VNIC link flags */
57typedef enum {
58	VNIC_OFFLINED		= 0x1,
59	VNIC_CONSUMER_OFFLINED	= 0x2,
60	VNIC_STALE		= 0x4
61} vnic_flag_t;
62
63/* link representation */
64typedef struct dl_vnic {
65	struct dl_vnic	*dlv_next;		/* next VNIC on the same link */
66	struct dl_vnic	*dlv_prev;		/* prev VNIC on the same link */
67	datalink_id_t	dlv_vnic_id;
68	vnic_flag_t	dlv_flags;		/* VNIC link flags */
69} dl_vnic_t;
70
71/* VNIC Cache state flags */
72typedef enum {
73	CACHE_NODE_STALE	= 0x1,		/* stale cached data */
74	CACHE_NODE_NEW		= 0x2,		/* new cached nodes */
75	CACHE_NODE_OFFLINED	= 0x4		/* nodes offlined */
76} cache_node_state_t;
77
78/* Network Cache lookup options */
79#define	CACHE_NO_REFRESH	0x1		/* cache refresh not needed */
80#define	CACHE_REFRESH		0x2		/* refresh cache */
81
82/* Cache element */
83typedef struct link_cache {
84	struct link_cache	*vc_next;	/* next cached resource */
85	struct link_cache	*vc_prev;	/* prev cached resource */
86	char			*vc_resource;	/* resource name */
87	datalink_id_t		vc_linkid;	/* linkid */
88	dl_vnic_t		*vc_vnic;	/* VNIC list on this link */
89	cache_node_state_t	vc_state;	/* cache state flags */
90} link_cache_t;
91
92/*
93 * Global cache for network VNICs
94 */
95static link_cache_t	cache_head;
96static link_cache_t	cache_tail;
97static mutex_t		cache_lock;
98static int		events_registered = 0;
99
100static dladm_handle_t	dld_handle = NULL;
101
102/*
103 * RCM module interface prototypes
104 */
105static int		vnic_register(rcm_handle_t *);
106static int		vnic_unregister(rcm_handle_t *);
107static int		vnic_get_info(rcm_handle_t *, char *, id_t, uint_t,
108			    char **, char **, nvlist_t *, rcm_info_t **);
109static int		vnic_suspend(rcm_handle_t *, char *, id_t,
110			    timespec_t *, uint_t, char **, rcm_info_t **);
111static int		vnic_resume(rcm_handle_t *, char *, id_t, uint_t,
112			    char **, rcm_info_t **);
113static int		vnic_offline(rcm_handle_t *, char *, id_t, uint_t,
114			    char **, rcm_info_t **);
115static int		vnic_undo_offline(rcm_handle_t *, char *, id_t, uint_t,
116			    char **, rcm_info_t **);
117static int		vnic_remove(rcm_handle_t *, char *, id_t, uint_t,
118			    char **, rcm_info_t **);
119static int		vnic_notify_event(rcm_handle_t *, char *, id_t, uint_t,
120			    char **, nvlist_t *, rcm_info_t **);
121static int		vnic_configure(rcm_handle_t *, datalink_id_t);
122
123/* Module private routines */
124static void 		cache_free();
125static int 		cache_update(rcm_handle_t *);
126static void 		cache_remove(link_cache_t *);
127static void 		node_free(link_cache_t *);
128static void 		cache_insert(link_cache_t *);
129static link_cache_t	*cache_lookup(rcm_handle_t *, char *, char);
130static int		vnic_consumer_offline(rcm_handle_t *, link_cache_t *,
131			    char **, uint_t, rcm_info_t **);
132static void		vnic_consumer_online(rcm_handle_t *, link_cache_t *,
133			    char **, uint_t, rcm_info_t **);
134static int		vnic_offline_vnic(link_cache_t *, uint32_t,
135			    cache_node_state_t);
136static void		vnic_online_vnic(link_cache_t *);
137static char 		*vnic_usage(link_cache_t *);
138static void 		vnic_log_err(datalink_id_t, char **, char *);
139static int		vnic_consumer_notify(rcm_handle_t *, datalink_id_t,
140			    char **, uint_t, rcm_info_t **);
141
142/* Module-Private data */
143static struct rcm_mod_ops vnic_ops =
144{
145	RCM_MOD_OPS_VERSION,
146	vnic_register,
147	vnic_unregister,
148	vnic_get_info,
149	vnic_suspend,
150	vnic_resume,
151	vnic_offline,
152	vnic_undo_offline,
153	vnic_remove,
154	NULL,
155	NULL,
156	vnic_notify_event
157};
158
159/*
160 * rcm_mod_init() - Update registrations, and return the ops structure.
161 */
162struct rcm_mod_ops *
163rcm_mod_init(void)
164{
165	char errmsg[DLADM_STRSIZE];
166	dladm_status_t status;
167
168	rcm_log_message(RCM_TRACE1, "VNIC: mod_init\n");
169
170	cache_head.vc_next = &cache_tail;
171	cache_head.vc_prev = NULL;
172	cache_tail.vc_prev = &cache_head;
173	cache_tail.vc_next = NULL;
174	(void) mutex_init(&cache_lock, 0, NULL);
175
176	if ((status = dladm_open(&dld_handle)) != DLADM_STATUS_OK) {
177		rcm_log_message(RCM_WARNING,
178		    "VNIC: mod_init failed: cannot open datalink handle: %s\n",
179		    dladm_status2str(status, errmsg));
180		return (NULL);
181	}
182
183	/* Return the ops vectors */
184	return (&vnic_ops);
185}
186
187/*
188 * rcm_mod_info() - Return a string describing this module.
189 */
190const char *
191rcm_mod_info(void)
192{
193	rcm_log_message(RCM_TRACE1, "VNIC: mod_info\n");
194
195	return ("VNIC module");
196}
197
198/*
199 * rcm_mod_fini() - Destroy the network VNIC cache.
200 */
201int
202rcm_mod_fini(void)
203{
204	rcm_log_message(RCM_TRACE1, "VNIC: mod_fini\n");
205
206	/*
207	 * Note that vnic_unregister() does not seem to be called anywhere,
208	 * therefore we free the cache nodes here. In theory we should call
209	 * rcm_register_interest() for each node before we free it, the
210	 * framework does not provide the rcm_handle to allow us to do so.
211	 */
212	cache_free();
213	(void) mutex_destroy(&cache_lock);
214
215	dladm_close(dld_handle);
216	return (RCM_SUCCESS);
217}
218
219/*
220 * vnic_register() - Make sure the cache is properly sync'ed, and its
221 *		 registrations are in order.
222 */
223static int
224vnic_register(rcm_handle_t *hd)
225{
226	rcm_log_message(RCM_TRACE1, "VNIC: register\n");
227
228	if (cache_update(hd) < 0)
229		return (RCM_FAILURE);
230
231	/*
232	 * Need to register interest in all new resources
233	 * getting attached, so we get attach event notifications
234	 */
235	if (!events_registered) {
236		if (rcm_register_event(hd, RCM_RESOURCE_LINK_NEW, 0, NULL)
237		    != RCM_SUCCESS) {
238			rcm_log_message(RCM_ERROR,
239			    _("VNIC: failed to register %s\n"),
240			    RCM_RESOURCE_LINK_NEW);
241			return (RCM_FAILURE);
242		} else {
243			rcm_log_message(RCM_DEBUG, "VNIC: registered %s\n",
244			    RCM_RESOURCE_LINK_NEW);
245			events_registered++;
246		}
247	}
248
249	return (RCM_SUCCESS);
250}
251
252/*
253 * vnic_unregister() - Walk the cache, unregistering all the networks.
254 */
255static int
256vnic_unregister(rcm_handle_t *hd)
257{
258	link_cache_t *node;
259
260	rcm_log_message(RCM_TRACE1, "VNIC: unregister\n");
261
262	/* Walk the cache, unregistering everything */
263	(void) mutex_lock(&cache_lock);
264	node = cache_head.vc_next;
265	while (node != &cache_tail) {
266		if (rcm_unregister_interest(hd, node->vc_resource, 0)
267		    != RCM_SUCCESS) {
268			rcm_log_message(RCM_ERROR,
269			    _("VNIC: failed to unregister %s\n"),
270			    node->vc_resource);
271			(void) mutex_unlock(&cache_lock);
272			return (RCM_FAILURE);
273		}
274		cache_remove(node);
275		node_free(node);
276		node = cache_head.vc_next;
277	}
278	(void) mutex_unlock(&cache_lock);
279
280	/*
281	 * Unregister interest in all new resources
282	 */
283	if (events_registered) {
284		if (rcm_unregister_event(hd, RCM_RESOURCE_LINK_NEW, 0)
285		    != RCM_SUCCESS) {
286			rcm_log_message(RCM_ERROR,
287			    _("VNIC: failed to unregister %s\n"),
288			    RCM_RESOURCE_LINK_NEW);
289			return (RCM_FAILURE);
290		} else {
291			rcm_log_message(RCM_DEBUG, "VNIC: unregistered %s\n",
292			    RCM_RESOURCE_LINK_NEW);
293			events_registered--;
294		}
295	}
296
297	return (RCM_SUCCESS);
298}
299
300/*
301 * vnic_offline() - Offline VNICs on a specific node.
302 */
303static int
304vnic_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
305    char **errorp, rcm_info_t **info)
306{
307	link_cache_t *node;
308
309	rcm_log_message(RCM_TRACE1, "VNIC: offline(%s)\n", rsrc);
310
311	/* Lock the cache and lookup the resource */
312	(void) mutex_lock(&cache_lock);
313	node = cache_lookup(hd, rsrc, CACHE_REFRESH);
314	if (node == NULL) {
315		/* should not happen because the resource is registered. */
316		vnic_log_err(DATALINK_INVALID_LINKID, errorp,
317		    "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*/
361static int
362vnic_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
399static void
400vnic_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
429static int
430vnic_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*/
467int
468vnic_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*/
508static int
509vnic_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*/
520static int
521vnic_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 */
533static int
534vnic_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*/
572static int
573vnic_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*/
608static int
609vnic_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 */
673static char *
674vnic_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 */
764static link_cache_t *
765cache_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 */
793static void
794node_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 */
813static void
814cache_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 */
829static void
830cache_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
839typedef 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 */
847static int
848vnic_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;
948done:
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 */
956static int
957vnic_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 */
973static int
974cache_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 */
1053static void
1054cache_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 */
1073static void
1074vnic_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 */
1120static void
1121vnic_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 */
1149static int
1150vnic_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 */
1182static int
1183vnic_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;
1229done:
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 */
1238static int
1239vnic_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
1270typedef struct vnic_up_arg_s {
1271	datalink_id_t	linkid;
1272	int		retval;
1273} vnic_up_arg_t;
1274
1275static int
1276vnic_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 */
1312static int
1313vnic_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