netfs: Miscellaneous fixes
Subsystems (6)
David Howells
maintainerv2 cover netfs: Miscellaneous fixes
Hi Christian,
Here are some miscellaneous fixes for netfslib:
(1) Fix triggering of a VM_BUG_ON_FOLIO() in netfs_write_begin().
(2) Fix error handling in netfs_extract_user_iter().
(3) Fix netfs_invalidate_folio() to clear the folio dirty bit if all dirty data removed.
(4) Fix the handling of a partially failed copy (ie. EFAULT) into a streaming write folio. Also remove the netfs_folio if a streaming write folio is entirely overwritten.
(5) Fix netfs_read_gaps() to remove the netfs_folio from a filled folio.
(6) Fix the calculation of zero_point in netfs_release_folio() to limit it
to ->remote_i_size, not ->i_size.
(7) Fix netfs_perform_read() to not disable streaming writes when writing to an fd that's open O_RDWR.
The patches can also be found here:
https://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs.git/log/?h=netfs-fixes
Thanks,
David
Changes
=======
ver #2)
- Rebase on v7.0
- Drop the cachefiles dentry count fix as that's now upstream.
- Add four new patches from testing the generic/522 xfstest.
David Howells (5):
netfs: Fix netfs_invalidate_folio() to clear dirty bit if all changes
gone
netfs: Fix streaming write being overwritten
netfs: Fix read-gaps to remove netfs_folio from filled folio
netfs: Fix zeropoint update where i_size > remote_i_size
netfs: Fix write streaming disablement if fd open O_RDWR
Paulo Alcantara (1):
netfs: fix error handling in netfs_extract_user_iter()
Viacheslav Dubeyko (1):
netfs: fix VM_BUG_ON_FOLIO() issue in netfs_write_begin() call
fs/netfs/buffered_read.c | 9 ++++--- fs/netfs/buffered_write.c | 51 +++++++++++++++++++++++------------- fs/netfs/iterator.c | 13 ++++++--- fs/netfs/misc.c | 8 ++++-- fs/netfs/read_retry.c | 11 ++++++-- include/trace/events/netfs.h | 5 ++++ 6 files changed, 69 insertions(+), 28 deletions(-)
David Howells
maintainerPATCH v2 1/7 netfs: fix VM_BUG_ON_FOLIO() issue in netfs_write_begin() call
From: Viacheslav Dubeyko <[email protected]>
The multiple runs of generic/013 test-case is capable to reproduce a kernel BUG at mm/filemap.c:1504 with probability of 30%.
while true; do
sudo ./check generic/013
done
[ 9849.452376] page: refcount:3 mapcount:0 mapping:00000000e58ff252 index:0x10781 pfn:0x1c322 [ 9849.452412] memcg:ffff8881a1915800 [ 9849.452417] aops:ceph_aops ino:1000058db9e dentry name(?):"f9XXXXXX" [ 9849.452432] flags: 0x17ffffc0000000(node=0|zone=2|lastcpupid=0x1fffff) [ 9849.452441] raw: 0017ffffc0000000 0000000000000000 dead000000000122 ffff88816110d248 [ 9849.452445] raw: 0000000000010781 0000000000000000 00000003ffffffff ffff8881a1915800 [ 9849.452447] page dumped because: VM_BUG_ON_FOLIO(!folio_test_locked(folio)) [ 9849.452474] ------------[ cut here ]------------ [ 9849.452476] kernel BUG at mm/filemap.c:1504! [ 9849.478635] Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI [ 9849.481772] CPU: 2 UID: 0 PID: 84223 Comm: fsstress Not tainted 7.0.0-rc1+ #18 PREEMPT(full) [ 9849.482881] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-9.fc43 06/1 0/2025 [ 9849.484539] RIP: 0010:folio_unlock+0x85/0xa0 [ 9849.485076] Code: 89 df 31 f6 e8 1c f3 ff ff 48 8b 5d f8 c9 31 c0 31 d2 31 f6 31 ff c3 cc cc cc cc 48 c7 c6 80 6c d9 a7 48 89 df e8 4b b3 10 00 <0f> 0b 48 89 df e8 21 e6 2c 00 eb 9d 0f 1f 40 00 66 66 2e 0f 1f 84 [ 9849.493818] RSP: 0018:ffff8881bb8076b0 EFLAGS: 00010246 [ 9849.495740] RAX: 0000000000000000 RBX: ffffea00070c8980 RCX: 0000000000000000 [ 9849.498678] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 [ 9849.500559] RBP: ffff8881bb8076b8 R08: 0000000000000000 R09: 0000000000000000 [ 9849.501097] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000010782000 [ 9849.502108] R13: ffff8881935de738 R14: ffff88816110d010 R15: 0000000000001000 [ 9849.502516] FS: 00007e36cbe94740(0000) GS:ffff88824a899000(0000) knlGS:0000000000000000 [ 9849.502996] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 9849.503810] CR2: 000000c0002b0000 CR3: 000000011bbf6004 CR4: 0000000000772ef0 [ 9849.504459] PKRU: 55555554 [ 9849.504626] Call Trace: [ 9849.505242] <TASK> [ 9849.505379] netfs_write_begin+0x7c8/0x10a0 [ 9849.505877] ? __kasan_check_read+0x11/0x20 [ 9849.506384] ? __pfx_netfs_write_begin+0x10/0x10 [ 9849.507178] ceph_write_begin+0x8c/0x1c0 [ 9849.507934] generic_perform_write+0x391/0x8f0 [ 9849.508503] ? __pfx_generic_perform_write+0x10/0x10 [ 9849.509062] ? file_update_time_flags+0x19a/0x4b0 [ 9849.509581] ? ceph_get_caps+0x63/0xf0 [ 9849.510259] ? ceph_get_caps+0x63/0xf0 [ 9849.510530] ceph_write_iter+0xe79/0x1ae0 [ 9849.511282] ? __pfx_ceph_write_iter+0x10/0x10 [ 9849.511839] ? lock_acquire+0x1ad/0x310 [ 9849.512334] ? ksys_write+0xf9/0x230 [ 9849.512582] ? lock_is_held_type+0xaa/0x140 [ 9849.513128] vfs_write+0x512/0x1110 [ 9849.513634] ? __fget_files+0x33/0x350 [ 9849.513893] ? __pfx_vfs_write+0x10/0x10 [ 9849.514143] ? mutex_lock_nested+0x1b/0x30 [ 9849.514394] ksys_write+0xf9/0x230 [ 9849.514621] ? __pfx_ksys_write+0x10/0x10 [ 9849.514887] ? do_syscall_64+0x25e/0x1520 [ 9849.515122] ? __kasan_check_read+0x11/0x20 [ 9849.515366] ? trace_hardirqs_on_prepare+0x178/0x1c0 [ 9849.515655] __x64_sys_write+0x72/0xd0 [ 9849.515885] ? trace_hardirqs_on+0x24/0x1c0 [ 9849.516130] x64_sys_call+0x22f/0x2390 [ 9849.516341] do_syscall_64+0x12b/0x1520 [ 9849.516545] ? do_syscall_64+0x27c/0x1520 [ 9849.516783] ? do_syscall_64+0x27c/0x1520 [ 9849.517003] ? lock_release+0x318/0x480 [ 9849.517220] ? __x64_sys_io_getevents+0x143/0x2d0 [ 9849.517479] ? percpu_ref_put_many.constprop.0+0x8f/0x210 [ 9849.517779] ? entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 9849.518073] ? do_syscall_64+0x25e/0x1520 [ 9849.518291] ? __kasan_check_read+0x11/0x20 [ 9849.518519] ? trace_hardirqs_on_prepare+0x178/0x1c0 [ 9849.518799] ? do_syscall_64+0x27c/0x1520 [ 9849.519024] ? local_clock_noinstr+0xf/0x120 [ 9849.519262] ? entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 9849.519544] ? do_syscall_64+0x25e/0x1520 [ 9849.519781] ? __kasan_check_read+0x11/0x20 [ 9849.520008] ? trace_hardirqs_on_prepare+0x178/0x1c0 [ 9849.520273] ? do_syscall_64+0x27c/0x1520 [ 9849.520491] ? trace_hardirqs_on_prepare+0x178/0x1c0 [ 9849.520767] ? irqentry_exit+0x10c/0x6c0 [ 9849.520984] ? trace_hardirqs_off+0x86/0x1b0 [ 9849.521224] ? exc_page_fault+0xab/0x130 [ 9849.521472] entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 9849.521766] RIP: 0033:0x7e36cbd14907 [ 9849.521989] Code: 10 00 f7 d8 64 89 02 48 c7 c0 ff ff ff ff eb b7 0f 1f 00 f3 0f 1e fa 64 8b 04 25 18 00 00 00 85 c0 75 10 b8 01 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 51 c3 48 83 ec 28 48 89 54 24 18 48 89 74 24 [ 9849.523057] RSP: 002b:00007ffff2d2a968 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 [ 9849.523484] RAX: ffffffffffffffda RBX: 000000000000e549 RCX: 00007e36cbd14907 [ 9849.523885] RDX: 000000000000e549 RSI: 00005bd797ec6370 RDI: 0000000000000004 [ 9849.524277] RBP: 0000000000000004 R08: 0000000000000047 R09: 00005bd797ec6370 [ 9849.524652] R10: 0000000000000078 R11: 0000000000000246 R12: 0000000000000049 [ 9849.525062] R13: 0000000010781a37 R14: 00005bd797ec6370 R15: 0000000000000000 [ 9849.525447] </TASK> [ 9849.525574] Modules linked in: intel_rapl_msr intel_rapl_common intel_uncore_frequency_common intel_pmc_core pmt_telemetry pmt_discovery pmt_class intel_pmc_ssram_telemetry intel_vsec kvm_intel joydev kvm irqbypass ghash_clmulni_intel aesni_intel input_leds rapl mac_hid psmouse vga16fb serio_raw vgastate floppy i2c_piix4 bochs qemu_fw_cfg i2c_smbus pata_acpi sch_fq_codel rbd msr parport_pc ppdev lp parport efi_pstore [ 9849.529150] ---[ end trace 0000000000000000 ]--- [ 9849.529502] RIP: 0010:folio_unlock+0x85/0xa0 [ 9849.530813] Code: 89 df 31 f6 e8 1c f3 ff ff 48 8b 5d f8 c9 31 c0 31 d2 31 f6 31 ff c3 cc cc cc cc 48 c7 c6 80 6c d9 a7 48 89 df e8 4b b3 10 00 <0f> 0b 48 89 df e8 21 e6 2c 00 eb 9d 0f 1f 40 00 66 66 2e 0f 1f 84 [ 9849.534986] RSP: 0018:ffff8881bb8076b0 EFLAGS: 00010246 [ 9849.536198] RAX: 0000000000000000 RBX: ffffea00070c8980 RCX: 0000000000000000 [ 9849.537718] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 [ 9849.539321] RBP: ffff8881bb8076b8 R08: 0000000000000000 R09: 0000000000000000 [ 9849.540862] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000010782000 [ 9849.542438] R13: ffff8881935de738 R14: ffff88816110d010 R15: 0000000000001000 [ 9849.543996] FS: 00007e36cbe94740(0000) GS:ffff88824b899000(0000) knlGS:0000000000000000 [ 9849.545854] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 9849.547092] CR2: 00007e36cb3ff000 CR3: 000000011bbf6006 CR4: 0000000000772ef0 [ 9849.548679] PKRU: 55555554
The race sequence:
1. Read completes -> netfs_read_collection() runs
2. netfs_wake_rreq_flag(rreq, NETFS_RREQ_IN_PROGRESS, ...)
3. netfs_wait_for_read() returns -EFAULT to netfs_write_begin()
4. The netfs_unlock_abandoned_read_pages() unlocks the folio
5. netfs_write_begin() calls folio_unlock(folio) -> VM_BUG_ON_FOLIO()
The key reason of the issue that netfs_unlock_abandoned_read_pages() doesn't check the flag NETFS_RREQ_NO_UNLOCK_FOLIO and executes folio_unlock() unconditionally. This patch implements in netfs_unlock_abandoned_read_pages() logic similar to netfs_unlock_read_folio().
--- fs/netfs/read_retry.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
fs/netfs/read_retry.c +9 -2
@@ -288,8 +288,15 @@ void netfs_unlock_abandoned_read_pages(struct netfs_io_request *rreq)struct folio *folio =folioq_folio (p, slot);if (folio && !folioq_is_marked2 (p, slot)) { -trace_netfs_folio (folio, netfs_folio_trace_abandon); -folio_unlock (folio); +if (folio->index == rreq->no_unlock_folio && +test_bit (NETFS_RREQ_NO_UNLOCK_FOLIO , + &rreq->flags)) { +_debug ("no unlock" ); + }else { +trace_netfs_folio (folio, + netfs_folio_trace_abandon); +folio_unlock (folio); + } } } }
David Howells
maintainerPATCH v2 2/7 netfs: fix error handling in netfs_extract_user_iter()
In reply to David HowellsFrom: Paulo Alcantara <[email protected]>
In netfs_extract_user_iter(), if iov_iter_extract_pages() failed to extract user pages, bail out on -ENOMEM, otherwise return the error code only if @npages == 0, allowing short DIO reads and writes to be issued.
This fixes mmapstress02 from LTP tests against CIFS.
--- fs/netfs/iterator.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
fs/netfs/iterator.c +10 -3
@@ -22,7 +22,7 @@* * Extract the page fragments from the given amount of the source iterator and * build up a second iterator that refers to all of those bits. This allows -* the original iterator to disposed of. +* the original iterator to be disposed of. * * @extraction_flags can have ITER_ALLOW_P2PDMA set to request peer-to-peer DMA be * allowed on the pages extracted.
@@ -67,8 +67,8 @@ ssize_t netfs_extract_user_iter(struct iov_iter *orig, size_t orig_len, ret =iov_iter_extract_pages (orig, &pages, count, max_pages - npages, extraction_flags, &offset); -if (ret <0 ) { -pr_err ("Couldn't get user pages (rc=%zd)\n" , ret); +if (unlikely (ret <=0 )) { + ret = ret ?: -EIO ;break ; }
@@ -97,6 +97,13 @@ ssize_t netfs_extract_user_iter(struct iov_iter *orig, size_t orig_len, npages += cur_npages; } +if (ret <0 && (ret == -ENOMEM || npages ==0 )) { +for (i =0 ; i < npages; i++) +unpin_user_page (bv[i].bv_page); +kvfree (bv); +return ret; + } +iov_iter_bvec (new, orig->data_source, bv, npages, orig_len - count);return npages; }
David Howells
maintainerPATCH v2 3/7 netfs: Fix netfs_invalidate_folio() to clear dirty bit if all changes gone
In reply to David HowellsIf a streaming write is made, this will leave the relevant modified folio in a not-uptodate, but dirty state with a netfs_folio struct hung off of folio->private indicating the dirty range. Subsequently truncating the file such that the dirty data in the folio is removed, but the first part of the folio theoretically remains will cause the netfs_folio struct to be discarded... but will leave the dirty flag set.
If the folio is then read via mmap(), netfs_read_folio() will see that the page is dirty and jump to netfs_read_gaps() to fill in the missing bits. netfs_read_gaps(), however, expects there to be a netfs_folio struct present and can oops because truncate removed it.
Fix this by calling folio_cancel_dirty() in netfs_invalidate_folio() in the event that all the dirty data in the folio is erased (as nfs does).
Also add some tracepoints to log modifications to a dirty page.
This can be reproduced with something like:
dd if=/dev/zero of=/xfstest.test/foo bs=1M count=1
umount /xfstest.test
mount /xfstest.test
xfs_io -c "w 0xbbbf 0xf96c" \
-c "truncate 0xbbbf" \
-c "mmap -r 0xb000 0x11000" \
-c "mr 0xb000 0x11000" \
/xfstest.test/foowith fscaching disabled (otherwise streaming writes are suppressed) and a change to netfs_perform_write() to disallow streaming writes if the fd is open O_RDWR:
if (//(file->f_mode & FMODE_READ) || <--- comment this out
netfs_is_cache_enabled(ctx)) {It should be reproducible even without this change, but if prevents the above trivial xfs_io command from reproducing it.
Note that the initial dd is important: the file must start out sufficiently large that the zero-point logic doesn't just clear the gaps because it knows there's nothing in the file to read yet. Unmounting and mounting is needed to clear the pagecache (there are other ways to do that that may also work).
This was initially reproduced with the generic/522 xfstest on some patches that remove the FMODE_READ restriction.
--- fs/netfs/misc.c | 6 +++++- include/trace/events/netfs.h | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-)
fs/netfs/misc.c +5 -1
@@ -256,6 +256,7 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length)/* Move the start of the data. */ finfo->dirty_len = fend - iend; finfo->dirty_offset = offset; +trace_netfs_folio (folio, netfs_folio_trace_invalidate_front);return ; }
@@ -264,12 +265,14 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length)*/ if (iend >= fend) { finfo->dirty_len = offset - fstart; +trace_netfs_folio (folio, netfs_folio_trace_invalidate_tail);return ; }/* A partial write was split. The caller has already zeroed * it, so just absorb the hole. */ +trace_netfs_folio (folio, netfs_folio_trace_invalidate_middle); }return ;
@@ -277,8 +280,9 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length)netfs_put_group (netfs_folio_group (folio));folio_detach_private (folio);folio_clear_uptodate (folio); +folio_cancel_dirty (folio);kfree (finfo); -return ; +trace_netfs_folio (folio, netfs_folio_trace_invalidate_all); }EXPORT_SYMBOL (netfs_invalidate_folio);
include/trace/events/netfs.h +4 -0
@@ -194,6 +194,10 @@ EM(netfs_folio_trace_copy_to_cache, "mark-copy") \ EM(netfs_folio_trace_end_copy, "end-copy") \ EM(netfs_folio_trace_filled_gaps, "filled-gaps") \ + EM(netfs_folio_trace_invalidate_all, "inval-all") \ + EM(netfs_folio_trace_invalidate_front, "inval-front") \ + EM(netfs_folio_trace_invalidate_middle, "inval-mid") \ + EM(netfs_folio_trace_invalidate_tail, "inval-tail") \ EM(netfs_folio_trace_kill, "kill") \ EM(netfs_folio_trace_kill_cc, "kill-cc") \EM (netfs_folio_trace_kill_g,"kill-g" ) \
David Howells
maintainerPATCH v2 4/7 netfs: Fix streaming write being overwritten
In reply to David HowellsIn order to avoid reading whilst writing, netfslib will allow "streaming writes" in which dirty data is stored directly into folios without reading them first. Such folios are marked dirty but may not be marked uptodate. If a folio is entirely written by a streaming write, uptodate will be set, otherwise it will have a netfs_folio struct attached to ->private recording the dirty region.
In the event that a partially written streaming write page is to be overwritten entirely by a single write(), netfs_perform_write() will try to copy over it, but doesn't discard the netfs_folio if it succeeds; further, it doesn't correctly handle a partial copy that overwrites some of the dirty data.
Fix this by the following:
(1) If the folio is successfully overwritten, free the netfs_folio struct before marking the page uptodate.
(2) If the copy to the folio partially fails, but short of the dirty data, just ignore the copy.
(3) If the copy partially fails and overwrites some of the dirty data, accept the copy, update the netfs_folio struct to record the new data. If the folio is now filled, free the netfs_folio and set uptodate, otherwise return a partial write.
Found with:
fsx -q -N 1000000 -p 10000 -o 128000 -l 600000 \ /xfstest.test/junk --replay-ops=junk.fsxops
using the following as junk.fsxops:
truncate 0x0 0 0x927c0 write 0x63fb8 0x53c8 0 copy_range 0xb704 0x19b9 0x24429 0x79380 write 0x2402b 0x144a2 0x90660 * write 0x204d5 0x140a0 0x927c0 * copy_range 0x1f72c 0x137d0 0x7a906 0x927c0 * read 0x00000 0x20000 0x9157c read 0x20000 0x20000 0x9157c read 0x40000 0x20000 0x9157c read 0x60000 0x20000 0x9157c read 0x7e1a0 0xcfb9 0x9157c
on cifs with the default cache option.
It shows folio 0x24 misbehaving if the FMODE_READ check is commented out in netfs_perform_write():
if (//(file->f_mode & FMODE_READ) ||
netfs_is_cache_enabled(ctx)) {and no fscache. This was initially found with the generic/522 xfstest.
--- fs/netfs/buffered_write.c | 48 ++++++++++++++++++++++++------------ include/trace/events/netfs.h | 1 + 2 files changed, 33 insertions(+), 16 deletions(-)
fs/netfs/buffered_write.c +32 -16
@@ -246,18 +246,34 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,/* See if we can write a whole folio in one go. */ if (!maybe_trouble && offset ==0 && part >= flen) { copied =copy_folio_from_iter_atomic (folio, offset, part, iter); -if (unlikely (copied ==0 )) +if (likely (copied == part)) { +if (finfo) + goto folio_now_filled; +__netfs_set_group (folio, netfs_group); +folio_mark_uptodate (folio); +trace_netfs_folio (folio, netfs_whole_folio_modify); + goto copied; + } +if (copied ==0 ) goto copy_failed; -if (unlikely (copied < part)) { +if (!finfo || copied <= finfo->dirty_offset) { maybe_trouble = true;iov_iter_revert (iter, copied); copied =0 ;folio_unlock (folio); goto retry; } -__netfs_set_group (folio, netfs_group); -folio_mark_uptodate (folio); -trace_netfs_folio (folio, netfs_whole_folio_modify); + +/* We overwrote some existing dirty data, so we have to +* accept the partial write. +*/ + finfo->dirty_len += finfo->dirty_offset; +if (finfo->dirty_len == flen) + goto folio_now_filled; +if (copied > finfo->dirty_len) + finfo->dirty_len = copied; + finfo->dirty_offset =0 ; +trace_netfs_folio (folio, netfs_whole_folio_modify_efault); goto copied; }
@@ -326,17 +342,9 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,if (unlikely (copied ==0 )) goto copy_failed; finfo->dirty_len += copied; -if (finfo->dirty_offset ==0 && finfo->dirty_len == flen) { -if (finfo->netfs_group) -folio_change_private (folio, finfo->netfs_group); -else -folio_detach_private (folio); -folio_mark_uptodate (folio); -kfree (finfo); -trace_netfs_folio (folio, netfs_streaming_cont_filled_page); - }else { -trace_netfs_folio (folio, netfs_streaming_write_cont); - } +if (finfo->dirty_offset ==0 && finfo->dirty_len == flen) + goto folio_now_filled; +trace_netfs_folio (folio, netfs_streaming_write_cont); goto copied; }
@@ -350,6 +358,14 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, goto out;continue ; + folio_now_filled: +if (finfo->netfs_group) +folio_change_private (folio, finfo->netfs_group); +else +folio_detach_private (folio); +folio_mark_uptodate (folio); +kfree (finfo); +trace_netfs_folio (folio, netfs_streaming_cont_filled_page); copied:flush_dcache_folio (folio);
include/trace/events/netfs.h +1 -0
@@ -177,6 +177,7 @@ EM(netfs_folio_is_uptodate, "mod-uptodate") \ EM(netfs_just_prefetch, "mod-prefetch") \ EM(netfs_whole_folio_modify, "mod-whole-f") \ + EM(netfs_whole_folio_modify_efault, "mod-whole-f!") \ EM(netfs_modify_and_clear, "mod-n-clear") \ EM(netfs_streaming_write, "mod-streamw") \EM (netfs_streaming_write_cont,"mod-streamw+" ) \
David Howells
maintainerPATCH v2 5/7 netfs: Fix read-gaps to remove netfs_folio from filled folio
In reply to David HowellsFix netfs_read_gaps() to remove the netfs_folio record from the folio record before marking the folio uptodate if it successfully fills the gaps around the dirty data in a streaming write folio (dirty, but not uptodate).
Found with:
fsx -q -N 1000000 -p 10000 -o 128000 -l 600000 \
/xfstest.test/junk --replay-ops=junk.fsxopsusing the following as junk.fsxops:
truncate 0x0 0x138b1 0x8b15d *
write 0x507ee 0x10df7 0x927c0
write 0x19993 0x10e04 0x927c0 *
mapwrite 0x66214 0x1a253 0x927c0
copy_range 0xb704 0x89b9 0x24429 0x79380
write 0x2402b 0x144a2 0x90660 *
mapwrite 0x204d5 0x140a0 0x927c0 *
copy_range 0x1f72c 0x137d0 0x7a906 0x927c0 *
read 0 0x9157c 0x9157con cifs with the default cache option.
It shows folio 0x24 misbehaving if the FMODE_READ check is commented out in netfs_perform_write():
if (//(file->f_mode & FMODE_READ) ||
netfs_is_cache_enabled(ctx)) {and no fscache. This was initially found with the generic/522 xfstest.
--- fs/netfs/buffered_read.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
fs/netfs/buffered_read.c +6 -3
@@ -397,6 +397,7 @@ static int netfs_read_gaps(struct file *file, struct folio *folio) {struct netfs_io_request *rreq;struct address_space *mapping = folio->mapping; +struct netfs_group *group =netfs_folio_group (folio);struct netfs_folio *finfo =netfs_folio_info (folio);struct netfs_inode *ctx =netfs_inode (mapping->host);struct folio *sink =NULL ;
@@ -463,6 +464,10 @@ static int netfs_read_gaps(struct file *file, struct folio *folio) ret =netfs_wait_for_read (rreq);if (ret >=0 ) { +folio_detach_private (folio); +netfs_put_group (group); +kfree (finfo); +trace_netfs_folio (folio, netfs_folio_trace_filled_gaps);flush_dcache_folio (folio);folio_mark_uptodate (folio); }
@@ -498,10 +503,8 @@ int netfs_read_folio(struct file *file, struct folio *folio)struct netfs_inode *ctx =netfs_inode (mapping->host);int ret; -if (folio_test_dirty (folio)) { -trace_netfs_folio (folio, netfs_folio_trace_read_gaps); +if (folio_test_dirty (folio))return netfs_read_gaps (file, folio); - }_enter ("%lx" , folio->index);
David Howells
maintainerPATCH v2 6/7 netfs: Fix zeropoint update where i_size > remote_i_size
In reply to David HowellsFix the update of the zero point[*] by netfs_release_folio() when there is uncommitted data in the pagecache beyond the folio being released but the on-server EOF is in this folio (ie. i_size > remote_i_size). The update needs to limit zero_point to remote_i_size, not i_size as i_size is a local phenomenon reflecting updates made locally to the pagecache, not stuff written to the server. remote_i_size tracks the server's i_size.
[*] The zero point is the file position from which we can assume that the
server will just return zeros, so we can avoid generating reads.
Note that netfs_invalidate_folio() probably doesn't need fixing as zero_point should be updated by setattr after truncation.
Found with:
fsx -q -N 1000000 -p 10000 -o 128000 -l 600000 \
/xfstest.test/junk --replay-ops=junk.fsxopsusing the following as junk.fsxops:
truncate 0x0 0x1bbae 0x82864
write 0x3ef2e 0xf9c8 0x1bbae
write 0x67e05 0xcb5a 0x4e8f6
mapread 0x57781 0x85b6 0x7495f
copy_range 0x5d3d 0x10329 0x54fac 0x7495f
write 0x64710 0x1c2b 0x7495f
mapread 0x64000 0x1000 0x7495fon cifs with the default cache option.
It shows read-gaps on folio 0x64 failing with a short read (ie. it hits EOF) if the FMODE_READ check is commented out in netfs_perform_write():
if (//(file->f_mode & FMODE_READ) ||
netfs_is_cache_enabled(ctx)) {and no fscache. This was initially found with the generic/522 xfstest.
--- fs/netfs/misc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/netfs/misc.c +1 -1
@@ -302,7 +302,7 @@ bool netfs_release_folio(struct folio *folio, gfp_t gfp)if (folio_test_dirty (folio))return false; - end =umin (folio_next_pos (folio),i_size_read (&ctx->inode)); + end =umin (folio_next_pos (folio), ctx->remote_i_size);if (end > ctx->zero_point) ctx->zero_point = end;
David Howells
maintainerPATCH v2 7/7 netfs: Fix write streaming disablement if fd open O_RDWR
In reply to David HowellsIn netfs_perform_write(), "write streaming" (the caching of dirty data in dirty but !uptodate folios) is performed to avoid the need to read data that is just going to get immediately overwritten. However, this is/will be disabled in three circumstances: if the fd is open O_RDWR, if fscache is in use (as we need to round out the blocks for DIO) or if content encryption is enabled (again for rounding out purposes).
The idea behind disabling it if the fd is open O_RDWR is that we'd need to flush the write-streaming page before we could read the data, particularly through mmap. But netfs now fills in the gaps if ->read_folio() is called on the page, so that is unnecessary. Further, this doesn't actually work if a separate fd is open for reading.
Fix this by removing the check for O_RDWR, thereby allowing streaming writes even when we might read.
This caused a number of problems with the generic/522 xfstest, but those are now fixed.
--- fs/netfs/buffered_write.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
fs/netfs/buffered_write.c +1 -2
@@ -283,8 +283,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,* a file that's open for reading as ->read_folio() then has to * be able to flush it. */ -if ((file->f_mode &FMODE_READ ) || -netfs_is_cache_enabled (ctx)) { +if (netfs_is_cache_enabled (ctx)) {if (finfo) {netfs_stat (&netfs_n_wh_wstream_conflict); goto flush_content;
Quoted 10 lines · David Howells
Here are some miscellaneous fixes for netfslib:
(1) Fix triggering of a
VM_BUG_ON_FOLIO()innetfs_write_begin().(2) Fix error handling in
netfs_extract_user_iter().(3) Fix
netfs_invalidate_folio()to clear the folio dirty bit if all dirty data removed.[...]
Applied to the vfs.fixes branch of the vfs/vfs.git tree. Patches in the vfs.fixes branch should appear in linux-next soon.
Please report any outstanding bugs that were missed during review in a new review to the original patch series allowing us to drop it.
It's encouraged to provide Acked-bys and Reviewed-bys even though the patch has now been applied. If possible patch trailers will be updated.
Note that commit hashes shown below are subject to change due to rebase, trailer updates or similar. If in doubt, please check the listed branch.
tree: https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git branch: vfs.fixes
[1/7] netfs: fix VM_BUG_ON_FOLIO() issue in netfs_write_begin() call
https://git.kernel.org/vfs/vfs/c/adcb8efb5429 [2/7] netfs: fix error handling in netfs_extract_user_iter() https://git.kernel.org/vfs/vfs/c/8ef352bc015d [3/7] netfs: Fix netfs_invalidate_folio() to clear dirty bit if all changes gone https://git.kernel.org/vfs/vfs/c/920532e4996c [4/7] netfs: Fix streaming write being overwritten https://git.kernel.org/vfs/vfs/c/1bfeb53e990b [5/7] netfs: Fix read-gaps to remove netfs_folio from filled folio https://git.kernel.org/vfs/vfs/c/d259cfb7dd32 [6/7] netfs: Fix zeropoint update where i_size > remote_i_size https://git.kernel.org/vfs/vfs/c/bb3dd1ea2628 [7/7] netfs: Fix write streaming disablement if fd open O_RDWR https://git.kernel.org/vfs/vfs/c/eac38f8a2b85