]> granicus.if.org Git - zfs/commitdiff
Undirty freed spill blocks.
authorTim Chase <tim@chase2k.com>
Tue, 11 Nov 2014 05:26:33 +0000 (23:26 -0600)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Tue, 23 Dec 2014 00:31:32 +0000 (16:31 -0800)
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 <tim@chase2k.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #2663
Closes #2700
Closes #2701
Closes #2717
Closes #2863
Closes #2884

module/zfs/dbuf.c

index b414c756e3a6eeef6cea59d1344abf5b24db89f5..56c803b6d8faf2549d75533cd60f258c96e57fac 100644 (file)
@@ -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 */