Print this page
5056 ZFS deadlock on db_mtx and dn_holds
Reviewed by: Will Andrews <willa@spectralogic.com>
Reviewed by: Matt Ahrens <mahrens@delphix.com>
Reviewed by: George Wilson <george.wilson@delphix.com>
Approved by: Dan McDonald <danmcd@omniti.com>

Split Close
Expand all
Collapse all
          --- old/usr/src/uts/common/fs/zfs/dsl_dir.c
          +++ new/usr/src/uts/common/fs/zfs/dsl_dir.c
↓ open down ↓ 15 lines elided ↑ open up ↑
  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   * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  23   23   * Copyright (c) 2012, 2014 by Delphix. All rights reserved.
  24   24   * Copyright (c) 2013 Martin Matuska. All rights reserved.
  25   25   * Copyright (c) 2014 Joyent, Inc. All rights reserved.
       26 + * Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
  26   27   */
  27   28  
  28   29  #include <sys/dmu.h>
  29   30  #include <sys/dmu_objset.h>
  30   31  #include <sys/dmu_tx.h>
  31   32  #include <sys/dsl_dataset.h>
  32   33  #include <sys/dsl_dir.h>
  33   34  #include <sys/dsl_prop.h>
  34   35  #include <sys/dsl_synctask.h>
  35   36  #include <sys/dsl_deleg.h>
↓ open down ↓ 82 lines elided ↑ open up ↑
 118  119   * never update the filesystem counts for temporary clones.
 119  120   *
 120  121   * Likewise, we do not update the snapshot counts for temporary snapshots,
 121  122   * such as those created by zfs diff.
 122  123   */
 123  124  
 124  125  extern inline dsl_dir_phys_t *dsl_dir_phys(dsl_dir_t *dd);
 125  126  
 126  127  static uint64_t dsl_dir_space_towrite(dsl_dir_t *dd);
 127  128  
 128      -/* ARGSUSED */
 129  129  static void
 130      -dsl_dir_evict(dmu_buf_t *db, void *arg)
      130 +dsl_dir_evict(void *dbu)
 131  131  {
 132      -        dsl_dir_t *dd = arg;
      132 +        dsl_dir_t *dd = dbu;
 133  133          dsl_pool_t *dp = dd->dd_pool;
 134  134          int t;
 135  135  
      136 +        dd->dd_dbuf = NULL;
      137 +
 136  138          for (t = 0; t < TXG_SIZE; t++) {
 137  139                  ASSERT(!txg_list_member(&dp->dp_dirty_dirs, dd, t));
 138  140                  ASSERT(dd->dd_tempreserved[t] == 0);
 139  141                  ASSERT(dd->dd_space_towrite[t] == 0);
 140  142          }
 141  143  
 142  144          if (dd->dd_parent)
 143      -                dsl_dir_rele(dd->dd_parent, dd);
      145 +                dsl_dir_async_rele(dd->dd_parent, dd);
 144  146  
 145      -        spa_close(dd->dd_pool->dp_spa, dd);
      147 +        spa_async_close(dd->dd_pool->dp_spa, dd);
 146  148  
 147  149          /*
 148  150           * The props callback list should have been cleaned up by
 149  151           * objset_evict().
 150  152           */
 151  153          list_destroy(&dd->dd_prop_cbs);
 152  154          mutex_destroy(&dd->dd_lock);
 153  155          kmem_free(dd, sizeof (dsl_dir_t));
 154  156  }
 155  157  
↓ open down ↓ 75 lines elided ↑ open up ↑
 231  233                              dsl_dir_phys(dd)->dd_origin_obj, FTAG,
 232  234                              &origin_bonus);
 233  235                          if (err != 0)
 234  236                                  goto errout;
 235  237                          origin_phys = origin_bonus->db_data;
 236  238                          dd->dd_origin_txg =
 237  239                              origin_phys->ds_creation_txg;
 238  240                          dmu_buf_rele(origin_bonus, FTAG);
 239  241                  }
 240  242  
 241      -                winner = dmu_buf_set_user_ie(dbuf, dd, dsl_dir_evict);
 242      -                if (winner) {
      243 +                dmu_buf_init_user(&dd->dd_dbu, dsl_dir_evict, &dd->dd_dbuf);
      244 +                winner = dmu_buf_set_user_ie(dbuf, &dd->dd_dbu);
      245 +                if (winner != NULL) {
 243  246                          if (dd->dd_parent)
 244  247                                  dsl_dir_rele(dd->dd_parent, dd);
 245  248                          mutex_destroy(&dd->dd_lock);
 246  249                          kmem_free(dd, sizeof (dsl_dir_t));
 247  250                          dd = winner;
 248  251                  } else {
 249  252                          spa_open_ref(dp->dp_spa, dd);
 250  253                  }
 251  254          }
 252  255  
