netfs: Fix io_uring based write-through
authorDavid Howells <dhowells@redhat.com>
Tue, 21 May 2024 13:37:43 +0000 (14:37 +0100)
committerChristian Brauner <brauner@kernel.org>
Fri, 24 May 2024 11:34:06 +0000 (13:34 +0200)
This can be triggered by mounting a cifs filesystem with a cache=strict
mount option and then, using the fsx program from xfstests, doing:

        ltp/fsx -A -d -N 1000 -S 11463 -P /tmp /cifs-mount/foo \
          --replay-ops=gen112-fsxops

Where gen112-fsxops holds:

        fallocate 0x6be7 0x8fc5 0x377d3
        copy_range 0x9c71 0x77e8 0x2edaf 0x377d3
        write 0x2776d 0x8f65 0x377d3

The problem is that netfs_io_request::len is being used for two purposes
and ends up getting set to the amount of data we transferred, not the
amount of data the caller asked to be transferred (for various reasons,
such as mmap'd writes, we might end up rounding out the data written to the
server to include the entire folio at each end).

Fix this by keeping the amount we were asked to write in ->len and using
->submitted to track what we issued ops for.  Then, when we come to calling
->ki_complete(), ->len is the right size.

This also required netfs_cleanup_dio_write() to change since we're no
longer advancing wreq->len.  Use wreq->transferred instead as we might have
done a short read.

With this, the generic/112 xfstest passes if cifs is forced to put all
non-DIO opens into write-through mode.

Fixes: 288ace2f57c9 ("netfs: New writeback implementation")
Signed-off-by: David Howells <dhowells@redhat.com>
Link: https://lore.kernel.org/r/295086.1716298663@warthog.procyon.org.uk
cc: Jeff Layton <jlayton@kernel.org>
cc: Steve French <stfrench@microsoft.com>
cc: Enzo Matsumiya <ematsumiya@suse.de>
cc: netfs@lists.linux.dev
cc: v9fs@lists.linux.dev
cc: linux-afs@lists.infradead.org
cc: linux-cifs@vger.kernel.org
cc: linux-fsdevel@vger.kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/netfs/direct_write.c
fs/netfs/write_collect.c
fs/netfs/write_issue.c

index 608ba6416919a8f95fd06645980964ba40bd4308..28163516bf037f3597b4c5708c83bedc933db90e 100644 (file)
@@ -12,7 +12,7 @@
 static void netfs_cleanup_dio_write(struct netfs_io_request *wreq)
 {
        struct inode *inode = wreq->inode;
-       unsigned long long end = wreq->start + wreq->len;
+       unsigned long long end = wreq->start + wreq->transferred;
 
        if (!wreq->error &&
            i_size_read(inode) < end) {
index 60112e4b2c5eb70e350a873feb2cc397517534a7..426cf87aaf2ecd97f82ea43eabb326b2c83b8080 100644 (file)
@@ -510,7 +510,7 @@ reassess_streams:
         * stream has a gap that can be jumped.
         */
        if (notes & SOME_EMPTY) {
-               unsigned long long jump_to = wreq->start + wreq->len;
+               unsigned long long jump_to = wreq->start + READ_ONCE(wreq->submitted);
 
                for (s = 0; s < NR_IO_STREAMS; s++) {
                        stream = &wreq->io_streams[s];
@@ -690,10 +690,11 @@ void netfs_write_collection_worker(struct work_struct *work)
        wake_up_bit(&wreq->flags, NETFS_RREQ_IN_PROGRESS);
 
        if (wreq->iocb) {
-               wreq->iocb->ki_pos += wreq->transferred;
+               size_t written = min(wreq->transferred, wreq->len);
+               wreq->iocb->ki_pos += written;
                if (wreq->iocb->ki_complete)
                        wreq->iocb->ki_complete(
-                               wreq->iocb, wreq->error ? wreq->error : wreq->transferred);
+                               wreq->iocb, wreq->error ? wreq->error : written);
                wreq->iocb = VFS_PTR_POISON;
        }
 
index e190043bc0daa724f749003a2a80e151e02806d3..86dad7e4202b5cf5db849d059ee3989f6bbd88d6 100644 (file)
@@ -254,7 +254,7 @@ static void netfs_issue_write(struct netfs_io_request *wreq,
        stream->construct = NULL;
 
        if (subreq->start + subreq->len > wreq->start + wreq->submitted)
-               wreq->len = wreq->submitted = subreq->start + subreq->len - wreq->start;
+               WRITE_ONCE(wreq->submitted, subreq->start + subreq->len - wreq->start);
        netfs_do_issue_write(stream, subreq);
 }