12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 224c8dbbbSDavid Howells /* client.c: NFS client sharing and management code 324c8dbbbSDavid Howells * 424c8dbbbSDavid Howells * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. 524c8dbbbSDavid Howells * Written by David Howells (dhowells@redhat.com) 624c8dbbbSDavid Howells */ 724c8dbbbSDavid Howells 824c8dbbbSDavid Howells 924c8dbbbSDavid Howells #include <linux/module.h> 1024c8dbbbSDavid Howells #include <linux/init.h> 11e8edc6e0SAlexey Dobriyan #include <linux/sched.h> 1224c8dbbbSDavid Howells #include <linux/time.h> 1324c8dbbbSDavid Howells #include <linux/kernel.h> 1424c8dbbbSDavid Howells #include <linux/mm.h> 1524c8dbbbSDavid Howells #include <linux/string.h> 1624c8dbbbSDavid Howells #include <linux/stat.h> 1724c8dbbbSDavid Howells #include <linux/errno.h> 1824c8dbbbSDavid Howells #include <linux/unistd.h> 19d8efa4e6SAnna Schumaker #include <linux/sunrpc/addr.h> 2024c8dbbbSDavid Howells #include <linux/sunrpc/clnt.h> 2124c8dbbbSDavid Howells #include <linux/sunrpc/stats.h> 2224c8dbbbSDavid Howells #include <linux/sunrpc/metrics.h> 230896a725S\"Talpey, Thomas\ #include <linux/sunrpc/xprtsock.h> 242cf7ff7aS\"Talpey, Thomas\ #include <linux/sunrpc/xprtrdma.h> 2524c8dbbbSDavid Howells #include <linux/nfs_fs.h> 2624c8dbbbSDavid Howells #include <linux/nfs_mount.h> 2724c8dbbbSDavid Howells #include <linux/nfs4_mount.h> 2824c8dbbbSDavid Howells #include <linux/lockd/bind.h> 2924c8dbbbSDavid Howells #include <linux/seq_file.h> 3024c8dbbbSDavid Howells #include <linux/mount.h> 3124c8dbbbSDavid Howells #include <linux/vfs.h> 3224c8dbbbSDavid Howells #include <linux/inet.h> 333b0d3f93STrond Myklebust #include <linux/in6.h> 345a0e3ad6STejun Heo #include <linux/slab.h> 3540401530SAl Viro #include <linux/idr.h> 363b0d3f93STrond Myklebust #include <net/ipv6.h> 3724c8dbbbSDavid Howells #include <linux/nfs_xdr.h> 380b5b7ae0SAndy Adamson #include <linux/sunrpc/bc_xprt.h> 396b13168bSStanislav Kinsbursky #include <linux/nsproxy.h> 406b13168bSStanislav Kinsbursky #include <linux/pid_namespace.h> 4124c8dbbbSDavid Howells 4224c8dbbbSDavid Howells 4324c8dbbbSDavid Howells #include "nfs4_fs.h" 4424c8dbbbSDavid Howells #include "callback.h" 4524c8dbbbSDavid Howells #include "delegation.h" 4624c8dbbbSDavid Howells #include "iostat.h" 4724c8dbbbSDavid Howells #include "internal.h" 4814727281SDavid Howells #include "fscache.h" 4985e174baSRicardo Labiaga #include "pnfs.h" 50ab7017a3SBryan Schumaker #include "nfs.h" 516b13168bSStanislav Kinsbursky #include "netns.h" 52bf11fbdbSTrond Myklebust #include "sysfs.h" 5304a5da69SFrank van der Linden #include "nfs42.h" 5424c8dbbbSDavid Howells 5524c8dbbbSDavid Howells #define NFSDBG_FACILITY NFSDBG_CLIENT 5624c8dbbbSDavid Howells 5724c8dbbbSDavid Howells static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq); 58ab7017a3SBryan Schumaker static DEFINE_SPINLOCK(nfs_version_lock); 59ab7017a3SBryan Schumaker static DEFINE_MUTEX(nfs_version_mutex); 60ab7017a3SBryan Schumaker static LIST_HEAD(nfs_versions); 6124c8dbbbSDavid Howells 6224c8dbbbSDavid Howells /* 635006a76cSDavid Howells * RPC cruft for NFS 645006a76cSDavid Howells */ 65a613fa16STrond Myklebust static const struct rpc_version *nfs_version[5] = { 66ab7017a3SBryan Schumaker [2] = NULL, 67ab7017a3SBryan Schumaker [3] = NULL, 68ab7017a3SBryan Schumaker [4] = NULL, 695006a76cSDavid Howells }; 705006a76cSDavid Howells 71a613fa16STrond Myklebust const struct rpc_program nfs_program = { 725006a76cSDavid Howells .name = "nfs", 735006a76cSDavid Howells .number = NFS_PROGRAM, 745006a76cSDavid Howells .nrvers = ARRAY_SIZE(nfs_version), 755006a76cSDavid Howells .version = nfs_version, 765006a76cSDavid Howells .stats = &nfs_rpcstat, 77fe0a9b74SJim Rees .pipe_dir_name = NFS_PIPE_DIRNAME, 785006a76cSDavid Howells }; 795006a76cSDavid Howells 805006a76cSDavid Howells struct rpc_stat nfs_rpcstat = { 815006a76cSDavid Howells .program = &nfs_program 825006a76cSDavid Howells }; 835006a76cSDavid Howells 84ab7017a3SBryan Schumaker static struct nfs_subversion *find_nfs_version(unsigned int version) 85ab7017a3SBryan Schumaker { 86ab7017a3SBryan Schumaker struct nfs_subversion *nfs; 87ab7017a3SBryan Schumaker spin_lock(&nfs_version_lock); 88ab7017a3SBryan Schumaker 89ab7017a3SBryan Schumaker list_for_each_entry(nfs, &nfs_versions, list) { 90ab7017a3SBryan Schumaker if (nfs->rpc_ops->version == version) { 91ab7017a3SBryan Schumaker spin_unlock(&nfs_version_lock); 92ab7017a3SBryan Schumaker return nfs; 93ab7017a3SBryan Schumaker } 94ee34e136SYanchuan Nian } 95ab7017a3SBryan Schumaker 96ab7017a3SBryan Schumaker spin_unlock(&nfs_version_lock); 97ee34e136SYanchuan Nian return ERR_PTR(-EPROTONOSUPPORT); 98ab7017a3SBryan Schumaker } 99ab7017a3SBryan Schumaker 100ab7017a3SBryan Schumaker struct nfs_subversion *get_nfs_version(unsigned int version) 101ab7017a3SBryan Schumaker { 102ab7017a3SBryan Schumaker struct nfs_subversion *nfs = find_nfs_version(version); 103ab7017a3SBryan Schumaker 104ab7017a3SBryan Schumaker if (IS_ERR(nfs)) { 105ab7017a3SBryan Schumaker mutex_lock(&nfs_version_mutex); 1061ae811eeSbjschuma@gmail.com request_module("nfsv%d", version); 107ab7017a3SBryan Schumaker nfs = find_nfs_version(version); 108ab7017a3SBryan Schumaker mutex_unlock(&nfs_version_mutex); 109ab7017a3SBryan Schumaker } 110ab7017a3SBryan Schumaker 1111f70ef96SAlexey Khoroshilov if (!IS_ERR(nfs) && !try_module_get(nfs->owner)) 1121f70ef96SAlexey Khoroshilov return ERR_PTR(-EAGAIN); 113ab7017a3SBryan Schumaker return nfs; 114ab7017a3SBryan Schumaker } 115ab7017a3SBryan Schumaker 116ab7017a3SBryan Schumaker void put_nfs_version(struct nfs_subversion *nfs) 117ab7017a3SBryan Schumaker { 118ab7017a3SBryan Schumaker module_put(nfs->owner); 119ab7017a3SBryan Schumaker } 120ab7017a3SBryan Schumaker 121ab7017a3SBryan Schumaker void register_nfs_version(struct nfs_subversion *nfs) 122ab7017a3SBryan Schumaker { 123ab7017a3SBryan Schumaker spin_lock(&nfs_version_lock); 124ab7017a3SBryan Schumaker 125ab7017a3SBryan Schumaker list_add(&nfs->list, &nfs_versions); 126ab7017a3SBryan Schumaker nfs_version[nfs->rpc_ops->version] = nfs->rpc_vers; 127ab7017a3SBryan Schumaker 128ab7017a3SBryan Schumaker spin_unlock(&nfs_version_lock); 129ab7017a3SBryan Schumaker } 130ab7017a3SBryan Schumaker EXPORT_SYMBOL_GPL(register_nfs_version); 131ab7017a3SBryan Schumaker 132ab7017a3SBryan Schumaker void unregister_nfs_version(struct nfs_subversion *nfs) 133ab7017a3SBryan Schumaker { 134ab7017a3SBryan Schumaker spin_lock(&nfs_version_lock); 135ab7017a3SBryan Schumaker 136ab7017a3SBryan Schumaker nfs_version[nfs->rpc_ops->version] = NULL; 137ab7017a3SBryan Schumaker list_del(&nfs->list); 138ab7017a3SBryan Schumaker 139ab7017a3SBryan Schumaker spin_unlock(&nfs_version_lock); 140ab7017a3SBryan Schumaker } 141ab7017a3SBryan Schumaker EXPORT_SYMBOL_GPL(unregister_nfs_version); 142ab7017a3SBryan Schumaker 143ab7017a3SBryan Schumaker /* 14424c8dbbbSDavid Howells * Allocate a shared client record 14524c8dbbbSDavid Howells * 14624c8dbbbSDavid Howells * Since these are allocated/deallocated very rarely, we don't 14724c8dbbbSDavid Howells * bother putting them in a slab cache... 14824c8dbbbSDavid Howells */ 1496663ee7fSBryan Schumaker struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) 15024c8dbbbSDavid Howells { 15124c8dbbbSDavid Howells struct nfs_client *clp; 152a21bdd9bSChuck Lever int err = -ENOMEM; 15324c8dbbbSDavid Howells 15424c8dbbbSDavid Howells if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) 15524c8dbbbSDavid Howells goto error_0; 15624c8dbbbSDavid Howells 15755dee1bcSScott Mayhew clp->cl_minorversion = cl_init->minorversion; 158ab7017a3SBryan Schumaker clp->cl_nfs_mod = cl_init->nfs_mod; 1591f70ef96SAlexey Khoroshilov if (!try_module_get(clp->cl_nfs_mod->owner)) 1601f70ef96SAlexey Khoroshilov goto error_dealloc; 161ab7017a3SBryan Schumaker 162ab7017a3SBryan Schumaker clp->rpc_ops = clp->cl_nfs_mod->rpc_ops; 16340c55319STrond Myklebust 164212bf41dSElena Reshetova refcount_set(&clp->cl_count, 1); 16524c8dbbbSDavid Howells clp->cl_cons_state = NFS_CS_INITING; 16624c8dbbbSDavid Howells 1676e4cffd7SChuck Lever memcpy(&clp->cl_addr, cl_init->addr, cl_init->addrlen); 1686e4cffd7SChuck Lever clp->cl_addrlen = cl_init->addrlen; 16924c8dbbbSDavid Howells 1703a498026STrond Myklebust if (cl_init->hostname) { 171a21bdd9bSChuck Lever err = -ENOMEM; 1723a498026STrond Myklebust clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL); 17324c8dbbbSDavid Howells if (!clp->cl_hostname) 17471468513SBenny Halevy goto error_cleanup; 17524c8dbbbSDavid Howells } 17624c8dbbbSDavid Howells 17724c8dbbbSDavid Howells INIT_LIST_HEAD(&clp->cl_superblocks); 17824c8dbbbSDavid Howells clp->cl_rpcclient = ERR_PTR(-EINVAL); 17924c8dbbbSDavid Howells 180468d126dSTrond Myklebust clp->cl_flags = cl_init->init_flags; 18159dca3b2STrond Myklebust clp->cl_proto = cl_init->proto; 1826619079dSTrond Myklebust clp->cl_nconnect = cl_init->nconnect; 1837e134205SOlga Kornievskaia clp->cl_max_connect = cl_init->max_connect ? cl_init->max_connect : 1; 18473ea666cSChuck Lever clp->cl_net = get_net(cl_init->net); 18559dca3b2STrond Myklebust 1865e16923bSNeilBrown clp->cl_principal = "*"; 1876c0a8c5fSChuck Lever clp->cl_xprtsec = cl_init->xprtsec; 18824c8dbbbSDavid Howells return clp; 18924c8dbbbSDavid Howells 19071468513SBenny Halevy error_cleanup: 191ab7017a3SBryan Schumaker put_nfs_version(clp->cl_nfs_mod); 1921f70ef96SAlexey Khoroshilov error_dealloc: 19324c8dbbbSDavid Howells kfree(clp); 19424c8dbbbSDavid Howells error_0: 195a21bdd9bSChuck Lever return ERR_PTR(err); 19624c8dbbbSDavid Howells } 197ddda8e0aSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_alloc_client); 19824c8dbbbSDavid Howells 19989d77c8fSBryan Schumaker #if IS_ENABLED(CONFIG_NFS_V4) 20010b7a70cSTrond Myklebust static void nfs_cleanup_cb_ident_idr(struct net *net) 201f4eecd5dSAndy Adamson { 20228cd1b3fSStanislav Kinsbursky struct nfs_net *nn = net_generic(net, nfs_net_id); 20328cd1b3fSStanislav Kinsbursky 20428cd1b3fSStanislav Kinsbursky idr_destroy(&nn->cb_ident_idr); 205f4eecd5dSAndy Adamson } 206f4eecd5dSAndy Adamson 207f4eecd5dSAndy Adamson /* nfs_client_lock held */ 208f4eecd5dSAndy Adamson static void nfs_cb_idr_remove_locked(struct nfs_client *clp) 209f4eecd5dSAndy Adamson { 21073ea666cSChuck Lever struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); 21128cd1b3fSStanislav Kinsbursky 212f4eecd5dSAndy Adamson if (clp->cl_cb_ident) 21328cd1b3fSStanislav Kinsbursky idr_remove(&nn->cb_ident_idr, clp->cl_cb_ident); 214f4eecd5dSAndy Adamson } 215f4eecd5dSAndy Adamson 216f7e8917aSFred Isaman static void pnfs_init_server(struct nfs_server *server) 217f7e8917aSFred Isaman { 218f7e8917aSFred Isaman rpc_init_wait_queue(&server->roc_rpcwaitq, "pNFS ROC"); 219f7e8917aSFred Isaman } 220f7e8917aSFred Isaman 221888ef2e3SAlexandros Batsakis #else 22210b7a70cSTrond Myklebust static void nfs_cleanup_cb_ident_idr(struct net *net) 223f4eecd5dSAndy Adamson { 224f4eecd5dSAndy Adamson } 225f4eecd5dSAndy Adamson 226f4eecd5dSAndy Adamson static void nfs_cb_idr_remove_locked(struct nfs_client *clp) 227f4eecd5dSAndy Adamson { 228f4eecd5dSAndy Adamson } 229f7e8917aSFred Isaman 230f7e8917aSFred Isaman static void pnfs_init_server(struct nfs_server *server) 231f7e8917aSFred Isaman { 232f7e8917aSFred Isaman } 233f7e8917aSFred Isaman 234888ef2e3SAlexandros Batsakis #endif /* CONFIG_NFS_V4 */ 235888ef2e3SAlexandros Batsakis 236888ef2e3SAlexandros Batsakis /* 2375dd3177aSTrond Myklebust * Destroy a shared client record 2385dd3177aSTrond Myklebust */ 239cdb7ecedSBryan Schumaker void nfs_free_client(struct nfs_client *clp) 2405dd3177aSTrond Myklebust { 24124c8dbbbSDavid Howells /* -EIO all pending I/O */ 24224c8dbbbSDavid Howells if (!IS_ERR(clp->cl_rpcclient)) 24324c8dbbbSDavid Howells rpc_shutdown_client(clp->cl_rpcclient); 24424c8dbbbSDavid Howells 24573ea666cSChuck Lever put_net(clp->cl_net); 246ab7017a3SBryan Schumaker put_nfs_version(clp->cl_nfs_mod); 24724c8dbbbSDavid Howells kfree(clp->cl_hostname); 248f11b2a1cSJeff Layton kfree(clp->cl_acceptor); 24924c8dbbbSDavid Howells kfree(clp); 25024c8dbbbSDavid Howells } 251ddda8e0aSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_free_client); 25224c8dbbbSDavid Howells 25324c8dbbbSDavid Howells /* 25424c8dbbbSDavid Howells * Release a reference to a shared client record 25524c8dbbbSDavid Howells */ 25624c8dbbbSDavid Howells void nfs_put_client(struct nfs_client *clp) 25724c8dbbbSDavid Howells { 258dc030858SStanislav Kinsbursky struct nfs_net *nn; 259dc030858SStanislav Kinsbursky 26027ba8512SDavid Howells if (!clp) 26127ba8512SDavid Howells return; 26227ba8512SDavid Howells 26373ea666cSChuck Lever nn = net_generic(clp->cl_net, nfs_net_id); 26424c8dbbbSDavid Howells 265212bf41dSElena Reshetova if (refcount_dec_and_lock(&clp->cl_count, &nn->nfs_client_lock)) { 26624c8dbbbSDavid Howells list_del(&clp->cl_share_link); 267f4eecd5dSAndy Adamson nfs_cb_idr_remove_locked(clp); 268dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 26924c8dbbbSDavid Howells 2701fea73a8STrond Myklebust WARN_ON_ONCE(!list_empty(&clp->cl_superblocks)); 27124c8dbbbSDavid Howells 272cdb7ecedSBryan Schumaker clp->rpc_ops->free_client(clp); 27324c8dbbbSDavid Howells } 27424c8dbbbSDavid Howells } 27516b374caSAndy Adamson EXPORT_SYMBOL_GPL(nfs_put_client); 27624c8dbbbSDavid Howells 2773fbd67adSTrond Myklebust /* 278c81468a1STrond Myklebust * Find an nfs_client on the list that matches the initialisation data 279c81468a1STrond Myklebust * that is supplied. 280c81468a1STrond Myklebust */ 281c81468a1STrond Myklebust static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *data) 28224c8dbbbSDavid Howells { 28324c8dbbbSDavid Howells struct nfs_client *clp; 284cf0d7e7fSKees Cook const struct sockaddr *sap = (struct sockaddr *)data->addr; 2856b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(data->net, nfs_net_id); 286950a578cSRoberto Bergantinos Corpas int error; 28724c8dbbbSDavid Howells 288c156618eSScott Mayhew again: 2896b13168bSStanislav Kinsbursky list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) { 290d7371c41SIan Dall const struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; 29113bbc06aSTrond Myklebust /* Don't match clients that failed to initialise properly */ 29213bbc06aSTrond Myklebust if (clp->cl_cons_state < 0) 29313bbc06aSTrond Myklebust continue; 29413bbc06aSTrond Myklebust 295c156618eSScott Mayhew /* If a client is still initializing then we need to wait */ 296c156618eSScott Mayhew if (clp->cl_cons_state > NFS_CS_READY) { 297c156618eSScott Mayhew refcount_inc(&clp->cl_count); 298c156618eSScott Mayhew spin_unlock(&nn->nfs_client_lock); 299950a578cSRoberto Bergantinos Corpas error = nfs_wait_client_init_complete(clp); 300c156618eSScott Mayhew nfs_put_client(clp); 301c260121aSBenjamin Coddington spin_lock(&nn->nfs_client_lock); 302950a578cSRoberto Bergantinos Corpas if (error < 0) 303950a578cSRoberto Bergantinos Corpas return ERR_PTR(error); 304c156618eSScott Mayhew goto again; 305c156618eSScott Mayhew } 306c156618eSScott Mayhew 30724c8dbbbSDavid Howells /* Different NFS versions cannot share the same nfs_client */ 308ab7017a3SBryan Schumaker if (clp->rpc_ops != data->nfs_mod->rpc_ops) 30924c8dbbbSDavid Howells continue; 31024c8dbbbSDavid Howells 31159dca3b2STrond Myklebust if (clp->cl_proto != data->proto) 31259dca3b2STrond Myklebust continue; 3135aae4a9aSBenny Halevy /* Match nfsv4 minorversion */ 3145aae4a9aSBenny Halevy if (clp->cl_minorversion != data->minorversion) 3155aae4a9aSBenny Halevy continue; 31652f98f1aSTrond Myklebust 31752f98f1aSTrond Myklebust /* Match request for a dedicated DS */ 31852f98f1aSTrond Myklebust if (test_bit(NFS_CS_DS, &data->init_flags) != 31952f98f1aSTrond Myklebust test_bit(NFS_CS_DS, &clp->cl_flags)) 32052f98f1aSTrond Myklebust continue; 32152f98f1aSTrond Myklebust 322c81468a1STrond Myklebust /* Match the full socket address */ 323d8efa4e6SAnna Schumaker if (!rpc_cmp_addr_port(sap, clap)) 32404ea1b3eSAndy Adamson /* Match all xprt_switch full socket addresses */ 3258ef32955SPetr Vandrovec if (IS_ERR(clp->cl_rpcclient) || 3268ef32955SPetr Vandrovec !rpc_clnt_xprt_switch_has_addr(clp->cl_rpcclient, 32704ea1b3eSAndy Adamson sap)) 32824c8dbbbSDavid Howells continue; 32924c8dbbbSDavid Howells 3306c0a8c5fSChuck Lever /* Match the xprt security policy */ 3316c0a8c5fSChuck Lever if (clp->cl_xprtsec.policy != data->xprtsec.policy) 3326c0a8c5fSChuck Lever continue; 3336c0a8c5fSChuck Lever 334212bf41dSElena Reshetova refcount_inc(&clp->cl_count); 33524c8dbbbSDavid Howells return clp; 33624c8dbbbSDavid Howells } 337c81468a1STrond Myklebust return NULL; 33824c8dbbbSDavid Howells } 33924c8dbbbSDavid Howells 340a33e4b03SWeston Andros Adamson /* 341a33e4b03SWeston Andros Adamson * Return true if @clp is done initializing, false if still working on it. 342a33e4b03SWeston Andros Adamson * 343a33e4b03SWeston Andros Adamson * Use nfs_client_init_status to check if it was successful. 344a33e4b03SWeston Andros Adamson */ 345a33e4b03SWeston Andros Adamson bool nfs_client_init_is_complete(const struct nfs_client *clp) 3464697bd5eSTrond Myklebust { 34748d66b97STrond Myklebust return clp->cl_cons_state <= NFS_CS_READY; 3484697bd5eSTrond Myklebust } 349a33e4b03SWeston Andros Adamson EXPORT_SYMBOL_GPL(nfs_client_init_is_complete); 350a33e4b03SWeston Andros Adamson 351a33e4b03SWeston Andros Adamson /* 352a33e4b03SWeston Andros Adamson * Return 0 if @clp was successfully initialized, -errno otherwise. 353a33e4b03SWeston Andros Adamson * 354a33e4b03SWeston Andros Adamson * This must be called *after* nfs_client_init_is_complete() returns true, 355a33e4b03SWeston Andros Adamson * otherwise it will pop WARN_ON_ONCE and return -EINVAL 356a33e4b03SWeston Andros Adamson */ 357a33e4b03SWeston Andros Adamson int nfs_client_init_status(const struct nfs_client *clp) 358a33e4b03SWeston Andros Adamson { 359a33e4b03SWeston Andros Adamson /* called without checking nfs_client_init_is_complete */ 360a33e4b03SWeston Andros Adamson if (clp->cl_cons_state > NFS_CS_READY) { 361a33e4b03SWeston Andros Adamson WARN_ON_ONCE(1); 362a33e4b03SWeston Andros Adamson return -EINVAL; 363a33e4b03SWeston Andros Adamson } 364a33e4b03SWeston Andros Adamson return clp->cl_cons_state; 365a33e4b03SWeston Andros Adamson } 366a33e4b03SWeston Andros Adamson EXPORT_SYMBOL_GPL(nfs_client_init_status); 3674697bd5eSTrond Myklebust 3684697bd5eSTrond Myklebust int nfs_wait_client_init_complete(const struct nfs_client *clp) 3694697bd5eSTrond Myklebust { 3704697bd5eSTrond Myklebust return wait_event_killable(nfs_client_active_wq, 3714697bd5eSTrond Myklebust nfs_client_init_is_complete(clp)); 3724697bd5eSTrond Myklebust } 37389d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_wait_client_init_complete); 3744697bd5eSTrond Myklebust 37524c8dbbbSDavid Howells /* 376f411703aSChuck Lever * Found an existing client. Make sure it's ready before returning. 377f411703aSChuck Lever */ 378f411703aSChuck Lever static struct nfs_client * 379f411703aSChuck Lever nfs_found_client(const struct nfs_client_initdata *cl_init, 380f411703aSChuck Lever struct nfs_client *clp) 381f411703aSChuck Lever { 382f411703aSChuck Lever int error; 383f411703aSChuck Lever 3844697bd5eSTrond Myklebust error = nfs_wait_client_init_complete(clp); 385f411703aSChuck Lever if (error < 0) { 386f411703aSChuck Lever nfs_put_client(clp); 387f411703aSChuck Lever return ERR_PTR(-ERESTARTSYS); 388f411703aSChuck Lever } 389f411703aSChuck Lever 390f411703aSChuck Lever if (clp->cl_cons_state < NFS_CS_READY) { 391f411703aSChuck Lever error = clp->cl_cons_state; 392f411703aSChuck Lever nfs_put_client(clp); 393f411703aSChuck Lever return ERR_PTR(error); 394f411703aSChuck Lever } 395f411703aSChuck Lever 39654ac471cSTrond Myklebust smp_rmb(); 397f411703aSChuck Lever return clp; 398f411703aSChuck Lever } 399f411703aSChuck Lever 400f411703aSChuck Lever /* 40124c8dbbbSDavid Howells * Look up a client by IP address and protocol version 40224c8dbbbSDavid Howells * - creates a new record if one doesn't yet exist 40324c8dbbbSDavid Howells */ 4047d38de3fSAnna Schumaker struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_init) 40524c8dbbbSDavid Howells { 40624c8dbbbSDavid Howells struct nfs_client *clp, *new = NULL; 4076b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(cl_init->net, nfs_net_id); 408ab7017a3SBryan Schumaker const struct nfs_rpc_ops *rpc_ops = cl_init->nfs_mod->rpc_ops; 40924c8dbbbSDavid Howells 41031434f49SPeng Tao if (cl_init->hostname == NULL) { 41131434f49SPeng Tao WARN_ON(1); 41209226e83SDan Carpenter return ERR_PTR(-EINVAL); 41331434f49SPeng Tao } 41431434f49SPeng Tao 41524c8dbbbSDavid Howells /* see if the client already exists */ 41624c8dbbbSDavid Howells do { 417dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 41824c8dbbbSDavid Howells 419c81468a1STrond Myklebust clp = nfs_match_client(cl_init); 420f411703aSChuck Lever if (clp) { 421f411703aSChuck Lever spin_unlock(&nn->nfs_client_lock); 422f411703aSChuck Lever if (new) 423cdb7ecedSBryan Schumaker new->rpc_ops->free_client(new); 4249f7761cfSBenjamin Coddington if (IS_ERR(clp)) 4259f7761cfSBenjamin Coddington return clp; 426f411703aSChuck Lever return nfs_found_client(cl_init, clp); 427f411703aSChuck Lever } 4288cab4c39SChuck Lever if (new) { 42905f4c350SChuck Lever list_add_tail(&new->cl_share_link, 43005f4c350SChuck Lever &nn->nfs_client_list); 4318cab4c39SChuck Lever spin_unlock(&nn->nfs_client_lock); 4325c6e5b60STrond Myklebust return rpc_ops->init_client(new, cl_init); 4338cab4c39SChuck Lever } 43424c8dbbbSDavid Howells 435dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 43624c8dbbbSDavid Howells 437ab7017a3SBryan Schumaker new = rpc_ops->alloc_client(cl_init); 438a21bdd9bSChuck Lever } while (!IS_ERR(new)); 43924c8dbbbSDavid Howells 440a21bdd9bSChuck Lever return new; 44124c8dbbbSDavid Howells } 44289d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_get_client); 44324c8dbbbSDavid Howells 44424c8dbbbSDavid Howells /* 44524c8dbbbSDavid Howells * Mark a server as ready or failed 44624c8dbbbSDavid Howells */ 44776db6d95SAndy Adamson void nfs_mark_client_ready(struct nfs_client *clp, int state) 44824c8dbbbSDavid Howells { 44954ac471cSTrond Myklebust smp_wmb(); 45024c8dbbbSDavid Howells clp->cl_cons_state = state; 45124c8dbbbSDavid Howells wake_up_all(&nfs_client_active_wq); 45224c8dbbbSDavid Howells } 45389d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_mark_client_ready); 4545006a76cSDavid Howells 4555006a76cSDavid Howells /* 4565006a76cSDavid Howells * Initialise the timeout values for a connection 4575006a76cSDavid Howells */ 458fcf10398SBryan Schumaker void nfs_init_timeout_values(struct rpc_timeout *to, int proto, 459a956bedaSTrond Myklebust int timeo, int retrans) 4605006a76cSDavid Howells { 4615006a76cSDavid Howells to->to_initval = timeo * HZ / 10; 4625006a76cSDavid Howells to->to_retries = retrans; 4635006a76cSDavid Howells 4645006a76cSDavid Howells switch (proto) { 4650896a725S\"Talpey, Thomas\ case XPRT_TRANSPORT_TCP: 466c8407f2eSChuck Lever case XPRT_TRANSPORT_TCP_TLS: 4672cf7ff7aS\"Talpey, Thomas\ case XPRT_TRANSPORT_RDMA: 468a956bedaSTrond Myklebust if (retrans == NFS_UNSPEC_RETRANS) 469259875efSTrond Myklebust to->to_retries = NFS_DEF_TCP_RETRANS; 4705a698243STrond Myklebust if (timeo == NFS_UNSPEC_TIMEO || to->to_initval == 0) 471259875efSTrond Myklebust to->to_initval = NFS_DEF_TCP_TIMEO * HZ / 10; 4725006a76cSDavid Howells if (to->to_initval > NFS_MAX_TCP_TIMEOUT) 4735006a76cSDavid Howells to->to_initval = NFS_MAX_TCP_TIMEOUT; 4745006a76cSDavid Howells to->to_increment = to->to_initval; 4755006a76cSDavid Howells to->to_maxval = to->to_initval + (to->to_increment * to->to_retries); 4767a3e3e18STrond Myklebust if (to->to_maxval > NFS_MAX_TCP_TIMEOUT) 4777a3e3e18STrond Myklebust to->to_maxval = NFS_MAX_TCP_TIMEOUT; 4787a3e3e18STrond Myklebust if (to->to_maxval < to->to_initval) 4797a3e3e18STrond Myklebust to->to_maxval = to->to_initval; 4805006a76cSDavid Howells to->to_exponential = 0; 4815006a76cSDavid Howells break; 4820896a725S\"Talpey, Thomas\ case XPRT_TRANSPORT_UDP: 483a956bedaSTrond Myklebust if (retrans == NFS_UNSPEC_RETRANS) 484259875efSTrond Myklebust to->to_retries = NFS_DEF_UDP_RETRANS; 485a956bedaSTrond Myklebust if (timeo == NFS_UNSPEC_TIMEO || to->to_initval == 0) 486259875efSTrond Myklebust to->to_initval = NFS_DEF_UDP_TIMEO * HZ / 10; 4875006a76cSDavid Howells if (to->to_initval > NFS_MAX_UDP_TIMEOUT) 4885006a76cSDavid Howells to->to_initval = NFS_MAX_UDP_TIMEOUT; 4895006a76cSDavid Howells to->to_maxval = NFS_MAX_UDP_TIMEOUT; 4905006a76cSDavid Howells to->to_exponential = 1; 4915006a76cSDavid Howells break; 492259875efSTrond Myklebust default: 493259875efSTrond Myklebust BUG(); 4945006a76cSDavid Howells } 4955006a76cSDavid Howells } 49689d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_init_timeout_values); 4975006a76cSDavid Howells 4985006a76cSDavid Howells /* 4995006a76cSDavid Howells * Create an RPC client handle 5005006a76cSDavid Howells */ 501428360d7SBryan Schumaker int nfs_create_rpc_client(struct nfs_client *clp, 5025c6e5b60STrond Myklebust const struct nfs_client_initdata *cl_init, 5034bf590e0SChuck Lever rpc_authflavor_t flavor) 5045006a76cSDavid Howells { 5055006a76cSDavid Howells struct rpc_clnt *clnt = NULL; 50641877d20SChuck Lever struct rpc_create_args args = { 50773ea666cSChuck Lever .net = clp->cl_net, 50859dca3b2STrond Myklebust .protocol = clp->cl_proto, 5096619079dSTrond Myklebust .nconnect = clp->cl_nconnect, 51041877d20SChuck Lever .address = (struct sockaddr *)&clp->cl_addr, 5116e4cffd7SChuck Lever .addrsize = clp->cl_addrlen, 5125c6e5b60STrond Myklebust .timeout = cl_init->timeparms, 51341877d20SChuck Lever .servername = clp->cl_hostname, 5145c6e5b60STrond Myklebust .nodename = cl_init->nodename, 51541877d20SChuck Lever .program = &nfs_program, 51641877d20SChuck Lever .version = clp->rpc_ops->version, 51741877d20SChuck Lever .authflavor = flavor, 5181a58e8a0STrond Myklebust .cred = cl_init->cred, 519c8407f2eSChuck Lever .xprtsec = cl_init->xprtsec, 52041877d20SChuck Lever }; 5215006a76cSDavid Howells 5224bf590e0SChuck Lever if (test_bit(NFS_CS_DISCRTRY, &clp->cl_flags)) 5234a01b8a4SChuck Lever args.flags |= RPC_CLNT_CREATE_DISCRTRY; 52499875249STrond Myklebust if (test_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags)) 52599875249STrond Myklebust args.flags |= RPC_CLNT_CREATE_NO_RETRANS_TIMEOUT; 5264bf590e0SChuck Lever if (test_bit(NFS_CS_NORESVPORT, &clp->cl_flags)) 5274a01b8a4SChuck Lever args.flags |= RPC_CLNT_CREATE_NONPRIVPORT; 52898f98cf5STrond Myklebust if (test_bit(NFS_CS_INFINITE_SLOTS, &clp->cl_flags)) 52998f98cf5STrond Myklebust args.flags |= RPC_CLNT_CREATE_INFINITE_SLOTS; 5304b1b69ceSTrond Myklebust if (test_bit(NFS_CS_NOPING, &clp->cl_flags)) 5314b1b69ceSTrond Myklebust args.flags |= RPC_CLNT_CREATE_NOPING; 532e6237b6fSTrond Myklebust if (test_bit(NFS_CS_REUSEPORT, &clp->cl_flags)) 533e6237b6fSTrond Myklebust args.flags |= RPC_CLNT_CREATE_REUSEPORT; 5344a01b8a4SChuck Lever 5355006a76cSDavid Howells if (!IS_ERR(clp->cl_rpcclient)) 5365006a76cSDavid Howells return 0; 5375006a76cSDavid Howells 53841877d20SChuck Lever clnt = rpc_create(&args); 5395006a76cSDavid Howells if (IS_ERR(clnt)) { 5405006a76cSDavid Howells dprintk("%s: cannot create RPC client. Error = %ld\n", 5413110ff80SHarvey Harrison __func__, PTR_ERR(clnt)); 5425006a76cSDavid Howells return PTR_ERR(clnt); 5435006a76cSDavid Howells } 5445006a76cSDavid Howells 5455e16923bSNeilBrown clnt->cl_principal = clp->cl_principal; 5465006a76cSDavid Howells clp->cl_rpcclient = clnt; 547dc48e0abSOlga Kornievskaia clnt->cl_max_connect = clp->cl_max_connect; 5485006a76cSDavid Howells return 0; 5495006a76cSDavid Howells } 55089d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_create_rpc_client); 55154ceac45SDavid Howells 55254ceac45SDavid Howells /* 55354ceac45SDavid Howells * Version 2 or 3 client destruction 55454ceac45SDavid Howells */ 55554ceac45SDavid Howells static void nfs_destroy_server(struct nfs_server *server) 55654ceac45SDavid Howells { 557f259613aSNeilBrown if (server->nlm_host) 5589289e7f9SChuck Lever nlmclnt_done(server->nlm_host); 55954ceac45SDavid Howells } 56054ceac45SDavid Howells 56154ceac45SDavid Howells /* 56254ceac45SDavid Howells * Version 2 or 3 lockd setup 56354ceac45SDavid Howells */ 56454ceac45SDavid Howells static int nfs_start_lockd(struct nfs_server *server) 56554ceac45SDavid Howells { 5669289e7f9SChuck Lever struct nlm_host *host; 5679289e7f9SChuck Lever struct nfs_client *clp = server->nfs_client; 568883bb163SChuck Lever struct nlmclnt_initdata nlm_init = { 569883bb163SChuck Lever .hostname = clp->cl_hostname, 570883bb163SChuck Lever .address = (struct sockaddr *)&clp->cl_addr, 571883bb163SChuck Lever .addrlen = clp->cl_addrlen, 572883bb163SChuck Lever .nfs_version = clp->rpc_ops->version, 5730cb2659bSChuck Lever .noresvport = server->flags & NFS_MOUNT_NORESVPORT ? 5740cb2659bSChuck Lever 1 : 0, 57573ea666cSChuck Lever .net = clp->cl_net, 576b1ece737SBenjamin Coddington .nlmclnt_ops = clp->cl_nfs_mod->rpc_ops->nlmclnt_ops, 577d18a9d3fSSargun Dhillon .cred = server->cred, 578883bb163SChuck Lever }; 57954ceac45SDavid Howells 580883bb163SChuck Lever if (nlm_init.nfs_version > 3) 5819289e7f9SChuck Lever return 0; 5825eebde23SSuresh Jayaraman if ((server->flags & NFS_MOUNT_LOCAL_FLOCK) && 5835eebde23SSuresh Jayaraman (server->flags & NFS_MOUNT_LOCAL_FCNTL)) 5849289e7f9SChuck Lever return 0; 5859289e7f9SChuck Lever 5868a6e5debSTrond Myklebust switch (clp->cl_proto) { 5878a6e5debSTrond Myklebust default: 5888a6e5debSTrond Myklebust nlm_init.protocol = IPPROTO_TCP; 5898a6e5debSTrond Myklebust break; 590b24ee6c6SOlga Kornievskaia #ifndef CONFIG_NFS_DISABLE_UDP_SUPPORT 5918a6e5debSTrond Myklebust case XPRT_TRANSPORT_UDP: 5928a6e5debSTrond Myklebust nlm_init.protocol = IPPROTO_UDP; 593b24ee6c6SOlga Kornievskaia #endif 5948a6e5debSTrond Myklebust } 5958a6e5debSTrond Myklebust 596883bb163SChuck Lever host = nlmclnt_init(&nlm_init); 5979289e7f9SChuck Lever if (IS_ERR(host)) 5989289e7f9SChuck Lever return PTR_ERR(host); 5999289e7f9SChuck Lever 6009289e7f9SChuck Lever server->nlm_host = host; 60154ceac45SDavid Howells server->destroy = nfs_destroy_server; 602d97c0589SBenjamin Coddington nfs_sysfs_link_rpc_client(server, nlmclnt_rpc_clnt(host), NULL); 6039289e7f9SChuck Lever return 0; 60454ceac45SDavid Howells } 60554ceac45SDavid Howells 60654ceac45SDavid Howells /* 60754ceac45SDavid Howells * Create a general RPC client 60854ceac45SDavid Howells */ 609fcf10398SBryan Schumaker int nfs_init_server_rpcclient(struct nfs_server *server, 61033170233STrond Myklebust const struct rpc_timeout *timeo, 61133170233STrond Myklebust rpc_authflavor_t pseudoflavour) 61254ceac45SDavid Howells { 61354ceac45SDavid Howells struct nfs_client *clp = server->nfs_client; 61454ceac45SDavid Howells 615ba9b584cSChuck Lever server->client = rpc_clone_client_set_auth(clp->cl_rpcclient, 616ba9b584cSChuck Lever pseudoflavour); 61754ceac45SDavid Howells if (IS_ERR(server->client)) { 6183110ff80SHarvey Harrison dprintk("%s: couldn't create rpc_client!\n", __func__); 61954ceac45SDavid Howells return PTR_ERR(server->client); 62054ceac45SDavid Howells } 62154ceac45SDavid Howells 62233170233STrond Myklebust memcpy(&server->client->cl_timeout_default, 62333170233STrond Myklebust timeo, 62433170233STrond Myklebust sizeof(server->client->cl_timeout_default)); 62533170233STrond Myklebust server->client->cl_timeout = &server->client->cl_timeout_default; 62654ceac45SDavid Howells server->client->cl_softrtry = 0; 62791a575e1STrond Myklebust if (server->flags & NFS_MOUNT_SOFTERR) 62891a575e1STrond Myklebust server->client->cl_softerr = 1; 62954ceac45SDavid Howells if (server->flags & NFS_MOUNT_SOFT) 63054ceac45SDavid Howells server->client->cl_softrtry = 1; 63154ceac45SDavid Howells 632e13b5493SBenjamin Coddington nfs_sysfs_link_rpc_client(server, server->client, NULL); 63354ceac45SDavid Howells return 0; 63454ceac45SDavid Howells } 63589d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_init_server_rpcclient); 63654ceac45SDavid Howells 6378cab4c39SChuck Lever /** 6388cab4c39SChuck Lever * nfs_init_client - Initialise an NFS2 or NFS3 client 6398cab4c39SChuck Lever * 6408cab4c39SChuck Lever * @clp: nfs_client to initialise 6415c6e5b60STrond Myklebust * @cl_init: Initialisation parameters 6428cab4c39SChuck Lever * 6438cab4c39SChuck Lever * Returns pointer to an NFS client, or an ERR_PTR value. 64454ceac45SDavid Howells */ 6458cab4c39SChuck Lever struct nfs_client *nfs_init_client(struct nfs_client *clp, 6465c6e5b60STrond Myklebust const struct nfs_client_initdata *cl_init) 64754ceac45SDavid Howells { 64854ceac45SDavid Howells int error; 64954ceac45SDavid Howells 65054ceac45SDavid Howells /* the client is already initialised */ 6512844b6aeSAnna Schumaker if (clp->cl_cons_state == NFS_CS_READY) 6528cab4c39SChuck Lever return clp; 65354ceac45SDavid Howells 65454ceac45SDavid Howells /* 65554ceac45SDavid Howells * Create a client RPC handle for doing FSSTAT with UNIX auth only 65654ceac45SDavid Howells * - RFC 2623, sec 2.3.2 65754ceac45SDavid Howells */ 6585c6e5b60STrond Myklebust error = nfs_create_rpc_client(clp, cl_init, RPC_AUTH_UNIX); 6592844b6aeSAnna Schumaker nfs_mark_client_ready(clp, error == 0 ? NFS_CS_READY : error); 6602844b6aeSAnna Schumaker if (error < 0) { 6618cab4c39SChuck Lever nfs_put_client(clp); 6622844b6aeSAnna Schumaker clp = ERR_PTR(error); 6632844b6aeSAnna Schumaker } 6642844b6aeSAnna Schumaker return clp; 66554ceac45SDavid Howells } 666ddda8e0aSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_init_client); 66754ceac45SDavid Howells 66854ceac45SDavid Howells /* 66954ceac45SDavid Howells * Create a version 2 or 3 client 67054ceac45SDavid Howells */ 6712283f8d6S\"Talpey, Thomas\ static int nfs_init_server(struct nfs_server *server, 67262a55d08SScott Mayhew const struct fs_context *fc) 67354ceac45SDavid Howells { 67462a55d08SScott Mayhew const struct nfs_fs_context *ctx = nfs_fc2context(fc); 6755c6e5b60STrond Myklebust struct rpc_timeout timeparms; 6763a498026STrond Myklebust struct nfs_client_initdata cl_init = { 67738465f5dSScott Mayhew .hostname = ctx->nfs_server.hostname, 678cf0d7e7fSKees Cook .addr = &ctx->nfs_server._address, 67938465f5dSScott Mayhew .addrlen = ctx->nfs_server.addrlen, 68062a55d08SScott Mayhew .nfs_mod = ctx->nfs_mod, 68138465f5dSScott Mayhew .proto = ctx->nfs_server.protocol, 68262a55d08SScott Mayhew .net = fc->net_ns, 6835c6e5b60STrond Myklebust .timeparms = &timeparms, 6841a58e8a0STrond Myklebust .cred = server->cred, 68538465f5dSScott Mayhew .nconnect = ctx->nfs_server.nconnect, 686e6237b6fSTrond Myklebust .init_flags = (1UL << NFS_CS_REUSEPORT), 687c8407f2eSChuck Lever .xprtsec = ctx->xprtsec, 6883a498026STrond Myklebust }; 68954ceac45SDavid Howells struct nfs_client *clp; 6903a498026STrond Myklebust int error; 69154ceac45SDavid Howells 69238465f5dSScott Mayhew nfs_init_timeout_values(&timeparms, ctx->nfs_server.protocol, 69338465f5dSScott Mayhew ctx->timeo, ctx->retrans); 69438465f5dSScott Mayhew if (ctx->flags & NFS_MOUNT_NORESVPORT) 6954bf590e0SChuck Lever set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags); 69645a52a02SAndy Adamson 69754ceac45SDavid Howells /* Allocate or find a client reference we can use */ 6987d38de3fSAnna Schumaker clp = nfs_get_client(&cl_init); 6994cbb9768SAnna Schumaker if (IS_ERR(clp)) 70054ceac45SDavid Howells return PTR_ERR(clp); 70154ceac45SDavid Howells 70254ceac45SDavid Howells server->nfs_client = clp; 7031c725118SBenjamin Coddington nfs_sysfs_add_server(server); 704e13b5493SBenjamin Coddington nfs_sysfs_link_rpc_client(server, clp->cl_rpcclient, "_state"); 70554ceac45SDavid Howells 70654ceac45SDavid Howells /* Initialise the client representation from the mount data */ 70738465f5dSScott Mayhew server->flags = ctx->flags; 70838465f5dSScott Mayhew server->options = ctx->options; 709ce62b114STrond Myklebust server->caps |= NFS_CAP_HARDLINKS | NFS_CAP_SYMLINKS; 710ce62b114STrond Myklebust 711ce62b114STrond Myklebust switch (clp->rpc_ops->version) { 712ce62b114STrond Myklebust case 2: 713ce62b114STrond Myklebust server->fattr_valid = NFS_ATTR_FATTR_V2; 714ce62b114STrond Myklebust break; 715ce62b114STrond Myklebust case 3: 716ce62b114STrond Myklebust server->fattr_valid = NFS_ATTR_FATTR_V3; 717ce62b114STrond Myklebust break; 718ce62b114STrond Myklebust default: 719ce62b114STrond Myklebust server->fattr_valid = NFS_ATTR_FATTR_V4; 720ce62b114STrond Myklebust } 72154ceac45SDavid Howells 72238465f5dSScott Mayhew if (ctx->rsize) 723940261a1SAnna Schumaker server->rsize = nfs_io_size(ctx->rsize, clp->cl_proto); 72438465f5dSScott Mayhew if (ctx->wsize) 725940261a1SAnna Schumaker server->wsize = nfs_io_size(ctx->wsize, clp->cl_proto); 72654ceac45SDavid Howells 72738465f5dSScott Mayhew server->acregmin = ctx->acregmin * HZ; 72838465f5dSScott Mayhew server->acregmax = ctx->acregmax * HZ; 72938465f5dSScott Mayhew server->acdirmin = ctx->acdirmin * HZ; 73038465f5dSScott Mayhew server->acdirmax = ctx->acdirmax * HZ; 73154ceac45SDavid Howells 73254ceac45SDavid Howells /* Start lockd here, before we might error out */ 73354ceac45SDavid Howells error = nfs_start_lockd(server); 73454ceac45SDavid Howells if (error < 0) 73554ceac45SDavid Howells goto error; 73654ceac45SDavid Howells 73738465f5dSScott Mayhew server->port = ctx->nfs_server.port; 73838465f5dSScott Mayhew server->auth_info = ctx->auth_info; 739f22d6d79SChuck Lever 740a3f73c27SWeston Andros Adamson error = nfs_init_server_rpcclient(server, &timeparms, 74138465f5dSScott Mayhew ctx->selected_flavor); 74254ceac45SDavid Howells if (error < 0) 74354ceac45SDavid Howells goto error; 74454ceac45SDavid Howells 7453f8400d1SChuck Lever /* Preserve the values of mount_server-related mount options */ 74638465f5dSScott Mayhew if (ctx->mount_server.addrlen) { 74738465f5dSScott Mayhew memcpy(&server->mountd_address, &ctx->mount_server.address, 74838465f5dSScott Mayhew ctx->mount_server.addrlen); 74938465f5dSScott Mayhew server->mountd_addrlen = ctx->mount_server.addrlen; 7503f8400d1SChuck Lever } 75138465f5dSScott Mayhew server->mountd_version = ctx->mount_server.version; 75238465f5dSScott Mayhew server->mountd_port = ctx->mount_server.port; 75338465f5dSScott Mayhew server->mountd_protocol = ctx->mount_server.protocol; 7543f8400d1SChuck Lever 75538465f5dSScott Mayhew server->namelen = ctx->namlen; 75654ceac45SDavid Howells return 0; 75754ceac45SDavid Howells 75854ceac45SDavid Howells error: 75954ceac45SDavid Howells server->nfs_client = NULL; 76054ceac45SDavid Howells nfs_put_client(clp); 76154ceac45SDavid Howells return error; 76254ceac45SDavid Howells } 76354ceac45SDavid Howells 76454ceac45SDavid Howells /* 76554ceac45SDavid Howells * Load up the server record from information gained in an fsinfo record 76654ceac45SDavid Howells */ 767738fd0f3SBenny Halevy static void nfs_server_set_fsinfo(struct nfs_server *server, 768738fd0f3SBenny Halevy struct nfs_fsinfo *fsinfo) 76954ceac45SDavid Howells { 770940261a1SAnna Schumaker struct nfs_client *clp = server->nfs_client; 77104a5da69SFrank van der Linden unsigned long max_rpc_payload, raw_max_rpc_payload; 77254ceac45SDavid Howells 77354ceac45SDavid Howells /* Work out a lot of parameters */ 77454ceac45SDavid Howells if (server->rsize == 0) 775940261a1SAnna Schumaker server->rsize = nfs_io_size(fsinfo->rtpref, clp->cl_proto); 77654ceac45SDavid Howells if (server->wsize == 0) 777940261a1SAnna Schumaker server->wsize = nfs_io_size(fsinfo->wtpref, clp->cl_proto); 77854ceac45SDavid Howells 77954ceac45SDavid Howells if (fsinfo->rtmax >= 512 && server->rsize > fsinfo->rtmax) 780940261a1SAnna Schumaker server->rsize = nfs_io_size(fsinfo->rtmax, clp->cl_proto); 78154ceac45SDavid Howells if (fsinfo->wtmax >= 512 && server->wsize > fsinfo->wtmax) 782940261a1SAnna Schumaker server->wsize = nfs_io_size(fsinfo->wtmax, clp->cl_proto); 78354ceac45SDavid Howells 78404a5da69SFrank van der Linden raw_max_rpc_payload = rpc_max_payload(server->client); 78504a5da69SFrank van der Linden max_rpc_payload = nfs_block_size(raw_max_rpc_payload, NULL); 78604a5da69SFrank van der Linden 78754ceac45SDavid Howells if (server->rsize > max_rpc_payload) 78854ceac45SDavid Howells server->rsize = max_rpc_payload; 78954ceac45SDavid Howells if (server->rsize > NFS_MAX_FILE_IO_SIZE) 79054ceac45SDavid Howells server->rsize = NFS_MAX_FILE_IO_SIZE; 79109cbfeafSKirill A. Shutemov server->rpages = (server->rsize + PAGE_SIZE - 1) >> PAGE_SHIFT; 792e0bf68ddSPeter Zijlstra 79354ceac45SDavid Howells if (server->wsize > max_rpc_payload) 79454ceac45SDavid Howells server->wsize = max_rpc_payload; 79554ceac45SDavid Howells if (server->wsize > NFS_MAX_FILE_IO_SIZE) 79654ceac45SDavid Howells server->wsize = NFS_MAX_FILE_IO_SIZE; 79709cbfeafSKirill A. Shutemov server->wpages = (server->wsize + PAGE_SIZE - 1) >> PAGE_SHIFT; 79885e174baSRicardo Labiaga 79954ceac45SDavid Howells server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL); 80054ceac45SDavid Howells 80154ceac45SDavid Howells server->dtsize = nfs_block_size(fsinfo->dtpref, NULL); 8021a34c8c9STrond Myklebust if (server->dtsize > NFS_MAX_FILE_IO_SIZE) 8031a34c8c9STrond Myklebust server->dtsize = NFS_MAX_FILE_IO_SIZE; 80454ceac45SDavid Howells if (server->dtsize > server->rsize) 80554ceac45SDavid Howells server->dtsize = server->rsize; 80654ceac45SDavid Howells 80754ceac45SDavid Howells if (server->flags & NFS_MOUNT_NOAC) { 80854ceac45SDavid Howells server->acregmin = server->acregmax = 0; 80954ceac45SDavid Howells server->acdirmin = server->acdirmax = 0; 81054ceac45SDavid Howells } 81154ceac45SDavid Howells 81254ceac45SDavid Howells server->maxfilesize = fsinfo->maxfilesize; 81354ceac45SDavid Howells 8146b96724eSRicardo Labiaga server->time_delta = fsinfo->time_delta; 8157f08a335STrond Myklebust server->change_attr_type = fsinfo->change_attr_type; 8166b96724eSRicardo Labiaga 8172a92ee92SPeng Tao server->clone_blksize = fsinfo->clone_blksize; 81854ceac45SDavid Howells /* We're airborne Set socket buffersize */ 81954ceac45SDavid Howells rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100); 82004a5da69SFrank van der Linden 82104a5da69SFrank van der Linden #ifdef CONFIG_NFS_V4_2 82204a5da69SFrank van der Linden /* 82304a5da69SFrank van der Linden * Defaults until limited by the session parameters. 82404a5da69SFrank van der Linden */ 82504a5da69SFrank van der Linden server->gxasize = min_t(unsigned int, raw_max_rpc_payload, 82604a5da69SFrank van der Linden XATTR_SIZE_MAX); 82704a5da69SFrank van der Linden server->sxasize = min_t(unsigned int, raw_max_rpc_payload, 82804a5da69SFrank van der Linden XATTR_SIZE_MAX); 82904a5da69SFrank van der Linden server->lxasize = min_t(unsigned int, raw_max_rpc_payload, 83004a5da69SFrank van der Linden nfs42_listxattr_xdrsize(XATTR_LIST_MAX)); 831b78ef845SFrank van der Linden 832b78ef845SFrank van der Linden if (fsinfo->xattr_support) 833b78ef845SFrank van der Linden server->caps |= NFS_CAP_XATTR; 83404a5da69SFrank van der Linden #endif 83554ceac45SDavid Howells } 83654ceac45SDavid Howells 83754ceac45SDavid Howells /* 83854ceac45SDavid Howells * Probe filesystem information, including the FSID on v2/v3 83954ceac45SDavid Howells */ 8405fe1210dSAnna Schumaker static int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr) 84154ceac45SDavid Howells { 84254ceac45SDavid Howells struct nfs_fsinfo fsinfo; 84354ceac45SDavid Howells struct nfs_client *clp = server->nfs_client; 84454ceac45SDavid Howells int error; 84554ceac45SDavid Howells 84654ceac45SDavid Howells if (clp->rpc_ops->set_capabilities != NULL) { 84754ceac45SDavid Howells error = clp->rpc_ops->set_capabilities(server, mntfh); 84854ceac45SDavid Howells if (error < 0) 8494cbb9768SAnna Schumaker return error; 85054ceac45SDavid Howells } 85154ceac45SDavid Howells 85254ceac45SDavid Howells fsinfo.fattr = fattr; 853ca440c38SJeff Layton fsinfo.nlayouttypes = 0; 8543132e49eSJeff Layton memset(fsinfo.layouttype, 0, sizeof(fsinfo.layouttype)); 85554ceac45SDavid Howells error = clp->rpc_ops->fsinfo(server, mntfh, &fsinfo); 85654ceac45SDavid Howells if (error < 0) 8574cbb9768SAnna Schumaker return error; 85854ceac45SDavid Howells 85971f81e51SKinglong Mee nfs_server_set_fsinfo(server, &fsinfo); 86054ceac45SDavid Howells 86154ceac45SDavid Howells /* Get some general file system info */ 86254ceac45SDavid Howells if (server->namelen == 0) { 86354ceac45SDavid Howells struct nfs_pathconf pathinfo; 86454ceac45SDavid Howells 86554ceac45SDavid Howells pathinfo.fattr = fattr; 86654ceac45SDavid Howells nfs_fattr_init(fattr); 86754ceac45SDavid Howells 86854ceac45SDavid Howells if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0) 86954ceac45SDavid Howells server->namelen = pathinfo.max_namelen; 87054ceac45SDavid Howells } 87154ceac45SDavid Howells 8721976b2b3SOlga Kornievskaia if (clp->rpc_ops->discover_trunking != NULL && 873a43bf604SOlga Kornievskaia (server->caps & NFS_CAP_FS_LOCATIONS && 874a43bf604SOlga Kornievskaia (server->flags & NFS_MOUNT_TRUNK_DISCOVERY))) { 8751976b2b3SOlga Kornievskaia error = clp->rpc_ops->discover_trunking(server, mntfh); 8761976b2b3SOlga Kornievskaia if (error < 0) 8771976b2b3SOlga Kornievskaia return error; 8781976b2b3SOlga Kornievskaia } 8791976b2b3SOlga Kornievskaia 88054ceac45SDavid Howells return 0; 88154ceac45SDavid Howells } 88254ceac45SDavid Howells 88354ceac45SDavid Howells /* 884e5731131SAnna Schumaker * Grab the destination's particulars, including lease expiry time. 885e5731131SAnna Schumaker * 886e5731131SAnna Schumaker * Returns zero if probe succeeded and retrieved FSID matches the FSID 887e5731131SAnna Schumaker * we have cached. 888e5731131SAnna Schumaker */ 889e5731131SAnna Schumaker int nfs_probe_server(struct nfs_server *server, struct nfs_fh *mntfh) 890e5731131SAnna Schumaker { 891e5731131SAnna Schumaker struct nfs_fattr *fattr; 892e5731131SAnna Schumaker int error; 893e5731131SAnna Schumaker 894e5731131SAnna Schumaker fattr = nfs_alloc_fattr(); 895e5731131SAnna Schumaker if (fattr == NULL) 896e5731131SAnna Schumaker return -ENOMEM; 897e5731131SAnna Schumaker 898e5731131SAnna Schumaker /* Sanity: the probe won't work if the destination server 899e5731131SAnna Schumaker * does not recognize the migrated FH. */ 900e5731131SAnna Schumaker error = nfs_probe_fsinfo(server, mntfh, fattr); 901e5731131SAnna Schumaker 902e5731131SAnna Schumaker nfs_free_fattr(fattr); 903e5731131SAnna Schumaker return error; 904e5731131SAnna Schumaker } 905e5731131SAnna Schumaker EXPORT_SYMBOL_GPL(nfs_probe_server); 906e5731131SAnna Schumaker 907e5731131SAnna Schumaker /* 90854ceac45SDavid Howells * Copy useful information when duplicating a server record 90954ceac45SDavid Howells */ 910fcf10398SBryan Schumaker void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source) 91154ceac45SDavid Howells { 91254ceac45SDavid Howells target->flags = source->flags; 913356e76b8SChuck Lever target->rsize = source->rsize; 914356e76b8SChuck Lever target->wsize = source->wsize; 91554ceac45SDavid Howells target->acregmin = source->acregmin; 91654ceac45SDavid Howells target->acregmax = source->acregmax; 91754ceac45SDavid Howells target->acdirmin = source->acdirmin; 91854ceac45SDavid Howells target->acdirmax = source->acdirmax; 91954ceac45SDavid Howells target->caps = source->caps; 9202df54806SDavid Howells target->options = source->options; 9210f5f49b8SWeston Andros Adamson target->auth_info = source->auth_info; 92289a6814dSSteve Dickson target->port = source->port; 92354ceac45SDavid Howells } 92489d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_server_copy_userdata); 92554ceac45SDavid Howells 926fcf10398SBryan Schumaker void nfs_server_insert_lists(struct nfs_server *server) 927fca5238eSChuck Lever { 928fca5238eSChuck Lever struct nfs_client *clp = server->nfs_client; 92973ea666cSChuck Lever struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); 930fca5238eSChuck Lever 931dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 932fca5238eSChuck Lever list_add_tail_rcu(&server->client_link, &clp->cl_superblocks); 933c25d32b2SStanislav Kinsbursky list_add_tail(&server->master_link, &nn->nfs_volume_list); 934d3b4c9d7SAndy Adamson clear_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state); 935dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 936fca5238eSChuck Lever 937fca5238eSChuck Lever } 93889d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_server_insert_lists); 939fca5238eSChuck Lever 94032e62b7cSChuck Lever void nfs_server_remove_lists(struct nfs_server *server) 941fca5238eSChuck Lever { 942d3b4c9d7SAndy Adamson struct nfs_client *clp = server->nfs_client; 9434c03ae4aSTrond Myklebust struct nfs_net *nn; 944d3b4c9d7SAndy Adamson 9454c03ae4aSTrond Myklebust if (clp == NULL) 9464c03ae4aSTrond Myklebust return; 94773ea666cSChuck Lever nn = net_generic(clp->cl_net, nfs_net_id); 948dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 949fca5238eSChuck Lever list_del_rcu(&server->client_link); 9504c03ae4aSTrond Myklebust if (list_empty(&clp->cl_superblocks)) 951d3b4c9d7SAndy Adamson set_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state); 952fca5238eSChuck Lever list_del(&server->master_link); 953dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 954fca5238eSChuck Lever 955fca5238eSChuck Lever synchronize_rcu(); 956fca5238eSChuck Lever } 95732e62b7cSChuck Lever EXPORT_SYMBOL_GPL(nfs_server_remove_lists); 958fca5238eSChuck Lever 9591c725118SBenjamin Coddington static DEFINE_IDA(s_sysfs_ids); 9601c725118SBenjamin Coddington 96154ceac45SDavid Howells /* 96254ceac45SDavid Howells * Allocate and initialise a server record 96354ceac45SDavid Howells */ 964fcf10398SBryan Schumaker struct nfs_server *nfs_alloc_server(void) 96554ceac45SDavid Howells { 96654ceac45SDavid Howells struct nfs_server *server; 96754ceac45SDavid Howells 96854ceac45SDavid Howells server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL); 96954ceac45SDavid Howells if (!server) 97054ceac45SDavid Howells return NULL; 97154ceac45SDavid Howells 9721c725118SBenjamin Coddington server->s_sysfs_id = ida_alloc(&s_sysfs_ids, GFP_KERNEL); 9731c725118SBenjamin Coddington if (server->s_sysfs_id < 0) { 9741c725118SBenjamin Coddington kfree(server); 9751c725118SBenjamin Coddington return NULL; 9761c725118SBenjamin Coddington } 9771c725118SBenjamin Coddington 97854ceac45SDavid Howells server->client = server->client_acl = ERR_PTR(-EINVAL); 97954ceac45SDavid Howells 98054ceac45SDavid Howells /* Zero out the NFS state stuff */ 98154ceac45SDavid Howells INIT_LIST_HEAD(&server->client_link); 98254ceac45SDavid Howells INIT_LIST_HEAD(&server->master_link); 983d3978bb3SChuck Lever INIT_LIST_HEAD(&server->delegations); 9846382a441SWeston Andros Adamson INIT_LIST_HEAD(&server->layouts); 9850aaaf5c4SChuck Lever INIT_LIST_HEAD(&server->state_owners_lru); 98662164f31SOlga Kornievskaia INIT_LIST_HEAD(&server->ss_copies); 98754ceac45SDavid Howells 988ef818a28SSteve Dickson atomic_set(&server->active, 0); 989ef818a28SSteve Dickson 99054ceac45SDavid Howells server->io_stats = nfs_alloc_iostats(); 99154ceac45SDavid Howells if (!server->io_stats) { 99254ceac45SDavid Howells kfree(server); 99354ceac45SDavid Howells return NULL; 99454ceac45SDavid Howells } 99554ceac45SDavid Howells 9967f08a335STrond Myklebust server->change_attr_type = NFS4_CHANGE_TYPE_IS_UNDEFINED; 9977f08a335STrond Myklebust 9989157c31dSTrond Myklebust ida_init(&server->openowner_id); 999d2d7ce28STrond Myklebust ida_init(&server->lockowner_id); 1000f7e8917aSFred Isaman pnfs_init_server(server); 100168ebf8feSBenjamin Coddington rpc_init_wait_queue(&server->uoc_rpcwaitq, "NFS UOC"); 1002f7e8917aSFred Isaman 100354ceac45SDavid Howells return server; 100454ceac45SDavid Howells } 100589d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_alloc_server); 100654ceac45SDavid Howells 100754ceac45SDavid Howells /* 100854ceac45SDavid Howells * Free up a server record 100954ceac45SDavid Howells */ 101054ceac45SDavid Howells void nfs_free_server(struct nfs_server *server) 101154ceac45SDavid Howells { 1012fca5238eSChuck Lever nfs_server_remove_lists(server); 101354ceac45SDavid Howells 101454ceac45SDavid Howells if (server->destroy != NULL) 101554ceac45SDavid Howells server->destroy(server); 10165cef338bSTrond Myklebust 10175cef338bSTrond Myklebust if (!IS_ERR(server->client_acl)) 10185cef338bSTrond Myklebust rpc_shutdown_client(server->client_acl); 101954ceac45SDavid Howells if (!IS_ERR(server->client)) 102054ceac45SDavid Howells rpc_shutdown_client(server->client); 102154ceac45SDavid Howells 102254ceac45SDavid Howells nfs_put_client(server->nfs_client); 102354ceac45SDavid Howells 1024*e901f17bSBenjamin Coddington if (server->kobj.state_initialized) { 10251c725118SBenjamin Coddington nfs_sysfs_remove_server(server); 10261c725118SBenjamin Coddington kobject_put(&server->kobj); 1027*e901f17bSBenjamin Coddington } 10281c725118SBenjamin Coddington ida_free(&s_sysfs_ids, server->s_sysfs_id); 10291c725118SBenjamin Coddington 1030d2d7ce28STrond Myklebust ida_destroy(&server->lockowner_id); 10319157c31dSTrond Myklebust ida_destroy(&server->openowner_id); 103254ceac45SDavid Howells nfs_free_iostats(server->io_stats); 10331a58e8a0STrond Myklebust put_cred(server->cred); 103454ceac45SDavid Howells kfree(server); 103554ceac45SDavid Howells nfs_release_automount_timer(); 103654ceac45SDavid Howells } 103789d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_free_server); 103854ceac45SDavid Howells 103954ceac45SDavid Howells /* 104054ceac45SDavid Howells * Create a version 2 or 3 volume record 104154ceac45SDavid Howells * - keyed on server and FSID 104254ceac45SDavid Howells */ 104362a55d08SScott Mayhew struct nfs_server *nfs_create_server(struct fs_context *fc) 104454ceac45SDavid Howells { 104562a55d08SScott Mayhew struct nfs_fs_context *ctx = nfs_fc2context(fc); 104654ceac45SDavid Howells struct nfs_server *server; 1047fbca779aSTrond Myklebust struct nfs_fattr *fattr; 104854ceac45SDavid Howells int error; 104954ceac45SDavid Howells 105054ceac45SDavid Howells server = nfs_alloc_server(); 105154ceac45SDavid Howells if (!server) 105254ceac45SDavid Howells return ERR_PTR(-ENOMEM); 105354ceac45SDavid Howells 1054d18a9d3fSSargun Dhillon server->cred = get_cred(fc->cred); 10551a58e8a0STrond Myklebust 1056fbca779aSTrond Myklebust error = -ENOMEM; 1057fbca779aSTrond Myklebust fattr = nfs_alloc_fattr(); 1058fbca779aSTrond Myklebust if (fattr == NULL) 1059fbca779aSTrond Myklebust goto error; 1060fbca779aSTrond Myklebust 106154ceac45SDavid Howells /* Get a client representation */ 106262a55d08SScott Mayhew error = nfs_init_server(server, fc); 106354ceac45SDavid Howells if (error < 0) 106454ceac45SDavid Howells goto error; 106554ceac45SDavid Howells 106654ceac45SDavid Howells /* Probe the root fh to retrieve its FSID */ 106762a55d08SScott Mayhew error = nfs_probe_fsinfo(server, ctx->mntfh, fattr); 106854ceac45SDavid Howells if (error < 0) 106954ceac45SDavid Howells goto error; 107054af3bb5STrond Myklebust if (server->nfs_client->rpc_ops->version == 3) { 107154af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) 107254af3bb5STrond Myklebust server->namelen = NFS3_MAXNAMLEN; 107362a55d08SScott Mayhew if (!(ctx->flags & NFS_MOUNT_NORDIRPLUS)) 107454af3bb5STrond Myklebust server->caps |= NFS_CAP_READDIRPLUS; 107554af3bb5STrond Myklebust } else { 107654af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN) 107754af3bb5STrond Myklebust server->namelen = NFS2_MAXNAMLEN; 107854af3bb5STrond Myklebust } 107954af3bb5STrond Myklebust 1080fbca779aSTrond Myklebust if (!(fattr->valid & NFS_ATTR_FATTR)) { 108162a55d08SScott Mayhew error = ctx->nfs_mod->rpc_ops->getattr(server, ctx->mntfh, 10822ef61e0eSAnna Schumaker fattr, NULL); 108354ceac45SDavid Howells if (error < 0) { 108454ceac45SDavid Howells dprintk("nfs_create_server: getattr error = %d\n", -error); 108554ceac45SDavid Howells goto error; 108654ceac45SDavid Howells } 108754ceac45SDavid Howells } 1088fbca779aSTrond Myklebust memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid)); 108954ceac45SDavid Howells 10906daabf1bSDavid Howells dprintk("Server FSID: %llx:%llx\n", 10916daabf1bSDavid Howells (unsigned long long) server->fsid.major, 10926daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 109354ceac45SDavid Howells 1094fca5238eSChuck Lever nfs_server_insert_lists(server); 109554ceac45SDavid Howells server->mount_time = jiffies; 1096fbca779aSTrond Myklebust nfs_free_fattr(fattr); 109754ceac45SDavid Howells return server; 109854ceac45SDavid Howells 109954ceac45SDavid Howells error: 1100fbca779aSTrond Myklebust nfs_free_fattr(fattr); 110154ceac45SDavid Howells nfs_free_server(server); 110254ceac45SDavid Howells return ERR_PTR(error); 110354ceac45SDavid Howells } 1104ddda8e0aSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_create_server); 110554ceac45SDavid Howells 110654ceac45SDavid Howells /* 110754ceac45SDavid Howells * Clone an NFS2, NFS3 or NFS4 server record 110854ceac45SDavid Howells */ 110954ceac45SDavid Howells struct nfs_server *nfs_clone_server(struct nfs_server *source, 111054ceac45SDavid Howells struct nfs_fh *fh, 11117e6eb683SBryan Schumaker struct nfs_fattr *fattr, 11127e6eb683SBryan Schumaker rpc_authflavor_t flavor) 111354ceac45SDavid Howells { 111454ceac45SDavid Howells struct nfs_server *server; 111554ceac45SDavid Howells int error; 111654ceac45SDavid Howells 111754ceac45SDavid Howells server = nfs_alloc_server(); 111854ceac45SDavid Howells if (!server) 111954ceac45SDavid Howells return ERR_PTR(-ENOMEM); 112054ceac45SDavid Howells 11211a58e8a0STrond Myklebust server->cred = get_cred(source->cred); 11221a58e8a0STrond Myklebust 112354ceac45SDavid Howells /* Copy data from the source */ 112454ceac45SDavid Howells server->nfs_client = source->nfs_client; 11250aaaf5c4SChuck Lever server->destroy = source->destroy; 1126212bf41dSElena Reshetova refcount_inc(&server->nfs_client->cl_count); 112754ceac45SDavid Howells nfs_server_copy_userdata(server, source); 112854ceac45SDavid Howells 112954ceac45SDavid Howells server->fsid = fattr->fsid; 113054ceac45SDavid Howells 11311c725118SBenjamin Coddington nfs_sysfs_add_server(server); 11321c725118SBenjamin Coddington 1133e13b5493SBenjamin Coddington nfs_sysfs_link_rpc_client(server, 1134e13b5493SBenjamin Coddington server->nfs_client->cl_rpcclient, "_state"); 1135e13b5493SBenjamin Coddington 113633170233STrond Myklebust error = nfs_init_server_rpcclient(server, 113733170233STrond Myklebust source->client->cl_timeout, 11387e6eb683SBryan Schumaker flavor); 113954ceac45SDavid Howells if (error < 0) 114054ceac45SDavid Howells goto out_free_server; 114154ceac45SDavid Howells 114254ceac45SDavid Howells /* probe the filesystem info for this server filesystem */ 11434d4cf8d2SAnna Schumaker error = nfs_probe_server(server, fh); 114454ceac45SDavid Howells if (error < 0) 114554ceac45SDavid Howells goto out_free_server; 114654ceac45SDavid Howells 114754af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 114854af3bb5STrond Myklebust server->namelen = NFS4_MAXNAMLEN; 114954af3bb5STrond Myklebust 115054ceac45SDavid Howells error = nfs_start_lockd(server); 115154ceac45SDavid Howells if (error < 0) 115254ceac45SDavid Howells goto out_free_server; 115354ceac45SDavid Howells 1154fca5238eSChuck Lever nfs_server_insert_lists(server); 115554ceac45SDavid Howells server->mount_time = jiffies; 115654ceac45SDavid Howells 115754ceac45SDavid Howells return server; 115854ceac45SDavid Howells 115954ceac45SDavid Howells out_free_server: 116054ceac45SDavid Howells nfs_free_server(server); 116154ceac45SDavid Howells return ERR_PTR(error); 116254ceac45SDavid Howells } 1163ddda8e0aSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_clone_server); 11646aaca566SDavid Howells 11656b13168bSStanislav Kinsbursky void nfs_clients_init(struct net *net) 11666b13168bSStanislav Kinsbursky { 11676b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(net, nfs_net_id); 11686b13168bSStanislav Kinsbursky 11696b13168bSStanislav Kinsbursky INIT_LIST_HEAD(&nn->nfs_client_list); 1170c25d32b2SStanislav Kinsbursky INIT_LIST_HEAD(&nn->nfs_volume_list); 117189d77c8fSBryan Schumaker #if IS_ENABLED(CONFIG_NFS_V4) 117228cd1b3fSStanislav Kinsbursky idr_init(&nn->cb_ident_idr); 117328cd1b3fSStanislav Kinsbursky #endif 11744c03ae4aSTrond Myklebust spin_lock_init(&nn->nfs_client_lock); 11752f86e091SDeepa Dinamani nn->boot_time = ktime_get_real(); 1176bf11fbdbSTrond Myklebust 1177bf11fbdbSTrond Myklebust nfs_netns_sysfs_setup(nn, net); 11786b13168bSStanislav Kinsbursky } 11796b13168bSStanislav Kinsbursky 118010b7a70cSTrond Myklebust void nfs_clients_exit(struct net *net) 118110b7a70cSTrond Myklebust { 118210b7a70cSTrond Myklebust struct nfs_net *nn = net_generic(net, nfs_net_id); 118310b7a70cSTrond Myklebust 1184bf11fbdbSTrond Myklebust nfs_netns_sysfs_destroy(nn); 118510b7a70cSTrond Myklebust nfs_cleanup_cb_ident_idr(net); 118610b7a70cSTrond Myklebust WARN_ON_ONCE(!list_empty(&nn->nfs_client_list)); 118710b7a70cSTrond Myklebust WARN_ON_ONCE(!list_empty(&nn->nfs_volume_list)); 118810b7a70cSTrond Myklebust } 118910b7a70cSTrond Myklebust 11906aaca566SDavid Howells #ifdef CONFIG_PROC_FS 11916aaca566SDavid Howells static void *nfs_server_list_start(struct seq_file *p, loff_t *pos); 11926aaca566SDavid Howells static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos); 11936aaca566SDavid Howells static void nfs_server_list_stop(struct seq_file *p, void *v); 11946aaca566SDavid Howells static int nfs_server_list_show(struct seq_file *m, void *v); 11956aaca566SDavid Howells 119688e9d34cSJames Morris static const struct seq_operations nfs_server_list_ops = { 11976aaca566SDavid Howells .start = nfs_server_list_start, 11986aaca566SDavid Howells .next = nfs_server_list_next, 11996aaca566SDavid Howells .stop = nfs_server_list_stop, 12006aaca566SDavid Howells .show = nfs_server_list_show, 12016aaca566SDavid Howells }; 12026aaca566SDavid Howells 12036aaca566SDavid Howells static void *nfs_volume_list_start(struct seq_file *p, loff_t *pos); 12046aaca566SDavid Howells static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos); 12056aaca566SDavid Howells static void nfs_volume_list_stop(struct seq_file *p, void *v); 12066aaca566SDavid Howells static int nfs_volume_list_show(struct seq_file *m, void *v); 12076aaca566SDavid Howells 120888e9d34cSJames Morris static const struct seq_operations nfs_volume_list_ops = { 12096aaca566SDavid Howells .start = nfs_volume_list_start, 12106aaca566SDavid Howells .next = nfs_volume_list_next, 12116aaca566SDavid Howells .stop = nfs_volume_list_stop, 12126aaca566SDavid Howells .show = nfs_volume_list_show, 12136aaca566SDavid Howells }; 12146aaca566SDavid Howells 12156aaca566SDavid Howells /* 12166aaca566SDavid Howells * set up the iterator to start reading from the server list and return the first item 12176aaca566SDavid Howells */ 12186aaca566SDavid Howells static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos) 12198d11620eSJeff Layton __acquires(&nn->nfs_client_lock) 12206aaca566SDavid Howells { 122165b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); 12226b13168bSStanislav Kinsbursky 12236aaca566SDavid Howells /* lock the list against modification */ 1224dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 12256b13168bSStanislav Kinsbursky return seq_list_start_head(&nn->nfs_client_list, *_pos); 12266aaca566SDavid Howells } 12276aaca566SDavid Howells 12286aaca566SDavid Howells /* 12296aaca566SDavid Howells * move to next server 12306aaca566SDavid Howells */ 12316aaca566SDavid Howells static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos) 12326aaca566SDavid Howells { 123365b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); 12346b13168bSStanislav Kinsbursky 12356b13168bSStanislav Kinsbursky return seq_list_next(v, &nn->nfs_client_list, pos); 12366aaca566SDavid Howells } 12376aaca566SDavid Howells 12386aaca566SDavid Howells /* 12396aaca566SDavid Howells * clean up after reading from the transports list 12406aaca566SDavid Howells */ 12416aaca566SDavid Howells static void nfs_server_list_stop(struct seq_file *p, void *v) 12428d11620eSJeff Layton __releases(&nn->nfs_client_lock) 12436aaca566SDavid Howells { 124465b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); 1245dc030858SStanislav Kinsbursky 1246dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 12476aaca566SDavid Howells } 12486aaca566SDavid Howells 12496aaca566SDavid Howells /* 12506aaca566SDavid Howells * display a header line followed by a load of call lines 12516aaca566SDavid Howells */ 12526aaca566SDavid Howells static int nfs_server_list_show(struct seq_file *m, void *v) 12536aaca566SDavid Howells { 12546aaca566SDavid Howells struct nfs_client *clp; 125565b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); 12566aaca566SDavid Howells 12576aaca566SDavid Howells /* display header on line 1 */ 12586b13168bSStanislav Kinsbursky if (v == &nn->nfs_client_list) { 12596aaca566SDavid Howells seq_puts(m, "NV SERVER PORT USE HOSTNAME\n"); 12606aaca566SDavid Howells return 0; 12616aaca566SDavid Howells } 12626aaca566SDavid Howells 12636aaca566SDavid Howells /* display one transport per line on subsequent lines */ 12646aaca566SDavid Howells clp = list_entry(v, struct nfs_client, cl_share_link); 12656aaca566SDavid Howells 1266940aab49SMalahal Naineni /* Check if the client is initialized */ 1267940aab49SMalahal Naineni if (clp->cl_cons_state != NFS_CS_READY) 1268940aab49SMalahal Naineni return 0; 1269940aab49SMalahal Naineni 12702446ab60STrond Myklebust rcu_read_lock(); 12715d8515caSChuck Lever seq_printf(m, "v%u %s %s %3d %s\n", 127240c55319STrond Myklebust clp->rpc_ops->version, 12735d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), 12745d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), 1275212bf41dSElena Reshetova refcount_read(&clp->cl_count), 12766aaca566SDavid Howells clp->cl_hostname); 12772446ab60STrond Myklebust rcu_read_unlock(); 12786aaca566SDavid Howells 12796aaca566SDavid Howells return 0; 12806aaca566SDavid Howells } 12816aaca566SDavid Howells 12826aaca566SDavid Howells /* 12836aaca566SDavid Howells * set up the iterator to start reading from the volume list and return the first item 12846aaca566SDavid Howells */ 12856aaca566SDavid Howells static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos) 12868d11620eSJeff Layton __acquires(&nn->nfs_client_lock) 12876aaca566SDavid Howells { 128865b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); 1289c25d32b2SStanislav Kinsbursky 12906aaca566SDavid Howells /* lock the list against modification */ 1291dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 1292c25d32b2SStanislav Kinsbursky return seq_list_start_head(&nn->nfs_volume_list, *_pos); 12936aaca566SDavid Howells } 12946aaca566SDavid Howells 12956aaca566SDavid Howells /* 12966aaca566SDavid Howells * move to next volume 12976aaca566SDavid Howells */ 12986aaca566SDavid Howells static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos) 12996aaca566SDavid Howells { 130065b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); 1301c25d32b2SStanislav Kinsbursky 1302c25d32b2SStanislav Kinsbursky return seq_list_next(v, &nn->nfs_volume_list, pos); 13036aaca566SDavid Howells } 13046aaca566SDavid Howells 13056aaca566SDavid Howells /* 13066aaca566SDavid Howells * clean up after reading from the transports list 13076aaca566SDavid Howells */ 13086aaca566SDavid Howells static void nfs_volume_list_stop(struct seq_file *p, void *v) 13098d11620eSJeff Layton __releases(&nn->nfs_client_lock) 13106aaca566SDavid Howells { 131165b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); 1312dc030858SStanislav Kinsbursky 1313dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 13146aaca566SDavid Howells } 13156aaca566SDavid Howells 13166aaca566SDavid Howells /* 13176aaca566SDavid Howells * display a header line followed by a load of call lines 13186aaca566SDavid Howells */ 13196aaca566SDavid Howells static int nfs_volume_list_show(struct seq_file *m, void *v) 13206aaca566SDavid Howells { 13216aaca566SDavid Howells struct nfs_server *server; 13226aaca566SDavid Howells struct nfs_client *clp; 1323df05a49fSKinglong Mee char dev[13]; // 8 for 2^24, 1 for ':', 3 for 2^8, 1 for '\0' 1324df05a49fSKinglong Mee char fsid[34]; // 2 * 16 for %llx, 1 for ':', 1 for '\0' 132565b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); 13266aaca566SDavid Howells 13276aaca566SDavid Howells /* display header on line 1 */ 1328c25d32b2SStanislav Kinsbursky if (v == &nn->nfs_volume_list) { 1329df05a49fSKinglong Mee seq_puts(m, "NV SERVER PORT DEV FSID" 1330df05a49fSKinglong Mee " FSC\n"); 13316aaca566SDavid Howells return 0; 13326aaca566SDavid Howells } 13336aaca566SDavid Howells /* display one transport per line on subsequent lines */ 13346aaca566SDavid Howells server = list_entry(v, struct nfs_server, master_link); 13356aaca566SDavid Howells clp = server->nfs_client; 13366aaca566SDavid Howells 1337df05a49fSKinglong Mee snprintf(dev, sizeof(dev), "%u:%u", 13386aaca566SDavid Howells MAJOR(server->s_dev), MINOR(server->s_dev)); 13396aaca566SDavid Howells 1340df05a49fSKinglong Mee snprintf(fsid, sizeof(fsid), "%llx:%llx", 13416daabf1bSDavid Howells (unsigned long long) server->fsid.major, 13426daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 13436aaca566SDavid Howells 13442446ab60STrond Myklebust rcu_read_lock(); 1345df05a49fSKinglong Mee seq_printf(m, "v%u %s %s %-12s %-33s %s\n", 134640c55319STrond Myklebust clp->rpc_ops->version, 13475d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), 13485d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), 13496aaca566SDavid Howells dev, 13505d1acff1SDavid Howells fsid, 13515d1acff1SDavid Howells nfs_server_fscache_state(server)); 13522446ab60STrond Myklebust rcu_read_unlock(); 13536aaca566SDavid Howells 13546aaca566SDavid Howells return 0; 13556aaca566SDavid Howells } 13566aaca566SDavid Howells 135765b38851SEric W. Biederman int nfs_fs_proc_net_init(struct net *net) 135865b38851SEric W. Biederman { 135965b38851SEric W. Biederman struct nfs_net *nn = net_generic(net, nfs_net_id); 136065b38851SEric W. Biederman struct proc_dir_entry *p; 136165b38851SEric W. Biederman 136265b38851SEric W. Biederman nn->proc_nfsfs = proc_net_mkdir(net, "nfsfs", net->proc_net); 136365b38851SEric W. Biederman if (!nn->proc_nfsfs) 136465b38851SEric W. Biederman goto error_0; 136565b38851SEric W. Biederman 136665b38851SEric W. Biederman /* a file of servers with which we're dealing */ 1367c3506372SChristoph Hellwig p = proc_create_net("servers", S_IFREG|S_IRUGO, nn->proc_nfsfs, 1368c3506372SChristoph Hellwig &nfs_server_list_ops, sizeof(struct seq_net_private)); 136965b38851SEric W. Biederman if (!p) 137065b38851SEric W. Biederman goto error_1; 137165b38851SEric W. Biederman 137265b38851SEric W. Biederman /* a file of volumes that we have mounted */ 1373c3506372SChristoph Hellwig p = proc_create_net("volumes", S_IFREG|S_IRUGO, nn->proc_nfsfs, 1374c3506372SChristoph Hellwig &nfs_volume_list_ops, sizeof(struct seq_net_private)); 137565b38851SEric W. Biederman if (!p) 137621e81002SCong Wang goto error_1; 137765b38851SEric W. Biederman return 0; 137865b38851SEric W. Biederman 137965b38851SEric W. Biederman error_1: 138021e81002SCong Wang remove_proc_subtree("nfsfs", net->proc_net); 138165b38851SEric W. Biederman error_0: 138265b38851SEric W. Biederman return -ENOMEM; 138365b38851SEric W. Biederman } 138465b38851SEric W. Biederman 138565b38851SEric W. Biederman void nfs_fs_proc_net_exit(struct net *net) 138665b38851SEric W. Biederman { 138721e81002SCong Wang remove_proc_subtree("nfsfs", net->proc_net); 138865b38851SEric W. Biederman } 138965b38851SEric W. Biederman 13906aaca566SDavid Howells /* 13916aaca566SDavid Howells * initialise the /proc/fs/nfsfs/ directory 13926aaca566SDavid Howells */ 13936aaca566SDavid Howells int __init nfs_fs_proc_init(void) 13946aaca566SDavid Howells { 13956a062a36SKinglong Mee if (!proc_mkdir("fs/nfsfs", NULL)) 13966aaca566SDavid Howells goto error_0; 13976aaca566SDavid Howells 13986aaca566SDavid Howells /* a file of servers with which we're dealing */ 13996a062a36SKinglong Mee if (!proc_symlink("fs/nfsfs/servers", NULL, "../../net/nfsfs/servers")) 14006aaca566SDavid Howells goto error_1; 14016aaca566SDavid Howells 14026aaca566SDavid Howells /* a file of volumes that we have mounted */ 14036a062a36SKinglong Mee if (!proc_symlink("fs/nfsfs/volumes", NULL, "../../net/nfsfs/volumes")) 14046a062a36SKinglong Mee goto error_1; 14056aaca566SDavid Howells 14066a062a36SKinglong Mee return 0; 14076aaca566SDavid Howells error_1: 14086a062a36SKinglong Mee remove_proc_subtree("fs/nfsfs", NULL); 14096aaca566SDavid Howells error_0: 14106aaca566SDavid Howells return -ENOMEM; 14116aaca566SDavid Howells } 14126aaca566SDavid Howells 14136aaca566SDavid Howells /* 14146aaca566SDavid Howells * clean up the /proc/fs/nfsfs/ directory 14156aaca566SDavid Howells */ 14166aaca566SDavid Howells void nfs_fs_proc_exit(void) 14176aaca566SDavid Howells { 14186a062a36SKinglong Mee remove_proc_subtree("fs/nfsfs", NULL); 14191c725118SBenjamin Coddington ida_destroy(&s_sysfs_ids); 14206aaca566SDavid Howells } 14216aaca566SDavid Howells 14226aaca566SDavid Howells #endif /* CONFIG_PROC_FS */ 1423