Re: [inline_data] ext4: Stale flags before sync when convert to non-inline
From: Luis Henriques
Date: Wed Jan 24 2024 - 12:13:34 EST
Daniel Dawson <danielcdawson@xxxxxxxxx> writes:
> On 11/28/23 10:15 PM, Daniel Dawson wrote:
>> When a file is converted from inline to non-inline, it has stale flags until
>> sync.
>
>> Why is this a problem? Because some code will fail under such a condition, for
>> example, lseek(..., SEEK_HOLE) will result in ENOENT.
>
>
> Just tested. Still happening on 6.8-rc1.
FWIW, I've been looking into a similar issue related with inline-data and
delayed allocation. It may however be quite different because it seems to
add small block sizes into the mix:
https://bugzilla.kernel.org/show_bug.cgi?id=200681
Unfortunately, I'm still trying to find my way around all this code and I
can't say I fully understand the whole flow using the reproducer provided
in that bugzilla.
Bellow, I'm inlining a patch that started as debug patch that I've used to
try to understand what was going on. It seems to workaround that bug, but
I know it's not a real fix -- I don't yet understand what's going on.
Regarding your specific usecase, I can reproduce it and, unfortunately, I
don't thing Ted's suggestion will fix it as I don't even see
ext4_iomap_begin_report() being executed at all. Anyway, just my 2
cents... let's see if I can come up with something.
Cheers,
--
Luís
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index d5bd1e3a5d36..d0c3d6fd48de 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -528,7 +528,19 @@ int ext4_readpage_inline(struct inode *inode, struct folio *folio)
if (!folio->index)
ret = ext4_read_inline_folio(inode, folio);
else if (!folio_test_uptodate(folio)) {
- folio_zero_segment(folio, 0, folio_size(folio));
+ struct buffer_head *bh, *head;
+ size_t start = 0;
+
+ head = folio_buffers(folio);
+ if (head) {
+ bh = head;
+ do {
+ if (!buffer_uptodate(bh))
+ break;
+ start += inode->i_sb->s_blocksize;
+ } while ((bh = bh->b_this_page) != head);
+ }
+ folio_zero_segment(folio, start, folio_size(folio));
folio_mark_uptodate(folio);
}