From 58806b4cdc32e6f4e4a214cfba3b62a24efb34b7 Mon Sep 17 00:00:00 2001 From: Ned Bass Date: Tue, 24 Mar 2015 17:00:08 -0700 Subject: [PATCH] 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 Signed-off-by: Tim Chase Signed-off-by: Brian Behlendorf Fixes #3195 Fixes #3204 Fixes #3222 --- module/zfs/dbuf.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 */