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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * This RCM module adds support to the RCM framework for an abstract
28 * namespace for network devices (DLPI providers).
39 #include <libdevinfo.h>
40 #include <sys/types.h>
42 #include <libdllink.h>
43 #include "rcm_module.h"
48 #define _(x) gettext(x)
50 #define CACHE_STALE 1 /* flags */
51 #define CACHE_NEW 2 /* flags */
60 typedef struct net_cache
65 struct net_cache
*next
;
66 struct net_cache
*prev
;
69 static net_cache_t cache_head
;
70 static net_cache_t cache_tail
;
71 static mutex_t cache_lock
;
72 static int events_registered
= 0;
74 static dladm_handle_t dld_handle
= NULL
;
76 /* module interface routines */
77 static int net_register(rcm_handle_t
*);
78 static int net_unregister(rcm_handle_t
*);
79 static int net_getinfo(rcm_handle_t
*, char *, id_t
, uint_t
, char **,
80 char **, nvlist_t
*, rcm_info_t
**);
81 static int net_suspend(rcm_handle_t
*, char *, id_t
, timespec_t
*,
82 uint_t
, char **, rcm_info_t
**);
83 static int net_resume(rcm_handle_t
*, char *, id_t
, uint_t
, char **,
85 static int net_offline(rcm_handle_t
*, char *, id_t
, uint_t
, char **,
87 static int net_online(rcm_handle_t
*, char *, id_t
, uint_t
, char **,
89 static int net_remove(rcm_handle_t
*, char *, id_t
, uint_t
, char **,
91 static int net_notify_event(rcm_handle_t
*, char *, id_t
, uint_t
,
92 char **, nvlist_t
*, rcm_info_t
**);
94 /* module private routines */
95 static void free_cache(void);
96 static void update_cache(rcm_handle_t
*hd
);
97 static int devfs_entry(di_node_t node
, di_minor_t minor
, void *arg
);
98 static void cache_remove(net_cache_t
*node
);
99 static net_cache_t
*cache_lookup(const char *resource
);
100 static void free_node(net_cache_t
*);
101 static void cache_insert(net_cache_t
*);
104 * Module-Private data
106 static struct rcm_mod_ops net_ops
= {
122 * Module Interface Routines
128 * Update registrations, and return the ops structure.
133 dladm_status_t status
;
134 char errmsg
[DLADM_STRSIZE
];
136 cache_head
.next
= &cache_tail
;
137 cache_head
.prev
= NULL
;
138 cache_tail
.prev
= &cache_head
;
139 cache_tail
.next
= NULL
;
140 (void) mutex_init(&cache_lock
, 0, NULL
);
142 if ((status
= dladm_open(&dld_handle
)) != DLADM_STATUS_OK
) {
143 rcm_log_message(RCM_WARNING
,
144 "NET: mod_init failed: cannot open datalink handle: %s\n",
145 dladm_status2str(status
, errmsg
));
149 /* Return the ops vectors */
156 * Return a string describing this module.
161 return ("Network namespace module 1.13");
173 (void) mutex_destroy(&cache_lock
);
175 dladm_close(dld_handle
);
176 return (RCM_SUCCESS
);
182 * Make sure the cache is properly sync'ed, and its registrations
185 * Locking: the cache is locked by update_cache, and is held
186 * throughout update_cache's execution because it reads and
187 * possibly modifies cache links continuously.
190 net_register(rcm_handle_t
*hd
)
194 * Need to register interest in all new resources
195 * getting attached, so we get attach event notifications
197 if (!events_registered
) {
198 if (rcm_register_event(hd
, RCM_RESOURCE_PHYSLINK_NEW
, 0, NULL
)
200 rcm_log_message(RCM_ERROR
,
201 _("NET: failed to register %s\n"),
202 RCM_RESOURCE_PHYSLINK_NEW
);
203 return (RCM_FAILURE
);
205 rcm_log_message(RCM_DEBUG
, _("NET: registered %s \n"),
206 RCM_RESOURCE_PHYSLINK_NEW
);
211 return (RCM_SUCCESS
);
217 * Manually walk through the cache, unregistering all the networks.
219 * Locking: the cache is locked throughout the execution of this routine
220 * because it reads and modifies cache links continuously.
223 net_unregister(rcm_handle_t
*hd
)
229 /* Walk the cache, unregistering everything */
230 (void) mutex_lock(&cache_lock
);
231 probe
= cache_head
.next
;
232 while (probe
!= &cache_tail
) {
233 (void) rcm_unregister_interest(hd
, probe
->resource
, 0);
236 probe
= cache_head
.next
;
238 (void) mutex_unlock(&cache_lock
);
241 * Need to unregister interest in all new resources
243 if (events_registered
) {
244 if (rcm_unregister_event(hd
, RCM_RESOURCE_PHYSLINK_NEW
, 0)
246 rcm_log_message(RCM_ERROR
,
247 _("NET: failed to unregister %s\n"),
248 RCM_RESOURCE_PHYSLINK_NEW
);
249 return (RCM_FAILURE
);
251 rcm_log_message(RCM_DEBUG
, _("NET: unregistered %s\n"),
252 RCM_RESOURCE_PHYSLINK_NEW
);
257 return (RCM_SUCCESS
);
261 * Since all we do is pass operations thru, we provide a general
262 * routine for passing through operations.
266 net_passthru(rcm_handle_t
*hd
, int op
, const char *rsrc
, uint_t flag
,
267 char **reason
, rcm_info_t
**dependent_reason
, void *arg
)
271 datalink_id_t linkid
;
276 * Lock the cache just long enough to extract information about this
279 (void) mutex_lock(&cache_lock
);
280 node
= cache_lookup(rsrc
);
282 rcm_log_message(RCM_WARNING
,
283 _("NET: unrecognized resource %s\n"), rsrc
);
284 (void) mutex_unlock(&cache_lock
);
285 return (RCM_SUCCESS
);
289 * Since node could be freed after we drop cache_lock, allocate a
290 * stack-local copy. We don't use malloc() because some of the
291 * operations (such as NET_REMOVE) are not allowed to fail. Note
292 * that exported is never more than MAXPATHLEN bytes.
294 len
= strlen("SUNW_datalink/") + LINKID_STR_WIDTH
+ 1;
295 exported
= alloca(len
);
296 linkid
= node
->linkid
;
297 (void) snprintf(exported
, len
, "SUNW_datalink/%u", linkid
);
300 * Remove notifications are unconditional in the RCM state model,
301 * so it's safe to remove the node from the cache at this point.
302 * And we need to remove it so that we will recognize it as a new
303 * resource following the reattachment of the resource.
305 if (op
== NET_REMOVE
) {
309 (void) mutex_unlock(&cache_lock
);
313 rv
= rcm_request_suspend(hd
, exported
, flag
,
314 (timespec_t
*)arg
, dependent_reason
);
317 rv
= rcm_request_offline(hd
, exported
, flag
, dependent_reason
);
320 rv
= rcm_notify_online(hd
, exported
, flag
, dependent_reason
);
323 rv
= rcm_notify_remove(hd
, exported
, flag
, dependent_reason
);
324 if (rv
== RCM_SUCCESS
) {
325 rcm_log_message(RCM_DEBUG
,
326 _("NET: mark link %d as removed\n"), linkid
);
329 * Delete active linkprop before this active link
332 (void) dladm_set_linkprop(dld_handle
, linkid
, NULL
,
333 NULL
, 0, DLADM_OPT_ACTIVE
);
334 (void) dladm_destroy_datalink_id(dld_handle
, linkid
,
339 rv
= rcm_notify_resume(hd
, exported
, flag
, dependent_reason
);
342 rcm_log_message(RCM_WARNING
,
343 _("NET: bad RCM operation %1$d for %2$s\n"), op
, exported
);
345 return (RCM_FAILURE
);
348 if (rv
!= RCM_SUCCESS
) {
350 (void) snprintf(format
, sizeof (format
),
351 _("RCM operation on dependent %s did not succeed"),
353 rcm_log_message(RCM_WARNING
, "NET: %s\n", format
);
362 * Determine dependents of the resource being offlined, and offline
366 net_offline(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flags
,
367 char **reason
, rcm_info_t
**dependent_reason
)
370 assert(rsrc
!= NULL
);
371 assert(id
== (id_t
)0);
372 assert(reason
!= NULL
);
373 assert(dependent_reason
!= NULL
);
375 rcm_log_message(RCM_TRACE1
, _("NET: offline(%s)\n"), rsrc
);
377 return (net_passthru(hd
, NET_OFFLINE
, rsrc
, flags
, reason
,
378 dependent_reason
, NULL
));
384 * Online the previously offlined resource, and online its dependents.
387 net_online(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flag
, char **reason
,
388 rcm_info_t
**dependent_reason
)
391 assert(rsrc
!= NULL
);
392 assert(id
== (id_t
)0);
394 rcm_log_message(RCM_TRACE1
, _("NET: online(%s)\n"), rsrc
);
396 return (net_passthru(hd
, NET_ONLINE
, rsrc
, flag
, reason
,
397 dependent_reason
, NULL
));
403 * Gather usage information for this resource.
405 * Locking: the cache is locked while this routine looks up the
406 * resource and extracts copies of any piece of information it needs.
407 * The cache is then unlocked, and this routine performs the rest of
408 * its functions without touching any part of the cache.
412 net_getinfo(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flag
,
413 char **info
, char **errstr
, nvlist_t
*proplist
, rcm_info_t
**depend_info
)
416 dladm_status_t status
;
417 char link
[MAXLINKNAMELEN
];
418 char errmsg
[DLADM_STRSIZE
];
420 const char *info_fmt
;
424 assert(rsrc
!= NULL
);
425 assert(id
== (id_t
)0);
426 assert(info
!= NULL
);
427 assert(depend_info
!= NULL
);
429 rcm_log_message(RCM_TRACE1
, _("NET: getinfo(%s)\n"), rsrc
);
431 info_fmt
= _("Network interface %s");
433 (void) mutex_lock(&cache_lock
);
434 node
= cache_lookup(rsrc
);
436 rcm_log_message(RCM_WARNING
,
437 _("NET: unrecognized resource %s\n"), rsrc
);
438 (void) mutex_unlock(&cache_lock
);
440 return (RCM_FAILURE
);
443 len
= strlen(info_fmt
) + MAXLINKNAMELEN
+ 1;
444 if ((status
= dladm_datalink_id2info(dld_handle
, node
->linkid
, NULL
,
445 NULL
, NULL
, link
, sizeof (link
))) != DLADM_STATUS_OK
) {
446 rcm_log_message(RCM_ERROR
,
447 _("NET: usage(%s) get link name failure(%s)\n"),
448 node
->resource
, dladm_status2str(status
, errmsg
));
449 (void) mutex_unlock(&cache_lock
);
450 return (RCM_FAILURE
);
451 } else if ((*info
= (char *)malloc(len
)) == NULL
) {
452 rcm_log_message(RCM_ERROR
, _("NET: malloc failure"));
453 (void) mutex_unlock(&cache_lock
);
454 return (RCM_FAILURE
);
457 /* Fill in the string */
458 (void) snprintf(*info
, len
, info_fmt
, link
);
460 len
= strlen("SUNW_datalink/") + LINKID_STR_WIDTH
+ 1;
461 exported
= malloc(len
);
463 rcm_log_message(RCM_ERROR
, _("NET: allocation failure"));
465 (void) mutex_unlock(&cache_lock
);
466 return (RCM_FAILURE
);
468 (void) snprintf(exported
, len
, "SUNW_datalink/%u", node
->linkid
);
469 (void) mutex_unlock(&cache_lock
);
471 /* Get dependent info if requested */
472 if ((flag
& RCM_INCLUDE_DEPENDENT
) || (flag
& RCM_INCLUDE_SUBTREE
)) {
473 (void) rcm_get_info(hd
, exported
, flag
, depend_info
);
476 (void) nvlist_add_string(proplist
, RCM_CLIENT_NAME
, "SunOS");
477 (void) nvlist_add_string_array(proplist
, RCM_CLIENT_EXPORTS
,
481 return (RCM_SUCCESS
);
487 * Notify all dependents that the resource is being suspended.
488 * Since no real operation is involved, QUERY or not doesn't matter.
490 * Locking: the cache is only used to retrieve some information about
491 * this resource, so it is only locked during that retrieval.
494 net_suspend(rcm_handle_t
*hd
, char *rsrc
, id_t id
, timespec_t
*interval
,
495 uint_t flag
, char **reason
, rcm_info_t
**dependent_reason
)
498 assert(rsrc
!= NULL
);
499 assert(id
== (id_t
)0);
500 assert(interval
!= NULL
);
501 assert(reason
!= NULL
);
502 assert(dependent_reason
!= NULL
);
504 rcm_log_message(RCM_TRACE1
, _("NET: suspend(%s)\n"), rsrc
);
506 return (net_passthru(hd
, NET_SUSPEND
, rsrc
, flag
, reason
,
507 dependent_reason
, (void *)interval
));
513 * Resume all the dependents of a suspended network.
515 * Locking: the cache is only used to retrieve some information about
516 * this resource, so it is only locked during that retrieval.
519 net_resume(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flag
, char **info
,
520 rcm_info_t
**dependent_info
)
523 assert(rsrc
!= NULL
);
524 assert(id
== (id_t
)0);
525 assert(info
!= NULL
);
526 assert(dependent_info
!= NULL
);
528 rcm_log_message(RCM_TRACE1
, _("NET: resume(%s)\n"), rsrc
);
530 return (net_passthru(hd
, NET_RESUME
, rsrc
, flag
, info
, dependent_info
,
537 * This is another NO-OP for us, we just passthru the information. We
538 * don't need to remove it from our cache. We don't unregister
539 * interest at this point either; the network device name is still
540 * around. This way we don't have to change this logic when we
541 * gain the ability to learn about DR attach operations.
544 net_remove(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flag
, char **info
,
545 rcm_info_t
**dependent_info
)
548 assert(rsrc
!= NULL
);
549 assert(id
== (id_t
)0);
550 assert(info
!= NULL
);
551 assert(dependent_info
!= NULL
);
553 rcm_log_message(RCM_TRACE1
, _("NET: remove(%s)\n"), rsrc
);
555 return (net_passthru(hd
, NET_REMOVE
, rsrc
, flag
, info
, dependent_info
,
560 * Cache management routines. Note that the cache is implemented as a
561 * trivial linked list, and is only required because RCM doesn't
562 * provide enough state about our own registrations back to us. This
563 * linked list implementation probably clobbers the CPU cache pretty
570 * Get a cache node for a resource. Call with cache lock held.
573 cache_lookup(const char *resource
)
576 probe
= cache_head
.next
;
577 while (probe
!= &cache_tail
) {
578 if (probe
->resource
&&
579 (strcmp(resource
, probe
->resource
) == 0)) {
590 * Free a node. Make sure it isn't in the list!
593 free_node(net_cache_t
*node
)
596 free(node
->resource
);
604 * Call with the cache_lock held.
607 cache_insert(net_cache_t
*node
)
609 /* insert at the head for best performance */
610 node
->next
= cache_head
.next
;
611 node
->prev
= &cache_head
;
613 node
->next
->prev
= node
;
614 node
->prev
->next
= node
;
620 * Call with the cache_lock held.
623 cache_remove(net_cache_t
*node
)
625 node
->next
->prev
= node
->prev
;
626 node
->prev
->next
= node
->next
;
634 * Call with the cache_lock held.
638 devfs_entry(di_node_t node
, di_minor_t minor
, void *arg
)
641 char resource
[MAXPATHLEN
];
642 char dev
[MAXNAMELEN
];
643 datalink_id_t linkid
;
648 cp
= di_minor_nodetype(minor
);
649 if ((cp
== NULL
) || (strcmp(cp
, DDI_NT_NET
))) {
650 /* doesn't look like a network device */
651 return (DI_WALK_CONTINUE
);
654 drv
= di_driver_name(node
);
656 /* what else can we do? */
657 return (DI_WALK_CONTINUE
);
660 devfspath
= di_devfs_path(node
);
662 /* no devfs path?!? */
663 rcm_log_message(RCM_DEBUG
, _("NET: missing devfs path\n"));
664 return (DI_WALK_CONTINUE
);
667 if (strncmp("/pseudo", devfspath
, strlen("/pseudo")) == 0) {
668 /* ignore pseudo devices, probably not really NICs */
669 rcm_log_message(RCM_DEBUG
,
670 _("NET: ignoring pseudo device %s\n"), devfspath
);
671 di_devfs_path_free(devfspath
);
672 return (DI_WALK_CONTINUE
);
675 (void) snprintf(resource
, sizeof (resource
), "/devices%s", devfspath
);
676 di_devfs_path_free(devfspath
);
678 (void) snprintf(dev
, sizeof (dev
), "%s%d", drv
, di_instance(node
));
679 if (dladm_dev2linkid(dld_handle
, dev
, &linkid
) != DLADM_STATUS_OK
) {
680 rcm_log_message(RCM_DEBUG
,
681 _("NET: failed to find the linkid for %s\n"), dev
);
682 return (DI_WALK_CONTINUE
);
685 probe
= cache_lookup(resource
);
687 rcm_log_message(RCM_DEBUG
,
688 _("NET: %s already registered (linkid %u)\n"),
690 probe
->linkid
= linkid
;
691 probe
->flags
&= ~(CACHE_STALE
);
693 rcm_log_message(RCM_DEBUG
,
694 _("NET: %s is new resource (linkid %u)\n"),
696 probe
= calloc(1, sizeof (net_cache_t
));
698 rcm_log_message(RCM_ERROR
, _("NET: malloc failure"));
699 return (DI_WALK_CONTINUE
);
702 probe
->resource
= strdup(resource
);
703 probe
->linkid
= linkid
;
705 if (!probe
->resource
) {
707 return (DI_WALK_CONTINUE
);
710 probe
->flags
|= CACHE_NEW
;
714 return (DI_WALK_CONTINUE
);
720 * The devinfo tree walking code is lifted from ifconfig.c.
723 update_cache(rcm_handle_t
*hd
)
729 (void) mutex_lock(&cache_lock
);
731 /* first we walk the entire cache, marking each entry stale */
732 probe
= cache_head
.next
;
733 while (probe
!= &cache_tail
) {
734 probe
->flags
|= CACHE_STALE
;
738 root
= di_init("/", DINFOSUBTREE
| DINFOMINOR
);
739 if (root
== DI_NODE_NIL
) {
743 (void) di_walk_minor(root
, DDI_NT_NET
, DI_CHECK_ALIAS
, NULL
,
748 probe
= cache_head
.next
;
749 while (probe
!= &cache_tail
) {
751 if (probe
->flags
& CACHE_STALE
) {
752 (void) rcm_unregister_interest(hd
, probe
->resource
, 0);
753 rcm_log_message(RCM_DEBUG
, _("NET: unregistered %s\n"),
757 cache_remove(freeit
);
762 if (!(probe
->flags
& CACHE_NEW
)) {
767 rcm_log_message(RCM_DEBUG
, _("NET: registering %s\n"),
769 rv
= rcm_register_interest(hd
, probe
->resource
, 0, NULL
);
770 if (rv
!= RCM_SUCCESS
) {
771 rcm_log_message(RCM_ERROR
,
772 _("NET: failed to register %s\n"),
775 rcm_log_message(RCM_DEBUG
,
776 _("NET: registered %s as SUNW_datalink/%u\n"),
777 probe
->resource
, probe
->linkid
);
778 probe
->flags
&= ~(CACHE_NEW
);
784 (void) mutex_unlock(&cache_lock
);
795 (void) mutex_lock(&cache_lock
);
796 probe
= cache_head
.next
;
797 while (probe
!= &cache_tail
) {
800 probe
= cache_head
.next
;
802 (void) mutex_unlock(&cache_lock
);
806 * net_notify_event - Project private implementation to receive new
807 * resource events. It intercepts all new resource
808 * events. If the new resource is a network resource,
809 * update the physical link cache.
813 net_notify_event(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flags
,
814 char **errorp
, nvlist_t
*nvl
, rcm_info_t
**depend_info
)
816 nvpair_t
*nvp
= NULL
;
817 uint64_t id64
= (uint64_t)DATALINK_INVALID_LINKID
;
818 boolean_t reconfigured
= B_FALSE
;
820 rcm_log_message(RCM_TRACE1
, _("NET: notify_event(%s)\n"), rsrc
);
822 if (strcmp(rsrc
, RCM_RESOURCE_PHYSLINK_NEW
) != 0) {
823 rcm_log_message(RCM_INFO
,
824 _("NET: unrecognized event for %s\n"), rsrc
);
826 return (RCM_FAILURE
);
829 /* Update cache to reflect latest physical links */
832 while ((nvp
= nvlist_next_nvpair(nvl
, nvp
)) != NULL
) {
833 if (strcmp(nvpair_name(nvp
), RCM_NV_RECONFIGURED
) == 0) {
834 if (nvpair_value_boolean_value(nvp
,
835 &reconfigured
) != 0) {
836 rcm_log_message(RCM_INFO
,
837 _("NET: unrecognized %s event data\n"),
838 RCM_NV_RECONFIGURED
);
840 return (RCM_FAILURE
);
843 rcm_log_message(RCM_TRACE1
,
844 "NET: %s event data (%sreconfiguration)\n",
845 RCM_NV_RECONFIGURED
, reconfigured
? "" : "not ");
848 if (strcmp(nvpair_name(nvp
), RCM_NV_LINKID
) == 0) {
849 if (nvpair_value_uint64(nvp
, &id64
) != 0) {
850 rcm_log_message(RCM_INFO
,
851 _("NET: unrecognized %s event data\n"),
854 return (RCM_FAILURE
);
857 rcm_log_message(RCM_TRACE1
,
858 "NET: %s event data (linkid %d)\n", RCM_NV_LINKID
,
859 (datalink_id_t
)id64
);
863 if ((datalink_id_t
)id64
== DATALINK_INVALID_LINKID
) {
864 rcm_log_message(RCM_INFO
, _("NET: invalid datalink\n"));
866 return (RCM_FAILURE
);
870 * If this is device reconfiguration, populate the LINK_NEW event
871 * to start the DR process.
874 nvlist_t
*nnvl
= NULL
;
876 rcm_log_message(RCM_TRACE1
,
877 "NET: reconfigured data-link (id %d)\n",
878 (datalink_id_t
)id64
);
880 if ((nvlist_alloc(&nnvl
, 0, 0) != 0) || (nvlist_add_uint64(nnvl
,
881 RCM_NV_LINKID
, id64
) != 0) || (rcm_notify_event(hd
,
882 RCM_RESOURCE_LINK_NEW
, 0, nnvl
, NULL
) != RCM_SUCCESS
)) {
884 rcm_log_message(RCM_INFO
,
885 _("NET: notify %s event failed\n"),
886 RCM_RESOURCE_LINK_NEW
);
888 return (RCM_FAILURE
);
893 rcm_log_message(RCM_TRACE1
,
894 _("NET: notify_event: device configuration complete\n"));
896 return (RCM_SUCCESS
);