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 *
rcm_mod_init(void)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, USYNC_THREAD, 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 *
rcm_mod_info(void)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
rcm_mod_fini(void)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
net_register(rcm_handle_t * hd)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
net_unregister(rcm_handle_t * hd)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
net_passthru(rcm_handle_t * hd,int op,const char * rsrc,uint_t flag,char ** reason,rcm_info_t ** dependent_reason,void * arg)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
net_offline(rcm_handle_t * hd,char * rsrc,id_t id,uint_t flags,char ** reason,rcm_info_t ** dependent_reason)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
net_online(rcm_handle_t * hd,char * rsrc,id_t id,uint_t flag,char ** reason,rcm_info_t ** dependent_reason)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
net_getinfo(rcm_handle_t * hd,char * rsrc,id_t id,uint_t flag,char ** info,char ** errstr,nvlist_t * proplist,rcm_info_t ** depend_info)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
net_suspend(rcm_handle_t * hd,char * rsrc,id_t id,timespec_t * interval,uint_t flag,char ** reason,rcm_info_t ** dependent_reason)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
net_resume(rcm_handle_t * hd,char * rsrc,id_t id,uint_t flag,char ** info,rcm_info_t ** dependent_info)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
net_remove(rcm_handle_t * hd,char * rsrc,id_t id,uint_t flag,char ** info,rcm_info_t ** dependent_info)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 *
cache_lookup(const char * resource)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
free_node(net_cache_t * node)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
cache_insert(net_cache_t * node)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
cache_remove(net_cache_t * node)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
devfs_entry(di_node_t node,di_minor_t minor,void * arg)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
update_cache(rcm_handle_t * hd)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
free_cache(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
net_notify_event(rcm_handle_t * hd,char * rsrc,id_t id,uint_t flags,char ** errorp,nvlist_t * nvl,rcm_info_t ** depend_info)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