]> www.infradead.org Git - nvme.git/commitdiff
fs/nfs/io: make nfs_start_io_*() killable
authorMax Kellermann <max.kellermann@ionos.com>
Thu, 21 Nov 2024 13:53:51 +0000 (14:53 +0100)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Thu, 28 Nov 2024 17:55:33 +0000 (12:55 -0500)
This allows killing processes that wait for a lock when one process is
stuck waiting for the NFS server.  This aims to complete the coverage
of NFS operations being killable, like nfs_direct_wait() does, for
example.

Signed-off-by: Max Kellermann <max.kellermann@ionos.com>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
fs/nfs/direct.c
fs/nfs/file.c
fs/nfs/internal.h
fs/nfs/io.c

index 90079ca134dd3cbf6591c6a6e3b98c0e3a9bf5c7..b08dbe96bc579603f03839023bd8f7a1abffac09 100644 (file)
@@ -454,8 +454,16 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, struct iov_iter *iter,
        if (user_backed_iter(iter))
                dreq->flags = NFS_ODIRECT_SHOULD_DIRTY;
 
-       if (!swap)
-               nfs_start_io_direct(inode);
+       if (!swap) {
+               result = nfs_start_io_direct(inode);
+               if (result) {
+                       /* release the reference that would usually be
+                        * consumed by nfs_direct_read_schedule_iovec()
+                        */
+                       nfs_direct_req_release(dreq);
+                       goto out_release;
+               }
+       }
 
        NFS_I(inode)->read_io += count;
        requested = nfs_direct_read_schedule_iovec(dreq, iter, iocb->ki_pos);
@@ -1007,7 +1015,14 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, struct iov_iter *iter,
                requested = nfs_direct_write_schedule_iovec(dreq, iter, pos,
                                                            FLUSH_STABLE);
        } else {
-               nfs_start_io_direct(inode);
+               result = nfs_start_io_direct(inode);
+               if (result) {
+                       /* release the reference that would usually be
+                        * consumed by nfs_direct_write_schedule_iovec()
+                        */
+                       nfs_direct_req_release(dreq);
+                       goto out_release;
+               }
 
                requested = nfs_direct_write_schedule_iovec(dreq, iter, pos,
                                                            FLUSH_COND_STABLE);
index 6800ee92d742a86c55363701f72a966a7a83a035..1bb646752e4663ee79dc94c0b1f8231bf77ca598 100644 (file)
@@ -166,7 +166,10 @@ nfs_file_read(struct kiocb *iocb, struct iov_iter *to)
                iocb->ki_filp,
                iov_iter_count(to), (unsigned long) iocb->ki_pos);
 
