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" 5324c8dbbbSDavid Howells 5424c8dbbbSDavid Howells #define NFSDBG_FACILITY NFSDBG_CLIENT 5524c8dbbbSDavid Howells 5624c8dbbbSDavid Howells static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq); 57ab7017a3SBryan Schumaker static DEFINE_SPINLOCK(nfs_version_lock); 58ab7017a3SBryan Schumaker static DEFINE_MUTEX(nfs_version_mutex); 59ab7017a3SBryan Schumaker static LIST_HEAD(nfs_versions); 6024c8dbbbSDavid Howells 6124c8dbbbSDavid Howells /* 625006a76cSDavid Howells * RPC cruft for NFS 635006a76cSDavid Howells */ 64a613fa16STrond Myklebust static const struct rpc_version *nfs_version[5] = { 65ab7017a3SBryan Schumaker [2] = NULL, 66ab7017a3SBryan Schumaker [3] = NULL, 67ab7017a3SBryan Schumaker [4] = NULL, 685006a76cSDavid Howells }; 695006a76cSDavid Howells 70a613fa16STrond Myklebust const struct rpc_program nfs_program = { 715006a76cSDavid Howells .name = "nfs", 725006a76cSDavid Howells .number = NFS_PROGRAM, 735006a76cSDavid Howells .nrvers = ARRAY_SIZE(nfs_version), 745006a76cSDavid Howells .version = nfs_version, 755006a76cSDavid Howells .stats = &nfs_rpcstat, 76fe0a9b74SJim Rees .pipe_dir_name = NFS_PIPE_DIRNAME, 775006a76cSDavid Howells }; 785006a76cSDavid Howells 795006a76cSDavid Howells struct rpc_stat nfs_rpcstat = { 805006a76cSDavid Howells .program = &nfs_program 815006a76cSDavid Howells }; 825006a76cSDavid Howells 83ab7017a3SBryan Schumaker static struct nfs_subversion *find_nfs_version(unsigned int version) 84ab7017a3SBryan Schumaker { 85ab7017a3SBryan Schumaker struct nfs_subversion *nfs; 86ab7017a3SBryan Schumaker spin_lock(&nfs_version_lock); 87ab7017a3SBryan Schumaker 88ab7017a3SBryan Schumaker list_for_each_entry(nfs, &nfs_versions, list) { 89ab7017a3SBryan Schumaker if (nfs->rpc_ops->version == version) { 90ab7017a3SBryan Schumaker spin_unlock(&nfs_version_lock); 91ab7017a3SBryan Schumaker return nfs; 92ab7017a3SBryan Schumaker } 93ee34e136SYanchuan Nian } 94ab7017a3SBryan Schumaker 95ab7017a3SBryan Schumaker spin_unlock(&nfs_version_lock); 96ee34e136SYanchuan Nian return ERR_PTR(-EPROTONOSUPPORT); 97ab7017a3SBryan Schumaker } 98ab7017a3SBryan Schumaker 99ab7017a3SBryan Schumaker struct nfs_subversion *get_nfs_version(unsigned int version) 100ab7017a3SBryan Schumaker { 101ab7017a3SBryan Schumaker struct nfs_subversion *nfs = find_nfs_version(version); 102ab7017a3SBryan Schumaker 103ab7017a3SBryan Schumaker if (IS_ERR(nfs)) { 104ab7017a3SBryan Schumaker mutex_lock(&nfs_version_mutex); 1051ae811eeSbjschuma@gmail.com request_module("nfsv%d", version); 106ab7017a3SBryan Schumaker nfs = find_nfs_version(version); 107ab7017a3SBryan Schumaker mutex_unlock(&nfs_version_mutex); 108ab7017a3SBryan Schumaker } 109ab7017a3SBryan Schumaker 1101f70ef96SAlexey Khoroshilov if (!IS_ERR(nfs) && !try_module_get(nfs->owner)) 1111f70ef96SAlexey Khoroshilov return ERR_PTR(-EAGAIN); 112ab7017a3SBryan Schumaker return nfs; 113ab7017a3SBryan Schumaker } 114ab7017a3SBryan Schumaker 115ab7017a3SBryan Schumaker void put_nfs_version(struct nfs_subversion *nfs) 116ab7017a3SBryan Schumaker { 117ab7017a3SBryan Schumaker module_put(nfs->owner); 118ab7017a3SBryan Schumaker } 119ab7017a3SBryan Schumaker 120ab7017a3SBryan Schumaker void register_nfs_version(struct nfs_subversion *nfs) 121ab7017a3SBryan Schumaker { 122ab7017a3SBryan Schumaker spin_lock(&nfs_version_lock); 123ab7017a3SBryan Schumaker 124ab7017a3SBryan Schumaker list_add(&nfs->list, &nfs_versions); 125ab7017a3SBryan Schumaker nfs_version[nfs->rpc_ops->version] = nfs->rpc_vers; 126ab7017a3SBryan Schumaker 127ab7017a3SBryan Schumaker spin_unlock(&nfs_version_lock); 128ab7017a3SBryan Schumaker } 129ab7017a3SBryan Schumaker EXPORT_SYMBOL_GPL(register_nfs_version); 130ab7017a3SBryan Schumaker 131ab7017a3SBryan Schumaker void unregister_nfs_version(struct nfs_subversion *nfs) 132ab7017a3SBryan Schumaker { 133ab7017a3SBryan Schumaker spin_lock(&nfs_version_lock); 134ab7017a3SBryan Schumaker 135ab7017a3SBryan Schumaker nfs_version[nfs->rpc_ops->version] = NULL; 136ab7017a3SBryan Schumaker list_del(&nfs->list); 137ab7017a3SBryan Schumaker 138ab7017a3SBryan Schumaker spin_unlock(&nfs_version_lock); 139ab7017a3SBryan Schumaker } 140ab7017a3SBryan Schumaker EXPORT_SYMBOL_GPL(unregister_nfs_version); 141ab7017a3SBryan Schumaker 142ab7017a3SBryan Schumaker /* 14324c8dbbbSDavid Howells * Allocate a shared client record 14424c8dbbbSDavid Howells * 14524c8dbbbSDavid Howells * Since these are allocated/deallocated very rarely, we don't 14624c8dbbbSDavid Howells * bother putting them in a slab cache... 14724c8dbbbSDavid Howells */ 1486663ee7fSBryan Schumaker struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) 14924c8dbbbSDavid Howells { 15024c8dbbbSDavid Howells struct nfs_client *clp; 151a21bdd9bSChuck Lever int err = -ENOMEM; 15224c8dbbbSDavid Howells 15324c8dbbbSDavid Howells if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) 15424c8dbbbSDavid Howells goto error_0; 15524c8dbbbSDavid Howells 156ab7017a3SBryan Schumaker clp->cl_nfs_mod = cl_init->nfs_mod; 1571f70ef96SAlexey Khoroshilov if (!try_module_get(clp->cl_nfs_mod->owner)) 1581f70ef96SAlexey Khoroshilov goto error_dealloc; 159ab7017a3SBryan Schumaker 160ab7017a3SBryan Schumaker clp->rpc_ops = clp->cl_nfs_mod->rpc_ops; 16140c55319STrond Myklebust 162212bf41dSElena Reshetova refcount_set(&clp->cl_count, 1); 16324c8dbbbSDavid Howells clp->cl_cons_state = NFS_CS_INITING; 16424c8dbbbSDavid Howells 1656e4cffd7SChuck Lever memcpy(&clp->cl_addr, cl_init->addr, cl_init->addrlen); 1666e4cffd7SChuck Lever clp->cl_addrlen = cl_init->addrlen; 16724c8dbbbSDavid Howells 1683a498026STrond Myklebust if (cl_init->hostname) { 169a21bdd9bSChuck Lever err = -ENOMEM; 1703a498026STrond Myklebust clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL); 17124c8dbbbSDavid Howells if (!clp->cl_hostname) 17271468513SBenny Halevy goto error_cleanup; 17324c8dbbbSDavid Howells } 17424c8dbbbSDavid Howells 17524c8dbbbSDavid Howells INIT_LIST_HEAD(&clp->cl_superblocks); 17624c8dbbbSDavid Howells clp->cl_rpcclient = ERR_PTR(-EINVAL); 17724c8dbbbSDavid Howells 17859dca3b2STrond Myklebust clp->cl_proto = cl_init->proto; 1796619079dSTrond Myklebust clp->cl_nconnect = cl_init->nconnect; 18073ea666cSChuck Lever clp->cl_net = get_net(cl_init->net); 18159dca3b2STrond Myklebust 1825e16923bSNeilBrown clp->cl_principal = "*"; 18314727281SDavid Howells nfs_fscache_get_client_cookie(clp); 18414727281SDavid Howells 18524c8dbbbSDavid Howells return clp; 18624c8dbbbSDavid Howells 18771468513SBenny Halevy error_cleanup: 188ab7017a3SBryan Schumaker put_nfs_version(clp->cl_nfs_mod); 1891f70ef96SAlexey Khoroshilov error_dealloc: 19024c8dbbbSDavid Howells kfree(clp); 19124c8dbbbSDavid Howells error_0: 192a21bdd9bSChuck Lever return ERR_PTR(err); 19324c8dbbbSDavid Howells } 194ddda8e0aSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_alloc_client); 19524c8dbbbSDavid Howells 19689d77c8fSBryan Schumaker #if IS_ENABLED(CONFIG_NFS_V4) 19710b7a70cSTrond Myklebust static void nfs_cleanup_cb_ident_idr(struct net *net) 198f4eecd5dSAndy Adamson { 19928cd1b3fSStanislav Kinsbursky struct nfs_net *nn = net_generic(net, nfs_net_id); 20028cd1b3fSStanislav Kinsbursky 20128cd1b3fSStanislav Kinsbursky idr_destroy(&nn->cb_ident_idr); 202f4eecd5dSAndy Adamson } 203f4eecd5dSAndy Adamson 204f4eecd5dSAndy Adamson /* nfs_client_lock held */ 205f4eecd5dSAndy Adamson static void nfs_cb_idr_remove_locked(struct nfs_client *clp) 206f4eecd5dSAndy Adamson { 20773ea666cSChuck Lever struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); 20828cd1b3fSStanislav Kinsbursky 209f4eecd5dSAndy Adamson if (clp->cl_cb_ident) 21028cd1b3fSStanislav Kinsbursky idr_remove(&nn->cb_ident_idr, clp->cl_cb_ident); 211f4eecd5dSAndy Adamson } 212f4eecd5dSAndy Adamson 213f7e8917aSFred Isaman static void pnfs_init_server(struct nfs_server *server) 214f7e8917aSFred Isaman { 215f7e8917aSFred Isaman rpc_init_wait_queue(&server->roc_rpcwaitq, "pNFS ROC"); 216f7e8917aSFred Isaman } 217f7e8917aSFred Isaman 218888ef2e3SAlexandros Batsakis #else 21910b7a70cSTrond Myklebust static void nfs_cleanup_cb_ident_idr(struct net *net) 220f4eecd5dSAndy Adamson { 221f4eecd5dSAndy Adamson } 222f4eecd5dSAndy Adamson 223f4eecd5dSAndy Adamson static void nfs_cb_idr_remove_locked(struct nfs_client *clp) 224f4eecd5dSAndy Adamson { 225f4eecd5dSAndy Adamson } 226f7e8917aSFred Isaman 227f7e8917aSFred Isaman static void pnfs_init_server(struct nfs_server *server) 228f7e8917aSFred Isaman { 229f7e8917aSFred Isaman } 230f7e8917aSFred Isaman 231888ef2e3SAlexandros Batsakis #endif /* CONFIG_NFS_V4 */ 232888ef2e3SAlexandros Batsakis 233888ef2e3SAlexandros Batsakis /* 2345dd3177aSTrond Myklebust * Destroy a shared client record 2355dd3177aSTrond Myklebust */ 236cdb7ecedSBryan Schumaker void nfs_free_client(struct nfs_client *clp) 2375dd3177aSTrond Myklebust { 23814727281SDavid Howells nfs_fscache_release_client_cookie(clp); 23914727281SDavid Howells 24024c8dbbbSDavid Howells /* -EIO all pending I/O */ 24124c8dbbbSDavid Howells if (!IS_ERR(clp->cl_rpcclient)) 24224c8dbbbSDavid Howells rpc_shutdown_client(clp->cl_rpcclient); 24324c8dbbbSDavid Howells 24473ea666cSChuck Lever put_net(clp->cl_net); 245ab7017a3SBryan Schumaker put_nfs_version(clp->cl_nfs_mod); 24624c8dbbbSDavid Howells kfree(clp->cl_hostname); 247f11b2a1cSJeff Layton kfree(clp->cl_acceptor); 24824c8dbbbSDavid Howells kfree(clp); 24924c8dbbbSDavid Howells } 250ddda8e0aSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_free_client); 25124c8dbbbSDavid Howells 25224c8dbbbSDavid Howells /* 25324c8dbbbSDavid Howells * Release a reference to a shared client record 25424c8dbbbSDavid Howells */ 25524c8dbbbSDavid Howells void nfs_put_client(struct nfs_client *clp) 25624c8dbbbSDavid Howells { 257dc030858SStanislav Kinsbursky struct nfs_net *nn; 258dc030858SStanislav Kinsbursky 25927ba8512SDavid Howells if (!clp) 26027ba8512SDavid Howells return; 26127ba8512SDavid Howells 26273ea666cSChuck Lever nn = net_generic(clp->cl_net, nfs_net_id); 26324c8dbbbSDavid Howells 264212bf41dSElena Reshetova if (refcount_dec_and_lock(&clp->cl_count, &nn->nfs_client_lock)) { 26524c8dbbbSDavid Howells list_del(&clp->cl_share_link); 266f4eecd5dSAndy Adamson nfs_cb_idr_remove_locked(clp); 267dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 26824c8dbbbSDavid Howells 2691fea73a8STrond Myklebust WARN_ON_ONCE(!list_empty(&clp->cl_superblocks)); 27024c8dbbbSDavid Howells 271cdb7ecedSBryan Schumaker clp->rpc_ops->free_client(clp); 27224c8dbbbSDavid Howells } 27324c8dbbbSDavid Howells } 27416b374caSAndy Adamson EXPORT_SYMBOL_GPL(nfs_put_client); 27524c8dbbbSDavid Howells 2763fbd67adSTrond Myklebust /* 277c81468a1STrond Myklebust * Find an nfs_client on the list that matches the initialisation data 278c81468a1STrond Myklebust * that is supplied. 279c81468a1STrond Myklebust */ 280c81468a1STrond Myklebust static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *data) 28124c8dbbbSDavid Howells { 28224c8dbbbSDavid Howells struct nfs_client *clp; 283d7371c41SIan Dall const struct sockaddr *sap = data->addr; 2846b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(data->net, nfs_net_id); 285950a578cSRoberto Bergantinos Corpas int error; 28624c8dbbbSDavid Howells 287c156618eSScott Mayhew again: 2886b13168bSStanislav Kinsbursky list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) { 289d7371c41SIan Dall const struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; 29013bbc06aSTrond Myklebust /* Don't match clients that failed to initialise properly */ 29113bbc06aSTrond Myklebust if (clp->cl_cons_state < 0) 29213bbc06aSTrond Myklebust continue; 29313bbc06aSTrond Myklebust 294c156618eSScott Mayhew /* If a client is still initializing then we need to wait */ 295c156618eSScott Mayhew if (clp->cl_cons_state > NFS_CS_READY) { 296c156618eSScott Mayhew refcount_inc(&clp->cl_count); 297c156618eSScott Mayhew spin_unlock(&nn->nfs_client_lock); 298950a578cSRoberto Bergantinos Corpas error = nfs_wait_client_init_complete(clp); 299c156618eSScott Mayhew nfs_put_client(clp); 300c260121aSBenjamin Coddington spin_lock(&nn->nfs_client_lock); 301950a578cSRoberto Bergantinos Corpas if (error < 0) 302950a578cSRoberto Bergantinos Corpas return ERR_PTR(error); 303c156618eSScott Mayhew goto again; 304c156618eSScott Mayhew } 305c156618eSScott Mayhew 30624c8dbbbSDavid Howells /* Different NFS versions cannot share the same nfs_client */ 307ab7017a3SBryan Schumaker if (clp->rpc_ops != data->nfs_mod->rpc_ops) 30824c8dbbbSDavid Howells continue; 30924c8dbbbSDavid Howells 31059dca3b2STrond Myklebust if (clp->cl_proto != data->proto) 31159dca3b2STrond Myklebust continue; 3125aae4a9aSBenny Halevy /* Match nfsv4 minorversion */ 3135aae4a9aSBenny Halevy if (clp->cl_minorversion != data->minorversion) 3145aae4a9aSBenny Halevy continue; 315*52f98f1aSTrond Myklebust 316*52f98f1aSTrond Myklebust /* Match request for a dedicated DS */ 317*52f98f1aSTrond Myklebust if (test_bit(NFS_CS_DS, &data->init_flags) != 318*52f98f1aSTrond Myklebust test_bit(NFS_CS_DS, &clp->cl_flags)) 319*52f98f1aSTrond Myklebust continue; 320*52f98f1aSTrond Myklebust 321c81468a1STrond Myklebust /* Match the full socket address */ 322d8efa4e6SAnna Schumaker if (!rpc_cmp_addr_port(sap, clap)) 32304ea1b3eSAndy Adamson /* Match all xprt_switch full socket addresses */ 3248ef32955SPetr Vandrovec if (IS_ERR(clp->cl_rpcclient) || 3258ef32955SPetr Vandrovec !rpc_clnt_xprt_switch_has_addr(clp->cl_rpcclient, 32604ea1b3eSAndy Adamson sap)) 32724c8dbbbSDavid Howells continue; 32824c8dbbbSDavid Howells 329212bf41dSElena Reshetova refcount_inc(&clp->cl_count); 33024c8dbbbSDavid Howells return clp; 33124c8dbbbSDavid Howells } 332c81468a1STrond Myklebust return NULL; 33324c8dbbbSDavid Howells } 33424c8dbbbSDavid Howells 335a33e4b03SWeston Andros Adamson /* 336a33e4b03SWeston Andros Adamson * Return true if @clp is done initializing, false if still working on it. 337a33e4b03SWeston Andros Adamson * 338a33e4b03SWeston Andros Adamson * Use nfs_client_init_status to check if it was successful. 339a33e4b03SWeston Andros Adamson */ 340a33e4b03SWeston Andros Adamson bool nfs_client_init_is_complete(const struct nfs_client *clp) 3414697bd5eSTrond Myklebust { 34248d66b97STrond Myklebust return clp->cl_cons_state <= NFS_CS_READY; 3434697bd5eSTrond Myklebust } 344a33e4b03SWeston Andros Adamson EXPORT_SYMBOL_GPL(nfs_client_init_is_complete); 345a33e4b03SWeston Andros Adamson 346a33e4b03SWeston Andros Adamson /* 347a33e4b03SWeston Andros Adamson * Return 0 if @clp was successfully initialized, -errno otherwise. 348a33e4b03SWeston Andros Adamson * 349a33e4b03SWeston Andros Adamson * This must be called *after* nfs_client_init_is_complete() returns true, 350a33e4b03SWeston Andros Adamson * otherwise it will pop WARN_ON_ONCE and return -EINVAL 351a33e4b03SWeston Andros Adamson */ 352a33e4b03SWeston Andros Adamson int nfs_client_init_status(const struct nfs_client *clp) 353a33e4b03SWeston Andros Adamson { 354a33e4b03SWeston Andros Adamson /* called without checking nfs_client_init_is_complete */ 355a33e4b03SWeston Andros Adamson if (clp->cl_cons_state > NFS_CS_READY) { 356a33e4b03SWeston Andros Adamson WARN_ON_ONCE(1); 357a33e4b03SWeston Andros Adamson return -EINVAL; 358a33e4b03SWeston Andros Adamson } 359a33e4b03SWeston Andros Adamson return clp->cl_cons_state; 360a33e4b03SWeston Andros Adamson } 361a33e4b03SWeston Andros Adamson EXPORT_SYMBOL_GPL(nfs_client_init_status); 3624697bd5eSTrond Myklebust 3634697bd5eSTrond Myklebust int nfs_wait_client_init_complete(const struct nfs_client *clp) 3644697bd5eSTrond Myklebust { 3654697bd5eSTrond Myklebust return wait_event_killable(nfs_client_active_wq, 3664697bd5eSTrond Myklebust nfs_client_init_is_complete(clp)); 3674697bd5eSTrond Myklebust } 36889d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_wait_client_init_complete); 3694697bd5eSTrond Myklebust 37024c8dbbbSDavid Howells /* 371f411703aSChuck Lever * Found an existing client. Make sure it's ready before returning. 372f411703aSChuck Lever */ 373f411703aSChuck Lever static struct nfs_client * 374f411703aSChuck Lever nfs_found_client(const struct nfs_client_initdata *cl_init, 375f411703aSChuck Lever struct nfs_client *clp) 376f411703aSChuck Lever { 377f411703aSChuck Lever int error; 378f411703aSChuck Lever 3794697bd5eSTrond Myklebust error = nfs_wait_client_init_complete(clp); 380f411703aSChuck Lever if (error < 0) { 381f411703aSChuck Lever nfs_put_client(clp); 382f411703aSChuck Lever return ERR_PTR(-ERESTARTSYS); 383f411703aSChuck Lever } 384f411703aSChuck Lever 385f411703aSChuck Lever if (clp->cl_cons_state < NFS_CS_READY) { 386f411703aSChuck Lever error = clp->cl_cons_state; 387f411703aSChuck Lever nfs_put_client(clp); 388f411703aSChuck Lever return ERR_PTR(error); 389f411703aSChuck Lever } 390f411703aSChuck Lever 39154ac471cSTrond Myklebust smp_rmb(); 392f411703aSChuck Lever return clp; 393f411703aSChuck Lever } 394f411703aSChuck Lever 395f411703aSChuck Lever /* 39624c8dbbbSDavid Howells * Look up a client by IP address and protocol version 39724c8dbbbSDavid Howells * - creates a new record if one doesn't yet exist 39824c8dbbbSDavid Howells */ 3997d38de3fSAnna Schumaker struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_init) 40024c8dbbbSDavid Howells { 40124c8dbbbSDavid Howells struct nfs_client *clp, *new = NULL; 4026b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(cl_init->net, nfs_net_id); 403ab7017a3SBryan Schumaker const struct nfs_rpc_ops *rpc_ops = cl_init->nfs_mod->rpc_ops; 40424c8dbbbSDavid Howells 40531434f49SPeng Tao if (cl_init->hostname == NULL) { 40631434f49SPeng Tao WARN_ON(1); 40731434f49SPeng Tao return NULL; 40831434f49SPeng Tao } 40931434f49SPeng Tao 41024c8dbbbSDavid Howells /* see if the client already exists */ 41124c8dbbbSDavid Howells do { 412dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 41324c8dbbbSDavid Howells 414c81468a1STrond Myklebust clp = nfs_match_client(cl_init); 415f411703aSChuck Lever if (clp) { 416f411703aSChuck Lever spin_unlock(&nn->nfs_client_lock); 417f411703aSChuck Lever if (new) 418cdb7ecedSBryan Schumaker new->rpc_ops->free_client(new); 4199f7761cfSBenjamin Coddington if (IS_ERR(clp)) 4209f7761cfSBenjamin Coddington return clp; 421f411703aSChuck Lever return nfs_found_client(cl_init, clp); 422f411703aSChuck Lever } 4238cab4c39SChuck Lever if (new) { 42405f4c350SChuck Lever list_add_tail(&new->cl_share_link, 42505f4c350SChuck Lever &nn->nfs_client_list); 4268cab4c39SChuck Lever spin_unlock(&nn->nfs_client_lock); 4274bf590e0SChuck Lever new->cl_flags = cl_init->init_flags; 4285c6e5b60STrond Myklebust return rpc_ops->init_client(new, cl_init); 4298cab4c39SChuck Lever } 43024c8dbbbSDavid Howells 431dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 43224c8dbbbSDavid Howells 433ab7017a3SBryan Schumaker new = rpc_ops->alloc_client(cl_init); 434a21bdd9bSChuck Lever } while (!IS_ERR(new)); 43524c8dbbbSDavid Howells 436a21bdd9bSChuck Lever return new; 43724c8dbbbSDavid Howells } 43889d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_get_client); 43924c8dbbbSDavid Howells 44024c8dbbbSDavid Howells /* 44124c8dbbbSDavid Howells * Mark a server as ready or failed 44224c8dbbbSDavid Howells */ 44376db6d95SAndy Adamson void nfs_mark_client_ready(struct nfs_client *clp, int state) 44424c8dbbbSDavid Howells { 44554ac471cSTrond Myklebust smp_wmb(); 44624c8dbbbSDavid Howells clp->cl_cons_state = state; 44724c8dbbbSDavid Howells wake_up_all(&nfs_client_active_wq); 44824c8dbbbSDavid Howells } 44989d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_mark_client_ready); 4505006a76cSDavid Howells 4515006a76cSDavid Howells /* 4525006a76cSDavid Howells * Initialise the timeout values for a connection 4535006a76cSDavid Howells */ 454fcf10398SBryan Schumaker void nfs_init_timeout_values(struct rpc_timeout *to, int proto, 455a956bedaSTrond Myklebust int timeo, int retrans) 4565006a76cSDavid Howells { 4575006a76cSDavid Howells to->to_initval = timeo * HZ / 10; 4585006a76cSDavid Howells to->to_retries = retrans; 4595006a76cSDavid Howells 4605006a76cSDavid Howells switch (proto) { 4610896a725S\"Talpey, Thomas\ case XPRT_TRANSPORT_TCP: 4622cf7ff7aS\"Talpey, Thomas\ case XPRT_TRANSPORT_RDMA: 463a956bedaSTrond Myklebust if (retrans == NFS_UNSPEC_RETRANS) 464259875efSTrond Myklebust to->to_retries = NFS_DEF_TCP_RETRANS; 4655a698243STrond Myklebust if (timeo == NFS_UNSPEC_TIMEO || to->to_initval == 0) 466259875efSTrond Myklebust to->to_initval = NFS_DEF_TCP_TIMEO * HZ / 10; 4675006a76cSDavid Howells if (to->to_initval > NFS_MAX_TCP_TIMEOUT) 4685006a76cSDavid Howells to->to_initval = NFS_MAX_TCP_TIMEOUT; 4695006a76cSDavid Howells to->to_increment = to->to_initval; 4705006a76cSDavid Howells to->to_maxval = to->to_initval + (to->to_increment * to->to_retries); 4717a3e3e18STrond Myklebust if (to->to_maxval > NFS_MAX_TCP_TIMEOUT) 4727a3e3e18STrond Myklebust to->to_maxval = NFS_MAX_TCP_TIMEOUT; 4737a3e3e18STrond Myklebust if (to->to_maxval < to->to_initval) 4747a3e3e18STrond Myklebust to->to_maxval = to->to_initval; 4755006a76cSDavid Howells to->to_exponential = 0; 4765006a76cSDavid Howells break; 4770896a725S\"Talpey, Thomas\ case XPRT_TRANSPORT_UDP: 478a956bedaSTrond Myklebust if (retrans == NFS_UNSPEC_RETRANS) 479259875efSTrond Myklebust to->to_retries = NFS_DEF_UDP_RETRANS; 480a956bedaSTrond Myklebust if (timeo == NFS_UNSPEC_TIMEO || to->to_initval == 0) 481259875efSTrond Myklebust to->to_initval = NFS_DEF_UDP_TIMEO * HZ / 10; 4825006a76cSDavid Howells if (to->to_initval > NFS_MAX_UDP_TIMEOUT) 4835006a76cSDavid Howells to->to_initval = NFS_MAX_UDP_TIMEOUT; 4845006a76cSDavid Howells to->to_maxval = NFS_MAX_UDP_TIMEOUT; 4855006a76cSDavid Howells to->to_exponential = 1; 4865006a76cSDavid Howells break; 487259875efSTrond Myklebust default: 488259875efSTrond Myklebust BUG(); 4895006a76cSDavid Howells } 4905006a76cSDavid Howells } 49189d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_init_timeout_values); 4925006a76cSDavid Howells 4935006a76cSDavid Howells /* 4945006a76cSDavid Howells * Create an RPC client handle 4955006a76cSDavid Howells */ 496428360d7SBryan Schumaker int nfs_create_rpc_client(struct nfs_client *clp, 4975c6e5b60STrond Myklebust const struct nfs_client_initdata *cl_init, 4984bf590e0SChuck Lever rpc_authflavor_t flavor) 4995006a76cSDavid Howells { 5005006a76cSDavid Howells struct rpc_clnt *clnt = NULL; 50141877d20SChuck Lever struct rpc_create_args args = { 50273ea666cSChuck Lever .net = clp->cl_net, 50359dca3b2STrond Myklebust .protocol = clp->cl_proto, 5046619079dSTrond Myklebust .nconnect = clp->cl_nconnect, 50541877d20SChuck Lever .address = (struct sockaddr *)&clp->cl_addr, 5066e4cffd7SChuck Lever .addrsize = clp->cl_addrlen, 5075c6e5b60STrond Myklebust .timeout = cl_init->timeparms, 50841877d20SChuck Lever .servername = clp->cl_hostname, 5095c6e5b60STrond Myklebust .nodename = cl_init->nodename, 51041877d20SChuck Lever .program = &nfs_program, 51141877d20SChuck Lever .version = clp->rpc_ops->version, 51241877d20SChuck Lever .authflavor = flavor, 5131a58e8a0STrond Myklebust .cred = cl_init->cred, 51441877d20SChuck Lever }; 5155006a76cSDavid Howells 5164bf590e0SChuck Lever if (test_bit(NFS_CS_DISCRTRY, &clp->cl_flags)) 5174a01b8a4SChuck Lever args.flags |= RPC_CLNT_CREATE_DISCRTRY; 51899875249STrond Myklebust if (test_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags)) 51999875249STrond Myklebust args.flags |= RPC_CLNT_CREATE_NO_RETRANS_TIMEOUT; 5204bf590e0SChuck Lever if (test_bit(NFS_CS_NORESVPORT, &clp->cl_flags)) 5214a01b8a4SChuck Lever args.flags |= RPC_CLNT_CREATE_NONPRIVPORT; 52298f98cf5STrond Myklebust if (test_bit(NFS_CS_INFINITE_SLOTS, &clp->cl_flags)) 52398f98cf5STrond Myklebust args.flags |= RPC_CLNT_CREATE_INFINITE_SLOTS; 5244b1b69ceSTrond Myklebust if (test_bit(NFS_CS_NOPING, &clp->cl_flags)) 5254b1b69ceSTrond Myklebust args.flags |= RPC_CLNT_CREATE_NOPING; 5264a01b8a4SChuck Lever 5275006a76cSDavid Howells if (!IS_ERR(clp->cl_rpcclient)) 5285006a76cSDavid Howells return 0; 5295006a76cSDavid Howells 53041877d20SChuck Lever clnt = rpc_create(&args); 5315006a76cSDavid Howells if (IS_ERR(clnt)) { 5325006a76cSDavid Howells dprintk("%s: cannot create RPC client. Error = %ld\n", 5333110ff80SHarvey Harrison __func__, PTR_ERR(clnt)); 5345006a76cSDavid Howells return PTR_ERR(clnt); 5355006a76cSDavid Howells } 5365006a76cSDavid Howells 5375e16923bSNeilBrown clnt->cl_principal = clp->cl_principal; 5385006a76cSDavid Howells clp->cl_rpcclient = clnt; 5395006a76cSDavid Howells return 0; 5405006a76cSDavid Howells } 54189d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_create_rpc_client); 54254ceac45SDavid Howells 54354ceac45SDavid Howells /* 54454ceac45SDavid Howells * Version 2 or 3 client destruction 54554ceac45SDavid Howells */ 54654ceac45SDavid Howells static void nfs_destroy_server(struct nfs_server *server) 54754ceac45SDavid Howells { 548f259613aSNeilBrown if (server->nlm_host) 5499289e7f9SChuck Lever nlmclnt_done(server->nlm_host); 55054ceac45SDavid Howells } 55154ceac45SDavid Howells 55254ceac45SDavid Howells /* 55354ceac45SDavid Howells * Version 2 or 3 lockd setup 55454ceac45SDavid Howells */ 55554ceac45SDavid Howells static int nfs_start_lockd(struct nfs_server *server) 55654ceac45SDavid Howells { 5579289e7f9SChuck Lever struct nlm_host *host; 5589289e7f9SChuck Lever struct nfs_client *clp = server->nfs_client; 559883bb163SChuck Lever struct nlmclnt_initdata nlm_init = { 560883bb163SChuck Lever .hostname = clp->cl_hostname, 561883bb163SChuck Lever .address = (struct sockaddr *)&clp->cl_addr, 562883bb163SChuck Lever .addrlen = clp->cl_addrlen, 563883bb163SChuck Lever .nfs_version = clp->rpc_ops->version, 5640cb2659bSChuck Lever .noresvport = server->flags & NFS_MOUNT_NORESVPORT ? 5650cb2659bSChuck Lever 1 : 0, 56673ea666cSChuck Lever .net = clp->cl_net, 567b1ece737SBenjamin Coddington .nlmclnt_ops = clp->cl_nfs_mod->rpc_ops->nlmclnt_ops, 56840373b12STrond Myklebust .cred = current_cred(), 569883bb163SChuck Lever }; 57054ceac45SDavid Howells 571883bb163SChuck Lever if (nlm_init.nfs_version > 3) 5729289e7f9SChuck Lever return 0; 5735eebde23SSuresh Jayaraman if ((server->flags & NFS_MOUNT_LOCAL_FLOCK) && 5745eebde23SSuresh Jayaraman (server->flags & NFS_MOUNT_LOCAL_FCNTL)) 5759289e7f9SChuck Lever return 0; 5769289e7f9SChuck Lever 5778a6e5debSTrond Myklebust switch (clp->cl_proto) { 5788a6e5debSTrond Myklebust default: 5798a6e5debSTrond Myklebust nlm_init.protocol = IPPROTO_TCP; 5808a6e5debSTrond Myklebust break; 5818a6e5debSTrond Myklebust case XPRT_TRANSPORT_UDP: 5828a6e5debSTrond Myklebust nlm_init.protocol = IPPROTO_UDP; 5838a6e5debSTrond Myklebust } 5848a6e5debSTrond Myklebust 585883bb163SChuck Lever host = nlmclnt_init(&nlm_init); 5869289e7f9SChuck Lever if (IS_ERR(host)) 5879289e7f9SChuck Lever return PTR_ERR(host); 5889289e7f9SChuck Lever 5899289e7f9SChuck Lever server->nlm_host = host; 59054ceac45SDavid Howells server->destroy = nfs_destroy_server; 5919289e7f9SChuck Lever return 0; 59254ceac45SDavid Howells } 59354ceac45SDavid Howells 59454ceac45SDavid Howells /* 59554ceac45SDavid Howells * Create a general RPC client 59654ceac45SDavid Howells */ 597fcf10398SBryan Schumaker int nfs_init_server_rpcclient(struct nfs_server *server, 59833170233STrond Myklebust const struct rpc_timeout *timeo, 59933170233STrond Myklebust rpc_authflavor_t pseudoflavour) 60054ceac45SDavid Howells { 60154ceac45SDavid Howells struct nfs_client *clp = server->nfs_client; 60254ceac45SDavid Howells 603ba9b584cSChuck Lever server->client = rpc_clone_client_set_auth(clp->cl_rpcclient, 604ba9b584cSChuck Lever pseudoflavour); 60554ceac45SDavid Howells if (IS_ERR(server->client)) { 6063110ff80SHarvey Harrison dprintk("%s: couldn't create rpc_client!\n", __func__); 60754ceac45SDavid Howells return PTR_ERR(server->client); 60854ceac45SDavid Howells } 60954ceac45SDavid Howells 61033170233STrond Myklebust memcpy(&server->client->cl_timeout_default, 61133170233STrond Myklebust timeo, 61233170233STrond Myklebust sizeof(server->client->cl_timeout_default)); 61333170233STrond Myklebust server->client->cl_timeout = &server->client->cl_timeout_default; 61454ceac45SDavid Howells server->client->cl_softrtry = 0; 61591a575e1STrond Myklebust if (server->flags & NFS_MOUNT_SOFTERR) 61691a575e1STrond Myklebust server->client->cl_softerr = 1; 61754ceac45SDavid Howells if (server->flags & NFS_MOUNT_SOFT) 61854ceac45SDavid Howells server->client->cl_softrtry = 1; 61954ceac45SDavid Howells 62054ceac45SDavid Howells return 0; 62154ceac45SDavid Howells } 62289d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_init_server_rpcclient); 62354ceac45SDavid Howells 6248cab4c39SChuck Lever /** 6258cab4c39SChuck Lever * nfs_init_client - Initialise an NFS2 or NFS3 client 6268cab4c39SChuck Lever * 6278cab4c39SChuck Lever * @clp: nfs_client to initialise 6285c6e5b60STrond Myklebust * @cl_init: Initialisation parameters 6298cab4c39SChuck Lever * 6308cab4c39SChuck Lever * Returns pointer to an NFS client, or an ERR_PTR value. 63154ceac45SDavid Howells */ 6328cab4c39SChuck Lever struct nfs_client *nfs_init_client(struct nfs_client *clp, 6335c6e5b60STrond Myklebust const struct nfs_client_initdata *cl_init) 63454ceac45SDavid Howells { 63554ceac45SDavid Howells int error; 63654ceac45SDavid Howells 63754ceac45SDavid Howells /* the client is already initialised */ 6382844b6aeSAnna Schumaker if (clp->cl_cons_state == NFS_CS_READY) 6398cab4c39SChuck Lever return clp; 64054ceac45SDavid Howells 64154ceac45SDavid Howells /* 64254ceac45SDavid Howells * Create a client RPC handle for doing FSSTAT with UNIX auth only 64354ceac45SDavid Howells * - RFC 2623, sec 2.3.2 64454ceac45SDavid Howells */ 6455c6e5b60STrond Myklebust error = nfs_create_rpc_client(clp, cl_init, RPC_AUTH_UNIX); 6462844b6aeSAnna Schumaker nfs_mark_client_ready(clp, error == 0 ? NFS_CS_READY : error); 6472844b6aeSAnna Schumaker if (error < 0) { 6488cab4c39SChuck Lever nfs_put_client(clp); 6492844b6aeSAnna Schumaker clp = ERR_PTR(error); 6502844b6aeSAnna Schumaker } 6512844b6aeSAnna Schumaker return clp; 65254ceac45SDavid Howells } 653ddda8e0aSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_init_client); 65454ceac45SDavid Howells 65554ceac45SDavid Howells /* 65654ceac45SDavid Howells * Create a version 2 or 3 client 65754ceac45SDavid Howells */ 6582283f8d6S\"Talpey, Thomas\ static int nfs_init_server(struct nfs_server *server, 659ab7017a3SBryan Schumaker const struct nfs_parsed_mount_data *data, 660ab7017a3SBryan Schumaker struct nfs_subversion *nfs_mod) 66154ceac45SDavid Howells { 6625c6e5b60STrond Myklebust struct rpc_timeout timeparms; 6633a498026STrond Myklebust struct nfs_client_initdata cl_init = { 6643a498026STrond Myklebust .hostname = data->nfs_server.hostname, 665d7422c47SChuck Lever .addr = (const struct sockaddr *)&data->nfs_server.address, 6664c568017SChuck Lever .addrlen = data->nfs_server.addrlen, 667ab7017a3SBryan Schumaker .nfs_mod = nfs_mod, 66859dca3b2STrond Myklebust .proto = data->nfs_server.protocol, 669e50a7a1aSStanislav Kinsbursky .net = data->net, 6705c6e5b60STrond Myklebust .timeparms = &timeparms, 6711a58e8a0STrond Myklebust .cred = server->cred, 67253c32630STrond Myklebust .nconnect = data->nfs_server.nconnect, 6733a498026STrond Myklebust }; 67454ceac45SDavid Howells struct nfs_client *clp; 6753a498026STrond Myklebust int error; 67654ceac45SDavid Howells 67745a52a02SAndy Adamson nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, 67845a52a02SAndy Adamson data->timeo, data->retrans); 6794bf590e0SChuck Lever if (data->flags & NFS_MOUNT_NORESVPORT) 6804bf590e0SChuck Lever set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags); 68145a52a02SAndy Adamson 68254ceac45SDavid Howells /* Allocate or find a client reference we can use */ 6837d38de3fSAnna Schumaker clp = nfs_get_client(&cl_init); 6844cbb9768SAnna Schumaker if (IS_ERR(clp)) 68554ceac45SDavid Howells return PTR_ERR(clp); 68654ceac45SDavid Howells 68754ceac45SDavid Howells server->nfs_client = clp; 68854ceac45SDavid Howells 68954ceac45SDavid Howells /* Initialise the client representation from the mount data */ 690ff3525a5STrond Myklebust server->flags = data->flags; 691b797cac7SDavid Howells server->options = data->options; 69262ab460cSTrond Myklebust server->caps |= NFS_CAP_HARDLINKS|NFS_CAP_SYMLINKS|NFS_CAP_FILEID| 69362ab460cSTrond Myklebust NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|NFS_CAP_OWNER_GROUP| 694cd812599STrond Myklebust NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME; 69554ceac45SDavid Howells 69654ceac45SDavid Howells if (data->rsize) 69754ceac45SDavid Howells server->rsize = nfs_block_size(data->rsize, NULL); 69854ceac45SDavid Howells if (data->wsize) 69954ceac45SDavid Howells server->wsize = nfs_block_size(data->wsize, NULL); 70054ceac45SDavid Howells 70154ceac45SDavid Howells server->acregmin = data->acregmin * HZ; 70254ceac45SDavid Howells server->acregmax = data->acregmax * HZ; 70354ceac45SDavid Howells server->acdirmin = data->acdirmin * HZ; 70454ceac45SDavid Howells server->acdirmax = data->acdirmax * HZ; 70554ceac45SDavid Howells 70654ceac45SDavid Howells /* Start lockd here, before we might error out */ 70754ceac45SDavid Howells error = nfs_start_lockd(server); 70854ceac45SDavid Howells if (error < 0) 70954ceac45SDavid Howells goto error; 71054ceac45SDavid Howells 711f22d6d79SChuck Lever server->port = data->nfs_server.port; 7120f5f49b8SWeston Andros Adamson server->auth_info = data->auth_info; 713f22d6d79SChuck Lever 714a3f73c27SWeston Andros Adamson error = nfs_init_server_rpcclient(server, &timeparms, 715a3f73c27SWeston Andros Adamson data->selected_flavor); 71654ceac45SDavid Howells if (error < 0) 71754ceac45SDavid Howells goto error; 71854ceac45SDavid Howells 7193f8400d1SChuck Lever /* Preserve the values of mount_server-related mount options */ 7203f8400d1SChuck Lever if (data->mount_server.addrlen) { 7213f8400d1SChuck Lever memcpy(&server->mountd_address, &data->mount_server.address, 7223f8400d1SChuck Lever data->mount_server.addrlen); 7233f8400d1SChuck Lever server->mountd_addrlen = data->mount_server.addrlen; 7243f8400d1SChuck Lever } 7253f8400d1SChuck Lever server->mountd_version = data->mount_server.version; 7263f8400d1SChuck Lever server->mountd_port = data->mount_server.port; 7273f8400d1SChuck Lever server->mountd_protocol = data->mount_server.protocol; 7283f8400d1SChuck Lever 72954ceac45SDavid Howells server->namelen = data->namlen; 73054ceac45SDavid Howells return 0; 73154ceac45SDavid Howells 73254ceac45SDavid Howells error: 73354ceac45SDavid Howells server->nfs_client = NULL; 73454ceac45SDavid Howells nfs_put_client(clp); 73554ceac45SDavid Howells return error; 73654ceac45SDavid Howells } 73754ceac45SDavid Howells 73854ceac45SDavid Howells /* 73954ceac45SDavid Howells * Load up the server record from information gained in an fsinfo record 74054ceac45SDavid Howells */ 741738fd0f3SBenny Halevy static void nfs_server_set_fsinfo(struct nfs_server *server, 742738fd0f3SBenny Halevy struct nfs_fsinfo *fsinfo) 74354ceac45SDavid Howells { 74454ceac45SDavid Howells unsigned long max_rpc_payload; 74554ceac45SDavid Howells 74654ceac45SDavid Howells /* Work out a lot of parameters */ 74754ceac45SDavid Howells if (server->rsize == 0) 74854ceac45SDavid Howells server->rsize = nfs_block_size(fsinfo->rtpref, NULL); 74954ceac45SDavid Howells if (server->wsize == 0) 75054ceac45SDavid Howells server->wsize = nfs_block_size(fsinfo->wtpref, NULL); 75154ceac45SDavid Howells 75254ceac45SDavid Howells if (fsinfo->rtmax >= 512 && server->rsize > fsinfo->rtmax) 75354ceac45SDavid Howells server->rsize = nfs_block_size(fsinfo->rtmax, NULL); 75454ceac45SDavid Howells if (fsinfo->wtmax >= 512 && server->wsize > fsinfo->wtmax) 75554ceac45SDavid Howells server->wsize = nfs_block_size(fsinfo->wtmax, NULL); 75654ceac45SDavid Howells 75754ceac45SDavid Howells max_rpc_payload = nfs_block_size(rpc_max_payload(server->client), NULL); 75854ceac45SDavid Howells if (server->rsize > max_rpc_payload) 75954ceac45SDavid Howells server->rsize = max_rpc_payload; 76054ceac45SDavid Howells if (server->rsize > NFS_MAX_FILE_IO_SIZE) 76154ceac45SDavid Howells server->rsize = NFS_MAX_FILE_IO_SIZE; 76209cbfeafSKirill A. Shutemov server->rpages = (server->rsize + PAGE_SIZE - 1) >> PAGE_SHIFT; 763e0bf68ddSPeter Zijlstra 76454ceac45SDavid Howells if (server->wsize > max_rpc_payload) 76554ceac45SDavid Howells server->wsize = max_rpc_payload; 76654ceac45SDavid Howells if (server->wsize > NFS_MAX_FILE_IO_SIZE) 76754ceac45SDavid Howells server->wsize = NFS_MAX_FILE_IO_SIZE; 76809cbfeafSKirill A. Shutemov server->wpages = (server->wsize + PAGE_SIZE - 1) >> PAGE_SHIFT; 76985e174baSRicardo Labiaga 77054ceac45SDavid Howells server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL); 77154ceac45SDavid Howells 77254ceac45SDavid Howells server->dtsize = nfs_block_size(fsinfo->dtpref, NULL); 77309cbfeafSKirill A. Shutemov if (server->dtsize > PAGE_SIZE * NFS_MAX_READDIR_PAGES) 77409cbfeafSKirill A. Shutemov server->dtsize = PAGE_SIZE * NFS_MAX_READDIR_PAGES; 77554ceac45SDavid Howells if (server->dtsize > server->rsize) 77654ceac45SDavid Howells server->dtsize = server->rsize; 77754ceac45SDavid Howells 77854ceac45SDavid Howells if (server->flags & NFS_MOUNT_NOAC) { 77954ceac45SDavid Howells server->acregmin = server->acregmax = 0; 78054ceac45SDavid Howells server->acdirmin = server->acdirmax = 0; 78154ceac45SDavid Howells } 78254ceac45SDavid Howells 78354ceac45SDavid Howells server->maxfilesize = fsinfo->maxfilesize; 78454ceac45SDavid Howells 7856b96724eSRicardo Labiaga server->time_delta = fsinfo->time_delta; 7866b96724eSRicardo Labiaga 7872a92ee92SPeng Tao server->clone_blksize = fsinfo->clone_blksize; 78854ceac45SDavid Howells /* We're airborne Set socket buffersize */ 78954ceac45SDavid Howells rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100); 79054ceac45SDavid Howells } 79154ceac45SDavid Howells 79254ceac45SDavid Howells /* 79354ceac45SDavid Howells * Probe filesystem information, including the FSID on v2/v3 79454ceac45SDavid Howells */ 795fcf10398SBryan Schumaker int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr) 79654ceac45SDavid Howells { 79754ceac45SDavid Howells struct nfs_fsinfo fsinfo; 79854ceac45SDavid Howells struct nfs_client *clp = server->nfs_client; 79954ceac45SDavid Howells int error; 80054ceac45SDavid Howells 80154ceac45SDavid Howells if (clp->rpc_ops->set_capabilities != NULL) { 80254ceac45SDavid Howells error = clp->rpc_ops->set_capabilities(server, mntfh); 80354ceac45SDavid Howells if (error < 0) 8044cbb9768SAnna Schumaker return error; 80554ceac45SDavid Howells } 80654ceac45SDavid Howells 80754ceac45SDavid Howells fsinfo.fattr = fattr; 808ca440c38SJeff Layton fsinfo.nlayouttypes = 0; 8093132e49eSJeff Layton memset(fsinfo.layouttype, 0, sizeof(fsinfo.layouttype)); 81054ceac45SDavid Howells error = clp->rpc_ops->fsinfo(server, mntfh, &fsinfo); 81154ceac45SDavid Howells if (error < 0) 8124cbb9768SAnna Schumaker return error; 81354ceac45SDavid Howells 81471f81e51SKinglong Mee nfs_server_set_fsinfo(server, &fsinfo); 81554ceac45SDavid Howells 81654ceac45SDavid Howells /* Get some general file system info */ 81754ceac45SDavid Howells if (server->namelen == 0) { 81854ceac45SDavid Howells struct nfs_pathconf pathinfo; 81954ceac45SDavid Howells 82054ceac45SDavid Howells pathinfo.fattr = fattr; 82154ceac45SDavid Howells nfs_fattr_init(fattr); 82254ceac45SDavid Howells 82354ceac45SDavid Howells if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0) 82454ceac45SDavid Howells server->namelen = pathinfo.max_namelen; 82554ceac45SDavid Howells } 82654ceac45SDavid Howells 82754ceac45SDavid Howells return 0; 82854ceac45SDavid Howells } 82989d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_probe_fsinfo); 83054ceac45SDavid Howells 83154ceac45SDavid Howells /* 83254ceac45SDavid Howells * Copy useful information when duplicating a server record 83354ceac45SDavid Howells */ 834fcf10398SBryan Schumaker void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source) 83554ceac45SDavid Howells { 83654ceac45SDavid Howells target->flags = source->flags; 837356e76b8SChuck Lever target->rsize = source->rsize; 838356e76b8SChuck Lever target->wsize = source->wsize; 83954ceac45SDavid Howells target->acregmin = source->acregmin; 84054ceac45SDavid Howells target->acregmax = source->acregmax; 84154ceac45SDavid Howells target->acdirmin = source->acdirmin; 84254ceac45SDavid Howells target->acdirmax = source->acdirmax; 84354ceac45SDavid Howells target->caps = source->caps; 8442df54806SDavid Howells target->options = source->options; 8450f5f49b8SWeston Andros Adamson target->auth_info = source->auth_info; 84689a6814dSSteve Dickson target->port = source->port; 84754ceac45SDavid Howells } 84889d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_server_copy_userdata); 84954ceac45SDavid Howells 850fcf10398SBryan Schumaker void nfs_server_insert_lists(struct nfs_server *server) 851fca5238eSChuck Lever { 852fca5238eSChuck Lever struct nfs_client *clp = server->nfs_client; 85373ea666cSChuck Lever struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); 854fca5238eSChuck Lever 855dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 856fca5238eSChuck Lever list_add_tail_rcu(&server->client_link, &clp->cl_superblocks); 857c25d32b2SStanislav Kinsbursky list_add_tail(&server->master_link, &nn->nfs_volume_list); 858d3b4c9d7SAndy Adamson clear_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state); 859dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 860fca5238eSChuck Lever 861fca5238eSChuck Lever } 86289d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_server_insert_lists); 863fca5238eSChuck Lever 86432e62b7cSChuck Lever void nfs_server_remove_lists(struct nfs_server *server) 865fca5238eSChuck Lever { 866d3b4c9d7SAndy Adamson struct nfs_client *clp = server->nfs_client; 8674c03ae4aSTrond Myklebust struct nfs_net *nn; 868d3b4c9d7SAndy Adamson 8694c03ae4aSTrond Myklebust if (clp == NULL) 8704c03ae4aSTrond Myklebust return; 87173ea666cSChuck Lever nn = net_generic(clp->cl_net, nfs_net_id); 872dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 873fca5238eSChuck Lever list_del_rcu(&server->client_link); 8744c03ae4aSTrond Myklebust if (list_empty(&clp->cl_superblocks)) 875d3b4c9d7SAndy Adamson set_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state); 876fca5238eSChuck Lever list_del(&server->master_link); 877dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 878fca5238eSChuck Lever 879fca5238eSChuck Lever synchronize_rcu(); 880fca5238eSChuck Lever } 88132e62b7cSChuck Lever EXPORT_SYMBOL_GPL(nfs_server_remove_lists); 882fca5238eSChuck Lever 88354ceac45SDavid Howells /* 88454ceac45SDavid Howells * Allocate and initialise a server record 88554ceac45SDavid Howells */ 886fcf10398SBryan Schumaker struct nfs_server *nfs_alloc_server(void) 88754ceac45SDavid Howells { 88854ceac45SDavid Howells struct nfs_server *server; 88954ceac45SDavid Howells 89054ceac45SDavid Howells server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL); 89154ceac45SDavid Howells if (!server) 89254ceac45SDavid Howells return NULL; 89354ceac45SDavid Howells 89454ceac45SDavid Howells server->client = server->client_acl = ERR_PTR(-EINVAL); 89554ceac45SDavid Howells 89654ceac45SDavid Howells /* Zero out the NFS state stuff */ 89754ceac45SDavid Howells INIT_LIST_HEAD(&server->client_link); 89854ceac45SDavid Howells INIT_LIST_HEAD(&server->master_link); 899d3978bb3SChuck Lever INIT_LIST_HEAD(&server->delegations); 9006382a441SWeston Andros Adamson INIT_LIST_HEAD(&server->layouts); 9010aaaf5c4SChuck Lever INIT_LIST_HEAD(&server->state_owners_lru); 90262164f31SOlga Kornievskaia INIT_LIST_HEAD(&server->ss_copies); 90354ceac45SDavid Howells 904ef818a28SSteve Dickson atomic_set(&server->active, 0); 905ef818a28SSteve Dickson 90654ceac45SDavid Howells server->io_stats = nfs_alloc_iostats(); 90754ceac45SDavid Howells if (!server->io_stats) { 90854ceac45SDavid Howells kfree(server); 90954ceac45SDavid Howells return NULL; 91054ceac45SDavid Howells } 91154ceac45SDavid Howells 9129157c31dSTrond Myklebust ida_init(&server->openowner_id); 913d2d7ce28STrond Myklebust ida_init(&server->lockowner_id); 914f7e8917aSFred Isaman pnfs_init_server(server); 91568ebf8feSBenjamin Coddington rpc_init_wait_queue(&server->uoc_rpcwaitq, "NFS UOC"); 916f7e8917aSFred Isaman 91754ceac45SDavid Howells return server; 91854ceac45SDavid Howells } 91989d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_alloc_server); 92054ceac45SDavid Howells 92154ceac45SDavid Howells /* 92254ceac45SDavid Howells * Free up a server record 92354ceac45SDavid Howells */ 92454ceac45SDavid Howells void nfs_free_server(struct nfs_server *server) 92554ceac45SDavid Howells { 926fca5238eSChuck Lever nfs_server_remove_lists(server); 92754ceac45SDavid Howells 92854ceac45SDavid Howells if (server->destroy != NULL) 92954ceac45SDavid Howells server->destroy(server); 9305cef338bSTrond Myklebust 9315cef338bSTrond Myklebust if (!IS_ERR(server->client_acl)) 9325cef338bSTrond Myklebust rpc_shutdown_client(server->client_acl); 93354ceac45SDavid Howells if (!IS_ERR(server->client)) 93454ceac45SDavid Howells rpc_shutdown_client(server->client); 93554ceac45SDavid Howells 93654ceac45SDavid Howells nfs_put_client(server->nfs_client); 93754ceac45SDavid Howells 938d2d7ce28STrond Myklebust ida_destroy(&server->lockowner_id); 9399157c31dSTrond Myklebust ida_destroy(&server->openowner_id); 94054ceac45SDavid Howells nfs_free_iostats(server->io_stats); 9411a58e8a0STrond Myklebust put_cred(server->cred); 94254ceac45SDavid Howells kfree(server); 94354ceac45SDavid Howells nfs_release_automount_timer(); 94454ceac45SDavid Howells } 94589d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_free_server); 94654ceac45SDavid Howells 94754ceac45SDavid Howells /* 94854ceac45SDavid Howells * Create a version 2 or 3 volume record 94954ceac45SDavid Howells * - keyed on server and FSID 95054ceac45SDavid Howells */ 9511179acc6SBryan Schumaker struct nfs_server *nfs_create_server(struct nfs_mount_info *mount_info, 952ab7017a3SBryan Schumaker struct nfs_subversion *nfs_mod) 95354ceac45SDavid Howells { 95454ceac45SDavid Howells struct nfs_server *server; 955fbca779aSTrond Myklebust struct nfs_fattr *fattr; 95654ceac45SDavid Howells int error; 95754ceac45SDavid Howells 95854ceac45SDavid Howells server = nfs_alloc_server(); 95954ceac45SDavid Howells if (!server) 96054ceac45SDavid Howells return ERR_PTR(-ENOMEM); 96154ceac45SDavid Howells 9621a58e8a0STrond Myklebust server->cred = get_cred(current_cred()); 9631a58e8a0STrond Myklebust 964fbca779aSTrond Myklebust error = -ENOMEM; 965fbca779aSTrond Myklebust fattr = nfs_alloc_fattr(); 966fbca779aSTrond Myklebust if (fattr == NULL) 967fbca779aSTrond Myklebust goto error; 968fbca779aSTrond Myklebust 96954ceac45SDavid Howells /* Get a client representation */ 9701179acc6SBryan Schumaker error = nfs_init_server(server, mount_info->parsed, nfs_mod); 97154ceac45SDavid Howells if (error < 0) 97254ceac45SDavid Howells goto error; 97354ceac45SDavid Howells 97454ceac45SDavid Howells /* Probe the root fh to retrieve its FSID */ 9751179acc6SBryan Schumaker error = nfs_probe_fsinfo(server, mount_info->mntfh, fattr); 97654ceac45SDavid Howells if (error < 0) 97754ceac45SDavid Howells goto error; 97854af3bb5STrond Myklebust if (server->nfs_client->rpc_ops->version == 3) { 97954af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) 98054af3bb5STrond Myklebust server->namelen = NFS3_MAXNAMLEN; 9811179acc6SBryan Schumaker if (!(mount_info->parsed->flags & NFS_MOUNT_NORDIRPLUS)) 98254af3bb5STrond Myklebust server->caps |= NFS_CAP_READDIRPLUS; 98354af3bb5STrond Myklebust } else { 98454af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN) 98554af3bb5STrond Myklebust server->namelen = NFS2_MAXNAMLEN; 98654af3bb5STrond Myklebust } 98754af3bb5STrond Myklebust 988fbca779aSTrond Myklebust if (!(fattr->valid & NFS_ATTR_FATTR)) { 989a841b54dSTrond Myklebust error = nfs_mod->rpc_ops->getattr(server, mount_info->mntfh, 990a841b54dSTrond Myklebust fattr, NULL, NULL); 99154ceac45SDavid Howells if (error < 0) { 99254ceac45SDavid Howells dprintk("nfs_create_server: getattr error = %d\n", -error); 99354ceac45SDavid Howells goto error; 99454ceac45SDavid Howells } 99554ceac45SDavid Howells } 996fbca779aSTrond Myklebust memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid)); 99754ceac45SDavid Howells 9986daabf1bSDavid Howells dprintk("Server FSID: %llx:%llx\n", 9996daabf1bSDavid Howells (unsigned long long) server->fsid.major, 10006daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 100154ceac45SDavid Howells 1002fca5238eSChuck Lever nfs_server_insert_lists(server); 100354ceac45SDavid Howells server->mount_time = jiffies; 1004fbca779aSTrond Myklebust nfs_free_fattr(fattr); 100554ceac45SDavid Howells return server; 100654ceac45SDavid Howells 100754ceac45SDavid Howells error: 1008fbca779aSTrond Myklebust nfs_free_fattr(fattr); 100954ceac45SDavid Howells nfs_free_server(server); 101054ceac45SDavid Howells return ERR_PTR(error); 101154ceac45SDavid Howells } 1012ddda8e0aSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_create_server); 101354ceac45SDavid Howells 101454ceac45SDavid Howells /* 101554ceac45SDavid Howells * Clone an NFS2, NFS3 or NFS4 server record 101654ceac45SDavid Howells */ 101754ceac45SDavid Howells struct nfs_server *nfs_clone_server(struct nfs_server *source, 101854ceac45SDavid Howells struct nfs_fh *fh, 10197e6eb683SBryan Schumaker struct nfs_fattr *fattr, 10207e6eb683SBryan Schumaker rpc_authflavor_t flavor) 102154ceac45SDavid Howells { 102254ceac45SDavid Howells struct nfs_server *server; 1023fbca779aSTrond Myklebust struct nfs_fattr *fattr_fsinfo; 102454ceac45SDavid Howells int error; 102554ceac45SDavid Howells 102654ceac45SDavid Howells server = nfs_alloc_server(); 102754ceac45SDavid Howells if (!server) 102854ceac45SDavid Howells return ERR_PTR(-ENOMEM); 102954ceac45SDavid Howells 10301a58e8a0STrond Myklebust server->cred = get_cred(source->cred); 10311a58e8a0STrond Myklebust 1032fbca779aSTrond Myklebust error = -ENOMEM; 1033fbca779aSTrond Myklebust fattr_fsinfo = nfs_alloc_fattr(); 1034fbca779aSTrond Myklebust if (fattr_fsinfo == NULL) 1035fbca779aSTrond Myklebust goto out_free_server; 1036fbca779aSTrond Myklebust 103754ceac45SDavid Howells /* Copy data from the source */ 103854ceac45SDavid Howells server->nfs_client = source->nfs_client; 10390aaaf5c4SChuck Lever server->destroy = source->destroy; 1040212bf41dSElena Reshetova refcount_inc(&server->nfs_client->cl_count); 104154ceac45SDavid Howells nfs_server_copy_userdata(server, source); 104254ceac45SDavid Howells 104354ceac45SDavid Howells server->fsid = fattr->fsid; 104454ceac45SDavid Howells 104533170233STrond Myklebust error = nfs_init_server_rpcclient(server, 104633170233STrond Myklebust source->client->cl_timeout, 10477e6eb683SBryan Schumaker flavor); 104854ceac45SDavid Howells if (error < 0) 104954ceac45SDavid Howells goto out_free_server; 105054ceac45SDavid Howells 105154ceac45SDavid Howells /* probe the filesystem info for this server filesystem */ 1052fbca779aSTrond Myklebust error = nfs_probe_fsinfo(server, fh, fattr_fsinfo); 105354ceac45SDavid Howells if (error < 0) 105454ceac45SDavid Howells goto out_free_server; 105554ceac45SDavid Howells 105654af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 105754af3bb5STrond Myklebust server->namelen = NFS4_MAXNAMLEN; 105854af3bb5STrond Myklebust 105954ceac45SDavid Howells error = nfs_start_lockd(server); 106054ceac45SDavid Howells if (error < 0) 106154ceac45SDavid Howells goto out_free_server; 106254ceac45SDavid Howells 1063fca5238eSChuck Lever nfs_server_insert_lists(server); 106454ceac45SDavid Howells server->mount_time = jiffies; 106554ceac45SDavid Howells 1066fbca779aSTrond Myklebust nfs_free_fattr(fattr_fsinfo); 106754ceac45SDavid Howells return server; 106854ceac45SDavid Howells 106954ceac45SDavid Howells out_free_server: 1070fbca779aSTrond Myklebust nfs_free_fattr(fattr_fsinfo); 107154ceac45SDavid Howells nfs_free_server(server); 107254ceac45SDavid Howells return ERR_PTR(error); 107354ceac45SDavid Howells } 1074ddda8e0aSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_clone_server); 10756aaca566SDavid Howells 10766b13168bSStanislav Kinsbursky void nfs_clients_init(struct net *net) 10776b13168bSStanislav Kinsbursky { 10786b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(net, nfs_net_id); 10796b13168bSStanislav Kinsbursky 10806b13168bSStanislav Kinsbursky INIT_LIST_HEAD(&nn->nfs_client_list); 1081c25d32b2SStanislav Kinsbursky INIT_LIST_HEAD(&nn->nfs_volume_list); 108289d77c8fSBryan Schumaker #if IS_ENABLED(CONFIG_NFS_V4) 108328cd1b3fSStanislav Kinsbursky idr_init(&nn->cb_ident_idr); 108428cd1b3fSStanislav Kinsbursky #endif 10854c03ae4aSTrond Myklebust spin_lock_init(&nn->nfs_client_lock); 10862f86e091SDeepa Dinamani nn->boot_time = ktime_get_real(); 1087bf11fbdbSTrond Myklebust 1088bf11fbdbSTrond Myklebust nfs_netns_sysfs_setup(nn, net); 10896b13168bSStanislav Kinsbursky } 10906b13168bSStanislav Kinsbursky 109110b7a70cSTrond Myklebust void nfs_clients_exit(struct net *net) 109210b7a70cSTrond Myklebust { 109310b7a70cSTrond Myklebust struct nfs_net *nn = net_generic(net, nfs_net_id); 109410b7a70cSTrond Myklebust 1095bf11fbdbSTrond Myklebust nfs_netns_sysfs_destroy(nn); 109610b7a70cSTrond Myklebust nfs_cleanup_cb_ident_idr(net); 109710b7a70cSTrond Myklebust WARN_ON_ONCE(!list_empty(&nn->nfs_client_list)); 109810b7a70cSTrond Myklebust WARN_ON_ONCE(!list_empty(&nn->nfs_volume_list)); 109910b7a70cSTrond Myklebust } 110010b7a70cSTrond Myklebust 11016aaca566SDavid Howells #ifdef CONFIG_PROC_FS 11026aaca566SDavid Howells static void *nfs_server_list_start(struct seq_file *p, loff_t *pos); 11036aaca566SDavid Howells static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos); 11046aaca566SDavid Howells static void nfs_server_list_stop(struct seq_file *p, void *v); 11056aaca566SDavid Howells static int nfs_server_list_show(struct seq_file *m, void *v); 11066aaca566SDavid Howells 110788e9d34cSJames Morris static const struct seq_operations nfs_server_list_ops = { 11086aaca566SDavid Howells .start = nfs_server_list_start, 11096aaca566SDavid Howells .next = nfs_server_list_next, 11106aaca566SDavid Howells .stop = nfs_server_list_stop, 11116aaca566SDavid Howells .show = nfs_server_list_show, 11126aaca566SDavid Howells }; 11136aaca566SDavid Howells 11146aaca566SDavid Howells static void *nfs_volume_list_start(struct seq_file *p, loff_t *pos); 11156aaca566SDavid Howells static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos); 11166aaca566SDavid Howells static void nfs_volume_list_stop(struct seq_file *p, void *v); 11176aaca566SDavid Howells static int nfs_volume_list_show(struct seq_file *m, void *v); 11186aaca566SDavid Howells 111988e9d34cSJames Morris static const struct seq_operations nfs_volume_list_ops = { 11206aaca566SDavid Howells .start = nfs_volume_list_start, 11216aaca566SDavid Howells .next = nfs_volume_list_next, 11226aaca566SDavid Howells .stop = nfs_volume_list_stop, 11236aaca566SDavid Howells .show = nfs_volume_list_show, 11246aaca566SDavid Howells }; 11256aaca566SDavid Howells 11266aaca566SDavid Howells /* 11276aaca566SDavid Howells * set up the iterator to start reading from the server list and return the first item 11286aaca566SDavid Howells */ 11296aaca566SDavid Howells static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos) 11308d11620eSJeff Layton __acquires(&nn->nfs_client_lock) 11316aaca566SDavid Howells { 113265b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); 11336b13168bSStanislav Kinsbursky 11346aaca566SDavid Howells /* lock the list against modification */ 1135dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 11366b13168bSStanislav Kinsbursky return seq_list_start_head(&nn->nfs_client_list, *_pos); 11376aaca566SDavid Howells } 11386aaca566SDavid Howells 11396aaca566SDavid Howells /* 11406aaca566SDavid Howells * move to next server 11416aaca566SDavid Howells */ 11426aaca566SDavid Howells static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos) 11436aaca566SDavid Howells { 114465b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); 11456b13168bSStanislav Kinsbursky 11466b13168bSStanislav Kinsbursky return seq_list_next(v, &nn->nfs_client_list, pos); 11476aaca566SDavid Howells } 11486aaca566SDavid Howells 11496aaca566SDavid Howells /* 11506aaca566SDavid Howells * clean up after reading from the transports list 11516aaca566SDavid Howells */ 11526aaca566SDavid Howells static void nfs_server_list_stop(struct seq_file *p, void *v) 11538d11620eSJeff Layton __releases(&nn->nfs_client_lock) 11546aaca566SDavid Howells { 115565b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); 1156dc030858SStanislav Kinsbursky 1157dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 11586aaca566SDavid Howells } 11596aaca566SDavid Howells 11606aaca566SDavid Howells /* 11616aaca566SDavid Howells * display a header line followed by a load of call lines 11626aaca566SDavid Howells */ 11636aaca566SDavid Howells static int nfs_server_list_show(struct seq_file *m, void *v) 11646aaca566SDavid Howells { 11656aaca566SDavid Howells struct nfs_client *clp; 116665b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); 11676aaca566SDavid Howells 11686aaca566SDavid Howells /* display header on line 1 */ 11696b13168bSStanislav Kinsbursky if (v == &nn->nfs_client_list) { 11706aaca566SDavid Howells seq_puts(m, "NV SERVER PORT USE HOSTNAME\n"); 11716aaca566SDavid Howells return 0; 11726aaca566SDavid Howells } 11736aaca566SDavid Howells 11746aaca566SDavid Howells /* display one transport per line on subsequent lines */ 11756aaca566SDavid Howells clp = list_entry(v, struct nfs_client, cl_share_link); 11766aaca566SDavid Howells 1177940aab49SMalahal Naineni /* Check if the client is initialized */ 1178940aab49SMalahal Naineni if (clp->cl_cons_state != NFS_CS_READY) 1179940aab49SMalahal Naineni return 0; 1180940aab49SMalahal Naineni 11812446ab60STrond Myklebust rcu_read_lock(); 11825d8515caSChuck Lever seq_printf(m, "v%u %s %s %3d %s\n", 118340c55319STrond Myklebust clp->rpc_ops->version, 11845d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), 11855d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), 1186212bf41dSElena Reshetova refcount_read(&clp->cl_count), 11876aaca566SDavid Howells clp->cl_hostname); 11882446ab60STrond Myklebust rcu_read_unlock(); 11896aaca566SDavid Howells 11906aaca566SDavid Howells return 0; 11916aaca566SDavid Howells } 11926aaca566SDavid Howells 11936aaca566SDavid Howells /* 11946aaca566SDavid Howells * set up the iterator to start reading from the volume list and return the first item 11956aaca566SDavid Howells */ 11966aaca566SDavid Howells static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos) 11978d11620eSJeff Layton __acquires(&nn->nfs_client_lock) 11986aaca566SDavid Howells { 119965b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); 1200c25d32b2SStanislav Kinsbursky 12016aaca566SDavid Howells /* lock the list against modification */ 1202dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 1203c25d32b2SStanislav Kinsbursky return seq_list_start_head(&nn->nfs_volume_list, *_pos); 12046aaca566SDavid Howells } 12056aaca566SDavid Howells 12066aaca566SDavid Howells /* 12076aaca566SDavid Howells * move to next volume 12086aaca566SDavid Howells */ 12096aaca566SDavid Howells static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos) 12106aaca566SDavid Howells { 121165b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); 1212c25d32b2SStanislav Kinsbursky 1213c25d32b2SStanislav Kinsbursky return seq_list_next(v, &nn->nfs_volume_list, pos); 12146aaca566SDavid Howells } 12156aaca566SDavid Howells 12166aaca566SDavid Howells /* 12176aaca566SDavid Howells * clean up after reading from the transports list 12186aaca566SDavid Howells */ 12196aaca566SDavid Howells static void nfs_volume_list_stop(struct seq_file *p, void *v) 12208d11620eSJeff Layton __releases(&nn->nfs_client_lock) 12216aaca566SDavid Howells { 122265b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); 1223dc030858SStanislav Kinsbursky 1224dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 12256aaca566SDavid Howells } 12266aaca566SDavid Howells 12276aaca566SDavid Howells /* 12286aaca566SDavid Howells * display a header line followed by a load of call lines 12296aaca566SDavid Howells */ 12306aaca566SDavid Howells static int nfs_volume_list_show(struct seq_file *m, void *v) 12316aaca566SDavid Howells { 12326aaca566SDavid Howells struct nfs_server *server; 12336aaca566SDavid Howells struct nfs_client *clp; 1234df05a49fSKinglong Mee char dev[13]; // 8 for 2^24, 1 for ':', 3 for 2^8, 1 for '\0' 1235df05a49fSKinglong Mee char fsid[34]; // 2 * 16 for %llx, 1 for ':', 1 for '\0' 123665b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); 12376aaca566SDavid Howells 12386aaca566SDavid Howells /* display header on line 1 */ 1239c25d32b2SStanislav Kinsbursky if (v == &nn->nfs_volume_list) { 1240df05a49fSKinglong Mee seq_puts(m, "NV SERVER PORT DEV FSID" 1241df05a49fSKinglong Mee " FSC\n"); 12426aaca566SDavid Howells return 0; 12436aaca566SDavid Howells } 12446aaca566SDavid Howells /* display one transport per line on subsequent lines */ 12456aaca566SDavid Howells server = list_entry(v, struct nfs_server, master_link); 12466aaca566SDavid Howells clp = server->nfs_client; 12476aaca566SDavid Howells 1248df05a49fSKinglong Mee snprintf(dev, sizeof(dev), "%u:%u", 12496aaca566SDavid Howells MAJOR(server->s_dev), MINOR(server->s_dev)); 12506aaca566SDavid Howells 1251df05a49fSKinglong Mee snprintf(fsid, sizeof(fsid), "%llx:%llx", 12526daabf1bSDavid Howells (unsigned long long) server->fsid.major, 12536daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 12546aaca566SDavid Howells 12552446ab60STrond Myklebust rcu_read_lock(); 1256df05a49fSKinglong Mee seq_printf(m, "v%u %s %s %-12s %-33s %s\n", 125740c55319STrond Myklebust clp->rpc_ops->version, 12585d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), 12595d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), 12606aaca566SDavid Howells dev, 12615d1acff1SDavid Howells fsid, 12625d1acff1SDavid Howells nfs_server_fscache_state(server)); 12632446ab60STrond Myklebust rcu_read_unlock(); 12646aaca566SDavid Howells 12656aaca566SDavid Howells return 0; 12666aaca566SDavid Howells } 12676aaca566SDavid Howells 126865b38851SEric W. Biederman int nfs_fs_proc_net_init(struct net *net) 126965b38851SEric W. Biederman { 127065b38851SEric W. Biederman struct nfs_net *nn = net_generic(net, nfs_net_id); 127165b38851SEric W. Biederman struct proc_dir_entry *p; 127265b38851SEric W. Biederman 127365b38851SEric W. Biederman nn->proc_nfsfs = proc_net_mkdir(net, "nfsfs", net->proc_net); 127465b38851SEric W. Biederman if (!nn->proc_nfsfs) 127565b38851SEric W. Biederman goto error_0; 127665b38851SEric W. Biederman 127765b38851SEric W. Biederman /* a file of servers with which we're dealing */ 1278c3506372SChristoph Hellwig p = proc_create_net("servers", S_IFREG|S_IRUGO, nn->proc_nfsfs, 1279c3506372SChristoph Hellwig &nfs_server_list_ops, sizeof(struct seq_net_private)); 128065b38851SEric W. Biederman if (!p) 128165b38851SEric W. Biederman goto error_1; 128265b38851SEric W. Biederman 128365b38851SEric W. Biederman /* a file of volumes that we have mounted */ 1284c3506372SChristoph Hellwig p = proc_create_net("volumes", S_IFREG|S_IRUGO, nn->proc_nfsfs, 1285c3506372SChristoph Hellwig &nfs_volume_list_ops, sizeof(struct seq_net_private)); 128665b38851SEric W. Biederman if (!p) 128721e81002SCong Wang goto error_1; 128865b38851SEric W. Biederman return 0; 128965b38851SEric W. Biederman 129065b38851SEric W. Biederman error_1: 129121e81002SCong Wang remove_proc_subtree("nfsfs", net->proc_net); 129265b38851SEric W. Biederman error_0: 129365b38851SEric W. Biederman return -ENOMEM; 129465b38851SEric W. Biederman } 129565b38851SEric W. Biederman 129665b38851SEric W. Biederman void nfs_fs_proc_net_exit(struct net *net) 129765b38851SEric W. Biederman { 129821e81002SCong Wang remove_proc_subtree("nfsfs", net->proc_net); 129965b38851SEric W. Biederman } 130065b38851SEric W. Biederman 13016aaca566SDavid Howells /* 13026aaca566SDavid Howells * initialise the /proc/fs/nfsfs/ directory 13036aaca566SDavid Howells */ 13046aaca566SDavid Howells int __init nfs_fs_proc_init(void) 13056aaca566SDavid Howells { 13066a062a36SKinglong Mee if (!proc_mkdir("fs/nfsfs", NULL)) 13076aaca566SDavid Howells goto error_0; 13086aaca566SDavid Howells 13096aaca566SDavid Howells /* a file of servers with which we're dealing */ 13106a062a36SKinglong Mee if (!proc_symlink("fs/nfsfs/servers", NULL, "../../net/nfsfs/servers")) 13116aaca566SDavid Howells goto error_1; 13126aaca566SDavid Howells 13136aaca566SDavid Howells /* a file of volumes that we have mounted */ 13146a062a36SKinglong Mee if (!proc_symlink("fs/nfsfs/volumes", NULL, "../../net/nfsfs/volumes")) 13156a062a36SKinglong Mee goto error_1; 13166aaca566SDavid Howells 13176a062a36SKinglong Mee return 0; 13186aaca566SDavid Howells error_1: 13196a062a36SKinglong Mee remove_proc_subtree("fs/nfsfs", NULL); 13206aaca566SDavid Howells error_0: 13216aaca566SDavid Howells return -ENOMEM; 13226aaca566SDavid Howells } 13236aaca566SDavid Howells 13246aaca566SDavid Howells /* 13256aaca566SDavid Howells * clean up the /proc/fs/nfsfs/ directory 13266aaca566SDavid Howells */ 13276aaca566SDavid Howells void nfs_fs_proc_exit(void) 13286aaca566SDavid Howells { 13296a062a36SKinglong Mee remove_proc_subtree("fs/nfsfs", NULL); 13306aaca566SDavid Howells } 13316aaca566SDavid Howells 13326aaca566SDavid Howells #endif /* CONFIG_PROC_FS */ 1333