diff options
author | Tim Chase <[email protected]> | 2014-11-10 23:26:33 -0600 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2014-11-17 11:25:48 -0800 |
commit | 4254acb05743dc2175ae76f6e15b0785d4b688fd (patch) | |
tree | 4a3e1dd0e51bd2846c05d79146cf581ebdf4e699 /module/zfs | |
parent | bc9f4131a15a177f1d355ad1c5c844e0ab9a505a (diff) |
Undirty freed spill blocks.
If a spill block's dbuf hasn't yet been written when a spill block is
freed, the unwritten version will still be written. This patch handles
the case in which a spill block's dbuf is freed and undirties it to
prevent it from being written.
The most common case in which this could happen is when xattr=sa is being
used and a long xattr is immediately replaced by a short xattr as in:
setfattr -n user.test -v very_very_very..._long_value <file>
setfattr -n user.test -v short_value <file>
The first value must be sufficiently long that a spill block is generated
and the second value must be short enough to not require a spill block.
In practice, this would typically happen due to internal xattr operations
as a result of setting acltype=posixacl.
Signed-off-by: Tim Chase <[email protected]>
Signed-off-by: Brian Behlendorf <[email protected]>
Closes #2663
Closes #2700
Closes #2701
Closes #2717
Closes #2863
Closes #2884
Diffstat (limited to 'module/zfs')
-rw-r--r-- | module/zfs/dbuf.c | 9 |
1 files changed, 6 insertions, 3 deletions
diff --git a/module/zfs/dbuf.c b/module/zfs/dbuf.c index 13a0dbfd1..b2a105efb 100644 --- a/module/zfs/dbuf.c +++ b/module/zfs/dbuf.c @@ -869,13 +869,16 @@ dbuf_free_range(dnode_t *dn, uint64_t start, uint64_t end, dmu_tx_t *tx) { dmu_buf_impl_t *db, *db_next; uint64_t txg = tx->tx_txg; + boolean_t freespill = + (start == DMU_SPILL_BLKID || end == DMU_SPILL_BLKID); - if (end > dn->dn_maxblkid && (end != DMU_SPILL_BLKID)) + if (end > dn->dn_maxblkid && !freespill) end = dn->dn_maxblkid; dprintf_dnode(dn, "start=%llu end=%llu\n", start, end); mutex_enter(&dn->dn_dbufs_mtx); - if (start >= dn->dn_unlisted_l0_blkid * dn->dn_datablksz) { + if (start >= dn->dn_unlisted_l0_blkid * dn->dn_datablksz && + !freespill) { /* There can't be any dbufs in this range; no need to search. */ mutex_exit(&dn->dn_dbufs_mtx); return; @@ -896,7 +899,7 @@ dbuf_free_range(dnode_t *dn, uint64_t start, uint64_t end, dmu_tx_t *tx) if (db->db_level != 0) continue; - if (db->db_blkid < start || db->db_blkid > end) + if ((db->db_blkid < start || db->db_blkid > end) && !freespill) continue; /* found a level 0 buffer in the range */ |