Print this page
    
Fixes to allow compilation on OmniOS and OI
OS-3342+co
OS-3007 dlmgmtd needs to work with non-native zones
OS-375 i_dls_mgmt_upcall()/dlmgmt_zfop() deadlock in dlmgmtd
OS-383 dladm rename-link doesn't update /etc/svc/volatile/dladm/network-datalink-management:default.cache
OS-249
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/cmd/dlmgmtd/dlmgmt_db.c
          +++ new/usr/src/cmd/dlmgmtd/dlmgmt_db.c
   1    1  /*
   2    2   * CDDL HEADER START
   3    3   *
   4    4   * The contents of this file are subject to the terms of the
   5    5   * Common Development and Distribution License (the "License").
   6    6   * You may not use this file except in compliance with the License.
   7    7   *
   8    8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9    9   * or http://www.opensolaris.org/os/licensing.
  10   10   * See the License for the specific language governing permissions
  11   11   * and limitations under the License.
  12   12   *
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  
    | 
      ↓ open down ↓ | 
    13 lines elided | 
    
      ↑ open up ↑ | 
  
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  
  22   22  /*
  23   23   * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
       24 + * Copyright 2014, Joyent Inc. All rights reserved.
  24   25   */
  25   26  
  26   27  #include <assert.h>
  27   28  #include <ctype.h>
  28   29  #include <errno.h>
  29   30  #include <fcntl.h>
  30   31  #include <stdio.h>
  31   32  #include <stdlib.h>
  32   33  #include <string.h>
  33   34  #include <strings.h>
  34   35  #include <syslog.h>
  35   36  #include <zone.h>
  
    | 
      ↓ open down ↓ | 
    2 lines elided | 
    
      ↑ open up ↑ | 
  
  36   37  #include <sys/types.h>
  37   38  #include <sys/stat.h>
  38   39  #include <stropts.h>
  39   40  #include <sys/conf.h>
  40   41  #include <pthread.h>
  41   42  #include <unistd.h>
  42   43  #include <wait.h>
  43   44  #include <libcontract.h>
  44   45  #include <libcontract_priv.h>
  45   46  #include <sys/contract/process.h>
       47 +#include <sys/vnic.h>
       48 +#include <zone.h>
  46   49  #include "dlmgmt_impl.h"
  47   50  
  48   51  typedef enum dlmgmt_db_op {
  49   52          DLMGMT_DB_OP_WRITE,
  50   53          DLMGMT_DB_OP_DELETE,
  51   54          DLMGMT_DB_OP_READ
  52   55  } dlmgmt_db_op_t;
  53   56  
  54   57  typedef struct dlmgmt_db_req_s {
  55   58          struct dlmgmt_db_req_s  *ls_next;
  56   59          dlmgmt_db_op_t          ls_op;
  57   60          char                    ls_link[MAXLINKNAMELEN];
  58   61          datalink_id_t           ls_linkid;
  59   62          zoneid_t                ls_zoneid;
  60   63          uint32_t                ls_flags;       /* Either DLMGMT_ACTIVE or   */
  61   64                                                  /* DLMGMT_PERSIST, not both. */
  62   65  } dlmgmt_db_req_t;
  63   66  
  64   67  /*
  65   68   * List of pending db updates (e.g., because of a read-only filesystem).
  66   69   */
  67   70  static dlmgmt_db_req_t  *dlmgmt_db_req_head = NULL;
  68   71  static dlmgmt_db_req_t  *dlmgmt_db_req_tail = NULL;
  69   72  
  70   73  /*
  71   74   * rewrite_needed is set to B_TRUE by process_link_line() if it encounters a
  72   75   * line with an old format.  This will cause the file being read to be
  73   76   * re-written with the current format.
  74   77   */
  75   78  static boolean_t        rewrite_needed;
  76   79  
  77   80  static int              dlmgmt_db_update(dlmgmt_db_op_t, const char *,
  78   81                              dlmgmt_link_t *, uint32_t);
  79   82  static int              dlmgmt_process_db_req(dlmgmt_db_req_t *);
  80   83  static int              dlmgmt_process_db_onereq(dlmgmt_db_req_t *, boolean_t);
  81   84  static void             *dlmgmt_db_update_thread(void *);
  82   85  static boolean_t        process_link_line(char *, dlmgmt_link_t *);
  83   86  static int              process_db_write(dlmgmt_db_req_t *, FILE *, FILE *);
  84   87  static int              process_db_read(dlmgmt_db_req_t *, FILE *);
  85   88  static void             generate_link_line(dlmgmt_link_t *, boolean_t, char *);
  86   89  
  87   90  #define BUFLEN(lim, ptr)        (((lim) > (ptr)) ? ((lim) - (ptr)) : 0)
  88   91  #define MAXLINELEN              1024
  89   92  
  90   93  typedef void db_walk_func_t(dlmgmt_link_t *);
  91   94  
  92   95  /*
  93   96   * Translator functions to go from dladm_datatype_t to character strings.
  94   97   * Each function takes a pointer to a buffer, the size of the buffer,
  95   98   * the name of the attribute, and the value to be written.  The functions
  96   99   * return the number of bytes written to the buffer.  If the buffer is not big
  97  100   * enough to hold the string representing the value, then nothing is written
  98  101   * and 0 is returned.
  99  102   */
 100  103  typedef size_t write_func_t(char *, size_t, char *, void *);
 101  104  
 102  105  /*
 103  106   * Translator functions to read from a NULL terminated string buffer into
 104  107   * something of the given DLADM_TYPE_*.  The functions each return the number
 105  108   * of bytes read from the string buffer.  If there is an error reading data
 106  109   * from the buffer, then 0 is returned.  It is the caller's responsibility
 107  110   * to free the data allocated by these functions.
 108  111   */
 109  112  typedef size_t read_func_t(char *, void **);
 110  113  
 111  114  typedef struct translator_s {
 112  115          const char      *type_name;
 113  116          write_func_t    *write_func;
 114  117          read_func_t     *read_func;
 115  118  } translator_t;
 116  119  
 117  120  /*
 118  121   * Translator functions, defined later but declared here so that
 119  122   * the translator table can be defined.
 120  123   */
 121  124  static write_func_t     write_str, write_boolean, write_uint64;
 122  125  static read_func_t      read_str, read_boolean, read_int64;
 123  126  
 124  127  /*
 125  128   * Translator table, indexed by dladm_datatype_t.
 126  129   */
 127  130  static translator_t translators[] = {
 128  131          { "string",     write_str,      read_str        },
 129  132          { "boolean",    write_boolean,  read_boolean    },
 130  133          { "int",        write_uint64,   read_int64      }
 131  134  };
 132  135  
 133  136  static size_t ntranslators = sizeof (translators) / sizeof (translator_t);
 134  137  
 135  138  #define LINK_PROPERTY_DELIMINATOR       ";"
 136  139  #define LINK_PROPERTY_TYPE_VALUE_SEP    ","
 137  140  #define BASE_PROPERTY_LENGTH(t, n) (strlen(translators[(t)].type_name) +\
 138  141                                      strlen(LINK_PROPERTY_TYPE_VALUE_SEP) +\
 139  142                                      strlen(LINK_PROPERTY_DELIMINATOR) +\
 140  143                                      strlen((n)))
 141  144  #define GENERATE_PROPERTY_STRING(buf, length, conv, name, type, val) \
 142  145              (snprintf((buf), (length), "%s=%s%s" conv "%s", (name), \
 143  146              translators[(type)].type_name, \
 144  147              LINK_PROPERTY_TYPE_VALUE_SEP, (val), LINK_PROPERTY_DELIMINATOR))
 145  148  
 146  149  /*
 147  150   * Name of the cache file to keep the active <link name, linkid> mapping
 148  151   */
 149  152  char    cachefile[MAXPATHLEN];
 150  153  
 151  154  #define DLMGMT_PERSISTENT_DB_PATH       "/etc/dladm/datalink.conf"
 152  155  #define DLMGMT_MAKE_FILE_DB_PATH(buffer, persistent)    \
 153  156          (void) snprintf((buffer), MAXPATHLEN, "%s", \
 154  157          (persistent) ? DLMGMT_PERSISTENT_DB_PATH : cachefile);
 155  158  
 156  159  typedef struct zopen_arg {
 157  160          const char      *zopen_modestr;
 158  161          int             *zopen_pipe;
 159  162          int             zopen_fd;
 160  163  } zopen_arg_t;
 161  164  
 162  165  typedef struct zrename_arg {
 163  166          const char      *zrename_newname;
 164  167  } zrename_arg_t;
 165  168  
 166  169  typedef union zfoparg {
 167  170          zopen_arg_t     zfop_openarg;
 168  171          zrename_arg_t   zfop_renamearg;
 169  172  } zfoparg_t;
 170  173  
 171  174  typedef struct zfcbarg {
 172  175          boolean_t       zfarg_inglobalzone; /* is callback in global zone? */
 173  176          zoneid_t        zfarg_finglobalzone; /* is file in global zone? */
 174  177          const char      *zfarg_filename;
 175  178          zfoparg_t       *zfarg_oparg;
 176  179  } zfarg_t;
 177  180  #define zfarg_openarg   zfarg_oparg->zfop_openarg
 178  181  #define zfarg_renamearg zfarg_oparg->zfop_renamearg
 179  182  
 180  183  /* zone file callback */
 181  184  typedef int zfcb_t(zfarg_t *);
 182  185  
 183  186  /*
 184  187   * Execute an operation on filename relative to zoneid's zone root.  If the
 185  188   * file is in the global zone, then the zfcb() callback will simply be called
 186  189   * directly.  If the file is in a non-global zone, then zfcb() will be called
 187  190   * both from the global zone's context, and from the non-global zone's context
 188  191   * (from a fork()'ed child that has entered the non-global zone).  This is
 189  192   * done to allow the callback to communicate with itself if needed (e.g. to
 190  193   * pass back the file descriptor of an opened file).
 191  194   */
 192  195  static int
 193  196  dlmgmt_zfop(const char *filename, zoneid_t zoneid, zfcb_t *zfcb,
 194  197      zfoparg_t *zfoparg)
 195  198  {
 196  199          int             ctfd;
 197  200          int             err;
 198  201          pid_t           childpid;
 199  202          siginfo_t       info;
 200  203          zfarg_t         zfarg;
 201  204          ctid_t          ct;
 202  205  
 203  206          if (zoneid != GLOBAL_ZONEID) {
 204  207                  /*
 205  208                   * We need to access a file that isn't in the global zone.
 206  209                   * Accessing non-global zone files from the global zone is
 207  210                   * unsafe (due to symlink attacks), we'll need to fork a child
 208  211                   * that enters the zone in question and executes the callback
 209  212                   * that will operate on the file.
 210  213                   *
 211  214                   * Before we proceed with this zone tango, we need to create a
 212  215                   * new process contract for the child, as required by
 213  216                   * zone_enter().
 214  217                   */
 215  218                  errno = 0;
 216  219                  ctfd = open64("/system/contract/process/template", O_RDWR);
 217  220                  if (ctfd == -1)
 218  221                          return (errno);
 219  222                  if ((err = ct_tmpl_set_critical(ctfd, 0)) != 0 ||
 220  223                      (err = ct_tmpl_set_informative(ctfd, 0)) != 0 ||
 221  224                      (err = ct_pr_tmpl_set_fatal(ctfd, CT_PR_EV_HWERR)) != 0 ||
 222  225                      (err = ct_pr_tmpl_set_param(ctfd, CT_PR_PGRPONLY)) != 0 ||
 223  226                      (err = ct_tmpl_activate(ctfd)) != 0) {
 224  227                          (void) close(ctfd);
 225  228                          return (err);
 226  229                  }
 227  230                  childpid = fork();
 228  231                  switch (childpid) {
 229  232                  case -1:
 230  233                          (void) ct_tmpl_clear(ctfd);
 231  234                          (void) close(ctfd);
 232  235                          return (err);
 233  236                  case 0:
 234  237                          (void) ct_tmpl_clear(ctfd);
 235  238                          (void) close(ctfd);
 236  239                          /*
 237  240                           * Elevate our privileges as zone_enter() requires all
 238  241                           * privileges.
 239  242                           */
 240  243                          if ((err = dlmgmt_elevate_privileges()) != 0)
 241  244                                  _exit(err);
 242  245                          if (zone_enter(zoneid) == -1)
 243  246                                  _exit(errno);
 244  247                          if ((err = dlmgmt_drop_privileges()) != 0)
 245  248                                  _exit(err);
 246  249                          break;
 247  250                  default:
 248  251                          if (contract_latest(&ct) == -1)
 249  252                                  ct = -1;
 250  253                          (void) ct_tmpl_clear(ctfd);
 251  254                          (void) close(ctfd);
 252  255                          if (waitid(P_PID, childpid, &info, WEXITED) == -1) {
 253  256                                  (void) contract_abandon_id(ct);
 254  257                                  return (errno);
 255  258                          }
 256  259                          (void) contract_abandon_id(ct);
 257  260                          if (info.si_status != 0)
 258  261                                  return (info.si_status);
 259  262                  }
 260  263          }
 261  264  
 262  265          zfarg.zfarg_inglobalzone = (zoneid == GLOBAL_ZONEID || childpid != 0);
 263  266          zfarg.zfarg_finglobalzone = (zoneid == GLOBAL_ZONEID);
 264  267          zfarg.zfarg_filename = filename;
 265  268          zfarg.zfarg_oparg = zfoparg;
 266  269          err = zfcb(&zfarg);
 267  270          if (!zfarg.zfarg_inglobalzone)
 268  271                  _exit(err);
 269  272          return (err);
 270  273  }
 271  274  
 272  275  static int
 273  276  dlmgmt_zopen_cb(zfarg_t *zfarg)
 274  277  {
 275  278          struct strrecvfd recvfd;
 276  279          boolean_t       newfile = B_FALSE;
 277  280          boolean_t       inglobalzone = zfarg->zfarg_inglobalzone;
 278  281          zoneid_t        finglobalzone = zfarg->zfarg_finglobalzone;
 279  282          const char      *filename = zfarg->zfarg_filename;
 280  283          const char      *modestr = zfarg->zfarg_openarg.zopen_modestr;
 281  284          int             *p = zfarg->zfarg_openarg.zopen_pipe;
 282  285          struct stat     statbuf;
 283  286          int             oflags;
 284  287          mode_t          mode;
 285  288          int             fd = -1;
 286  289          int             err;
 287  290  
 288  291          /* We only ever open a file for reading or writing, not both. */
 289  292          oflags = (modestr[0] == 'r') ? O_RDONLY : O_WRONLY | O_CREAT | O_TRUNC;
 290  293          mode = (modestr[0] == 'r') ? 0 : S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
 291  294  
 292  295          /* Open the file if we're in the same zone as the file. */
 293  296          if (inglobalzone == finglobalzone) {
 294  297                  /*
 295  298                   * First determine if we will be creating the file as part of
 296  299                   * opening it.  If so, then we'll need to ensure that it has
 297  300                   * the proper ownership after having opened it.
 298  301                   */
 299  302                  if (oflags & O_CREAT) {
 300  303                          if (stat(filename, &statbuf) == -1) {
 301  304                                  if (errno == ENOENT)
 302  305                                          newfile = B_TRUE;
 303  306                                  else
 304  307                                          return (errno);
 305  308                          }
 306  309                  }
 307  310                  if ((fd = open(filename, oflags, mode)) == -1)
 308  311                          return (errno);
 309  312                  if (newfile) {
 310  313                          if (chown(filename, UID_DLADM, GID_NETADM) == -1) {
 311  314                                  err = errno;
 312  315                                  (void) close(fd);
 313  316                                  return (err);
 314  317                          }
 315  318                  }
 316  319          }
 317  320  
 318  321          /*
 319  322           * If we're not in the global zone, send the file-descriptor back to
 320  323           * our parent in the global zone.
 321  324           */
 322  325          if (!inglobalzone) {
 323  326                  assert(!finglobalzone);
 324  327                  assert(fd != -1);
 325  328                  return (ioctl(p[1], I_SENDFD, fd) == -1 ? errno : 0);
 326  329          }
 327  330  
 328  331          /*
 329  332           * At this point, we know we're in the global zone.  If the file was
 330  333           * in a non-global zone, receive the file-descriptor from our child in
 331  334           * the non-global zone.
 332  335           */
 333  336          if (!finglobalzone) {
 334  337                  if (ioctl(p[0], I_RECVFD, &recvfd) == -1)
 335  338                          return (errno);
 336  339                  fd = recvfd.fd;
 337  340          }
 338  341  
 339  342          zfarg->zfarg_openarg.zopen_fd = fd;
 340  343          return (0);
 341  344  }
 342  345  
 343  346  static int
 344  347  dlmgmt_zunlink_cb(zfarg_t *zfarg)
 345  348  {
 346  349          if (zfarg->zfarg_inglobalzone != zfarg->zfarg_finglobalzone)
 347  350                  return (0);
 348  351          return (unlink(zfarg->zfarg_filename) == 0 ? 0 : errno);
 349  352  }
 350  353  
 351  354  static int
 352  355  dlmgmt_zrename_cb(zfarg_t *zfarg)
 353  356  {
 354  357          if (zfarg->zfarg_inglobalzone != zfarg->zfarg_finglobalzone)
 355  358                  return (0);
 356  359          return (rename(zfarg->zfarg_filename,
 357  360              zfarg->zfarg_renamearg.zrename_newname) == 0 ? 0 : errno);
 358  361  }
 359  362  
 360  363  /*
 361  364   * Same as fopen(3C), except that it opens the file relative to zoneid's zone
 362  365   * root.
 363  366   */
 364  367  static FILE *
 365  368  dlmgmt_zfopen(const char *filename, const char *modestr, zoneid_t zoneid,
 366  369      int *err)
 367  370  {
 368  371          int             p[2];
 369  372          zfoparg_t       zfoparg;
 370  373          FILE            *fp = NULL;
 371  374  
 372  375          if (zoneid != GLOBAL_ZONEID && pipe(p) == -1) {
 373  376                  *err = errno;
 374  377                  return (NULL);
 375  378          }
 376  379  
 377  380          zfoparg.zfop_openarg.zopen_modestr = modestr;
 378  381          zfoparg.zfop_openarg.zopen_pipe = p;
 379  382          *err = dlmgmt_zfop(filename, zoneid, dlmgmt_zopen_cb, &zfoparg);
 380  383          if (zoneid != GLOBAL_ZONEID) {
 381  384                  (void) close(p[0]);
 382  385                  (void) close(p[1]);
 383  386          }
 384  387          if (*err == 0) {
 385  388                  fp = fdopen(zfoparg.zfop_openarg.zopen_fd, modestr);
 386  389                  if (fp == NULL) {
 387  390                          *err = errno;
 388  391                          (void) close(zfoparg.zfop_openarg.zopen_fd);
 389  392                  }
 390  393          }
 391  394          return (fp);
 392  395  }
 393  396  
 394  397  /*
 395  398   * Same as rename(2), except that old and new are relative to zoneid's zone
 396  399   * root.
 397  400   */
 398  401  static int
 399  402  dlmgmt_zrename(const char *old, const char *new, zoneid_t zoneid)
 400  403  {
 401  404          zfoparg_t zfoparg;
 402  405  
 403  406          zfoparg.zfop_renamearg.zrename_newname = new;
 404  407          return (dlmgmt_zfop(old, zoneid, dlmgmt_zrename_cb, &zfoparg));
 405  408  }
 406  409  
 407  410  /*
 408  411   * Same as unlink(2), except that filename is relative to zoneid's zone root.
 409  412   */
 410  413  static int
 411  414  dlmgmt_zunlink(const char *filename, zoneid_t zoneid)
 412  415  {
 413  416          return (dlmgmt_zfop(filename, zoneid, dlmgmt_zunlink_cb, NULL));
 414  417  }
 415  418  
 416  419  static size_t
 417  420  write_str(char *buffer, size_t buffer_length, char *name, void *value)
 418  421  {
 419  422          char    *ptr = value;
 420  423          size_t  data_length = strnlen(ptr, buffer_length);
 421  424  
 422  425          /*
 423  426           * Strings are assumed to be NULL terminated.  In order to fit in
 424  427           * the buffer, the string's length must be less then buffer_length.
 425  428           * If the value is empty, there's no point in writing it, in fact,
 426  429           * we shouldn't even see that case.
 427  430           */
 428  431          if (data_length + BASE_PROPERTY_LENGTH(DLADM_TYPE_STR, name) ==
 429  432              buffer_length || data_length == 0)
 430  433                  return (0);
 431  434  
 432  435          /*
 433  436           * Since we know the string will fit in the buffer, snprintf will
 434  437           * always return less than buffer_length, so we can just return
 435  438           * whatever snprintf returns.
 436  439           */
 437  440          return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%s",
 438  441              name, DLADM_TYPE_STR, ptr));
 439  442  }
 440  443  
 441  444  static size_t
 442  445  write_boolean(char *buffer, size_t buffer_length, char *name, void *value)
 443  446  {
 444  447          boolean_t       *ptr = value;
 445  448  
 446  449          /*
 447  450           * Booleans are either zero or one, so we only need room for two
 448  451           * characters in the buffer.
 449  452           */
 450  453          if (buffer_length <= 1 + BASE_PROPERTY_LENGTH(DLADM_TYPE_BOOLEAN, name))
 451  454                  return (0);
 452  455  
 453  456          return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%d",
 454  457              name, DLADM_TYPE_BOOLEAN, *ptr));
 455  458  }
 456  459  
 457  460  static size_t
 458  461  write_uint64(char *buffer, size_t buffer_length, char *name, void *value)
 459  462  {
 460  463          uint64_t        *ptr = value;
 461  464  
 462  465          /*
 463  466           * Limit checking for uint64_t is a little trickier.
 464  467           */
 465  468          if (snprintf(NULL, 0, "%lld", *ptr)  +
 466  469              BASE_PROPERTY_LENGTH(DLADM_TYPE_UINT64, name) >= buffer_length)
 467  470                  return (0);
 468  471  
 469  472          return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%lld",
 470  473              name, DLADM_TYPE_UINT64, *ptr));
 471  474  }
 472  475  
 473  476  static size_t
 474  477  read_str(char *buffer, void **value)
 475  478  {
 476  479          char            *ptr = calloc(MAXLINKATTRVALLEN, sizeof (char));
 477  480          ssize_t         len;
 478  481  
 479  482          if (ptr == NULL || (len = strlcpy(ptr, buffer, MAXLINKATTRVALLEN))
 480  483              >= MAXLINKATTRVALLEN) {
 481  484                  free(ptr);
 482  485                  return (0);
 483  486          }
 484  487  
 485  488          *(char **)value = ptr;
 486  489  
 487  490          /* Account for NULL terminator */
 488  491          return (len + 1);
 489  492  }
 490  493  
 491  494  static size_t
 492  495  read_boolean(char *buffer, void **value)
 493  496  {
 494  497          boolean_t       *ptr = calloc(1, sizeof (boolean_t));
 495  498  
 496  499          if (ptr == NULL)
 497  500                  return (0);
 498  501  
 499  502          *ptr = atoi(buffer);
 500  503          *(boolean_t **)value = ptr;
 501  504  
 502  505          return (sizeof (boolean_t));
 503  506  }
 504  507  
 505  508  static size_t
 506  509  read_int64(char *buffer, void **value)
 507  510  {
 508  511          int64_t *ptr = calloc(1, sizeof (int64_t));
 509  512  
 510  513          if (ptr == NULL)
 511  514                  return (0);
 512  515  
 513  516          *ptr = (int64_t)atoll(buffer);
 514  517          *(int64_t **)value = ptr;
 515  518  
 516  519          return (sizeof (int64_t));
 517  520  }
 518  521  
 519  522  static dlmgmt_db_req_t *
 520  523  dlmgmt_db_req_alloc(dlmgmt_db_op_t op, const char *linkname,
 521  524      datalink_id_t linkid, zoneid_t zoneid, uint32_t flags, int *err)
 522  525  {
 523  526          dlmgmt_db_req_t *req;
 524  527  
 525  528          if ((req = calloc(1, sizeof (dlmgmt_db_req_t))) == NULL) {
 526  529                  *err = errno;
 527  530          } else {
 528  531                  req->ls_op = op;
 529  532                  if (linkname != NULL)
 530  533                          (void) strlcpy(req->ls_link, linkname, MAXLINKNAMELEN);
 531  534                  req->ls_linkid = linkid;
 532  535                  req->ls_zoneid = zoneid;
 533  536                  req->ls_flags = flags;
 534  537          }
 535  538          return (req);
 536  539  }
 537  540  
 538  541  /*
 539  542   * Update the db entry with name "entryname" using information from "linkp".
 540  543   */
 541  544  static int
 542  545  dlmgmt_db_update(dlmgmt_db_op_t op, const char *entryname, dlmgmt_link_t *linkp,
 543  546      uint32_t flags)
 544  547  {
  
    | 
      ↓ open down ↓ | 
    489 lines elided | 
    
      ↑ open up ↑ | 
  
 545  548          dlmgmt_db_req_t *req;
 546  549          int             err;
 547  550  
 548  551          /* It is either a persistent request or an active request, not both. */
 549  552          assert((flags == DLMGMT_PERSIST) || (flags == DLMGMT_ACTIVE));
 550  553  
 551  554          if ((req = dlmgmt_db_req_alloc(op, entryname, linkp->ll_linkid,
 552  555              linkp->ll_zoneid, flags, &err)) == NULL)
 553  556                  return (err);
 554  557  
      558 +        /* If transient op and onloan, use the global zone cache file. */
      559 +        if (flags == DLMGMT_ACTIVE && linkp->ll_onloan)
      560 +                req->ls_zoneid = GLOBAL_ZONEID;
      561 +
 555  562          /*
 556  563           * If the return error is EINPROGRESS, this request is handled
 557  564           * asynchronously; return success.
 558  565           */
 559  566          err = dlmgmt_process_db_req(req);
 560  567          if (err != EINPROGRESS)
 561  568                  free(req);
 562  569          else
 563  570                  err = 0;
 564  571          return (err);
 565  572  }
 566  573  
 567  574  #define DLMGMT_DB_OP_STR(op)                                    \
 568  575          (((op) == DLMGMT_DB_OP_READ) ? "read" :                 \
 569  576          (((op) == DLMGMT_DB_OP_WRITE) ? "write" : "delete"))
 570  577  
 571  578  #define DLMGMT_DB_CONF_STR(flag)                                \
 572  579          (((flag) == DLMGMT_ACTIVE) ? "active" :                 \
 573  580          (((flag) == DLMGMT_PERSIST) ? "persistent" : ""))
 574  581  
 575  582  static int
 576  583  dlmgmt_process_db_req(dlmgmt_db_req_t *req)
 577  584  {
 578  585          pthread_t       tid;
 579  586          boolean_t       writeop;
 580  587          int             err;
 581  588  
 582  589          /*
 583  590           * If there are already pending "write" requests, queue this request in
 584  591           * the pending list.  Note that this function is called while the
 585  592           * dlmgmt_rw_lock is held, so it is safe to access the global variables.
 586  593           */
 587  594          writeop = (req->ls_op != DLMGMT_DB_OP_READ);
 588  595          if (writeop && (req->ls_flags == DLMGMT_PERSIST) &&
 589  596              (dlmgmt_db_req_head != NULL)) {
 590  597                  dlmgmt_db_req_tail->ls_next = req;
 591  598                  dlmgmt_db_req_tail = req;
 592  599                  return (EINPROGRESS);
 593  600          }
 594  601  
 595  602          err = dlmgmt_process_db_onereq(req, writeop);
 596  603          if (err != EINPROGRESS && err != 0 && err != ENOENT) {
 597  604                  /*
 598  605                   * Log the error unless the request processing is still in
 599  606                   * progress or if the configuration file hasn't been created
 600  607                   * yet (ENOENT).
 601  608                   */
 602  609                  dlmgmt_log(LOG_WARNING, "dlmgmt_process_db_onereq() %s "
 603  610                      "operation on %s configuration failed: %s",
 604  611                      DLMGMT_DB_OP_STR(req->ls_op),
 605  612                      DLMGMT_DB_CONF_STR(req->ls_flags), strerror(err));
 606  613          }
 607  614  
 608  615          if (err == EINPROGRESS) {
 609  616                  assert(req->ls_flags == DLMGMT_PERSIST);
 610  617                  assert(writeop && dlmgmt_db_req_head == NULL);
 611  618                  dlmgmt_db_req_tail = dlmgmt_db_req_head = req;
 612  619                  err = pthread_create(&tid, NULL, dlmgmt_db_update_thread, NULL);
 613  620                  if (err == 0)
 614  621                          return (EINPROGRESS);
 615  622          }
 616  623          return (err);
 617  624  }
 618  625  
 619  626  static int
 620  627  dlmgmt_process_db_onereq(dlmgmt_db_req_t *req, boolean_t writeop)
 621  628  {
 622  629          int     err = 0;
 623  630          FILE    *fp, *nfp = NULL;
 624  631          char    file[MAXPATHLEN];
 625  632          char    newfile[MAXPATHLEN];
 626  633  
 627  634          DLMGMT_MAKE_FILE_DB_PATH(file, (req->ls_flags == DLMGMT_PERSIST));
 628  635          fp = dlmgmt_zfopen(file, "r", req->ls_zoneid, &err);
 629  636          /*
 630  637           * Note that it is not an error if the file doesn't exist.  If we're
 631  638           * reading, we treat this case the same way as an empty file.  If
 632  639           * we're writing, the file will be created when we open the file for
 633  640           * writing below.
 634  641           */
 635  642          if (fp == NULL && !writeop)
 636  643                  return (err);
 637  644  
 638  645          if (writeop) {
 639  646                  (void) snprintf(newfile, MAXPATHLEN, "%s.new", file);
 640  647                  nfp = dlmgmt_zfopen(newfile, "w", req->ls_zoneid, &err);
 641  648                  if (nfp == NULL) {
 642  649                          /*
 643  650                           * EROFS can happen at boot when the file system is
 644  651                           * read-only.  Return EINPROGRESS so that the caller
 645  652                           * can add this request to the pending request list
 646  653                           * and start a retry thread.
 647  654                           */
 648  655                          err = (errno == EROFS ? EINPROGRESS : errno);
 649  656                          goto done;
 650  657                  }
 651  658          }
 652  659          if (writeop) {
 653  660                  if ((err = process_db_write(req, fp, nfp)) == 0)
 654  661                          err = dlmgmt_zrename(newfile, file, req->ls_zoneid);
 655  662          } else {
 656  663                  err = process_db_read(req, fp);
 657  664          }
 658  665  
 659  666  done:
 660  667          if (nfp != NULL) {
 661  668                  (void) fclose(nfp);
 662  669                  if (err != 0)
 663  670                          (void) dlmgmt_zunlink(newfile, req->ls_zoneid);
 664  671          }
 665  672          (void) fclose(fp);
 666  673          return (err);
 667  674  }
 668  675  
 669  676  /*ARGSUSED*/
 670  677  static void *
 671  678  dlmgmt_db_update_thread(void *arg)
 672  679  {
 673  680          dlmgmt_db_req_t *req;
 674  681  
 675  682          dlmgmt_table_lock(B_TRUE);
 676  683  
 677  684          assert(dlmgmt_db_req_head != NULL);
 678  685          while ((req = dlmgmt_db_req_head) != NULL) {
 679  686                  assert(req->ls_flags == DLMGMT_PERSIST);
 680  687                  if (dlmgmt_process_db_onereq(req, B_TRUE) == EINPROGRESS) {
 681  688                          /*
 682  689                           * The filesystem is still read only. Go to sleep and
 683  690                           * try again.
 684  691                           */
 685  692                          dlmgmt_table_unlock();
 686  693                          (void) sleep(5);
 687  694                          dlmgmt_table_lock(B_TRUE);
 688  695                          continue;
 689  696                  }
 690  697  
 691  698                  /*
 692  699                   * The filesystem is no longer read only. Continue processing
 693  700                   * and remove the request from the pending list.
 694  701                   */
 695  702                  dlmgmt_db_req_head = req->ls_next;
 696  703                  if (dlmgmt_db_req_tail == req) {
 697  704                          assert(dlmgmt_db_req_head == NULL);
 698  705                          dlmgmt_db_req_tail = NULL;
 699  706                  }
 700  707                  free(req);
 701  708          }
 702  709  
 703  710          dlmgmt_table_unlock();
 704  711          return (NULL);
 705  712  }
 706  713  
  
    | 
      ↓ open down ↓ | 
    142 lines elided | 
    
      ↑ open up ↑ | 
  
 707  714  static int
 708  715  parse_linkprops(char *buf, dlmgmt_link_t *linkp)
 709  716  {
 710  717          boolean_t               found_type = B_FALSE;
 711  718          dladm_datatype_t        type = DLADM_TYPE_STR;
 712  719          int                     i, len;
 713  720          char                    *curr;
 714  721          char                    attr_name[MAXLINKATTRLEN];
 715  722          size_t                  attr_buf_len = 0;
 716  723          void                    *attr_buf = NULL;
      724 +        boolean_t               rename;
 717  725  
 718  726          curr = buf;
 719  727          len = strlen(buf);
 720  728          attr_name[0] = '\0';
 721  729          for (i = 0; i < len; i++) {
 722  730                  char            c = buf[i];
 723  731                  boolean_t       match = (c == '=' ||
 724  732                      (c == ',' && !found_type) || c == ';');
 725  733  
      734 +                rename = B_FALSE;
      735 +
 726  736                  /*
 727  737                   * Move to the next character if there is no match and
 728  738                   * if we have not reached the last character.
 729  739                   */
 730  740                  if (!match && i != len - 1)
 731  741                          continue;
 732  742  
 733  743                  if (match) {
 734  744                          /*
 735  745                           * NUL-terminate the string pointed to by 'curr'.
 736  746                           */
 737  747                          buf[i] = '\0';
 738  748                          if (*curr == '\0')
 739  749                                  goto parse_fail;
 740  750                  }
 741  751  
 742  752                  if (attr_name[0] != '\0' && found_type) {
 743  753                          /*
 744  754                           * We get here after we have processed the "<prop>="
 745  755                           * pattern. The pattern we are now interested in is
 746  756                           * "<val>;".
 747  757                           */
 748  758                          if (c == '=')
 749  759                                  goto parse_fail;
 750  760  
 751  761                          if (strcmp(attr_name, "linkid") == 0) {
 752  762                                  if (read_int64(curr, &attr_buf) == 0)
 753  763                                          goto parse_fail;
 754  764                                  linkp->ll_linkid =
 755  765                                      (datalink_class_t)*(int64_t *)attr_buf;
 756  766                          } else if (strcmp(attr_name, "name") == 0) {
 757  767                                  if (read_str(curr, &attr_buf) == 0)
 758  768                                          goto parse_fail;
 759  769                                  (void) snprintf(linkp->ll_link,
 760  770                                      MAXLINKNAMELEN, "%s", attr_buf);
  
    | 
      ↓ open down ↓ | 
    25 lines elided | 
    
      ↑ open up ↑ | 
  
 761  771                          } else if (strcmp(attr_name, "class") == 0) {
 762  772                                  if (read_int64(curr, &attr_buf) == 0)
 763  773                                          goto parse_fail;
 764  774                                  linkp->ll_class =
 765  775                                      (datalink_class_t)*(int64_t *)attr_buf;
 766  776                          } else if (strcmp(attr_name, "media") == 0) {
 767  777                                  if (read_int64(curr, &attr_buf) == 0)
 768  778                                          goto parse_fail;
 769  779                                  linkp->ll_media =
 770  780                                      (uint32_t)*(int64_t *)attr_buf;
      781 +                        } else if (strcmp(attr_name, "zone") == 0) {
      782 +                                if (read_str(curr, &attr_buf) == 0)
      783 +                                        goto parse_fail;
      784 +                                linkp->ll_zoneid = getzoneidbyname(attr_buf);
      785 +                                if (linkp->ll_zoneid == -1) {
      786 +                                        if (errno == EFAULT)
      787 +                                                abort();
      788 +                                        /*
      789 +                                         * If we can't find the zone, assign the
      790 +                                         * link to the GZ and mark it for being
      791 +                                         * renamed.
      792 +                                         */
      793 +                                        linkp->ll_zoneid = 0;
      794 +                                        rename = B_TRUE;
      795 +                                }
 771  796                          } else {
 772  797                                  attr_buf_len = translators[type].read_func(curr,
 773  798                                      &attr_buf);
 774  799                                  if (attr_buf_len == 0)
 775  800                                          goto parse_fail;
 776  801  
 777  802                                  if (linkattr_set(&(linkp->ll_head), attr_name,
 778  803                                      attr_buf, attr_buf_len, type) != 0) {
 779  804                                          free(attr_buf);
 780  805                                          goto parse_fail;
 781  806                                  }
 782  807                          }
 783  808  
 784  809                          free(attr_buf);
 785  810                          attr_name[0] = '\0';
 786  811                          found_type = B_FALSE;
 787  812                  } else if (attr_name[0] != '\0') {
 788  813                          /*
 789  814                           * Non-zero length attr_name and found_type of false
 790  815                           * indicates that we have not found the type for this
 791  816                           * attribute.  The pattern now is "<type>,<val>;", we
 792  817                           * want the <type> part of the pattern.
 793  818                           */
 794  819                          for (type = 0; type < ntranslators; type++) {
 795  820                                  if (strcmp(curr,
 796  821                                      translators[type].type_name) == 0) {
 797  822                                          found_type = B_TRUE;
 798  823                                          break;
 799  824                                  }
 800  825                          }
 801  826  
 802  827                          if (!found_type)
 803  828                                  goto parse_fail;
  
    | 
      ↓ open down ↓ | 
    23 lines elided | 
    
      ↑ open up ↑ | 
  
 804  829                  } else {
 805  830                          /*
 806  831                           * A zero length attr_name indicates we are looking
 807  832                           * at the beginning of a link attribute.
 808  833                           */
 809  834                          if (c != '=')
 810  835                                  goto parse_fail;
 811  836  
 812  837                          (void) snprintf(attr_name, MAXLINKATTRLEN, "%s", curr);
 813  838                  }
      839 +
      840 +                /*
      841 +                 * The zone that this link belongs to has died, we are
      842 +                 * reparenting it to the GZ and renaming it to avoid name
      843 +                 * collisions.
      844 +                 */
      845 +                if (rename == B_TRUE) {
      846 +                        (void) snprintf(linkp->ll_link, MAXLINKNAMELEN,
      847 +                            "SUNWorphan%u", (uint16_t)(gethrtime() / 1000));
      848 +                }
 814  849                  curr = buf + i + 1;
 815  850          }
 816  851  
 817  852          /* Correct any erroneous IPTUN datalink class constant in the file */
 818  853          if (linkp->ll_class == 0x60) {
 819  854                  linkp->ll_class = DATALINK_CLASS_IPTUN;
 820  855                  rewrite_needed = B_TRUE;
 821  856          }
 822  857  
 823  858          return (0);
 824  859  
 825  860  parse_fail:
 826  861          /*
 827  862           * Free linkp->ll_head (link attribute list)
 828  863           */
 829  864          linkattr_destroy(linkp);
 830  865          return (-1);
 831  866  }
 832  867  
 833  868  static boolean_t
 834  869  process_link_line(char *buf, dlmgmt_link_t *linkp)
 835  870  {
 836  871          int     i, len, llen;
 837  872          char    *str, *lasts;
 838  873          char    tmpbuf[MAXLINELEN];
 839  874  
 840  875          bzero(linkp, sizeof (*linkp));
 841  876          linkp->ll_linkid = DATALINK_INVALID_LINKID;
 842  877  
 843  878          /*
 844  879           * Use a copy of buf for parsing so that we can do whatever we want.
 845  880           */
 846  881          (void) strlcpy(tmpbuf, buf, MAXLINELEN);
 847  882  
 848  883          /*
 849  884           * Skip leading spaces, blank lines, and comments.
 850  885           */
 851  886          len = strlen(tmpbuf);
 852  887          for (i = 0; i < len; i++) {
 853  888                  if (!isspace(tmpbuf[i]))
 854  889                          break;
 855  890          }
 856  891          if (i == len || tmpbuf[i] == '#')
 857  892                  return (B_TRUE);
 858  893  
 859  894          str = tmpbuf + i;
 860  895          /*
 861  896           * Find the link name and assign it to the link structure.
 862  897           */
 863  898          if (strtok_r(str, " \n\t", &lasts) == NULL)
 864  899                  goto fail;
 865  900  
 866  901          llen = strlen(str);
 867  902          /*
 868  903           * Note that a previous version of the persistent datalink.conf file
 869  904           * stored the linkid as the first field.  In that case, the name will
 870  905           * be obtained through parse_linkprops from a property with the format
 871  906           * "name=<linkname>".  If we encounter such a format, we set
 872  907           * rewrite_needed so that dlmgmt_db_init() can rewrite the file with
 873  908           * the new format after it's done reading in the data.
 874  909           */
 875  910          if (isdigit(str[0])) {
 876  911                  linkp->ll_linkid = atoi(str);
 877  912                  rewrite_needed = B_TRUE;
 878  913          } else {
 879  914                  if (strlcpy(linkp->ll_link, str, sizeof (linkp->ll_link)) >=
 880  915                      sizeof (linkp->ll_link))
 881  916                          goto fail;
 882  917          }
 883  918  
 884  919          str += llen + 1;
 885  920          if (str >= tmpbuf + len)
 886  921                  goto fail;
 887  922  
 888  923          /*
 889  924           * Now find the list of link properties.
 890  925           */
 891  926          if ((str = strtok_r(str, " \n\t", &lasts)) == NULL)
 892  927                  goto fail;
 893  928  
 894  929          if (parse_linkprops(str, linkp) < 0)
 895  930                  goto fail;
 896  931  
 897  932          return (B_TRUE);
 898  933  
 899  934  fail:
 900  935          /*
 901  936           * Delete corrupted line.
 902  937           */
 903  938          buf[0] = '\0';
 904  939          return (B_FALSE);
 905  940  }
 906  941  
 907  942  /*
 908  943   * Find any properties in linkp that refer to "old", and rename to "new".
 909  944   * Return B_TRUE if any renaming occurred.
 910  945   */
 911  946  static int
 912  947  dlmgmt_attr_rename(dlmgmt_link_t *linkp, const char *old, const char *new,
 913  948      boolean_t *renamed)
 914  949  {
 915  950          dlmgmt_linkattr_t       *attrp;
 916  951          char                    *newval = NULL, *pname;
 917  952          char                    valcp[MAXLINKATTRVALLEN];
 918  953          size_t                  newsize;
 919  954  
 920  955          *renamed = B_FALSE;
 921  956  
 922  957          if ((attrp = linkattr_find(linkp->ll_head, "linkover")) != NULL ||
 923  958              (attrp = linkattr_find(linkp->ll_head, "simnetpeer")) != NULL) {
 924  959                  if (strcmp(old, (char *)attrp->lp_val) == 0) {
 925  960                          newsize = strlen(new) + 1;
 926  961                          if ((newval = malloc(newsize)) == NULL)
 927  962                                  return (errno);
 928  963                          (void) strcpy(newval, new);
 929  964                          free(attrp->lp_val);
 930  965                          attrp->lp_val = newval;
 931  966                          attrp->lp_sz = newsize;
 932  967                          *renamed = B_TRUE;
 933  968                  }
 934  969                  return (0);
 935  970          }
 936  971  
 937  972          if ((attrp = linkattr_find(linkp->ll_head, "portnames")) == NULL)
 938  973                  return (0);
 939  974  
 940  975          /* <linkname>:[<linkname>:]... */
 941  976          if ((newval = calloc(MAXLINKATTRVALLEN, sizeof (char))) == NULL)
 942  977                  return (errno);
 943  978  
 944  979          bcopy(attrp->lp_val, valcp, sizeof (valcp));
 945  980          pname = strtok(valcp, ":");
 946  981          while (pname != NULL) {
 947  982                  if (strcmp(pname, old) == 0) {
 948  983                          (void) strcat(newval, new);
 949  984                          *renamed = B_TRUE;
 950  985                  } else {
 951  986                          (void) strcat(newval, pname);
 952  987                  }
 953  988                  (void) strcat(newval, ":");
 954  989                  pname = strtok(NULL, ":");
 955  990          }
 956  991          if (*renamed) {
 957  992                  free(attrp->lp_val);
 958  993                  attrp->lp_val = newval;
 959  994                  attrp->lp_sz = strlen(newval) + 1;
 960  995          } else {
 961  996                  free(newval);
 962  997          }
 963  998          return (0);
 964  999  }
 965 1000  
 966 1001  static int
 967 1002  process_db_write(dlmgmt_db_req_t *req, FILE *fp, FILE *nfp)
 968 1003  {
 969 1004          boolean_t               done = B_FALSE;
 970 1005          int                     err = 0;
 971 1006          dlmgmt_link_t           link_in_file, *linkp = NULL, *dblinkp;
 972 1007          boolean_t               persist = (req->ls_flags == DLMGMT_PERSIST);
 973 1008          boolean_t               writeall, rename, attr_renamed;
 974 1009          char                    buf[MAXLINELEN];
 975 1010  
 976 1011          writeall = (req->ls_linkid == DATALINK_ALL_LINKID);
 977 1012  
 978 1013          if (req->ls_op == DLMGMT_DB_OP_WRITE && !writeall) {
 979 1014                  /*
 980 1015                   * find the link in the avl tree with the given linkid.
 981 1016                   */
 982 1017                  linkp = link_by_id(req->ls_linkid, req->ls_zoneid);
 983 1018                  if (linkp == NULL || (linkp->ll_flags & req->ls_flags) == 0) {
 984 1019                          /*
 985 1020                           * This link has already been changed. This could
 986 1021                           * happen if the request is pending because of
 987 1022                           * read-only file-system. If so, we are done.
 988 1023                           */
 989 1024                          return (0);
 990 1025                  }
 991 1026                  /*
 992 1027                   * In the case of a rename, linkp's name has been updated to
 993 1028                   * the new name, and req->ls_link is the old link name.
 994 1029                   */
 995 1030                  rename = (strcmp(req->ls_link, linkp->ll_link) != 0);
 996 1031          }
 997 1032  
 998 1033          /*
 999 1034           * fp can be NULL if the file didn't initially exist and we're
1000 1035           * creating it as part of this write operation.
1001 1036           */
1002 1037          if (fp == NULL)
1003 1038                  goto write;
1004 1039  
1005 1040          while (err == 0 && fgets(buf, sizeof (buf), fp) != NULL &&
1006 1041              process_link_line(buf, &link_in_file)) {
1007 1042                  /*
1008 1043                   * Only the link name is needed. Free the memory allocated for
1009 1044                   * the link attributes list of link_in_file.
1010 1045                   */
1011 1046                  linkattr_destroy(&link_in_file);
1012 1047  
1013 1048                  if (link_in_file.ll_link[0] == '\0' || done) {
1014 1049                          /*
1015 1050                           * this is a comment line or we are done updating the
1016 1051                           * line for the specified link, write the rest of
1017 1052                           * lines out.
1018 1053                           */
1019 1054                          if (fputs(buf, nfp) == EOF)
1020 1055                                  err = errno;
1021 1056                          continue;
1022 1057                  }
1023 1058  
1024 1059                  switch (req->ls_op) {
1025 1060                  case DLMGMT_DB_OP_WRITE:
1026 1061                          /*
1027 1062                           * For write operations, we generate a new output line
1028 1063                           * if we're either writing all links (writeall) or if
1029 1064                           * the name of the link in the file matches the one
1030 1065                           * we're looking for.  Otherwise, we write out the
1031 1066                           * buffer as-is.
1032 1067                           *
1033 1068                           * If we're doing a rename operation, ensure that any
1034 1069                           * references to the link being renamed in link
1035 1070                           * properties are also updated before we write
1036 1071                           * anything.
1037 1072                           */
1038 1073                          if (writeall) {
1039 1074                                  linkp = link_by_name(link_in_file.ll_link,
1040 1075                                      req->ls_zoneid);
1041 1076                          }
1042 1077                          if (writeall || strcmp(req->ls_link,
1043 1078                              link_in_file.ll_link) == 0) {
1044 1079                                  generate_link_line(linkp, persist, buf);
1045 1080                                  if (!writeall && !rename)
1046 1081                                          done = B_TRUE;
1047 1082                          } else if (rename && persist) {
1048 1083                                  dblinkp = link_by_name(link_in_file.ll_link,
1049 1084                                      req->ls_zoneid);
1050 1085                                  err = dlmgmt_attr_rename(dblinkp, req->ls_link,
1051 1086                                      linkp->ll_link, &attr_renamed);
1052 1087                                  if (err != 0)
1053 1088                                          break;
1054 1089                                  if (attr_renamed) {
1055 1090                                          generate_link_line(dblinkp, persist,
1056 1091                                              buf);
1057 1092                                  }
1058 1093                          }
1059 1094                          if (fputs(buf, nfp) == EOF)
1060 1095                                  err = errno;
1061 1096                          break;
1062 1097                  case DLMGMT_DB_OP_DELETE:
1063 1098                          /*
1064 1099                           * Delete is simple.  If buf does not represent the
1065 1100                           * link we're deleting, write it out.
1066 1101                           */
1067 1102                          if (strcmp(req->ls_link, link_in_file.ll_link) != 0) {
1068 1103                                  if (fputs(buf, nfp) == EOF)
1069 1104                                          err = errno;
1070 1105                          } else {
1071 1106                                  done = B_TRUE;
1072 1107                          }
1073 1108                          break;
1074 1109                  case DLMGMT_DB_OP_READ:
1075 1110                  default:
1076 1111                          err = EINVAL;
1077 1112                          break;
1078 1113                  }
1079 1114          }
1080 1115  
1081 1116  write:
1082 1117          /*
1083 1118           * If we get to the end of the file and have not seen what linkid
1084 1119           * points to, write it out then.
1085 1120           */
1086 1121          if (req->ls_op == DLMGMT_DB_OP_WRITE && !writeall && !rename && !done) {
1087 1122                  generate_link_line(linkp, persist, buf);
1088 1123                  done = B_TRUE;
1089 1124                  if (fputs(buf, nfp) == EOF)
1090 1125                          err = errno;
1091 1126          }
1092 1127  
1093 1128          return (err);
1094 1129  }
1095 1130  
1096 1131  static int
1097 1132  process_db_read(dlmgmt_db_req_t *req, FILE *fp)
1098 1133  {
1099 1134          avl_index_t     name_where, id_where;
1100 1135          dlmgmt_link_t   link_in_file, *newlink, *link_in_db;
1101 1136          char            buf[MAXLINELEN];
1102 1137          int             err = 0;
1103 1138  
1104 1139          /*
1105 1140           * This loop processes each line of the configuration file.
1106 1141           */
1107 1142          while (fgets(buf, MAXLINELEN, fp) != NULL) {
1108 1143                  if (!process_link_line(buf, &link_in_file)) {
1109 1144                          err = EINVAL;
1110 1145                          break;
1111 1146                  }
1112 1147  
1113 1148                  /*
1114 1149                   * Skip the comment line.
1115 1150                   */
1116 1151                  if (link_in_file.ll_link[0] == '\0') {
1117 1152                          linkattr_destroy(&link_in_file);
1118 1153                          continue;
1119 1154                  }
1120 1155  
1121 1156                  if ((req->ls_flags & DLMGMT_ACTIVE) &&
1122 1157                      link_in_file.ll_linkid == DATALINK_INVALID_LINKID) {
1123 1158                          linkattr_destroy(&link_in_file);
1124 1159                          continue;
1125 1160                  }
1126 1161  
1127 1162                  link_in_file.ll_zoneid = req->ls_zoneid;
1128 1163                  link_in_db = link_by_name(link_in_file.ll_link,
1129 1164                      link_in_file.ll_zoneid);
1130 1165                  if (link_in_db != NULL) {
1131 1166                          /*
1132 1167                           * If the link in the database already has the flag
1133 1168                           * for this request set, then the entry is a
1134 1169                           * duplicate.  If it's not a duplicate, then simply
1135 1170                           * turn on the appropriate flag on the existing link.
1136 1171                           */
1137 1172                          if (link_in_db->ll_flags & req->ls_flags) {
1138 1173                                  dlmgmt_log(LOG_WARNING, "Duplicate links "
1139 1174                                      "in the repository: %s",
1140 1175                                      link_in_file.ll_link);
1141 1176                                  linkattr_destroy(&link_in_file);
1142 1177                          } else {
1143 1178                                  if (req->ls_flags & DLMGMT_PERSIST) {
1144 1179                                          /*
1145 1180                                           * Save the newly read properties into
1146 1181                                           * the existing link.
1147 1182                                           */
1148 1183                                          assert(link_in_db->ll_head == NULL);
1149 1184                                          link_in_db->ll_head =
1150 1185                                              link_in_file.ll_head;
1151 1186                                  } else {
1152 1187                                          linkattr_destroy(&link_in_file);
1153 1188                                  }
1154 1189                                  link_in_db->ll_flags |= req->ls_flags;
1155 1190                          }
1156 1191                  } else {
1157 1192                          /*
1158 1193                           * This is a new link.  Allocate a new dlmgmt_link_t
1159 1194                           * and add it to the trees.
1160 1195                           */
1161 1196                          newlink = calloc(1, sizeof (*newlink));
1162 1197                          if (newlink == NULL) {
1163 1198                                  dlmgmt_log(LOG_WARNING, "Unable to allocate "
1164 1199                                      "memory to create new link %s",
1165 1200                                      link_in_file.ll_link);
1166 1201                                  linkattr_destroy(&link_in_file);
1167 1202                                  continue;
1168 1203                          }
1169 1204                          bcopy(&link_in_file, newlink, sizeof (*newlink));
1170 1205  
1171 1206                          if (newlink->ll_linkid == DATALINK_INVALID_LINKID)
1172 1207                                  newlink->ll_linkid = dlmgmt_nextlinkid;
1173 1208                          if (avl_find(&dlmgmt_id_avl, newlink, &id_where) !=
1174 1209                              NULL) {
1175 1210                                  dlmgmt_log(LOG_WARNING, "Link ID %d is already"
1176 1211                                      " in use, destroying link %s",
1177 1212                                      newlink->ll_linkid, newlink->ll_link);
1178 1213                                  link_destroy(newlink);
1179 1214                                  continue;
1180 1215                          }
1181 1216  
1182 1217                          if ((req->ls_flags & DLMGMT_ACTIVE) &&
1183 1218                              link_activate(newlink) != 0) {
1184 1219                                  dlmgmt_log(LOG_WARNING, "Unable to activate %s",
1185 1220                                      newlink->ll_link);
1186 1221                                  link_destroy(newlink);
1187 1222                                  continue;
1188 1223                          }
1189 1224  
1190 1225                          avl_insert(&dlmgmt_id_avl, newlink, id_where);
1191 1226                          /*
1192 1227                           * link_activate call above can insert newlink in
1193 1228                           * dlmgmt_name_avl tree when activating a link that is
1194 1229                           * assigned to a NGZ.
1195 1230                           */
1196 1231                          if (avl_find(&dlmgmt_name_avl, newlink,
1197 1232                              &name_where) == NULL)
1198 1233                                  avl_insert(&dlmgmt_name_avl, newlink,
1199 1234                                      name_where);
1200 1235  
1201 1236                          dlmgmt_advance(newlink);
1202 1237                          newlink->ll_flags |= req->ls_flags;
1203 1238                  }
1204 1239          }
1205 1240  
1206 1241          return (err);
1207 1242  }
1208 1243  
1209 1244  /*
1210 1245   * Generate an entry in the link database.
1211 1246   * Each entry has this format:
1212 1247   * <link name>  <prop0>=<type>,<val>;...;<propn>=<type>,<val>;
1213 1248   */
1214 1249  static void
  
    | 
      ↓ open down ↓ | 
    391 lines elided | 
    
      ↑ open up ↑ | 
  
