Print this page
4047 panic from dbuf_free_range() from dmu_free_object() while doing zfs receive
Reviewed by: Adam Leventhal <ahl@delphix.com>
Reviewed by: George Wilson <george.wilson@delphix.com>

Split Close
Expand all
Collapse all
          --- old/usr/src/uts/common/fs/zfs/dbuf.c
          +++ new/usr/src/uts/common/fs/zfs/dbuf.c
↓ open down ↓ 32 lines elided ↑ open up ↑
  33   33  #include <sys/dmu_objset.h>
  34   34  #include <sys/dsl_dataset.h>
  35   35  #include <sys/dsl_dir.h>
  36   36  #include <sys/dmu_tx.h>
  37   37  #include <sys/spa.h>
  38   38  #include <sys/zio.h>
  39   39  #include <sys/dmu_zfetch.h>
  40   40  #include <sys/sa.h>
  41   41  #include <sys/sa_impl.h>
  42   42  
       43 +/*
       44 + * Number of times that zfs_free_range() took the slow path while doing
       45 + * a zfs receive.  A nonzero value indicates a potential performance problem.
       46 + */
       47 +uint64_t zfs_free_range_recv_miss;
       48 +
  43   49  static void dbuf_destroy(dmu_buf_impl_t *db);
  44   50  static boolean_t dbuf_undirty(dmu_buf_impl_t *db, dmu_tx_t *tx);
  45   51  static void dbuf_write(dbuf_dirty_record_t *dr, arc_buf_t *data, dmu_tx_t *tx);
  46   52  
  47   53  /*
  48   54   * Global data structures and functions for the dbuf cache.
  49   55   */
  50   56  static kmem_cache_t *dbuf_cache;
  51   57  
  52   58  /* ARGSUSED */
↓ open down ↓ 759 lines elided ↑ open up ↑
 812  818          int epbs = dn->dn_indblkshift - SPA_BLKPTRSHIFT;
 813  819          uint64_t first_l1 = start >> epbs;
 814  820          uint64_t last_l1 = end >> epbs;
 815  821  
 816  822          if (end > dn->dn_maxblkid && (end != DMU_SPILL_BLKID)) {
 817  823                  end = dn->dn_maxblkid;
 818  824                  last_l1 = end >> epbs;
 819  825          }
 820  826          dprintf_dnode(dn, "start=%llu end=%llu\n", start, end);
 821  827  
 822      -        if (dmu_objset_is_receiving(dn->dn_objset)) {
      828 +        mutex_enter(&dn->dn_dbufs_mtx);
      829 +        if (start >= dn->dn_unlisted_l0_blkid * dn->dn_datablksz) {
      830 +                /* There can't be any dbufs in this range; no need to search. */
      831 +                mutex_exit(&dn->dn_dbufs_mtx);
      832 +                return;
      833 +        } else if (dmu_objset_is_receiving(dn->dn_objset)) {
 823  834                  /*
 824      -                 * When processing a free record from a zfs receive,
 825      -                 * there should have been no previous modifications to the
 826      -                 * data in this range.  Therefore there should be no dbufs
 827      -                 * in the range.  Searching dn_dbufs for these non-existent
 828      -                 * dbufs can be very expensive, so simply ignore this.
      835 +                 * If we are receiving, we expect there to be no dbufs in
      836 +                 * the range to be freed, because receive modifies each
      837 +                 * block at most once, and in offset order.  If this is
      838 +                 * not the case, it can lead to performance problems,
      839 +                 * so note that we unexpectedly took the slow path.
 829  840                   */
 830      -                VERIFY3P(dbuf_find(dn, 0, start), ==, NULL);
 831      -                VERIFY3P(dbuf_find(dn, 0, end), ==, NULL);
 832      -                return;
      841 +                atomic_inc_64(&zfs_free_range_recv_miss);
 833  842          }
 834  843  
 835      -        mutex_enter(&dn->dn_dbufs_mtx);
 836  844          for (db = list_head(&dn->dn_dbufs); db; db = db_next) {
 837  845                  db_next = list_next(&dn->dn_dbufs, db);
 838  846                  ASSERT(db->db_blkid != DMU_BONUS_BLKID);
 839  847  
 840  848                  if (db->db_level == 1 &&
 841  849                      db->db_blkid >= first_l1 && db->db_blkid <= last_l1) {
 842  850                          mutex_enter(&db->db_mtx);
 843  851                          if (db->db_last_dirty &&
 844  852                              db->db_last_dirty->dr_txg < txg) {
 845  853                                  dbuf_add_ref(db, FTAG);
↓ open down ↓ 867 lines elided ↑ open up ↑
1713 1721           */
1714 1722          mutex_enter(&dn->dn_dbufs_mtx);
1715 1723          db->db_state = DB_EVICTING;
1716 1724          if ((odb = dbuf_hash_insert(db)) != NULL) {
1717 1725                  /* someone else inserted it first */
1718 1726                  kmem_cache_free(dbuf_cache, db);
1719 1727                  mutex_exit(&dn->dn_dbufs_mtx);
1720 1728                  return (odb);
1721 1729          }
1722 1730          list_insert_head(&dn->dn_dbufs, db);
     1731 +        if (db->db_level == 0 && db->db_blkid >=
     1732 +            dn->dn_unlisted_l0_blkid)
     1733 +                dn->dn_unlisted_l0_blkid = db->db_blkid + 1;
1723 1734          db->db_state = DB_UNCACHED;
1724 1735          mutex_exit(&dn->dn_dbufs_mtx);
1725 1736          arc_space_consume(sizeof (dmu_buf_impl_t), ARC_SPACE_OTHER);
1726 1737  
1727 1738          if (parent && parent != dn->dn_dbuf)
1728 1739                  dbuf_add_ref(parent, db);
1729 1740  
1730 1741          ASSERT(dn->dn_object == DMU_META_DNODE_OBJECT ||
1731 1742              refcount_count(&dn->dn_holds) > 0);
1732 1743          (void) refcount_add(&dn->dn_holds, db);
↓ open down ↓ 1025 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX