Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 423eaf8f authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* git://git.linux-nfs.org/pub/linux/nfs-2.6:
  NFS: Clean up new multi-segment direct I/O changes
  NFS: Ensure we return zero if applications attempt to write zero bytes
  NFS: Support multiple segment iovecs in the NFS direct I/O path
  NFS: Introduce iovec I/O helpers to fs/nfs/direct.c
  SUNRPC: Add missing "space" to net/sunrpc/auth_gss.c
  SUNRPC: make sunrpc/xprtsock.c:xs_setup_{udp,tcp}() static
  NFS: fs/nfs/dir.c should #include "internal.h"
  NFS: make nfs_wb_page_priority() static
  NFS: mount failure causes bad page state
  SUNRPC: remove NFS/RDMA client's binary sysctls
  kernel BUG at fs/nfs/namespace.c:108! - can be triggered by bad server
  sunrpc: rpc_pipe_poll may miss available data in some cases
  sunrpc: return error if unsupported enctype or cksumtype is encountered
  sunrpc: gss_pipe_downcall(), don't assume all errors are transient
  NFS: Fix the ustat() regression
parents 0685ab4f 02fe4946
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -38,6 +38,7 @@
#include "nfs4_fs.h"
#include "nfs4_fs.h"
#include "delegation.h"
#include "delegation.h"
#include "iostat.h"
#include "iostat.h"
#include "internal.h"


/* #define NFS_DEBUG_VERBOSE 1 */
/* #define NFS_DEBUG_VERBOSE 1 */


+99 −43
Original line number Original line Diff line number Diff line
@@ -263,17 +263,19 @@ static const struct rpc_call_ops nfs_read_direct_ops = {
 * handled automatically by nfs_direct_read_result().  Otherwise, if
 * handled automatically by nfs_direct_read_result().  Otherwise, if
 * no requests have been sent, just return an error.
 * no requests have been sent, just return an error.
 */
 */
static ssize_t nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos)
static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq,
						const struct iovec *iov,
						loff_t pos)
{
{
	struct nfs_open_context *ctx = dreq->ctx;
	struct nfs_open_context *ctx = dreq->ctx;
	struct inode *inode = ctx->path.dentry->d_inode;
	struct inode *inode = ctx->path.dentry->d_inode;
	unsigned long user_addr = (unsigned long)iov->iov_base;
	size_t count = iov->iov_len;
	size_t rsize = NFS_SERVER(inode)->rsize;
	size_t rsize = NFS_SERVER(inode)->rsize;
	unsigned int pgbase;
	unsigned int pgbase;
	int result;
	int result;
	ssize_t started = 0;
	ssize_t started = 0;


	get_dreq(dreq);

	do {
	do {
		struct nfs_read_data *data;
		struct nfs_read_data *data;
		size_t bytes;
		size_t bytes;
@@ -347,15 +349,46 @@ static ssize_t nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned lo
		count -= bytes;
		count -= bytes;
	} while (count != 0);
	} while (count != 0);


	if (started)
		return started;
	return result < 0 ? (ssize_t) result : -EFAULT;
}

static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
					      const struct iovec *iov,
					      unsigned long nr_segs,
					      loff_t pos)
{
	ssize_t result = -EINVAL;
	size_t requested_bytes = 0;
	unsigned long seg;

	get_dreq(dreq);

	for (seg = 0; seg < nr_segs; seg++) {
		const struct iovec *vec = &iov[seg];
		result = nfs_direct_read_schedule_segment(dreq, vec, pos);
		if (result < 0)
			break;
		requested_bytes += result;
		if ((size_t)result < vec->iov_len)
			break;
		pos += vec->iov_len;
	}

	if (put_dreq(dreq))
	if (put_dreq(dreq))
		nfs_direct_complete(dreq);
		nfs_direct_complete(dreq);


	if (started)
	if (requested_bytes != 0)
		return 0;
		return 0;
	return result < 0 ? (ssize_t) result : -EFAULT;

	if (result < 0)
		return result;
	return -EIO;
}
}


