]> git.proxmox.com Git - mirror_zfs.git/commitdiff
Fix dnode_hold() freeing dnode behavior
authorBrian Behlendorf <behlendorf1@llnl.gov>
Wed, 5 Dec 2018 17:29:33 +0000 (09:29 -0800)
committerGitHub <noreply@github.com>
Wed, 5 Dec 2018 17:29:33 +0000 (09:29 -0800)
Commit 4c5b89f59 refactored dnode_hold() and in the process
accidentally introduced a slight change in behavior which was
not intended.  The required behavior is that once the ZPL,
or other consumer, declares its intent to free a dnode then
dnode_hold() should immediately start failing.  This updated
code wouldn't return the failure until after it was freed.

When DNODE_MUST_BE_ALLOCATED is set it must return ENOENT, and
when DNODE_MUST_BE_FREE is set it must return EEXIST;

This issue was uncovered by ztest_remap() which attempted
to remap a freeing object which should have been skipped as
described by the comment in dmu_objset_remap_indirects_impl().

Reviewed-by: George Melikov <mail@gmelikov.ru>
Reviewed-by: Tom Caputi <tcaputi@datto.com>
Reviewed-by: Olaf Faaland <faaland1@llnl.gov>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #8172

module/zfs/dnode.c

index 6b9f940531c6dd54c634d6ce08b89353c7cf4931..0e9a4dabec50b8aa62fefdb7c46bed50f76e2f12 100644 (file)
@@ -1253,8 +1253,10 @@ dnode_buf_evict_async(void *dbu)
  * 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)
  *        - Refers to an interior dnode slot (DNODE_MUST_BE_ALLOCATED)
  * ENOENT - The requested dnode is not allocated (DNODE_MUST_BE_ALLOCATED)
+ *        - The requested dnode is being freed (DNODE_MUST_BE_ALLOCATED)
  * EIO    - I/O error when reading the meta dnode dbuf.
  *
  * succeeds even for free dnodes.
@@ -1443,7 +1445,7 @@ dnode_hold_impl(objset_t *os, uint64_t object, int flag, int slots,
                }
 
                mutex_enter(&dn->dn_mtx);
-               if (dn->dn_type == DMU_OT_NONE) {
+               if (dn->dn_type == DMU_OT_NONE || dn->dn_free_txg != 0) {
                        DNODE_STAT_BUMP(dnode_hold_alloc_type_none);
                        mutex_exit(&dn->dn_mtx);
                        dnode_slots_rele(dnc, idx, slots);
@@ -1502,7 +1504,7 @@ dnode_hold_impl(objset_t *os, uint64_t object, int flag, int slots,
                }
 
                mutex_enter(&dn->dn_mtx);
-               if (!zfs_refcount_is_zero(&dn->dn_holds)) {
+               if (!zfs_refcount_is_zero(&dn->dn_holds) || dn->dn_free_txg) {
                        DNODE_STAT_BUMP(dnode_hold_free_refcount);
                        mutex_exit(&dn->dn_mtx);
                        dnode_slots_rele(dnc, idx, slots);