xref: /illumos-gate/usr/src/cmd/rcm_daemon/common/bridge_rcm.c (revision 4eaa471005973e11a6110b69fe990530b3b95a38)
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 */
58 typedef 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 */
69 typedef 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  */
81 static link_cache_t	cache_head;
82 static link_cache_t	cache_tail;
83 static mutex_t		cache_lock;
84 static boolean_t	events_registered = B_FALSE;
85 
86 static dladm_handle_t	dld_handle = NULL;
87 
88 /*
89  * RCM module interface prototypes
90  */
91 static int		bridge_register(rcm_handle_t *);
92 static int		bridge_unregister(rcm_handle_t *);
93 static int		bridge_get_info(rcm_handle_t *, char *, id_t, uint_t,
94 			    char **, char **, nvlist_t *, rcm_info_t **);
95 static int		bridge_suspend(rcm_handle_t *, char *, id_t,
96 			    timespec_t *, uint_t, char **, rcm_info_t **);
97 static int		bridge_resume(rcm_handle_t *, char *, id_t, uint_t,
98 			    char **, rcm_info_t **);
99 static int		bridge_offline(rcm_handle_t *, char *, id_t, uint_t,
100 			    char **, rcm_info_t **);
101 static int		bridge_undo_offline(rcm_handle_t *, char *, id_t,
102 			    uint_t, char **, rcm_info_t **);
103 static int		bridge_remove(rcm_handle_t *, char *, id_t, uint_t,
104 			    char **, rcm_info_t **);
105 static int		bridge_notify_event(rcm_handle_t *, char *, id_t,
106 			    uint_t, char **, nvlist_t *, rcm_info_t **);
107 static int		bridge_configure(rcm_handle_t *, datalink_id_t);
108 
109 /* Module private routines */
110 static void 		cache_free(void);
111 static int 		cache_update(rcm_handle_t *);
112 static void 		cache_remove(link_cache_t *);
113 static void 		node_free(link_cache_t *);
114 static void 		cache_insert(link_cache_t *);
115 static link_cache_t	*cache_lookup(rcm_handle_t *, char *, uint_t);
116 static char 		*bridge_usage(link_cache_t *);
117 static void 		bridge_log_err(datalink_id_t, char **, char *);
118 
119 /* Module-Private data */
120 static 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  */
139 struct rcm_mod_ops *
rcm_mod_init(void)140 rcm_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  */
167 const char *
rcm_mod_info(void)168 rcm_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  */
178 int
rcm_mod_fini(void)179 rcm_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  */
200 static int
bridge_register(rcm_handle_t * hd)201 bridge_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  */
233 static int
bridge_unregister(rcm_handle_t * hd)234 bridge_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  */
281 static int
bridge_offline(rcm_handle_t * hd,char * rsrc,id_t id,uint_t flags,char ** errorp,rcm_info_t ** info)282 bridge_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*/
328 static int
bridge_undo_offline(rcm_handle_t * hd,char * rsrc,id_t id,uint_t flags,char ** errorp,rcm_info_t ** info)329 bridge_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*/
380 int
bridge_get_info(rcm_handle_t * hd,char * rsrc,id_t id,uint_t flags,char ** usagep,char ** errorp,nvlist_t * props,rcm_info_t ** info)381 bridge_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*/
421 static int
bridge_suspend(rcm_handle_t * hd,char * rsrc,id_t id,timespec_t * interval,uint_t flags,char ** errorp,rcm_info_t ** info)422 bridge_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*/
433 static int
bridge_resume(rcm_handle_t * hd,char * rsrc,id_t id,uint_t flags,char ** errorp,rcm_info_t ** info)434 bridge_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*/
445 static int
bridge_remove(rcm_handle_t * hd,char * rsrc,id_t id,uint_t flags,char ** errorp,rcm_info_t ** info)446 bridge_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*/
481 static int
bridge_notify_event(rcm_handle_t * hd,char * rsrc,id_t id,uint_t flags,char ** errorp,nvlist_t * nvl,rcm_info_t ** info)482 bridge_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  */
538 static char *
bridge_usage(link_cache_t * node)539 bridge_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  */
592 static link_cache_t *
cache_lookup(rcm_handle_t * hd,char * rsrc,uint_t options)593 cache_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  */
622 static void
node_free(link_cache_t * node)623 node_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  */
634 static void
cache_insert(link_cache_t * node)635 cache_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  */
650 static void
cache_remove(link_cache_t * node)651 cache_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 
660 typedef 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  */
668 static int
bridge_update(dladm_handle_t handle,datalink_id_t linkid,void * arg)669 bridge_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;
728 done:
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  */
737 static int
cache_update(rcm_handle_t * hd)738 cache_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  */
803 static void
cache_free(void)804 cache_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  */
823 static void
bridge_log_err(datalink_id_t linkid,char ** errorp,char * errmsg)824 bridge_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  */
861 static int
bridge_configure(rcm_handle_t * hd,datalink_id_t linkid)862 bridge_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