↓ open down ↓ 23 lines elided ↑ open up ↑
 276  279  }
 277  280  
 278  281  void
 279  282  dsl_dir_rele(dsl_dir_t *dd, void *tag)
 280  283  {
 281  284          dprintf_dd(dd, "%s\n", "");
 282  285          spa_close(dd->dd_pool->dp_spa, tag);
 283  286          dmu_buf_rele(dd->dd_dbuf, tag);
 284  287  }
 285  288  
      289 +/*
      290 + * Remove a reference to the given dsl dir that is being asynchronously
      291 + * released.  Async releases occur from a taskq performing eviction of
      292 + * dsl datasets and dirs.  This process is identical to a normal release
      293 + * with the exception of using the async API for releasing the reference on
      294 + * the spa.
      295 + */
      296 +void
      297 +dsl_dir_async_rele(dsl_dir_t *dd, void *tag)
      298 +{
      299 +        dprintf_dd(dd, "%s\n", "");
      300 +        spa_async_close(dd->dd_pool->dp_spa, tag);
      301 +        dmu_buf_rele(dd->dd_dbuf, tag);
      302 +}
      303 +
 286  304  /* buf must be long enough (MAXNAMELEN + strlen(MOS_DIR_NAME) + 1 should do) */
 287  305  void
 288  306  dsl_dir_name(dsl_dir_t *dd, char *buf)
 289  307  {
 290  308          if (dd->dd_parent) {
 291  309                  dsl_dir_name(dd->dd_parent, buf);
 292  310                  (void) strcat(buf, "/");
 293  311          } else {
 294  312                  buf[0] = '\0';
 295  313          }
↓ open down ↓ 110 lines elided ↑ open up ↑
 406  424                  return (SET_ERROR(EINVAL));
 407  425  
 408  426          ASSERT(dsl_pool_config_held(dp));
 409  427  
 410  428          err = dsl_dir_hold_obj(dp, dp->dp_root_dir_obj, NULL, tag, &dd);
 411  429          if (err != 0) {
 412  430                  return (err);
 413  431          }
 414  432  
 415  433          while (next != NULL) {
 416      -                dsl_dir_t *child_ds;
      434 +                dsl_dir_t *child_dd;
 417  435                  err = getcomponent(next, buf, &nextnext);
 418  436                  if (err != 0)
 419  437                          break;
 420  438                  ASSERT(next[0] != '\0');
 421  439                  if (next[0] == '@')
 422  440                          break;
 423  441                  dprintf("looking up %s in obj%lld\n",
 424  442                      buf, dsl_dir_phys(dd)->dd_child_dir_zapobj);
 425  443  
 426  444                  err = zap_lookup(dp->dp_meta_objset,
 427  445                      dsl_dir_phys(dd)->dd_child_dir_zapobj,
 428  446                      buf, sizeof (ddobj), 1, &ddobj);
 429  447                  if (err != 0) {
 430  448                          if (err == ENOENT)
 431  449                                  err = 0;
 432  450                          break;
 433  451                  }
 434  452  
 435      -                err = dsl_dir_hold_obj(dp, ddobj, buf, tag, &child_ds);
      453 +                err = dsl_dir_hold_obj(dp, ddobj, buf, tag, &child_dd);
 436  454                  if (err != 0)
 437  455                          break;
 438  456                  dsl_dir_rele(dd, tag);
 439      -                dd = child_ds;
      457 +                dd = child_dd;
 440  458                  next = nextnext;
 441  459          }
 442  460  
 443  461          if (err != 0) {
 444  462                  dsl_dir_rele(dd, tag);
 445  463                  return (err);
 446  464          }
 447  465  
 448  466          /*
 449  467           * It's an error if there's more than one component left, or
↓ open down ↓ 1518 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX