Home | History | Annotate | Download | only in common
      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, NULL, 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