bridge_rcm.c revision 10491:8893b747ecdf
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 Bridge 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 <libdlbridge.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/* Bridge Cache state flags */
58typedef enum {
59	CACHE_NODE_STALE	= 0x1,		/* stale cached data */
60	CACHE_NODE_NEW		= 0x2,		/* new cached nodes */
61	CACHE_NODE_OFFLINED	= 0x4		/* nodes offlined */
62} cache_node_state_t;
63
64/* Network Cache lookup options */
65#define	CACHE_NO_REFRESH	0x1		/* cache refresh not needed */
66#define	CACHE_REFRESH		0x2		/* refresh cache */
67
68/* Cache element */
69typedef struct link_cache {
70	struct link_cache	*vc_next;	/* next cached resource */
71	struct link_cache	*vc_prev;	/* prev cached resource */
72	char			*vc_resource;	/* resource name */
73	datalink_id_t		vc_linkid;	/* linkid */
74	cache_node_state_t	vc_state;	/* cache state flags */
75	char			vc_bridge[MAXLINKNAMELEN];
76} link_cache_t;
77
78/*
79 * Global cache for network Bridges
80 */
81static link_cache_t	cache_head;
82static link_cache_t	cache_tail;
83static mutex_t		cache_lock;
84static boolean_t	events_registered = B_FALSE;
85
86static dladm_handle_t	dld_handle = NULL;
87
88/*
89 * RCM module interface prototypes
90 */
91static int		bridge_register(rcm_handle_t *);
92static int		bridge_unregister(rcm_handle_t *);
93static int		bridge_get_info(rcm_handle_t *, char *, id_t, uint_t,
94			    char **, char **, nvlist_t *, rcm_info_t **);
95static int		bridge_suspend(rcm_handle_t *, char *, id_t,
96			    timespec_t *, uint_t, char **, rcm_info_t **);
97static int		bridge_resume(rcm_handle_t *, char *, id_t, uint_t,
98			    char **, rcm_info_t **);
99static int		bridge_offline(rcm_handle_t *, char *, id_t, uint_t,
100			    char **, rcm_info_t **);
101static int		bridge_undo_offline(rcm_handle_t *, char *, id_t,
102			    uint_t, char **, rcm_info_t **);
103static int		bridge_remove(rcm_handle_t *, char *, id_t, uint_t,
104			    char **, rcm_info_t **);
105static int		bridge_notify_event(rcm_handle_t *, char *, id_t,
106			    uint_t, char **, nvlist_t *, rcm_info_t **);
107static int		bridge_configure(rcm_handle_t *, datalink_id_t);
108
109/* Module private routines */
110static void 		cache_free(void);
111static int 		cache_update(rcm_handle_t *);
112static void 		cache_remove(link_cache_t *);
113static void 		node_free(link_cache_t *);
114static void 		cache_insert(link_cache_t *);
115static link_cache_t	*cache_lookup(rcm_handle_t *, char *, uint_t);
116static char 		*bridge_usage(link_cache_t *);
117static void 		bridge_log_err(datalink_id_t, char **, char *);
118
119/* Module-Private data */
120static struct rcm_mod_ops bridge_ops =
121{
122	RCM_MOD_OPS_VERSION,
123	bridge_register,
124	bridge_unregister,
125	bridge_get_info,
126	bridge_suspend,
127	bridge_resume,
128	bridge_offline,
129	bridge_undo_offline,
130	bridge_remove,
131	NULL,
132	NULL,
133	bridge_notify_event
134};
135
136/*
137 * rcm_mod_init() - Update registrations, and return the ops structure.
138 */
139struct rcm_mod_ops *
140rcm_mod_init(void)
141{
142	dladm_status_t status;
143	char errmsg[DLADM_STRSIZE];
144
145	rcm_log_message(RCM_TRACE1, "Bridge: mod_init\n");
146
147	cache_head.vc_next = &cache_tail;
148	cache_head.vc_prev = NULL;
149	cache_tail.vc_prev = &cache_head;
150	cache_tail.vc_next = NULL;
151	(void) mutex_init(&cache_lock, 0, NULL);
152
153	if ((status = dladm_open(&dld_handle)) != DLADM_STATUS_OK) {
154		rcm_log_message(RCM_WARNING,
155		    "Bridge: cannot open datalink handle: %s\n",
156		    dladm_status2str(status, errmsg));
157		return (NULL);
158	}
159
160	/* Return the ops vectors */
161	return (&bridge_ops);
162}
163
164/*
165 * rcm_mod_info() - Return a string describing this module.
166 */
167const char *
168rcm_mod_info(void)
169{
170	rcm_log_message(RCM_TRACE1, "Bridge: mod_info\n");
171
172	return ("Bridge module version 1.0");
173}
174
175/*
176 * rcm_mod_fini() - Destroy the network Bridge cache.
177 */
178int
179rcm_mod_fini(void)
180{
181	rcm_log_message(RCM_TRACE1, "Bridge: mod_fini\n");
182
183	/*
184	 * Note that bridge_unregister() does not seem to be called anywhere,
185	 * therefore we free the cache nodes here. In theory we should call
186	 * rcm_register_interest() for each node before we free it, but the
187	 * framework does not provide the rcm_handle to allow us to do so.
188	 */
189	cache_free();
190	(void) mutex_destroy(&cache_lock);
191
192	dladm_close(dld_handle);
193	return (RCM_SUCCESS);
194}
195
196/*
197 * bridge_register() - Make sure the cache is properly sync'ed, and its
198 *		       registrations are in order.
199 */
200static int
201bridge_register(rcm_handle_t *hd)
202{
203	int retv;
204
205	rcm_log_message(RCM_TRACE1, "Bridge: register\n");
206
207	if ((retv = cache_update(hd)) != RCM_SUCCESS)
208		return (retv);
209
210	/*
211	 * Need to register interest in all new resources
212	 * getting attached, so we get attach event notifications
213	 */
214	if (!events_registered) {
215		retv = rcm_register_event(hd, RCM_RESOURCE_LINK_NEW, 0, NULL);
216		if (retv != RCM_SUCCESS) {
217			rcm_log_message(RCM_ERROR,
218			    _("Bridge: failed to register %s\n"),
219			    RCM_RESOURCE_LINK_NEW);
220		} else {
221			rcm_log_message(RCM_DEBUG, "Bridge: registered %s\n",
222			    RCM_RESOURCE_LINK_NEW);
223			events_registered = B_TRUE;
224		}
225	}
226
227	return (retv);
228}
229
230/*
231 * bridge_unregister() - Walk the cache, unregistering all the links.
232 */
233static int
234bridge_unregister(rcm_handle_t *hd)
235{
236	link_cache_t *node;
237	int retv = RCM_SUCCESS;
238
239	rcm_log_message(RCM_TRACE1, "Bridge: unregister\n");
240
241	/* Walk the cache, unregistering everything */
242	(void) mutex_lock(&cache_lock);
243	node = cache_head.vc_next;
244	while (node != &cache_tail) {
245		retv = rcm_unregister_interest(hd, node->vc_resource, 0);
246		if (retv != RCM_SUCCESS)
247			break;
248		cache_remove(node);
249		node_free(node);
250		node = cache_head.vc_next;
251	}
252	(void) mutex_unlock(&cache_lock);
253	if (retv != RCM_SUCCESS) {
254		rcm_log_message(RCM_ERROR,
255		    _("Bridge: failed to unregister %s\n"), node->vc_resource);
256		return (retv);
257	}
258
259	/*
260	 * Unregister interest in all new resources
261	 */
262	if (events_registered) {
263		retv = rcm_unregister_event(hd, RCM_RESOURCE_LINK_NEW, 0);
264		if (retv != RCM_SUCCESS) {
265			rcm_log_message(RCM_ERROR,
266			    _("Bridge: failed to unregister %s\n"),
267			    RCM_RESOURCE_LINK_NEW);
268		} else {
269			rcm_log_message(RCM_DEBUG, "Bridge: unregistered %s\n",
270			    RCM_RESOURCE_LINK_NEW);
271			events_registered = B_FALSE;
272		}
273	}
274
275	return (retv);
276}
277
278/*
279 * bridge_offline() - Offline the bridge on a specific link.
280 */
281static int
282bridge_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
283    char **errorp, rcm_info_t **info)
284{
285	link_cache_t *node;
286	dladm_status_t status;
287
288	rcm_log_message(RCM_TRACE1, "Bridge: offline(%s)\n", rsrc);
289
290	/* Lock the cache and lookup the resource */
291	(void) mutex_lock(&cache_lock);
292	node = cache_lookup(hd, rsrc, CACHE_REFRESH);
293	if (node == NULL) {
294		/* should not happen because the resource is registered. */
295		bridge_log_err(DATALINK_INVALID_LINKID, errorp,
296		    "unrecognized resource");
297		(void) mutex_unlock(&cache_lock);
298		return (RCM_SUCCESS);
299	}
300
301	/* Check if it's a query */
302	if (flags & RCM_QUERY) {
303		rcm_log_message(RCM_TRACE1,
304		    "Bridge: offline query succeeded(%s)\n", rsrc);
305		(void) mutex_unlock(&cache_lock);
306		return (RCM_SUCCESS);
307	}
308
309	status = dladm_bridge_setlink(dld_handle, node->vc_linkid, "");
310	if (status != DLADM_STATUS_OK) {
311		bridge_log_err(node->vc_linkid, errorp, "offline failed");
312		(void) mutex_unlock(&cache_lock);
313		return (RCM_FAILURE);
314	}
315
316	node->vc_state |= CACHE_NODE_OFFLINED;
317
318	rcm_log_message(RCM_TRACE1, "Bridge: Offline succeeded(%s %s)\n", rsrc,
319	    node->vc_bridge);
320	(void) mutex_unlock(&cache_lock);
321	return (RCM_SUCCESS);
322}
323
324/*
325 * bridge_undo_offline() - Undo offline of a previously offlined node.
326 */
327/*ARGSUSED*/
328static int
329bridge_undo_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
330    char **errorp, rcm_info_t **info)
331{
332	link_cache_t *node;
333	dladm_status_t status;
334	char errmsg[DLADM_STRSIZE];
335
336	rcm_log_message(RCM_TRACE1, "Bridge: online(%s)\n", rsrc);
337
338	(void) mutex_lock(&cache_lock);
339	node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH);
340	if (node == NULL) {
341		bridge_log_err(DATALINK_INVALID_LINKID, errorp, "no such link");
342		(void) mutex_unlock(&cache_lock);
343		errno = ENOENT;
344		return (RCM_FAILURE);
345	}
346
347	/* Check if no attempt should be made to online the link here */
348	if (!(node->vc_state & CACHE_NODE_OFFLINED)) {
349		bridge_log_err(node->vc_linkid, errorp, "link not offlined");
350		(void) mutex_unlock(&cache_lock);
351		errno = ENOTSUP;
352		return (RCM_SUCCESS);
353	}
354
355	/*
356	 * Try to bring on an offlined bridge link.
357	 */
358	status = dladm_bridge_setlink(dld_handle, node->vc_linkid,
359	    node->vc_bridge);
360	if (status != DLADM_STATUS_OK) {
361		/*
362		 * Print a warning message.
363		 */
364		rcm_log_message(RCM_WARNING,
365		    _("Bridge: Bridge online failed %u %s: %s\n"),
366		    node->vc_linkid, node->vc_bridge,
367		    dladm_status2str(status, errmsg));
368	}
369
370	node->vc_state &= ~CACHE_NODE_OFFLINED;
371	rcm_log_message(RCM_TRACE1, "Bridge: online succeeded(%s)\n", rsrc);
372	(void) mutex_unlock(&cache_lock);
373	return (RCM_SUCCESS);
374}
375
376/*
377 * bridge_get_info() - Gather usage information for this resource.
378 */
379/*ARGSUSED*/
380int
381bridge_get_info(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
382    char **usagep, char **errorp, nvlist_t *props, rcm_info_t **info)
383{
384	link_cache_t *node;
385
386	rcm_log_message(RCM_TRACE1, "Bridge: get_info(%s)\n", rsrc);
387
388	(void) mutex_lock(&cache_lock);
389	node = cache_lookup(hd, rsrc, CACHE_REFRESH);
390	if (node == NULL) {
391		rcm_log_message(RCM_INFO,
392		    _("Bridge: get_info(%s) unrecognized resource\n"), rsrc);
393		(void) mutex_unlock(&cache_lock);
394		errno = ENOENT;
395		return (RCM_FAILURE);
396	}
397
398	*usagep = bridge_usage(node);
399	(void) mutex_unlock(&cache_lock);
400	if (*usagep == NULL) {
401		/* most likely malloc failure */
402		rcm_log_message(RCM_ERROR,
403		    _("Bridge: get_info(%s) malloc failure\n"), rsrc);
404		(void) mutex_unlock(&cache_lock);
405		errno = ENOMEM;
406		return (RCM_FAILURE);
407	}
408
409	/* Set client/role properties */
410	(void) nvlist_add_string(props, RCM_CLIENT_NAME, "Bridge");
411
412	rcm_log_message(RCM_TRACE1, "Bridge: get_info(%s) info = %s\n",
413	    rsrc, *usagep);
414	return (RCM_SUCCESS);
415}
416
417/*
418 * bridge_suspend() - Nothing to do, always okay
419 */
420/*ARGSUSED*/
421static int
422bridge_suspend(rcm_handle_t *hd, char *rsrc, id_t id, timespec_t *interval,
423    uint_t flags, char **errorp, rcm_info_t **info)
424{
425	rcm_log_message(RCM_TRACE1, "Bridge: suspend(%s)\n", rsrc);
426	return (RCM_SUCCESS);
427}
428
429/*
430 * bridge_resume() - Nothing to do, always okay
431 */
432/*ARGSUSED*/
433static int
434bridge_resume(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
435    char **errorp, rcm_info_t **info)
436{
437	rcm_log_message(RCM_TRACE1, "Bridge: resume(%s)\n", rsrc);
438	return (RCM_SUCCESS);
439}
440
441/*
442 * bridge_remove() - remove a resource from cache
443 */
444/*ARGSUSED*/
445static int
446bridge_remove(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
447    char **errorp, rcm_info_t **info)
448{
449	link_cache_t *node;
450
451	rcm_log_message(RCM_TRACE1, "Bridge: remove(%s)\n", rsrc);
452
453	(void) mutex_lock(&cache_lock);
454	node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH);
455	if (node == NULL) {
456		rcm_log_message(RCM_INFO,
457		    _("Bridge: remove(%s) unrecognized resource\n"), rsrc);
458		(void) mutex_unlock(&cache_lock);
459		errno = ENOENT;
460		return (RCM_FAILURE);
461	}
462
463	/* remove the cached entry for the resource */
464	rcm_log_message(RCM_TRACE2,
465	    "Bridge: remove succeeded(%s, %s)\n", rsrc, node->vc_bridge);
466	cache_remove(node);
467	(void) mutex_unlock(&cache_lock);
468
469	node_free(node);
470	return (RCM_SUCCESS);
471}
472
473/*
474 * bridge_notify_event - Project private implementation to receive new resource
475 *		   events. It intercepts all new resource events. If the
476 *		   new resource is a network resource, pass up a notify
477 *		   for it too. The new resource need not be cached, since
478 *		   it is done at register again.
479 */
480/*ARGSUSED*/
481static int
482bridge_notify_event(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
483    char **errorp, nvlist_t *nvl, rcm_info_t **info)
484{
485	nvpair_t	*nvp = NULL;
486	datalink_id_t	linkid;
487	uint64_t	id64;
488	int		rv, lastrv;
489
490	rcm_log_message(RCM_TRACE1, "Bridge: notify_event(%s)\n", rsrc);
491
492	if (strcmp(rsrc, RCM_RESOURCE_LINK_NEW) != 0) {
493		bridge_log_err(DATALINK_INVALID_LINKID, errorp,
494		    "unrecognized event");
495		errno = EINVAL;
496		return (RCM_FAILURE);
497	}
498
499	/* Update cache to reflect latest Bridges */
500	if ((lastrv = cache_update(hd)) != RCM_SUCCESS) {
501		bridge_log_err(DATALINK_INVALID_LINKID, errorp,
502		    "private Cache update failed");
503		return (lastrv);
504	}
505
506	/*
507	 * Try best to recover all configuration.
508	 */
509	rcm_log_message(RCM_DEBUG, "Bridge: process_nvlist\n");
510	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
511		if (strcmp(nvpair_name(nvp), RCM_NV_LINKID) != 0)
512			continue;
513
514		if (nvpair_value_uint64(nvp, &id64) != 0) {
515			bridge_log_err(DATALINK_INVALID_LINKID, errorp,
516			    "cannot get linkid");
517			lastrv = RCM_FAILURE;
518			continue;
519		}
520
521		linkid = (datalink_id_t)id64;
522		if ((rv = bridge_configure(hd, linkid)) != RCM_SUCCESS) {
523			bridge_log_err(linkid, errorp, "configuring failed");
524			lastrv = rv;
525		}
526	}
527
528	rcm_log_message(RCM_TRACE1,
529	    "Bridge: notify_event: link configuration complete\n");
530	return (lastrv);
531}
532
533/*
534 * bridge_usage - Determine the usage of a link.
535 *	    The returned buffer is owned by caller, and the caller
536 *	    must free it up when done.
537 */
538static char *
539bridge_usage(link_cache_t *node)
540{
541	char *buf;
542	const char *fmt;
543	char errmsg[DLADM_STRSIZE];
544	char name[MAXLINKNAMELEN];
545	char bridge[MAXLINKNAMELEN];
546	dladm_status_t status;
547
548	rcm_log_message(RCM_TRACE2, "Bridge: usage(%s)\n", node->vc_resource);
549
550	assert(MUTEX_HELD(&cache_lock));
551
552	status = dladm_datalink_id2info(dld_handle, node->vc_linkid, NULL,
553	    NULL, NULL, name, sizeof (name));
554
555	if (status != DLADM_STATUS_OK) {
556		rcm_log_message(RCM_ERROR,
557		    _("Bridge: usage(%s) get link name failure(%s)\n"),
558		    node->vc_resource, dladm_status2str(status, errmsg));
559		return (NULL);
560	}
561
562	(void) dladm_bridge_getlink(dld_handle, node->vc_linkid, bridge,
563	    sizeof (bridge));
564
565	if (node->vc_state & CACHE_NODE_OFFLINED)
566		fmt = _("%1$s offlined");
567	else if (bridge[0] == '\0')
568		fmt = _("%1$s not bridged");
569	else
570		fmt = _("%1$s bridge: %2$s");
571
572	(void) asprintf(&buf, fmt, name, bridge);
573
574	rcm_log_message(RCM_TRACE2, "Bridge: usage (%s) info = %s\n",
575	    node->vc_resource, buf);
576
577	return (buf);
578}
579
580/*
581 * Cache management routines, all cache management functions should be
582 * be called with cache_lock held.
583 */
584
585/*
586 * cache_lookup() - Get a cache node for a resource.
587 *		  Call with cache lock held.
588 *
589 * This ensures that the cache is consistent with the system state and
590 * returns a pointer to the cache element corresponding to the resource.
591 */
592static link_cache_t *
593cache_lookup(rcm_handle_t *hd, char *rsrc, uint_t options)
594{
595	link_cache_t *node;
596
597	rcm_log_message(RCM_TRACE2, "Bridge: cache lookup(%s)\n", rsrc);
598
599	assert(MUTEX_HELD(&cache_lock));
600	if (options & CACHE_REFRESH) {
601		/* drop lock since update locks cache again */
602		(void) mutex_unlock(&cache_lock);
603		(void) cache_update(hd);
604		(void) mutex_lock(&cache_lock);
605	}
606
607	node = cache_head.vc_next;
608	for (; node != &cache_tail; node = node->vc_next) {
609		if (strcmp(rsrc, node->vc_resource) == 0) {
610			rcm_log_message(RCM_TRACE2,
611			    "Bridge: cache lookup succeeded(%s, %s)\n", rsrc,
612			    node->vc_bridge);
613			return (node);
614		}
615	}
616	return (NULL);
617}
618
619/*
620 * node_free - Free a node from the cache
621 */
622static void
623node_free(link_cache_t *node)
624{
625	if (node != NULL) {
626		free(node->vc_resource);
627		free(node);
628	}
629}
630
631/*
632 * cache_insert - Insert a resource node in cache
633 */
634static void
635cache_insert(link_cache_t *node)
636{
637	assert(MUTEX_HELD(&cache_lock));
638
639	/* insert at the head for best performance */
640	node->vc_next = cache_head.vc_next;
641	node->vc_prev = &cache_head;
642
643	node->vc_next->vc_prev = node;
644	node->vc_prev->vc_next = node;
645}
646
647/*
648 * cache_remove() - Remove a resource node from cache.
649 */
650static void
651cache_remove(link_cache_t *node)
652{
653	assert(MUTEX_HELD(&cache_lock));
654	node->vc_next->vc_prev = node->vc_prev;
655	node->vc_prev->vc_next = node->vc_next;
656	node->vc_next = NULL;
657	node->vc_prev = NULL;
658}
659
660typedef struct bridge_update_arg_s {
661	rcm_handle_t	*hd;
662	int		retval;
663} bridge_update_arg_t;
664
665/*
666 * bridge_update() - Update physical interface properties
667 */
668static int
669bridge_update(dladm_handle_t handle, datalink_id_t linkid, void *arg)
670{
671	bridge_update_arg_t *bua = arg;
672	rcm_handle_t *hd = bua->hd;
673	link_cache_t *node;
674	char *rsrc;
675	dladm_status_t status;
676	char errmsg[DLADM_STRSIZE];
677	char bridge[MAXLINKNAMELEN];
678	int ret = RCM_FAILURE;
679
680	rcm_log_message(RCM_TRACE2, "Bridge: bridge_update(%u)\n", linkid);
681
682	assert(MUTEX_HELD(&cache_lock));
683	status = dladm_bridge_getlink(dld_handle, linkid, bridge,
684	    sizeof (bridge));
685	if (status != DLADM_STATUS_OK) {
686		rcm_log_message(RCM_TRACE1,
687		    "Bridge: no bridge information for %u (%s)\n",
688		    linkid, dladm_status2str(status, errmsg));
689		return (DLADM_WALK_CONTINUE);
690	}
691
692	(void) asprintf(&rsrc, "%s/%u", RCM_LINK_PREFIX, linkid);
693	if (rsrc == NULL) {
694		rcm_log_message(RCM_ERROR,
695		    _("Bridge: allocation failure: %s %u: %s\n"),
696		    bridge, linkid, strerror(errno));
697		goto done;
698	}
699
700	node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH);
701	if (node != NULL) {
702		rcm_log_message(RCM_DEBUG, "Bridge: %s already registered\n",
703		    rsrc);
704		free(rsrc);
705		node->vc_state &= ~CACHE_NODE_STALE;
706	} else {
707		rcm_log_message(RCM_DEBUG,
708		    "Bridge: %s is a new resource (bridge %s)\n",
709		    rsrc, bridge);
710		if ((node = calloc(1, sizeof (link_cache_t))) == NULL) {
711			free(rsrc);
712			rcm_log_message(RCM_ERROR, _("Bridge: calloc: %s\n"),
713			    strerror(errno));
714			goto done;
715		}
716
717		node->vc_resource = rsrc;
718		node->vc_linkid = linkid;
719		(void) strlcpy(node->vc_bridge, bridge,
720		    sizeof (node->vc_bridge));
721		node->vc_state |= CACHE_NODE_NEW;
722		cache_insert(node);
723	}
724
725	rcm_log_message(RCM_TRACE3, "Bridge: bridge_update: succeeded(%u %s)\n",
726	    linkid, node->vc_bridge);
727	ret = RCM_SUCCESS;
728done:
729	bua->retval = ret;
730	return (ret == RCM_SUCCESS ? DLADM_WALK_CONTINUE :
731	    DLADM_WALK_TERMINATE);
732}
733
734/*
735 * cache_update() - Update cache with latest interface info
736 */
737static int
738cache_update(rcm_handle_t *hd)
739{
740	link_cache_t *node, *nnode;
741	int rv, lastrv;
742	bridge_update_arg_t bua;
743
744	rcm_log_message(RCM_TRACE2, "Bridge: cache_update\n");
745
746	(void) mutex_lock(&cache_lock);
747
748	/* first we walk the entire cache, marking each entry stale */
749	node = cache_head.vc_next;
750	for (; node != &cache_tail; node = node->vc_next)
751		node->vc_state |= CACHE_NODE_STALE;
752
753	/* now walk the links and update all of the entries */
754	bua.hd = hd;
755	bua.retval = RCM_SUCCESS;
756	(void) dladm_walk_datalink_id(bridge_update, dld_handle, &bua,
757	    DATALINK_CLASS_AGGR | DATALINK_CLASS_PHYS |
758	    DATALINK_CLASS_ETHERSTUB, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
759	lastrv = bua.retval;
760
761	/*
762	 * Continue to delete all stale nodes from the cache even if the walk
763	 * above failed.  Unregister links that are not offlined and still in
764	 * the cache.
765	 */
766	for (node = cache_head.vc_next; node != &cache_tail; node = nnode) {
767		nnode = node->vc_next;
768
769		if (node->vc_state & CACHE_NODE_STALE) {
770			(void) rcm_unregister_interest(hd, node->vc_resource,
771			    0);
772			rcm_log_message(RCM_DEBUG,
773			    "Bridge: unregistered %s %s\n",
774			    node->vc_resource, node->vc_bridge);
775			cache_remove(node);
776			node_free(node);
777			continue;
778		}
779
780		if (!(node->vc_state & CACHE_NODE_NEW))
781			continue;
782
783		rv = rcm_register_interest(hd, node->vc_resource, 0, NULL);
784		if (rv != RCM_SUCCESS) {
785			rcm_log_message(RCM_ERROR,
786			    _("Bridge: failed to register %s\n"),
787			    node->vc_resource);
788			lastrv = rv;
789		} else {
790			rcm_log_message(RCM_DEBUG, "Bridge: registered %s\n",
791			    node->vc_resource);
792			node->vc_state &= ~CACHE_NODE_NEW;
793		}
794	}
795
796	(void) mutex_unlock(&cache_lock);
797	return (lastrv);
798}
799
800/*
801 * cache_free() - Empty the cache
802 */
803static void
804cache_free(void)
805{
806	link_cache_t *node;
807
808	rcm_log_message(RCM_TRACE2, "Bridge: cache_free\n");
809
810	(void) mutex_lock(&cache_lock);
811	node = cache_head.vc_next;
812	while (node != &cache_tail) {
813		cache_remove(node);
814		node_free(node);
815		node = cache_head.vc_next;
816	}
817	(void) mutex_unlock(&cache_lock);
818}
819
820/*
821 * bridge_log_err() - RCM error log wrapper
822 */
823static void
824bridge_log_err(datalink_id_t linkid, char **errorp, char *errmsg)
825{
826	char link[MAXLINKNAMELEN];
827	char errstr[DLADM_STRSIZE];
828	dladm_status_t status;
829	char *error;
830
831	link[0] = '\0';
832	if (linkid != DATALINK_INVALID_LINKID) {
833		char rsrc[RCM_LINK_RESOURCE_MAX];
834
835		(void) snprintf(rsrc, sizeof (rsrc), "%s/%u",
836		    RCM_LINK_PREFIX, linkid);
837
838		rcm_log_message(RCM_ERROR, _("Bridge: %s(%s)\n"), errmsg, rsrc);
839		if ((status = dladm_datalink_id2info(dld_handle, linkid, NULL,
840		    NULL, NULL, link, sizeof (link))) != DLADM_STATUS_OK) {
841			rcm_log_message(RCM_WARNING,
842			    _("Bridge: cannot get link name for (%s) %s\n"),
843			    rsrc, dladm_status2str(status, errstr));
844		}
845	} else {
846		rcm_log_message(RCM_ERROR, _("Bridge: %s\n"), errmsg);
847	}
848
849	if (link[0] != '\0')
850		(void) asprintf(&error, _("Bridge: %s(%s)"), errmsg, link);
851	else
852		(void) asprintf(&error, _("Bridge: %s"), errmsg);
853
854	if (errorp != NULL)
855		*errorp = error;
856}
857
858/*
859 * bridge_configure() - Configure bridge on a physical link after it attaches
860 */
861static int
862bridge_configure(rcm_handle_t *hd, datalink_id_t linkid)
863{
864	char rsrc[RCM_LINK_RESOURCE_MAX];
865	link_cache_t *node;
866	char bridge[MAXLINKNAMELEN];
867
868	/* Check for the bridge links in the cache */
869	(void) snprintf(rsrc, sizeof (rsrc), "%s/%u", RCM_LINK_PREFIX, linkid);
870
871	rcm_log_message(RCM_TRACE2, "Bridge: bridge_configure(%s)\n", rsrc);
872
873	/* Check if the link is new or was previously offlined */
874	(void) mutex_lock(&cache_lock);
875	if (((node = cache_lookup(hd, rsrc, CACHE_REFRESH)) != NULL) &&
876	    (!(node->vc_state & CACHE_NODE_OFFLINED))) {
877		rcm_log_message(RCM_TRACE2,
878		    "Bridge: Skipping configured interface(%s)\n", rsrc);
879		(void) mutex_unlock(&cache_lock);
880		return (RCM_SUCCESS);
881	}
882	(void) mutex_unlock(&cache_lock);
883
884	/* clear out previous bridge, if any */
885	if (dladm_bridge_getlink(dld_handle, linkid, bridge, sizeof (bridge)) ==
886	    DLADM_STATUS_OK) {
887		if (bridge[0] != '\0')
888			(void) dladm_bridge_setlink(dld_handle, linkid, "");
889	}
890
891	/* now set up the new one */
892	if (node != NULL && node->vc_bridge[0] != '\0' &&
893	    dladm_bridge_setlink(dld_handle, linkid, node->vc_bridge) !=
894	    DLADM_STATUS_OK)
895		return (RCM_FAILURE);
896	else
897		return (RCM_SUCCESS);
898}
899