Commit f43574d0 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Puyll NFS client updates from Anna Schumaker:
 "Stable bugfixes:
   - Fix memory leaks and corruption in readdir # v2.6.37+
   - Directory page cache needs to be locked when read # v2.6.37+

  New features:
   - Convert NFS to use the new mount API
   - Add "softreval" mount option to let clients use cache if server goes down
   - Add a config option to compile without UDP support
   - Limit the number of inactive delegations the client can cache at once
   - Improved readdir concurrency using iterate_shared()

  Other bugfixes and cleanups:
   - More 64-bit time conversions
   - Add additional diagnostic tracepoints
   - Check for holes in swapfiles, and add dependency on CONFIG_SWAP
   - Various xprtrdma cleanups to prepare for 5.7's changes
   - Several fixes for NFS writeback and commit handling
   - Fix acls over krb5i/krb5p mounts
   - Recover from premature loss of openstateids
   - Fix NFS v3 chacl and chmod bug
   - Compare creds using cred_fscmp()
   - Use kmemdup_nul() in more places
   - Optimize readdir cache page invalidation
   - Lease renewal and recovery fixes"

* tag 'nfs-for-5.6-1' of git://git.linux-nfs.org/projects/anna/linux-nfs: (93 commits)
  NFSv4.0: nfs4_do_fsinfo() should not do implicit lease renewals
  NFSv4: try lease recovery on NFS4ERR_EXPIRED
  NFS: Fix memory leaks
  nfs: optimise readdir cache page invalidation
  NFS: Switch readdir to using iterate_shared()
  NFS: Use kmemdup_nul() in nfs_readdir_make_qstr()
  NFS: Directory page cache pages need to be locked when read
  NFS: Fix memory leaks and corruption in readdir
  SUNRPC: Use kmemdup_nul() in rpc_parse_scope_id()
  NFS: Replace various occurrences of kstrndup() with kmemdup_nul()
  NFSv4: Limit the total number of cached delegations
  NFSv4: Add accounting for the number of active delegations held
  NFSv4: Try to return the delegation immediately when marked for return on close
  NFS: Clear NFS_DELEGATION_RETURN_IF_CLOSED when the delegation is returned
  NFSv4: nfs_inode_evict_delegation() should set NFS_DELEGATION_RETURNING
  NFS: nfs_find_open_context() should use cred_fscmp()
  NFS: nfs_access_get_cached_rcu() should use cred_fscmp()
  NFSv4: pnfs_roc() must use cred_fscmp() to compare creds
  NFS: remove unused macros
  nfs: Return EINVAL rather than ERANGE for mount parse errors
  ...
parents 41dcd67e 7dc2993a
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -90,7 +90,7 @@ config NFS_V4
config NFS_SWAP
	bool "Provide swap over NFS support"
	default n
	depends on NFS_FS
	depends on NFS_FS && SWAP
	select SUNRPC_SWAP
	help
	  This option enables swapon to work on files located on NFS mounts.
@@ -196,3 +196,12 @@ config NFS_DEBUG
	depends on NFS_FS && SUNRPC_DEBUG
	select CRC32
	default y

config NFS_DISABLE_UDP_SUPPORT
       bool "NFS: Disable NFS UDP protocol support"
       depends on NFS_FS
       default y
       help
	 Choose Y here to disable the use of NFS over UDP. NFS over UDP
	 on modern networks (1Gb+) can lead to data corruption caused by
	 fragmentation during high loads.
+1 −1
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@ CFLAGS_nfstrace.o += -I$(src)
nfs-y 			:= client.o dir.o file.o getroot.o inode.o super.o \
			   io.o direct.o pagelist.o read.o symlink.o unlink.o \
			   write.o namespace.o mount_clnt.o nfstrace.o \
			   export.o sysfs.o
			   export.o sysfs.o fs_context.o
nfs-$(CONFIG_ROOT_NFS)	+= nfsroot.o
nfs-$(CONFIG_SYSCTL)	+= sysctl.o
nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o
+8 −3
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#include "callback.h"
#include "internal.h"
#include "nfs4session.h"
#include "nfs4trace.h"

#define CB_OP_TAGLEN_MAXSZ		(512)
#define CB_OP_HDR_RES_MAXSZ		(2 * 4) // opcode, status
@@ -946,8 +947,12 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp)

	if (hdr_arg.minorversion == 0) {
		cps.clp = nfs4_find_client_ident(SVC_NET(rqstp), hdr_arg.cb_ident);
		if (!cps.clp || !check_gss_callback_principal(cps.clp, rqstp)) {
			if (cps.clp)
		if (!cps.clp) {
			trace_nfs_cb_no_clp(rqstp->rq_xid, hdr_arg.cb_ident);
			goto out_invalidcred;
		}
		if (!check_gss_callback_principal(cps.clp, rqstp)) {
			trace_nfs_cb_badprinc(rqstp->rq_xid, hdr_arg.cb_ident);
			nfs_put_client(cps.clp);
			goto out_invalidcred;
		}
+44 −40
Original line number Diff line number Diff line
@@ -474,6 +474,7 @@ void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
			to->to_maxval = to->to_initval;
		to->to_exponential = 0;
		break;
#ifndef CONFIG_NFS_DISABLE_UDP_SUPPORT
	case XPRT_TRANSPORT_UDP:
		if (retrans == NFS_UNSPEC_RETRANS)
			to->to_retries = NFS_DEF_UDP_RETRANS;
@@ -484,6 +485,7 @@ void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
		to->to_maxval = NFS_MAX_UDP_TIMEOUT;
		to->to_exponential = 1;
		break;
#endif
	default:
		BUG();
	}
@@ -580,8 +582,10 @@ static int nfs_start_lockd(struct nfs_server *server)
		default:
			nlm_init.protocol = IPPROTO_TCP;
			break;
#ifndef CONFIG_NFS_DISABLE_UDP_SUPPORT
		case XPRT_TRANSPORT_UDP:
			nlm_init.protocol = IPPROTO_UDP;
#endif
	}

	host = nlmclnt_init(&nlm_init);
@@ -658,28 +662,28 @@ EXPORT_SYMBOL_GPL(nfs_init_client);
 * Create a version 2 or 3 client
 */
static int nfs_init_server(struct nfs_server *server,
			   const struct nfs_parsed_mount_data *data,
			   struct nfs_subversion *nfs_mod)
			   const struct fs_context *fc)
{
	const struct nfs_fs_context *ctx = nfs_fc2context(fc);
	struct rpc_timeout timeparms;
	struct nfs_client_initdata cl_init = {
		.hostname = data->nfs_server.hostname,
		.addr = (const struct sockaddr *)&data->nfs_server.address,
		.addrlen = data->nfs_server.addrlen,
		.nfs_mod = nfs_mod,
		.proto = data->nfs_server.protocol,
		.net = data->net,
		.hostname = ctx->nfs_server.hostname,
		.addr = (const struct sockaddr *)&ctx->nfs_server.address,
		.addrlen = ctx->nfs_server.addrlen,
		.nfs_mod = ctx->nfs_mod,
		.proto = ctx->nfs_server.protocol,
		.net = fc->net_ns,
		.timeparms = &timeparms,
		.cred = server->cred,
		.nconnect = data->nfs_server.nconnect,
		.nconnect = ctx->nfs_server.nconnect,
		.init_flags = (1UL << NFS_CS_REUSEPORT),
	};
	struct nfs_client *clp;
	int error;

	nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
			data->timeo, data->retrans);
	if (data->flags & NFS_MOUNT_NORESVPORT)
	nfs_init_timeout_values(&timeparms, ctx->nfs_server.protocol,
				ctx->timeo, ctx->retrans);
	if (ctx->flags & NFS_MOUNT_NORESVPORT)
		set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);

	/* Allocate or find a client reference we can use */
@@ -690,46 +694,46 @@ static int nfs_init_server(struct nfs_server *server,
	server->nfs_client = clp;

	/* Initialise the client representation from the mount data */
	server->flags = data->flags;
	server->options = data->options;
	server->flags = ctx->flags;
	server->options = ctx->options;
	server->caps |= NFS_CAP_HARDLINKS|NFS_CAP_SYMLINKS|NFS_CAP_FILEID|
		NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|NFS_CAP_OWNER_GROUP|
		NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME;

	if (data->rsize)
		server->rsize = nfs_block_size(data->rsize, NULL);
	if (data->wsize)
		server->wsize = nfs_block_size(data->wsize, NULL);
	if (ctx->rsize)
		server->rsize = nfs_block_size(ctx->rsize, NULL);
	if (ctx->wsize)
		server->wsize = nfs_block_size(ctx->wsize, NULL);

	server->acregmin = data->acregmin * HZ;
	server->acregmax = data->acregmax * HZ;
	server->acdirmin = data->acdirmin * HZ;
	server->acdirmax = data->acdirmax * HZ;
	server->acregmin = ctx->acregmin * HZ;
	server->acregmax = ctx->acregmax * HZ;
	server->acdirmin = ctx->acdirmin * HZ;
	server->acdirmax = ctx->acdirmax * HZ;

	/* Start lockd here, before we might error out */
	error = nfs_start_lockd(server);
	if (error < 0)
		goto error;

	server->port = data->nfs_server.port;
	server->auth_info = data->auth_info;
	server->port = ctx->nfs_server.port;
	server->auth_info = ctx->auth_info;

	error = nfs_init_server_rpcclient(server, &timeparms,
					  data->selected_flavor);
					  ctx->selected_flavor);
	if (error < 0)
		goto error;

	/* Preserve the values of mount_server-related mount options */
	if (data->mount_server.addrlen) {
		memcpy(&server->mountd_address, &data->mount_server.address,
			data->mount_server.addrlen);
		server->mountd_addrlen = data->mount_server.addrlen;
	if (ctx->mount_server.addrlen) {
		memcpy(&server->mountd_address, &ctx->mount_server.address,
			ctx->mount_server.addrlen);
		server->mountd_addrlen = ctx->mount_server.addrlen;
	}
	server->mountd_version = data->mount_server.version;
	server->mountd_port = data->mount_server.port;
	server->mountd_protocol = data->mount_server.protocol;
	server->mountd_version = ctx->mount_server.version;
	server->mountd_port = ctx->mount_server.port;
	server->mountd_protocol = ctx->mount_server.protocol;

	server->namelen  = data->namlen;
	server->namelen  = ctx->namlen;
	return 0;

error:
@@ -951,9 +955,9 @@ EXPORT_SYMBOL_GPL(nfs_free_server);
 * Create a version 2 or 3 volume record
 * - keyed on server and FSID
 */
struct nfs_server *nfs_create_server(struct nfs_mount_info *mount_info,
				     struct nfs_subversion *nfs_mod)
struct nfs_server *nfs_create_server(struct fs_context *fc)
{
	struct nfs_fs_context *ctx = nfs_fc2context(fc);
	struct nfs_server *server;
	struct nfs_fattr *fattr;
	int error;
@@ -970,18 +974,18 @@ struct nfs_server *nfs_create_server(struct nfs_mount_info *mount_info,
		goto error;

	/* Get a client representation */
	error = nfs_init_server(server, mount_info->parsed, nfs_mod);
	error = nfs_init_server(server, fc);
	if (error < 0)
		goto error;

	/* Probe the root fh to retrieve its FSID */
	error = nfs_probe_fsinfo(server, mount_info->mntfh, fattr);
	error = nfs_probe_fsinfo(server, ctx->mntfh, fattr);
	if (error < 0)
		goto error;
	if (server->nfs_client->rpc_ops->version == 3) {
		if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN)
			server->namelen = NFS3_MAXNAMLEN;
		if (!(mount_info->parsed->flags & NFS_MOUNT_NORDIRPLUS))
		if (!(ctx->flags & NFS_MOUNT_NORDIRPLUS))
			server->caps |= NFS_CAP_READDIRPLUS;
	} else {
		if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN)
@@ -989,7 +993,7 @@ struct nfs_server *nfs_create_server(struct nfs_mount_info *mount_info,
	}

	if (!(fattr->valid & NFS_ATTR_FATTR)) {
		error = nfs_mod->rpc_ops->getattr(server, mount_info->mntfh,
		error = ctx->nfs_mod->rpc_ops->getattr(server, ctx->mntfh,
						       fattr, NULL, NULL);
		if (error < 0) {
			dprintk("nfs_create_server: getattr error = %d\n", -error);
+67 −13
Original line number Diff line number Diff line
@@ -25,13 +25,32 @@
#include "internal.h"
#include "nfs4trace.h"

static void nfs_free_delegation(struct nfs_delegation *delegation)
#define NFS_DEFAULT_DELEGATION_WATERMARK (5000U)

static atomic_long_t nfs_active_delegations;
static unsigned nfs_delegation_watermark = NFS_DEFAULT_DELEGATION_WATERMARK;

static void __nfs_free_delegation(struct nfs_delegation *delegation)
{
	put_cred(delegation->cred);
	delegation->cred = NULL;
	kfree_rcu(delegation, rcu);
}

static void nfs_mark_delegation_revoked(struct nfs_delegation *delegation)
{
	if (!test_and_set_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
		delegation->stateid.type = NFS4_INVALID_STATEID_TYPE;
		atomic_long_dec(&nfs_active_delegations);
	}
}

static void nfs_free_delegation(struct nfs_delegation *delegation)
{
	nfs_mark_delegation_revoked(delegation);
	__nfs_free_delegation(delegation);
}

/**
 * nfs_mark_delegation_referenced - set delegation's REFERENCED flag
 * @delegation: delegation to process
@@ -343,7 +362,8 @@ nfs_update_inplace_delegation(struct nfs_delegation *delegation,
		delegation->stateid.seqid = update->stateid.seqid;
		smp_wmb();
		delegation->type = update->type;
		clear_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
		if (test_and_clear_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
			atomic_long_inc(&nfs_active_delegations);
	}
}

@@ -423,6 +443,8 @@ add_new:
	rcu_assign_pointer(nfsi->delegation, delegation);
	delegation = NULL;

	atomic_long_inc(&nfs_active_delegations);

	trace_nfs4_set_delegation(inode, type);

	spin_lock(&inode->i_lock);
@@ -432,7 +454,7 @@ add_new:
out:
	spin_unlock(&clp->cl_lock);
	if (delegation != NULL)
		nfs_free_delegation(delegation);
		__nfs_free_delegation(delegation);
	if (freeme != NULL) {
		nfs_do_return_delegation(inode, freeme, 0);
		nfs_free_delegation(freeme);
@@ -479,7 +501,7 @@ static bool nfs_delegation_need_return(struct nfs_delegation *delegation)

	if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags))
		ret = true;
	if (test_and_clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) && !ret) {
	else if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags)) {
		struct inode *inode;

		spin_lock(&delegation->lock);
@@ -488,6 +510,8 @@ static bool nfs_delegation_need_return(struct nfs_delegation *delegation)
			ret = true;
		spin_unlock(&delegation->lock);
	}
	if (ret)
		clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
	if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags) ||
	    test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
		ret = false;
@@ -607,6 +631,7 @@ void nfs_inode_evict_delegation(struct inode *inode)

	delegation = nfs_inode_detach_delegation(inode);
	if (delegation != NULL) {
		set_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
		set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags);
		nfs_do_return_delegation(inode, delegation, 1);
		nfs_free_delegation(delegation);
@@ -636,6 +661,40 @@ int nfs4_inode_return_delegation(struct inode *inode)
	return err;
}

/**
 * nfs_inode_return_delegation_on_close - asynchronously return a delegation
 * @inode: inode to process
 *
 * This routine is called on file close in order to determine if the
 * inode delegation needs to be returned immediately.
 */
void nfs4_inode_return_delegation_on_close(struct inode *inode)
{
	struct nfs_delegation *delegation;
	struct nfs_delegation *ret = NULL;

	if (!inode)
		return;
	rcu_read_lock();
	delegation = nfs4_get_valid_delegation(inode);
	if (!delegation)
		goto out;
	if (test_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) ||
	    atomic_long_read(&nfs_active_delegations) >= nfs_delegation_watermark) {
		spin_lock(&delegation->lock);
		if (delegation->inode &&
		    list_empty(&NFS_I(inode)->open_files) &&
		    !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
			clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
			ret = delegation;
		}
		spin_unlock(&delegation->lock);
	}
out:
	rcu_read_unlock();
	nfs_end_delegation_return(inode, ret, 0);
}

/**
 * nfs4_inode_make_writeable
 * @inode: pointer to inode
@@ -760,13 +819,6 @@ static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *cl
	rcu_read_unlock();
}

static void nfs_mark_delegation_revoked(struct nfs_server *server,
		struct nfs_delegation *delegation)
{
	set_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
	delegation->stateid.type = NFS4_INVALID_STATEID_TYPE;
}

static void nfs_revoke_delegation(struct inode *inode,
		const nfs4_stateid *stateid)
{
@@ -794,7 +846,7 @@ static void nfs_revoke_delegation(struct inode *inode,
		}
		spin_unlock(&delegation->lock);
	}
	nfs_mark_delegation_revoked(NFS_SERVER(inode), delegation);
	nfs_mark_delegation_revoked(delegation);
	ret = true;
out:
	rcu_read_unlock();
@@ -833,7 +885,7 @@ void nfs_delegation_mark_returned(struct inode *inode,
			delegation->stateid.seqid = stateid->seqid;
	}

	nfs_mark_delegation_revoked(NFS_SERVER(inode), delegation);
	nfs_mark_delegation_revoked(delegation);

out_clear_returning:
	clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
@@ -1317,3 +1369,5 @@ out:
	rcu_read_unlock();
	return ret;
}

module_param_named(delegation_watermark, nfs_delegation_watermark, uint, 0644);
Loading