1215 1250  generate_link_line(dlmgmt_link_t *linkp, boolean_t persist, char *buf)
1216 1251  {
1217 1252          char                    tmpbuf[MAXLINELEN];
1218 1253          char                    *ptr = tmpbuf;
1219 1254          char                    *lim = tmpbuf + MAXLINELEN;
1220 1255          dlmgmt_linkattr_t       *cur_p = NULL;
1221 1256          uint64_t                u64;
1222 1257  
1223 1258          ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", linkp->ll_link);
1224 1259          if (!persist) {
     1260 +                char zname[ZONENAME_MAX];
1225 1261                  /*
1226      -                 * We store the linkid in the active database so that dlmgmtd
1227      -                 * can recover in the event that it is restarted.
     1262 +                 * We store the linkid and the zone name in the active database
     1263 +                 * so that dlmgmtd can recover in the event that it is
     1264 +                 * restarted.
1228 1265                   */
1229 1266                  u64 = linkp->ll_linkid;
1230 1267                  ptr += write_uint64(ptr, BUFLEN(lim, ptr), "linkid", &u64);
     1268 +
     1269 +                if (getzonenamebyid(linkp->ll_zoneid, zname,
     1270 +                    sizeof (zname)) != -1) {
     1271 +                        ptr += write_str(ptr, BUFLEN(lim, ptr), "zone", zname);
     1272 +                }
1231 1273          }
1232 1274          u64 = linkp->ll_class;
1233 1275          ptr += write_uint64(ptr, BUFLEN(lim, ptr), "class", &u64);
1234 1276          u64 = linkp->ll_media;
1235 1277          ptr += write_uint64(ptr, BUFLEN(lim, ptr), "media", &u64);
1236 1278  
1237 1279          /*
1238 1280           * The daemon does not keep any active link attribute. Only store the
1239 1281           * attributes if this request is for persistent configuration,
1240 1282           */
1241 1283          if (persist) {
1242 1284                  for (cur_p = linkp->ll_head; cur_p != NULL;
1243 1285                      cur_p = cur_p->lp_next) {
1244 1286                          ptr += translators[cur_p->lp_type].write_func(ptr,
1245 1287                              BUFLEN(lim, ptr), cur_p->lp_name, cur_p->lp_val);
1246 1288                  }
1247 1289          }
1248 1290  
1249 1291          if (ptr <= lim)
1250 1292                  (void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf);
1251 1293  }
1252 1294  
1253 1295  int
1254 1296  dlmgmt_delete_db_entry(dlmgmt_link_t *linkp, uint32_t flags)
1255 1297  {
1256 1298          return (dlmgmt_db_update(DLMGMT_DB_OP_DELETE, linkp->ll_link, linkp,
1257 1299              flags));
1258 1300  }
1259 1301  
1260 1302  int
1261 1303  dlmgmt_write_db_entry(const char *entryname, dlmgmt_link_t *linkp,
1262 1304      uint32_t flags)
1263 1305  {
1264 1306          int err;
1265 1307  
1266 1308          if (flags & DLMGMT_PERSIST) {
1267 1309                  if ((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, entryname,
1268 1310                      linkp, DLMGMT_PERSIST)) != 0) {
1269 1311                          return (err);
1270 1312                  }
1271 1313          }
1272 1314  
1273 1315          if (flags & DLMGMT_ACTIVE) {
1274 1316                  if (((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, entryname,
1275 1317                      linkp, DLMGMT_ACTIVE)) != 0) && (flags & DLMGMT_PERSIST)) {
1276 1318                          (void) dlmgmt_db_update(DLMGMT_DB_OP_DELETE, entryname,
1277 1319                              linkp, DLMGMT_PERSIST);
1278 1320                          return (err);
1279 1321                  }
1280 1322          }
1281 1323  
1282 1324          return (0);
1283 1325  }
1284 1326  
1285 1327  /*
1286 1328   * Upgrade properties that have link IDs as values to link names.  Because '.'
1287 1329   * is a valid linkname character, the port separater for link aggregations
1288 1330   * must be changed to ':'.
1289 1331   */
1290 1332  static void
1291 1333  linkattr_upgrade(dlmgmt_linkattr_t *attrp)
1292 1334  {
1293 1335          datalink_id_t   linkid;
1294 1336          char            *portidstr;
1295 1337          char            portname[MAXLINKNAMELEN + 1];
1296 1338          dlmgmt_link_t   *linkp;
1297 1339          char            *new_attr_val;
1298 1340          size_t          new_attr_sz;
1299 1341          boolean_t       upgraded = B_FALSE;
1300 1342  
1301 1343          if (strcmp(attrp->lp_name, "linkover") == 0 ||
1302 1344              strcmp(attrp->lp_name, "simnetpeer") == 0) {
1303 1345                  if (attrp->lp_type == DLADM_TYPE_UINT64) {
1304 1346                          linkid = (datalink_id_t)*(uint64_t *)attrp->lp_val;
1305 1347                          if ((linkp = link_by_id(linkid, GLOBAL_ZONEID)) == NULL)
1306 1348                                  return;
1307 1349                          new_attr_sz = strlen(linkp->ll_link) + 1;
1308 1350                          if ((new_attr_val = malloc(new_attr_sz)) == NULL)
1309 1351                                  return;
1310 1352                          (void) strcpy(new_attr_val, linkp->ll_link);
1311 1353                          upgraded = B_TRUE;
1312 1354                  }
1313 1355          } else if (strcmp(attrp->lp_name, "portnames") == 0) {
1314 1356                  /*
1315 1357                   * The old format for "portnames" was
1316 1358                   * "<linkid>.[<linkid>.]...".  The new format is
1317 1359                   * "<linkname>:[<linkname>:]...".
1318 1360                   */
1319 1361                  if (!isdigit(((char *)attrp->lp_val)[0]))
1320 1362                          return;
1321 1363                  new_attr_val = calloc(MAXLINKATTRVALLEN, sizeof (char));
1322 1364                  if (new_attr_val == NULL)
1323 1365                          return;
1324 1366                  portidstr = (char *)attrp->lp_val;
1325 1367                  while (*portidstr != '\0') {
1326 1368                          errno = 0;
1327 1369                          linkid = strtol(portidstr, &portidstr, 10);
1328 1370                          if (linkid == 0 || *portidstr != '.' ||
1329 1371                              (linkp = link_by_id(linkid, GLOBAL_ZONEID)) ==
1330 1372                              NULL) {
1331 1373                                  free(new_attr_val);
1332 1374                                  return;
1333 1375                          }
1334 1376                          (void) snprintf(portname, sizeof (portname), "%s:",
1335 1377                              linkp->ll_link);
1336 1378                          if (strlcat(new_attr_val, portname,
1337 1379                              MAXLINKATTRVALLEN) >= MAXLINKATTRVALLEN) {
1338 1380                                  free(new_attr_val);
1339 1381                                  return;
1340 1382                          }
1341 1383                          /* skip the '.' delimiter */
1342 1384                          portidstr++;
1343 1385                  }
1344 1386                  new_attr_sz = strlen(new_attr_val) + 1;
1345 1387                  upgraded = B_TRUE;
1346 1388          }
1347 1389  
1348 1390          if (upgraded) {
1349 1391                  attrp->lp_type = DLADM_TYPE_STR;
1350 1392                  attrp->lp_sz = new_attr_sz;
1351 1393                  free(attrp->lp_val);
1352 1394                  attrp->lp_val = new_attr_val;
1353 1395          }
1354 1396  }
1355 1397  
1356 1398  static void
1357 1399  dlmgmt_db_upgrade(dlmgmt_link_t *linkp)
1358 1400  {
1359 1401          dlmgmt_linkattr_t *attrp;
1360 1402  
1361 1403          for (attrp = linkp->ll_head; attrp != NULL; attrp = attrp->lp_next)
1362 1404                  linkattr_upgrade(attrp);
1363 1405  }
1364 1406  
1365 1407  static void
1366 1408  dlmgmt_db_phys_activate(dlmgmt_link_t *linkp)
1367 1409  {
1368 1410          linkp->ll_flags |= DLMGMT_ACTIVE;
1369 1411          (void) dlmgmt_write_db_entry(linkp->ll_link, linkp, DLMGMT_ACTIVE);
1370 1412  }
1371 1413  
1372 1414  static void
1373 1415  dlmgmt_db_walk(zoneid_t zoneid, datalink_class_t class, db_walk_func_t *func)
1374 1416  {
  
    | 
      ↓ open down ↓ | 
    134 lines elided | 
    
      ↑ open up ↑ | 
  
1375 1417          dlmgmt_link_t *linkp;
1376 1418  
1377 1419          for (linkp = avl_first(&dlmgmt_id_avl); linkp != NULL;
1378 1420              linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) {
1379 1421                  if (linkp->ll_zoneid == zoneid && (linkp->ll_class & class))
1380 1422                          func(linkp);
1381 1423          }
1382 1424  }
1383 1425  
1384 1426  /*
     1427 + * Attempt to mitigate one of the deadlocks in the dlmgmtd architecture.
     1428 + *
     1429 + * dlmgmt_db_init() calls dlmgmt_process_db_req() which eventually gets to
     1430 + * dlmgmt_zfop() which tries to fork, enter the zone and read the file.
     1431 + * Because of the upcall architecture of dlmgmtd this can lead to deadlock
     1432 + * with the following scenario:
     1433 + *    a) the thread preparing to fork will have acquired the malloc locks
     1434 + *       then attempt to suspend every thread in preparation to fork.
     1435 + *    b) all of the upcalls will be blocked in door_ucred() trying to malloc()
     1436 + *       and get the credentials of their caller.
     1437 + *    c) we can't suspend the in-kernel thread making the upcall.
     1438 + *
     1439 + * Thus, we cannot serve door requests because we're blocked in malloc()
     1440 + * which fork() owns, but fork() is in turn blocked on the in-kernel thread
     1441 + * making the door upcall.  This is a fundamental architectural problem with
     1442 + * any server handling upcalls and also trying to fork().
     1443 + *
     1444 + * To minimize the chance of this deadlock occuring, we check ahead of time to
     1445 + * see if the file we want to read actually exists in the zone (which it almost
     1446 + * never does), so we don't need fork in that case (i.e. rarely to never).
     1447 + */
     1448 +static boolean_t
     1449 +zone_file_exists(char *zoneroot, char *filename)
     1450 +{
     1451 +        struct stat     sb;
     1452 +        char            fname[MAXPATHLEN];
     1453 +
     1454 +        (void) snprintf(fname, sizeof (fname), "%s/%s", zoneroot, filename);
     1455 +
     1456 +        if (stat(fname, &sb) == -1)
     1457 +                return (B_FALSE);
     1458 +
     1459 +        return (B_TRUE);
     1460 +}
     1461 +
     1462 +/*
1385 1463   * Initialize the datalink <link name, linkid> mapping and the link's
1386 1464   * attributes list based on the configuration file /etc/dladm/datalink.conf
1387 1465   * and the active configuration cache file
1388 1466   * /etc/svc/volatile/dladm/datalink-management:default.cache.
1389 1467   */
1390 1468  int
1391      -dlmgmt_db_init(zoneid_t zoneid)
     1469 +dlmgmt_db_init(zoneid_t zoneid, char *zoneroot)
