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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * This RCM module adds support to the RCM framework for an abstract 31 * namespace for network devices (DLPI providers). 32 */ 33 #include <alloca.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <unistd.h> 37 #include <assert.h> 38 #include <string.h> 39 #include <synch.h> 40 #include <libintl.h> 41 #include <errno.h> 42 #include <libdevinfo.h> 43 #include <sys/types.h> 44 #include <net/if.h> 45 #include <liblaadm.h> 46 #include "rcm_module.h" 47 48 /* 49 * Definitions 50 */ 51 #ifndef lint 52 #define _(x) gettext(x) 53 #else 54 #define _(x) x 55 #endif 56 57 #define CACHE_STALE 1 /* flags */ 58 #define CACHE_NEW 2 /* flags */ 59 60 /* operations */ 61 #define NET_OFFLINE 1 62 #define NET_ONLINE 2 63 #define NET_REMOVE 3 64 #define NET_SUSPEND 4 65 #define NET_RESUME 5 66 67 /* 68 * PSARC decided that DLPI providers are not allowed to end in a digit. 69 * If this ever changes we could add a delimiter with this macro. 70 */ 71 #define NET_DELIMITER "" 72 73 typedef struct net_cache 74 { 75 char *resource; 76 char *exported; 77 char *driver; 78 int ppa; 79 int flags; 80 struct net_cache *next; 81 struct net_cache *prev; 82 } net_cache_t; 83 84 static net_cache_t cache_head; 85 static net_cache_t cache_tail; 86 static mutex_t cache_lock; 87 static int events_registered = 0; 88 89 /* module interface routines */ 90 static int net_register(rcm_handle_t *); 91 static int net_unregister(rcm_handle_t *); 92 static int net_getinfo(rcm_handle_t *, char *, id_t, uint_t, char **, 93 char **, nvlist_t *, rcm_info_t **); 94 static int net_suspend(rcm_handle_t *, char *, id_t, timespec_t *, 95 uint_t, char **, rcm_info_t **); 96 static int net_resume(rcm_handle_t *, char *, id_t, uint_t, char **, 97 rcm_info_t **); 98 static int net_offline(rcm_handle_t *, char *, id_t, uint_t, char **, 99 rcm_info_t **); 100 static int net_online(rcm_handle_t *, char *, id_t, uint_t, char **, 101 rcm_info_t **); 102 static int net_remove(rcm_handle_t *, char *, id_t, uint_t, char **, 103 rcm_info_t **); 104 static int net_notify_event(rcm_handle_t *, char *, id_t, uint_t, 105 char **, nvlist_t *, rcm_info_t **); 106 107 /* module private routines */ 108 static void free_cache(void); 109 static void update_cache(rcm_handle_t *hd); 110 static int devfs_entry(di_node_t node, di_minor_t minor, void *arg); 111 static void cache_remove(net_cache_t *node); 112 static net_cache_t *cache_lookup(const char *resource); 113 static void free_node(net_cache_t *); 114 static void cache_insert(net_cache_t *); 115 static boolean_t is_aggregated(char *driver, int ppa); 116 117 /* 118 * Module-Private data 119 */ 120 static struct rcm_mod_ops net_ops = { 121 RCM_MOD_OPS_VERSION, 122 net_register, 123 net_unregister, 124 net_getinfo, 125 net_suspend, 126 net_resume, 127 net_offline, 128 net_online, 129 net_remove, 130 NULL, /* request_capacity_change */ 131 NULL, /* notify_capacity_change */ 132 net_notify_event 133 }; 134 135 /* 136 * Module Interface Routines 137 */ 138 139 /* 140 * rcm_mod_init() 141 * 142 * Update registrations, and return the ops structure. 143 */ 144 struct rcm_mod_ops * 145 rcm_mod_init(void) 146 { 147 cache_head.next = &cache_tail; 148 cache_head.prev = NULL; 149 cache_tail.prev = &cache_head; 150 cache_tail.next = NULL; 151 (void) mutex_init(&cache_lock, NULL, NULL); 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 %I%"); 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 return (RCM_SUCCESS); 179 } 180 181 /* 182 * net_register() 183 * 184 * Make sure the cache is properly sync'ed, and its registrations 185 * are in order. 186 * 187 * Locking: the cache is locked by update_cache, and is held 188 * throughout update_cache's execution because it reads and 189 * possibly modifies cache links continuously. 190 */ 191 static int 192 net_register(rcm_handle_t *hd) 193 { 194 if (events_registered == 0) { 195 (void) rcm_register_event(hd, "SUNW_resource/new", 0, NULL); 196 events_registered++; 197 } 198 update_cache(hd); 199 return (RCM_SUCCESS); 200 } 201 202 /* 203 * net_unregister() 204 * 205 * Manually walk through the cache, unregistering all the networks. 206 * 207 * Locking: the cache is locked throughout the execution of this routine 208 * because it reads and modifies cache links continuously. 209 */ 210 static int 211 net_unregister(rcm_handle_t *hd) 212 { 213 net_cache_t *probe; 214 215 assert(hd != NULL); 216 217 /* Walk the cache, unregistering everything */ 218 (void) mutex_lock(&cache_lock); 219 probe = cache_head.next; 220 while (probe != &cache_tail) { 221 (void) rcm_unregister_interest(hd, probe->resource, 0); 222 cache_remove(probe); 223 free_node(probe); 224 probe = cache_head.next; 225 } 226 (void) mutex_unlock(&cache_lock); 227 if (events_registered > 0) { 228 (void) rcm_unregister_event(hd, "SUNW_resource/new", 0); 229 events_registered--; 230 } 231 return (RCM_SUCCESS); 232 } 233 234 /* 235 * Since all we do is pass operations thru, we provide a general 236 * routine for passing through operations. 237 */ 238 /*ARGSUSED*/ 239 static int 240 net_passthru(rcm_handle_t *hd, int op, const char *rsrc, uint_t flag, 241 char **reason, rcm_info_t **dependent_reason, void *arg) 242 { 243 net_cache_t *node; 244 char *exported; 245 int rv; 246 247 /* 248 * Lock the cache just long enough to extract information about this 249 * resource. 250 */ 251 (void) mutex_lock(&cache_lock); 252 node = cache_lookup(rsrc); 253 if (!node) { 254 rcm_log_message(RCM_WARNING, 255 _("NET: unrecognized resource %s\n"), rsrc); 256 (void) mutex_unlock(&cache_lock); 257 return (RCM_SUCCESS); 258 } 259 260 /* 261 * Since node->exported could be freed after we drop cache_lock, 262 * allocate a stack-local copy. We don't use strdup() because some of 263 * the operations (such as NET_REMOVE) are not allowed to fail. Note 264 * that node->exported is never more than MAXPATHLEN bytes. 265 */ 266 exported = alloca(strlen(node->exported) + 1); 267 (void) strlcpy(exported, node->exported, strlen(node->exported) + 1); 268 269 /* 270 * Remove notifications are unconditional in the RCM state model, 271 * so it's safe to remove the node from the cache at this point. 272 * And we need to remove it so that we will recognize it as a new 273 * resource following the reattachment of the resource. 274 */ 275 if (op == NET_REMOVE) { 276 cache_remove(node); 277 free_node(node); 278 } 279 (void) mutex_unlock(&cache_lock); 280 281 switch (op) { 282 case NET_SUSPEND: 283 rv = rcm_request_suspend(hd, exported, flag, 284 (timespec_t *)arg, dependent_reason); 285 break; 286 case NET_OFFLINE: 287 if (is_aggregated(node->driver, node->ppa)) { 288 /* device is aggregated */ 289 *reason = strdup(gettext( 290 "Resource is in use by aggregation")); 291 if (*reason == NULL) { 292 rcm_log_message(RCM_ERROR, 293 gettext("NET: malloc failure")); 294 } 295 errno = EBUSY; 296 return (RCM_FAILURE); 297 } 298 299 rv = rcm_request_offline(hd, exported, flag, dependent_reason); 300 break; 301 case NET_ONLINE: 302 rv = rcm_notify_online(hd, exported, flag, dependent_reason); 303 break; 304 case NET_REMOVE: 305 rv = rcm_notify_remove(hd, exported, flag, dependent_reason); 306 break; 307 case NET_RESUME: 308 rv = rcm_notify_resume(hd, exported, flag, dependent_reason); 309 break; 310 default: 311 rcm_log_message(RCM_WARNING, 312 _("NET: bad RCM operation %1$d for %2$s\n"), op, exported); 313 errno = EINVAL; 314 return (RCM_FAILURE); 315 } 316 317 if (rv != RCM_SUCCESS) { 318 char format[256]; 319 (void) snprintf(format, sizeof (format), 320 _("RCM operation on dependent %s did not succeed"), 321 exported); 322 rcm_log_message(RCM_WARNING, "NET: %s\n", format); 323 } 324 325 return (rv); 326 } 327 328 329 /* 330 * net_offline() 331 * 332 * Determine dependents of the resource being offlined, and offline 333 * them all. 334 */ 335 static int 336 net_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, 337 char **reason, rcm_info_t **dependent_reason) 338 { 339 assert(hd != NULL); 340 assert(rsrc != NULL); 341 assert(id == (id_t)0); 342 assert(reason != NULL); 343 assert(dependent_reason != NULL); 344 345 rcm_log_message(RCM_TRACE1, "NET: offline(%s)\n", rsrc); 346 347 return (net_passthru(hd, NET_OFFLINE, rsrc, flags, reason, 348 dependent_reason, NULL)); 349 } 350 351 /* 352 * net_online() 353 * 354 * Remount the previously offlined filesystem, and online its dependents. 355 */ 356 static int 357 net_online(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, char **reason, 358 rcm_info_t **dependent_reason) 359 { 360 assert(hd != NULL); 361 assert(rsrc != NULL); 362 assert(id == (id_t)0); 363 364 rcm_log_message(RCM_TRACE1, "NET: online(%s)\n", rsrc); 365 366 return (net_passthru(hd, NET_ONLINE, rsrc, flag, reason, 367 dependent_reason, NULL)); 368 } 369 370 /* 371 * net_getinfo() 372 * 373 * Gather usage information for this resource. 374 * 375 * Locking: the cache is locked while this routine looks up the 376 * resource and extracts copies of any piece of information it needs. 377 * The cache is then unlocked, and this routine performs the rest of 378 * its functions without touching any part of the cache. 379 */ 380 /*ARGSUSED*/ 381 static int 382 net_getinfo(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, 383 char **info, char **errstr, nvlist_t *proplist, rcm_info_t **depend_info) 384 { 385 int len; 386 char *exported; 387 char nic[64]; 388 const char *info_fmt; 389 net_cache_t *node; 390 391 assert(hd != NULL); 392 assert(rsrc != NULL); 393 assert(id == (id_t)0); 394 assert(info != NULL); 395 assert(depend_info != NULL); 396 397 rcm_log_message(RCM_TRACE1, "NET: getinfo(%s)\n", rsrc); 398 399 info_fmt = _("Network interface %s"); 400 401 (void) mutex_lock(&cache_lock); 402 node = cache_lookup(rsrc); 403 if (!node) { 404 rcm_log_message(RCM_WARNING, 405 _("NET: unrecognized resource %s\n"), rsrc); 406 (void) mutex_unlock(&cache_lock); 407 errno = ENOENT; 408 return (RCM_FAILURE); 409 } 410 exported = strdup(node->exported); 411 if (!exported) { 412 rcm_log_message(RCM_ERROR, _("NET: strdup failure")); 413 (void) mutex_unlock(&cache_lock); 414 return (RCM_FAILURE); 415 } 416 417 (void) snprintf(nic, sizeof (nic), "%s%d", node->driver, node->ppa); 418 (void) mutex_unlock(&cache_lock); 419 420 len = strlen(info_fmt) + strlen(nic) + 1; 421 if ((*info = (char *)malloc(len)) == NULL) { 422 rcm_log_message(RCM_ERROR, _("NET: malloc failure")); 423 free(exported); 424 return (RCM_FAILURE); 425 } 426 427 /* Fill in the string */ 428 (void) snprintf(*info, len, info_fmt, nic); 429 430 /* Get dependent info if requested */ 431 if ((flag & RCM_INCLUDE_DEPENDENT) || (flag & RCM_INCLUDE_SUBTREE)) { 432 (void) rcm_get_info(hd, exported, flag, depend_info); 433 } 434 435 (void) nvlist_add_string(proplist, RCM_CLIENT_NAME, "SunOS"); 436 (void) nvlist_add_string_array(proplist, RCM_CLIENT_EXPORTS, 437 &exported, 1); 438 439 free(exported); 440 return (RCM_SUCCESS); 441 } 442 443 /* 444 * net_suspend() 445 * 446 * Notify all dependents that the resource is being suspended. 447 * Since no real operation is involved, QUERY or not doesn't matter. 448 * 449 * Locking: the cache is only used to retrieve some information about 450 * this resource, so it is only locked during that retrieval. 451 */ 452 static int 453 net_suspend(rcm_handle_t *hd, char *rsrc, id_t id, timespec_t *interval, 454 uint_t flag, char **reason, rcm_info_t **dependent_reason) 455 { 456 assert(hd != NULL); 457 assert(rsrc != NULL); 458 assert(id == (id_t)0); 459 assert(interval != NULL); 460 assert(reason != NULL); 461 assert(dependent_reason != NULL); 462 463 rcm_log_message(RCM_TRACE1, "NET: suspend(%s)\n", rsrc); 464 465 return (net_passthru(hd, NET_SUSPEND, rsrc, flag, reason, 466 dependent_reason, (void *)interval)); 467 } 468 469 /* 470 * net_resume() 471 * 472 * Resume all the dependents of a suspended network. 473 * 474 * Locking: the cache is only used to retrieve some information about 475 * this resource, so it is only locked during that retrieval. 476 */ 477 static int 478 net_resume(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, char **info, 479 rcm_info_t **dependent_info) 480 { 481 assert(hd != NULL); 482 assert(rsrc != NULL); 483 assert(id == (id_t)0); 484 assert(info != NULL); 485 assert(dependent_info != NULL); 486 487 rcm_log_message(RCM_TRACE1, "NET: resume(%s)\n", rsrc); 488 489 return (net_passthru(hd, NET_RESUME, rsrc, flag, info, dependent_info, 490 NULL)); 491 } 492 493 /* 494 * net_remove() 495 * 496 * This is another NO-OP for us, we just passthru the information. We 497 * don't need to remove it from our cache. We don't unregister 498 * interest at this point either; the network device name is still 499 * around. This way we don't have to change this logic when we 500 * gain the ability to learn about DR attach operations. 501 */ 502 static int 503 net_remove(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, char **info, 504 rcm_info_t **dependent_info) 505 { 506 assert(hd != NULL); 507 assert(rsrc != NULL); 508 assert(id == (id_t)0); 509 assert(info != NULL); 510 assert(dependent_info != NULL); 511 512 rcm_log_message(RCM_TRACE1, "NET: remove(%s)\n", rsrc); 513 514 return (net_passthru(hd, NET_REMOVE, rsrc, flag, info, dependent_info, 515 NULL)); 516 } 517 518 /* 519 * net_notify_event() 520 * 521 * Receive new resource events. If the resource is a network 522 * device, then pass up a notify for it too. No need to cache 523 * it, though, since we'll do that in our register() routine the 524 * next time we're called. 525 */ 526 /*ARGSUSED*/ 527 static int 528 net_notify_event(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, 529 char **errstr, nvlist_t *nvl, rcm_info_t **result) 530 { 531 char *devname = NULL, *nodetype, *driver, *kpath; 532 char ifname[MAXPATHLEN]; 533 di_node_t node; 534 di_minor_t minor; 535 nvlist_t *nvlist; 536 nvpair_t *nvp = NULL; 537 int rv; 538 539 assert(hd != NULL); 540 assert(rsrc != NULL); 541 assert(id == (id_t)0); 542 assert(nvl != NULL); 543 assert(result != NULL); 544 545 rcm_log_message(RCM_TRACE1, "NET: notify_event(%s)\n", rsrc); 546 547 if (strcmp(rsrc, "SUNW_resource/new") != 0) { 548 /* how did we get this? we didn't ask for it! */ 549 rcm_log_message(RCM_WARNING, 550 _("NET: unrecognized event for %s\n"), rsrc); 551 return (RCM_FAILURE); 552 } 553 554 /* is it a /devices resource? */ 555 /* 556 * note: we'd like to use nvlist_lookup_string, but a bug in 557 * libnvpair breaks lookups, so we have to walk it ourself. 558 */ 559 #ifdef NVLIST_LOOKUP_NOTBROKEN 560 if (nvlist_lookup_string(nvl, RCM_RSRCNAME, &devname) != 0) { 561 /* resource not found */ 562 rcm_log_message(RCM_WARNING, 563 _("NET: event without resource name\n")); 564 return (RCM_FAILURE); 565 } 566 #else 567 while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 568 if (strcmp(nvpair_name(nvp), RCM_RSRCNAME) == 0) { 569 if (nvpair_value_string(nvp, &devname) != 0) { 570 rcm_log_message(RCM_WARNING, 571 _("NET: cannot get event " 572 "resource value\n")); 573 return (RCM_FAILURE); 574 } 575 break; 576 } 577 } 578 if (devname == NULL) { 579 rcm_log_message(RCM_WARNING, 580 _("NET: event without resource name\n")); 581 return (RCM_FAILURE); 582 } 583 #endif 584 rcm_log_message(RCM_TRACE1, "NET: new rsrc(%s)\n", devname); 585 if (strncmp(devname, "/devices/", strlen("/devices/")) != 0) { 586 /* not a /devices resource, we ignore it */ 587 rcm_log_message(RCM_TRACE1, "NET: %s not for us\n", devname); 588 return (RCM_SUCCESS); 589 } 590 kpath = devname + strlen("/devices"); 591 if (strncmp(kpath, "/pseudo/", strlen("/pseudo/")) == 0) { 592 /* pseudo device , not for us */ 593 rcm_log_message(RCM_TRACE1, "NET: ignoring pseudo %s\n", 594 devname); 595 return (RCM_SUCCESS); 596 } 597 598 /* just snapshot the specific tree we need */ 599 if ((node = di_init(kpath, DINFOMINOR)) == NULL) { 600 rcm_log_message(RCM_ERROR, 601 _("NET: cannot initialize device tree\n")); 602 return (RCM_FAILURE); 603 } 604 605 /* network devices usually only have a single minor node */ 606 if ((minor = di_minor_next(node, DI_MINOR_NIL)) == DI_MINOR_NIL) { 607 rcm_log_message(RCM_WARNING, 608 _("NET: cannot find minor for %s\n"), 609 devname); 610 di_fini(node); 611 return (RCM_FAILURE); 612 } 613 614 nodetype = di_minor_nodetype(minor); 615 if ((nodetype == NULL) || (strcmp(nodetype, DDI_NT_NET) != 0)) { 616 /* doesn't look like a network device */ 617 rcm_log_message(RCM_TRACE1, "NET: %s not a NIC\n", devname); 618 goto done; 619 } 620 if ((driver = di_driver_name(node)) == NULL) { 621 rcm_log_message(RCM_TRACE1, "NET: no driver (%s)\n", devname); 622 goto done; 623 } 624 (void) snprintf(ifname, sizeof (ifname), "SUNW_network/%s%s%d", driver, 625 NET_DELIMITER, di_instance(node)); 626 627 rcm_log_message(RCM_TRACE1, "NET: notifying arrival of %s\n", ifname); 628 /* build up our nvlist -- these shouldn't ever fail */ 629 if ((rv = nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0)) != 0) { 630 rcm_log_message(RCM_TRACE1, 631 "NET: nvlist alloc failed %d, errno %d\n", rv, errno); 632 } 633 634 if ((rv = nvlist_add_string(nvlist, RCM_RSRCNAME, ifname)) != 0) { 635 rcm_log_message(RCM_TRACE1, 636 "NET: nvlist_add_string failed %d, errno %d\n", rv, errno); 637 } 638 /* now we need to do our own notification */ 639 rv = rcm_notify_event(hd, "SUNW_resource/new", 0, nvlist, result); 640 if (rv != RCM_SUCCESS) { 641 rcm_log_message(RCM_TRACE1, 642 "NET: notify_event failed: %s\n", strerror(errno)); 643 } else { 644 rcm_log_message(RCM_TRACE1, "NET: notify_event succeeded\n"); 645 } 646 647 /* and clean up our nvlist */ 648 nvlist_free(nvlist); 649 650 done: 651 di_fini(node); 652 return (RCM_SUCCESS); 653 } 654 655 /* 656 * Cache management routines. Note that the cache is implemented as a 657 * trivial linked list, and is only required because RCM doesn't 658 * provide enough state about our own registrations back to us. This 659 * linked list implementation probably clobbers the CPU cache pretty 660 * well. 661 */ 662 663 /* 664 * cache_lookup() 665 * 666 * Get a cache node for a resource. Call with cache lock held. 667 */ 668 static net_cache_t * 669 cache_lookup(const char *resource) 670 { 671 net_cache_t *probe; 672 probe = cache_head.next; 673 while (probe != &cache_tail) { 674 if (probe->resource && 675 (strcmp(resource, probe->resource) == 0)) { 676 return (probe); 677 } 678 probe = probe->next; 679 } 680 return (NULL); 681 } 682 683 /* 684 * free_node() 685 * 686 * Free a node. Make sure it isn't in the list! 687 */ 688 static void 689 free_node(net_cache_t *node) 690 { 691 if (node) { 692 free(node->resource); 693 free(node->exported); 694 free(node->driver); 695 free(node); 696 } 697 } 698 699 /* 700 * cache_insert() 701 * 702 * Call with the cache_lock held. 703 */ 704 static void 705 cache_insert(net_cache_t *node) 706 { 707 /* insert at the head for best performance */ 708 node->next = cache_head.next; 709 node->prev = &cache_head; 710 711 node->next->prev = node; 712 node->prev->next = node; 713 } 714 715 /* 716 * cache_remove() 717 * 718 * Call with the cache_lock held. 719 */ 720 static void 721 cache_remove(net_cache_t *node) 722 { 723 node->next->prev = node->prev; 724 node->prev->next = node->next; 725 node->next = NULL; 726 node->prev = NULL; 727 } 728 729 /* 730 * devfs_entry() 731 * 732 * Call with the cache_lock held. 733 */ 734 /*ARGSUSED*/ 735 static int 736 devfs_entry(di_node_t node, di_minor_t minor, void *arg) 737 { 738 char ifname [MAXPATHLEN]; /* should be big enough! */ 739 char *devfspath; 740 char resource[MAXPATHLEN]; 741 char *name; 742 char *cp; 743 int instance; 744 net_cache_t *probe; 745 746 cp = di_minor_nodetype(minor); 747 if ((cp == NULL) || (strcmp(cp, DDI_NT_NET))) { 748 /* doesn't look like a network device */ 749 return (DI_WALK_CONTINUE); 750 } 751 752 name = di_driver_name(node); 753 if (name == NULL) { 754 /* what else can we do? */ 755 return (DI_WALK_CONTINUE); 756 } 757 758 instance = di_instance(node); 759 760 (void) snprintf(ifname, sizeof (ifname), "SUNW_network/%s%s%d", 761 name, NET_DELIMITER, instance); 762 763 devfspath = di_devfs_path(node); 764 if (!devfspath) { 765 /* no devfs path?!? */ 766 rcm_log_message(RCM_DEBUG, "NET: missing devfs path\n"); 767 return (DI_WALK_CONTINUE); 768 } 769 770 if (strncmp("/pseudo", devfspath, strlen("/pseudo")) == 0) { 771 /* ignore pseudo devices, probably not really NICs */ 772 rcm_log_message(RCM_DEBUG, "NET: ignoring pseudo device %s\n", 773 devfspath); 774 di_devfs_path_free(devfspath); 775 return (DI_WALK_CONTINUE); 776 } 777 778 (void) snprintf(resource, sizeof (resource), "/devices%s", devfspath); 779 di_devfs_path_free(devfspath); 780 781 probe = cache_lookup(resource); 782 if (probe != NULL) { 783 rcm_log_message(RCM_DEBUG, "NET: %s already registered\n", 784 resource); 785 probe->flags &= ~(CACHE_STALE); 786 } else { 787 rcm_log_message(RCM_DEBUG, "NET: %s is new resource\n", 788 resource); 789 probe = calloc(1, sizeof (net_cache_t)); 790 if (!probe) { 791 rcm_log_message(RCM_ERROR, _("NET: malloc failure")); 792 return (DI_WALK_CONTINUE); 793 } 794 795 probe->resource = strdup(resource); 796 probe->ppa = instance; 797 probe->driver = strdup(name); 798 probe->exported = strdup(ifname); 799 800 if ((!probe->resource) || (!probe->exported) || 801 (!probe->driver)) { 802 free_node(probe); 803 return (DI_WALK_CONTINUE); 804 } 805 806 probe->flags |= CACHE_NEW; 807 cache_insert(probe); 808 } 809 810 return (DI_WALK_CONTINUE); 811 } 812 813 /* 814 * update_cache() 815 * 816 * The devinfo tree walking code is lifted from ifconfig.c. 817 */ 818 static void 819 update_cache(rcm_handle_t *hd) 820 { 821 net_cache_t *probe; 822 di_node_t root; 823 int rv; 824 825 (void) mutex_lock(&cache_lock); 826 827 /* first we walk the entire cache, marking each entry stale */ 828 probe = cache_head.next; 829 while (probe != &cache_tail) { 830 probe->flags |= CACHE_STALE; 831 probe = probe->next; 832 } 833 834 root = di_init("/", DINFOSUBTREE | DINFOMINOR); 835 if (root == DI_NODE_NIL) { 836 goto done; 837 } 838 839 (void) di_walk_minor(root, DDI_NT_NET, DI_CHECK_ALIAS, NULL, 840 devfs_entry); 841 842 di_fini(root); 843 844 probe = cache_head.next; 845 while (probe != &cache_tail) { 846 net_cache_t *freeit; 847 if (probe->flags & CACHE_STALE) { 848 (void) rcm_unregister_interest(hd, probe->resource, 0); 849 rcm_log_message(RCM_DEBUG, "NET: unregistered %s\n", 850 probe->resource); 851 freeit = probe; 852 probe = probe->next; 853 cache_remove(freeit); 854 free_node(freeit); 855 continue; 856 } 857 858 if (!(probe->flags & CACHE_NEW)) { 859 probe = probe->next; 860 continue; 861 } 862 863 rcm_log_message(RCM_DEBUG, "NET: registering %s\n", 864 probe->resource); 865 rv = rcm_register_interest(hd, probe->resource, 0, NULL); 866 if (rv != RCM_SUCCESS) { 867 rcm_log_message(RCM_ERROR, 868 _("NET: failed to register %s\n"), 869 probe->resource); 870 } else { 871 rcm_log_message(RCM_DEBUG, 872 "NET: registered %s (as %s)\n", 873 probe->resource, probe->exported); 874 probe->flags &= ~(CACHE_NEW); 875 } 876 probe = probe->next; 877 } 878 879 done: 880 (void) mutex_unlock(&cache_lock); 881 } 882 883 /* 884 * free_cache() 885 */ 886 static void 887 free_cache(void) 888 { 889 net_cache_t *probe; 890 891 (void) mutex_lock(&cache_lock); 892 probe = cache_head.next; 893 while (probe != &cache_tail) { 894 cache_remove(probe); 895 free_node(probe); 896 probe = cache_head.next; 897 } 898 (void) mutex_unlock(&cache_lock); 899 } 900 901 /* 902 * is_aggregated() checks whether a NIC being removed is part of an 903 * aggregation. 904 */ 905 906 typedef struct aggr_walker_state_s { 907 uint_t naggr; 908 char dev_name[LIFNAMSIZ]; 909 } aggr_walker_state_t; 910 911 static int 912 aggr_walker(void *arg, laadm_grp_attr_sys_t *grp) 913 { 914 aggr_walker_state_t *state = arg; 915 laadm_port_attr_sys_t *port; 916 int i; 917 918 for (i = 0; i < grp->lg_nports; i++) { 919 port = &grp->lg_ports[i]; 920 921 rcm_log_message(RCM_TRACE1, "MAC: aggr (%d) port %s/%d\n", 922 grp->lg_key, port->lp_devname, port->lp_port); 923 924 if (strcmp(port->lp_devname, state->dev_name) != 0) 925 continue; 926 927 /* found matching MAC port */ 928 state->naggr++; 929 } 930 931 return (0); 932 } 933 934 static boolean_t 935 is_aggregated(char *driver, int ppa) 936 { 937 aggr_walker_state_t state; 938 939 state.naggr = 0; 940 (void) snprintf(state.dev_name, sizeof (state.dev_name), "%s%d", 941 driver, ppa); 942 943 if (laadm_walk_sys(aggr_walker, &state) != 0) { 944 rcm_log_message(RCM_ERROR, gettext("NET: cannot walk " 945 "aggregations (%s)\n"), strerror(errno)); 946 return (B_FALSE); 947 } 948 949 return (state.naggr > 0); 950 } 951