static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos)
static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov,
			       unsigned long nr_segs, loff_t pos)
{
{
	ssize_t result = 0;
	ssize_t result = 0;
	sigset_t oldset;
	sigset_t oldset;
@@ -372,9 +405,8 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size
	if (!is_sync_kiocb(iocb))
	if (!is_sync_kiocb(iocb))
		dreq->iocb = iocb;
		dreq->iocb = iocb;


	nfs_add_stats(inode, NFSIOS_DIRECTREADBYTES, count);
	rpc_clnt_sigmask(clnt, &oldset);
	rpc_clnt_sigmask(clnt, &oldset);
	result = nfs_direct_read_schedule(dreq, user_addr, count, pos);
	result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos);
	if (!result)
	if (!result)
		result = nfs_direct_wait(dreq);
		result = nfs_direct_wait(dreq);
	rpc_clnt_sigunmask(clnt, &oldset);
	rpc_clnt_sigunmask(clnt, &oldset);
@@ -601,17 +633,19 @@ static const struct rpc_call_ops nfs_write_direct_ops = {
 * handled automatically by nfs_direct_write_result().  Otherwise, if
 * handled automatically by nfs_direct_write_result().  Otherwise, if
 * no requests have been sent, just return an error.
 * no requests have been sent, just return an error.
 */
 */
static ssize_t nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos, int sync)
static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
						 const struct iovec *iov,
						 loff_t pos, int sync)
{
{
	struct nfs_open_context *ctx = dreq->ctx;
	struct nfs_open_context *ctx = dreq->ctx;
	struct inode *inode = ctx->path.dentry->d_inode;
	struct inode *inode = ctx->path.dentry->d_inode;
	unsigned long user_addr = (unsigned long)iov->iov_base;
	size_t count = iov->iov_len;
	size_t wsize = NFS_SERVER(inode)->wsize;
	size_t wsize = NFS_SERVER(inode)->wsize;
	unsigned int pgbase;
	unsigned int pgbase;
	int result;
	int result;
	ssize_t started = 0;
	ssize_t started = 0;


	get_dreq(dreq);

	do {
	do {
		struct nfs_write_data *data;
		struct nfs_write_data *data;
		size_t bytes;
		size_t bytes;
@@ -689,15 +723,48 @@ static ssize_t nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned l
		count -= bytes;
		count -= bytes;
	} while (count != 0);
	} while (count != 0);


	if (started)
		return started;
	return result < 0 ? (ssize_t) result : -EFAULT;
}

static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
					       const struct iovec *iov,
					       unsigned long nr_segs,
					       loff_t pos, int sync)
{
	ssize_t result = 0;
	size_t requested_bytes = 0;
	unsigned long seg;

	get_dreq(dreq);

	for (seg = 0; seg < nr_segs; seg++) {
		const struct iovec *vec = &iov[seg];
		result = nfs_direct_write_schedule_segment(dreq, vec,
							   pos, sync);
		if (result < 0)
			break;
		requested_bytes += result;
		if ((size_t)result < vec->iov_len)
			break;
		pos += vec->iov_len;
	}

	if (put_dreq(dreq))
	if (put_dreq(dreq))
		nfs_direct_write_complete(dreq, inode);
		nfs_direct_write_complete(dreq, dreq->inode);


	if (started)
	if (requested_bytes != 0)
		return 0;
		return 0;
	return result < 0 ? (ssize_t) result : -EFAULT;

	if (result < 0)
		return result;
	return -EIO;
}
}


static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos)
static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov,
				unsigned long nr_segs, loff_t pos,
				size_t count)
{
{
	ssize_t result = 0;
	ssize_t result = 0;
	sigset_t oldset;
	sigset_t oldset;
@@ -720,10 +787,8 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, siz
	if (!is_sync_kiocb(iocb))
	if (!is_sync_kiocb(iocb))
		dreq->iocb = iocb;
		dreq->iocb = iocb;


	nfs_add_stats(inode, NFSIOS_DIRECTWRITTENBYTES, count);

	rpc_clnt_sigmask(clnt, &oldset);
	rpc_clnt_sigmask(clnt, &oldset);
	result = nfs_direct_write_schedule(dreq, user_addr, count, pos, sync);
	result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos, sync);
	if (!result)
	if (!result)
		result = nfs_direct_wait(dreq);
		result = nfs_direct_wait(dreq);
	rpc_clnt_sigunmask(clnt, &oldset);
	rpc_clnt_sigunmask(clnt, &oldset);
@@ -759,21 +824,16 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov,
	ssize_t retval = -EINVAL;
	ssize_t retval = -EINVAL;
	struct file *file = iocb->ki_filp;
	struct file *file = iocb->ki_filp;
	struct address_space *mapping = file->f_mapping;
	struct address_space *mapping = file->f_mapping;
	/* XXX: temporary */
	size_t count;
	const char __user *buf = iov[0].iov_base;

	size_t count = iov[0].iov_len;
	count = iov_length(iov, nr_segs);
	nfs_add_stats(mapping->host, NFSIOS_DIRECTREADBYTES, count);


	dprintk("nfs: direct read(%s/%s, %lu@%Ld)\n",
	dprintk("nfs: direct read(%s/%s, %zd@%Ld)\n",
		file->f_path.dentry->d_parent->d_name.name,
		file->f_path.dentry->d_parent->d_name.name,
		file->f_path.dentry->d_name.name,
		file->f_path.dentry->d_name.name,
		(unsigned long) count, (long long) pos);
		count, (long long) pos);


	if (nr_segs != 1)
		goto out;

	retval = -EFAULT;
	if (!access_ok(VERIFY_WRITE, buf, count))
		goto out;
	retval = 0;
	retval = 0;
	if (!count)
	if (!count)
		goto out;
		goto out;
@@ -782,7 +842,7 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov,
	if (retval)
	if (retval)
		goto out;
		goto out;


	retval = nfs_direct_read(iocb, (unsigned long) buf, count, pos);
	retval = nfs_direct_read(iocb, iov, nr_segs, pos);
	if (retval > 0)
	if (retval > 0)
		iocb->ki_pos = pos + retval;
		iocb->ki_pos = pos + retval;


@@ -821,21 +881,21 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
	ssize_t retval = -EINVAL;
	ssize_t retval = -EINVAL;
	struct file *file = iocb->ki_filp;
	struct file *file = iocb->ki_filp;
	struct address_space *mapping = file->f_mapping;
	struct address_space *mapping = file->f_mapping;
	/* XXX: temporary */
	size_t count;
	const char __user *buf = iov[0].iov_base;
	size_t count = iov[0].iov_len;


	dprintk("nfs: direct write(%s/%s, %lu@%Ld)\n",
	count = iov_length(iov, nr_segs);
	nfs_add_stats(mapping->host, NFSIOS_DIRECTWRITTENBYTES, count);

	dfprintk(VFS, "nfs: direct write(%s/%s, %zd@%Ld)\n",
		file->f_path.dentry->d_parent->d_name.name,
		file->f_path.dentry->d_parent->d_name.name,
		file->f_path.dentry->d_name.name,
		file->f_path.dentry->d_name.name,
		(unsigned long) count, (long long) pos);
		count, (long long) pos);

	if (nr_segs != 1)
		goto out;


	retval = generic_write_checks(file, &pos, &count, 0);
	retval = generic_write_checks(file, &pos, &count, 0);
	if (retval)
	if (retval)
		goto out;
		goto out;
	if (!count)
		goto out;	/* return 0 */


	retval = -EINVAL;
	retval = -EINVAL;
	if ((ssize_t) count < 0)
	if ((ssize_t) count < 0)
@@ -844,15 +904,11 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
	if (!count)
	if (!count)
		goto out;
		goto out;


	retval = -EFAULT;
	if (!access_ok(VERIFY_READ, buf, count))
		goto out;

	retval = nfs_sync_mapping(mapping);
	retval = nfs_sync_mapping(mapping);
	if (retval)
	if (retval)
		goto out;
		goto out;


	retval = nfs_direct_write(iocb, (unsigned long) buf, count, pos);
	retval = nfs_direct_write(iocb, iov, nr_segs, pos, count);


	if (retval > 0)
	if (retval > 0)
		iocb->ki_pos = pos + retval;
		iocb->ki_pos = pos + retval;
+27 −54
Original line number Original line Diff line number Diff line
@@ -42,6 +42,25 @@


#define NFSDBG_FACILITY		NFSDBG_CLIENT
#define NFSDBG_FACILITY		NFSDBG_CLIENT


/*
 * Set the superblock root dentry.
 * Note that this function frees the inode in case of error.
 */
static int nfs_superblock_set_dummy_root(struct super_block *sb, struct inode *inode)
{
	/* The mntroot acts as the dummy root dentry for this superblock */
	if (sb->s_root == NULL) {
		sb->s_root = d_alloc_root(inode);
		if (sb->s_root == NULL) {
			iput(inode);
			return -ENOMEM;
		}
		/* Circumvent igrab(): we know the inode is not being freed */
		atomic_inc(&inode->i_count);
	}
	return 0;
}

/*
/*
 * get an NFS2/NFS3 root dentry from the root filehandle
 * get an NFS2/NFS3 root dentry from the root filehandle
 */
 */
@@ -54,33 +73,6 @@ struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh)
	struct inode *inode;
	struct inode *inode;
	int error;
	int error;


	/* create a dummy root dentry with dummy inode for this superblock */
	if (!sb->s_root) {
		struct nfs_fh dummyfh;
		struct dentry *root;
		struct inode *iroot;

		memset(&dummyfh, 0, sizeof(dummyfh));
		memset(&fattr, 0, sizeof(fattr));
		nfs_fattr_init(&fattr);
		fattr.valid = NFS_ATTR_FATTR;
		fattr.type = NFDIR;
		fattr.mode = S_IFDIR | S_IRUSR | S_IWUSR;
		fattr.nlink = 2;

		iroot = nfs_fhget(sb, &dummyfh, &fattr);
		if (IS_ERR(iroot))
			return ERR_PTR(PTR_ERR(iroot));

		root = d_alloc_root(iroot);
		if (!root) {
			iput(iroot);
			return ERR_PTR(-ENOMEM);
		}

		sb->s_root = root;
	}

	/* get the actual root for this mount */
	/* get the actual root for this mount */
	fsinfo.fattr = &fattr;
	fsinfo.fattr = &fattr;