1392 1470  {
1393 1471          dlmgmt_db_req_t *req;
1394 1472          int             err;
1395 1473          boolean_t       boot = B_FALSE;
1396 1474  
1397 1475          if ((req = dlmgmt_db_req_alloc(DLMGMT_DB_OP_READ, NULL,
1398 1476              DATALINK_INVALID_LINKID, zoneid, DLMGMT_ACTIVE, &err)) == NULL)
1399 1477                  return (err);
1400 1478  
1401      -        if ((err = dlmgmt_process_db_req(req)) != 0) {
1402      -                /*
1403      -                 * If we get back ENOENT, that means that the active
1404      -                 * configuration file doesn't exist yet, and is not an error.
1405      -                 * We'll create it down below after we've loaded the
1406      -                 * persistent configuration.
1407      -                 */
1408      -                if (err != ENOENT)
1409      -                        goto done;
     1479 +        /* Handle running in a non-native branded zone (i.e. has /native) */
     1480 +        if (zone_file_exists(zoneroot, "/native" DLMGMT_TMPFS_DIR)) {
     1481 +                char tdir[MAXPATHLEN];
     1482 +
     1483 +                (void) snprintf(tdir, sizeof (tdir), "/native%s", cachefile);
     1484 +                (void) strlcpy(cachefile, tdir, sizeof (cachefile));
     1485 +        }
     1486 +
     1487 +        if (zone_file_exists(zoneroot, cachefile)) {
     1488 +                if ((err = dlmgmt_process_db_req(req)) != 0) {
     1489 +                        /*
     1490 +                         * If we get back ENOENT, that means that the active
     1491 +                         * configuration file doesn't exist yet, and is not an
     1492 +                         * error.  We'll create it down below after we've
     1493 +                         * loaded the persistent configuration.
     1494 +                         */
     1495 +                        if (err != ENOENT)
     1496 +                                goto done;
     1497 +                        boot = B_TRUE;
     1498 +                }
     1499 +        } else {
1410 1500                  boot = B_TRUE;
1411 1501          }
1412 1502  
1413      -        req->ls_flags = DLMGMT_PERSIST;
1414      -        err = dlmgmt_process_db_req(req);
1415      -        if (err != 0 && err != ENOENT)
1416      -                goto done;
     1503 +        if (zone_file_exists(zoneroot, DLMGMT_PERSISTENT_DB_PATH)) {
     1504 +                req->ls_flags = DLMGMT_PERSIST;
     1505 +                err = dlmgmt_process_db_req(req);
     1506 +                if (err != 0 && err != ENOENT)
     1507 +                        goto done;
     1508 +        }
1417 1509          err = 0;
1418 1510          if (rewrite_needed) {
1419 1511                  /*
1420 1512                   * First update links in memory, then dump the entire db to
1421 1513                   * disk.
1422 1514                   */
1423 1515                  dlmgmt_db_walk(zoneid, DATALINK_CLASS_ALL, dlmgmt_db_upgrade);
1424 1516                  req->ls_op = DLMGMT_DB_OP_WRITE;
1425 1517                  req->ls_linkid = DATALINK_ALL_LINKID;
1426 1518                  if ((err = dlmgmt_process_db_req(req)) != 0 &&
1427 1519                      err != EINPROGRESS)
1428 1520                          goto done;
1429 1521          }
1430 1522          if (boot) {
1431 1523                  dlmgmt_db_walk(zoneid, DATALINK_CLASS_PHYS,
1432 1524                      dlmgmt_db_phys_activate);
1433 1525          }
1434 1526  
  
    | 
      ↓ open down ↓ | 
    8 lines elided | 
    
      ↑ open up ↑ | 
  
1435 1527  done:
1436 1528          if (err == EINPROGRESS)
1437 1529                  err = 0;
1438 1530          else
1439 1531                  free(req);
1440 1532          return (err);
1441 1533  }
1442 1534  
1443 1535  /*
1444 1536   * Remove all links in the given zoneid.
     1537 + *
     1538 + * We do this work in two different passes. In the first pass, we remove any
     1539 + * entry that hasn't been loaned and mark every entry that has been loaned as
     1540 + * something that is going to be tombstomed. In the second pass, we drop the
     1541 + * table lock for every entry and remove the tombstombed entry for our zone.
1445 1542   */
1446 1543  void
1447 1544  dlmgmt_db_fini(zoneid_t zoneid)
1448 1545  {
1449 1546          dlmgmt_link_t *linkp = avl_first(&dlmgmt_name_avl), *next_linkp;
1450 1547  
1451 1548          while (linkp != NULL) {
1452 1549                  next_linkp = AVL_NEXT(&dlmgmt_name_avl, linkp);
1453 1550                  if (linkp->ll_zoneid == zoneid) {
1454      -                        (void) dlmgmt_destroy_common(linkp,
1455      -                            DLMGMT_ACTIVE | DLMGMT_PERSIST);
     1551 +                        boolean_t onloan = linkp->ll_onloan;
     1552 +
     1553 +                        /*
     1554 +                         * Cleanup any VNICs that were loaned to the zone
     1555 +                         * before the zone goes away and we can no longer
     1556 +                         * refer to the VNIC by the name/zoneid.
     1557 +                         */
     1558 +                        if (onloan) {
     1559 +                                (void) dlmgmt_delete_db_entry(linkp,
     1560 +                                    DLMGMT_ACTIVE);
     1561 +                                linkp->ll_tomb = B_TRUE;
     1562 +                        } else {
     1563 +                                (void) dlmgmt_destroy_common(linkp,
     1564 +                                    DLMGMT_ACTIVE | DLMGMT_PERSIST);
     1565 +                        }
     1566 +
1456 1567                  }
1457 1568                  linkp = next_linkp;
1458 1569          }
     1570 +
     1571 +again:
     1572 +        linkp = avl_first(&dlmgmt_name_avl);
     1573 +        while (linkp != NULL) {
     1574 +                vnic_ioc_delete_t ioc;
     1575 +
     1576 +                next_linkp = AVL_NEXT(&dlmgmt_name_avl, linkp);
     1577 +
     1578 +                if (linkp->ll_zoneid != zoneid) {
     1579 +                        linkp = next_linkp;
     1580 +                        continue;
     1581 +                }
     1582 +                ioc.vd_vnic_id = linkp->ll_linkid;
     1583 +                if (linkp->ll_tomb != B_TRUE)
     1584 +                        abort();
     1585 +
     1586 +                /*
     1587 +                 * We have to drop the table lock while going up into the
     1588 +                 * kernel. If we hold the table lock while deleting a vnic, we
     1589 +                 * may get blocked on the mac perimeter and the holder of it may
     1590 +                 * want something from dlmgmtd.
     1591 +                 */
     1592 +                dlmgmt_table_unlock();
     1593 +
     1594 +                if (ioctl(dladm_dld_fd(dld_handle),
     1595 +                    VNIC_IOC_DELETE, &ioc) < 0)
     1596 +                        dlmgmt_log(LOG_WARNING, "dlmgmt_db_fini "
     1597 +                            "delete VNIC ioctl failed %d %d",
     1598 +                            ioc.vd_vnic_id, errno);
     1599 +
     1600 +                /*
     1601 +                 * Even though we've dropped the lock, we know that nothing else
     1602 +                 * could have removed us. Therefore, it should be safe to go
     1603 +                 * through and delete ourselves, but do nothing else. We'll have
     1604 +                 * to restart iteration from the beginning. This can be painful.
     1605 +                 */
     1606 +                dlmgmt_table_lock(B_TRUE);
     1607 +
     1608 +                (void) dlmgmt_destroy_common(linkp,
     1609 +                    DLMGMT_ACTIVE | DLMGMT_PERSIST);
     1610 +                goto again;
     1611 +        }
     1612 +
1459 1613  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX