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 an abstract
28  * namespace for network devices (DLPI providers).
29  */
30 #include <alloca.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <assert.h>
35 #include <string.h>
36 #include <synch.h>
37 #include <libintl.h>
38 #include <errno.h>
39 #include <libdevinfo.h>
40 #include <sys/types.h>
41 #include <net/if.h>
42 #include <libdllink.h>
43 #include "rcm_module.h"
44 
45 /*
46  * Definitions
47  */
48 #ifndef	lint
49 #define	_(x)	gettext(x)
50 #else
51 #define	_(x)	x
52 #endif
53 
54 #define	CACHE_STALE	1	/* flags */
55 #define	CACHE_NEW	2	/* flags */
56 
57 /* operations */
58 #define	NET_OFFLINE	1
59 #define	NET_ONLINE	2
60 #define	NET_REMOVE	3
61 #define	NET_SUSPEND	4
62 #define	NET_RESUME	5
63 
64 typedef struct net_cache
65 {
66 	char			*resource;
67 	datalink_id_t		linkid;
68 	int			flags;
69 	struct net_cache	*next;
70 	struct net_cache	*prev;
71 } net_cache_t;
72 
73 static net_cache_t	cache_head;
74 static net_cache_t	cache_tail;
75 static mutex_t		cache_lock;
76 static int		events_registered = 0;
77 
78 static dladm_handle_t	dld_handle = NULL;
79 
80 /* module interface routines */
81 static int net_register(rcm_handle_t *);
82 static int net_unregister(rcm_handle_t *);
83 static int net_getinfo(rcm_handle_t *, char *, id_t, uint_t, char **,
84     char **, nvlist_t *, rcm_info_t **);
85 static int net_suspend(rcm_handle_t *, char *, id_t, timespec_t *,
86     uint_t, char **, rcm_info_t **);
87 static int net_resume(rcm_handle_t *, char *, id_t, uint_t, char **,
88     rcm_info_t **);
89 static int net_offline(rcm_handle_t *, char *, id_t, uint_t, char **,
90     rcm_info_t **);
91 static int net_online(rcm_handle_t *, char *, id_t, uint_t, char **,
92     rcm_info_t **);
93 static int net_remove(rcm_handle_t *, char *, id_t, uint_t, char **,
94     rcm_info_t **);
95 static int net_notify_event(rcm_handle_t *, char *, id_t, uint_t,
96     char **, nvlist_t *, rcm_info_t **);
97 
98 /* module private routines */
99 static void free_cache(void);
100 static void update_cache(rcm_handle_t *hd);
101 static int devfs_entry(di_node_t node, di_minor_t minor, void *arg);
102 static void cache_remove(net_cache_t *node);
103 static net_cache_t *cache_lookup(const char *resource);
104 static void free_node(net_cache_t *);
105 static void cache_insert(net_cache_t *);
106 
107 /*
108  * Module-Private data
109  */
110 static struct rcm_mod_ops net_ops = {
111 	RCM_MOD_OPS_VERSION,
112 	net_register,
113 	net_unregister,
114 	net_getinfo,
115 	net_suspend,
116 	net_resume,
117 	net_offline,
118 	net_online,
119 	net_remove,
120 	NULL,
121 	NULL,
122 	net_notify_event
123 };
124 
125 /*
126  * Module Interface Routines
127  */
128 
129 /*
130  * rcm_mod_init()
131  *
132  *	Update registrations, and return the ops structure.
133  */
134 struct rcm_mod_ops *
135 rcm_mod_init(void)
136 {
137 	dladm_status_t	status;
138 	char		errmsg[DLADM_STRSIZE];
139 
140 	cache_head.next = &cache_tail;
141 	cache_head.prev = NULL;
142 	cache_tail.prev = &cache_head;
143 	cache_tail.next = NULL;
144 	(void) mutex_init(&cache_lock, USYNC_THREAD, NULL);
145 
146 	if ((status = dladm_open(&dld_handle)) != DLADM_STATUS_OK) {
147 		rcm_log_message(RCM_WARNING,
148 		    "NET: mod_init failed: cannot open datalink handle: %s\n",
149 		    dladm_status2str(status, errmsg));
150 		return (NULL);
151 	}
152 
153 	/* Return the ops vectors */
154 	return (&net_ops);
155 }
156 
157 /*
158  * rcm_mod_info()
159  *
160  *	Return a string describing this module.
161  */
162 const char *
163 rcm_mod_info(void)
164 {
165 	return ("Network namespace module 1.13");
166 }
167 
168 /*
169  * rcm_mod_fini()
170  *
171  *	Destroy the cache.
172  */
173 int
174 rcm_mod_fini(void)
175 {
176 	free_cache();
177 	(void) mutex_destroy(&cache_lock);
178 
179 	dladm_close(dld_handle);
180 	return (RCM_SUCCESS);
181 }
182 
183 /*
184  * net_register()
185  *
186  *	Make sure the cache is properly sync'ed, and its registrations
187  *	are in order.
188  *
189  *	Locking: the cache is locked by update_cache, and is held
190  *	throughout update_cache's execution because it reads and
191  *	possibly modifies cache links continuously.
192  */
193 static int
194 net_register(rcm_handle_t *hd)
195 {
196 	update_cache(hd);
197 	/*
198 	 * Need to register interest in all new resources
199 	 * getting attached, so we get attach event notifications
200 	 */
201 	if (!events_registered) {
202 		if (rcm_register_event(hd, RCM_RESOURCE_PHYSLINK_NEW, 0, NULL)
203 		    != RCM_SUCCESS) {
204 			rcm_log_message(RCM_ERROR,
205 			    _("NET: failed to register %s\n"),
206 			    RCM_RESOURCE_PHYSLINK_NEW);
207 			return (RCM_FAILURE);
208 		} else {
209 			rcm_log_message(RCM_DEBUG, _("NET: registered %s \n"),
210 			    RCM_RESOURCE_PHYSLINK_NEW);
211 			events_registered++;
212 		}
213 	}
214 
215 	return (RCM_SUCCESS);
216 }
217 
218 /*
219  * net_unregister()
220  *
221  *	Manually walk through the cache, unregistering all the networks.
222  *
223  *	Locking: the cache is locked throughout the execution of this routine
224  *	because it reads and modifies cache links continuously.
225  */
226 static int
227 net_unregister(rcm_handle_t *hd)
228 {
229 	net_cache_t *probe;
230 
231 	assert(hd != NULL);
232 
233 	/* Walk the cache, unregistering everything */
234 	(void) mutex_lock(&cache_lock);
235 	probe = cache_head.next;
236 	while (probe != &cache_tail) {
237 		(void) rcm_unregister_interest(hd, probe->resource, 0);
238 		cache_remove(probe);
239 		free_node(probe);
240 		probe = cache_head.next;
241 	}
242 	(void) mutex_unlock(&cache_lock);
243 
244 	/*
245 	 * Need to unregister interest in all new resources
246 	 */
247 	if (events_registered) {
248 		if (rcm_unregister_event(hd, RCM_RESOURCE_PHYSLINK_NEW, 0)
249 		    != RCM_SUCCESS) {
250 			rcm_log_message(RCM_ERROR,
251 			    _("NET: failed to unregister %s\n"),
252 			    RCM_RESOURCE_PHYSLINK_NEW);
253 			return (RCM_FAILURE);
254 		} else {
255 			rcm_log_message(RCM_DEBUG, _("NET: unregistered %s\n"),
256 			    RCM_RESOURCE_PHYSLINK_NEW);
257 			events_registered--;
258 		}
259 	}
260 
261 	return (RCM_SUCCESS);
262 }
263 
264 /*
265  * Since all we do is pass operations thru, we provide a general
266  * routine for passing through operations.
267  */
268 /*ARGSUSED*/
269 static int
270 net_passthru(rcm_handle_t *hd, int op, const char *rsrc, uint_t flag,
271     char **reason, rcm_info_t **dependent_reason, void *arg)
272 {
273 	net_cache_t	*node;
274 	char		*exported;
275 	datalink_id_t	linkid;
276 	int		len;
277 	int		rv;
278 
279 	/*
280 	 * Lock the cache just long enough to extract information about this
281 	 * resource.
282 	 */
283 	(void) mutex_lock(&cache_lock);
284 	node = cache_lookup(rsrc);
285 	if (!node) {
286 		rcm_log_message(RCM_WARNING,
287 		    _("NET: unrecognized resource %s\n"), rsrc);
288 		(void) mutex_unlock(&cache_lock);
289 		return (RCM_SUCCESS);
290 	}
291 
292 	/*
293 	 * Since node could be freed after we drop cache_lock, allocate a
294 	 * stack-local copy. We don't use malloc() because some of the
295 	 * operations (such as NET_REMOVE) are not allowed to fail. Note
296 	 * that exported is never more than MAXPATHLEN bytes.
297 	 */
298 	len = strlen("SUNW_datalink/") + LINKID_STR_WIDTH + 1;
299 	exported = alloca(len);
300 	linkid = node->linkid;
301 	(void) snprintf(exported, len, "SUNW_datalink/%u", linkid);
302 
303 	/*
304 	 * Remove notifications are unconditional in the RCM state model,
305 	 * so it's safe to remove the node from the cache at this point.
306 	 * And we need to remove it so that we will recognize it as a new
307 	 * resource following the reattachment of the resource.
308 	 */
309 	if (op == NET_REMOVE) {
310 		cache_remove(node);
311 		free_node(node);
312 	}
313 	(void) mutex_unlock(&cache_lock);
314 
315 	switch (op) {
316 	case NET_SUSPEND:
317 		rv = rcm_request_suspend(hd, exported, flag,
318 		    (timespec_t *)arg, dependent_reason);
319 		break;
320 	case NET_OFFLINE:
321 		rv = rcm_request_offline(hd, exported, flag, dependent_reason);
322 		break;
323 	case NET_ONLINE:
324 		rv = rcm_notify_online(hd, exported, flag, dependent_reason);
325 		break;
326 	case NET_REMOVE:
327 		rv = rcm_notify_remove(hd, exported, flag, dependent_reason);
328 		if (rv == RCM_SUCCESS) {
329 			rcm_log_message(RCM_DEBUG,
330 			    _("NET: mark link %d as removed\n"), linkid);
331 
332 			/*
333 			 * Delete active linkprop before this active link
334 			 * is deleted.
335 			 */
336 			(void) dladm_set_linkprop(dld_handle, linkid, NULL,
337 			    NULL, 0, DLADM_OPT_ACTIVE);
338 			(void) dladm_destroy_datalink_id(dld_handle, linkid,
339 			    DLADM_OPT_ACTIVE);
340 		}
341 		break;
342 	case NET_RESUME:
343 		rv = rcm_notify_resume(hd, exported, flag, dependent_reason);
344 		break;
345 	default:
346 		rcm_log_message(RCM_WARNING,
347 		    _("NET: bad RCM operation %1$d for %2$s\n"), op, exported);
348 		errno = EINVAL;
349 		return (RCM_FAILURE);
350 	}
351 
352 	if (rv != RCM_SUCCESS) {
353 		char format[256];
354 		(void) snprintf(format, sizeof (format),
355 		    _("RCM operation on dependent %s did not succeed"),
356 		    exported);
357 		rcm_log_message(RCM_WARNING, "NET: %s\n", format);
358 	}
359 	return (rv);
360 }
361 
362 
363 /*
364  * net_offline()
365  *
366  *	Determine dependents of the resource being offlined, and offline
367  *	them all.
368  */
369 static int
370 net_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
371     char **reason, rcm_info_t **dependent_reason)
372 {
373 	assert(hd != NULL);
374 	assert(rsrc != NULL);
375 	assert(id == (id_t)0);
376 	assert(reason != NULL);
377 	assert(dependent_reason != NULL);
378 
379 	rcm_log_message(RCM_TRACE1, _("NET: offline(%s)\n"), rsrc);
380 
381 	return (net_passthru(hd, NET_OFFLINE, rsrc, flags, reason,
382 	    dependent_reason, NULL));
383 }
384 
385 /*
386  * net_online()
387  *
388  *	Online the previously offlined resource, and online its dependents.
389  */
390 static int
391 net_online(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, char **reason,
392     rcm_info_t **dependent_reason)
393 {
394 	assert(hd != NULL);
395 	assert(rsrc != NULL);
396 	assert(id == (id_t)0);
397 
398 	rcm_log_message(RCM_TRACE1, _("NET: online(%s)\n"), rsrc);
399 
400 	return (net_passthru(hd, NET_ONLINE, rsrc, flag, reason,
401 	    dependent_reason, NULL));
402 }
403 
404 /*
405  * net_getinfo()
406  *
407  *	Gather usage information for this resource.
408  *
409  *	Locking: the cache is locked while this routine looks up the
410  *	resource and extracts copies of any piece of information it needs.
411  *	The cache is then unlocked, and this routine performs the rest of
412  *	its functions without touching any part of the cache.
413  */
414 /*ARGSUSED*/
415 static int
416 net_getinfo(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag,
417     char **info, char **errstr, nvlist_t *proplist, rcm_info_t **depend_info)
418 {
419 	int		len;
420 	dladm_status_t	status;
421 	char		link[MAXLINKNAMELEN];
422 	char		errmsg[DLADM_STRSIZE];
423 	char		*exported;
424 	const char	*info_fmt;
425 	net_cache_t	*node;
426 
427 	assert(hd != NULL);
428 	assert(rsrc != NULL);
429 	assert(id == (id_t)0);
430 	assert(info != NULL);
431 	assert(depend_info != NULL);
432 
433 	rcm_log_message(RCM_TRACE1, _("NET: getinfo(%s)\n"), rsrc);
434 
435 	info_fmt = _("Network interface %s");
436 
437 	(void) mutex_lock(&cache_lock);
438 	node = cache_lookup(rsrc);
439 	if (!node) {
440 		rcm_log_message(RCM_WARNING,
441 		    _("NET: unrecognized resource %s\n"), rsrc);
442 		(void) mutex_unlock(&cache_lock);
443 		errno = ENOENT;
444 		return (RCM_FAILURE);
445 	}
446 
447 	len = strlen(info_fmt) + MAXLINKNAMELEN + 1;
448 	if ((status = dladm_datalink_id2info(dld_handle, node->linkid, NULL,
449 	    NULL, NULL, link, sizeof (link))) != DLADM_STATUS_OK) {
450 		rcm_log_message(RCM_ERROR,
451 		    _("NET: usage(%s) get link name failure(%s)\n"),
452 		    node->resource, dladm_status2str(status, errmsg));
453 		(void) mutex_unlock(&cache_lock);
454 		return (RCM_FAILURE);
455 	} else if ((*info = (char *)malloc(len)) == NULL) {
456 		rcm_log_message(RCM_ERROR, _("NET: malloc failure"));
457 		(void) mutex_unlock(&cache_lock);
458 		return (RCM_FAILURE);
459 	}
460 
461 	/* Fill in the string */
462 	(void) snprintf(*info, len, info_fmt, link);
463 
464 	len = strlen("SUNW_datalink/") + LINKID_STR_WIDTH + 1;
465 	exported = malloc(len);
466 	if (!exported) {
467 		rcm_log_message(RCM_ERROR, _("NET: allocation failure"));
468 		free(*info);
469 		(void) mutex_unlock(&cache_lock);
470 		return (RCM_FAILURE);
471 	}
472 	(void) snprintf(exported, len, "SUNW_datalink/%u", node->linkid);
473 	(void) mutex_unlock(&cache_lock);
474 
475 	/* Get dependent info if requested */
476 	if ((flag & RCM_INCLUDE_DEPENDENT) || (flag & RCM_INCLUDE_SUBTREE)) {
477 		(void) rcm_get_info(hd, exported, flag, depend_info);
478 	}
479 
480 	(void) nvlist_add_string(proplist, RCM_CLIENT_NAME, "SunOS");
481 	(void) nvlist_add_string_array(proplist, RCM_CLIENT_EXPORTS,
482 	    &exported, 1);
483 
484 	free(exported);
485 	return (RCM_SUCCESS);
486 }
487 
488 /*
489  * net_suspend()
490  *
491  *	Notify all dependents that the resource is being suspended.
492  *	Since no real operation is involved, QUERY or not doesn't matter.
493  *
494  *	Locking: the cache is only used to retrieve some information about
495  *	this resource, so it is only locked during that retrieval.
496  */
497 static int
498 net_suspend(rcm_handle_t *hd, char *rsrc, id_t id, timespec_t *interval,
499     uint_t flag, char **reason, rcm_info_t **dependent_reason)
500 {
501 	assert(hd != NULL);
502 	assert(rsrc != NULL);
503 	assert(id == (id_t)0);
504 	assert(interval != NULL);
505 	assert(reason != NULL);
506 	assert(dependent_reason != NULL);
507 
508 	rcm_log_message(RCM_TRACE1, _("NET: suspend(%s)\n"), rsrc);
509 
510 	return (net_passthru(hd, NET_SUSPEND, rsrc, flag, reason,
511 	    dependent_reason, (void *)interval));
512 }
513 
514 /*
515  * net_resume()
516  *
517  *	Resume all the dependents of a suspended network.
518  *
519  *	Locking: the cache is only used to retrieve some information about
520  *	this resource, so it is only locked during that retrieval.
521  */
522 static int
523 net_resume(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, char **info,
524     rcm_info_t **dependent_info)
525 {
526 	assert(hd != NULL);
527 	assert(rsrc != NULL);
528 	assert(id == (id_t)0);
529 	assert(info != NULL);
530 	assert(dependent_info != NULL);
531 
532 	rcm_log_message(RCM_TRACE1, _("NET: resume(%s)\n"), rsrc);
533 
534 	return (net_passthru(hd, NET_RESUME, rsrc, flag, info, dependent_info,
535 	    NULL));
536 }
537 
538 /*
539  * net_remove()
540  *
541  *	This is another NO-OP for us, we just passthru the information.  We
542  *	don't need to remove it from our cache.  We don't unregister
543  *	interest at this point either; the network device name is still
544  *	around.  This way we don't have to change this logic when we
545  *	gain the ability to learn about DR attach operations.
546  */
547 static int
548 net_remove(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, char **info,
549     rcm_info_t **dependent_info)
550 {
551 	assert(hd != NULL);
552 	assert(rsrc != NULL);
553 	assert(id == (id_t)0);
554 	assert(info != NULL);
555 	assert(dependent_info != NULL);
556 
557 	rcm_log_message(RCM_TRACE1, _("NET: remove(%s)\n"), rsrc);
558 
559 	return (net_passthru(hd, NET_REMOVE, rsrc, flag, info, dependent_info,
560 	    NULL));
561 }
562 
563 /*
564  * Cache management routines.  Note that the cache is implemented as a
565  * trivial linked list, and is only required because RCM doesn't
566  * provide enough state about our own registrations back to us.  This
567  * linked list implementation probably clobbers the CPU cache pretty
568  * well.
569  */
570 
571 /*
572  * cache_lookup()
573  *
574  * Get a cache node for a resource.  Call with cache lock held.
575  */
576 static net_cache_t *
577 cache_lookup(const char *resource)
578 {
579 	net_cache_t *probe;
580 	probe = cache_head.next;
581 	while (probe != &cache_tail) {
582 		if (probe->resource &&
583 		    (strcmp(resource, probe->resource) == 0)) {
584 			return (probe);
585 		}
586 		probe = probe->next;
587 	}
588 	return (NULL);
589 }
590 
591 /*
592  * free_node()
593  *
594  * Free a node.  Make sure it isn't in the list!
595  */
596 static void
597 free_node(net_cache_t *node)
598 {
599 	if (node) {
600 		free(node->resource);
601 		free(node);
602 	}
603 }
604 
605 /*
606  * cache_insert()
607  *
608  * Call with the cache_lock held.
609  */
610 static void
611 cache_insert(net_cache_t *node)
612 {
613 	/* insert at the head for best performance */
614 	node->next = cache_head.next;
615 	node->prev = &cache_head;
616 
617 	node->next->prev = node;
618 	node->prev->next = node;
619 }
620 
621 /*
622  * cache_remove()
623  *
624  * Call with the cache_lock held.
625  */
626 static void
627 cache_remove(net_cache_t *node)
628 {
629 	node->next->prev = node->prev;
630 	node->prev->next = node->next;
631 	node->next = NULL;
632 	node->prev = NULL;
633 }
634 
635 /*
636  * devfs_entry()
637  *
638  * Call with the cache_lock held.
639  */
640 /*ARGSUSED*/
641 static int
642 devfs_entry(di_node_t node, di_minor_t minor, void *arg)
643 {
644 	char		*devfspath;
645 	char		resource[MAXPATHLEN];
646 	char		dev[MAXNAMELEN];
647 	datalink_id_t	linkid;
648 	char		*drv;
649 	char		*cp;
650 	net_cache_t	*probe;
651 
652 	cp = di_minor_nodetype(minor);
653 	if ((cp == NULL) || (strcmp(cp, DDI_NT_NET))) {
654 		/* doesn't look like a network device */
655 		return (DI_WALK_CONTINUE);
656 	}
657 
658 	drv = di_driver_name(node);
659 	if (drv == NULL) {
660 		/* what else can we do? */
661 		return (DI_WALK_CONTINUE);
662 	}
663 
664 	devfspath = di_devfs_path(node);
665 	if (!devfspath) {
666 		/* no devfs path?!? */
667 		rcm_log_message(RCM_DEBUG, _("NET: missing devfs path\n"));
668 		return (DI_WALK_CONTINUE);
669 	}
670 
671 	if (strncmp("/pseudo", devfspath, strlen("/pseudo")) == 0) {
672 		/* ignore pseudo devices, probably not really NICs */
673 		rcm_log_message(RCM_DEBUG,
674 		    _("NET: ignoring pseudo device %s\n"), devfspath);
675 		di_devfs_path_free(devfspath);
676 		return (DI_WALK_CONTINUE);
677 	}
678 
679 	(void) snprintf(resource, sizeof (resource), "/devices%s", devfspath);
680 	di_devfs_path_free(devfspath);
681 
682 	(void) snprintf(dev, sizeof (dev), "%s%d", drv, di_instance(node));
683 	if (dladm_dev2linkid(dld_handle, dev, &linkid) != DLADM_STATUS_OK) {
684 		rcm_log_message(RCM_DEBUG,
685 		    _("NET: failed to find the linkid for %s\n"), dev);
686 		return (DI_WALK_CONTINUE);
687 	}
688 
689 	probe = cache_lookup(resource);
690 	if (probe != NULL) {
691 		rcm_log_message(RCM_DEBUG,
692 		    _("NET: %s already registered (linkid %u)\n"),
693 		    resource, linkid);
694 		probe->linkid = linkid;
695 		probe->flags &= ~(CACHE_STALE);
696 	} else {
697 		rcm_log_message(RCM_DEBUG,
698 		    _("NET: %s is new resource (linkid %u)\n"),
699 		    resource, linkid);
700 		probe = calloc(1, sizeof (net_cache_t));
701 		if (!probe) {
702 			rcm_log_message(RCM_ERROR, _("NET: malloc failure"));
703 			return (DI_WALK_CONTINUE);
704 		}
705 
706 		probe->resource = strdup(resource);
707 		probe->linkid = linkid;
708 
709 		if (!probe->resource) {
710 			free_node(probe);
711 			return (DI_WALK_CONTINUE);
712 		}
713 
714 		probe->flags |= CACHE_NEW;
715 		cache_insert(probe);
716 	}
717 
718 	return (DI_WALK_CONTINUE);
719 }
720 
721 /*
722  * update_cache()
723  *
724  * The devinfo tree walking code is lifted from ifconfig.c.
725  */
726 static void
727 update_cache(rcm_handle_t *hd)
728 {
729 	net_cache_t	*probe;
730 	di_node_t	root;
731 	int		rv;
732 
733 	(void) mutex_lock(&cache_lock);
734 
735 	/* first we walk the entire cache, marking each entry stale */
736 	probe = cache_head.next;
737 	while (probe != &cache_tail) {
738 		probe->flags |= CACHE_STALE;
739 		probe = probe->next;
740 	}
741 
742 	root = di_init("/", DINFOSUBTREE | DINFOMINOR);
743 	if (root == DI_NODE_NIL) {
744 		goto done;
745 	}
746 
747 	(void) di_walk_minor(root, DDI_NT_NET, DI_CHECK_ALIAS, NULL,
748 	    devfs_entry);
749 
750 	di_fini(root);
751 
752 	probe = cache_head.next;
753 	while (probe != &cache_tail) {
754 		net_cache_t *freeit;
755 		if (probe->flags & CACHE_STALE) {
756 			(void) rcm_unregister_interest(hd, probe->resource, 0);
757 			rcm_log_message(RCM_DEBUG, _("NET: unregistered %s\n"),
758 			    probe->resource);
759 			freeit = probe;
760 			probe = probe->next;
761 			cache_remove(freeit);
762 			free_node(freeit);
763 			continue;
764 		}
765 
766 		if (!(probe->flags & CACHE_NEW)) {
767 			probe = probe->next;
768 			continue;
769 		}
770 
771 		rcm_log_message(RCM_DEBUG, _("NET: registering %s\n"),
772 		    probe->resource);
773 		rv = rcm_register_interest(hd, probe->resource, 0, NULL);
774 		if (rv != RCM_SUCCESS) {
775 			rcm_log_message(RCM_ERROR,
776 			    _("NET: failed to register %s\n"),
777 			    probe->resource);
778 		} else {
779 			rcm_log_message(RCM_DEBUG,
780 			    _("NET: registered %s as SUNW_datalink/%u\n"),
781 			    probe->resource, probe->linkid);
782 			probe->flags &= ~(CACHE_NEW);
783 		}
784 		probe = probe->next;
785 	}
786 
787 done:
788 	(void) mutex_unlock(&cache_lock);
789 }
790 
791 /*
792  * free_cache()
793  */
794 static void
795 free_cache(void)
796 {
797 	net_cache_t *probe;
798 
799 	(void) mutex_lock(&cache_lock);
800 	probe = cache_head.next;
801 	while (probe != &cache_tail) {
802 		cache_remove(probe);
803 		free_node(probe);
804 		probe = cache_head.next;
805 	}
806 	(void) mutex_unlock(&cache_lock);
807 }
808 
809 /*
810  * net_notify_event - Project private implementation to receive new
811  *			resource events. It intercepts all new resource
812  *			events. If the new resource is a network resource,
813  *			update the physical link cache.
814  */
815 /*ARGSUSED*/
816 static int
817 net_notify_event(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
818     char **errorp, nvlist_t *nvl, rcm_info_t **depend_info)
819 {
820 	nvpair_t	*nvp = NULL;
821 	uint64_t	id64 = (uint64_t)DATALINK_INVALID_LINKID;
822 	boolean_t	reconfigured = B_FALSE;
823 
824 	rcm_log_message(RCM_TRACE1, _("NET: notify_event(%s)\n"), rsrc);
825 
826 	if (strcmp(rsrc, RCM_RESOURCE_PHYSLINK_NEW) != 0) {
827 		rcm_log_message(RCM_INFO,
828 		    _("NET: unrecognized event for %s\n"), rsrc);
829 		errno = EINVAL;
830 		return (RCM_FAILURE);
831 	}
832 
833 	/* Update cache to reflect latest physical links */
834 	update_cache(hd);
835 
836 	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
837 		if (strcmp(nvpair_name(nvp), RCM_NV_RECONFIGURED) == 0) {
838 			if (nvpair_value_boolean_value(nvp,
839 			    &reconfigured) != 0) {
840 				rcm_log_message(RCM_INFO,
841 				    _("NET: unrecognized %s event data\n"),
842 				    RCM_NV_RECONFIGURED);
843 				errno = EINVAL;
844 				return (RCM_FAILURE);
845 			}
846 
847 			rcm_log_message(RCM_TRACE1,
848 			    "NET: %s event data (%sreconfiguration)\n",
849 			    RCM_NV_RECONFIGURED, reconfigured ? "" : "not ");
850 		}
851 
852 		if (strcmp(nvpair_name(nvp), RCM_NV_LINKID) == 0) {
853 			if (nvpair_value_uint64(nvp, &id64) != 0) {
854 				rcm_log_message(RCM_INFO,
855 				    _("NET: unrecognized %s event data\n"),
856 				    RCM_NV_LINKID);
857 				errno = EINVAL;
858 				return (RCM_FAILURE);
859 			}
860 
861 			rcm_log_message(RCM_TRACE1,
862 			    "NET: %s event data (linkid %d)\n", RCM_NV_LINKID,
863 			    (datalink_id_t)id64);
864 		}
865 	}
866 
867 	if ((datalink_id_t)id64 == DATALINK_INVALID_LINKID) {
868 		rcm_log_message(RCM_INFO, _("NET: invalid datalink\n"));
869 		errno = EINVAL;
870 		return (RCM_FAILURE);
871 	}
872 
873 	/*
874 	 * If this is device reconfiguration, populate the LINK_NEW event
875 	 * to start the DR process.
876 	 */
877 	if (reconfigured) {
878 		nvlist_t *nnvl = NULL;
879 
880 		rcm_log_message(RCM_TRACE1,
881 		    "NET: reconfigured data-link (id %d)\n",
882 		    (datalink_id_t)id64);
883 
884 		if ((nvlist_alloc(&nnvl, 0, 0) != 0) || (nvlist_add_uint64(nnvl,
885 		    RCM_NV_LINKID, id64) != 0) || (rcm_notify_event(hd,
886 		    RCM_RESOURCE_LINK_NEW, 0, nnvl, NULL) != RCM_SUCCESS)) {
887 			nvlist_free(nnvl);
888 			rcm_log_message(RCM_INFO,
889 			    _("NET: notify %s event failed\n"),
890 			    RCM_RESOURCE_LINK_NEW);
891 			errno = EINVAL;
892 			return (RCM_FAILURE);
893 		}
894 		nvlist_free(nnvl);
895 	}
896 
897 	rcm_log_message(RCM_TRACE1,
898 	    _("NET: notify_event: device configuration complete\n"));
899 
900 	return (RCM_SUCCESS);
901 }
902