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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * This RCM module adds support to the RCM framework for an abstract 30 * namespace for network devices (DLPI providers). 31 */ 32 #include <alloca.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <unistd.h> 36 #include <assert.h> 37 #include <string.h> 38 #include <synch.h> 39 #include <libintl.h> 40 #include <errno.h> 41 #include <libdevinfo.h> 42 #include <sys/types.h> 43 #include <net/if.h> 44 #include <libdllink.h> 45 #include "rcm_module.h" 46 47 /* 48 * Definitions 49 */ 50 #ifndef lint 51 #define _(x) gettext(x) 52 #else 53 #define _(x) x 54 #endif 55 56 #define CACHE_STALE 1 /* flags */ 57 #define CACHE_NEW 2 /* flags */ 58 59 /* devfsadm attach nvpair values */ 60 #define PROP_NV_DDI_NETWORK "ddi_network" 61 62 /* 63 * Global NIC list to be configured after DR-attach 64 */ 65 struct ni_list { 66 struct ni_list *next; 67 char dev[MAXNAMELEN]; /* device instance name (le0, ie0, etc.) */ 68 }; 69 70 static struct ni_list *nil_head = NULL; /* Global new if list */ 71 static mutex_t nil_lock; /* NIC list lock */ 72 73 /* operations */ 74 #define NET_OFFLINE 1 75 #define NET_ONLINE 2 76 #define NET_REMOVE 3 77 #define NET_SUSPEND 4 78 #define NET_RESUME 5 79 80 typedef struct net_cache 81 { 82 char *resource; 83 datalink_id_t linkid; 84 int flags; 85 struct net_cache *next; 86 struct net_cache *prev; 87 } net_cache_t; 88 89 static net_cache_t cache_head; 90 static net_cache_t cache_tail; 91 static mutex_t cache_lock; 92 static int events_registered = 0; 93 94 struct devfs_minor_data { 95 int32_t minor_type; 96 char *minor_name; 97 char *minor_node_type; 98 }; 99 100 /* module interface routines */ 101 static int net_register(rcm_handle_t *); 102 static int net_unregister(rcm_handle_t *); 103 static int net_getinfo(rcm_handle_t *, char *, id_t, uint_t, char **, 104 char **, nvlist_t *, rcm_info_t **); 105 static int net_suspend(rcm_handle_t *, char *, id_t, timespec_t *, 106 uint_t, char **, rcm_info_t **); 107 static int net_resume(rcm_handle_t *, char *, id_t, uint_t, char **, 108 rcm_info_t **); 109 static int net_offline(rcm_handle_t *, char *, id_t, uint_t, char **, 110 rcm_info_t **); 111 static int net_online(rcm_handle_t *, char *, id_t, uint_t, char **, 112 rcm_info_t **); 113 static int net_remove(rcm_handle_t *, char *, id_t, uint_t, char **, 114 rcm_info_t **); 115 static int net_notify_event(rcm_handle_t *, char *, id_t, uint_t, 116 char **, nvlist_t *, rcm_info_t **); 117 118 /* module private routines */ 119 static void free_cache(void); 120 static void update_cache(rcm_handle_t *hd); 121 static int devfs_entry(di_node_t node, di_minor_t minor, void *arg); 122 static void cache_remove(net_cache_t *node); 123 static net_cache_t *cache_lookup(const char *resource); 124 static void free_node(net_cache_t *); 125 static void cache_insert(net_cache_t *); 126 static int notify_new_link(rcm_handle_t *, const char *); 127 static void process_minor(char *, int, struct devfs_minor_data *); 128 static int process_nvlist(rcm_handle_t *, nvlist_t *); 129 130 /* 131 * Module-Private data 132 */ 133 static struct rcm_mod_ops net_ops = { 134 RCM_MOD_OPS_VERSION, 135 net_register, 136 net_unregister, 137 net_getinfo, 138 net_suspend, 139 net_resume, 140 net_offline, 141 net_online, 142 net_remove, 143 NULL, 144 NULL, 145 net_notify_event 146 }; 147 148 /* 149 * Module Interface Routines 150 */ 151 152 /* 153 * rcm_mod_init() 154 * 155 * Update registrations, and return the ops structure. 156 */ 157 struct rcm_mod_ops * 158 rcm_mod_init(void) 159 { 160 cache_head.next = &cache_tail; 161 cache_head.prev = NULL; 162 cache_tail.prev = &cache_head; 163 cache_tail.next = NULL; 164 (void) mutex_init(&cache_lock, NULL, NULL); 165 166 /* Return the ops vectors */ 167 return (&net_ops); 168 } 169 170 /* 171 * rcm_mod_info() 172 * 173 * Return a string describing this module. 174 */ 175 const char * 176 rcm_mod_info(void) 177 { 178 return ("Network namespace module %I%"); 179 } 180 181 /* 182 * rcm_mod_fini() 183 * 184 * Destroy the cache. 185 */ 186 int 187 rcm_mod_fini(void) 188 { 189 free_cache(); 190 (void) mutex_destroy(&cache_lock); 191 return (RCM_SUCCESS); 192 } 193 194 /* 195 * net_register() 196 * 197 * Make sure the cache is properly sync'ed, and its registrations 198 * are in order. 199 * 200 * Locking: the cache is locked by update_cache, and is held 201 * throughout update_cache's execution because it reads and 202 * possibly modifies cache links continuously. 203 */ 204 static int 205 net_register(rcm_handle_t *hd) 206 { 207 update_cache(hd); 208 /* 209 * Need to register interest in all new resources 210 * getting attached, so we get attach event notifications 211 */ 212 if (!events_registered) { 213 if (rcm_register_event(hd, RCM_RESOURCE_NETWORK_NEW, 0, NULL) 214 != RCM_SUCCESS) { 215 rcm_log_message(RCM_ERROR, 216 _("NET: failed to register %s\n"), 217 RCM_RESOURCE_NETWORK_NEW); 218 return (RCM_FAILURE); 219 } else { 220 rcm_log_message(RCM_DEBUG, _("NET: registered %s\n"), 221 RCM_RESOURCE_NETWORK_NEW); 222 events_registered++; 223 } 224 } 225 226 return (RCM_SUCCESS); 227 } 228 229 /* 230 * net_unregister() 231 * 232 * Manually walk through the cache, unregistering all the networks. 233 * 234 * Locking: the cache is locked throughout the execution of this routine 235 * because it reads and modifies cache links continuously. 236 */ 237 static int 238 net_unregister(rcm_handle_t *hd) 239 { 240 net_cache_t *probe; 241 242 assert(hd != NULL); 243 244 /* Walk the cache, unregistering everything */ 245 (void) mutex_lock(&cache_lock); 246 probe = cache_head.next; 247 while (probe != &cache_tail) { 248 (void) rcm_unregister_interest(hd, probe->resource, 0); 249 cache_remove(probe); 250 free_node(probe); 251 probe = cache_head.next; 252 } 253 (void) mutex_unlock(&cache_lock); 254 255 /* 256 * Need to unregister interest in all new resources 257 */ 258 if (events_registered) { 259 if (rcm_unregister_event(hd, RCM_RESOURCE_NETWORK_NEW, 0) 260 != RCM_SUCCESS) { 261 rcm_log_message(RCM_ERROR, 262 _("NET: failed to unregister %s\n"), 263 RCM_RESOURCE_NETWORK_NEW); 264 return (RCM_FAILURE); 265 } else { 266 rcm_log_message(RCM_DEBUG, _("NET: unregistered %s\n"), 267 RCM_RESOURCE_NETWORK_NEW); 268 events_registered--; 269 } 270 } 271 272 return (RCM_SUCCESS); 273 } 274 275 /* 276 * Since all we do is pass operations thru, we provide a general 277 * routine for passing through operations. 278 */ 279 /*ARGSUSED*/ 280 static int 281 net_passthru(rcm_handle_t *hd, int op, const char *rsrc, uint_t flag, 282 char **reason, rcm_info_t **dependent_reason, void *arg) 283 { 284 net_cache_t *node; 285 char *exported; 286 datalink_id_t linkid; 287 int len; 288 int rv; 289 290 /* 291 * Lock the cache just long enough to extract information about this 292 * resource. 293 */ 294 (void) mutex_lock(&cache_lock); 295 node = cache_lookup(rsrc); 296 if (!node) { 297 rcm_log_message(RCM_WARNING, 298 _("NET: unrecognized resource %s\n"), rsrc); 299 (void) mutex_unlock(&cache_lock); 300 return (RCM_SUCCESS); 301 } 302 303 /* 304 * Since node could be freed after we drop cache_lock, allocate a 305 * stack-local copy. We don't use malloc() because some of the 306 * operations (such as NET_REMOVE) are not allowed to fail. Note 307 * that exported is never more than MAXPATHLEN bytes. 308 */ 309 len = strlen("SUNW_datalink/") + LINKID_STR_WIDTH + 1; 310 exported = alloca(len); 311 linkid = node->linkid; 312 (void) snprintf(exported, len, "SUNW_datalink/%u", linkid); 313 314 /* 315 * Remove notifications are unconditional in the RCM state model, 316 * so it's safe to remove the node from the cache at this point. 317 * And we need to remove it so that we will recognize it as a new 318 * resource following the reattachment of the resource. 319 */ 320 if (op == NET_REMOVE) { 321 cache_remove(node); 322 free_node(node); 323 } 324 (void) mutex_unlock(&cache_lock); 325 326 switch (op) { 327 case NET_SUSPEND: 328 rv = rcm_request_suspend(hd, exported, flag, 329 (timespec_t *)arg, dependent_reason); 330 break; 331 case NET_OFFLINE: 332 rv = rcm_request_offline(hd, exported, flag, dependent_reason); 333 break; 334 case NET_ONLINE: 335 rv = rcm_notify_online(hd, exported, flag, dependent_reason); 336 break; 337 case NET_REMOVE: 338 rv = rcm_notify_remove(hd, exported, flag, dependent_reason); 339 if (rv == RCM_SUCCESS) { 340 rcm_log_message(RCM_DEBUG, 341 _("NET: mark link %d as removed\n"), linkid); 342 343 /* 344 * Delete active linkprop before this active link 345 * is deleted. 346 */ 347 (void) dladm_set_linkprop(linkid, NULL, NULL, 0, 348 DLADM_OPT_ACTIVE); 349 (void) dladm_destroy_datalink_id(linkid, 350 DLADM_OPT_ACTIVE); 351 } 352 break; 353 case NET_RESUME: 354 rv = rcm_notify_resume(hd, exported, flag, dependent_reason); 355 break; 356 default: 357 rcm_log_message(RCM_WARNING, 358 _("NET: bad RCM operation %1$d for %2$s\n"), op, exported); 359 errno = EINVAL; 360 return (RCM_FAILURE); 361 } 362 363 if (rv != RCM_SUCCESS) { 364 char format[256]; 365 (void) snprintf(format, sizeof (format), 366 _("RCM operation on dependent %s did not succeed"), 367 exported); 368 rcm_log_message(RCM_WARNING, "NET: %s\n", format); 369 } 370 return (rv); 371 } 372 373 374 /* 375 * net_offline() 376 * 377 * Determine dependents of the resource being offlined, and offline 378 * them all. 379 */ 380 static int 381 net_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, 382 char **reason, rcm_info_t **dependent_reason) 383 { 384 assert(hd != NULL); 385 assert(rsrc != NULL); 386 assert(id == (id_t)0); 387 assert(reason != NULL); 388 assert(dependent_reason != NULL); 389 390 rcm_log_message(RCM_TRACE1, _("NET: offline(%s)\n"), rsrc); 391 392 return (net_passthru(hd, NET_OFFLINE, rsrc, flags, reason, 393 dependent_reason, NULL)); 394 } 395 396 /* 397 * net_online() 398 * 399 * Online the previously offlined resource, and online its dependents. 400 */ 401 static int 402 net_online(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, char **reason, 403 rcm_info_t **dependent_reason) 404 { 405 assert(hd != NULL); 406 assert(rsrc != NULL); 407 assert(id == (id_t)0); 408 409 rcm_log_message(RCM_TRACE1, _("NET: online(%s)\n"), rsrc); 410 411 return (net_passthru(hd, NET_ONLINE, rsrc, flag, reason, 412 dependent_reason, NULL)); 413 } 414 415 /* 416 * net_getinfo() 417 * 418 * Gather usage information for this resource. 419 * 420 * Locking: the cache is locked while this routine looks up the 421 * resource and extracts copies of any piece of information it needs. 422 * The cache is then unlocked, and this routine performs the rest of 423 * its functions without touching any part of the cache. 424 */ 425 /*ARGSUSED*/ 426 static int 427 net_getinfo(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, 428 char **info, char **errstr, nvlist_t *proplist, rcm_info_t **depend_info) 429 { 430 int len; 431 dladm_status_t status; 432 char link[MAXLINKNAMELEN]; 433 char errmsg[DLADM_STRSIZE]; 434 char *exported; 435 const char *info_fmt; 436 net_cache_t *node; 437 438 assert(hd != NULL); 439 assert(rsrc != NULL); 440 assert(id == (id_t)0); 441 assert(info != NULL); 442 assert(depend_info != NULL); 443 444 rcm_log_message(RCM_TRACE1, _("NET: getinfo(%s)\n"), rsrc); 445 446 info_fmt = _("Network interface %s"); 447 448 (void) mutex_lock(&cache_lock); 449 node = cache_lookup(rsrc); 450 if (!node) { 451 rcm_log_message(RCM_WARNING, 452 _("NET: unrecognized resource %s\n"), rsrc); 453 (void) mutex_unlock(&cache_lock); 454 errno = ENOENT; 455 return (RCM_FAILURE); 456 } 457 458 len = strlen(info_fmt) + MAXLINKNAMELEN + 1; 459 if ((status = dladm_datalink_id2info(node->linkid, NULL, NULL, NULL, 460 link, sizeof (link))) != DLADM_STATUS_OK) { 461 rcm_log_message(RCM_ERROR, 462 _("NET: usage(%s) get link name failure(%s)\n"), 463 node->resource, dladm_status2str(status, errmsg)); 464 (void) mutex_unlock(&cache_lock); 465 return (RCM_FAILURE); 466 } else if ((*info = (char *)malloc(len)) == NULL) { 467 rcm_log_message(RCM_ERROR, _("NET: malloc failure")); 468 (void) mutex_unlock(&cache_lock); 469 return (RCM_FAILURE); 470 } 471 472 /* Fill in the string */ 473 (void) snprintf(*info, len, info_fmt, link); 474 475 len = strlen("SUNW_datalink/") + LINKID_STR_WIDTH + 1; 476 exported = malloc(len); 477 if (!exported) { 478 rcm_log_message(RCM_ERROR, _("NET: allocation failure")); 479 free(*info); 480 (void) mutex_unlock(&cache_lock); 481 return (RCM_FAILURE); 482 } 483 (void) snprintf(exported, len, "SUNW_datalink/%u", node->linkid); 484 (void) mutex_unlock(&cache_lock); 485 486 /* Get dependent info if requested */ 487 if ((flag & RCM_INCLUDE_DEPENDENT) || (flag & RCM_INCLUDE_SUBTREE)) { 488 (void) rcm_get_info(hd, exported, flag, depend_info); 489 } 490 491 (void) nvlist_add_string(proplist, RCM_CLIENT_NAME, "SunOS"); 492 (void) nvlist_add_string_array(proplist, RCM_CLIENT_EXPORTS, 493 &exported, 1); 494 495 free(exported); 496 return (RCM_SUCCESS); 497 } 498 499 /* 500 * net_suspend() 501 * 502 * Notify all dependents that the resource is being suspended. 503 * Since no real operation is involved, QUERY or not doesn't matter. 504 * 505 * Locking: the cache is only used to retrieve some information about 506 * this resource, so it is only locked during that retrieval. 507 */ 508 static int 509 net_suspend(rcm_handle_t *hd, char *rsrc, id_t id, timespec_t *interval, 510 uint_t flag, char **reason, rcm_info_t **dependent_reason) 511 { 512 assert(hd != NULL); 513 assert(rsrc != NULL); 514 assert(id == (id_t)0); 515 assert(interval != NULL); 516 assert(reason != NULL); 517 assert(dependent_reason != NULL); 518 519 rcm_log_message(RCM_TRACE1, _("NET: suspend(%s)\n"), rsrc); 520 521 return (net_passthru(hd, NET_SUSPEND, rsrc, flag, reason, 522 dependent_reason, (void *)interval)); 523 } 524 525 /* 526 * net_resume() 527 * 528 * Resume all the dependents of a suspended network. 529 * 530 * Locking: the cache is only used to retrieve some information about 531 * this resource, so it is only locked during that retrieval. 532 */ 533 static int 534 net_resume(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, char **info, 535 rcm_info_t **dependent_info) 536 { 537 assert(hd != NULL); 538 assert(rsrc != NULL); 539 assert(id == (id_t)0); 540 assert(info != NULL); 541 assert(dependent_info != NULL); 542 543 rcm_log_message(RCM_TRACE1, _("NET: resume(%s)\n"), rsrc); 544 545 return (net_passthru(hd, NET_RESUME, rsrc, flag, info, dependent_info, 546 NULL)); 547 } 548 549 /* 550 * net_remove() 551 * 552 * This is another NO-OP for us, we just passthru the information. We 553 * don't need to remove it from our cache. We don't unregister 554 * interest at this point either; the network device name is still 555 * around. This way we don't have to change this logic when we 556 * gain the ability to learn about DR attach operations. 557 */ 558 static int 559 net_remove(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, char **info, 560 rcm_info_t **dependent_info) 561 { 562 assert(hd != NULL); 563 assert(rsrc != NULL); 564 assert(id == (id_t)0); 565 assert(info != NULL); 566 assert(dependent_info != NULL); 567 568 rcm_log_message(RCM_TRACE1, _("NET: remove(%s)\n"), rsrc); 569 570 return (net_passthru(hd, NET_REMOVE, rsrc, flag, info, dependent_info, 571 NULL)); 572 } 573 574 /* 575 * Cache management routines. Note that the cache is implemented as a 576 * trivial linked list, and is only required because RCM doesn't 577 * provide enough state about our own registrations back to us. This 578 * linked list implementation probably clobbers the CPU cache pretty 579 * well. 580 */ 581 582 /* 583 * cache_lookup() 584 * 585 * Get a cache node for a resource. Call with cache lock held. 586 */ 587 static net_cache_t * 588 cache_lookup(const char *resource) 589 { 590 net_cache_t *probe; 591 probe = cache_head.next; 592 while (probe != &cache_tail) { 593 if (probe->resource && 594 (strcmp(resource, probe->resource) == 0)) { 595 return (probe); 596 } 597 probe = probe->next; 598 } 599 return (NULL); 600 } 601 602 /* 603 * free_node() 604 * 605 * Free a node. Make sure it isn't in the list! 606 */ 607 static void 608 free_node(net_cache_t *node) 609 { 610 if (node) { 611 free(node->resource); 612 free(node); 613 } 614 } 615 616 /* 617 * cache_insert() 618 * 619 * Call with the cache_lock held. 620 */ 621 static void 622 cache_insert(net_cache_t *node) 623 { 624 /* insert at the head for best performance */ 625 node->next = cache_head.next; 626 node->prev = &cache_head; 627 628 node->next->prev = node; 629 node->prev->next = node; 630 } 631 632 /* 633 * cache_remove() 634 * 635 * Call with the cache_lock held. 636 */ 637 static void 638 cache_remove(net_cache_t *node) 639 { 640 node->next->prev = node->prev; 641 node->prev->next = node->next; 642 node->next = NULL; 643 node->prev = NULL; 644 } 645 646 /* 647 * devfs_entry() 648 * 649 * Call with the cache_lock held. 650 */ 651 /*ARGSUSED*/ 652 static int 653 devfs_entry(di_node_t node, di_minor_t minor, void *arg) 654 { 655 char *devfspath; 656 char resource[MAXPATHLEN]; 657 char dev[MAXNAMELEN]; 658 datalink_id_t linkid; 659 char *drv; 660 char *cp; 661 net_cache_t *probe; 662 663 cp = di_minor_nodetype(minor); 664 if ((cp == NULL) || (strcmp(cp, DDI_NT_NET))) { 665 /* doesn't look like a network device */ 666 return (DI_WALK_CONTINUE); 667 } 668 669 drv = di_driver_name(node); 670 if (drv == NULL) { 671 /* what else can we do? */ 672 return (DI_WALK_CONTINUE); 673 } 674 675 devfspath = di_devfs_path(node); 676 if (!devfspath) { 677 /* no devfs path?!? */ 678 rcm_log_message(RCM_DEBUG, _("NET: missing devfs path\n")); 679 return (DI_WALK_CONTINUE); 680 } 681 682 if (strncmp("/pseudo", devfspath, strlen("/pseudo")) == 0) { 683 /* ignore pseudo devices, probably not really NICs */ 684 rcm_log_message(RCM_DEBUG, 685 _("NET: ignoring pseudo device %s\n"), devfspath); 686 di_devfs_path_free(devfspath); 687 return (DI_WALK_CONTINUE); 688 } 689 690 (void) snprintf(resource, sizeof (resource), "/devices%s", devfspath); 691 di_devfs_path_free(devfspath); 692 693 (void) snprintf(dev, sizeof (dev), "%s%d", drv, di_instance(node)); 694 if (dladm_dev2linkid(dev, &linkid) != DLADM_STATUS_OK) { 695 rcm_log_message(RCM_DEBUG, 696 _("NET: failed to find the linkid for %s\n"), dev); 697 return (DI_WALK_CONTINUE); 698 } 699 700 probe = cache_lookup(resource); 701 if (probe != NULL) { 702 rcm_log_message(RCM_DEBUG, 703 _("NET: %s already registered (linkid %u)\n"), 704 resource, linkid); 705 probe->linkid = linkid; 706 probe->flags &= ~(CACHE_STALE); 707 } else { 708 rcm_log_message(RCM_DEBUG, 709 _("NET: %s is new resource (linkid %u)\n"), 710 resource, linkid); 711 probe = calloc(1, sizeof (net_cache_t)); 712 if (!probe) { 713 rcm_log_message(RCM_ERROR, _("NET: malloc failure")); 714 return (DI_WALK_CONTINUE); 715 } 716 717 probe->resource = strdup(resource); 718 probe->linkid = linkid; 719 720 if (!probe->resource) { 721 free_node(probe); 722 return (DI_WALK_CONTINUE); 723 } 724 725 probe->flags |= CACHE_NEW; 726 cache_insert(probe); 727 } 728 729 return (DI_WALK_CONTINUE); 730 } 731 732 /* 733 * update_cache() 734 * 735 * The devinfo tree walking code is lifted from ifconfig.c. 736 */ 737 static void 738 update_cache(rcm_handle_t *hd) 739 { 740 net_cache_t *probe; 741 di_node_t root; 742 int rv; 743 744 (void) mutex_lock(&cache_lock); 745 746 /* first we walk the entire cache, marking each entry stale */ 747 probe = cache_head.next; 748 while (probe != &cache_tail) { 749 probe->flags |= CACHE_STALE; 750 probe = probe->next; 751 } 752 753 root = di_init("/", DINFOSUBTREE | DINFOMINOR); 754 if (root == DI_NODE_NIL) { 755 goto done; 756 } 757 758 (void) di_walk_minor(root, DDI_NT_NET, DI_CHECK_ALIAS, NULL, 759 devfs_entry); 760 761 di_fini(root); 762 763 probe = cache_head.next; 764 while (probe != &cache_tail) { 765 net_cache_t *freeit; 766 if (probe->flags & CACHE_STALE) { 767 (void) rcm_unregister_interest(hd, probe->resource, 0); 768 rcm_log_message(RCM_DEBUG, _("NET: unregistered %s\n"), 769 probe->resource); 770 freeit = probe; 771 probe = probe->next; 772 cache_remove(freeit); 773 free_node(freeit); 774 continue; 775 } 776 777 if (!(probe->flags & CACHE_NEW)) { 778 probe = probe->next; 779 continue; 780 } 781 782 rcm_log_message(RCM_DEBUG, _("NET: registering %s\n"), 783 probe->resource); 784 rv = rcm_register_interest(hd, probe->resource, 0, NULL); 785 if (rv != RCM_SUCCESS) { 786 rcm_log_message(RCM_ERROR, 787 _("NET: failed to register %s\n"), 788 probe->resource); 789 } else { 790 rcm_log_message(RCM_DEBUG, 791 _("NET: registered %s as SUNW_datalink/%u\n"), 792 probe->resource, probe->linkid); 793 probe->flags &= ~(CACHE_NEW); 794 } 795 probe = probe->next; 796 } 797 798 done: 799 (void) mutex_unlock(&cache_lock); 800 } 801 802 /* 803 * free_cache() 804 */ 805 static void 806 free_cache(void) 807 { 808 net_cache_t *probe; 809 810 (void) mutex_lock(&cache_lock); 811 probe = cache_head.next; 812 while (probe != &cache_tail) { 813 cache_remove(probe); 814 free_node(probe); 815 probe = cache_head.next; 816 } 817 (void) mutex_unlock(&cache_lock); 818 } 819 820 /* 821 * net_notify_event - Project private implementation to receive new 822 * resource events. It intercepts all new resource 823 * events. If the new resource is a network resource, 824 * pass up a event for the resource. The new resource 825 * need not be cached, since it is done at register again. 826 */ 827 /*ARGSUSED*/ 828 static int 829 net_notify_event(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, 830 char **errorp, nvlist_t *nvl, rcm_info_t **depend_info) 831 { 832 assert(hd != NULL); 833 assert(rsrc != NULL); 834 assert(id == (id_t)0); 835 assert(nvl != NULL); 836 837 rcm_log_message(RCM_TRACE1, _("NET: notify_event(%s)\n"), rsrc); 838 839 if (strcmp(rsrc, RCM_RESOURCE_NETWORK_NEW) != 0) { 840 rcm_log_message(RCM_INFO, 841 _("NET: unrecognized event for %s\n"), rsrc); 842 errno = EINVAL; 843 return (RCM_FAILURE); 844 } 845 846 /* Update cache to reflect latest physical links */ 847 update_cache(hd); 848 849 /* Process the nvlist for the event */ 850 if (process_nvlist(hd, nvl) != 0) { 851 rcm_log_message(RCM_WARNING, 852 _("NET: Error processing resource attributes(%s)\n"), rsrc); 853 rcm_log_message(RCM_WARNING, 854 _("NET: One or more devices may not be configured.\n")); 855 } 856 857 rcm_log_message(RCM_TRACE1, 858 _("NET: notify_event: device configuration complete\n")); 859 860 return (RCM_SUCCESS); 861 } 862 863 /* 864 * process_nvlist() - Determine network interfaces on a new attach by 865 * processing the nvlist 866 */ 867 static int 868 process_nvlist(rcm_handle_t *hd, nvlist_t *nvl) 869 { 870 nvpair_t *nvp = NULL; 871 char *driver; 872 char *devfspath; 873 int32_t instance; 874 char *minor_byte_array; /* packed nvlist of minor_data */ 875 uint_t nminor; /* # of minor nodes */ 876 struct devfs_minor_data *mdata; 877 nvlist_t *mnvl; 878 nvpair_t *mnvp = NULL; 879 struct ni_list *nilp, *next; 880 881 rcm_log_message(RCM_TRACE1, "NET: process_nvlist\n"); 882 883 while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 884 /* Get driver name */ 885 if (strcmp(nvpair_name(nvp), RCM_NV_DRIVER_NAME) == 0) { 886 if (nvpair_value_string(nvp, &driver) != 0) { 887 rcm_log_message(RCM_WARNING, 888 _("NET: cannot get driver name\n")); 889 return (-1); 890 } 891 } 892 /* Get instance */ 893 if (strcmp(nvpair_name(nvp), RCM_NV_INSTANCE) == 0) { 894 if (nvpair_value_int32(nvp, &instance) != 0) { 895 rcm_log_message(RCM_WARNING, 896 _("NET: cannot get device instance\n")); 897 return (-1); 898 } 899 } 900 /* Get devfspath */ 901 if (strcmp(nvpair_name(nvp), RCM_NV_DEVFS_PATH) == 0) { 902 if (nvpair_value_string(nvp, &devfspath) != 0) { 903 rcm_log_message(RCM_WARNING, 904 _("NET: cannot get device path\n")); 905 return (-1); 906 } 907 if (strncmp("/pseudo", devfspath, 908 strlen("/pseudo")) == 0) { 909 /* Ignore pseudo devices, not really NICs */ 910 rcm_log_message(RCM_DEBUG, 911 _("NET: ignoring pseudo device %s\n"), 912 devfspath); 913 return (0); 914 } 915 } 916 917 /* Get minor data */ 918 if (strcmp(nvpair_name(nvp), RCM_NV_MINOR_DATA) == 0) { 919 if (nvpair_value_byte_array(nvp, 920 (uchar_t **)&minor_byte_array, &nminor) != 0) { 921 rcm_log_message(RCM_WARNING, 922 _("NET: cannot get device minor data\n")); 923 return (-1); 924 } 925 if (nvlist_unpack(minor_byte_array, 926 nminor, &mnvl, 0) != 0) { 927 rcm_log_message(RCM_WARNING, 928 _("NET: cannot get minor node data\n")); 929 return (-1); 930 } 931 mdata = (struct devfs_minor_data *)calloc(1, 932 sizeof (struct devfs_minor_data)); 933 if (mdata == NULL) { 934 rcm_log_message(RCM_WARNING, 935 _("NET: calloc error(%s)\n"), 936 strerror(errno)); 937 nvlist_free(mnvl); 938 return (-1); 939 } 940 /* Enumerate minor node data */ 941 while ((mnvp = nvlist_next_nvpair(mnvl, mnvp)) != 942 NULL) { 943 /* Get minor type */ 944 if (strcmp(nvpair_name(mnvp), 945 RCM_NV_MINOR_TYPE) == 0) { 946 if (nvpair_value_int32(mnvp, 947 &mdata->minor_type) != 0) { 948 rcm_log_message(RCM_WARNING, 949 _("NET: cannot get minor " 950 "type \n")); 951 nvlist_free(mnvl); 952 return (-1); 953 } 954 } 955 /* Get minor name */ 956 if (strcmp(nvpair_name(mnvp), 957 RCM_NV_MINOR_NAME) == 0) { 958 if (nvpair_value_string(mnvp, 959 &mdata->minor_name) != 0) { 960 rcm_log_message(RCM_WARNING, 961 _("NET: cannot get minor " 962 "name \n")); 963 nvlist_free(mnvl); 964 return (-1); 965 } 966 } 967 /* Get minor node type */ 968 if (strcmp(nvpair_name(mnvp), 969 RCM_NV_MINOR_NODE_TYPE) == 0) { 970 if (nvpair_value_string(mnvp, 971 &mdata->minor_node_type) != 0) { 972 rcm_log_message(RCM_WARNING, 973 _("NET: cannot get minor " 974 "node type \n")); 975 nvlist_free(mnvl); 976 return (-1); 977 } 978 } 979 } 980 (void) process_minor(driver, instance, mdata); 981 nvlist_free(mnvl); 982 } 983 } 984 985 (void) mutex_lock(&nil_lock); 986 987 /* Notify the event for all new devices found, then clean up the list */ 988 for (nilp = nil_head; nilp != NULL; nilp = next) { 989 if (notify_new_link(hd, nilp->dev) != 0) { 990 rcm_log_message(RCM_ERROR, 991 _(": Notify %s event failed (%s)\n"), 992 RCM_RESOURCE_LINK_NEW, nilp->dev); 993 } 994 next = nilp->next; 995 free(nilp); 996 } 997 nil_head = NULL; 998 999 (void) mutex_unlock(&nil_lock); 1000 1001 rcm_log_message(RCM_TRACE1, _("NET: process_nvlist success\n")); 1002 return (0); 1003 } 1004 1005 static void 1006 process_minor(char *name, int instance, struct devfs_minor_data *mdata) 1007 { 1008 char dev[MAXNAMELEN]; 1009 struct ni_list **pp; 1010 struct ni_list *p; 1011 1012 rcm_log_message(RCM_TRACE1, _("NET: process_minor %s%d\n"), 1013 name, instance); 1014 1015 if ((mdata->minor_node_type != NULL) && 1016 strcmp(mdata->minor_node_type, PROP_NV_DDI_NETWORK) != 0) { 1017 /* Process network devices only */ 1018 return; 1019 } 1020 1021 (void) snprintf(dev, sizeof (dev), "%s%d", name, instance); 1022 1023 /* Add new interface to the list */ 1024 (void) mutex_lock(&nil_lock); 1025 for (pp = &nil_head; (p = *pp) != NULL; pp = &(p->next)) { 1026 if (strcmp(dev, p->dev) == 0) 1027 break; 1028 } 1029 if (p != NULL) { 1030 rcm_log_message(RCM_TRACE1, 1031 _("NET: secondary node - ignoring\n")); 1032 goto done; 1033 } 1034 1035 /* Add new device to the list */ 1036 if ((p = malloc(sizeof (struct ni_list))) == NULL) { 1037 rcm_log_message(RCM_ERROR, _("NET: malloc failure(%s)\n"), 1038 strerror(errno)); 1039 goto done; 1040 } 1041 (void) strncpy(p->dev, dev, sizeof (p->dev)); 1042 p->next = NULL; 1043 *pp = p; 1044 1045 rcm_log_message(RCM_TRACE1, _("NET: added new node %s\n"), dev); 1046 done: 1047 (void) mutex_unlock(&nil_lock); 1048 } 1049 1050 /* 1051 * Notify the RCM_RESOURCE_LINK_NEW event to other modules. 1052 * Return 0 on success, -1 on failure. 1053 */ 1054 static int 1055 notify_new_link(rcm_handle_t *hd, const char *dev) 1056 { 1057 nvlist_t *nvl = NULL; 1058 datalink_id_t linkid; 1059 uint64_t id; 1060 int ret = -1; 1061 1062 rcm_log_message(RCM_TRACE1, _("NET: notify_new_link %s\n"), dev); 1063 if (dladm_dev2linkid(dev, &linkid) != DLADM_STATUS_OK) { 1064 rcm_log_message(RCM_TRACE1, 1065 _("NET: new link %s has not attached yet\n"), dev); 1066 ret = 0; 1067 goto done; 1068 } 1069 1070 id = linkid; 1071 if ((nvlist_alloc(&nvl, 0, 0) != 0) || 1072 (nvlist_add_uint64(nvl, RCM_NV_LINKID, id) != 0)) { 1073 rcm_log_message(RCM_ERROR, 1074 _("NET: failed to construct nvlist for %s\n"), dev); 1075 goto done; 1076 } 1077 1078 /* 1079 * Reset the active linkprop of this specific link. 1080 */ 1081 (void) dladm_init_linkprop(linkid); 1082 1083 rcm_log_message(RCM_TRACE1, _("NET: notify new link %u (%s)\n"), 1084 linkid, dev); 1085 1086 if (rcm_notify_event(hd, RCM_RESOURCE_LINK_NEW, 0, nvl, NULL) != 1087 RCM_SUCCESS) { 1088 rcm_log_message(RCM_ERROR, 1089 _("NET: failed to notify %s event for %s\n"), 1090 RCM_RESOURCE_LINK_NEW, dev); 1091 goto done; 1092 } 1093 1094 ret = 0; 1095 done: 1096 if (nvl != NULL) 1097 nvlist_free(nvl); 1098 return (ret); 1099 } 1100