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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  23  */
  24 
  25 /*
  26  * This RCM module adds support to the RCM framework for VNIC links
  27  */
  28 
  29 #include <stdio.h>
  30 #include <stdlib.h>
  31 #include <string.h>
  32 #include <errno.h>
  33 #include <sys/types.h>
  34 #include <synch.h>
  35 #include <assert.h>
  36 #include <strings.h>
  37 #include "rcm_module.h"
  38 #include <libintl.h>
  39 #include <libdllink.h>
  40 #include <libdlvnic.h>
  41 #include <libdlpi.h>
  42 
  43 /*
  44  * Definitions
  45  */
  46 #ifndef lint
  47 #define _(x)    gettext(x)
  48 #else
  49 #define _(x)    x
  50 #endif
  51 
  52 /* Some generic well-knowns and defaults used in this module */
  53 #define RCM_LINK_PREFIX         "SUNW_datalink" /* RCM datalink name prefix */
  54 #define RCM_LINK_RESOURCE_MAX   (13 + LINKID_STR_WIDTH)
  55 
  56 /* VNIC link flags */
  57 typedef enum {
  58         VNIC_OFFLINED           = 0x1,
  59         VNIC_CONSUMER_OFFLINED  = 0x2,
  60         VNIC_STALE              = 0x4
  61 } vnic_flag_t;
  62 
  63 /* link representation */
  64 typedef struct dl_vnic {
  65         struct dl_vnic  *dlv_next;              /* next VNIC on the same link */
  66         struct dl_vnic  *dlv_prev;              /* prev VNIC on the same link */
  67         datalink_id_t   dlv_vnic_id;
  68         vnic_flag_t     dlv_flags;              /* VNIC link flags */
  69 } dl_vnic_t;
  70 
  71 /* VNIC Cache state flags */
  72 typedef enum {
  73         CACHE_NODE_STALE        = 0x1,          /* stale cached data */
  74         CACHE_NODE_NEW          = 0x2,          /* new cached nodes */
  75         CACHE_NODE_OFFLINED     = 0x4           /* nodes offlined */
  76 } cache_node_state_t;
  77 
  78 /* Network Cache lookup options */
  79 #define CACHE_NO_REFRESH        0x1             /* cache refresh not needed */
  80 #define CACHE_REFRESH           0x2             /* refresh cache */
  81 
  82 /* Cache element */
  83 typedef struct link_cache {
  84         struct link_cache       *vc_next;       /* next cached resource */
  85         struct link_cache       *vc_prev;       /* prev cached resource */
  86         char                    *vc_resource;   /* resource name */
  87         datalink_id_t           vc_linkid;      /* linkid */
  88         dl_vnic_t               *vc_vnic;       /* VNIC list on this link */
  89         cache_node_state_t      vc_state;       /* cache state flags */
  90 } link_cache_t;
  91 
  92 /*
  93  * Global cache for network VNICs
  94  */
  95 static link_cache_t     cache_head;
  96 static link_cache_t     cache_tail;
  97 static mutex_t          cache_lock;
  98 static int              events_registered = 0;
  99 
 100 static dladm_handle_t   dld_handle = NULL;
 101 
 102 /*
 103  * RCM module interface prototypes
 104  */
 105 static int              vnic_register(rcm_handle_t *);
 106 static int              vnic_unregister(rcm_handle_t *);
 107 static int              vnic_get_info(rcm_handle_t *, char *, id_t, uint_t,
 108                             char **, char **, nvlist_t *, rcm_info_t **);
 109 static int              vnic_suspend(rcm_handle_t *, char *, id_t,
 110                             timespec_t *, uint_t, char **, rcm_info_t **);
 111 static int              vnic_resume(rcm_handle_t *, char *, id_t, uint_t,
 112                             char **, rcm_info_t **);
 113 static int              vnic_offline(rcm_handle_t *, char *, id_t, uint_t,
 114                             char **, rcm_info_t **);
 115 static int              vnic_undo_offline(rcm_handle_t *, char *, id_t, uint_t,
 116                             char **, rcm_info_t **);
 117 static int              vnic_remove(rcm_handle_t *, char *, id_t, uint_t,
 118                             char **, rcm_info_t **);
 119 static int              vnic_notify_event(rcm_handle_t *, char *, id_t, uint_t,
 120                             char **, nvlist_t *, rcm_info_t **);
 121 static int              vnic_configure(rcm_handle_t *, datalink_id_t);
 122 
 123 /* Module private routines */
 124 static void             cache_free();
 125 static int              cache_update(rcm_handle_t *);
 126 static void             cache_remove(link_cache_t *);
 127 static void             node_free(link_cache_t *);
 128 static void             cache_insert(link_cache_t *);
 129 static link_cache_t     *cache_lookup(rcm_handle_t *, char *, char);
 130 static int              vnic_consumer_offline(rcm_handle_t *, link_cache_t *,
 131                             char **, uint_t, rcm_info_t **);
 132 static void             vnic_consumer_online(rcm_handle_t *, link_cache_t *,
 133                             char **, uint_t, rcm_info_t **);
 134 static int              vnic_offline_vnic(link_cache_t *, uint32_t,
 135                             cache_node_state_t);
 136 static void             vnic_online_vnic(link_cache_t *);
 137 static char             *vnic_usage(link_cache_t *);
 138 static void             vnic_log_err(datalink_id_t, char **, char *);
 139 static int              vnic_consumer_notify(rcm_handle_t *, datalink_id_t,
 140                             char **, uint_t, rcm_info_t **);
 141 
 142 /* Module-Private data */
 143 static struct rcm_mod_ops vnic_ops =
 144 {
 145         RCM_MOD_OPS_VERSION,
 146         vnic_register,
 147         vnic_unregister,
 148         vnic_get_info,
 149         vnic_suspend,
 150         vnic_resume,
 151         vnic_offline,
 152         vnic_undo_offline,
 153         vnic_remove,
 154         NULL,
 155         NULL,
 156         vnic_notify_event
 157 };
 158 
 159 /*
 160  * rcm_mod_init() - Update registrations, and return the ops structure.
 161  */
 162 struct rcm_mod_ops *
 163 rcm_mod_init(void)
 164 {
 165         char errmsg[DLADM_STRSIZE];
 166         dladm_status_t status;
 167 
 168         rcm_log_message(RCM_TRACE1, "VNIC: mod_init\n");
 169 
 170         cache_head.vc_next = &cache_tail;
 171         cache_head.vc_prev = NULL;
 172         cache_tail.vc_prev = &cache_head;
 173         cache_tail.vc_next = NULL;
 174         (void) mutex_init(&cache_lock, 0, NULL);
 175 
 176         if ((status = dladm_open(&dld_handle)) != DLADM_STATUS_OK) {
 177                 rcm_log_message(RCM_WARNING,
 178                     "VNIC: mod_init failed: cannot open datalink handle: %s\n",
 179                     dladm_status2str(status, errmsg));
 180                 return (NULL);
 181         }
 182 
 183         /* Return the ops vectors */
 184         return (&vnic_ops);
 185 }
 186 
 187 /*
 188  * rcm_mod_info() - Return a string describing this module.
 189  */
 190 const char *
 191 rcm_mod_info(void)
 192 {
 193         rcm_log_message(RCM_TRACE1, "VNIC: mod_info\n");
 194 
 195         return ("VNIC module");
 196 }
 197 
 198 /*
 199  * rcm_mod_fini() - Destroy the network VNIC cache.
 200  */
 201 int
 202 rcm_mod_fini(void)
 203 {
 204         rcm_log_message(RCM_TRACE1, "VNIC: mod_fini\n");
 205 
 206         /*
 207          * Note that vnic_unregister() does not seem to be called anywhere,
 208          * therefore we free the cache nodes here. In theory we should call
 209          * rcm_register_interest() for each node before we free it, the
 210          * framework does not provide the rcm_handle to allow us to do so.
 211          */
 212         cache_free();
 213         (void) mutex_destroy(&cache_lock);
 214 
 215         dladm_close(dld_handle);
 216         return (RCM_SUCCESS);
 217 }
 218 
 219 /*
 220  * vnic_register() - Make sure the cache is properly sync'ed, and its
 221  *               registrations are in order.
 222  */
 223 static int
 224 vnic_register(rcm_handle_t *hd)
 225 {
 226         rcm_log_message(RCM_TRACE1, "VNIC: register\n");
 227 
 228         if (cache_update(hd) < 0)
 229                 return (RCM_FAILURE);
 230 
 231         /*
 232          * Need to register interest in all new resources
 233          * getting attached, so we get attach event notifications
 234          */
 235         if (!events_registered) {
 236                 if (rcm_register_event(hd, RCM_RESOURCE_LINK_NEW, 0, NULL)
 237                     != RCM_SUCCESS) {
 238                         rcm_log_message(RCM_ERROR,
 239                             _("VNIC: failed to register %s\n"),
 240                             RCM_RESOURCE_LINK_NEW);
 241                         return (RCM_FAILURE);
 242                 } else {
 243                         rcm_log_message(RCM_DEBUG, "VNIC: registered %s\n",
 244                             RCM_RESOURCE_LINK_NEW);
 245                         events_registered++;
 246                 }
 247         }
 248 
 249         return (RCM_SUCCESS);
 250 }
 251 
 252 /*
 253  * vnic_unregister() - Walk the cache, unregistering all the networks.
 254  */
 255 static int
 256 vnic_unregister(rcm_handle_t *hd)
 257 {
 258         link_cache_t *node;
 259 
 260         rcm_log_message(RCM_TRACE1, "VNIC: unregister\n");
 261 
 262         /* Walk the cache, unregistering everything */
 263         (void) mutex_lock(&cache_lock);
 264         node = cache_head.vc_next;
 265         while (node != &cache_tail) {
 266                 if (rcm_unregister_interest(hd, node->vc_resource, 0)
 267                     != RCM_SUCCESS) {
 268                         rcm_log_message(RCM_ERROR,
 269                             _("VNIC: failed to unregister %s\n"),
 270                             node->vc_resource);
 271                         (void) mutex_unlock(&cache_lock);
 272                         return (RCM_FAILURE);
 273                 }
 274                 cache_remove(node);
 275                 node_free(node);
 276                 node = cache_head.vc_next;
 277         }
 278         (void) mutex_unlock(&cache_lock);
 279 
 280         /*
 281          * Unregister interest in all new resources
 282          */
 283         if (events_registered) {
 284                 if (rcm_unregister_event(hd, RCM_RESOURCE_LINK_NEW, 0)
 285                     != RCM_SUCCESS) {
 286                         rcm_log_message(RCM_ERROR,
 287                             _("VNIC: failed to unregister %s\n"),
 288                             RCM_RESOURCE_LINK_NEW);
 289                         return (RCM_FAILURE);
 290                 } else {
 291                         rcm_log_message(RCM_DEBUG, "VNIC: unregistered %s\n",
 292                             RCM_RESOURCE_LINK_NEW);
 293                         events_registered--;
 294                 }
 295         }
 296 
 297         return (RCM_SUCCESS);
 298 }
 299 
 300 /*
 301  * vnic_offline() - Offline VNICs on a specific node.
 302  */
 303 static int
 304 vnic_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
 305     char **errorp, rcm_info_t **info)
 306 {
 307         link_cache_t *node;
 308 
 309         rcm_log_message(RCM_TRACE1, "VNIC: offline(%s)\n", rsrc);
 310 
 311         /* Lock the cache and lookup the resource */
 312         (void) mutex_lock(&cache_lock);
 313         node = cache_lookup(hd, rsrc, CACHE_REFRESH);
 314         if (node == NULL) {
 315                 /* should not happen because the resource is registered. */
 316                 vnic_log_err(DATALINK_INVALID_LINKID, errorp,
 317                     "unrecognized resource");
 318                 (void) mutex_unlock(&cache_lock);
 319                 return (RCM_SUCCESS);
 320         }
 321 
 322         /*
 323          * Inform consumers (IP interfaces) of associated VNICs to be offlined
 324          */
 325         if (vnic_consumer_offline(hd, node, errorp, flags, info) ==
 326             RCM_SUCCESS) {
 327                 rcm_log_message(RCM_DEBUG,
 328                     "VNIC: consumers agreed on offline\n");
 329         } else {
 330                 vnic_log_err(node->vc_linkid, errorp,
 331                     "consumers failed to offline");
 332                 (void) mutex_unlock(&cache_lock);
 333                 return (RCM_FAILURE);
 334         }
 335 
 336         /* Check if it's a query */
 337         if (flags & RCM_QUERY) {
 338                 rcm_log_message(RCM_TRACE1,
 339                     "VNIC: offline query succeeded(%s)\n", rsrc);
 340                 (void) mutex_unlock(&cache_lock);
 341                 return (RCM_SUCCESS);
 342         }
 343 
 344         if (vnic_offline_vnic(node, VNIC_OFFLINED, CACHE_NODE_OFFLINED) !=
 345             RCM_SUCCESS) {
 346                 vnic_online_vnic(node);
 347                 vnic_log_err(node->vc_linkid, errorp, "offline failed");
 348                 (void) mutex_unlock(&cache_lock);
 349                 return (RCM_FAILURE);
 350         }
 351 
 352         rcm_log_message(RCM_TRACE1, "VNIC: Offline succeeded(%s)\n", rsrc);
 353         (void) mutex_unlock(&cache_lock);
 354         return (RCM_SUCCESS);
 355 }
 356 
 357 /*
 358  * vnic_undo_offline() - Undo offline of a previously offlined node.
 359  */
 360 /*ARGSUSED*/
 361 static int
 362 vnic_undo_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
 363     char **errorp, rcm_info_t **info)
 364 {
 365         link_cache_t *node;
 366 
 367         rcm_log_message(RCM_TRACE1, "VNIC: online(%s)\n", rsrc);
 368 
 369         (void) mutex_lock(&cache_lock);
 370         node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH);
 371         if (node == NULL) {
 372                 vnic_log_err(DATALINK_INVALID_LINKID, errorp, "no such link");
 373                 (void) mutex_unlock(&cache_lock);
 374                 errno = ENOENT;
 375                 return (RCM_FAILURE);
 376         }
 377 
 378         /* Check if no attempt should be made to online the link here */
 379         if (!(node->vc_state & CACHE_NODE_OFFLINED)) {
 380                 vnic_log_err(node->vc_linkid, errorp, "link not offlined");
 381                 (void) mutex_unlock(&cache_lock);
 382                 errno = ENOTSUP;
 383                 return (RCM_SUCCESS);
 384         }
 385 
 386         vnic_online_vnic(node);
 387 
 388         /*
 389          * Inform IP interfaces on associated VNICs to be onlined
 390          */
 391         vnic_consumer_online(hd, node, errorp, flags, info);
 392 
 393         node->vc_state &= ~CACHE_NODE_OFFLINED;
 394         rcm_log_message(RCM_TRACE1, "VNIC: online succeeded(%s)\n", rsrc);
 395         (void) mutex_unlock(&cache_lock);
 396         return (RCM_SUCCESS);
 397 }
 398 
 399 static void
 400 vnic_online_vnic(link_cache_t *node)
 401 {
 402         dl_vnic_t *vnic;
 403         dladm_status_t status;
 404         char errmsg[DLADM_STRSIZE];
 405 
 406         /*
 407          * Try to bring on all offlined VNICs
 408          */
 409         for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
 410                 if (!(vnic->dlv_flags & VNIC_OFFLINED))
 411                         continue;
 412 
 413                 if ((status = dladm_vnic_up(dld_handle, vnic->dlv_vnic_id, 0))
 414                     != DLADM_STATUS_OK) {
 415                         /*
 416                          * Print a warning message and continue to online
 417                          * other VNICs.
 418                          */
 419                         rcm_log_message(RCM_WARNING,
 420                             _("VNIC: VNIC online failed (%u): %s\n"),
 421                             vnic->dlv_vnic_id,
 422                             dladm_status2str(status, errmsg));
 423                 } else {
 424                         vnic->dlv_flags &= ~VNIC_OFFLINED;
 425                 }
 426         }
 427 }
 428 
 429 static int
 430 vnic_offline_vnic(link_cache_t *node, uint32_t flags, cache_node_state_t state)
 431 {
 432         dl_vnic_t *vnic;
 433         dladm_status_t status;
 434         char errmsg[DLADM_STRSIZE];
 435 
 436         rcm_log_message(RCM_TRACE2, "VNIC: vnic_offline_vnic (%s %u %u)\n",
 437             node->vc_resource, flags, state);
 438 
 439         /*
 440          * Try to delete all explicit created VNIC
 441          */
 442         for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
 443 
 444                 if ((status = dladm_vnic_delete(dld_handle, vnic->dlv_vnic_id,
 445                     DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) {
 446                         rcm_log_message(RCM_WARNING,
 447                             _("VNIC: VNIC offline failed (%u): %s\n"),
 448                             vnic->dlv_vnic_id,
 449                             dladm_status2str(status, errmsg));
 450                         return (RCM_FAILURE);
 451                 } else {
 452                         rcm_log_message(RCM_TRACE1,
 453                             "VNIC: VNIC offline succeeded(%u)\n",
 454                             vnic->dlv_vnic_id);
 455                         vnic->dlv_flags |= flags;
 456                 }
 457         }
 458 
 459         node->vc_state |= state;
 460         return (RCM_SUCCESS);
 461 }
 462 
 463 /*
 464  * vnic_get_info() - Gather usage information for this resource.
 465  */
 466 /*ARGSUSED*/
 467 int
 468 vnic_get_info(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
 469     char **usagep, char **errorp, nvlist_t *props, rcm_info_t **info)
 470 {
 471         link_cache_t *node;
 472 
 473         rcm_log_message(RCM_TRACE1, "VNIC: get_info(%s)\n", rsrc);
 474 
 475         (void) mutex_lock(&cache_lock);
 476         node = cache_lookup(hd, rsrc, CACHE_REFRESH);
 477         if (node == NULL) {
 478                 rcm_log_message(RCM_INFO,
 479                     _("VNIC: get_info(%s) unrecognized resource\n"), rsrc);
 480                 (void) mutex_unlock(&cache_lock);
 481                 errno = ENOENT;
 482                 return (RCM_FAILURE);
 483         }
 484 
 485         *usagep = vnic_usage(node);
 486         (void) mutex_unlock(&cache_lock);
 487         if (*usagep == NULL) {
 488                 /* most likely malloc failure */
 489                 rcm_log_message(RCM_ERROR,
 490                     _("VNIC: get_info(%s) malloc failure\n"), rsrc);
 491                 (void) mutex_unlock(&cache_lock);
 492                 errno = ENOMEM;
 493                 return (RCM_FAILURE);
 494         }
 495 
 496         /* Set client/role properties */
 497         (void) nvlist_add_string(props, RCM_CLIENT_NAME, "VNIC");
 498 
 499         rcm_log_message(RCM_TRACE1, "VNIC: get_info(%s) info = %s\n",
 500             rsrc, *usagep);
 501         return (RCM_SUCCESS);
 502 }
 503 
 504 /*
 505  * vnic_suspend() - Nothing to do, always okay
 506  */
 507 /*ARGSUSED*/
 508 static int
 509 vnic_suspend(rcm_handle_t *hd, char *rsrc, id_t id, timespec_t *interval,
 510     uint_t flags, char **errorp, rcm_info_t **info)
 511 {
 512         rcm_log_message(RCM_TRACE1, "VNIC: suspend(%s)\n", rsrc);
 513         return (RCM_SUCCESS);
 514 }
 515 
 516 /*
 517  * vnic_resume() - Nothing to do, always okay
 518  */
 519 /*ARGSUSED*/
 520 static int
 521 vnic_resume(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
 522     char **errorp, rcm_info_t **info)
 523 {
 524         rcm_log_message(RCM_TRACE1, "VNIC: resume(%s)\n", rsrc);
 525         return (RCM_SUCCESS);
 526 }
 527 
 528 /*
 529  * vnic_consumer_remove()
 530  *
 531  *      Notify VNIC consumers to remove cache.
 532  */
 533 static int
 534 vnic_consumer_remove(rcm_handle_t *hd, link_cache_t *node, uint_t flags,
 535     rcm_info_t **info)
 536 {
 537         dl_vnic_t *vnic = NULL;
 538         char rsrc[RCM_LINK_RESOURCE_MAX];
 539         int ret = RCM_SUCCESS;
 540 
 541         rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_remove (%s)\n",
 542             node->vc_resource);
 543 
 544         for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
 545 
 546                 /*
 547                  * This will only be called when the offline operation
 548                  * succeeds, so the VNIC consumers must have been offlined
 549                  * at this point.
 550                  */
 551                 assert(vnic->dlv_flags & VNIC_CONSUMER_OFFLINED);
 552 
 553                 (void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u",
 554                     RCM_LINK_PREFIX, vnic->dlv_vnic_id);
 555 
 556                 ret = rcm_notify_remove(hd, rsrc, flags, info);
 557                 if (ret != RCM_SUCCESS) {
 558                         rcm_log_message(RCM_WARNING,
 559                             _("VNIC: notify remove failed (%s)\n"), rsrc);
 560                         break;
 561                 }
 562         }
 563 
 564         rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_remove done\n");
 565         return (ret);
 566 }
 567 
 568 /*
 569  * vnic_remove() - remove a resource from cache
 570  */
 571 /*ARGSUSED*/
 572 static int
 573 vnic_remove(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
 574     char **errorp, rcm_info_t **info)
 575 {
 576         link_cache_t *node;
 577         int rv;
 578 
 579         rcm_log_message(RCM_TRACE1, "VNIC: remove(%s)\n", rsrc);
 580 
 581         (void) mutex_lock(&cache_lock);
 582         node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH);
 583         if (node == NULL) {
 584                 rcm_log_message(RCM_INFO,
 585                     _("VNIC: remove(%s) unrecognized resource\n"), rsrc);
 586                 (void) mutex_unlock(&cache_lock);
 587                 errno = ENOENT;
 588                 return (RCM_FAILURE);
 589         }
 590 
 591         /* remove the cached entry for the resource */
 592         cache_remove(node);
 593         (void) mutex_unlock(&cache_lock);
 594 
 595         rv = vnic_consumer_remove(hd, node, flags, info);
 596         node_free(node);
 597         return (rv);
 598 }
 599 
 600 /*
 601  * vnic_notify_event - Project private implementation to receive new resource
 602  *                 events. It intercepts all new resource events. If the
 603  *                 new resource is a network resource, pass up a notify
 604  *                 for it too. The new resource need not be cached, since
 605  *                 it is done at register again.
 606  */
 607 /*ARGSUSED*/
 608 static int
 609 vnic_notify_event(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags,
 610     char **errorp, nvlist_t *nvl, rcm_info_t **info)
 611 {
 612         nvpair_t        *nvp = NULL;
 613         datalink_id_t   linkid;
 614         uint64_t        id64;
 615         int             rv = RCM_SUCCESS;
 616 
 617         rcm_log_message(RCM_TRACE1, "VNIC: notify_event(%s)\n", rsrc);
 618 
 619         if (strcmp(rsrc, RCM_RESOURCE_LINK_NEW) != 0) {
 620                 vnic_log_err(DATALINK_INVALID_LINKID, errorp,
 621                     "unrecognized event");
 622                 errno = EINVAL;
 623                 return (RCM_FAILURE);
 624         }
 625 
 626         /* Update cache to reflect latest VNICs */
 627         if (cache_update(hd) < 0) {
 628                 vnic_log_err(DATALINK_INVALID_LINKID, errorp,
 629                     "private Cache update failed");
 630                 return (RCM_FAILURE);
 631         }
 632 
 633         /*
 634          * Try best to recover all configuration.
 635          */
 636         rcm_log_message(RCM_DEBUG, "VNIC: process_nvlist\n");
 637         while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
 638                 if (strcmp(nvpair_name(nvp), RCM_NV_LINKID) != 0)
 639                         continue;
 640 
 641                 if (nvpair_value_uint64(nvp, &id64) != 0) {
 642                         vnic_log_err(DATALINK_INVALID_LINKID, errorp,
 643                             "cannot get linkid");
 644                         rv = RCM_FAILURE;
 645                         continue;
 646                 }
 647 
 648                 linkid = (datalink_id_t)id64;
 649                 if (vnic_configure(hd, linkid) != 0) {
 650                         vnic_log_err(linkid, errorp, "configuring failed");
 651                         rv = RCM_FAILURE;
 652                         continue;
 653                 }
 654 
 655                 /* Notify all VNIC consumers */
 656                 if (vnic_consumer_notify(hd, linkid, errorp, flags,
 657                     info) != 0) {
 658                         vnic_log_err(linkid, errorp, "consumer notify failed");
 659                         rv = RCM_FAILURE;
 660                 }
 661         }
 662 
 663         rcm_log_message(RCM_TRACE1,
 664             "VNIC: notify_event: link configuration complete\n");
 665         return (rv);
 666 }
 667 
 668 /*
 669  * vnic_usage - Determine the usage of a link.
 670  *          The returned buffer is owned by caller, and the caller
 671  *          must free it up when done.
 672  */
 673 static char *
 674 vnic_usage(link_cache_t *node)
 675 {
 676         dl_vnic_t *vnic;
 677         int nvnic;
 678         char *buf;
 679         const char *fmt;
 680         char *sep;
 681         char errmsg[DLADM_STRSIZE];
 682         char name[MAXLINKNAMELEN];
 683         dladm_status_t status;
 684         size_t bufsz;
 685 
 686         rcm_log_message(RCM_TRACE2, "VNIC: usage(%s)\n", node->vc_resource);
 687 
 688         assert(MUTEX_HELD(&cache_lock));
 689         if ((status = dladm_datalink_id2info(dld_handle, node->vc_linkid, NULL,
 690             NULL, NULL, name, sizeof (name))) != DLADM_STATUS_OK) {
 691                 rcm_log_message(RCM_ERROR,
 692                     _("VNIC: usage(%s) get link name failure(%s)\n"),
 693                     node->vc_resource, dladm_status2str(status, errmsg));
 694                 return (NULL);
 695         }
 696 
 697         if (node->vc_state & CACHE_NODE_OFFLINED)
 698                 fmt = _("%1$s offlined");
 699         else
 700                 fmt = _("%1$s VNICs: ");
 701 
 702         /* TRANSLATION_NOTE: separator used between VNIC linkids */
 703         sep = _(", ");
 704 
 705         nvnic = 0;
 706         for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next)
 707                 nvnic++;
 708 
 709         /* space for VNICs and separators, plus message */
 710         bufsz = nvnic * (MAXLINKNAMELEN + strlen(sep)) +
 711             strlen(fmt) + MAXLINKNAMELEN + 1;
 712         if ((buf = malloc(bufsz)) == NULL) {
 713                 rcm_log_message(RCM_ERROR,
 714                     _("VNIC: usage(%s) malloc failure(%s)\n"),
 715                     node->vc_resource, strerror(errno));
 716                 return (NULL);
 717         }
 718         (void) snprintf(buf, bufsz, fmt, name);
 719 
 720         if (node->vc_state & CACHE_NODE_OFFLINED) {
 721                 /* Nothing else to do */
 722                 rcm_log_message(RCM_TRACE2, "VNIC: usage (%s) info = %s\n",
 723                     node->vc_resource, buf);
 724                 return (buf);
 725         }
 726 
 727         for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
 728                 rcm_log_message(RCM_DEBUG, "VNIC:= %u\n", vnic->dlv_vnic_id);
 729 
 730                 if ((status = dladm_datalink_id2info(dld_handle,
 731                     vnic->dlv_vnic_id, NULL, NULL, NULL, name, sizeof (name)))
 732                     != DLADM_STATUS_OK) {
 733                         rcm_log_message(RCM_ERROR,
 734                             _("VNIC: usage(%s) get vnic %u name failure(%s)\n"),
 735                             node->vc_resource, vnic->dlv_vnic_id,
 736                             dladm_status2str(status, errmsg));
 737                         free(buf);
 738                         return (NULL);
 739                 }
 740 
 741                 (void) strlcat(buf, name, bufsz);
 742                 if (vnic->dlv_next != NULL)
 743                         (void) strlcat(buf, sep, bufsz);
 744         }
 745 
 746         rcm_log_message(RCM_TRACE2, "VNIC: usage (%s) info = %s\n",
 747             node->vc_resource, buf);
 748 
 749         return (buf);
 750 }
 751 
 752 /*
 753  * Cache management routines, all cache management functions should be
 754  * be called with cache_lock held.
 755  */
 756 
 757 /*
 758  * cache_lookup() - Get a cache node for a resource.
 759  *                Call with cache lock held.
 760  *
 761  * This ensures that the cache is consistent with the system state and
 762  * returns a pointer to the cache element corresponding to the resource.
 763  */
 764 static link_cache_t *
 765 cache_lookup(rcm_handle_t *hd, char *rsrc, char options)
 766 {
 767         link_cache_t *node;
 768 
 769         rcm_log_message(RCM_TRACE2, "VNIC: cache lookup(%s)\n", rsrc);
 770 
 771         assert(MUTEX_HELD(&cache_lock));
 772         if (options & CACHE_REFRESH) {
 773                 /* drop lock since update locks cache again */
 774                 (void) mutex_unlock(&cache_lock);
 775                 (void) cache_update(hd);
 776                 (void) mutex_lock(&cache_lock);
 777         }
 778 
 779         node = cache_head.vc_next;
 780         for (; node != &cache_tail; node = node->vc_next) {
 781                 if (strcmp(rsrc, node->vc_resource) == 0) {
 782                         rcm_log_message(RCM_TRACE2,
 783                             "VNIC: cache lookup succeeded(%s)\n", rsrc);
 784                         return (node);
 785                 }
 786         }
 787         return (NULL);
 788 }
 789 
 790 /*
 791  * node_free - Free a node from the cache
 792  */
 793 static void
 794 node_free(link_cache_t *node)
 795 {
 796         dl_vnic_t *vnic, *next;
 797 
 798         if (node != NULL) {
 799                 free(node->vc_resource);
 800 
 801                 /* free the VNIC list */
 802                 for (vnic = node->vc_vnic; vnic != NULL; vnic = next) {
 803                         next = vnic->dlv_next;
 804                         free(vnic);
 805                 }
 806                 free(node);
 807         }
 808 }
 809 
 810 /*
 811  * cache_insert - Insert a resource node in cache
 812  */
 813 static void
 814 cache_insert(link_cache_t *node)
 815 {
 816         assert(MUTEX_HELD(&cache_lock));
 817 
 818         /* insert at the head for best performance */
 819         node->vc_next = cache_head.vc_next;
 820         node->vc_prev = &cache_head;
 821 
 822         node->vc_next->vc_prev = node;
 823         node->vc_prev->vc_next = node;
 824 }
 825 
 826 /*
 827  * cache_remove() - Remove a resource node from cache.
 828  */
 829 static void
 830 cache_remove(link_cache_t *node)
 831 {
 832         assert(MUTEX_HELD(&cache_lock));
 833         node->vc_next->vc_prev = node->vc_prev;
 834         node->vc_prev->vc_next = node->vc_next;
 835         node->vc_next = NULL;
 836         node->vc_prev = NULL;
 837 }
 838 
 839 typedef struct vnic_update_arg_s {
 840         rcm_handle_t    *hd;
 841         int             retval;
 842 } vnic_update_arg_t;
 843 
 844 /*
 845  * vnic_update() - Update physical interface properties
 846  */
 847 static int
 848 vnic_update(dladm_handle_t handle, datalink_id_t vnicid, void *arg)
 849 {
 850         vnic_update_arg_t *vnic_update_argp = arg;
 851         rcm_handle_t *hd = vnic_update_argp->hd;
 852         link_cache_t *node;
 853         dl_vnic_t *vnic;
 854         char *rsrc;
 855         dladm_vnic_attr_t vnic_attr;
 856         dladm_status_t status;
 857         char errmsg[DLADM_STRSIZE];
 858         boolean_t newnode = B_FALSE;
 859         int ret = -1;
 860 
 861         rcm_log_message(RCM_TRACE2, "VNIC: vnic_update(%u)\n", vnicid);
 862 
 863         assert(MUTEX_HELD(&cache_lock));
 864         status = dladm_vnic_info(handle, vnicid, &vnic_attr, DLADM_OPT_ACTIVE);
 865         if (status != DLADM_STATUS_OK) {
 866                 rcm_log_message(RCM_TRACE1,
 867                     "VNIC: vnic_update() cannot get vnic information for "
 868                     "%u(%s)\n", vnicid, dladm_status2str(status, errmsg));
 869                 return (DLADM_WALK_CONTINUE);
 870         }
 871 
 872         if (vnic_attr.va_link_id == DATALINK_INVALID_LINKID) {
 873                 /*
 874                  * Skip the etherstubs.
 875                  */
 876                 rcm_log_message(RCM_TRACE1,
 877                     "VNIC: vnic_update(): skip the etherstub %u\n", vnicid);
 878                 return (DLADM_WALK_CONTINUE);
 879         }
 880 
 881         rsrc = malloc(RCM_LINK_RESOURCE_MAX);
 882         if (rsrc == NULL) {
 883                 rcm_log_message(RCM_ERROR, _("VNIC: malloc error(%s): %u\n"),
 884                     strerror(errno), vnicid);
 885                 goto done;
 886         }
 887 
 888         (void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u",
 889             RCM_LINK_PREFIX, vnic_attr.va_link_id);
 890 
 891         node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH);
 892         if (node != NULL) {
 893                 rcm_log_message(RCM_DEBUG,
 894                     "VNIC: %s already registered (vnicid:%d)\n",
 895                     rsrc, vnic_attr.va_vnic_id);
 896                 free(rsrc);
 897         } else {
 898                 rcm_log_message(RCM_DEBUG,
 899                     "VNIC: %s is a new resource (vnicid:%d)\n",
 900                     rsrc, vnic_attr.va_vnic_id);
 901                 if ((node = calloc(1, sizeof (link_cache_t))) == NULL) {
 902                         free(rsrc);
 903                         rcm_log_message(RCM_ERROR, _("VNIC: calloc: %s\n"),
 904                             strerror(errno));
 905                         goto done;
 906                 }
 907 
 908                 node->vc_resource = rsrc;
 909                 node->vc_vnic = NULL;
 910                 node->vc_linkid = vnic_attr.va_link_id;
 911                 node->vc_state |= CACHE_NODE_NEW;
 912                 newnode = B_TRUE;
 913         }
 914 
 915         for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
 916                 if (vnic->dlv_vnic_id == vnicid) {
 917                         vnic->dlv_flags &= ~VNIC_STALE;
 918                         break;
 919                 }
 920         }
 921 
 922         if (vnic == NULL) {
 923                 if ((vnic = calloc(1, sizeof (dl_vnic_t))) == NULL) {
 924                         rcm_log_message(RCM_ERROR, _("VNIC: malloc: %s\n"),
 925                             strerror(errno));
 926                         if (newnode) {
 927                                 free(rsrc);
 928                                 free(node);
 929                         }
 930                         goto done;
 931                 }
 932                 vnic->dlv_vnic_id = vnicid;
 933                 vnic->dlv_next = node->vc_vnic;
 934                 vnic->dlv_prev = NULL;
 935                 if (node->vc_vnic != NULL)
 936                         node->vc_vnic->dlv_prev = vnic;
 937                 node->vc_vnic = vnic;
 938         }
 939 
 940         node->vc_state &= ~CACHE_NODE_STALE;
 941 
 942         if (newnode)
 943                 cache_insert(node);
 944 
 945         rcm_log_message(RCM_TRACE3, "VNIC: vnic_update: succeeded(%u)\n",
 946             vnicid);
 947         ret = 0;
 948 done:
 949         vnic_update_argp->retval = ret;
 950         return (ret == 0 ? DLADM_WALK_CONTINUE : DLADM_WALK_TERMINATE);
 951 }
 952 
 953 /*
 954  * vnic_update_all() - Determine all VNIC links in the system
 955  */
 956 static int
 957 vnic_update_all(rcm_handle_t *hd)
 958 {
 959         vnic_update_arg_t arg = {NULL, 0};
 960 
 961         rcm_log_message(RCM_TRACE2, "VNIC: vnic_update_all\n");
 962 
 963         assert(MUTEX_HELD(&cache_lock));
 964         arg.hd = hd;
 965         (void) dladm_walk_datalink_id(vnic_update, dld_handle, &arg,
 966             DATALINK_CLASS_VNIC, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
 967         return (arg.retval);
 968 }
 969 
 970 /*
 971  * cache_update() - Update cache with latest interface info
 972  */
 973 static int
 974 cache_update(rcm_handle_t *hd)
 975 {
 976         link_cache_t *node, *nnode;
 977         dl_vnic_t *vnic;
 978         int rv;
 979 
 980         rcm_log_message(RCM_TRACE2, "VNIC: cache_update\n");
 981 
 982         (void) mutex_lock(&cache_lock);
 983 
 984         /* first we walk the entire cache, marking each entry stale */
 985         node = cache_head.vc_next;
 986         for (; node != &cache_tail; node = node->vc_next) {
 987                 node->vc_state |= CACHE_NODE_STALE;
 988                 for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next)
 989                         vnic->dlv_flags |= VNIC_STALE;
 990         }
 991 
 992         rv = vnic_update_all(hd);
 993 
 994         /*
 995          * Continue to delete all stale nodes from the cache even
 996          * vnic_update_all() failed. Unregister link that are not offlined
 997          * and still in cache
 998          */
 999         for (node = cache_head.vc_next; node != &cache_tail; node = nnode) {
1000                 dl_vnic_t *vnic, *next;
1001 
1002                 for (vnic = node->vc_vnic; vnic != NULL; vnic = next) {
1003                         next = vnic->dlv_next;
1004 
1005                         /* clear stale VNICs */
1006                         if (vnic->dlv_flags & VNIC_STALE) {
1007                                 if (vnic->dlv_prev != NULL)
1008                                         vnic->dlv_prev->dlv_next = next;
1009                                 else
1010                                         node->vc_vnic = next;
1011 
1012                                 if (next != NULL)
1013                                         next->dlv_prev = vnic->dlv_prev;
1014                                 free(vnic);
1015                         }
1016                 }
1017 
1018                 nnode = node->vc_next;
1019                 if (node->vc_state & CACHE_NODE_STALE) {
1020                         (void) rcm_unregister_interest(hd, node->vc_resource,
1021                             0);
1022                         rcm_log_message(RCM_DEBUG, "VNIC: unregistered %s\n",
1023                             node->vc_resource);
1024                         assert(node->vc_vnic == NULL);
1025                         cache_remove(node);
1026                         node_free(node);
1027                         continue;
1028                 }
1029 
1030                 if (!(node->vc_state & CACHE_NODE_NEW))
1031                         continue;
1032 
1033                 if (rcm_register_interest(hd, node->vc_resource, 0, NULL) !=
1034                     RCM_SUCCESS) {
1035                         rcm_log_message(RCM_ERROR,
1036                             _("VNIC: failed to register %s\n"),
1037                             node->vc_resource);
1038                         rv = -1;
1039                 } else {
1040                         rcm_log_message(RCM_DEBUG, "VNIC: registered %s\n",
1041                             node->vc_resource);
1042                         node->vc_state &= ~CACHE_NODE_NEW;
1043                 }
1044         }
1045 
1046         (void) mutex_unlock(&cache_lock);
1047         return (rv);
1048 }
1049 
1050 /*
1051  * cache_free() - Empty the cache
1052  */
1053 static void
1054 cache_free()
1055 {
1056         link_cache_t *node;
1057 
1058         rcm_log_message(RCM_TRACE2, "VNIC: cache_free\n");
1059 
1060         (void) mutex_lock(&cache_lock);
1061         node = cache_head.vc_next;
1062         while (node != &cache_tail) {
1063                 cache_remove(node);
1064                 node_free(node);
1065                 node = cache_head.vc_next;
1066         }
1067         (void) mutex_unlock(&cache_lock);
1068 }
1069 
1070 /*
1071  * vnic_log_err() - RCM error log wrapper
1072  */
1073 static void
1074 vnic_log_err(datalink_id_t linkid, char **errorp, char *errmsg)
1075 {
1076         char link[MAXLINKNAMELEN];
1077         char errstr[DLADM_STRSIZE];
1078         dladm_status_t status;
1079         int len;
1080         const char *errfmt;
1081         char *error;
1082 
1083         link[0] = '\0';
1084         if (linkid != DATALINK_INVALID_LINKID) {
1085                 char rsrc[RCM_LINK_RESOURCE_MAX];
1086 
1087                 (void) snprintf(rsrc, sizeof (rsrc), "%s/%u",
1088                     RCM_LINK_PREFIX, linkid);
1089 
1090                 rcm_log_message(RCM_ERROR, _("VNIC: %s(%s)\n"), errmsg, rsrc);
1091                 if ((status = dladm_datalink_id2info(dld_handle, linkid, NULL,
1092                     NULL, NULL, link, sizeof (link))) != DLADM_STATUS_OK) {
1093                         rcm_log_message(RCM_WARNING,
1094                             _("VNIC: cannot get link name for (%s) %s\n"),
1095                             rsrc, dladm_status2str(status, errstr));
1096                 }
1097         } else {
1098                 rcm_log_message(RCM_ERROR, _("VNIC: %s\n"), errmsg);
1099         }
1100 
1101         errfmt = strlen(link) > 0 ? _("VNIC: %s(%s)") : _("VNIC: %s");
1102         len = strlen(errfmt) + strlen(errmsg) + MAXLINKNAMELEN + 1;
1103         if ((error = malloc(len)) != NULL) {
1104                 if (strlen(link) > 0)
1105                         (void) snprintf(error, len, errfmt, errmsg, link);
1106                 else
1107                         (void) snprintf(error, len, errfmt, errmsg);
1108         }
1109 
1110         if (errorp != NULL)
1111                 *errorp = error;
1112 }
1113 
1114 /*
1115  * vnic_consumer_online()
1116  *
1117  *      Notify online to VNIC consumers.
1118  */
1119 /* ARGSUSED */
1120 static void
1121 vnic_consumer_online(rcm_handle_t *hd, link_cache_t *node, char **errorp,
1122     uint_t flags, rcm_info_t **info)
1123 {
1124         dl_vnic_t *vnic;
1125         char rsrc[RCM_LINK_RESOURCE_MAX];
1126 
1127         rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_online (%s)\n",
1128             node->vc_resource);
1129 
1130         for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
1131                 if (!(vnic->dlv_flags & VNIC_CONSUMER_OFFLINED))
1132                         continue;
1133 
1134                 (void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u",
1135                     RCM_LINK_PREFIX, vnic->dlv_vnic_id);
1136 
1137                 if (rcm_notify_online(hd, rsrc, flags, info) == RCM_SUCCESS)
1138                         vnic->dlv_flags &= ~VNIC_CONSUMER_OFFLINED;
1139         }
1140 
1141         rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_online done\n");
1142 }
1143 
1144 /*
1145  * vnic_consumer_offline()
1146  *
1147  *      Offline VNIC consumers.
1148  */
1149 static int
1150 vnic_consumer_offline(rcm_handle_t *hd, link_cache_t *node, char **errorp,
1151     uint_t flags, rcm_info_t **info)
1152 {
1153         dl_vnic_t *vnic;
1154         char rsrc[RCM_LINK_RESOURCE_MAX];
1155         int ret = RCM_SUCCESS;
1156 
1157         rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_offline (%s)\n",
1158             node->vc_resource);
1159 
1160         for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
1161                 (void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u",
1162                     RCM_LINK_PREFIX, vnic->dlv_vnic_id);
1163 
1164                 ret = rcm_request_offline(hd, rsrc, flags, info);
1165                 if (ret != RCM_SUCCESS)
1166                         break;
1167 
1168                 vnic->dlv_flags |= VNIC_CONSUMER_OFFLINED;
1169         }
1170 
1171         if (vnic != NULL)
1172                 vnic_consumer_online(hd, node, errorp, flags, info);
1173 
1174         rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_offline done\n");
1175         return (ret);
1176 }
1177 
1178 /*
1179  * Send RCM_RESOURCE_LINK_NEW events to other modules about new VNICs.
1180  * Return 0 on success, -1 on failure.
1181  */
1182 static int
1183 vnic_notify_new_vnic(rcm_handle_t *hd, char *rsrc)
1184 {
1185         link_cache_t *node;
1186         dl_vnic_t *vnic;
1187         nvlist_t *nvl = NULL;
1188         uint64_t id;
1189         int ret = -1;
1190 
1191         rcm_log_message(RCM_TRACE2, "VNIC: vnic_notify_new_vnic (%s)\n", rsrc);
1192 
1193         (void) mutex_lock(&cache_lock);
1194         if ((node = cache_lookup(hd, rsrc, CACHE_REFRESH)) == NULL) {
1195                 (void) mutex_unlock(&cache_lock);
1196                 return (0);
1197         }
1198 
1199         if (nvlist_alloc(&nvl, 0, 0) != 0) {
1200                 (void) mutex_unlock(&cache_lock);
1201                 rcm_log_message(RCM_WARNING,
1202                     _("VNIC: failed to allocate nvlist\n"));
1203                 goto done;
1204         }
1205 
1206         for (vnic = node->vc_vnic; vnic != NULL; vnic = vnic->dlv_next) {
1207                 rcm_log_message(RCM_TRACE2,
1208                     "VNIC: vnic_notify_new_vnic add (%u)\n", vnic->dlv_vnic_id);
1209 
1210                 id = vnic->dlv_vnic_id;
1211                 if (nvlist_add_uint64(nvl, RCM_NV_LINKID, id) != 0) {
1212                         rcm_log_message(RCM_ERROR,
1213                             _("VNIC: failed to construct nvlist\n"));
1214                         (void) mutex_unlock(&cache_lock);
1215                         goto done;
1216                 }
1217         }
1218         (void) mutex_unlock(&cache_lock);
1219 
1220         if (rcm_notify_event(hd, RCM_RESOURCE_LINK_NEW, 0, nvl, NULL) !=
1221             RCM_SUCCESS) {
1222                 rcm_log_message(RCM_ERROR,
1223                     _("VNIC: failed to notify %s event for %s\n"),
1224                     RCM_RESOURCE_LINK_NEW, node->vc_resource);
1225                 goto done;
1226         }
1227 
1228         ret = 0;
1229 done:
1230         if (nvl != NULL)
1231                 nvlist_free(nvl);
1232         return (ret);
1233 }
1234 
1235 /*
1236  * vnic_consumer_notify() - Notify consumers of VNICs coming back online.
1237  */
1238 static int
1239 vnic_consumer_notify(rcm_handle_t *hd, datalink_id_t linkid, char **errorp,
1240     uint_t flags, rcm_info_t **info)
1241 {
1242         char rsrc[RCM_LINK_RESOURCE_MAX];
1243         link_cache_t *node;
1244 
1245         /* Check for the interface in the cache */
1246         (void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u", RCM_LINK_PREFIX,
1247             linkid);
1248 
1249         rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_notify(%s)\n", rsrc);
1250 
1251         /*
1252          * Inform IP consumers of the new link.
1253          */
1254         if (vnic_notify_new_vnic(hd, rsrc) != 0) {
1255                 (void) mutex_lock(&cache_lock);
1256                 if ((node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH)) != NULL) {
1257                         (void) vnic_offline_vnic(node, VNIC_STALE,
1258                             CACHE_NODE_STALE);
1259                 }
1260                 (void) mutex_unlock(&cache_lock);
1261                 rcm_log_message(RCM_TRACE2,
1262                     "VNIC: vnic_notify_new_vnic failed(%s)\n", rsrc);
1263                 return (-1);
1264         }
1265 
1266         rcm_log_message(RCM_TRACE2, "VNIC: vnic_consumer_notify succeeded\n");
1267         return (0);
1268 }
1269 
1270 typedef struct vnic_up_arg_s {
1271         datalink_id_t   linkid;
1272         int             retval;
1273 } vnic_up_arg_t;
1274 
1275 static int
1276 vnic_up(dladm_handle_t handle, datalink_id_t vnicid, void *arg)
1277 {
1278         vnic_up_arg_t *vnic_up_argp = arg;
1279         dladm_status_t status;
1280         dladm_vnic_attr_t vnic_attr;
1281         char errmsg[DLADM_STRSIZE];
1282 
1283         status = dladm_vnic_info(handle, vnicid, &vnic_attr, DLADM_OPT_PERSIST);
1284         if (status != DLADM_STATUS_OK) {
1285                 rcm_log_message(RCM_TRACE1,
1286                     "VNIC: vnic_up(): cannot get information for VNIC %u "
1287                     "(%s)\n", vnicid, dladm_status2str(status, errmsg));
1288                 return (DLADM_WALK_CONTINUE);
1289         }
1290 
1291         if (vnic_attr.va_link_id != vnic_up_argp->linkid)
1292                 return (DLADM_WALK_CONTINUE);
1293 
1294         rcm_log_message(RCM_TRACE3, "VNIC: vnic_up(%u)\n", vnicid);
1295         if ((status = dladm_vnic_up(handle, vnicid, 0)) == DLADM_STATUS_OK)
1296                 return (DLADM_WALK_CONTINUE);
1297 
1298         /*
1299          * Prompt the warning message and continue to UP other VNICs.
1300          */
1301         rcm_log_message(RCM_WARNING,
1302             _("VNIC: VNIC up failed (%u): %s\n"),
1303             vnicid, dladm_status2str(status, errmsg));
1304 
1305         vnic_up_argp->retval = -1;
1306         return (DLADM_WALK_CONTINUE);
1307 }
1308 
1309 /*
1310  * vnic_configure() - Configure VNICs over a physical link after it attaches
1311  */
1312 static int
1313 vnic_configure(rcm_handle_t *hd, datalink_id_t linkid)
1314 {
1315         char rsrc[RCM_LINK_RESOURCE_MAX];
1316         link_cache_t *node;
1317         vnic_up_arg_t arg = {DATALINK_INVALID_LINKID, 0};
1318 
1319         /* Check for the VNICs in the cache */
1320         (void) snprintf(rsrc, sizeof (rsrc), "%s/%u", RCM_LINK_PREFIX, linkid);
1321 
1322         rcm_log_message(RCM_TRACE2, "VNIC: vnic_configure(%s)\n", rsrc);
1323 
1324         /* Check if the link is new or was previously offlined */
1325         (void) mutex_lock(&cache_lock);
1326         if (((node = cache_lookup(hd, rsrc, CACHE_REFRESH)) != NULL) &&
1327             (!(node->vc_state & CACHE_NODE_OFFLINED))) {
1328                 rcm_log_message(RCM_TRACE2,
1329                     "VNIC: Skipping configured interface(%s)\n", rsrc);
1330                 (void) mutex_unlock(&cache_lock);
1331                 return (0);
1332         }
1333         (void) mutex_unlock(&cache_lock);
1334 
1335         arg.linkid = linkid;
1336         (void) dladm_walk_datalink_id(vnic_up, dld_handle, &arg,
1337             DATALINK_CLASS_VNIC, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
1338 
1339         if (arg.retval == 0) {
1340                 rcm_log_message(RCM_TRACE2,
1341                     "VNIC: vnic_configure succeeded(%s)\n", rsrc);
1342         }
1343         return (arg.retval);
1344 }