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