* [PATCH 0/2] dm snapshot: fix discards spilling into the next COW chunk
@ 2026-10-30 9:21 Chen Cheng
2026-10-30 9:21 ` [PATCH 1/2] dm snapshot: split discards on chunk boundaries Chen Cheng
2026-10-30 9:21 ` [PATCH 2/2] dm snapshot: fix discard writing into the next COW chunk Chen Cheng
0 siblings, 2 replies; 3+ messages in thread
From: Chen Cheng @ 2026-10-30 9:21 UTC (permalink / raw)
To: dm-devel, linux-kernel, agk, snitzer, mpatocka, bmarzins
Cc: stable, yukuai, chencheng
From: Chen Cheng <chencheng@fnnas.com>
Hi,
dm only splits discards at a chunk boundary for targets that ask for
it with ti->max_discard_granularity, and dm-snapshot never did.
So a discard can arrive starting in the middle of a chunk, or covering
several chunks at once.
zero_exception() zeroes the COW chunk using bio->bi_iter.bi_sector, which
remap_exception() has already turned into "COW chunk start + offset
inside the chunk". The zeroing window is shifted by that offset, so the
head of the chunk we were asked to discard keeps its old data, and the
tail spills into the next COW chunk, which belongs to a different
exception.
Patch 1 sets ti->max_discard_granularity, so dm hands dm-snapshot
discards that are split on chunk boundaries.
Patch 2 makes io_overlaps_chunk() also require the discard to start on
a chunk boundary, and completes the discards that don't cover a whole
chunk with bio_endio() instead of sending them to the COW device.
Thanks,
Chen Cheng
Chen Cheng (2):
dm snapshot: split discards on chunk boundaries
dm snapshot: fix discard writing into the next COW chunk
drivers/md/dm-snap.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
--
2.56.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH 1/2] dm snapshot: split discards on chunk boundaries
2026-10-30 9:21 [PATCH 0/2] dm snapshot: fix discards spilling into the next COW chunk Chen Cheng
@ 2026-10-30 9:21 ` Chen Cheng
2026-10-30 9:21 ` [PATCH 2/2] dm snapshot: fix discard writing into the next COW chunk Chen Cheng
1 sibling, 0 replies; 3+ messages in thread
From: Chen Cheng @ 2026-10-30 9:21 UTC (permalink / raw)
To: dm-devel, linux-kernel, agk, snitzer, mpatocka, bmarzins
Cc: stable, yukuai, chencheng
From: Chen Cheng <chencheng@fnnas.com>
dm-snapshot did not set ti->max_discard_granularity when it allows
discards, so a discard could reach the target unaligned, or spanning
several chunks. Set the flag, so dm splits discards on chunk
boundaries.
Fixes: 2e6023850e17 ("dm snapshot: add optional discard support features")
Cc: stable@vger.kernel.org
Signed-off-by: Chen Cheng <chencheng@fnnas.com>
---
drivers/md/dm-snap.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 31676ee05514..e49427bdbeef 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -1356,12 +1356,14 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
spin_lock_init(&s->tracked_chunk_lock);
ti->private = s;
ti->num_flush_bios = num_flush_bios;
- if (s->discard_zeroes_cow)
+ if (s->discard_zeroes_cow) {
ti->num_discard_bios = (s->discard_passdown_origin ? 2 : 1);
+ ti->max_discard_granularity = true;
+ }
ti->per_io_data_size = sizeof(struct dm_snap_tracked_chunk);
/* Add snapshot to the list of snapshots for this origin */
/* Exceptions aren't triggered till snapshot_resume() is called */
r = register_snapshot(s);
--
2.56.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH 2/2] dm snapshot: fix discard writing into the next COW chunk
2026-10-30 9:21 [PATCH 0/2] dm snapshot: fix discards spilling into the next COW chunk Chen Cheng
2026-10-30 9:21 ` [PATCH 1/2] dm snapshot: split discards on chunk boundaries Chen Cheng
@ 2026-10-30 9:21 ` Chen Cheng
1 sibling, 0 replies; 3+ messages in thread
From: Chen Cheng @ 2026-10-30 9:21 UTC (permalink / raw)
To: dm-devel, linux-kernel, agk, snitzer, mpatocka, bmarzins
Cc: stable, yukuai, chencheng
From: Chen Cheng <chencheng@fnnas.com>
zero_exception() zeroes one chunk of the COW device and takes the start
sector from bio->bi_iter.bi_sector. remap_exception() has already set
that to the COW chunk start plus the offset inside the chunk. That
offset is needed for reads and writes, but for a discard it is wrong:
the zeroing window is shifted, so the head of the target chunk keeps
its old data and the tail lands in the next COW chunk, which holds a
different exception.
io_overlaps_chunk() should have caught this, but it only compares the
length. "maps to entire chunks" in snapshot.rst also means the discard
has to start on a chunk boundary.
A discard that is not a full chunk has a second problem: after
remap_exception() it is sent to the COW device, where it hits a chunk
that still backs a live exception. After a discard the contents are
undefined, but the exception stays in the table and keeps pointing at
that chunk, so a later read returns whatever the discard left instead
of the snapshot's data.
Reproducer:
truncate -s 4M o.img c.img
o=$(losetup -f --show o.img)
c=$(losetup -f --show c.img)
dmsetup create snap --table \
"0 8192 snapshot $o $c P 512 1 discard_zeroes_cow"
# write two chunks, this creates two exceptions
head -c 256K /dev/zero | tr '\000' '\252' > pat
dd if=pat of=/dev/mapper/snap bs=256K seek=1 count=1 conv=notrunc
dd if=pat of=/dev/mapper/snap bs=256K seek=2 count=1 conv=notrunc
# shift the snapshot by 8 sectors, so a discard that is aligned on
# top is unaligned inside the snapshot
dmsetup create top --table "0 8184 linear /dev/mapper/snap 8"
blkdiscard -o 262144 -l 262144 /dev/mapper/top
sync
echo 3 > /proc/sys/vm/drop_caches
# chunk 1
dd if=/dev/mapper/snap bs=512 skip=512 count=8 | od -An -tx1 -N1
# chunk 2
dd if=/dev/mapper/snap bs=512 skip=1024 count=8 | od -An -tx1 -N1
Before: aa 00 <- chunk 2 was zeroed although it was not discarded
After: aa aa <- nothing written, a discard is only a hint
Fix it by requiring the discard to start on a chunk boundary in
io_overlaps_chunk(), and by finishing a discard that is not a full
chunk with bio_endio() instead of passing it down to the COW device.
Fixes: 2e6023850e17 ("dm snapshot: add optional discard support features")
Cc: stable@vger.kernel.org
Signed-off-by: Chen Cheng <chencheng@fnnas.com>
---
drivers/md/dm-snap.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index e49427bdbeef..6968e806eb84 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -1946,12 +1946,12 @@ static void zero_exception(struct dm_snapshot *s, struct dm_exception *e,
dm_kcopyd_zero(s->kcopyd_client, 1, &dest, 0, zero_callback, bio);
}
static bool io_overlaps_chunk(struct dm_snapshot *s, struct bio *bio)
{
- return bio->bi_iter.bi_size ==
- (s->store->chunk_size << SECTOR_SHIFT);
+ return bio->bi_iter.bi_size == (s->store->chunk_size << SECTOR_SHIFT) &&
+ !(bio->bi_iter.bi_sector & s->store->chunk_mask);
}
static int snapshot_map(struct dm_target *ti, struct bio *bio)
{
struct dm_exception *e;
@@ -2007,15 +2007,17 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio)
/* If the block is already remapped - use that, else remap it */
e = dm_lookup_exception(&s->complete, chunk);
if (e) {
remap_exception(s, e, bio, chunk);
- if (unlikely(bio_op(bio) == REQ_OP_DISCARD) &&
- io_overlaps_chunk(s, bio)) {
+ if (unlikely(bio_op(bio) == REQ_OP_DISCARD)) {
dm_exception_table_unlock(&lock);
up_read(&s->lock);
- zero_exception(s, e, bio, chunk);
+ if (io_overlaps_chunk(s, bio))
+ zero_exception(s, e, bio, chunk);
+ else
+ bio_endio(bio);
r = DM_MAPIO_SUBMITTED; /* discard is not issued */
goto out;
}
goto out_unlock;
}
--
2.56.0
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-30 9:21 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-30 9:21 [PATCH 0/2] dm snapshot: fix discards spilling into the next COW chunk Chen Cheng
2026-10-30 9:21 ` [PATCH 1/2] dm snapshot: split discards on chunk boundaries Chen Cheng
2026-10-30 9:21 ` [PATCH 2/2] dm snapshot: fix discard writing into the next COW chunk Chen Cheng
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®