summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNed Bass <[email protected]>2015-03-24 17:00:08 -0700
committerBrian Behlendorf <[email protected]>2015-03-25 14:48:22 -0700
commit58806b4cdc32e6f4e4a214cfba3b62a24efb34b7 (patch)
tree899fab4a1431d5f3279d2e21c7f1eff0af5cdf14
parentded576e28fe70a40e78a90e4668de8130d599380 (diff)
dbuf_free_range() overzealously frees dbufs
When called to free a spill block from a dnode, dbuf_free_range() has a bug that results in all dbufs for the dnode getting freed. A variety of problems may result from this bug, but a common one was a zap lookup tripping an ASSERT because the zap buffers had been zeroed out. This could happen on a dataset with xattr=sa set when extended attributes are written and removed on a directory concurrently with I/O to files in that directory. Signed-off-by: Ned Bass <[email protected]> Signed-off-by: Tim Chase <[email protected]> Signed-off-by: Brian Behlendorf <[email protected]> Fixes #3195 Fixes #3204 Fixes #3222
-rw-r--r--module/zfs/dbuf.c7
1 files changed, 6 insertions, 1 deletions
diff --git a/module/zfs/dbuf.c b/module/zfs/dbuf.c
index f10a04d11..7a0c66639 100644
--- a/module/zfs/dbuf.c
+++ b/module/zfs/dbuf.c
@@ -898,9 +898,14 @@ dbuf_free_range(dnode_t *dn, uint64_t start, uint64_t end, dmu_tx_t *tx)
db_next = list_next(&dn->dn_dbufs, db);
ASSERT(db->db_blkid != DMU_BONUS_BLKID);
+ /* Skip indirect blocks. */
if (db->db_level != 0)
continue;
- if ((db->db_blkid < start || db->db_blkid > end) && !freespill)
+ /* Skip direct blocks outside the range. */
+ if (!freespill && (db->db_blkid < start || db->db_blkid > end))
+ continue;
+ /* Skip all direct blocks, only free spill blocks. */
+ if (freespill && (db->db_blkid != DMU_SPILL_BLKID))
continue;
/* found a level 0 buffer in the range */