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