diff options
author | Brian Behlendorf <[email protected]> | 2018-12-05 09:29:33 -0800 |
---|---|---|
committer | GitHub <[email protected]> | 2018-12-05 09:29:33 -0800 |
commit | 78e21394679ed7435f9b0d10a56ce4f679c680fe (patch) | |
tree | 224dd7c736ae3ed1914c9ceeefcb63ef09395ae4 /module | |
parent | c5eea0ab9c0ce31b172eac948c32a6cea5d0965f (diff) |
Fix dnode_hold() freeing dnode behavior
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 <[email protected]>
Reviewed-by: Tom Caputi <[email protected]>
Reviewed-by: Olaf Faaland <[email protected]>
Signed-off-by: Brian Behlendorf <[email protected]>
Closes #8172
Diffstat (limited to 'module')
-rw-r--r-- | module/zfs/dnode.c | 6 |
1 files changed, 4 insertions, 2 deletions
diff --git a/module/zfs/dnode.c b/module/zfs/dnode.c index 6b9f94053..0e9a4dabe 100644 --- a/module/zfs/dnode.c +++ b/module/zfs/dnode.c @@ -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); |