162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* client.c: NFS client sharing and management code 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/sched.h> 1262306a36Sopenharmony_ci#include <linux/time.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/mm.h> 1562306a36Sopenharmony_ci#include <linux/string.h> 1662306a36Sopenharmony_ci#include <linux/stat.h> 1762306a36Sopenharmony_ci#include <linux/errno.h> 1862306a36Sopenharmony_ci#include <linux/unistd.h> 1962306a36Sopenharmony_ci#include <linux/sunrpc/addr.h> 2062306a36Sopenharmony_ci#include <linux/sunrpc/clnt.h> 2162306a36Sopenharmony_ci#include <linux/sunrpc/stats.h> 2262306a36Sopenharmony_ci#include <linux/sunrpc/metrics.h> 2362306a36Sopenharmony_ci#include <linux/sunrpc/xprtsock.h> 2462306a36Sopenharmony_ci#include <linux/sunrpc/xprtrdma.h> 2562306a36Sopenharmony_ci#include <linux/nfs_fs.h> 2662306a36Sopenharmony_ci#include <linux/nfs_mount.h> 2762306a36Sopenharmony_ci#include <linux/nfs4_mount.h> 2862306a36Sopenharmony_ci#include <linux/lockd/bind.h> 2962306a36Sopenharmony_ci#include <linux/seq_file.h> 3062306a36Sopenharmony_ci#include <linux/mount.h> 3162306a36Sopenharmony_ci#include <linux/vfs.h> 3262306a36Sopenharmony_ci#include <linux/inet.h> 3362306a36Sopenharmony_ci#include <linux/in6.h> 3462306a36Sopenharmony_ci#include <linux/slab.h> 3562306a36Sopenharmony_ci#include <linux/idr.h> 3662306a36Sopenharmony_ci#include <net/ipv6.h> 3762306a36Sopenharmony_ci#include <linux/nfs_xdr.h> 3862306a36Sopenharmony_ci#include <linux/sunrpc/bc_xprt.h> 3962306a36Sopenharmony_ci#include <linux/nsproxy.h> 4062306a36Sopenharmony_ci#include <linux/pid_namespace.h> 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#include "nfs4_fs.h" 4462306a36Sopenharmony_ci#include "callback.h" 4562306a36Sopenharmony_ci#include "delegation.h" 4662306a36Sopenharmony_ci#include "iostat.h" 4762306a36Sopenharmony_ci#include "internal.h" 4862306a36Sopenharmony_ci#include "fscache.h" 4962306a36Sopenharmony_ci#include "pnfs.h" 5062306a36Sopenharmony_ci#include "nfs.h" 5162306a36Sopenharmony_ci#include "netns.h" 5262306a36Sopenharmony_ci#include "sysfs.h" 5362306a36Sopenharmony_ci#include "nfs42.h" 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define NFSDBG_FACILITY NFSDBG_CLIENT 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq); 5862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(nfs_version_lock); 5962306a36Sopenharmony_cistatic DEFINE_MUTEX(nfs_version_mutex); 6062306a36Sopenharmony_cistatic LIST_HEAD(nfs_versions); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* 6362306a36Sopenharmony_ci * RPC cruft for NFS 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_cistatic const struct rpc_version *nfs_version[5] = { 6662306a36Sopenharmony_ci [2] = NULL, 6762306a36Sopenharmony_ci [3] = NULL, 6862306a36Sopenharmony_ci [4] = NULL, 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ciconst struct rpc_program nfs_program = { 7262306a36Sopenharmony_ci .name = "nfs", 7362306a36Sopenharmony_ci .number = NFS_PROGRAM, 7462306a36Sopenharmony_ci .nrvers = ARRAY_SIZE(nfs_version), 7562306a36Sopenharmony_ci .version = nfs_version, 7662306a36Sopenharmony_ci .stats = &nfs_rpcstat, 7762306a36Sopenharmony_ci .pipe_dir_name = NFS_PIPE_DIRNAME, 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistruct rpc_stat nfs_rpcstat = { 8162306a36Sopenharmony_ci .program = &nfs_program 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic struct nfs_subversion *find_nfs_version(unsigned int version) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct nfs_subversion *nfs; 8762306a36Sopenharmony_ci spin_lock(&nfs_version_lock); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci list_for_each_entry(nfs, &nfs_versions, list) { 9062306a36Sopenharmony_ci if (nfs->rpc_ops->version == version) { 9162306a36Sopenharmony_ci spin_unlock(&nfs_version_lock); 9262306a36Sopenharmony_ci return nfs; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci spin_unlock(&nfs_version_lock); 9762306a36Sopenharmony_ci return ERR_PTR(-EPROTONOSUPPORT); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistruct nfs_subversion *get_nfs_version(unsigned int version) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct nfs_subversion *nfs = find_nfs_version(version); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (IS_ERR(nfs)) { 10562306a36Sopenharmony_ci mutex_lock(&nfs_version_mutex); 10662306a36Sopenharmony_ci request_module("nfsv%d", version); 10762306a36Sopenharmony_ci nfs = find_nfs_version(version); 10862306a36Sopenharmony_ci mutex_unlock(&nfs_version_mutex); 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (!IS_ERR(nfs) && !try_module_get(nfs->owner)) 11262306a36Sopenharmony_ci return ERR_PTR(-EAGAIN); 11362306a36Sopenharmony_ci return nfs; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_civoid put_nfs_version(struct nfs_subversion *nfs) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci module_put(nfs->owner); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_civoid register_nfs_version(struct nfs_subversion *nfs) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci spin_lock(&nfs_version_lock); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci list_add(&nfs->list, &nfs_versions); 12662306a36Sopenharmony_ci nfs_version[nfs->rpc_ops->version] = nfs->rpc_vers; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci spin_unlock(&nfs_version_lock); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(register_nfs_version); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_civoid unregister_nfs_version(struct nfs_subversion *nfs) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci spin_lock(&nfs_version_lock); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci nfs_version[nfs->rpc_ops->version] = NULL; 13762306a36Sopenharmony_ci list_del(&nfs->list); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci spin_unlock(&nfs_version_lock); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(unregister_nfs_version); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/* 14462306a36Sopenharmony_ci * Allocate a shared client record 14562306a36Sopenharmony_ci * 14662306a36Sopenharmony_ci * Since these are allocated/deallocated very rarely, we don't 14762306a36Sopenharmony_ci * bother putting them in a slab cache... 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_cistruct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct nfs_client *clp; 15262306a36Sopenharmony_ci int err = -ENOMEM; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) 15562306a36Sopenharmony_ci goto error_0; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci clp->cl_minorversion = cl_init->minorversion; 15862306a36Sopenharmony_ci clp->cl_nfs_mod = cl_init->nfs_mod; 15962306a36Sopenharmony_ci if (!try_module_get(clp->cl_nfs_mod->owner)) 16062306a36Sopenharmony_ci goto error_dealloc; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci clp->rpc_ops = clp->cl_nfs_mod->rpc_ops; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci refcount_set(&clp->cl_count, 1); 16562306a36Sopenharmony_ci clp->cl_cons_state = NFS_CS_INITING; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci memcpy(&clp->cl_addr, cl_init->addr, cl_init->addrlen); 16862306a36Sopenharmony_ci clp->cl_addrlen = cl_init->addrlen; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (cl_init->hostname) { 17162306a36Sopenharmony_ci err = -ENOMEM; 17262306a36Sopenharmony_ci clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL); 17362306a36Sopenharmony_ci if (!clp->cl_hostname) 17462306a36Sopenharmony_ci goto error_cleanup; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci INIT_LIST_HEAD(&clp->cl_superblocks); 17862306a36Sopenharmony_ci clp->cl_rpcclient = ERR_PTR(-EINVAL); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci clp->cl_flags = cl_init->init_flags; 18162306a36Sopenharmony_ci clp->cl_proto = cl_init->proto; 18262306a36Sopenharmony_ci clp->cl_nconnect = cl_init->nconnect; 18362306a36Sopenharmony_ci clp->cl_max_connect = cl_init->max_connect ? cl_init->max_connect : 1; 18462306a36Sopenharmony_ci clp->cl_net = get_net(cl_init->net); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci clp->cl_principal = "*"; 18762306a36Sopenharmony_ci clp->cl_xprtsec = cl_init->xprtsec; 18862306a36Sopenharmony_ci return clp; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cierror_cleanup: 19162306a36Sopenharmony_ci put_nfs_version(clp->cl_nfs_mod); 19262306a36Sopenharmony_cierror_dealloc: 19362306a36Sopenharmony_ci kfree(clp); 19462306a36Sopenharmony_cierror_0: 19562306a36Sopenharmony_ci return ERR_PTR(err); 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_alloc_client); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NFS_V4) 20062306a36Sopenharmony_cistatic void nfs_cleanup_cb_ident_idr(struct net *net) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct nfs_net *nn = net_generic(net, nfs_net_id); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci idr_destroy(&nn->cb_ident_idr); 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci/* nfs_client_lock held */ 20862306a36Sopenharmony_cistatic void nfs_cb_idr_remove_locked(struct nfs_client *clp) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (clp->cl_cb_ident) 21362306a36Sopenharmony_ci idr_remove(&nn->cb_ident_idr, clp->cl_cb_ident); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic void pnfs_init_server(struct nfs_server *server) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci rpc_init_wait_queue(&server->roc_rpcwaitq, "pNFS ROC"); 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci#else 22262306a36Sopenharmony_cistatic void nfs_cleanup_cb_ident_idr(struct net *net) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic void nfs_cb_idr_remove_locked(struct nfs_client *clp) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic void pnfs_init_server(struct nfs_server *server) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci#endif /* CONFIG_NFS_V4 */ 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci/* 23762306a36Sopenharmony_ci * Destroy a shared client record 23862306a36Sopenharmony_ci */ 23962306a36Sopenharmony_civoid nfs_free_client(struct nfs_client *clp) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci /* -EIO all pending I/O */ 24262306a36Sopenharmony_ci if (!IS_ERR(clp->cl_rpcclient)) 24362306a36Sopenharmony_ci rpc_shutdown_client(clp->cl_rpcclient); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci put_net(clp->cl_net); 24662306a36Sopenharmony_ci put_nfs_version(clp->cl_nfs_mod); 24762306a36Sopenharmony_ci kfree(clp->cl_hostname); 24862306a36Sopenharmony_ci kfree(clp->cl_acceptor); 24962306a36Sopenharmony_ci kfree(clp); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_free_client); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci/* 25462306a36Sopenharmony_ci * Release a reference to a shared client record 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_civoid nfs_put_client(struct nfs_client *clp) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct nfs_net *nn; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (!clp) 26162306a36Sopenharmony_ci return; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci nn = net_generic(clp->cl_net, nfs_net_id); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (refcount_dec_and_lock(&clp->cl_count, &nn->nfs_client_lock)) { 26662306a36Sopenharmony_ci list_del(&clp->cl_share_link); 26762306a36Sopenharmony_ci nfs_cb_idr_remove_locked(clp); 26862306a36Sopenharmony_ci spin_unlock(&nn->nfs_client_lock); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci WARN_ON_ONCE(!list_empty(&clp->cl_superblocks)); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci clp->rpc_ops->free_client(clp); 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_put_client); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci/* 27862306a36Sopenharmony_ci * Find an nfs_client on the list that matches the initialisation data 27962306a36Sopenharmony_ci * that is supplied. 28062306a36Sopenharmony_ci */ 28162306a36Sopenharmony_cistatic struct nfs_client *nfs_match_client(const struct nfs_client_initdata *data) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct nfs_client *clp; 28462306a36Sopenharmony_ci const struct sockaddr *sap = (struct sockaddr *)data->addr; 28562306a36Sopenharmony_ci struct nfs_net *nn = net_generic(data->net, nfs_net_id); 28662306a36Sopenharmony_ci int error; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ciagain: 28962306a36Sopenharmony_ci list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) { 29062306a36Sopenharmony_ci const struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; 29162306a36Sopenharmony_ci /* Don't match clients that failed to initialise properly */ 29262306a36Sopenharmony_ci if (clp->cl_cons_state < 0) 29362306a36Sopenharmony_ci continue; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* If a client is still initializing then we need to wait */ 29662306a36Sopenharmony_ci if (clp->cl_cons_state > NFS_CS_READY) { 29762306a36Sopenharmony_ci refcount_inc(&clp->cl_count); 29862306a36Sopenharmony_ci spin_unlock(&nn->nfs_client_lock); 29962306a36Sopenharmony_ci error = nfs_wait_client_init_complete(clp); 30062306a36Sopenharmony_ci nfs_put_client(clp); 30162306a36Sopenharmony_ci spin_lock(&nn->nfs_client_lock); 30262306a36Sopenharmony_ci if (error < 0) 30362306a36Sopenharmony_ci return ERR_PTR(error); 30462306a36Sopenharmony_ci goto again; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* Different NFS versions cannot share the same nfs_client */ 30862306a36Sopenharmony_ci if (clp->rpc_ops != data->nfs_mod->rpc_ops) 30962306a36Sopenharmony_ci continue; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (clp->cl_proto != data->proto) 31262306a36Sopenharmony_ci continue; 31362306a36Sopenharmony_ci /* Match nfsv4 minorversion */ 31462306a36Sopenharmony_ci if (clp->cl_minorversion != data->minorversion) 31562306a36Sopenharmony_ci continue; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* Match request for a dedicated DS */ 31862306a36Sopenharmony_ci if (test_bit(NFS_CS_DS, &data->init_flags) != 31962306a36Sopenharmony_ci test_bit(NFS_CS_DS, &clp->cl_flags)) 32062306a36Sopenharmony_ci continue; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* Match the full socket address */ 32362306a36Sopenharmony_ci if (!rpc_cmp_addr_port(sap, clap)) 32462306a36Sopenharmony_ci /* Match all xprt_switch full socket addresses */ 32562306a36Sopenharmony_ci if (IS_ERR(clp->cl_rpcclient) || 32662306a36Sopenharmony_ci !rpc_clnt_xprt_switch_has_addr(clp->cl_rpcclient, 32762306a36Sopenharmony_ci sap)) 32862306a36Sopenharmony_ci continue; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* Match the xprt security policy */ 33162306a36Sopenharmony_ci if (clp->cl_xprtsec.policy != data->xprtsec.policy) 33262306a36Sopenharmony_ci continue; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci refcount_inc(&clp->cl_count); 33562306a36Sopenharmony_ci return clp; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci return NULL; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci/* 34162306a36Sopenharmony_ci * Return true if @clp is done initializing, false if still working on it. 34262306a36Sopenharmony_ci * 34362306a36Sopenharmony_ci * Use nfs_client_init_status to check if it was successful. 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_cibool nfs_client_init_is_complete(const struct nfs_client *clp) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci return clp->cl_cons_state <= NFS_CS_READY; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_client_init_is_complete); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci/* 35262306a36Sopenharmony_ci * Return 0 if @clp was successfully initialized, -errno otherwise. 35362306a36Sopenharmony_ci * 35462306a36Sopenharmony_ci * This must be called *after* nfs_client_init_is_complete() returns true, 35562306a36Sopenharmony_ci * otherwise it will pop WARN_ON_ONCE and return -EINVAL 35662306a36Sopenharmony_ci */ 35762306a36Sopenharmony_ciint nfs_client_init_status(const struct nfs_client *clp) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci /* called without checking nfs_client_init_is_complete */ 36062306a36Sopenharmony_ci if (clp->cl_cons_state > NFS_CS_READY) { 36162306a36Sopenharmony_ci WARN_ON_ONCE(1); 36262306a36Sopenharmony_ci return -EINVAL; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci return clp->cl_cons_state; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_client_init_status); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ciint nfs_wait_client_init_complete(const struct nfs_client *clp) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci return wait_event_killable(nfs_client_active_wq, 37162306a36Sopenharmony_ci nfs_client_init_is_complete(clp)); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_wait_client_init_complete); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci/* 37662306a36Sopenharmony_ci * Found an existing client. Make sure it's ready before returning. 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_cistatic struct nfs_client * 37962306a36Sopenharmony_cinfs_found_client(const struct nfs_client_initdata *cl_init, 38062306a36Sopenharmony_ci struct nfs_client *clp) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci int error; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci error = nfs_wait_client_init_complete(clp); 38562306a36Sopenharmony_ci if (error < 0) { 38662306a36Sopenharmony_ci nfs_put_client(clp); 38762306a36Sopenharmony_ci return ERR_PTR(-ERESTARTSYS); 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (clp->cl_cons_state < NFS_CS_READY) { 39162306a36Sopenharmony_ci error = clp->cl_cons_state; 39262306a36Sopenharmony_ci nfs_put_client(clp); 39362306a36Sopenharmony_ci return ERR_PTR(error); 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci smp_rmb(); 39762306a36Sopenharmony_ci return clp; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci/* 40162306a36Sopenharmony_ci * Look up a client by IP address and protocol version 40262306a36Sopenharmony_ci * - creates a new record if one doesn't yet exist 40362306a36Sopenharmony_ci */ 40462306a36Sopenharmony_cistruct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_init) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci struct nfs_client *clp, *new = NULL; 40762306a36Sopenharmony_ci struct nfs_net *nn = net_generic(cl_init->net, nfs_net_id); 40862306a36Sopenharmony_ci const struct nfs_rpc_ops *rpc_ops = cl_init->nfs_mod->rpc_ops; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (cl_init->hostname == NULL) { 41162306a36Sopenharmony_ci WARN_ON(1); 41262306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci /* see if the client already exists */ 41662306a36Sopenharmony_ci do { 41762306a36Sopenharmony_ci spin_lock(&nn->nfs_client_lock); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci clp = nfs_match_client(cl_init); 42062306a36Sopenharmony_ci if (clp) { 42162306a36Sopenharmony_ci spin_unlock(&nn->nfs_client_lock); 42262306a36Sopenharmony_ci if (new) 42362306a36Sopenharmony_ci new->rpc_ops->free_client(new); 42462306a36Sopenharmony_ci if (IS_ERR(clp)) 42562306a36Sopenharmony_ci return clp; 42662306a36Sopenharmony_ci return nfs_found_client(cl_init, clp); 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci if (new) { 42962306a36Sopenharmony_ci list_add_tail(&new->cl_share_link, 43062306a36Sopenharmony_ci &nn->nfs_client_list); 43162306a36Sopenharmony_ci spin_unlock(&nn->nfs_client_lock); 43262306a36Sopenharmony_ci return rpc_ops->init_client(new, cl_init); 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci spin_unlock(&nn->nfs_client_lock); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci new = rpc_ops->alloc_client(cl_init); 43862306a36Sopenharmony_ci } while (!IS_ERR(new)); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci return new; 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_get_client); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci/* 44562306a36Sopenharmony_ci * Mark a server as ready or failed 44662306a36Sopenharmony_ci */ 44762306a36Sopenharmony_civoid nfs_mark_client_ready(struct nfs_client *clp, int state) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci smp_wmb(); 45062306a36Sopenharmony_ci clp->cl_cons_state = state; 45162306a36Sopenharmony_ci wake_up_all(&nfs_client_active_wq); 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_mark_client_ready); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci/* 45662306a36Sopenharmony_ci * Initialise the timeout values for a connection 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_civoid nfs_init_timeout_values(struct rpc_timeout *to, int proto, 45962306a36Sopenharmony_ci int timeo, int retrans) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci to->to_initval = timeo * HZ / 10; 46262306a36Sopenharmony_ci to->to_retries = retrans; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci switch (proto) { 46562306a36Sopenharmony_ci case XPRT_TRANSPORT_TCP: 46662306a36Sopenharmony_ci case XPRT_TRANSPORT_TCP_TLS: 46762306a36Sopenharmony_ci case XPRT_TRANSPORT_RDMA: 46862306a36Sopenharmony_ci if (retrans == NFS_UNSPEC_RETRANS) 46962306a36Sopenharmony_ci to->to_retries = NFS_DEF_TCP_RETRANS; 47062306a36Sopenharmony_ci if (timeo == NFS_UNSPEC_TIMEO || to->to_initval == 0) 47162306a36Sopenharmony_ci to->to_initval = NFS_DEF_TCP_TIMEO * HZ / 10; 47262306a36Sopenharmony_ci if (to->to_initval > NFS_MAX_TCP_TIMEOUT) 47362306a36Sopenharmony_ci to->to_initval = NFS_MAX_TCP_TIMEOUT; 47462306a36Sopenharmony_ci to->to_increment = to->to_initval; 47562306a36Sopenharmony_ci to->to_maxval = to->to_initval + (to->to_increment * to->to_retries); 47662306a36Sopenharmony_ci if (to->to_maxval > NFS_MAX_TCP_TIMEOUT) 47762306a36Sopenharmony_ci to->to_maxval = NFS_MAX_TCP_TIMEOUT; 47862306a36Sopenharmony_ci if (to->to_maxval < to->to_initval) 47962306a36Sopenharmony_ci to->to_maxval = to->to_initval; 48062306a36Sopenharmony_ci to->to_exponential = 0; 48162306a36Sopenharmony_ci break; 48262306a36Sopenharmony_ci case XPRT_TRANSPORT_UDP: 48362306a36Sopenharmony_ci if (retrans == NFS_UNSPEC_RETRANS) 48462306a36Sopenharmony_ci to->to_retries = NFS_DEF_UDP_RETRANS; 48562306a36Sopenharmony_ci if (timeo == NFS_UNSPEC_TIMEO || to->to_initval == 0) 48662306a36Sopenharmony_ci to->to_initval = NFS_DEF_UDP_TIMEO * HZ / 10; 48762306a36Sopenharmony_ci if (to->to_initval > NFS_MAX_UDP_TIMEOUT) 48862306a36Sopenharmony_ci to->to_initval = NFS_MAX_UDP_TIMEOUT; 48962306a36Sopenharmony_ci to->to_maxval = NFS_MAX_UDP_TIMEOUT; 49062306a36Sopenharmony_ci to->to_exponential = 1; 49162306a36Sopenharmony_ci break; 49262306a36Sopenharmony_ci default: 49362306a36Sopenharmony_ci BUG(); 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_init_timeout_values); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci/* 49962306a36Sopenharmony_ci * Create an RPC client handle 50062306a36Sopenharmony_ci */ 50162306a36Sopenharmony_ciint nfs_create_rpc_client(struct nfs_client *clp, 50262306a36Sopenharmony_ci const struct nfs_client_initdata *cl_init, 50362306a36Sopenharmony_ci rpc_authflavor_t flavor) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci struct rpc_clnt *clnt = NULL; 50662306a36Sopenharmony_ci struct rpc_create_args args = { 50762306a36Sopenharmony_ci .net = clp->cl_net, 50862306a36Sopenharmony_ci .protocol = clp->cl_proto, 50962306a36Sopenharmony_ci .nconnect = clp->cl_nconnect, 51062306a36Sopenharmony_ci .address = (struct sockaddr *)&clp->cl_addr, 51162306a36Sopenharmony_ci .addrsize = clp->cl_addrlen, 51262306a36Sopenharmony_ci .timeout = cl_init->timeparms, 51362306a36Sopenharmony_ci .servername = clp->cl_hostname, 51462306a36Sopenharmony_ci .nodename = cl_init->nodename, 51562306a36Sopenharmony_ci .program = &nfs_program, 51662306a36Sopenharmony_ci .version = clp->rpc_ops->version, 51762306a36Sopenharmony_ci .authflavor = flavor, 51862306a36Sopenharmony_ci .cred = cl_init->cred, 51962306a36Sopenharmony_ci .xprtsec = cl_init->xprtsec, 52062306a36Sopenharmony_ci .connect_timeout = cl_init->connect_timeout, 52162306a36Sopenharmony_ci .reconnect_timeout = cl_init->reconnect_timeout, 52262306a36Sopenharmony_ci }; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (test_bit(NFS_CS_DISCRTRY, &clp->cl_flags)) 52562306a36Sopenharmony_ci args.flags |= RPC_CLNT_CREATE_DISCRTRY; 52662306a36Sopenharmony_ci if (test_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags)) 52762306a36Sopenharmony_ci args.flags |= RPC_CLNT_CREATE_NO_RETRANS_TIMEOUT; 52862306a36Sopenharmony_ci if (test_bit(NFS_CS_NORESVPORT, &clp->cl_flags)) 52962306a36Sopenharmony_ci args.flags |= RPC_CLNT_CREATE_NONPRIVPORT; 53062306a36Sopenharmony_ci if (test_bit(NFS_CS_INFINITE_SLOTS, &clp->cl_flags)) 53162306a36Sopenharmony_ci args.flags |= RPC_CLNT_CREATE_INFINITE_SLOTS; 53262306a36Sopenharmony_ci if (test_bit(NFS_CS_NOPING, &clp->cl_flags)) 53362306a36Sopenharmony_ci args.flags |= RPC_CLNT_CREATE_NOPING; 53462306a36Sopenharmony_ci if (test_bit(NFS_CS_REUSEPORT, &clp->cl_flags)) 53562306a36Sopenharmony_ci args.flags |= RPC_CLNT_CREATE_REUSEPORT; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (!IS_ERR(clp->cl_rpcclient)) 53862306a36Sopenharmony_ci return 0; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci clnt = rpc_create(&args); 54162306a36Sopenharmony_ci if (IS_ERR(clnt)) { 54262306a36Sopenharmony_ci dprintk("%s: cannot create RPC client. Error = %ld\n", 54362306a36Sopenharmony_ci __func__, PTR_ERR(clnt)); 54462306a36Sopenharmony_ci return PTR_ERR(clnt); 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci clnt->cl_principal = clp->cl_principal; 54862306a36Sopenharmony_ci clp->cl_rpcclient = clnt; 54962306a36Sopenharmony_ci clnt->cl_max_connect = clp->cl_max_connect; 55062306a36Sopenharmony_ci return 0; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_create_rpc_client); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci/* 55562306a36Sopenharmony_ci * Version 2 or 3 client destruction 55662306a36Sopenharmony_ci */ 55762306a36Sopenharmony_cistatic void nfs_destroy_server(struct nfs_server *server) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci if (server->nlm_host) 56062306a36Sopenharmony_ci nlmclnt_done(server->nlm_host); 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci/* 56462306a36Sopenharmony_ci * Version 2 or 3 lockd setup 56562306a36Sopenharmony_ci */ 56662306a36Sopenharmony_cistatic int nfs_start_lockd(struct nfs_server *server) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci struct nlm_host *host; 56962306a36Sopenharmony_ci struct nfs_client *clp = server->nfs_client; 57062306a36Sopenharmony_ci struct nlmclnt_initdata nlm_init = { 57162306a36Sopenharmony_ci .hostname = clp->cl_hostname, 57262306a36Sopenharmony_ci .address = (struct sockaddr *)&clp->cl_addr, 57362306a36Sopenharmony_ci .addrlen = clp->cl_addrlen, 57462306a36Sopenharmony_ci .nfs_version = clp->rpc_ops->version, 57562306a36Sopenharmony_ci .noresvport = server->flags & NFS_MOUNT_NORESVPORT ? 57662306a36Sopenharmony_ci 1 : 0, 57762306a36Sopenharmony_ci .net = clp->cl_net, 57862306a36Sopenharmony_ci .nlmclnt_ops = clp->cl_nfs_mod->rpc_ops->nlmclnt_ops, 57962306a36Sopenharmony_ci .cred = server->cred, 58062306a36Sopenharmony_ci }; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (nlm_init.nfs_version > 3) 58362306a36Sopenharmony_ci return 0; 58462306a36Sopenharmony_ci if ((server->flags & NFS_MOUNT_LOCAL_FLOCK) && 58562306a36Sopenharmony_ci (server->flags & NFS_MOUNT_LOCAL_FCNTL)) 58662306a36Sopenharmony_ci return 0; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci switch (clp->cl_proto) { 58962306a36Sopenharmony_ci default: 59062306a36Sopenharmony_ci nlm_init.protocol = IPPROTO_TCP; 59162306a36Sopenharmony_ci break; 59262306a36Sopenharmony_ci#ifndef CONFIG_NFS_DISABLE_UDP_SUPPORT 59362306a36Sopenharmony_ci case XPRT_TRANSPORT_UDP: 59462306a36Sopenharmony_ci nlm_init.protocol = IPPROTO_UDP; 59562306a36Sopenharmony_ci#endif 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci host = nlmclnt_init(&nlm_init); 59962306a36Sopenharmony_ci if (IS_ERR(host)) 60062306a36Sopenharmony_ci return PTR_ERR(host); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci server->nlm_host = host; 60362306a36Sopenharmony_ci server->destroy = nfs_destroy_server; 60462306a36Sopenharmony_ci nfs_sysfs_link_rpc_client(server, nlmclnt_rpc_clnt(host), NULL); 60562306a36Sopenharmony_ci return 0; 60662306a36Sopenharmony_ci} 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci/* 60962306a36Sopenharmony_ci * Create a general RPC client 61062306a36Sopenharmony_ci */ 61162306a36Sopenharmony_ciint nfs_init_server_rpcclient(struct nfs_server *server, 61262306a36Sopenharmony_ci const struct rpc_timeout *timeo, 61362306a36Sopenharmony_ci rpc_authflavor_t pseudoflavour) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci struct nfs_client *clp = server->nfs_client; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci server->client = rpc_clone_client_set_auth(clp->cl_rpcclient, 61862306a36Sopenharmony_ci pseudoflavour); 61962306a36Sopenharmony_ci if (IS_ERR(server->client)) { 62062306a36Sopenharmony_ci dprintk("%s: couldn't create rpc_client!\n", __func__); 62162306a36Sopenharmony_ci return PTR_ERR(server->client); 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci memcpy(&server->client->cl_timeout_default, 62562306a36Sopenharmony_ci timeo, 62662306a36Sopenharmony_ci sizeof(server->client->cl_timeout_default)); 62762306a36Sopenharmony_ci server->client->cl_timeout = &server->client->cl_timeout_default; 62862306a36Sopenharmony_ci server->client->cl_softrtry = 0; 62962306a36Sopenharmony_ci if (server->flags & NFS_MOUNT_SOFTERR) 63062306a36Sopenharmony_ci server->client->cl_softerr = 1; 63162306a36Sopenharmony_ci if (server->flags & NFS_MOUNT_SOFT) 63262306a36Sopenharmony_ci server->client->cl_softrtry = 1; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci nfs_sysfs_link_rpc_client(server, server->client, NULL); 63562306a36Sopenharmony_ci return 0; 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_init_server_rpcclient); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci/** 64062306a36Sopenharmony_ci * nfs_init_client - Initialise an NFS2 or NFS3 client 64162306a36Sopenharmony_ci * 64262306a36Sopenharmony_ci * @clp: nfs_client to initialise 64362306a36Sopenharmony_ci * @cl_init: Initialisation parameters 64462306a36Sopenharmony_ci * 64562306a36Sopenharmony_ci * Returns pointer to an NFS client, or an ERR_PTR value. 64662306a36Sopenharmony_ci */ 64762306a36Sopenharmony_cistruct nfs_client *nfs_init_client(struct nfs_client *clp, 64862306a36Sopenharmony_ci const struct nfs_client_initdata *cl_init) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci int error; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci /* the client is already initialised */ 65362306a36Sopenharmony_ci if (clp->cl_cons_state == NFS_CS_READY) 65462306a36Sopenharmony_ci return clp; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci /* 65762306a36Sopenharmony_ci * Create a client RPC handle for doing FSSTAT with UNIX auth only 65862306a36Sopenharmony_ci * - RFC 2623, sec 2.3.2 65962306a36Sopenharmony_ci */ 66062306a36Sopenharmony_ci error = nfs_create_rpc_client(clp, cl_init, RPC_AUTH_UNIX); 66162306a36Sopenharmony_ci nfs_mark_client_ready(clp, error == 0 ? NFS_CS_READY : error); 66262306a36Sopenharmony_ci if (error < 0) { 66362306a36Sopenharmony_ci nfs_put_client(clp); 66462306a36Sopenharmony_ci clp = ERR_PTR(error); 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci return clp; 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_init_client); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci/* 67162306a36Sopenharmony_ci * Create a version 2 or 3 client 67262306a36Sopenharmony_ci */ 67362306a36Sopenharmony_cistatic int nfs_init_server(struct nfs_server *server, 67462306a36Sopenharmony_ci const struct fs_context *fc) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci const struct nfs_fs_context *ctx = nfs_fc2context(fc); 67762306a36Sopenharmony_ci struct rpc_timeout timeparms; 67862306a36Sopenharmony_ci struct nfs_client_initdata cl_init = { 67962306a36Sopenharmony_ci .hostname = ctx->nfs_server.hostname, 68062306a36Sopenharmony_ci .addr = &ctx->nfs_server._address, 68162306a36Sopenharmony_ci .addrlen = ctx->nfs_server.addrlen, 68262306a36Sopenharmony_ci .nfs_mod = ctx->nfs_mod, 68362306a36Sopenharmony_ci .proto = ctx->nfs_server.protocol, 68462306a36Sopenharmony_ci .net = fc->net_ns, 68562306a36Sopenharmony_ci .timeparms = &timeparms, 68662306a36Sopenharmony_ci .cred = server->cred, 68762306a36Sopenharmony_ci .nconnect = ctx->nfs_server.nconnect, 68862306a36Sopenharmony_ci .init_flags = (1UL << NFS_CS_REUSEPORT), 68962306a36Sopenharmony_ci .xprtsec = ctx->xprtsec, 69062306a36Sopenharmony_ci }; 69162306a36Sopenharmony_ci struct nfs_client *clp; 69262306a36Sopenharmony_ci int error; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci nfs_init_timeout_values(&timeparms, ctx->nfs_server.protocol, 69562306a36Sopenharmony_ci ctx->timeo, ctx->retrans); 69662306a36Sopenharmony_ci if (ctx->flags & NFS_MOUNT_NORESVPORT) 69762306a36Sopenharmony_ci set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* Allocate or find a client reference we can use */ 70062306a36Sopenharmony_ci clp = nfs_get_client(&cl_init); 70162306a36Sopenharmony_ci if (IS_ERR(clp)) 70262306a36Sopenharmony_ci return PTR_ERR(clp); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci server->nfs_client = clp; 70562306a36Sopenharmony_ci nfs_sysfs_add_server(server); 70662306a36Sopenharmony_ci nfs_sysfs_link_rpc_client(server, clp->cl_rpcclient, "_state"); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci /* Initialise the client representation from the mount data */ 70962306a36Sopenharmony_ci server->flags = ctx->flags; 71062306a36Sopenharmony_ci server->options = ctx->options; 71162306a36Sopenharmony_ci server->caps |= NFS_CAP_HARDLINKS | NFS_CAP_SYMLINKS; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci switch (clp->rpc_ops->version) { 71462306a36Sopenharmony_ci case 2: 71562306a36Sopenharmony_ci server->fattr_valid = NFS_ATTR_FATTR_V2; 71662306a36Sopenharmony_ci break; 71762306a36Sopenharmony_ci case 3: 71862306a36Sopenharmony_ci server->fattr_valid = NFS_ATTR_FATTR_V3; 71962306a36Sopenharmony_ci break; 72062306a36Sopenharmony_ci default: 72162306a36Sopenharmony_ci server->fattr_valid = NFS_ATTR_FATTR_V4; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if (ctx->rsize) 72562306a36Sopenharmony_ci server->rsize = nfs_io_size(ctx->rsize, clp->cl_proto); 72662306a36Sopenharmony_ci if (ctx->wsize) 72762306a36Sopenharmony_ci server->wsize = nfs_io_size(ctx->wsize, clp->cl_proto); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci server->acregmin = ctx->acregmin * HZ; 73062306a36Sopenharmony_ci server->acregmax = ctx->acregmax * HZ; 73162306a36Sopenharmony_ci server->acdirmin = ctx->acdirmin * HZ; 73262306a36Sopenharmony_ci server->acdirmax = ctx->acdirmax * HZ; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci /* Start lockd here, before we might error out */ 73562306a36Sopenharmony_ci error = nfs_start_lockd(server); 73662306a36Sopenharmony_ci if (error < 0) 73762306a36Sopenharmony_ci goto error; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci server->port = ctx->nfs_server.port; 74062306a36Sopenharmony_ci server->auth_info = ctx->auth_info; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci error = nfs_init_server_rpcclient(server, &timeparms, 74362306a36Sopenharmony_ci ctx->selected_flavor); 74462306a36Sopenharmony_ci if (error < 0) 74562306a36Sopenharmony_ci goto error; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci /* Preserve the values of mount_server-related mount options */ 74862306a36Sopenharmony_ci if (ctx->mount_server.addrlen) { 74962306a36Sopenharmony_ci memcpy(&server->mountd_address, &ctx->mount_server.address, 75062306a36Sopenharmony_ci ctx->mount_server.addrlen); 75162306a36Sopenharmony_ci server->mountd_addrlen = ctx->mount_server.addrlen; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci server->mountd_version = ctx->mount_server.version; 75462306a36Sopenharmony_ci server->mountd_port = ctx->mount_server.port; 75562306a36Sopenharmony_ci server->mountd_protocol = ctx->mount_server.protocol; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci server->namelen = ctx->namlen; 75862306a36Sopenharmony_ci return 0; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_cierror: 76162306a36Sopenharmony_ci server->nfs_client = NULL; 76262306a36Sopenharmony_ci nfs_put_client(clp); 76362306a36Sopenharmony_ci return error; 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci/* 76762306a36Sopenharmony_ci * Load up the server record from information gained in an fsinfo record 76862306a36Sopenharmony_ci */ 76962306a36Sopenharmony_cistatic void nfs_server_set_fsinfo(struct nfs_server *server, 77062306a36Sopenharmony_ci struct nfs_fsinfo *fsinfo) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci struct nfs_client *clp = server->nfs_client; 77362306a36Sopenharmony_ci unsigned long max_rpc_payload, raw_max_rpc_payload; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci /* Work out a lot of parameters */ 77662306a36Sopenharmony_ci if (server->rsize == 0) 77762306a36Sopenharmony_ci server->rsize = nfs_io_size(fsinfo->rtpref, clp->cl_proto); 77862306a36Sopenharmony_ci if (server->wsize == 0) 77962306a36Sopenharmony_ci server->wsize = nfs_io_size(fsinfo->wtpref, clp->cl_proto); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci if (fsinfo->rtmax >= 512 && server->rsize > fsinfo->rtmax) 78262306a36Sopenharmony_ci server->rsize = nfs_io_size(fsinfo->rtmax, clp->cl_proto); 78362306a36Sopenharmony_ci if (fsinfo->wtmax >= 512 && server->wsize > fsinfo->wtmax) 78462306a36Sopenharmony_ci server->wsize = nfs_io_size(fsinfo->wtmax, clp->cl_proto); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci raw_max_rpc_payload = rpc_max_payload(server->client); 78762306a36Sopenharmony_ci max_rpc_payload = nfs_block_size(raw_max_rpc_payload, NULL); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if (server->rsize > max_rpc_payload) 79062306a36Sopenharmony_ci server->rsize = max_rpc_payload; 79162306a36Sopenharmony_ci if (server->rsize > NFS_MAX_FILE_IO_SIZE) 79262306a36Sopenharmony_ci server->rsize = NFS_MAX_FILE_IO_SIZE; 79362306a36Sopenharmony_ci server->rpages = (server->rsize + PAGE_SIZE - 1) >> PAGE_SHIFT; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci if (server->wsize > max_rpc_payload) 79662306a36Sopenharmony_ci server->wsize = max_rpc_payload; 79762306a36Sopenharmony_ci if (server->wsize > NFS_MAX_FILE_IO_SIZE) 79862306a36Sopenharmony_ci server->wsize = NFS_MAX_FILE_IO_SIZE; 79962306a36Sopenharmony_ci server->wpages = (server->wsize + PAGE_SIZE - 1) >> PAGE_SHIFT; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci server->dtsize = nfs_block_size(fsinfo->dtpref, NULL); 80462306a36Sopenharmony_ci if (server->dtsize > NFS_MAX_FILE_IO_SIZE) 80562306a36Sopenharmony_ci server->dtsize = NFS_MAX_FILE_IO_SIZE; 80662306a36Sopenharmony_ci if (server->dtsize > server->rsize) 80762306a36Sopenharmony_ci server->dtsize = server->rsize; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci if (server->flags & NFS_MOUNT_NOAC) { 81062306a36Sopenharmony_ci server->acregmin = server->acregmax = 0; 81162306a36Sopenharmony_ci server->acdirmin = server->acdirmax = 0; 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci server->maxfilesize = fsinfo->maxfilesize; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci server->time_delta = fsinfo->time_delta; 81762306a36Sopenharmony_ci server->change_attr_type = fsinfo->change_attr_type; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci server->clone_blksize = fsinfo->clone_blksize; 82062306a36Sopenharmony_ci /* We're airborne Set socket buffersize */ 82162306a36Sopenharmony_ci rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci#ifdef CONFIG_NFS_V4_2 82462306a36Sopenharmony_ci /* 82562306a36Sopenharmony_ci * Defaults until limited by the session parameters. 82662306a36Sopenharmony_ci */ 82762306a36Sopenharmony_ci server->gxasize = min_t(unsigned int, raw_max_rpc_payload, 82862306a36Sopenharmony_ci XATTR_SIZE_MAX); 82962306a36Sopenharmony_ci server->sxasize = min_t(unsigned int, raw_max_rpc_payload, 83062306a36Sopenharmony_ci XATTR_SIZE_MAX); 83162306a36Sopenharmony_ci server->lxasize = min_t(unsigned int, raw_max_rpc_payload, 83262306a36Sopenharmony_ci nfs42_listxattr_xdrsize(XATTR_LIST_MAX)); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci if (fsinfo->xattr_support) 83562306a36Sopenharmony_ci server->caps |= NFS_CAP_XATTR; 83662306a36Sopenharmony_ci#endif 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci/* 84062306a36Sopenharmony_ci * Probe filesystem information, including the FSID on v2/v3 84162306a36Sopenharmony_ci */ 84262306a36Sopenharmony_cistatic int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci struct nfs_fsinfo fsinfo; 84562306a36Sopenharmony_ci struct nfs_client *clp = server->nfs_client; 84662306a36Sopenharmony_ci int error; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (clp->rpc_ops->set_capabilities != NULL) { 84962306a36Sopenharmony_ci error = clp->rpc_ops->set_capabilities(server, mntfh); 85062306a36Sopenharmony_ci if (error < 0) 85162306a36Sopenharmony_ci return error; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci fsinfo.fattr = fattr; 85562306a36Sopenharmony_ci fsinfo.nlayouttypes = 0; 85662306a36Sopenharmony_ci memset(fsinfo.layouttype, 0, sizeof(fsinfo.layouttype)); 85762306a36Sopenharmony_ci error = clp->rpc_ops->fsinfo(server, mntfh, &fsinfo); 85862306a36Sopenharmony_ci if (error < 0) 85962306a36Sopenharmony_ci return error; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci nfs_server_set_fsinfo(server, &fsinfo); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci /* Get some general file system info */ 86462306a36Sopenharmony_ci if (server->namelen == 0) { 86562306a36Sopenharmony_ci struct nfs_pathconf pathinfo; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci pathinfo.fattr = fattr; 86862306a36Sopenharmony_ci nfs_fattr_init(fattr); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0) 87162306a36Sopenharmony_ci server->namelen = pathinfo.max_namelen; 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci if (clp->rpc_ops->discover_trunking != NULL && 87562306a36Sopenharmony_ci (server->caps & NFS_CAP_FS_LOCATIONS && 87662306a36Sopenharmony_ci (server->flags & NFS_MOUNT_TRUNK_DISCOVERY))) { 87762306a36Sopenharmony_ci error = clp->rpc_ops->discover_trunking(server, mntfh); 87862306a36Sopenharmony_ci if (error < 0) 87962306a36Sopenharmony_ci return error; 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci return 0; 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci/* 88662306a36Sopenharmony_ci * Grab the destination's particulars, including lease expiry time. 88762306a36Sopenharmony_ci * 88862306a36Sopenharmony_ci * Returns zero if probe succeeded and retrieved FSID matches the FSID 88962306a36Sopenharmony_ci * we have cached. 89062306a36Sopenharmony_ci */ 89162306a36Sopenharmony_ciint nfs_probe_server(struct nfs_server *server, struct nfs_fh *mntfh) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci struct nfs_fattr *fattr; 89462306a36Sopenharmony_ci int error; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci fattr = nfs_alloc_fattr(); 89762306a36Sopenharmony_ci if (fattr == NULL) 89862306a36Sopenharmony_ci return -ENOMEM; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci /* Sanity: the probe won't work if the destination server 90162306a36Sopenharmony_ci * does not recognize the migrated FH. */ 90262306a36Sopenharmony_ci error = nfs_probe_fsinfo(server, mntfh, fattr); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci nfs_free_fattr(fattr); 90562306a36Sopenharmony_ci return error; 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_probe_server); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci/* 91062306a36Sopenharmony_ci * Copy useful information when duplicating a server record 91162306a36Sopenharmony_ci */ 91262306a36Sopenharmony_civoid nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci target->flags = source->flags; 91562306a36Sopenharmony_ci target->rsize = source->rsize; 91662306a36Sopenharmony_ci target->wsize = source->wsize; 91762306a36Sopenharmony_ci target->acregmin = source->acregmin; 91862306a36Sopenharmony_ci target->acregmax = source->acregmax; 91962306a36Sopenharmony_ci target->acdirmin = source->acdirmin; 92062306a36Sopenharmony_ci target->acdirmax = source->acdirmax; 92162306a36Sopenharmony_ci target->caps = source->caps; 92262306a36Sopenharmony_ci target->options = source->options; 92362306a36Sopenharmony_ci target->auth_info = source->auth_info; 92462306a36Sopenharmony_ci target->port = source->port; 92562306a36Sopenharmony_ci} 92662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_server_copy_userdata); 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_civoid nfs_server_insert_lists(struct nfs_server *server) 92962306a36Sopenharmony_ci{ 93062306a36Sopenharmony_ci struct nfs_client *clp = server->nfs_client; 93162306a36Sopenharmony_ci struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci spin_lock(&nn->nfs_client_lock); 93462306a36Sopenharmony_ci list_add_tail_rcu(&server->client_link, &clp->cl_superblocks); 93562306a36Sopenharmony_ci list_add_tail(&server->master_link, &nn->nfs_volume_list); 93662306a36Sopenharmony_ci clear_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state); 93762306a36Sopenharmony_ci spin_unlock(&nn->nfs_client_lock); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_server_insert_lists); 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_civoid nfs_server_remove_lists(struct nfs_server *server) 94362306a36Sopenharmony_ci{ 94462306a36Sopenharmony_ci struct nfs_client *clp = server->nfs_client; 94562306a36Sopenharmony_ci struct nfs_net *nn; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci if (clp == NULL) 94862306a36Sopenharmony_ci return; 94962306a36Sopenharmony_ci nn = net_generic(clp->cl_net, nfs_net_id); 95062306a36Sopenharmony_ci spin_lock(&nn->nfs_client_lock); 95162306a36Sopenharmony_ci list_del_rcu(&server->client_link); 95262306a36Sopenharmony_ci if (list_empty(&clp->cl_superblocks)) 95362306a36Sopenharmony_ci set_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state); 95462306a36Sopenharmony_ci list_del(&server->master_link); 95562306a36Sopenharmony_ci spin_unlock(&nn->nfs_client_lock); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci synchronize_rcu(); 95862306a36Sopenharmony_ci} 95962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_server_remove_lists); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_cistatic DEFINE_IDA(s_sysfs_ids); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci/* 96462306a36Sopenharmony_ci * Allocate and initialise a server record 96562306a36Sopenharmony_ci */ 96662306a36Sopenharmony_cistruct nfs_server *nfs_alloc_server(void) 96762306a36Sopenharmony_ci{ 96862306a36Sopenharmony_ci struct nfs_server *server; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL); 97162306a36Sopenharmony_ci if (!server) 97262306a36Sopenharmony_ci return NULL; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci server->s_sysfs_id = ida_alloc(&s_sysfs_ids, GFP_KERNEL); 97562306a36Sopenharmony_ci if (server->s_sysfs_id < 0) { 97662306a36Sopenharmony_ci kfree(server); 97762306a36Sopenharmony_ci return NULL; 97862306a36Sopenharmony_ci } 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci server->client = server->client_acl = ERR_PTR(-EINVAL); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci /* Zero out the NFS state stuff */ 98362306a36Sopenharmony_ci INIT_LIST_HEAD(&server->client_link); 98462306a36Sopenharmony_ci INIT_LIST_HEAD(&server->master_link); 98562306a36Sopenharmony_ci INIT_LIST_HEAD(&server->delegations); 98662306a36Sopenharmony_ci INIT_LIST_HEAD(&server->layouts); 98762306a36Sopenharmony_ci INIT_LIST_HEAD(&server->state_owners_lru); 98862306a36Sopenharmony_ci INIT_LIST_HEAD(&server->ss_copies); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci atomic_set(&server->active, 0); 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci server->io_stats = nfs_alloc_iostats(); 99362306a36Sopenharmony_ci if (!server->io_stats) { 99462306a36Sopenharmony_ci kfree(server); 99562306a36Sopenharmony_ci return NULL; 99662306a36Sopenharmony_ci } 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci server->change_attr_type = NFS4_CHANGE_TYPE_IS_UNDEFINED; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci ida_init(&server->openowner_id); 100162306a36Sopenharmony_ci ida_init(&server->lockowner_id); 100262306a36Sopenharmony_ci pnfs_init_server(server); 100362306a36Sopenharmony_ci rpc_init_wait_queue(&server->uoc_rpcwaitq, "NFS UOC"); 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci return server; 100662306a36Sopenharmony_ci} 100762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_alloc_server); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci/* 101062306a36Sopenharmony_ci * Free up a server record 101162306a36Sopenharmony_ci */ 101262306a36Sopenharmony_civoid nfs_free_server(struct nfs_server *server) 101362306a36Sopenharmony_ci{ 101462306a36Sopenharmony_ci nfs_server_remove_lists(server); 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci if (server->destroy != NULL) 101762306a36Sopenharmony_ci server->destroy(server); 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci if (!IS_ERR(server->client_acl)) 102062306a36Sopenharmony_ci rpc_shutdown_client(server->client_acl); 102162306a36Sopenharmony_ci if (!IS_ERR(server->client)) 102262306a36Sopenharmony_ci rpc_shutdown_client(server->client); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci nfs_put_client(server->nfs_client); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci if (server->kobj.state_initialized) { 102762306a36Sopenharmony_ci nfs_sysfs_remove_server(server); 102862306a36Sopenharmony_ci kobject_put(&server->kobj); 102962306a36Sopenharmony_ci } 103062306a36Sopenharmony_ci ida_free(&s_sysfs_ids, server->s_sysfs_id); 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci ida_destroy(&server->lockowner_id); 103362306a36Sopenharmony_ci ida_destroy(&server->openowner_id); 103462306a36Sopenharmony_ci nfs_free_iostats(server->io_stats); 103562306a36Sopenharmony_ci put_cred(server->cred); 103662306a36Sopenharmony_ci kfree(server); 103762306a36Sopenharmony_ci nfs_release_automount_timer(); 103862306a36Sopenharmony_ci} 103962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_free_server); 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci/* 104262306a36Sopenharmony_ci * Create a version 2 or 3 volume record 104362306a36Sopenharmony_ci * - keyed on server and FSID 104462306a36Sopenharmony_ci */ 104562306a36Sopenharmony_cistruct nfs_server *nfs_create_server(struct fs_context *fc) 104662306a36Sopenharmony_ci{ 104762306a36Sopenharmony_ci struct nfs_fs_context *ctx = nfs_fc2context(fc); 104862306a36Sopenharmony_ci struct nfs_server *server; 104962306a36Sopenharmony_ci struct nfs_fattr *fattr; 105062306a36Sopenharmony_ci int error; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci server = nfs_alloc_server(); 105362306a36Sopenharmony_ci if (!server) 105462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci server->cred = get_cred(fc->cred); 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci error = -ENOMEM; 105962306a36Sopenharmony_ci fattr = nfs_alloc_fattr(); 106062306a36Sopenharmony_ci if (fattr == NULL) 106162306a36Sopenharmony_ci goto error; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci /* Get a client representation */ 106462306a36Sopenharmony_ci error = nfs_init_server(server, fc); 106562306a36Sopenharmony_ci if (error < 0) 106662306a36Sopenharmony_ci goto error; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci /* Probe the root fh to retrieve its FSID */ 106962306a36Sopenharmony_ci error = nfs_probe_fsinfo(server, ctx->mntfh, fattr); 107062306a36Sopenharmony_ci if (error < 0) 107162306a36Sopenharmony_ci goto error; 107262306a36Sopenharmony_ci if (server->nfs_client->rpc_ops->version == 3) { 107362306a36Sopenharmony_ci if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) 107462306a36Sopenharmony_ci server->namelen = NFS3_MAXNAMLEN; 107562306a36Sopenharmony_ci if (!(ctx->flags & NFS_MOUNT_NORDIRPLUS)) 107662306a36Sopenharmony_ci server->caps |= NFS_CAP_READDIRPLUS; 107762306a36Sopenharmony_ci } else { 107862306a36Sopenharmony_ci if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN) 107962306a36Sopenharmony_ci server->namelen = NFS2_MAXNAMLEN; 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci if (!(fattr->valid & NFS_ATTR_FATTR)) { 108362306a36Sopenharmony_ci error = ctx->nfs_mod->rpc_ops->getattr(server, ctx->mntfh, 108462306a36Sopenharmony_ci fattr, NULL); 108562306a36Sopenharmony_ci if (error < 0) { 108662306a36Sopenharmony_ci dprintk("nfs_create_server: getattr error = %d\n", -error); 108762306a36Sopenharmony_ci goto error; 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci } 109062306a36Sopenharmony_ci memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid)); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci dprintk("Server FSID: %llx:%llx\n", 109362306a36Sopenharmony_ci (unsigned long long) server->fsid.major, 109462306a36Sopenharmony_ci (unsigned long long) server->fsid.minor); 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci nfs_server_insert_lists(server); 109762306a36Sopenharmony_ci server->mount_time = jiffies; 109862306a36Sopenharmony_ci nfs_free_fattr(fattr); 109962306a36Sopenharmony_ci return server; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_cierror: 110262306a36Sopenharmony_ci nfs_free_fattr(fattr); 110362306a36Sopenharmony_ci nfs_free_server(server); 110462306a36Sopenharmony_ci return ERR_PTR(error); 110562306a36Sopenharmony_ci} 110662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_create_server); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci/* 110962306a36Sopenharmony_ci * Clone an NFS2, NFS3 or NFS4 server record 111062306a36Sopenharmony_ci */ 111162306a36Sopenharmony_cistruct nfs_server *nfs_clone_server(struct nfs_server *source, 111262306a36Sopenharmony_ci struct nfs_fh *fh, 111362306a36Sopenharmony_ci struct nfs_fattr *fattr, 111462306a36Sopenharmony_ci rpc_authflavor_t flavor) 111562306a36Sopenharmony_ci{ 111662306a36Sopenharmony_ci struct nfs_server *server; 111762306a36Sopenharmony_ci int error; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci server = nfs_alloc_server(); 112062306a36Sopenharmony_ci if (!server) 112162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci server->cred = get_cred(source->cred); 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci /* Copy data from the source */ 112662306a36Sopenharmony_ci server->nfs_client = source->nfs_client; 112762306a36Sopenharmony_ci server->destroy = source->destroy; 112862306a36Sopenharmony_ci refcount_inc(&server->nfs_client->cl_count); 112962306a36Sopenharmony_ci nfs_server_copy_userdata(server, source); 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci server->fsid = fattr->fsid; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci nfs_sysfs_add_server(server); 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci nfs_sysfs_link_rpc_client(server, 113662306a36Sopenharmony_ci server->nfs_client->cl_rpcclient, "_state"); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci error = nfs_init_server_rpcclient(server, 113962306a36Sopenharmony_ci source->client->cl_timeout, 114062306a36Sopenharmony_ci flavor); 114162306a36Sopenharmony_ci if (error < 0) 114262306a36Sopenharmony_ci goto out_free_server; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci /* probe the filesystem info for this server filesystem */ 114562306a36Sopenharmony_ci error = nfs_probe_server(server, fh); 114662306a36Sopenharmony_ci if (error < 0) 114762306a36Sopenharmony_ci goto out_free_server; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 115062306a36Sopenharmony_ci server->namelen = NFS4_MAXNAMLEN; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci error = nfs_start_lockd(server); 115362306a36Sopenharmony_ci if (error < 0) 115462306a36Sopenharmony_ci goto out_free_server; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci nfs_server_insert_lists(server); 115762306a36Sopenharmony_ci server->mount_time = jiffies; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci return server; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ciout_free_server: 116262306a36Sopenharmony_ci nfs_free_server(server); 116362306a36Sopenharmony_ci return ERR_PTR(error); 116462306a36Sopenharmony_ci} 116562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_clone_server); 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_civoid nfs_clients_init(struct net *net) 116862306a36Sopenharmony_ci{ 116962306a36Sopenharmony_ci struct nfs_net *nn = net_generic(net, nfs_net_id); 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci INIT_LIST_HEAD(&nn->nfs_client_list); 117262306a36Sopenharmony_ci INIT_LIST_HEAD(&nn->nfs_volume_list); 117362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NFS_V4) 117462306a36Sopenharmony_ci idr_init(&nn->cb_ident_idr); 117562306a36Sopenharmony_ci#endif 117662306a36Sopenharmony_ci spin_lock_init(&nn->nfs_client_lock); 117762306a36Sopenharmony_ci nn->boot_time = ktime_get_real(); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci nfs_netns_sysfs_setup(nn, net); 118062306a36Sopenharmony_ci} 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_civoid nfs_clients_exit(struct net *net) 118362306a36Sopenharmony_ci{ 118462306a36Sopenharmony_ci struct nfs_net *nn = net_generic(net, nfs_net_id); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci nfs_netns_sysfs_destroy(nn); 118762306a36Sopenharmony_ci nfs_cleanup_cb_ident_idr(net); 118862306a36Sopenharmony_ci WARN_ON_ONCE(!list_empty(&nn->nfs_client_list)); 118962306a36Sopenharmony_ci WARN_ON_ONCE(!list_empty(&nn->nfs_volume_list)); 119062306a36Sopenharmony_ci} 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 119362306a36Sopenharmony_cistatic void *nfs_server_list_start(struct seq_file *p, loff_t *pos); 119462306a36Sopenharmony_cistatic void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos); 119562306a36Sopenharmony_cistatic void nfs_server_list_stop(struct seq_file *p, void *v); 119662306a36Sopenharmony_cistatic int nfs_server_list_show(struct seq_file *m, void *v); 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_cistatic const struct seq_operations nfs_server_list_ops = { 119962306a36Sopenharmony_ci .start = nfs_server_list_start, 120062306a36Sopenharmony_ci .next = nfs_server_list_next, 120162306a36Sopenharmony_ci .stop = nfs_server_list_stop, 120262306a36Sopenharmony_ci .show = nfs_server_list_show, 120362306a36Sopenharmony_ci}; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_cistatic void *nfs_volume_list_start(struct seq_file *p, loff_t *pos); 120662306a36Sopenharmony_cistatic void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos); 120762306a36Sopenharmony_cistatic void nfs_volume_list_stop(struct seq_file *p, void *v); 120862306a36Sopenharmony_cistatic int nfs_volume_list_show(struct seq_file *m, void *v); 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_cistatic const struct seq_operations nfs_volume_list_ops = { 121162306a36Sopenharmony_ci .start = nfs_volume_list_start, 121262306a36Sopenharmony_ci .next = nfs_volume_list_next, 121362306a36Sopenharmony_ci .stop = nfs_volume_list_stop, 121462306a36Sopenharmony_ci .show = nfs_volume_list_show, 121562306a36Sopenharmony_ci}; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci/* 121862306a36Sopenharmony_ci * set up the iterator to start reading from the server list and return the first item 121962306a36Sopenharmony_ci */ 122062306a36Sopenharmony_cistatic void *nfs_server_list_start(struct seq_file *m, loff_t *_pos) 122162306a36Sopenharmony_ci __acquires(&nn->nfs_client_lock) 122262306a36Sopenharmony_ci{ 122362306a36Sopenharmony_ci struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci /* lock the list against modification */ 122662306a36Sopenharmony_ci spin_lock(&nn->nfs_client_lock); 122762306a36Sopenharmony_ci return seq_list_start_head(&nn->nfs_client_list, *_pos); 122862306a36Sopenharmony_ci} 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci/* 123162306a36Sopenharmony_ci * move to next server 123262306a36Sopenharmony_ci */ 123362306a36Sopenharmony_cistatic void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos) 123462306a36Sopenharmony_ci{ 123562306a36Sopenharmony_ci struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci return seq_list_next(v, &nn->nfs_client_list, pos); 123862306a36Sopenharmony_ci} 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci/* 124162306a36Sopenharmony_ci * clean up after reading from the transports list 124262306a36Sopenharmony_ci */ 124362306a36Sopenharmony_cistatic void nfs_server_list_stop(struct seq_file *p, void *v) 124462306a36Sopenharmony_ci __releases(&nn->nfs_client_lock) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci spin_unlock(&nn->nfs_client_lock); 124962306a36Sopenharmony_ci} 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci/* 125262306a36Sopenharmony_ci * display a header line followed by a load of call lines 125362306a36Sopenharmony_ci */ 125462306a36Sopenharmony_cistatic int nfs_server_list_show(struct seq_file *m, void *v) 125562306a36Sopenharmony_ci{ 125662306a36Sopenharmony_ci struct nfs_client *clp; 125762306a36Sopenharmony_ci struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci /* display header on line 1 */ 126062306a36Sopenharmony_ci if (v == &nn->nfs_client_list) { 126162306a36Sopenharmony_ci seq_puts(m, "NV SERVER PORT USE HOSTNAME\n"); 126262306a36Sopenharmony_ci return 0; 126362306a36Sopenharmony_ci } 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci /* display one transport per line on subsequent lines */ 126662306a36Sopenharmony_ci clp = list_entry(v, struct nfs_client, cl_share_link); 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci /* Check if the client is initialized */ 126962306a36Sopenharmony_ci if (clp->cl_cons_state != NFS_CS_READY) 127062306a36Sopenharmony_ci return 0; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci rcu_read_lock(); 127362306a36Sopenharmony_ci seq_printf(m, "v%u %s %s %3d %s\n", 127462306a36Sopenharmony_ci clp->rpc_ops->version, 127562306a36Sopenharmony_ci rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), 127662306a36Sopenharmony_ci rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), 127762306a36Sopenharmony_ci refcount_read(&clp->cl_count), 127862306a36Sopenharmony_ci clp->cl_hostname); 127962306a36Sopenharmony_ci rcu_read_unlock(); 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci return 0; 128262306a36Sopenharmony_ci} 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci/* 128562306a36Sopenharmony_ci * set up the iterator to start reading from the volume list and return the first item 128662306a36Sopenharmony_ci */ 128762306a36Sopenharmony_cistatic void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos) 128862306a36Sopenharmony_ci __acquires(&nn->nfs_client_lock) 128962306a36Sopenharmony_ci{ 129062306a36Sopenharmony_ci struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci /* lock the list against modification */ 129362306a36Sopenharmony_ci spin_lock(&nn->nfs_client_lock); 129462306a36Sopenharmony_ci return seq_list_start_head(&nn->nfs_volume_list, *_pos); 129562306a36Sopenharmony_ci} 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci/* 129862306a36Sopenharmony_ci * move to next volume 129962306a36Sopenharmony_ci */ 130062306a36Sopenharmony_cistatic void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos) 130162306a36Sopenharmony_ci{ 130262306a36Sopenharmony_ci struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci return seq_list_next(v, &nn->nfs_volume_list, pos); 130562306a36Sopenharmony_ci} 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci/* 130862306a36Sopenharmony_ci * clean up after reading from the transports list 130962306a36Sopenharmony_ci */ 131062306a36Sopenharmony_cistatic void nfs_volume_list_stop(struct seq_file *p, void *v) 131162306a36Sopenharmony_ci __releases(&nn->nfs_client_lock) 131262306a36Sopenharmony_ci{ 131362306a36Sopenharmony_ci struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci spin_unlock(&nn->nfs_client_lock); 131662306a36Sopenharmony_ci} 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci/* 131962306a36Sopenharmony_ci * display a header line followed by a load of call lines 132062306a36Sopenharmony_ci */ 132162306a36Sopenharmony_cistatic int nfs_volume_list_show(struct seq_file *m, void *v) 132262306a36Sopenharmony_ci{ 132362306a36Sopenharmony_ci struct nfs_server *server; 132462306a36Sopenharmony_ci struct nfs_client *clp; 132562306a36Sopenharmony_ci char dev[13]; // 8 for 2^24, 1 for ':', 3 for 2^8, 1 for '\0' 132662306a36Sopenharmony_ci char fsid[34]; // 2 * 16 for %llx, 1 for ':', 1 for '\0' 132762306a36Sopenharmony_ci struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci /* display header on line 1 */ 133062306a36Sopenharmony_ci if (v == &nn->nfs_volume_list) { 133162306a36Sopenharmony_ci seq_puts(m, "NV SERVER PORT DEV FSID" 133262306a36Sopenharmony_ci " FSC\n"); 133362306a36Sopenharmony_ci return 0; 133462306a36Sopenharmony_ci } 133562306a36Sopenharmony_ci /* display one transport per line on subsequent lines */ 133662306a36Sopenharmony_ci server = list_entry(v, struct nfs_server, master_link); 133762306a36Sopenharmony_ci clp = server->nfs_client; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci snprintf(dev, sizeof(dev), "%u:%u", 134062306a36Sopenharmony_ci MAJOR(server->s_dev), MINOR(server->s_dev)); 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci snprintf(fsid, sizeof(fsid), "%llx:%llx", 134362306a36Sopenharmony_ci (unsigned long long) server->fsid.major, 134462306a36Sopenharmony_ci (unsigned long long) server->fsid.minor); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci rcu_read_lock(); 134762306a36Sopenharmony_ci seq_printf(m, "v%u %s %s %-12s %-33s %s\n", 134862306a36Sopenharmony_ci clp->rpc_ops->version, 134962306a36Sopenharmony_ci rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), 135062306a36Sopenharmony_ci rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), 135162306a36Sopenharmony_ci dev, 135262306a36Sopenharmony_ci fsid, 135362306a36Sopenharmony_ci nfs_server_fscache_state(server)); 135462306a36Sopenharmony_ci rcu_read_unlock(); 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci return 0; 135762306a36Sopenharmony_ci} 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ciint nfs_fs_proc_net_init(struct net *net) 136062306a36Sopenharmony_ci{ 136162306a36Sopenharmony_ci struct nfs_net *nn = net_generic(net, nfs_net_id); 136262306a36Sopenharmony_ci struct proc_dir_entry *p; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci nn->proc_nfsfs = proc_net_mkdir(net, "nfsfs", net->proc_net); 136562306a36Sopenharmony_ci if (!nn->proc_nfsfs) 136662306a36Sopenharmony_ci goto error_0; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci /* a file of servers with which we're dealing */ 136962306a36Sopenharmony_ci p = proc_create_net("servers", S_IFREG|S_IRUGO, nn->proc_nfsfs, 137062306a36Sopenharmony_ci &nfs_server_list_ops, sizeof(struct seq_net_private)); 137162306a36Sopenharmony_ci if (!p) 137262306a36Sopenharmony_ci goto error_1; 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci /* a file of volumes that we have mounted */ 137562306a36Sopenharmony_ci p = proc_create_net("volumes", S_IFREG|S_IRUGO, nn->proc_nfsfs, 137662306a36Sopenharmony_ci &nfs_volume_list_ops, sizeof(struct seq_net_private)); 137762306a36Sopenharmony_ci if (!p) 137862306a36Sopenharmony_ci goto error_1; 137962306a36Sopenharmony_ci return 0; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_cierror_1: 138262306a36Sopenharmony_ci remove_proc_subtree("nfsfs", net->proc_net); 138362306a36Sopenharmony_cierror_0: 138462306a36Sopenharmony_ci return -ENOMEM; 138562306a36Sopenharmony_ci} 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_civoid nfs_fs_proc_net_exit(struct net *net) 138862306a36Sopenharmony_ci{ 138962306a36Sopenharmony_ci remove_proc_subtree("nfsfs", net->proc_net); 139062306a36Sopenharmony_ci} 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci/* 139362306a36Sopenharmony_ci * initialise the /proc/fs/nfsfs/ directory 139462306a36Sopenharmony_ci */ 139562306a36Sopenharmony_ciint __init nfs_fs_proc_init(void) 139662306a36Sopenharmony_ci{ 139762306a36Sopenharmony_ci if (!proc_mkdir("fs/nfsfs", NULL)) 139862306a36Sopenharmony_ci goto error_0; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci /* a file of servers with which we're dealing */ 140162306a36Sopenharmony_ci if (!proc_symlink("fs/nfsfs/servers", NULL, "../../net/nfsfs/servers")) 140262306a36Sopenharmony_ci goto error_1; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci /* a file of volumes that we have mounted */ 140562306a36Sopenharmony_ci if (!proc_symlink("fs/nfsfs/volumes", NULL, "../../net/nfsfs/volumes")) 140662306a36Sopenharmony_ci goto error_1; 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci return 0; 140962306a36Sopenharmony_cierror_1: 141062306a36Sopenharmony_ci remove_proc_subtree("fs/nfsfs", NULL); 141162306a36Sopenharmony_cierror_0: 141262306a36Sopenharmony_ci return -ENOMEM; 141362306a36Sopenharmony_ci} 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci/* 141662306a36Sopenharmony_ci * clean up the /proc/fs/nfsfs/ directory 141762306a36Sopenharmony_ci */ 141862306a36Sopenharmony_civoid nfs_fs_proc_exit(void) 141962306a36Sopenharmony_ci{ 142062306a36Sopenharmony_ci remove_proc_subtree("fs/nfsfs", NULL); 142162306a36Sopenharmony_ci ida_destroy(&s_sysfs_ids); 142262306a36Sopenharmony_ci} 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 1425