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