Print this page
ZoL PR 9145

@@ -54,11 +54,10 @@
         { "dnode_hold_free_misses",             KSTAT_DATA_UINT64 },
         { "dnode_hold_free_lock_misses",        KSTAT_DATA_UINT64 },
         { "dnode_hold_free_lock_retry",         KSTAT_DATA_UINT64 },
         { "dnode_hold_free_overflow",           KSTAT_DATA_UINT64 },
         { "dnode_hold_free_refcount",           KSTAT_DATA_UINT64 },
-        { "dnode_hold_free_txg",                KSTAT_DATA_UINT64 },
         { "dnode_free_interior_lock_retry",     KSTAT_DATA_UINT64 },
         { "dnode_allocate",                     KSTAT_DATA_UINT64 },
         { "dnode_reallocate",                   KSTAT_DATA_UINT64 },
         { "dnode_buf_evict",                    KSTAT_DATA_UINT64 },
         { "dnode_alloc_next_chunk",             KSTAT_DATA_UINT64 },

@@ -1258,10 +1257,14 @@
  * If the DNODE_MUST_BE_ALLOCATED flag is set, "slots" must be 0.
  * dnode_hold_impl() will check if the requested dnode is already consumed
  * as an extra dnode slot by an large dnode, in which case it returns
  * ENOENT.
  *
+ * If the DNODE_DRY_RUN flag is set, we don't actually hold the dnode, just
+ * return whether the hold would succeed or not. tag and dnp should set to
+ * NULL in this case.
+ *
  * errors:
  * EINVAL - invalid object number or flags.
  * ENOSPC - hole too small to fulfill "slots" request (DNODE_MUST_BE_FREE)
  * EEXIST - Refers to an allocated dnode (DNODE_MUST_BE_FREE)
  *        - Refers to a freeing dnode (DNODE_MUST_BE_FREE)

@@ -1285,10 +1288,11 @@
         dnode_phys_t *dn_block;
         dnode_handle_t *dnh;
 
         ASSERT(!(flag & DNODE_MUST_BE_ALLOCATED) || (slots == 0));
         ASSERT(!(flag & DNODE_MUST_BE_FREE) || (slots > 0));
+        IMPLY(flag & DNODE_DRY_RUN, (tag == NULL) && (dnp == NULL));
 
         /*
          * If you are holding the spa config lock as writer, you shouldn't
          * be asking the DMU to do *anything* unless it's the root pool
          * which may require us to read from the root filesystem while

@@ -1314,12 +1318,15 @@
                 if ((flag & DNODE_MUST_BE_ALLOCATED) && type == DMU_OT_NONE)
                         return (SET_ERROR(ENOENT));
                 if ((flag & DNODE_MUST_BE_FREE) && type != DMU_OT_NONE)
                         return (SET_ERROR(EEXIST));
                 DNODE_VERIFY(dn);
+                /* Don't actually hold if dry run, just return 0 */
+                if (!(flag & DNODE_DRY_RUN)) {
                 (void) zfs_refcount_add(&dn->dn_holds, tag);
                 *dnp = dn;
+                }
                 return (0);
         }
 
         if (object == 0 || object >= DN_MAX_OBJECT)
                 return (SET_ERROR(EINVAL));

@@ -1460,10 +1467,18 @@
                         dnode_slots_rele(dnc, idx, slots);
                         dbuf_rele(db, FTAG);
                         return (SET_ERROR(ENOENT));
                 }
 
+                /* Don't actually hold if dry run, just return 0 */
+                if (flag & DNODE_DRY_RUN) {
+                        mutex_exit(&dn->dn_mtx);
+                        dnode_slots_rele(dnc, idx, slots);
+                        dbuf_rele(db, FTAG);
+                        return (0);
+                }
+
                 DNODE_STAT_BUMP(dnode_hold_alloc_hits);
         } else if (flag & DNODE_MUST_BE_FREE) {
 
                 if (idx + slots - 1 >= DNODES_PER_BLOCK) {
                         DNODE_STAT_BUMP(dnode_hold_free_overflow);

@@ -1519,26 +1534,26 @@
                         dnode_slots_rele(dnc, idx, slots);
                         dbuf_rele(db, FTAG);
                         return (SET_ERROR(EEXIST));
                 }
 
+                /* Don't actually hold if dry run, just return 0 */
+                if (flag & DNODE_DRY_RUN) {
+                        mutex_exit(&dn->dn_mtx);
+                        dnode_slots_rele(dnc, idx, slots);
+                        dbuf_rele(db, FTAG);
+                        return (0);
+                }
+
                 dnode_set_slots(dnc, idx + 1, slots - 1, DN_SLOT_INTERIOR);
                 DNODE_STAT_BUMP(dnode_hold_free_hits);
         } else {
                 dbuf_rele(db, FTAG);
                 return (SET_ERROR(EINVAL));
         }
 
-        if (dn->dn_free_txg) {
-                DNODE_STAT_BUMP(dnode_hold_free_txg);
-                type = dn->dn_type;
-                mutex_exit(&dn->dn_mtx);
-                dnode_slots_rele(dnc, idx, slots);
-                dbuf_rele(db, FTAG);
-                return (SET_ERROR((flag & DNODE_MUST_BE_ALLOCATED) ?
-                    ENOENT : EEXIST));
-        }
+        ASSERT0(dn->dn_free_txg);
 
         if (zfs_refcount_add(&dn->dn_holds, tag) == 1)
                 dbuf_add_ref(db, dnh);
 
         mutex_exit(&dn->dn_mtx);

@@ -1625,10 +1640,20 @@
                 mutex_enter(&db->db_mtx);
                 dbuf_rele_and_unlock(db, dnh, evicting);
         }
 }
 
+/*
+ * Test whether we can create a dnode at the specified location.
+ */
+int
+dnode_try_claim(objset_t *os, uint64_t object, int slots)
+{
+        return (dnode_hold_impl(os, object, DNODE_MUST_BE_FREE | DNODE_DRY_RUN,
+            slots, NULL, NULL));
+}
+
 void
 dnode_setdirty(dnode_t *dn, dmu_tx_t *tx)
 {
         objset_t *os = dn->dn_objset;
         uint64_t txg = tx->tx_txg;