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