-       nfs_start_io_read(inode);
+       result = nfs_start_io_read(inode);
+       if (result)
+               return result;
+
        result = nfs_revalidate_mapping(inode, iocb->ki_filp->f_mapping);
        if (!result) {
                result = generic_file_read_iter(iocb, to);
@@ -187,7 +190,10 @@ nfs_file_splice_read(struct file *in, loff_t *ppos, struct pipe_inode_info *pipe
 
        dprintk("NFS: splice_read(%pD2, %zu@%llu)\n", in, len, *ppos);
 
-       nfs_start_io_read(inode);
+       result = nfs_start_io_read(inode);
+       if (result)
+               return result;
+
        result = nfs_revalidate_mapping(inode, in->f_mapping);
        if (!result) {
                result = filemap_splice_read(in, ppos, pipe, len, flags);
@@ -668,7 +674,9 @@ ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)
        nfs_clear_invalid_mapping(file->f_mapping);
 
        since = filemap_sample_wb_err(file->f_mapping);
-       nfs_start_io_write(inode);
+       error = nfs_start_io_write(inode);
+       if (error)
+               return error;
        result = generic_write_checks(iocb, from);
        if (result > 0)
                result = generic_perform_write(iocb, from);
index 6bcc4b0e00ab72a04f70bb3ae72cf648ebba4b43..e564bd11ba607c5b954d91506bca230a619c0647 100644 (file)
@@ -6,6 +6,7 @@
 #include "nfs4_fs.h"
 #include <linux/fs_context.h>
 #include <linux/security.h>
+#include <linux/compiler_attributes.h>
 #include <linux/crc32.h>
 #include <linux/sunrpc/addr.h>
 #include <linux/nfs_page.h>
@@ -516,11 +517,11 @@ extern const struct netfs_request_ops nfs_netfs_ops;
 #endif
 
 /* io.c */
-extern void nfs_start_io_read(struct inode *inode);
+extern __must_check int nfs_start_io_read(struct inode *inode);
 extern void nfs_end_io_read(struct inode *inode);
-extern void nfs_start_io_write(struct inode *inode);
+extern  __must_check int nfs_start_io_write(struct inode *inode);
 extern void nfs_end_io_write(struct inode *inode);
-extern void nfs_start_io_direct(struct inode *inode);
+extern __must_check int nfs_start_io_direct(struct inode *inode);
 extern void nfs_end_io_direct(struct inode *inode);
 
 static inline bool nfs_file_io_is_buffered(struct nfs_inode *nfsi)
index b5551ed8f648bce3a73d62c423965bff3cc48426..3388faf2acb9f59c20feb5bfc3f01023e9c18147 100644 (file)
@@ -39,19 +39,28 @@ static void nfs_block_o_direct(struct nfs_inode *nfsi, struct inode *inode)
  * Note that buffered writes and truncates both take a write lock on
  * inode->i_rwsem, meaning that those are serialised w.r.t. the reads.
  */
-void
+int
 nfs_start_io_read(struct inode *inode)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
+       int err;
+
        /* Be an optimist! */
-       down_read(&inode->i_rwsem);
+       err = down_read_killable(&inode->i_rwsem);
+       if (err)
+               return err;
        if (test_bit(NFS_INO_ODIRECT, &nfsi->flags) == 0)
-               return;
+               return 0;
        up_read(&inode->i_rwsem);
+
        /* Slow path.... */
-       down_write(&inode->i_rwsem);
+       err = down_write_killable(&inode->i_rwsem);
+       if (err)
+               return err;
        nfs_block_o_direct(nfsi, inode);
        downgrade_write(&inode->i_rwsem);
+
+       return 0;
 }
 
 /**
@@ -74,11 +83,15 @@ nfs_end_io_read(struct inode *inode)
  * Declare that a buffered read operation is about to start, and ensure
  * that we block all direct I/O.
  */
-void
+int
 nfs_start_io_write(struct inode *inode)
 {
-       down_write(&inode->i_rwsem);
-       nfs_block_o_direct(NFS_I(inode), inode);
+       int err;
+
+       err = down_write_killable(&inode->i_rwsem);
+       if (!err)
+               nfs_block_o_direct(NFS_I(inode), inode);
+       return err;
 }
 
 /**
@@ -119,19 +132,28 @@ static void nfs_block_buffered(struct nfs_inode *nfsi, struct inode *inode)
  * Note that buffered writes and truncates both take a write lock on
  * inode->i_rwsem, meaning that those are serialised w.r.t. O_DIRECT.
  */
-void
+int
 nfs_start_io_direct(struct inode *inode)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
+       int err;
+
        /* Be an optimist! */
-       down_read(&inode->i_rwsem);
+       err = down_read_killable(&inode->i_rwsem);
+       if (err)
+               return err;
        if (test_bit(NFS_INO_ODIRECT, &nfsi->flags) != 0)
-               return;
+               return 0;
        up_read(&inode->i_rwsem);
+
        /* Slow path.... */
-       down_write(&inode->i_rwsem);
+       err = down_write_killable(&inode->i_rwsem);
+       if (err)
+               return err;
        nfs_block_buffered(nfsi, inode);
        downgrade_write(&inode->i_rwsem);
+
+       return 0;
 }
 
 /**