* [PATCH] ext4: don't report delalloc data as a hole on indirect-mapped inodes [not found] <CGME20260826052446epcms2p5a415be5fbfe23b8a32786f0ae6d05aea@epcms2p5> @ 2026-08-26 5:24 ` Daejun Park 2026-08-26 5:34 ` sashiko-bot 2026-08-26 16:55 ` Jan Kara 0 siblings, 2 replies; 3+ messages in thread From: Daejun Park @ 2026-08-26 5:24 UTC (permalink / raw) To: tytso@mit.edu, adilger.kernel@dilger.ca Cc: jack@suse.cz, yi.zhang@huawei.com, ritesh.list@gmail.com, libaokun@linux.alibaba.com, ojaswin@linux.ibm.com, linux-ext4@vger.kernel.org, linux-kernel@vger.kernel.org, amonakov@ispras.ru, Daejun Park When a plain lookup finds no block, ext4_ind_map_blocks() sizes the hole it reports by counting the empty subtrees under 'partial' in the on-disk indirect tree. That count knows nothing about delayed allocation, so a range holding delalloc data that has not been written back yet is reported as a plain hole. The extent-mapped path does not have this problem: ext4_ext_determine_insert_hole() trims the hole it found at the first delayed extent before returning it. ext4_map_blocks() consults the extent status tree before it calls into the mapping layer, so a query that starts exactly on the delayed block still finds it. A query that starts earlier does not, because the hole reported for the earlier block already spans the delayed one. On a 4k-block filesystem, lseek(SEEK_DATA) from offset 0 on a file whose only data block is at logical block N and is still dirty returns: N = 0..11 direct blocks N << 12 N = 12 first single-indirect block N << 12 N = 13..1035 inside that indirect block -1 ENXIO N = 1036 first double-indirect block N << 12 Blocks 12 and 1036 survive because the query lands on the delayed block itself. Blocks 13..1035 are swallowed by the 1024-block hole reported for block 12. fiemap loses the same data for the same reason. An ext3 filesystem mounted as ext4 hits this during ordinary use: ext3 inodes stay indirect mapped, while mount -t ext4 turns on delayed allocation even though mounting ext3 with -o delalloc is explicitly rejected. install(1) from coreutils uses SEEK_DATA to locate data in its source file, so installing a sparse file that has not been written back silently produces a destination of the right size holding none of the data: truncate -s 1G img && mkfs.ext3 -F img && mount -t ext4 img mnt echo | dd of=mnt/src bs=1 count=1 seek=64K install mnt/src mnt/dst cmp mnt/src mnt/dst # differ: char 65537 Trim the hole at the first delayed extent, the way the extent-mapped path does. ext4_es_find_extent_range() does not clip the extent it returns to the queried range, so skip one that begins at or before m_lblk and clamp the result against the hole already found. With this the sweep above returns N << 12 for every N, while the nodelalloc and extent-mapped controls are unchanged. generic/225, generic/285, generic/286, generic/436, generic/448 and generic/490 pass on ext4 made both with and without the extent feature. Fixes: facab4d9711e ("ext4: return hole from ext4_map_blocks()") Reported-by: Alexander Monakov <amonakov@ispras.ru> Closes: https://lore.kernel.org/linux-ext4/594c17d9-c00f-e485-96fb-cedf27ce3aa3@ispras.ru/ Cc: stable@vger.kernel.org Signed-off-by: Daejun Park <daejun7.park@samsung.com> --- fs/ext4/indirect.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c index 5aec759eed70..4a1ad86a42bd 100644 --- a/fs/ext4/indirect.c +++ b/fs/ext4/indirect.c @@ -574,6 +574,7 @@ int ext4_ind_map_blocks(handle_t *handle, struct inode *inode, /* Next simple case - plain lookup failed */ if ((flags & EXT4_GET_BLOCKS_CREATE) == 0) { unsigned epb = inode->i_sb->s_blocksize / sizeof(u32); + struct extent_status es; int i; /* @@ -589,6 +590,21 @@ int ext4_ind_map_blocks(handle_t *handle, struct inode *inode, /* Fill in size of a hole we found */ map->m_pblk = 0; map->m_len = umin(map->m_len, count); + + /* + * The hole was measured on the on-disk indirect tree, which + * knows nothing about delayed allocation. Trim it at the + * first delayed extent, the way the extent-mapped path does + * in ext4_ext_determine_insert_hole(), so that callers do not + * mistake data that is not written back yet for a hole. The + * extent found is not clipped to the queried range, so it may + * begin before m_lblk or past the end of the hole. + */ + ext4_es_find_extent_range(inode, &ext4_es_is_delayed, + map->m_lblk, + map->m_lblk + map->m_len - 1, &es); + if (es.es_len && es.es_lblk > map->m_lblk) + map->m_len = umin(map->m_len, es.es_lblk - map->m_lblk); goto cleanup; } base-commit: 9091c97be34083587a75db174aab51551d8e8543 -- 2.43.0 ^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH] ext4: don't report delalloc data as a hole on indirect-mapped inodes 2026-08-26 5:24 ` [PATCH] ext4: don't report delalloc data as a hole on indirect-mapped inodes Daejun Park @ 2026-08-26 5:34 ` sashiko-bot 2026-08-26 16:55 ` Jan Kara 1 sibling, 0 replies; 3+ messages in thread From: sashiko-bot @ 2026-08-26 5:34 UTC (permalink / raw) To: Daejun Park; +Cc: tytso, linux-ext4 > When a plain lookup finds no block, ext4_ind_map_blocks() sizes the hole > it reports by counting the empty subtrees under 'partial' in the on-disk > indirect tree. That count knows nothing about delayed allocation, so a > range holding delalloc data that has not been written back yet is > reported as a plain hole. The extent-mapped path does not have this > problem: ext4_ext_determine_insert_hole() trims the hole it found at the > first delayed extent before returning it. > > ext4_map_blocks() consults the extent status tree before it calls into > the mapping layer, so a query that starts exactly on the delayed block > still finds it. A query that starts earlier does not, because the hole > reported for the earlier block already spans the delayed one. On a > 4k-block filesystem, lseek(SEEK_DATA) from offset 0 on a file whose only > data block is at logical block N and is still dirty returns: > > [ ... ] > Fixes: facab4d9711e ("ext4: return hole from ext4_map_blocks()") > Reported-by: Alexander Monakov <amonakov@ispras.ru> > Closes: https://lore.kernel.org/linux-ext4/594c17d9-c00f-e485-96fb-cedf27ce3aa3@ispras.ru/ > Cc: stable@vger.kernel.org > Signed-off-by: Daejun Park <daejun7.park@samsung.com> Sashiko has reviewed this patch and found no issues. It looks great! -- Sashiko AI review · https://sashiko.dev/#/patchset/20260826052446epcms2p5a415be5fbfe23b8a32786f0ae6d05aea@epcms2p5?part=1 ^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] ext4: don't report delalloc data as a hole on indirect-mapped inodes 2026-08-26 5:24 ` [PATCH] ext4: don't report delalloc data as a hole on indirect-mapped inodes Daejun Park 2026-08-26 5:34 ` sashiko-bot @ 2026-08-26 16:55 ` Jan Kara 1 sibling, 0 replies; 3+ messages in thread From: Jan Kara @ 2026-08-26 16:55 UTC (permalink / raw) To: Daejun Park Cc: tytso@mit.edu, adilger.kernel@dilger.ca, jack@suse.cz, yi.zhang@huawei.com, ritesh.list@gmail.com, libaokun@linux.alibaba.com, ojaswin@linux.ibm.com, linux-ext4@vger.kernel.org, linux-kernel@vger.kernel.org, amonakov@ispras.ru On Wed 26-08-26 14:24:46, Daejun Park wrote: > When a plain lookup finds no block, ext4_ind_map_blocks() sizes the hole > it reports by counting the empty subtrees under 'partial' in the on-disk > indirect tree. That count knows nothing about delayed allocation, so a > range holding delalloc data that has not been written back yet is > reported as a plain hole. The extent-mapped path does not have this > problem: ext4_ext_determine_insert_hole() trims the hole it found at the > first delayed extent before returning it. > > ext4_map_blocks() consults the extent status tree before it calls into > the mapping layer, so a query that starts exactly on the delayed block > still finds it. A query that starts earlier does not, because the hole > reported for the earlier block already spans the delayed one. On a > 4k-block filesystem, lseek(SEEK_DATA) from offset 0 on a file whose only > data block is at logical block N and is still dirty returns: > > N = 0..11 direct blocks N << 12 > N = 12 first single-indirect block N << 12 > N = 13..1035 inside that indirect block -1 ENXIO > N = 1036 first double-indirect block N << 12 > > Blocks 12 and 1036 survive because the query lands on the delayed block > itself. Blocks 13..1035 are swallowed by the 1024-block hole reported > for block 12. fiemap loses the same data for the same reason. > > An ext3 filesystem mounted as ext4 hits this during ordinary use: ext3 > inodes stay indirect mapped, while mount -t ext4 turns on delayed > allocation even though mounting ext3 with -o delalloc is explicitly > rejected. install(1) from coreutils uses SEEK_DATA to locate data in > its source file, so installing a sparse file that has not been written > back silently produces a destination of the right size holding none of > the data: > > truncate -s 1G img && mkfs.ext3 -F img && mount -t ext4 img mnt > echo | dd of=mnt/src bs=1 count=1 seek=64K > install mnt/src mnt/dst > cmp mnt/src mnt/dst # differ: char 65537 > > Trim the hole at the first delayed extent, the way the extent-mapped > path does. ext4_es_find_extent_range() does not clip the extent it > returns to the queried range, so skip one that begins at or before > m_lblk and clamp the result against the hole already found. > > With this the sweep above returns N << 12 for every N, while the > nodelalloc and extent-mapped controls are unchanged. generic/225, > generic/285, generic/286, generic/436, generic/448 and generic/490 pass > on ext4 made both with and without the extent feature. > > Fixes: facab4d9711e ("ext4: return hole from ext4_map_blocks()") > Reported-by: Alexander Monakov <amonakov@ispras.ru> > Closes: https://lore.kernel.org/linux-ext4/594c17d9-c00f-e485-96fb-cedf27ce3aa3@ispras.ru/ > Cc: stable@vger.kernel.org > Signed-off-by: Daejun Park <daejun7.park@samsung.com> Thanks for report and the fix! So I think a better fix here is to lift most of the logic of ext4_ext_determine_insert_hole() into a helper function like: ext4_lblk_t ext4_determine_insert_hole(struct inode *inode, ext4_lblk_t hole_start, ext4_lblk_t hole_len) and then use it from both ext4_ext_map_block() and ext4_ind_map_blocks(). That avoids the duplication and also have all the comments regarding various special cases (like delalloc extent covering map->m_lblk) in one place. Honza -- Jan Kara <jack@suse.com> SUSE Labs, CR ^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-08-26 16:55 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <CGME20260826052446epcms2p5a415be5fbfe23b8a32786f0ae6d05aea@epcms2p5>
2026-08-26 5:24 ` [PATCH] ext4: don't report delalloc data as a hole on indirect-mapped inodes Daejun Park
2026-08-26 5:34 ` sashiko-bot
2026-08-26 16:55 ` Jan Kara
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox