diff options
Diffstat (limited to 'fs/nfs/client.c')
| -rw-r--r-- | fs/nfs/client.c | 1084 | 
1 files changed, 363 insertions, 721 deletions
diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 0870d0d4efc..1d09289c8f0 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -36,11 +36,13 @@  #include <linux/inet.h>  #include <linux/in6.h>  #include <linux/slab.h> +#include <linux/idr.h>  #include <net/ipv6.h>  #include <linux/nfs_xdr.h>  #include <linux/sunrpc/bc_xprt.h> +#include <linux/nsproxy.h> +#include <linux/pid_namespace.h> -#include <asm/system.h>  #include "nfs4_fs.h"  #include "callback.h" @@ -49,64 +51,96 @@  #include "internal.h"  #include "fscache.h"  #include "pnfs.h" +#include "nfs.h" +#include "netns.h"  #define NFSDBG_FACILITY		NFSDBG_CLIENT -static DEFINE_SPINLOCK(nfs_client_lock); -static LIST_HEAD(nfs_client_list); -static LIST_HEAD(nfs_volume_list);  static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq); +static DEFINE_SPINLOCK(nfs_version_lock); +static DEFINE_MUTEX(nfs_version_mutex); +static LIST_HEAD(nfs_versions);  /*   * RPC cruft for NFS   */ -static struct rpc_version *nfs_version[5] = { -	[2]			= &nfs_version2, -#ifdef CONFIG_NFS_V3 -	[3]			= &nfs_version3, -#endif -#ifdef CONFIG_NFS_V4 -	[4]			= &nfs_version4, -#endif +static const struct rpc_version *nfs_version[5] = { +	[2] = NULL, +	[3] = NULL, +	[4] = NULL,  }; -struct rpc_program nfs_program = { +const struct rpc_program nfs_program = {  	.name			= "nfs",  	.number			= NFS_PROGRAM,  	.nrvers			= ARRAY_SIZE(nfs_version),  	.version		= nfs_version,  	.stats			= &nfs_rpcstat, -	.pipe_dir_name		= "/nfs", +	.pipe_dir_name		= NFS_PIPE_DIRNAME,  };  struct rpc_stat nfs_rpcstat = {  	.program		= &nfs_program  }; +static struct nfs_subversion *find_nfs_version(unsigned int version) +{ +	struct nfs_subversion *nfs; +	spin_lock(&nfs_version_lock); -#ifdef CONFIG_NFS_V3_ACL -static struct rpc_stat		nfsacl_rpcstat = { &nfsacl_program }; -static struct rpc_version *	nfsacl_version[] = { -	[3]			= &nfsacl_version3, -}; +	list_for_each_entry(nfs, &nfs_versions, list) { +		if (nfs->rpc_ops->version == version) { +			spin_unlock(&nfs_version_lock); +			return nfs; +		} +	} -struct rpc_program		nfsacl_program = { -	.name			= "nfsacl", -	.number			= NFS_ACL_PROGRAM, -	.nrvers			= ARRAY_SIZE(nfsacl_version), -	.version		= nfsacl_version, -	.stats			= &nfsacl_rpcstat, -}; -#endif  /* CONFIG_NFS_V3_ACL */ - -struct nfs_client_initdata { -	const char *hostname; -	const struct sockaddr *addr; -	size_t addrlen; -	const struct nfs_rpc_ops *rpc_ops; -	int proto; -	u32 minorversion; -}; +	spin_unlock(&nfs_version_lock); +	return ERR_PTR(-EPROTONOSUPPORT); +} + +struct nfs_subversion *get_nfs_version(unsigned int version) +{ +	struct nfs_subversion *nfs = find_nfs_version(version); + +	if (IS_ERR(nfs)) { +		mutex_lock(&nfs_version_mutex); +		request_module("nfsv%d", version); +		nfs = find_nfs_version(version); +		mutex_unlock(&nfs_version_mutex); +	} + +	if (!IS_ERR(nfs)) +		try_module_get(nfs->owner); +	return nfs; +} + +void put_nfs_version(struct nfs_subversion *nfs) +{ +	module_put(nfs->owner); +} + +void register_nfs_version(struct nfs_subversion *nfs) +{ +	spin_lock(&nfs_version_lock); + +	list_add(&nfs->list, &nfs_versions); +	nfs_version[nfs->rpc_ops->version] = nfs->rpc_vers; + +	spin_unlock(&nfs_version_lock); +} +EXPORT_SYMBOL_GPL(register_nfs_version); + +void unregister_nfs_version(struct nfs_subversion *nfs) +{ +	spin_lock(&nfs_version_lock); + +	nfs_version[nfs->rpc_ops->version] = NULL; +	list_del(&nfs->list); + +	spin_unlock(&nfs_version_lock); +} +EXPORT_SYMBOL_GPL(unregister_nfs_version);  /*   * Allocate a shared client record @@ -114,7 +148,7 @@ struct nfs_client_initdata {   * Since these are allocated/deallocated very rarely, we don't   * bother putting them in a slab cache...   */ -static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) +struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)  {  	struct nfs_client *clp;  	struct rpc_cred *cred; @@ -123,7 +157,10 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_  	if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL)  		goto error_0; -	clp->rpc_ops = cl_init->rpc_ops; +	clp->cl_nfs_mod = cl_init->nfs_mod; +	try_module_get(clp->cl_nfs_mod->owner); + +	clp->rpc_ops = clp->cl_nfs_mod->rpc_ops;  	atomic_set(&clp->cl_count, 1);  	clp->cl_cons_state = NFS_CS_INITING; @@ -142,85 +179,67 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_  	clp->cl_rpcclient = ERR_PTR(-EINVAL);  	clp->cl_proto = cl_init->proto; +	clp->cl_net = get_net(cl_init->net); -#ifdef CONFIG_NFS_V4 -	INIT_LIST_HEAD(&clp->cl_delegations); -	spin_lock_init(&clp->cl_lock); -	INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state); -	rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); -	clp->cl_boot_time = CURRENT_TIME; -	clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED; -	clp->cl_minorversion = cl_init->minorversion; -	clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion]; -#endif -	cred = rpc_lookup_machine_cred(); +	cred = rpc_lookup_machine_cred("*");  	if (!IS_ERR(cred))  		clp->cl_machine_cred = cred; -#if defined(CONFIG_NFS_V4_1) -	INIT_LIST_HEAD(&clp->cl_layouts); -#endif  	nfs_fscache_get_client_cookie(clp);  	return clp;  error_cleanup: +	put_nfs_version(clp->cl_nfs_mod);  	kfree(clp);  error_0:  	return ERR_PTR(err);  } +EXPORT_SYMBOL_GPL(nfs_alloc_client); -#ifdef CONFIG_NFS_V4 -/* - * Clears/puts all minor version specific parts from an nfs_client struct - * reverting it to minorversion 0. - */ -static void nfs4_clear_client_minor_version(struct nfs_client *clp) +#if IS_ENABLED(CONFIG_NFS_V4) +void nfs_cleanup_cb_ident_idr(struct net *net)  { -#ifdef CONFIG_NFS_V4_1 -	if (nfs4_has_session(clp)) { -		nfs4_destroy_session(clp->cl_session); -		clp->cl_session = NULL; -	} +	struct nfs_net *nn = net_generic(net, nfs_net_id); -	clp->cl_mvops = nfs_v4_minor_ops[0]; -#endif /* CONFIG_NFS_V4_1 */ +	idr_destroy(&nn->cb_ident_idr);  } -/* - * Destroy the NFS4 callback service - */ -static void nfs4_destroy_callback(struct nfs_client *clp) +/* nfs_client_lock held */ +static void nfs_cb_idr_remove_locked(struct nfs_client *clp)  { -	if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) -		nfs_callback_down(clp->cl_mvops->minor_version); +	struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); + +	if (clp->cl_cb_ident) +		idr_remove(&nn->cb_ident_idr, clp->cl_cb_ident);  } -static void nfs4_shutdown_client(struct nfs_client *clp) +static void pnfs_init_server(struct nfs_server *server)  { -	if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state)) -		nfs4_kill_renewd(clp); -	nfs4_clear_client_minor_version(clp); -	nfs4_destroy_callback(clp); -	if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state)) -		nfs_idmap_delete(clp); - -	rpc_destroy_wait_queue(&clp->cl_rpcwaitq); +	rpc_init_wait_queue(&server->roc_rpcwaitq, "pNFS ROC");  } +  #else -static void nfs4_shutdown_client(struct nfs_client *clp) +void nfs_cleanup_cb_ident_idr(struct net *net) +{ +} + +static void nfs_cb_idr_remove_locked(struct nfs_client *clp) +{ +} + +static void pnfs_init_server(struct nfs_server *server)  {  } +  #endif /* CONFIG_NFS_V4 */  /*   * Destroy a shared client record   */ -static void nfs_free_client(struct nfs_client *clp) +void nfs_free_client(struct nfs_client *clp)  {  	dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version); -	nfs4_shutdown_client(clp); -  	nfs_fscache_release_client_cookie(clp);  	/* -EIO all pending I/O */ @@ -230,29 +249,36 @@ static void nfs_free_client(struct nfs_client *clp)  	if (clp->cl_machine_cred != NULL)  		put_rpccred(clp->cl_machine_cred); +	put_net(clp->cl_net); +	put_nfs_version(clp->cl_nfs_mod);  	kfree(clp->cl_hostname);  	kfree(clp);  	dprintk("<-- nfs_free_client()\n");  } +EXPORT_SYMBOL_GPL(nfs_free_client);  /*   * Release a reference to a shared client record   */  void nfs_put_client(struct nfs_client *clp)  { +	struct nfs_net *nn; +  	if (!clp)  		return;  	dprintk("--> nfs_put_client({%d})\n", atomic_read(&clp->cl_count)); +	nn = net_generic(clp->cl_net, nfs_net_id); -	if (atomic_dec_and_lock(&clp->cl_count, &nfs_client_lock)) { +	if (atomic_dec_and_lock(&clp->cl_count, &nn->nfs_client_lock)) {  		list_del(&clp->cl_share_link); -		spin_unlock(&nfs_client_lock); +		nfs_cb_idr_remove_locked(clp); +		spin_unlock(&nn->nfs_client_lock); -		BUG_ON(!list_empty(&clp->cl_superblocks)); +		WARN_ON_ONCE(!list_empty(&clp->cl_superblocks)); -		nfs_free_client(clp); +		clp->rpc_ops->free_client(clp);  	}  }  EXPORT_SYMBOL_GPL(nfs_put_client); @@ -275,11 +301,12 @@ static int nfs_sockaddr_match_ipaddr6(const struct sockaddr *sa1,  	const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sa1;  	const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2; -	if (ipv6_addr_scope(&sin1->sin6_addr) == IPV6_ADDR_SCOPE_LINKLOCAL && -	    sin1->sin6_scope_id != sin2->sin6_scope_id) +	if (!ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr))  		return 0; +	else if (ipv6_addr_type(&sin1->sin6_addr) & IPV6_ADDR_LINKLOCAL) +		return sin1->sin6_scope_id == sin2->sin6_scope_id; -	return ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr); +	return 1;  }  #else	/* !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE) */  static int nfs_sockaddr_match_ipaddr6(const struct sockaddr *sa1, @@ -325,12 +352,13 @@ static int nfs_sockaddr_cmp_ip4(const struct sockaddr *sa1,  		(sin1->sin_port == sin2->sin_port);  } +#if defined(CONFIG_NFS_V4_1)  /*   * Test if two socket addresses represent the same actual socket,   * by comparing (only) relevant fields, excluding the port number.   */ -static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1, -				     const struct sockaddr *sa2) +int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1, +			      const struct sockaddr *sa2)  {  	if (sa1->sa_family != sa2->sa_family)  		return 0; @@ -343,6 +371,8 @@ static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1,  	}  	return 0;  } +EXPORT_SYMBOL_GPL(nfs_sockaddr_match_ipaddr); +#endif /* CONFIG_NFS_V4_1 */  /*   * Test if two socket addresses represent the same actual socket, @@ -364,72 +394,6 @@ static int nfs_sockaddr_cmp(const struct sockaddr *sa1,  }  /* - * Find a client by IP address and protocol version - * - returns NULL if no such client - */ -struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion) -{ -	struct nfs_client *clp; - -	spin_lock(&nfs_client_lock); -	list_for_each_entry(clp, &nfs_client_list, cl_share_link) { -		struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; - -		/* Don't match clients that failed to initialise properly */ -		if (!(clp->cl_cons_state == NFS_CS_READY || -		      clp->cl_cons_state == NFS_CS_SESSION_INITING)) -			continue; - -		/* Different NFS versions cannot share the same nfs_client */ -		if (clp->rpc_ops->version != nfsversion) -			continue; - -		/* Match only the IP address, not the port number */ -		if (!nfs_sockaddr_match_ipaddr(addr, clap)) -			continue; - -		atomic_inc(&clp->cl_count); -		spin_unlock(&nfs_client_lock); -		return clp; -	} -	spin_unlock(&nfs_client_lock); -	return NULL; -} - -/* - * Find a client by IP address and protocol version - * - returns NULL if no such client - */ -struct nfs_client *nfs_find_client_next(struct nfs_client *clp) -{ -	struct sockaddr *sap = (struct sockaddr *)&clp->cl_addr; -	u32 nfsvers = clp->rpc_ops->version; - -	spin_lock(&nfs_client_lock); -	list_for_each_entry_continue(clp, &nfs_client_list, cl_share_link) { -		struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; - -		/* Don't match clients that failed to initialise properly */ -		if (clp->cl_cons_state != NFS_CS_READY) -			continue; - -		/* Different NFS versions cannot share the same nfs_client */ -		if (clp->rpc_ops->version != nfsvers) -			continue; - -		/* Match only the IP address, not the port number */ -		if (!nfs_sockaddr_match_ipaddr(sap, clap)) -			continue; - -		atomic_inc(&clp->cl_count); -		spin_unlock(&nfs_client_lock); -		return clp; -	} -	spin_unlock(&nfs_client_lock); -	return NULL; -} - -/*   * Find an nfs_client on the list that matches the initialisation data   * that is supplied.   */ @@ -437,15 +401,16 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat  {  	struct nfs_client *clp;  	const struct sockaddr *sap = data->addr; +	struct nfs_net *nn = net_generic(data->net, nfs_net_id); -	list_for_each_entry(clp, &nfs_client_list, cl_share_link) { +	list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) {  	        const struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;  		/* Don't match clients that failed to initialise properly */  		if (clp->cl_cons_state < 0)  			continue;  		/* Different NFS versions cannot share the same nfs_client */ -		if (clp->rpc_ops != data->rpc_ops) +		if (clp->rpc_ops != data->nfs_mod->rpc_ops)  			continue;  		if (clp->cl_proto != data->proto) @@ -463,55 +428,28 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat  	return NULL;  } +static bool nfs_client_init_is_complete(const struct nfs_client *clp) +{ +	return clp->cl_cons_state != NFS_CS_INITING; +} + +int nfs_wait_client_init_complete(const struct nfs_client *clp) +{ +	return wait_event_killable(nfs_client_active_wq, +			nfs_client_init_is_complete(clp)); +} +EXPORT_SYMBOL_GPL(nfs_wait_client_init_complete); +  /* - * Look up a client by IP address and protocol version - * - creates a new record if one doesn't yet exist + * Found an existing client.  Make sure it's ready before returning.   */ -static struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_init) +static struct nfs_client * +nfs_found_client(const struct nfs_client_initdata *cl_init, +		 struct nfs_client *clp)  { -	struct nfs_client *clp, *new = NULL;  	int error; -	dprintk("--> nfs_get_client(%s,v%u)\n", -		cl_init->hostname ?: "", cl_init->rpc_ops->version); - -	/* see if the client already exists */ -	do { -		spin_lock(&nfs_client_lock); - -		clp = nfs_match_client(cl_init); -		if (clp) -			goto found_client; -		if (new) -			goto install_client; - -		spin_unlock(&nfs_client_lock); - -		new = nfs_alloc_client(cl_init); -	} while (!IS_ERR(new)); - -	dprintk("--> nfs_get_client() = %ld [failed]\n", PTR_ERR(new)); -	return new; - -	/* install a new client and return with it unready */ -install_client: -	clp = new; -	list_add(&clp->cl_share_link, &nfs_client_list); -	spin_unlock(&nfs_client_lock); -	dprintk("--> nfs_get_client() = %p [new]\n", clp); -	return clp; - -	/* found an existing client -	 * - make sure it's ready before returning -	 */ -found_client: -	spin_unlock(&nfs_client_lock); - -	if (new) -		nfs_free_client(new); - -	error = wait_event_killable(nfs_client_active_wq, -				clp->cl_cons_state < NFS_CS_INITING); +	error = nfs_wait_client_init_complete(clp);  	if (error < 0) {  		nfs_put_client(clp);  		return ERR_PTR(-ERESTARTSYS); @@ -523,41 +461,75 @@ found_client:  		return ERR_PTR(error);  	} -	BUG_ON(clp->cl_cons_state != NFS_CS_READY); +	smp_rmb(); -	dprintk("--> nfs_get_client() = %p [share]\n", clp); +	dprintk("<-- %s found nfs_client %p for %s\n", +		__func__, clp, cl_init->hostname ?: "");  	return clp;  }  /* - * Mark a server as ready or failed + * Look up a client by IP address and protocol version + * - creates a new record if one doesn't yet exist   */ -void nfs_mark_client_ready(struct nfs_client *clp, int state) +struct nfs_client * +nfs_get_client(const struct nfs_client_initdata *cl_init, +	       const struct rpc_timeout *timeparms, +	       const char *ip_addr, +	       rpc_authflavor_t authflavour)  { -	clp->cl_cons_state = state; -	wake_up_all(&nfs_client_active_wq); +	struct nfs_client *clp, *new = NULL; +	struct nfs_net *nn = net_generic(cl_init->net, nfs_net_id); +	const struct nfs_rpc_ops *rpc_ops = cl_init->nfs_mod->rpc_ops; + +	dprintk("--> nfs_get_client(%s,v%u)\n", +		cl_init->hostname ?: "", rpc_ops->version); + +	/* see if the client already exists */ +	do { +		spin_lock(&nn->nfs_client_lock); + +		clp = nfs_match_client(cl_init); +		if (clp) { +			spin_unlock(&nn->nfs_client_lock); +			if (new) +				new->rpc_ops->free_client(new); +			return nfs_found_client(cl_init, clp); +		} +		if (new) { +			list_add_tail(&new->cl_share_link, +					&nn->nfs_client_list); +			spin_unlock(&nn->nfs_client_lock); +			new->cl_flags = cl_init->init_flags; +			return rpc_ops->init_client(new, timeparms, ip_addr); +		} + +		spin_unlock(&nn->nfs_client_lock); + +		new = rpc_ops->alloc_client(cl_init); +	} while (!IS_ERR(new)); + +	dprintk("<-- nfs_get_client() Failed to find %s (%ld)\n", +		cl_init->hostname ?: "", PTR_ERR(new)); +	return new;  } +EXPORT_SYMBOL_GPL(nfs_get_client);  /* - * With sessions, the client is not marked ready until after a - * successful EXCHANGE_ID and CREATE_SESSION. - * - * Map errors cl_cons_state errors to EPROTONOSUPPORT to indicate - * other versions of NFS can be tried. + * Mark a server as ready or failed   */ -int nfs4_check_client_ready(struct nfs_client *clp) +void nfs_mark_client_ready(struct nfs_client *clp, int state)  { -	if (!nfs4_has_session(clp)) -		return 0; -	if (clp->cl_cons_state < NFS_CS_READY) -		return -EPROTONOSUPPORT; -	return 0; +	smp_wmb(); +	clp->cl_cons_state = state; +	wake_up_all(&nfs_client_active_wq);  } +EXPORT_SYMBOL_GPL(nfs_mark_client_ready);  /*   * Initialise the timeout values for a connection   */ -static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, +void nfs_init_timeout_values(struct rpc_timeout *to, int proto,  				    unsigned int timeo, unsigned int retrans)  {  	to->to_initval = timeo * HZ / 10; @@ -594,18 +566,18 @@ static void nfs_init_timeout_values(struct rpc_timeout *to, int proto,  		BUG();  	}  } +EXPORT_SYMBOL_GPL(nfs_init_timeout_values);  /*   * Create an RPC client handle   */ -static int nfs_create_rpc_client(struct nfs_client *clp, -				 const struct rpc_timeout *timeparms, -				 rpc_authflavor_t flavor, -				 int discrtry, int noresvport) +int nfs_create_rpc_client(struct nfs_client *clp, +			  const struct rpc_timeout *timeparms, +			  rpc_authflavor_t flavor)  {  	struct rpc_clnt		*clnt = NULL;  	struct rpc_create_args args = { -		.net		= &init_net, +		.net		= clp->cl_net,  		.protocol	= clp->cl_proto,  		.address	= (struct sockaddr *)&clp->cl_addr,  		.addrsize	= clp->cl_addrlen, @@ -616,10 +588,14 @@ static int nfs_create_rpc_client(struct nfs_client *clp,  		.authflavor	= flavor,  	}; -	if (discrtry) +	if (test_bit(NFS_CS_DISCRTRY, &clp->cl_flags))  		args.flags |= RPC_CLNT_CREATE_DISCRTRY; -	if (noresvport) +	if (test_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags)) +		args.flags |= RPC_CLNT_CREATE_NO_RETRANS_TIMEOUT; +	if (test_bit(NFS_CS_NORESVPORT, &clp->cl_flags))  		args.flags |= RPC_CLNT_CREATE_NONPRIVPORT; +	if (test_bit(NFS_CS_INFINITE_SLOTS, &clp->cl_flags)) +		args.flags |= RPC_CLNT_CREATE_INFINITE_SLOTS;  	if (!IS_ERR(clp->cl_rpcclient))  		return 0; @@ -634,14 +610,14 @@ static int nfs_create_rpc_client(struct nfs_client *clp,  	clp->cl_rpcclient = clnt;  	return 0;  } +EXPORT_SYMBOL_GPL(nfs_create_rpc_client);  /*   * Version 2 or 3 client destruction   */  static void nfs_destroy_server(struct nfs_server *server)  { -	if (!(server->flags & NFS_MOUNT_LOCAL_FLOCK) || -			!(server->flags & NFS_MOUNT_LOCAL_FCNTL)) +	if (server->nlm_host)  		nlmclnt_done(server->nlm_host);  } @@ -659,6 +635,7 @@ static int nfs_start_lockd(struct nfs_server *server)  		.nfs_version	= clp->rpc_ops->version,  		.noresvport	= server->flags & NFS_MOUNT_NORESVPORT ?  					1 : 0, +		.net		= clp->cl_net,  	};  	if (nlm_init.nfs_version > 3) @@ -685,45 +662,16 @@ static int nfs_start_lockd(struct nfs_server *server)  }  /* - * Initialise an NFSv3 ACL client connection - */ -#ifdef CONFIG_NFS_V3_ACL -static void nfs_init_server_aclclient(struct nfs_server *server) -{ -	if (server->nfs_client->rpc_ops->version != 3) -		goto out_noacl; -	if (server->flags & NFS_MOUNT_NOACL) -		goto out_noacl; - -	server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3); -	if (IS_ERR(server->client_acl)) -		goto out_noacl; - -	/* No errors! Assume that Sun nfsacls are supported */ -	server->caps |= NFS_CAP_ACLS; -	return; - -out_noacl: -	server->caps &= ~NFS_CAP_ACLS; -} -#else -static inline void nfs_init_server_aclclient(struct nfs_server *server) -{ -	server->flags &= ~NFS_MOUNT_NOACL; -	server->caps &= ~NFS_CAP_ACLS; -} -#endif - -/*   * Create a general RPC client   */ -static int nfs_init_server_rpcclient(struct nfs_server *server, +int nfs_init_server_rpcclient(struct nfs_server *server,  		const struct rpc_timeout *timeo,  		rpc_authflavor_t pseudoflavour)  {  	struct nfs_client *clp = server->nfs_client; -	server->client = rpc_clone_client(clp->cl_rpcclient); +	server->client = rpc_clone_client_set_auth(clp->cl_rpcclient, +							pseudoflavour);  	if (IS_ERR(server->client)) {  		dprintk("%s: couldn't create rpc_client!\n", __func__);  		return PTR_ERR(server->client); @@ -733,67 +681,67 @@ static int nfs_init_server_rpcclient(struct nfs_server *server,  			timeo,  			sizeof(server->client->cl_timeout_default));  	server->client->cl_timeout = &server->client->cl_timeout_default; - -	if (pseudoflavour != clp->cl_rpcclient->cl_auth->au_flavor) { -		struct rpc_auth *auth; - -		auth = rpcauth_create(pseudoflavour, server->client); -		if (IS_ERR(auth)) { -			dprintk("%s: couldn't create credcache!\n", __func__); -			return PTR_ERR(auth); -		} -	}  	server->client->cl_softrtry = 0;  	if (server->flags & NFS_MOUNT_SOFT)  		server->client->cl_softrtry = 1;  	return 0;  } +EXPORT_SYMBOL_GPL(nfs_init_server_rpcclient); -/* - * Initialise an NFS2 or NFS3 client +/** + * nfs_init_client - Initialise an NFS2 or NFS3 client + * + * @clp: nfs_client to initialise + * @timeparms: timeout parameters for underlying RPC transport + * @ip_addr: IP presentation address (not used) + * + * Returns pointer to an NFS client, or an ERR_PTR value.   */ -static int nfs_init_client(struct nfs_client *clp, -			   const struct rpc_timeout *timeparms, -			   const struct nfs_parsed_mount_data *data) +struct nfs_client *nfs_init_client(struct nfs_client *clp, +		    const struct rpc_timeout *timeparms, +		    const char *ip_addr)  {  	int error;  	if (clp->cl_cons_state == NFS_CS_READY) {  		/* the client is already initialised */  		dprintk("<-- nfs_init_client() = 0 [already %p]\n", clp); -		return 0; +		return clp;  	}  	/*  	 * Create a client RPC handle for doing FSSTAT with UNIX auth only  	 * - RFC 2623, sec 2.3.2  	 */ -	error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX, -				      0, data->flags & NFS_MOUNT_NORESVPORT); +	error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX);  	if (error < 0)  		goto error;  	nfs_mark_client_ready(clp, NFS_CS_READY); -	return 0; +	return clp;  error:  	nfs_mark_client_ready(clp, error); +	nfs_put_client(clp);  	dprintk("<-- nfs_init_client() = xerror %d\n", error); -	return error; +	return ERR_PTR(error);  } +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) +			   const struct nfs_parsed_mount_data *data, +			   struct nfs_subversion *nfs_mod)  {  	struct nfs_client_initdata cl_init = {  		.hostname = data->nfs_server.hostname,  		.addr = (const struct sockaddr *)&data->nfs_server.address,  		.addrlen = data->nfs_server.addrlen, -		.rpc_ops = &nfs_v2_clientops, +		.nfs_mod = nfs_mod,  		.proto = data->nfs_server.protocol, +		.net = data->net,  	};  	struct rpc_timeout timeparms;  	struct nfs_client *clp; @@ -801,24 +749,18 @@ static int nfs_init_server(struct nfs_server *server,  	dprintk("--> nfs_init_server()\n"); -#ifdef CONFIG_NFS_V3 -	if (data->version == 3) -		cl_init.rpc_ops = &nfs_v3_clientops; -#endif +	nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, +			data->timeo, data->retrans); +	if (data->flags & NFS_MOUNT_NORESVPORT) +		set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);  	/* Allocate or find a client reference we can use */ -	clp = nfs_get_client(&cl_init); +	clp = nfs_get_client(&cl_init, &timeparms, NULL, RPC_AUTH_UNIX);  	if (IS_ERR(clp)) {  		dprintk("<-- nfs_init_server() = error %ld\n", PTR_ERR(clp));  		return PTR_ERR(clp);  	} -	nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, -			data->timeo, data->retrans); -	error = nfs_init_client(clp, &timeparms, data); -	if (error < 0) -		goto error; -  	server->nfs_client = clp;  	/* Initialise the client representation from the mount data */ @@ -826,7 +768,7 @@ static int nfs_init_server(struct nfs_server *server,  	server->options = data->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; +		NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME|NFS_CAP_CHANGE_ATTR;  	if (data->rsize)  		server->rsize = nfs_block_size(data->rsize, NULL); @@ -844,8 +786,10 @@ static int nfs_init_server(struct nfs_server *server,  		goto error;  	server->port = data->nfs_server.port; +	server->auth_info = data->auth_info; -	error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]); +	error = nfs_init_server_rpcclient(server, &timeparms, +					  data->selected_flavor);  	if (error < 0)  		goto error; @@ -860,8 +804,6 @@ static int nfs_init_server(struct nfs_server *server,  	server->mountd_protocol = data->mount_server.protocol;  	server->namelen  = data->namlen; -	/* Create a client RPC handle for the NFSv3 ACL management interface */ -	nfs_init_server_aclclient(server);  	dprintk("<-- nfs_init_server() = 0 [new %p]\n", clp);  	return 0; @@ -875,7 +817,9 @@ error:  /*   * Load up the server record from information gained in an fsinfo record   */ -static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo *fsinfo) +static void nfs_server_set_fsinfo(struct nfs_server *server, +				  struct nfs_fh *mntfh, +				  struct nfs_fsinfo *fsinfo)  {  	unsigned long max_rpc_payload; @@ -905,7 +849,6 @@ static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo *  	if (server->wsize > NFS_MAX_FILE_IO_SIZE)  		server->wsize = NFS_MAX_FILE_IO_SIZE;  	server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; -	set_pnfs_layoutdriver(server, fsinfo->layouttype);  	server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL); @@ -931,7 +874,7 @@ static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo *  /*   * Probe filesystem information, including the FSID on v2/v3   */ -static int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr) +int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr)  {  	struct nfs_fsinfo fsinfo;  	struct nfs_client *clp = server->nfs_client; @@ -951,7 +894,7 @@ static int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, str  	if (error < 0)  		goto out_error; -	nfs_server_set_fsinfo(server, &fsinfo); +	nfs_server_set_fsinfo(server, mntfh, &fsinfo);  	/* Get some general file system info */  	if (server->namelen == 0) { @@ -971,11 +914,12 @@ out_error:  	dprintk("nfs_probe_fsinfo: error = %d\n", -error);  	return error;  } +EXPORT_SYMBOL_GPL(nfs_probe_fsinfo);  /*   * Copy useful information when duplicating a server record   */ -static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source) +void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source)  {  	target->flags = source->flags;  	target->rsize = source->rsize; @@ -986,12 +930,47 @@ static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_serve  	target->acdirmax = source->acdirmax;  	target->caps = source->caps;  	target->options = source->options; +	target->auth_info = source->auth_info;  } +EXPORT_SYMBOL_GPL(nfs_server_copy_userdata); + +void nfs_server_insert_lists(struct nfs_server *server) +{ +	struct nfs_client *clp = server->nfs_client; +	struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); + +	spin_lock(&nn->nfs_client_lock); +	list_add_tail_rcu(&server->client_link, &clp->cl_superblocks); +	list_add_tail(&server->master_link, &nn->nfs_volume_list); +	clear_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state); +	spin_unlock(&nn->nfs_client_lock); + +} +EXPORT_SYMBOL_GPL(nfs_server_insert_lists); + +void nfs_server_remove_lists(struct nfs_server *server) +{ +	struct nfs_client *clp = server->nfs_client; +	struct nfs_net *nn; + +	if (clp == NULL) +		return; +	nn = net_generic(clp->cl_net, nfs_net_id); +	spin_lock(&nn->nfs_client_lock); +	list_del_rcu(&server->client_link); +	if (list_empty(&clp->cl_superblocks)) +		set_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state); +	list_del(&server->master_link); +	spin_unlock(&nn->nfs_client_lock); + +	synchronize_rcu(); +} +EXPORT_SYMBOL_GPL(nfs_server_remove_lists);  /*   * Allocate and initialise a server record   */ -static struct nfs_server *nfs_alloc_server(void) +struct nfs_server *nfs_alloc_server(void)  {  	struct nfs_server *server; @@ -1004,6 +983,9 @@ static struct nfs_server *nfs_alloc_server(void)  	/* Zero out the NFS state stuff */  	INIT_LIST_HEAD(&server->client_link);  	INIT_LIST_HEAD(&server->master_link); +	INIT_LIST_HEAD(&server->delegations); +	INIT_LIST_HEAD(&server->layouts); +	INIT_LIST_HEAD(&server->state_owners_lru);  	atomic_set(&server->active, 0); @@ -1019,8 +1001,13 @@ static struct nfs_server *nfs_alloc_server(void)  		return NULL;  	} +	ida_init(&server->openowner_id); +	ida_init(&server->lockowner_id); +	pnfs_init_server(server); +  	return server;  } +EXPORT_SYMBOL_GPL(nfs_alloc_server);  /*   * Free up a server record @@ -1029,11 +1016,7 @@ void nfs_free_server(struct nfs_server *server)  {  	dprintk("--> nfs_free_server()\n"); -	unset_pnfs_layoutdriver(server); -	spin_lock(&nfs_client_lock); -	list_del(&server->client_link); -	list_del(&server->master_link); -	spin_unlock(&nfs_client_lock); +	nfs_server_remove_lists(server);  	if (server->destroy != NULL)  		server->destroy(server); @@ -1045,19 +1028,22 @@ void nfs_free_server(struct nfs_server *server)  	nfs_put_client(server->nfs_client); +	ida_destroy(&server->lockowner_id); +	ida_destroy(&server->openowner_id);  	nfs_free_iostats(server->io_stats);  	bdi_destroy(&server->backing_dev_info);  	kfree(server);  	nfs_release_automount_timer();  	dprintk("<-- nfs_free_server()\n");  } +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(const struct nfs_parsed_mount_data *data, -				     struct nfs_fh *mntfh) +struct nfs_server *nfs_create_server(struct nfs_mount_info *mount_info, +				     struct nfs_subversion *nfs_mod)  {  	struct nfs_server *server;  	struct nfs_fattr *fattr; @@ -1073,22 +1059,18 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,  		goto error;  	/* Get a client representation */ -	error = nfs_init_server(server, data); +	error = nfs_init_server(server, mount_info->parsed, nfs_mod);  	if (error < 0)  		goto error; -	BUG_ON(!server->nfs_client); -	BUG_ON(!server->nfs_client->rpc_ops); -	BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); -  	/* Probe the root fh to retrieve its FSID */ -	error = nfs_probe_fsinfo(server, mntfh, fattr); +	error = nfs_probe_fsinfo(server, mount_info->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 (!(data->flags & NFS_MOUNT_NORDIRPLUS)) +		if (!(mount_info->parsed->flags & NFS_MOUNT_NORDIRPLUS))  			server->caps |= NFS_CAP_READDIRPLUS;  	} else {  		if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN) @@ -1096,7 +1078,7 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,  	}  	if (!(fattr->valid & NFS_ATTR_FATTR)) { -		error = server->nfs_client->rpc_ops->getattr(server, mntfh, fattr); +		error = nfs_mod->rpc_ops->getattr(server, mount_info->mntfh, fattr, NULL);  		if (error < 0) {  			dprintk("nfs_create_server: getattr error = %d\n", -error);  			goto error; @@ -1108,11 +1090,7 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,  		(unsigned long long) server->fsid.major,  		(unsigned long long) server->fsid.minor); -	spin_lock(&nfs_client_lock); -	list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); -	list_add_tail(&server->master_link, &nfs_volume_list); -	spin_unlock(&nfs_client_lock); - +	nfs_server_insert_lists(server);  	server->mount_time = jiffies;  	nfs_free_fattr(fattr);  	return server; @@ -1122,386 +1100,15 @@ error:  	nfs_free_server(server);  	return ERR_PTR(error);  } - -#ifdef CONFIG_NFS_V4 -/* - * Initialize the NFS4 callback service - */ -static int nfs4_init_callback(struct nfs_client *clp) -{ -	int error; - -	if (clp->rpc_ops->version == 4) { -		if (nfs4_has_session(clp)) { -			error = xprt_setup_backchannel( -						clp->cl_rpcclient->cl_xprt, -						NFS41_BC_MIN_CALLBACKS); -			if (error < 0) -				return error; -		} - -		error = nfs_callback_up(clp->cl_mvops->minor_version, -					clp->cl_rpcclient->cl_xprt); -		if (error < 0) { -			dprintk("%s: failed to start callback. Error = %d\n", -				__func__, error); -			return error; -		} -		__set_bit(NFS_CS_CALLBACK, &clp->cl_res_state); -	} -	return 0; -} - -/* - * Initialize the minor version specific parts of an NFS4 client record - */ -static int nfs4_init_client_minor_version(struct nfs_client *clp) -{ -#if defined(CONFIG_NFS_V4_1) -	if (clp->cl_mvops->minor_version) { -		struct nfs4_session *session = NULL; -		/* -		 * Create the session and mark it expired. -		 * When a SEQUENCE operation encounters the expired session -		 * it will do session recovery to initialize it. -		 */ -		session = nfs4_alloc_session(clp); -		if (!session) -			return -ENOMEM; - -		clp->cl_session = session; -		/* -		 * The create session reply races with the server back -		 * channel probe. Mark the client NFS_CS_SESSION_INITING -		 * so that the client back channel can find the -		 * nfs_client struct -		 */ -		clp->cl_cons_state = NFS_CS_SESSION_INITING; -	} -#endif /* CONFIG_NFS_V4_1 */ - -	return nfs4_init_callback(clp); -} - -/* - * Initialise an NFS4 client record - */ -static int nfs4_init_client(struct nfs_client *clp, -		const struct rpc_timeout *timeparms, -		const char *ip_addr, -		rpc_authflavor_t authflavour, -		int flags) -{ -	int error; - -	if (clp->cl_cons_state == NFS_CS_READY) { -		/* the client is initialised already */ -		dprintk("<-- nfs4_init_client() = 0 [already %p]\n", clp); -		return 0; -	} - -	/* Check NFS protocol revision and initialize RPC op vector */ -	clp->rpc_ops = &nfs_v4_clientops; - -	error = nfs_create_rpc_client(clp, timeparms, authflavour, -				      1, flags & NFS_MOUNT_NORESVPORT); -	if (error < 0) -		goto error; -	strlcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr)); - -	error = nfs_idmap_new(clp); -	if (error < 0) { -		dprintk("%s: failed to create idmapper. Error = %d\n", -			__func__, error); -		goto error; -	} -	__set_bit(NFS_CS_IDMAP, &clp->cl_res_state); - -	error = nfs4_init_client_minor_version(clp); -	if (error < 0) -		goto error; - -	if (!nfs4_has_session(clp)) -		nfs_mark_client_ready(clp, NFS_CS_READY); -	return 0; - -error: -	nfs_mark_client_ready(clp, error); -	dprintk("<-- nfs4_init_client() = xerror %d\n", error); -	return error; -} - -/* - * Set up an NFS4 client - */ -static int nfs4_set_client(struct nfs_server *server, -		const char *hostname, -		const struct sockaddr *addr, -		const size_t addrlen, -		const char *ip_addr, -		rpc_authflavor_t authflavour, -		int proto, const struct rpc_timeout *timeparms, -		u32 minorversion) -{ -	struct nfs_client_initdata cl_init = { -		.hostname = hostname, -		.addr = addr, -		.addrlen = addrlen, -		.rpc_ops = &nfs_v4_clientops, -		.proto = proto, -		.minorversion = minorversion, -	}; -	struct nfs_client *clp; -	int error; - -	dprintk("--> nfs4_set_client()\n"); - -	/* Allocate or find a client reference we can use */ -	clp = nfs_get_client(&cl_init); -	if (IS_ERR(clp)) { -		error = PTR_ERR(clp); -		goto error; -	} -	error = nfs4_init_client(clp, timeparms, ip_addr, authflavour, -					server->flags); -	if (error < 0) -		goto error_put; - -	server->nfs_client = clp; -	dprintk("<-- nfs4_set_client() = 0 [new %p]\n", clp); -	return 0; - -error_put: -	nfs_put_client(clp); -error: -	dprintk("<-- nfs4_set_client() = xerror %d\n", error); -	return error; -} - - -/* - * Session has been established, and the client marked ready. - * Set the mount rsize and wsize with negotiated fore channel - * attributes which will be bound checked in nfs_server_set_fsinfo. - */ -static void nfs4_session_set_rwsize(struct nfs_server *server) -{ -#ifdef CONFIG_NFS_V4_1 -	struct nfs4_session *sess; -	u32 server_resp_sz; -	u32 server_rqst_sz; - -	if (!nfs4_has_session(server->nfs_client)) -		return; -	sess = server->nfs_client->cl_session; -	server_resp_sz = sess->fc_attrs.max_resp_sz - nfs41_maxread_overhead; -	server_rqst_sz = sess->fc_attrs.max_rqst_sz - nfs41_maxwrite_overhead; - -	if (server->rsize > server_resp_sz) -		server->rsize = server_resp_sz; -	if (server->wsize > server_rqst_sz) -		server->wsize = server_rqst_sz; -#endif /* CONFIG_NFS_V4_1 */ -} - -static int nfs4_server_common_setup(struct nfs_server *server, -		struct nfs_fh *mntfh) -{ -	struct nfs_fattr *fattr; -	int error; - -	BUG_ON(!server->nfs_client); -	BUG_ON(!server->nfs_client->rpc_ops); -	BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); - -	fattr = nfs_alloc_fattr(); -	if (fattr == NULL) -		return -ENOMEM; - -	/* We must ensure the session is initialised first */ -	error = nfs4_init_session(server); -	if (error < 0) -		goto out; - -	/* Probe the root fh to retrieve its FSID and filehandle */ -	error = nfs4_get_rootfh(server, mntfh); -	if (error < 0) -		goto out; - -	dprintk("Server FSID: %llx:%llx\n", -			(unsigned long long) server->fsid.major, -			(unsigned long long) server->fsid.minor); -	dprintk("Mount FH: %d\n", mntfh->size); - -	nfs4_session_set_rwsize(server); - -	error = nfs_probe_fsinfo(server, mntfh, fattr); -	if (error < 0) -		goto out; - -	if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) -		server->namelen = NFS4_MAXNAMLEN; - -	spin_lock(&nfs_client_lock); -	list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); -	list_add_tail(&server->master_link, &nfs_volume_list); -	spin_unlock(&nfs_client_lock); - -	server->mount_time = jiffies; -out: -	nfs_free_fattr(fattr); -	return error; -} - -/* - * Create a version 4 volume record - */ -static int nfs4_init_server(struct nfs_server *server, -		const struct nfs_parsed_mount_data *data) -{ -	struct rpc_timeout timeparms; -	int error; - -	dprintk("--> nfs4_init_server()\n"); - -	nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, -			data->timeo, data->retrans); - -	/* Initialise the client representation from the mount data */ -	server->flags = data->flags; -	server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR|NFS_CAP_POSIX_LOCK; -	if (!(data->flags & NFS_MOUNT_NORDIRPLUS)) -			server->caps |= NFS_CAP_READDIRPLUS; -	server->options = data->options; - -	/* Get a client record */ -	error = nfs4_set_client(server, -			data->nfs_server.hostname, -			(const struct sockaddr *)&data->nfs_server.address, -			data->nfs_server.addrlen, -			data->client_address, -			data->auth_flavors[0], -			data->nfs_server.protocol, -			&timeparms, -			data->minorversion); -	if (error < 0) -		goto error; - -	if (data->rsize) -		server->rsize = nfs_block_size(data->rsize, NULL); -	if (data->wsize) -		server->wsize = nfs_block_size(data->wsize, NULL); - -	server->acregmin = data->acregmin * HZ; -	server->acregmax = data->acregmax * HZ; -	server->acdirmin = data->acdirmin * HZ; -	server->acdirmax = data->acdirmax * HZ; - -	server->port = data->nfs_server.port; - -	error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]); - -error: -	/* Done */ -	dprintk("<-- nfs4_init_server() = %d\n", error); -	return error; -} - -/* - * Create a version 4 volume record - * - keyed on server and FSID - */ -struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, -				      struct nfs_fh *mntfh) -{ -	struct nfs_server *server; -	int error; - -	dprintk("--> nfs4_create_server()\n"); - -	server = nfs_alloc_server(); -	if (!server) -		return ERR_PTR(-ENOMEM); - -	/* set up the general RPC client */ -	error = nfs4_init_server(server, data); -	if (error < 0) -		goto error; - -	error = nfs4_server_common_setup(server, mntfh); -	if (error < 0) -		goto error; - -	dprintk("<-- nfs4_create_server() = %p\n", server); -	return server; - -error: -	nfs_free_server(server); -	dprintk("<-- nfs4_create_server() = error %d\n", error); -	return ERR_PTR(error); -} - -/* - * Create an NFS4 referral server record - */ -struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, -					       struct nfs_fh *mntfh) -{ -	struct nfs_client *parent_client; -	struct nfs_server *server, *parent_server; -	int error; - -	dprintk("--> nfs4_create_referral_server()\n"); - -	server = nfs_alloc_server(); -	if (!server) -		return ERR_PTR(-ENOMEM); - -	parent_server = NFS_SB(data->sb); -	parent_client = parent_server->nfs_client; - -	/* Initialise the client representation from the parent server */ -	nfs_server_copy_userdata(server, parent_server); -	server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR; - -	/* Get a client representation. -	 * Note: NFSv4 always uses TCP, */ -	error = nfs4_set_client(server, data->hostname, -				data->addr, -				data->addrlen, -				parent_client->cl_ipaddr, -				data->authflavor, -				parent_server->client->cl_xprt->prot, -				parent_server->client->cl_timeout, -				parent_client->cl_mvops->minor_version); -	if (error < 0) -		goto error; - -	error = nfs_init_server_rpcclient(server, parent_server->client->cl_timeout, data->authflavor); -	if (error < 0) -		goto error; - -	error = nfs4_server_common_setup(server, mntfh); -	if (error < 0) -		goto error; - -	dprintk("<-- nfs_create_referral_server() = %p\n", server); -	return server; - -error: -	nfs_free_server(server); -	dprintk("<-- nfs4_create_referral_server() = error %d\n", error); -	return ERR_PTR(error); -} - -#endif /* CONFIG_NFS_V4 */ +EXPORT_SYMBOL_GPL(nfs_create_server);  /*   * Clone an NFS2, NFS3 or NFS4 server record   */  struct nfs_server *nfs_clone_server(struct nfs_server *source,  				    struct nfs_fh *fh, -				    struct nfs_fattr *fattr) +				    struct nfs_fattr *fattr, +				    rpc_authflavor_t flavor)  {  	struct nfs_server *server;  	struct nfs_fattr *fattr_fsinfo; @@ -1522,6 +1129,7 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source,  	/* Copy data from the source */  	server->nfs_client = source->nfs_client; +	server->destroy = source->destroy;  	atomic_inc(&server->nfs_client->cl_count);  	nfs_server_copy_userdata(server, source); @@ -1529,11 +1137,9 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source,  	error = nfs_init_server_rpcclient(server,  			source->client->cl_timeout, -			source->client->cl_auth->au_flavor); +			flavor);  	if (error < 0)  		goto out_free_server; -	if (!IS_ERR(source->client_acl)) -		nfs_init_server_aclclient(server);  	/* probe the filesystem info for this server filesystem */  	error = nfs_probe_fsinfo(server, fh, fattr_fsinfo); @@ -1551,11 +1157,7 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source,  	if (error < 0)  		goto out_free_server; -	spin_lock(&nfs_client_lock); -	list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); -	list_add_tail(&server->master_link, &nfs_volume_list); -	spin_unlock(&nfs_client_lock); - +	nfs_server_insert_lists(server);  	server->mount_time = jiffies;  	nfs_free_fattr(fattr_fsinfo); @@ -1568,6 +1170,20 @@ out_free_server:  	dprintk("<-- nfs_clone_server() = error %d\n", error);  	return ERR_PTR(error);  } +EXPORT_SYMBOL_GPL(nfs_clone_server); + +void nfs_clients_init(struct net *net) +{ +	struct nfs_net *nn = net_generic(net, nfs_net_id); + +	INIT_LIST_HEAD(&nn->nfs_client_list); +	INIT_LIST_HEAD(&nn->nfs_volume_list); +#if IS_ENABLED(CONFIG_NFS_V4) +	idr_init(&nn->cb_ident_idr); +#endif +	spin_lock_init(&nn->nfs_client_lock); +	nn->boot_time = CURRENT_TIME; +}  #ifdef CONFIG_PROC_FS  static struct proc_dir_entry *proc_fs_nfs; @@ -1622,13 +1238,15 @@ static int nfs_server_list_open(struct inode *inode, struct file *file)  {  	struct seq_file *m;  	int ret; +	struct pid_namespace *pid_ns = file->f_dentry->d_sb->s_fs_info; +	struct net *net = pid_ns->child_reaper->nsproxy->net_ns;  	ret = seq_open(file, &nfs_server_list_ops);  	if (ret < 0)  		return ret;  	m = file->private_data; -	m->private = PDE(inode)->data; +	m->private = net;  	return 0;  } @@ -1638,9 +1256,11 @@ static int nfs_server_list_open(struct inode *inode, struct file *file)   */  static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos)  { +	struct nfs_net *nn = net_generic(m->private, nfs_net_id); +  	/* lock the list against modification */ -	spin_lock(&nfs_client_lock); -	return seq_list_start_head(&nfs_client_list, *_pos); +	spin_lock(&nn->nfs_client_lock); +	return seq_list_start_head(&nn->nfs_client_list, *_pos);  }  /* @@ -1648,7 +1268,9 @@ static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos)   */  static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos)  { -	return seq_list_next(v, &nfs_client_list, pos); +	struct nfs_net *nn = net_generic(p->private, nfs_net_id); + +	return seq_list_next(v, &nn->nfs_client_list, pos);  }  /* @@ -1656,7 +1278,9 @@ static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos)   */  static void nfs_server_list_stop(struct seq_file *p, void *v)  { -	spin_unlock(&nfs_client_lock); +	struct nfs_net *nn = net_generic(p->private, nfs_net_id); + +	spin_unlock(&nn->nfs_client_lock);  }  /* @@ -1665,9 +1289,10 @@ static void nfs_server_list_stop(struct seq_file *p, void *v)  static int nfs_server_list_show(struct seq_file *m, void *v)  {  	struct nfs_client *clp; +	struct nfs_net *nn = net_generic(m->private, nfs_net_id);  	/* display header on line 1 */ -	if (v == &nfs_client_list) { +	if (v == &nn->nfs_client_list) {  		seq_puts(m, "NV SERVER   PORT USE HOSTNAME\n");  		return 0;  	} @@ -1675,12 +1300,18 @@ static int nfs_server_list_show(struct seq_file *m, void *v)  	/* display one transport per line on subsequent lines */  	clp = list_entry(v, struct nfs_client, cl_share_link); +	/* Check if the client is initialized */ +	if (clp->cl_cons_state != NFS_CS_READY) +		return 0; + +	rcu_read_lock();  	seq_printf(m, "v%u %s %s %3d %s\n",  		   clp->rpc_ops->version,  		   rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR),  		   rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT),  		   atomic_read(&clp->cl_count),  		   clp->cl_hostname); +	rcu_read_unlock();  	return 0;  } @@ -1692,13 +1323,15 @@ static int nfs_volume_list_open(struct inode *inode, struct file *file)  {  	struct seq_file *m;  	int ret; +	struct pid_namespace *pid_ns = file->f_dentry->d_sb->s_fs_info; +	struct net *net = pid_ns->child_reaper->nsproxy->net_ns;  	ret = seq_open(file, &nfs_volume_list_ops);  	if (ret < 0)  		return ret;  	m = file->private_data; -	m->private = PDE(inode)->data; +	m->private = net;  	return 0;  } @@ -1708,9 +1341,11 @@ static int nfs_volume_list_open(struct inode *inode, struct file *file)   */  static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos)  { +	struct nfs_net *nn = net_generic(m->private, nfs_net_id); +  	/* lock the list against modification */ -	spin_lock(&nfs_client_lock); -	return seq_list_start_head(&nfs_volume_list, *_pos); +	spin_lock(&nn->nfs_client_lock); +	return seq_list_start_head(&nn->nfs_volume_list, *_pos);  }  /* @@ -1718,7 +1353,9 @@ static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos)   */  static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos)  { -	return seq_list_next(v, &nfs_volume_list, pos); +	struct nfs_net *nn = net_generic(p->private, nfs_net_id); + +	return seq_list_next(v, &nn->nfs_volume_list, pos);  }  /* @@ -1726,7 +1363,9 @@ static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos)   */  static void nfs_volume_list_stop(struct seq_file *p, void *v)  { -	spin_unlock(&nfs_client_lock); +	struct nfs_net *nn = net_generic(p->private, nfs_net_id); + +	spin_unlock(&nn->nfs_client_lock);  }  /* @@ -1737,9 +1376,10 @@ static int nfs_volume_list_show(struct seq_file *m, void *v)  	struct nfs_server *server;  	struct nfs_client *clp;  	char dev[8], fsid[17]; +	struct nfs_net *nn = net_generic(m->private, nfs_net_id);  	/* display header on line 1 */ -	if (v == &nfs_volume_list) { +	if (v == &nn->nfs_volume_list) {  		seq_puts(m, "NV SERVER   PORT DEV     FSID              FSC\n");  		return 0;  	} @@ -1754,6 +1394,7 @@ static int nfs_volume_list_show(struct seq_file *m, void *v)  		 (unsigned long long) server->fsid.major,  		 (unsigned long long) server->fsid.minor); +	rcu_read_lock();  	seq_printf(m, "v%u %s %s %-7s %-17s %s\n",  		   clp->rpc_ops->version,  		   rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), @@ -1761,6 +1402,7 @@ static int nfs_volume_list_show(struct seq_file *m, void *v)  		   dev,  		   fsid,  		   nfs_server_fscache_state(server)); +	rcu_read_unlock();  	return 0;  }  | 
