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);
                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);
 
                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);
 
        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);
        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);
 
 #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>
 #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)
 
  * 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;
 }
 
 /**
  * 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;
 }
 
 /**
  * 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;
 }
 
 /**