@@ -96,6 +88,10 @@ struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh)
		return ERR_PTR(PTR_ERR(inode));
		return ERR_PTR(PTR_ERR(inode));
	}
	}


	error = nfs_superblock_set_dummy_root(sb, inode);
	if (error != 0)
		return ERR_PTR(error);

	/* root dentries normally start off anonymous and get spliced in later
	/* root dentries normally start off anonymous and get spliced in later
	 * if the dentry tree reaches them; however if the dentry already
	 * if the dentry tree reaches them; however if the dentry already
	 * exists, we'll pick it up at this point and use it as the root
	 * exists, we'll pick it up at this point and use it as the root
@@ -241,33 +237,6 @@ struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh)


	dprintk("--> nfs4_get_root()\n");
	dprintk("--> nfs4_get_root()\n");


	/* create a dummy root dentry with dummy inode for this superblock */
	if (!sb->s_root) {
		struct nfs_fh dummyfh;
		struct dentry *root;
		struct inode *iroot;

		memset(&dummyfh, 0, sizeof(dummyfh));
		memset(&fattr, 0, sizeof(fattr));
		nfs_fattr_init(&fattr);
		fattr.valid = NFS_ATTR_FATTR;
		fattr.type = NFDIR;
		fattr.mode = S_IFDIR | S_IRUSR | S_IWUSR;
		fattr.nlink = 2;

		iroot = nfs_fhget(sb, &dummyfh, &fattr);
		if (IS_ERR(iroot))
			return ERR_PTR(PTR_ERR(iroot));

		root = d_alloc_root(iroot);
		if (!root) {
			iput(iroot);
			return ERR_PTR(-ENOMEM);
		}

		sb->s_root = root;
	}

	/* get the info about the server and filesystem */
	/* get the info about the server and filesystem */
	error = nfs4_server_capabilities(server, mntfh);
	error = nfs4_server_capabilities(server, mntfh);
	if (error < 0) {
	if (error < 0) {
@@ -289,6 +258,10 @@ struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh)
		return ERR_PTR(PTR_ERR(inode));
		return ERR_PTR(PTR_ERR(inode));
	}
	}


	error = nfs_superblock_set_dummy_root(sb, inode);
	if (error != 0)
		return ERR_PTR(error);

	/* root dentries normally start off anonymous and get spliced in later
	/* root dentries normally start off anonymous and get spliced in later
	 * if the dentry tree reaches them; however if the dentry already
	 * if the dentry tree reaches them; however if the dentry already
	 * exists, we'll pick it up at this point and use it as the root
	 * exists, we'll pick it up at this point and use it as the root
+9 −2
Original line number Original line Diff line number Diff line
@@ -1054,10 +1054,11 @@ static int nfs_validate_mount_data(void *options,
{
{
	struct nfs_mount_data *data = (struct nfs_mount_data *)options;
	struct nfs_mount_data *data = (struct nfs_mount_data *)options;


	memset(args, 0, sizeof(*args));

	if (data == NULL)
	if (data == NULL)
		goto out_no_data;
		goto out_no_data;


	memset(args, 0, sizeof(*args));
	args->flags		= (NFS_MOUNT_VER3 | NFS_MOUNT_TCP);
	args->flags		= (NFS_MOUNT_VER3 | NFS_MOUNT_TCP);
	args->rsize		= NFS_MAX_FILE_IO_SIZE;
	args->rsize		= NFS_MAX_FILE_IO_SIZE;
	args->wsize		= NFS_MAX_FILE_IO_SIZE;
	args->wsize		= NFS_MAX_FILE_IO_SIZE;
@@ -1474,6 +1475,11 @@ static int nfs_xdev_get_sb(struct file_system_type *fs_type, int flags,
		error = PTR_ERR(mntroot);
		error = PTR_ERR(mntroot);
		goto error_splat_super;
		goto error_splat_super;
	}
	}
	if (mntroot->d_inode->i_op != &nfs_dir_inode_operations) {
		dput(mntroot);
		error = -ESTALE;
		goto error_splat_super;
	}


	s->s_flags |= MS_ACTIVE;
	s->s_flags |= MS_ACTIVE;
	mnt->mnt_sb = s;
	mnt->mnt_sb = s;
@@ -1531,10 +1537,11 @@ static int nfs4_validate_mount_data(void *options,
	struct nfs4_mount_data *data = (struct nfs4_mount_data *)options;
	struct nfs4_mount_data *data = (struct nfs4_mount_data *)options;
	char *c;
	char *c;


	memset(args, 0, sizeof(*args));

	if (data == NULL)
	if (data == NULL)
		goto out_no_data;
		goto out_no_data;


	memset(args, 0, sizeof(*args));
	args->rsize		= NFS_MAX_FILE_IO_SIZE;
	args->rsize		= NFS_MAX_FILE_IO_SIZE;
	args->wsize		= NFS_MAX_FILE_IO_SIZE;
	args->wsize		= NFS_MAX_FILE_IO_SIZE;
	args->timeo		= 600;
	args->timeo		= 600;
+2 −1
Original line number Original line Diff line number Diff line
@@ -1436,7 +1436,8 @@ int nfs_wb_page_cancel(struct inode *inode, struct page *page)
	return ret;
	return ret;
}
}


int nfs_wb_page_priority(struct inode *inode, struct page *page, int how)
static int nfs_wb_page_priority(struct inode *inode, struct page *page,
				int how)
{
{
	loff_t range_start = page_offset(page);
	loff_t range_start = page_offset(page);
	loff_t range_end = range_start + (loff_t)(PAGE_CACHE_SIZE - 1);
	loff_t range_end = range_start + (loff_t)(PAGE_CACHE_SIZE - 1);
Loading