Skip to content

Commit

Permalink
Undirty freed spill blocks.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
dweeezil authored and behlendorf committed Nov 17, 2014
1 parent bc9f413 commit 4254acb
Showing 1 changed file with 6 additions and 3 deletions.
9 changes: 6 additions & 3 deletions module/zfs/dbuf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 */
Expand Down

0 comments on commit 4254acb

Please sign in to comment.