Commit 38a125b3 authored by Max Kellermann's avatar Max Kellermann Committed by Trond Myklebust
Browse files

fs/nfs/io: make nfs_start_io_*() killable



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: default avatarMax Kellermann <max.kellermann@ionos.com>
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@hammerspace.com>
parent 614733f9
Loading
Loading
Loading
Loading
+18 −3
Original line number Diff line number Diff line
@@ -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);
+11 −3
Original line number Diff line number Diff line
@@ -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);
+4 −3
Original line number Diff line number Diff line
@@ -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)
+33 −11
Original line number Diff line number Diff line
@@ -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);
	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;
}

/**