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"
49 #define _(x) gettext(x)
54 #define CACHE_STALE 1 /* flags */
55 #define CACHE_NEW 2 /* flags */
64 typedef struct net_cache
69 struct net_cache
*next
;
70 struct net_cache
*prev
;
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;
78 static dladm_handle_t dld_handle
= NULL
;
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 **,
89 static int net_offline(rcm_handle_t
*, char *, id_t
, uint_t
, char **,
91 static int net_online(rcm_handle_t
*, char *, id_t
, uint_t
, char **,
93 static int net_remove(rcm_handle_t
*, char *, id_t
, uint_t
, char **,
95 static int net_notify_event(rcm_handle_t
*, char *, id_t
, uint_t
,
96 char **, nvlist_t
*, rcm_info_t
**);
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
*);
108 * Module-Private data
110 static struct rcm_mod_ops net_ops
= {
126 * Module Interface Routines
132 * Update registrations, and return the ops structure.
137 dladm_status_t status
;
138 char errmsg
[DLADM_STRSIZE
];
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
);
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
));
153 /* Return the ops vectors */
160 * Return a string describing this module.
165 return ("Network namespace module 1.13");
177 (void) mutex_destroy(&cache_lock
);
179 dladm_close(dld_handle
);
180 return (RCM_SUCCESS
);
186 * Make sure the cache is properly sync'ed, and its registrations
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.
194 net_register(rcm_handle_t
*hd
)
198 * Need to register interest in all new resources
199 * getting attached, so we get attach event notifications
201 if (!events_registered
) {
202 if (rcm_register_event(hd
, RCM_RESOURCE_PHYSLINK_NEW
, 0, NULL
)
204 rcm_log_message(RCM_ERROR
,
205 _("NET: failed to register %s\n"),
206 RCM_RESOURCE_PHYSLINK_NEW
);
207 return (RCM_FAILURE
);
209 rcm_log_message(RCM_DEBUG
, _("NET: registered %s \n"),
210 RCM_RESOURCE_PHYSLINK_NEW
);
215 return (RCM_SUCCESS
);
221 * Manually walk through the cache, unregistering all the networks.
223 * Locking: the cache is locked throughout the execution of this routine
224 * because it reads and modifies cache links continuously.
227 net_unregister(rcm_handle_t
*hd
)
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);
240 probe
= cache_head
.next
;
242 (void) mutex_unlock(&cache_lock
);
245 * Need to unregister interest in all new resources
247 if (events_registered
) {
248 if (rcm_unregister_event(hd
, RCM_RESOURCE_PHYSLINK_NEW
, 0)
250 rcm_log_message(RCM_ERROR
,
251 _("NET: failed to unregister %s\n"),
252 RCM_RESOURCE_PHYSLINK_NEW
);
253 return (RCM_FAILURE
);
255 rcm_log_message(RCM_DEBUG
, _("NET: unregistered %s\n"),
256 RCM_RESOURCE_PHYSLINK_NEW
);
261 return (RCM_SUCCESS
);
265 * Since all we do is pass operations thru, we provide a general
266 * routine for passing through operations.
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
)
275 datalink_id_t linkid
;
280 * Lock the cache just long enough to extract information about this
283 (void) mutex_lock(&cache_lock
);
284 node
= cache_lookup(rsrc
);
286 rcm_log_message(RCM_WARNING
,
287 _("NET: unrecognized resource %s\n"), rsrc
);
288 (void) mutex_unlock(&cache_lock
);
289 return (RCM_SUCCESS
);
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.
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
);
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.
309 if (op
== NET_REMOVE
) {
313 (void) mutex_unlock(&cache_lock
);
317 rv
= rcm_request_suspend(hd
, exported
, flag
,
318 (timespec_t
*)arg
, dependent_reason
);
321 rv
= rcm_request_offline(hd
, exported
, flag
, dependent_reason
);
324 rv
= rcm_notify_online(hd
, exported
, flag
, dependent_reason
);
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
);
333 * Delete active linkprop before this active link
336 (void) dladm_set_linkprop(dld_handle
, linkid
, NULL
,
337 NULL
, 0, DLADM_OPT_ACTIVE
);
338 (void) dladm_destroy_datalink_id(dld_handle
, linkid
,
343 rv
= rcm_notify_resume(hd
, exported
, flag
, dependent_reason
);
346 rcm_log_message(RCM_WARNING
,
347 _("NET: bad RCM operation %1$d for %2$s\n"), op
, exported
);
349 return (RCM_FAILURE
);
352 if (rv
!= RCM_SUCCESS
) {
354 (void) snprintf(format
, sizeof (format
),
355 _("RCM operation on dependent %s did not succeed"),
357 rcm_log_message(RCM_WARNING
, "NET: %s\n", format
);
366 * Determine dependents of the resource being offlined, and offline
370 net_offline(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flags
,
371 char **reason
, rcm_info_t
**dependent_reason
)
374 assert(rsrc
!= NULL
);
375 assert(id
== (id_t
)0);
376 assert(reason
!= NULL
);
377 assert(dependent_reason
!= NULL
);
379 rcm_log_message(RCM_TRACE1
, _("NET: offline(%s)\n"), rsrc
);
381 return (net_passthru(hd
, NET_OFFLINE
, rsrc
, flags
, reason
,
382 dependent_reason
, NULL
));
388 * Online the previously offlined resource, and online its dependents.
391 net_online(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flag
, char **reason
,
392 rcm_info_t
**dependent_reason
)
395 assert(rsrc
!= NULL
);
396 assert(id
== (id_t
)0);
398 rcm_log_message(RCM_TRACE1
, _("NET: online(%s)\n"), rsrc
);
400 return (net_passthru(hd
, NET_ONLINE
, rsrc
, flag
, reason
,
401 dependent_reason
, NULL
));
407 * Gather usage information for this resource.
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.
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
)
420 dladm_status_t status
;
421 char link
[MAXLINKNAMELEN
];
422 char errmsg
[DLADM_STRSIZE
];
424 const char *info_fmt
;
428 assert(rsrc
!= NULL
);
429 assert(id
== (id_t
)0);
430 assert(info
!= NULL
);
431 assert(depend_info
!= NULL
);
433 rcm_log_message(RCM_TRACE1
, _("NET: getinfo(%s)\n"), rsrc
);
435 info_fmt
= _("Network interface %s");
437 (void) mutex_lock(&cache_lock
);
438 node
= cache_lookup(rsrc
);
440 rcm_log_message(RCM_WARNING
,
441 _("NET: unrecognized resource %s\n"), rsrc
);
442 (void) mutex_unlock(&cache_lock
);
444 return (RCM_FAILURE
);
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
);
461 /* Fill in the string */
462 (void) snprintf(*info
, len
, info_fmt
, link
);
464 len
= strlen("SUNW_datalink/") + LINKID_STR_WIDTH
+ 1;
465 exported
= malloc(len
);
467 rcm_log_message(RCM_ERROR
, _("NET: allocation failure"));
469 (void) mutex_unlock(&cache_lock
);
470 return (RCM_FAILURE
);
472 (void) snprintf(exported
, len
, "SUNW_datalink/%u", node
->linkid
);
473 (void) mutex_unlock(&cache_lock
);
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
);
480 (void) nvlist_add_string(proplist
, RCM_CLIENT_NAME
, "SunOS");
481 (void) nvlist_add_string_array(proplist
, RCM_CLIENT_EXPORTS
,
485 return (RCM_SUCCESS
);
491 * Notify all dependents that the resource is being suspended.
492 * Since no real operation is involved, QUERY or not doesn't matter.
494 * Locking: the cache is only used to retrieve some information about
495 * this resource, so it is only locked during that retrieval.
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
)
502 assert(rsrc
!= NULL
);
503 assert(id
== (id_t
)0);
504 assert(interval
!= NULL
);
505 assert(reason
!= NULL
);
506 assert(dependent_reason
!= NULL
);
508 rcm_log_message(RCM_TRACE1
, _("NET: suspend(%s)\n"), rsrc
);
510 return (net_passthru(hd
, NET_SUSPEND
, rsrc
, flag
, reason
,
511 dependent_reason
, (void *)interval
));
517 * Resume all the dependents of a suspended network.
519 * Locking: the cache is only used to retrieve some information about
520 * this resource, so it is only locked during that retrieval.
523 net_resume(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flag
, char **info
,
524 rcm_info_t
**dependent_info
)
527 assert(rsrc
!= NULL
);
528 assert(id
== (id_t
)0);
529 assert(info
!= NULL
);
530 assert(dependent_info
!= NULL
);
532 rcm_log_message(RCM_TRACE1
, _("NET: resume(%s)\n"), rsrc
);
534 return (net_passthru(hd
, NET_RESUME
, rsrc
, flag
, info
, dependent_info
,
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.
548 net_remove(rcm_handle_t
*hd
, char *rsrc
, id_t id
, uint_t flag
, char **info
,
549 rcm_info_t
**dependent_info
)
552 assert(rsrc
!= NULL
);
553 assert(id
== (id_t
)0);
554 assert(info
!= NULL
);
555 assert(dependent_info
!= NULL
);
557 rcm_log_message(RCM_TRACE1
, _("NET: remove(%s)\n"), rsrc
);
559 return (net_passthru(hd
, NET_REMOVE
, rsrc
, flag
, info
, dependent_info
,
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
574 * Get a cache node for a resource. Call with cache lock held.
577 cache_lookup(const char *resource
)
580 probe
= cache_head
.next
;
581 while (probe
!= &cache_tail
) {
582 if (probe
->resource
&&
583 (strcmp(resource
, probe
->resource
) == 0)) {
594 * Free a node. Make sure it isn't in the list!
597 free_node(net_cache_t
*node
)
600 free(node
->resource
);
608 * Call with the cache_lock held.
611 cache_insert(net_cache_t
*node
)
613 /* insert at the head for best performance */
614 node
->next
= cache_head
.next
;
615 node
->prev
= &cache_head
;
617 node
->next
->prev
= node
;
618 node
->prev
->next
= node
;
624 * Call with the cache_lock held.
627 cache_remove(net_cache_t
*node
)
629 node
->next
->prev
= node
->prev
;
630 node
->prev
->next
= node
->next
;
638 * Call with the cache_lock held.
642 devfs_entry(di_node_t node
, di_minor_t minor
, void *arg
)
645 char resource
[MAXPATHLEN
];
646 char dev
[MAXNAMELEN
];
647 datalink_id_t linkid
;
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
);
658 drv
= di_driver_name(node
);
660 /* what else can we do? */
661 return (DI_WALK_CONTINUE
);
664 devfspath
= di_devfs_path(node
);
666 /* no devfs path?!? */
667 rcm_log_message(RCM_DEBUG
, _("NET: missing devfs path\n"));
668 return (DI_WALK_CONTINUE
);
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
);
679 (void) snprintf(resource
, sizeof (resource
), "/devices%s", devfspath
);
680 di_devfs_path_free(devfspath
);
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
);
689 probe
= cache_lookup(resource
);
691 rcm_log_message(RCM_DEBUG
,
692 _("NET: %s already registered (linkid %u)\n"),
694 probe
->linkid
= linkid
;
695 probe
->flags
&= ~(CACHE_STALE
);
697 rcm_log_message(RCM_DEBUG
,
698 _("NET: %s is new resource (linkid %u)\n"),
700 probe
= calloc(1, sizeof (net_cache_t
));
702 rcm_log_message(RCM_ERROR
, _("NET: malloc failure"));
703 return (DI_WALK_CONTINUE
);
706 probe
->resource
= strdup(resource
);
707 probe
->linkid
= linkid
;
709 if (!probe
->resource
) {
711 return (DI_WALK_CONTINUE
);
714 probe
->flags
|= CACHE_NEW
;
718 return (DI_WALK_CONTINUE
);
724 * The devinfo tree walking code is lifted from ifconfig.c.
727 update_cache(rcm_handle_t
*hd
)
733 (void) mutex_lock(&cache_lock
);
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
;
742 root
= di_init("/", DINFOSUBTREE
| DINFOMINOR
);
743 if (root
== DI_NODE_NIL
) {
747 (void) di_walk_minor(root
, DDI_NT_NET
, DI_CHECK_ALIAS
, NULL
,
752 probe
= cache_head
.next
;
753 while (probe
!= &cache_tail
) {
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"),
761 cache_remove(freeit
);
766 if (!(probe
->flags
& CACHE_NEW
)) {
771 rcm_log_message(RCM_DEBUG
, _("NET: registering %s\n"),
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"),
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
);
788 (void) mutex_unlock(&cache_lock
);
799 (void) mutex_lock(&cache_lock
);
800 probe
= cache_head
.next
;
801 while (probe
!= &cache_tail
) {
804 probe
= cache_head
.next
;
806 (void) mutex_unlock(&cache_lock
);
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.
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
)
820 nvpair_t
*nvp
= NULL
;
821 uint64_t id64
= (uint64_t)DATALINK_INVALID_LINKID
;
822 boolean_t reconfigured
= B_FALSE
;
824 rcm_log_message(RCM_TRACE1
, _("NET: notify_event(%s)\n"), rsrc
);
826 if (strcmp(rsrc
, RCM_RESOURCE_PHYSLINK_NEW
) != 0) {
827 rcm_log_message(RCM_INFO
,
828 _("NET: unrecognized event for %s\n"), rsrc
);
830 return (RCM_FAILURE
);
833 /* Update cache to reflect latest physical links */
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
);
844 return (RCM_FAILURE
);
847 rcm_log_message(RCM_TRACE1
,
848 "NET: %s event data (%sreconfiguration)\n",
849 RCM_NV_RECONFIGURED
, reconfigured
? "" : "not ");
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"),
858 return (RCM_FAILURE
);
861 rcm_log_message(RCM_TRACE1
,
862 "NET: %s event data (linkid %d)\n", RCM_NV_LINKID
,
863 (datalink_id_t
)id64
);
867 if ((datalink_id_t
)id64
== DATALINK_INVALID_LINKID
) {
868 rcm_log_message(RCM_INFO
, _("NET: invalid datalink\n"));
870 return (RCM_FAILURE
);
874 * If this is device reconfiguration, populate the LINK_NEW event
875 * to start the DR process.
878 nvlist_t
*nnvl
= NULL
;
880 rcm_log_message(RCM_TRACE1
,
881 "NET: reconfigured data-link (id %d)\n",
882 (datalink_id_t
)id64
);
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
)) {
888 rcm_log_message(RCM_INFO
,
889 _("NET: notify %s event failed\n"),
890 RCM_RESOURCE_LINK_NEW
);
892 return (RCM_FAILURE
);
897 rcm_log_message(RCM_TRACE1
,
898 _("NET: notify_event: device configuration complete\n"));
900 return (RCM_SUCCESS
);