From: Tim Chase Date: Tue, 11 Nov 2014 05:26:33 +0000 (-0600) Subject: Undirty freed spill blocks. X-Git-Tag: zfs-0.6.3-1.2~3 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a85804ecb574de755c086ee0cf26cdc8921f3172;p=zfs 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 setfattr -n user.test -v short_value 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 Signed-off-by: Brian Behlendorf Closes #2663 Closes #2700 Closes #2701 Closes #2717 Closes #2863 Closes #2884 --- diff --git a/module/zfs/dbuf.c b/module/zfs/dbuf.c index b414c756e..56c803b6d 100644 --- a/module/zfs/dbuf.c +++ b/module/zfs/dbuf.c @@ -875,15 +875,18 @@ dbuf_free_range(dnode_t *dn, uint64_t start, uint64_t end, dmu_tx_t *tx) int epbs = dn->dn_indblkshift - SPA_BLKPTRSHIFT; uint64_t first_l1 = start >> epbs; uint64_t last_l1 = end >> epbs; + 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; last_l1 = end >> epbs; } 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; @@ -919,7 +922,7 @@ dbuf_free_range(dnode_t *dn, uint64_t start, uint64_t end, dmu_tx_t *tx) if (db->db_level != 0) continue; dprintf_dbuf(db, "found buf %s\n", ""); - 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 */