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 <bass6@llnl.gov>
Signed-off-by: Tim Chase <tim@chase2k.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Fixes #3195
Fixes #3204
Fixes #3222
This commit is contained in:
Ned Bass 2015-03-24 17:00:08 -07:00 committed by Brian Behlendorf
parent ded576e28f
commit 58806b4cdc

View File

@ -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 */