124c8dbbbSDavid Howells /* client.c: NFS client sharing and management code 224c8dbbbSDavid Howells * 324c8dbbbSDavid Howells * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. 424c8dbbbSDavid Howells * Written by David Howells (dhowells@redhat.com) 524c8dbbbSDavid Howells * 624c8dbbbSDavid Howells * This program is free software; you can redistribute it and/or 724c8dbbbSDavid Howells * modify it under the terms of the GNU General Public License 824c8dbbbSDavid Howells * as published by the Free Software Foundation; either version 924c8dbbbSDavid Howells * 2 of the License, or (at your option) any later version. 1024c8dbbbSDavid Howells */ 1124c8dbbbSDavid Howells 1224c8dbbbSDavid Howells 1324c8dbbbSDavid Howells #include <linux/module.h> 1424c8dbbbSDavid Howells #include <linux/init.h> 15e8edc6e0SAlexey Dobriyan #include <linux/sched.h> 1624c8dbbbSDavid Howells #include <linux/time.h> 1724c8dbbbSDavid Howells #include <linux/kernel.h> 1824c8dbbbSDavid Howells #include <linux/mm.h> 1924c8dbbbSDavid Howells #include <linux/string.h> 2024c8dbbbSDavid Howells #include <linux/stat.h> 2124c8dbbbSDavid Howells #include <linux/errno.h> 2224c8dbbbSDavid Howells #include <linux/unistd.h> 23d8efa4e6SAnna Schumaker #include <linux/sunrpc/addr.h> 2424c8dbbbSDavid Howells #include <linux/sunrpc/clnt.h> 2524c8dbbbSDavid Howells #include <linux/sunrpc/stats.h> 2624c8dbbbSDavid Howells #include <linux/sunrpc/metrics.h> 270896a725S\"Talpey, Thomas\ #include <linux/sunrpc/xprtsock.h> 282cf7ff7aS\"Talpey, Thomas\ #include <linux/sunrpc/xprtrdma.h> 2924c8dbbbSDavid Howells #include <linux/nfs_fs.h> 3024c8dbbbSDavid Howells #include <linux/nfs_mount.h> 3124c8dbbbSDavid Howells #include <linux/nfs4_mount.h> 3224c8dbbbSDavid Howells #include <linux/lockd/bind.h> 3324c8dbbbSDavid Howells #include <linux/seq_file.h> 3424c8dbbbSDavid Howells #include <linux/mount.h> 3524c8dbbbSDavid Howells #include <linux/vfs.h> 3624c8dbbbSDavid Howells #include <linux/inet.h> 373b0d3f93STrond Myklebust #include <linux/in6.h> 385a0e3ad6STejun Heo #include <linux/slab.h> 3940401530SAl Viro #include <linux/idr.h> 403b0d3f93STrond Myklebust #include <net/ipv6.h> 4124c8dbbbSDavid Howells #include <linux/nfs_xdr.h> 420b5b7ae0SAndy Adamson #include <linux/sunrpc/bc_xprt.h> 436b13168bSStanislav Kinsbursky #include <linux/nsproxy.h> 446b13168bSStanislav Kinsbursky #include <linux/pid_namespace.h> 4524c8dbbbSDavid Howells 4624c8dbbbSDavid Howells 4724c8dbbbSDavid Howells #include "nfs4_fs.h" 4824c8dbbbSDavid Howells #include "callback.h" 4924c8dbbbSDavid Howells #include "delegation.h" 5024c8dbbbSDavid Howells #include "iostat.h" 5124c8dbbbSDavid Howells #include "internal.h" 5214727281SDavid Howells #include "fscache.h" 5385e174baSRicardo Labiaga #include "pnfs.h" 54ab7017a3SBryan Schumaker #include "nfs.h" 556b13168bSStanislav Kinsbursky #include "netns.h" 5624c8dbbbSDavid Howells 5724c8dbbbSDavid Howells #define NFSDBG_FACILITY NFSDBG_CLIENT 5824c8dbbbSDavid Howells 5924c8dbbbSDavid Howells static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq); 60ab7017a3SBryan Schumaker static DEFINE_SPINLOCK(nfs_version_lock); 61ab7017a3SBryan Schumaker static DEFINE_MUTEX(nfs_version_mutex); 62ab7017a3SBryan Schumaker static LIST_HEAD(nfs_versions); 6324c8dbbbSDavid Howells 6424c8dbbbSDavid Howells /* 655006a76cSDavid Howells * RPC cruft for NFS 665006a76cSDavid Howells */ 67a613fa16STrond Myklebust static const struct rpc_version *nfs_version[5] = { 68ab7017a3SBryan Schumaker [2] = NULL, 69ab7017a3SBryan Schumaker [3] = NULL, 70ab7017a3SBryan Schumaker [4] = NULL, 715006a76cSDavid Howells }; 725006a76cSDavid Howells 73a613fa16STrond Myklebust const struct rpc_program nfs_program = { 745006a76cSDavid Howells .name = "nfs", 755006a76cSDavid Howells .number = NFS_PROGRAM, 765006a76cSDavid Howells .nrvers = ARRAY_SIZE(nfs_version), 775006a76cSDavid Howells .version = nfs_version, 785006a76cSDavid Howells .stats = &nfs_rpcstat, 79fe0a9b74SJim Rees .pipe_dir_name = NFS_PIPE_DIRNAME, 805006a76cSDavid Howells }; 815006a76cSDavid Howells 825006a76cSDavid Howells struct rpc_stat nfs_rpcstat = { 835006a76cSDavid Howells .program = &nfs_program 845006a76cSDavid Howells }; 855006a76cSDavid Howells 86ab7017a3SBryan Schumaker static struct nfs_subversion *find_nfs_version(unsigned int version) 87ab7017a3SBryan Schumaker { 88ab7017a3SBryan Schumaker struct nfs_subversion *nfs; 89ab7017a3SBryan Schumaker spin_lock(&nfs_version_lock); 90ab7017a3SBryan Schumaker 91ab7017a3SBryan Schumaker list_for_each_entry(nfs, &nfs_versions, list) { 92ab7017a3SBryan Schumaker if (nfs->rpc_ops->version == version) { 93ab7017a3SBryan Schumaker spin_unlock(&nfs_version_lock); 94ab7017a3SBryan Schumaker return nfs; 95ab7017a3SBryan Schumaker } 96ee34e136SYanchuan Nian } 97ab7017a3SBryan Schumaker 98ab7017a3SBryan Schumaker spin_unlock(&nfs_version_lock); 99ee34e136SYanchuan Nian return ERR_PTR(-EPROTONOSUPPORT); 100ab7017a3SBryan Schumaker } 101ab7017a3SBryan Schumaker 102ab7017a3SBryan Schumaker struct nfs_subversion *get_nfs_version(unsigned int version) 103ab7017a3SBryan Schumaker { 104ab7017a3SBryan Schumaker struct nfs_subversion *nfs = find_nfs_version(version); 105ab7017a3SBryan Schumaker 106ab7017a3SBryan Schumaker if (IS_ERR(nfs)) { 107ab7017a3SBryan Schumaker mutex_lock(&nfs_version_mutex); 1081ae811eeSbjschuma@gmail.com request_module("nfsv%d", version); 109ab7017a3SBryan Schumaker nfs = find_nfs_version(version); 110ab7017a3SBryan Schumaker mutex_unlock(&nfs_version_mutex); 111ab7017a3SBryan Schumaker } 112ab7017a3SBryan Schumaker 1131f70ef96SAlexey Khoroshilov if (!IS_ERR(nfs) && !try_module_get(nfs->owner)) 1141f70ef96SAlexey Khoroshilov return ERR_PTR(-EAGAIN); 115ab7017a3SBryan Schumaker return nfs; 116ab7017a3SBryan Schumaker } 117ab7017a3SBryan Schumaker 118ab7017a3SBryan Schumaker void put_nfs_version(struct nfs_subversion *nfs) 119ab7017a3SBryan Schumaker { 120ab7017a3SBryan Schumaker module_put(nfs->owner); 121ab7017a3SBryan Schumaker } 122ab7017a3SBryan Schumaker 123ab7017a3SBryan Schumaker void register_nfs_version(struct nfs_subversion *nfs) 124ab7017a3SBryan Schumaker { 125ab7017a3SBryan Schumaker spin_lock(&nfs_version_lock); 126ab7017a3SBryan Schumaker 127ab7017a3SBryan Schumaker list_add(&nfs->list, &nfs_versions); 128ab7017a3SBryan Schumaker nfs_version[nfs->rpc_ops->version] = nfs->rpc_vers; 129ab7017a3SBryan Schumaker 130ab7017a3SBryan Schumaker spin_unlock(&nfs_version_lock); 131ab7017a3SBryan Schumaker } 132ab7017a3SBryan Schumaker EXPORT_SYMBOL_GPL(register_nfs_version); 133ab7017a3SBryan Schumaker 134ab7017a3SBryan Schumaker void unregister_nfs_version(struct nfs_subversion *nfs) 135ab7017a3SBryan Schumaker { 136ab7017a3SBryan Schumaker spin_lock(&nfs_version_lock); 137ab7017a3SBryan Schumaker 138ab7017a3SBryan Schumaker nfs_version[nfs->rpc_ops->version] = NULL; 139ab7017a3SBryan Schumaker list_del(&nfs->list); 140ab7017a3SBryan Schumaker 141ab7017a3SBryan Schumaker spin_unlock(&nfs_version_lock); 142ab7017a3SBryan Schumaker } 143ab7017a3SBryan Schumaker EXPORT_SYMBOL_GPL(unregister_nfs_version); 144ab7017a3SBryan Schumaker 145ab7017a3SBryan Schumaker /* 14624c8dbbbSDavid Howells * Allocate a shared client record 14724c8dbbbSDavid Howells * 14824c8dbbbSDavid Howells * Since these are allocated/deallocated very rarely, we don't 14924c8dbbbSDavid Howells * bother putting them in a slab cache... 15024c8dbbbSDavid Howells */ 1516663ee7fSBryan Schumaker struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) 15224c8dbbbSDavid Howells { 15324c8dbbbSDavid Howells struct nfs_client *clp; 1547c67db3aSTrond Myklebust struct rpc_cred *cred; 155a21bdd9bSChuck Lever int err = -ENOMEM; 15624c8dbbbSDavid Howells 15724c8dbbbSDavid Howells if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) 15824c8dbbbSDavid Howells goto error_0; 15924c8dbbbSDavid Howells 160ab7017a3SBryan Schumaker clp->cl_nfs_mod = cl_init->nfs_mod; 1611f70ef96SAlexey Khoroshilov if (!try_module_get(clp->cl_nfs_mod->owner)) 1621f70ef96SAlexey Khoroshilov goto error_dealloc; 163ab7017a3SBryan Schumaker 164ab7017a3SBryan Schumaker clp->rpc_ops = clp->cl_nfs_mod->rpc_ops; 16540c55319STrond Myklebust 16624c8dbbbSDavid Howells atomic_set(&clp->cl_count, 1); 16724c8dbbbSDavid Howells clp->cl_cons_state = NFS_CS_INITING; 16824c8dbbbSDavid Howells 1696e4cffd7SChuck Lever memcpy(&clp->cl_addr, cl_init->addr, cl_init->addrlen); 1706e4cffd7SChuck Lever clp->cl_addrlen = cl_init->addrlen; 17124c8dbbbSDavid Howells 1723a498026STrond Myklebust if (cl_init->hostname) { 173a21bdd9bSChuck Lever err = -ENOMEM; 1743a498026STrond Myklebust clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL); 17524c8dbbbSDavid Howells if (!clp->cl_hostname) 17671468513SBenny Halevy goto error_cleanup; 17724c8dbbbSDavid Howells } 17824c8dbbbSDavid Howells 17924c8dbbbSDavid Howells INIT_LIST_HEAD(&clp->cl_superblocks); 18024c8dbbbSDavid Howells clp->cl_rpcclient = ERR_PTR(-EINVAL); 18124c8dbbbSDavid Howells 18259dca3b2STrond Myklebust clp->cl_proto = cl_init->proto; 18373ea666cSChuck Lever clp->cl_net = get_net(cl_init->net); 18459dca3b2STrond Myklebust 18568c97153STrond Myklebust cred = rpc_lookup_machine_cred("*"); 1867c67db3aSTrond Myklebust if (!IS_ERR(cred)) 1877c67db3aSTrond Myklebust clp->cl_machine_cred = cred; 18814727281SDavid Howells nfs_fscache_get_client_cookie(clp); 18914727281SDavid Howells 19024c8dbbbSDavid Howells return clp; 19124c8dbbbSDavid Howells 19271468513SBenny Halevy error_cleanup: 193ab7017a3SBryan Schumaker put_nfs_version(clp->cl_nfs_mod); 1941f70ef96SAlexey Khoroshilov error_dealloc: 19524c8dbbbSDavid Howells kfree(clp); 19624c8dbbbSDavid Howells error_0: 197a21bdd9bSChuck Lever return ERR_PTR(err); 19824c8dbbbSDavid Howells } 199ddda8e0aSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_alloc_client); 20024c8dbbbSDavid Howells 20189d77c8fSBryan Schumaker #if IS_ENABLED(CONFIG_NFS_V4) 20228cd1b3fSStanislav Kinsbursky void nfs_cleanup_cb_ident_idr(struct net *net) 203f4eecd5dSAndy Adamson { 20428cd1b3fSStanislav Kinsbursky struct nfs_net *nn = net_generic(net, nfs_net_id); 20528cd1b3fSStanislav Kinsbursky 20628cd1b3fSStanislav Kinsbursky idr_destroy(&nn->cb_ident_idr); 207f4eecd5dSAndy Adamson } 208f4eecd5dSAndy Adamson 209f4eecd5dSAndy Adamson /* nfs_client_lock held */ 210f4eecd5dSAndy Adamson static void nfs_cb_idr_remove_locked(struct nfs_client *clp) 211f4eecd5dSAndy Adamson { 21273ea666cSChuck Lever struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); 21328cd1b3fSStanislav Kinsbursky 214f4eecd5dSAndy Adamson if (clp->cl_cb_ident) 21528cd1b3fSStanislav Kinsbursky idr_remove(&nn->cb_ident_idr, clp->cl_cb_ident); 216f4eecd5dSAndy Adamson } 217f4eecd5dSAndy Adamson 218f7e8917aSFred Isaman static void pnfs_init_server(struct nfs_server *server) 219f7e8917aSFred Isaman { 220f7e8917aSFred Isaman rpc_init_wait_queue(&server->roc_rpcwaitq, "pNFS ROC"); 221f7e8917aSFred Isaman } 222f7e8917aSFred Isaman 223888ef2e3SAlexandros Batsakis #else 22428cd1b3fSStanislav Kinsbursky void nfs_cleanup_cb_ident_idr(struct net *net) 225f4eecd5dSAndy Adamson { 226f4eecd5dSAndy Adamson } 227f4eecd5dSAndy Adamson 228f4eecd5dSAndy Adamson static void nfs_cb_idr_remove_locked(struct nfs_client *clp) 229f4eecd5dSAndy Adamson { 230f4eecd5dSAndy Adamson } 231f7e8917aSFred Isaman 232f7e8917aSFred Isaman static void pnfs_init_server(struct nfs_server *server) 233f7e8917aSFred Isaman { 234f7e8917aSFred Isaman } 235f7e8917aSFred Isaman 236888ef2e3SAlexandros Batsakis #endif /* CONFIG_NFS_V4 */ 237888ef2e3SAlexandros Batsakis 238888ef2e3SAlexandros Batsakis /* 2395dd3177aSTrond Myklebust * Destroy a shared client record 2405dd3177aSTrond Myklebust */ 241cdb7ecedSBryan Schumaker void nfs_free_client(struct nfs_client *clp) 2425dd3177aSTrond Myklebust { 24340c55319STrond Myklebust dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version); 2445dd3177aSTrond Myklebust 24514727281SDavid Howells nfs_fscache_release_client_cookie(clp); 24614727281SDavid Howells 24724c8dbbbSDavid Howells /* -EIO all pending I/O */ 24824c8dbbbSDavid Howells if (!IS_ERR(clp->cl_rpcclient)) 24924c8dbbbSDavid Howells rpc_shutdown_client(clp->cl_rpcclient); 25024c8dbbbSDavid Howells 2517c67db3aSTrond Myklebust if (clp->cl_machine_cred != NULL) 2527c67db3aSTrond Myklebust put_rpccred(clp->cl_machine_cred); 2537c67db3aSTrond Myklebust 25473ea666cSChuck Lever put_net(clp->cl_net); 255ab7017a3SBryan Schumaker put_nfs_version(clp->cl_nfs_mod); 25624c8dbbbSDavid Howells kfree(clp->cl_hostname); 257f11b2a1cSJeff Layton kfree(clp->cl_acceptor); 25824c8dbbbSDavid Howells kfree(clp); 25924c8dbbbSDavid Howells 26024c8dbbbSDavid Howells dprintk("<-- nfs_free_client()\n"); 26124c8dbbbSDavid Howells } 262ddda8e0aSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_free_client); 26324c8dbbbSDavid Howells 26424c8dbbbSDavid Howells /* 26524c8dbbbSDavid Howells * Release a reference to a shared client record 26624c8dbbbSDavid Howells */ 26724c8dbbbSDavid Howells void nfs_put_client(struct nfs_client *clp) 26824c8dbbbSDavid Howells { 269dc030858SStanislav Kinsbursky struct nfs_net *nn; 270dc030858SStanislav Kinsbursky 27127ba8512SDavid Howells if (!clp) 27227ba8512SDavid Howells return; 27327ba8512SDavid Howells 27424c8dbbbSDavid Howells dprintk("--> nfs_put_client({%d})\n", atomic_read(&clp->cl_count)); 27573ea666cSChuck Lever nn = net_generic(clp->cl_net, nfs_net_id); 27624c8dbbbSDavid Howells 277dc030858SStanislav Kinsbursky if (atomic_dec_and_lock(&clp->cl_count, &nn->nfs_client_lock)) { 27824c8dbbbSDavid Howells list_del(&clp->cl_share_link); 279f4eecd5dSAndy Adamson nfs_cb_idr_remove_locked(clp); 280dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 28124c8dbbbSDavid Howells 2821fea73a8STrond Myklebust WARN_ON_ONCE(!list_empty(&clp->cl_superblocks)); 28324c8dbbbSDavid Howells 284cdb7ecedSBryan Schumaker clp->rpc_ops->free_client(clp); 28524c8dbbbSDavid Howells } 28624c8dbbbSDavid Howells } 28716b374caSAndy Adamson EXPORT_SYMBOL_GPL(nfs_put_client); 28824c8dbbbSDavid Howells 2893fbd67adSTrond Myklebust /* 290c81468a1STrond Myklebust * Find an nfs_client on the list that matches the initialisation data 291c81468a1STrond Myklebust * that is supplied. 292c81468a1STrond Myklebust */ 293c81468a1STrond Myklebust static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *data) 29424c8dbbbSDavid Howells { 29524c8dbbbSDavid Howells struct nfs_client *clp; 296d7371c41SIan Dall const struct sockaddr *sap = data->addr; 2976b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(data->net, nfs_net_id); 29824c8dbbbSDavid Howells 2996b13168bSStanislav Kinsbursky list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) { 300d7371c41SIan Dall const struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; 30113bbc06aSTrond Myklebust /* Don't match clients that failed to initialise properly */ 30213bbc06aSTrond Myklebust if (clp->cl_cons_state < 0) 30313bbc06aSTrond Myklebust continue; 30413bbc06aSTrond Myklebust 30524c8dbbbSDavid Howells /* Different NFS versions cannot share the same nfs_client */ 306ab7017a3SBryan Schumaker if (clp->rpc_ops != data->nfs_mod->rpc_ops) 30724c8dbbbSDavid Howells continue; 30824c8dbbbSDavid Howells 30959dca3b2STrond Myklebust if (clp->cl_proto != data->proto) 31059dca3b2STrond Myklebust continue; 3115aae4a9aSBenny Halevy /* Match nfsv4 minorversion */ 3125aae4a9aSBenny Halevy if (clp->cl_minorversion != data->minorversion) 3135aae4a9aSBenny Halevy continue; 314c81468a1STrond Myklebust /* Match the full socket address */ 315d8efa4e6SAnna Schumaker if (!rpc_cmp_addr_port(sap, clap)) 31604ea1b3eSAndy Adamson /* Match all xprt_switch full socket addresses */ 31704ea1b3eSAndy Adamson if (!rpc_clnt_xprt_switch_has_addr(clp->cl_rpcclient, 31804ea1b3eSAndy Adamson sap)) 31924c8dbbbSDavid Howells continue; 32024c8dbbbSDavid Howells 32124c8dbbbSDavid Howells atomic_inc(&clp->cl_count); 32224c8dbbbSDavid Howells return clp; 32324c8dbbbSDavid Howells } 324c81468a1STrond Myklebust return NULL; 32524c8dbbbSDavid Howells } 32624c8dbbbSDavid Howells 3274697bd5eSTrond Myklebust static bool nfs_client_init_is_complete(const struct nfs_client *clp) 3284697bd5eSTrond Myklebust { 32948d66b97STrond Myklebust return clp->cl_cons_state <= NFS_CS_READY; 3304697bd5eSTrond Myklebust } 3314697bd5eSTrond Myklebust 3324697bd5eSTrond Myklebust int nfs_wait_client_init_complete(const struct nfs_client *clp) 3334697bd5eSTrond Myklebust { 3344697bd5eSTrond Myklebust return wait_event_killable(nfs_client_active_wq, 3354697bd5eSTrond Myklebust nfs_client_init_is_complete(clp)); 3364697bd5eSTrond Myklebust } 33789d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_wait_client_init_complete); 3384697bd5eSTrond Myklebust 33924c8dbbbSDavid Howells /* 340f411703aSChuck Lever * Found an existing client. Make sure it's ready before returning. 341f411703aSChuck Lever */ 342f411703aSChuck Lever static struct nfs_client * 343f411703aSChuck Lever nfs_found_client(const struct nfs_client_initdata *cl_init, 344f411703aSChuck Lever struct nfs_client *clp) 345f411703aSChuck Lever { 346f411703aSChuck Lever int error; 347f411703aSChuck Lever 3484697bd5eSTrond Myklebust error = nfs_wait_client_init_complete(clp); 349f411703aSChuck Lever if (error < 0) { 350f411703aSChuck Lever nfs_put_client(clp); 351f411703aSChuck Lever return ERR_PTR(-ERESTARTSYS); 352f411703aSChuck Lever } 353f411703aSChuck Lever 354f411703aSChuck Lever if (clp->cl_cons_state < NFS_CS_READY) { 355f411703aSChuck Lever error = clp->cl_cons_state; 356f411703aSChuck Lever nfs_put_client(clp); 357f411703aSChuck Lever return ERR_PTR(error); 358f411703aSChuck Lever } 359f411703aSChuck Lever 36054ac471cSTrond Myklebust smp_rmb(); 36154ac471cSTrond Myklebust 362f411703aSChuck Lever dprintk("<-- %s found nfs_client %p for %s\n", 363f411703aSChuck Lever __func__, clp, cl_init->hostname ?: ""); 364f411703aSChuck Lever return clp; 365f411703aSChuck Lever } 366f411703aSChuck Lever 367f411703aSChuck Lever /* 36824c8dbbbSDavid Howells * Look up a client by IP address and protocol version 36924c8dbbbSDavid Howells * - creates a new record if one doesn't yet exist 37024c8dbbbSDavid Howells */ 371fcf10398SBryan Schumaker struct nfs_client * 37245a52a02SAndy Adamson nfs_get_client(const struct nfs_client_initdata *cl_init, 3734bf590e0SChuck Lever rpc_authflavor_t authflavour) 37424c8dbbbSDavid Howells { 37524c8dbbbSDavid Howells struct nfs_client *clp, *new = NULL; 3766b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(cl_init->net, nfs_net_id); 377ab7017a3SBryan Schumaker const struct nfs_rpc_ops *rpc_ops = cl_init->nfs_mod->rpc_ops; 37824c8dbbbSDavid Howells 37931434f49SPeng Tao if (cl_init->hostname == NULL) { 38031434f49SPeng Tao WARN_ON(1); 38131434f49SPeng Tao return NULL; 38231434f49SPeng Tao } 38331434f49SPeng Tao 384d7422c47SChuck Lever dprintk("--> nfs_get_client(%s,v%u)\n", 38531434f49SPeng Tao cl_init->hostname, rpc_ops->version); 38624c8dbbbSDavid Howells 38724c8dbbbSDavid Howells /* see if the client already exists */ 38824c8dbbbSDavid Howells do { 389dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 39024c8dbbbSDavid Howells 391c81468a1STrond Myklebust clp = nfs_match_client(cl_init); 392f411703aSChuck Lever if (clp) { 393f411703aSChuck Lever spin_unlock(&nn->nfs_client_lock); 394f411703aSChuck Lever if (new) 395cdb7ecedSBryan Schumaker new->rpc_ops->free_client(new); 396f411703aSChuck Lever return nfs_found_client(cl_init, clp); 397f411703aSChuck Lever } 3988cab4c39SChuck Lever if (new) { 39905f4c350SChuck Lever list_add_tail(&new->cl_share_link, 40005f4c350SChuck Lever &nn->nfs_client_list); 4018cab4c39SChuck Lever spin_unlock(&nn->nfs_client_lock); 4024bf590e0SChuck Lever new->cl_flags = cl_init->init_flags; 4035c6e5b60STrond Myklebust return rpc_ops->init_client(new, cl_init); 4048cab4c39SChuck Lever } 40524c8dbbbSDavid Howells 406dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 40724c8dbbbSDavid Howells 408ab7017a3SBryan Schumaker new = rpc_ops->alloc_client(cl_init); 409a21bdd9bSChuck Lever } while (!IS_ERR(new)); 41024c8dbbbSDavid Howells 411f411703aSChuck Lever dprintk("<-- nfs_get_client() Failed to find %s (%ld)\n", 41231434f49SPeng Tao cl_init->hostname, PTR_ERR(new)); 413a21bdd9bSChuck Lever return new; 41424c8dbbbSDavid Howells } 41589d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_get_client); 41624c8dbbbSDavid Howells 41724c8dbbbSDavid Howells /* 41824c8dbbbSDavid Howells * Mark a server as ready or failed 41924c8dbbbSDavid Howells */ 42076db6d95SAndy Adamson void nfs_mark_client_ready(struct nfs_client *clp, int state) 42124c8dbbbSDavid Howells { 42254ac471cSTrond Myklebust smp_wmb(); 42324c8dbbbSDavid Howells clp->cl_cons_state = state; 42424c8dbbbSDavid Howells wake_up_all(&nfs_client_active_wq); 42524c8dbbbSDavid Howells } 42689d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_mark_client_ready); 4275006a76cSDavid Howells 4285006a76cSDavid Howells /* 4295006a76cSDavid Howells * Initialise the timeout values for a connection 4305006a76cSDavid Howells */ 431fcf10398SBryan Schumaker void nfs_init_timeout_values(struct rpc_timeout *to, int proto, 432a956bedaSTrond Myklebust int timeo, int retrans) 4335006a76cSDavid Howells { 4345006a76cSDavid Howells to->to_initval = timeo * HZ / 10; 4355006a76cSDavid Howells to->to_retries = retrans; 4365006a76cSDavid Howells 4375006a76cSDavid Howells switch (proto) { 4380896a725S\"Talpey, Thomas\ case XPRT_TRANSPORT_TCP: 4392cf7ff7aS\"Talpey, Thomas\ case XPRT_TRANSPORT_RDMA: 440a956bedaSTrond Myklebust if (retrans == NFS_UNSPEC_RETRANS) 441259875efSTrond Myklebust to->to_retries = NFS_DEF_TCP_RETRANS; 442a956bedaSTrond Myklebust if (timeo == NFS_UNSPEC_TIMEO || to->to_retries == 0) 443259875efSTrond Myklebust to->to_initval = NFS_DEF_TCP_TIMEO * HZ / 10; 4445006a76cSDavid Howells if (to->to_initval > NFS_MAX_TCP_TIMEOUT) 4455006a76cSDavid Howells to->to_initval = NFS_MAX_TCP_TIMEOUT; 4465006a76cSDavid Howells to->to_increment = to->to_initval; 4475006a76cSDavid Howells to->to_maxval = to->to_initval + (to->to_increment * to->to_retries); 4487a3e3e18STrond Myklebust if (to->to_maxval > NFS_MAX_TCP_TIMEOUT) 4497a3e3e18STrond Myklebust to->to_maxval = NFS_MAX_TCP_TIMEOUT; 4507a3e3e18STrond Myklebust if (to->to_maxval < to->to_initval) 4517a3e3e18STrond Myklebust to->to_maxval = to->to_initval; 4525006a76cSDavid Howells to->to_exponential = 0; 4535006a76cSDavid Howells break; 4540896a725S\"Talpey, Thomas\ case XPRT_TRANSPORT_UDP: 455a956bedaSTrond Myklebust if (retrans == NFS_UNSPEC_RETRANS) 456259875efSTrond Myklebust to->to_retries = NFS_DEF_UDP_RETRANS; 457a956bedaSTrond Myklebust if (timeo == NFS_UNSPEC_TIMEO || to->to_initval == 0) 458259875efSTrond Myklebust to->to_initval = NFS_DEF_UDP_TIMEO * HZ / 10; 4595006a76cSDavid Howells if (to->to_initval > NFS_MAX_UDP_TIMEOUT) 4605006a76cSDavid Howells to->to_initval = NFS_MAX_UDP_TIMEOUT; 4615006a76cSDavid Howells to->to_maxval = NFS_MAX_UDP_TIMEOUT; 4625006a76cSDavid Howells to->to_exponential = 1; 4635006a76cSDavid Howells break; 464259875efSTrond Myklebust default: 465259875efSTrond Myklebust BUG(); 4665006a76cSDavid Howells } 4675006a76cSDavid Howells } 46889d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_init_timeout_values); 4695006a76cSDavid Howells 4705006a76cSDavid Howells /* 4715006a76cSDavid Howells * Create an RPC client handle 4725006a76cSDavid Howells */ 473428360d7SBryan Schumaker int nfs_create_rpc_client(struct nfs_client *clp, 4745c6e5b60STrond Myklebust const struct nfs_client_initdata *cl_init, 4754bf590e0SChuck Lever rpc_authflavor_t flavor) 4765006a76cSDavid Howells { 4775006a76cSDavid Howells struct rpc_clnt *clnt = NULL; 47841877d20SChuck Lever struct rpc_create_args args = { 47973ea666cSChuck Lever .net = clp->cl_net, 48059dca3b2STrond Myklebust .protocol = clp->cl_proto, 48141877d20SChuck Lever .address = (struct sockaddr *)&clp->cl_addr, 4826e4cffd7SChuck Lever .addrsize = clp->cl_addrlen, 4835c6e5b60STrond Myklebust .timeout = cl_init->timeparms, 48441877d20SChuck Lever .servername = clp->cl_hostname, 4855c6e5b60STrond Myklebust .nodename = cl_init->nodename, 48641877d20SChuck Lever .program = &nfs_program, 48741877d20SChuck Lever .version = clp->rpc_ops->version, 48841877d20SChuck Lever .authflavor = flavor, 48941877d20SChuck Lever }; 4905006a76cSDavid Howells 4914bf590e0SChuck Lever if (test_bit(NFS_CS_DISCRTRY, &clp->cl_flags)) 4924a01b8a4SChuck Lever args.flags |= RPC_CLNT_CREATE_DISCRTRY; 49399875249STrond Myklebust if (test_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags)) 49499875249STrond Myklebust args.flags |= RPC_CLNT_CREATE_NO_RETRANS_TIMEOUT; 4954bf590e0SChuck Lever if (test_bit(NFS_CS_NORESVPORT, &clp->cl_flags)) 4964a01b8a4SChuck Lever args.flags |= RPC_CLNT_CREATE_NONPRIVPORT; 49798f98cf5STrond Myklebust if (test_bit(NFS_CS_INFINITE_SLOTS, &clp->cl_flags)) 49898f98cf5STrond Myklebust args.flags |= RPC_CLNT_CREATE_INFINITE_SLOTS; 4994a01b8a4SChuck Lever 5005006a76cSDavid Howells if (!IS_ERR(clp->cl_rpcclient)) 5015006a76cSDavid Howells return 0; 5025006a76cSDavid Howells 50341877d20SChuck Lever clnt = rpc_create(&args); 5045006a76cSDavid Howells if (IS_ERR(clnt)) { 5055006a76cSDavid Howells dprintk("%s: cannot create RPC client. Error = %ld\n", 5063110ff80SHarvey Harrison __func__, PTR_ERR(clnt)); 5075006a76cSDavid Howells return PTR_ERR(clnt); 5085006a76cSDavid Howells } 5095006a76cSDavid Howells 5105006a76cSDavid Howells clp->cl_rpcclient = clnt; 5115006a76cSDavid Howells return 0; 5125006a76cSDavid Howells } 51389d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_create_rpc_client); 51454ceac45SDavid Howells 51554ceac45SDavid Howells /* 51654ceac45SDavid Howells * Version 2 or 3 client destruction 51754ceac45SDavid Howells */ 51854ceac45SDavid Howells static void nfs_destroy_server(struct nfs_server *server) 51954ceac45SDavid Howells { 520f259613aSNeilBrown if (server->nlm_host) 5219289e7f9SChuck Lever nlmclnt_done(server->nlm_host); 52254ceac45SDavid Howells } 52354ceac45SDavid Howells 52454ceac45SDavid Howells /* 52554ceac45SDavid Howells * Version 2 or 3 lockd setup 52654ceac45SDavid Howells */ 52754ceac45SDavid Howells static int nfs_start_lockd(struct nfs_server *server) 52854ceac45SDavid Howells { 5299289e7f9SChuck Lever struct nlm_host *host; 5309289e7f9SChuck Lever struct nfs_client *clp = server->nfs_client; 531883bb163SChuck Lever struct nlmclnt_initdata nlm_init = { 532883bb163SChuck Lever .hostname = clp->cl_hostname, 533883bb163SChuck Lever .address = (struct sockaddr *)&clp->cl_addr, 534883bb163SChuck Lever .addrlen = clp->cl_addrlen, 535883bb163SChuck Lever .nfs_version = clp->rpc_ops->version, 5360cb2659bSChuck Lever .noresvport = server->flags & NFS_MOUNT_NORESVPORT ? 5370cb2659bSChuck Lever 1 : 0, 53873ea666cSChuck Lever .net = clp->cl_net, 539883bb163SChuck Lever }; 54054ceac45SDavid Howells 541883bb163SChuck Lever if (nlm_init.nfs_version > 3) 5429289e7f9SChuck Lever return 0; 5435eebde23SSuresh Jayaraman if ((server->flags & NFS_MOUNT_LOCAL_FLOCK) && 5445eebde23SSuresh Jayaraman (server->flags & NFS_MOUNT_LOCAL_FCNTL)) 5459289e7f9SChuck Lever return 0; 5469289e7f9SChuck Lever 5478a6e5debSTrond Myklebust switch (clp->cl_proto) { 5488a6e5debSTrond Myklebust default: 5498a6e5debSTrond Myklebust nlm_init.protocol = IPPROTO_TCP; 5508a6e5debSTrond Myklebust break; 5518a6e5debSTrond Myklebust case XPRT_TRANSPORT_UDP: 5528a6e5debSTrond Myklebust nlm_init.protocol = IPPROTO_UDP; 5538a6e5debSTrond Myklebust } 5548a6e5debSTrond Myklebust 555883bb163SChuck Lever host = nlmclnt_init(&nlm_init); 5569289e7f9SChuck Lever if (IS_ERR(host)) 5579289e7f9SChuck Lever return PTR_ERR(host); 5589289e7f9SChuck Lever 5599289e7f9SChuck Lever server->nlm_host = host; 56054ceac45SDavid Howells server->destroy = nfs_destroy_server; 5619289e7f9SChuck Lever return 0; 56254ceac45SDavid Howells } 56354ceac45SDavid Howells 56454ceac45SDavid Howells /* 56554ceac45SDavid Howells * Create a general RPC client 56654ceac45SDavid Howells */ 567fcf10398SBryan Schumaker int nfs_init_server_rpcclient(struct nfs_server *server, 56833170233STrond Myklebust const struct rpc_timeout *timeo, 56933170233STrond Myklebust rpc_authflavor_t pseudoflavour) 57054ceac45SDavid Howells { 57154ceac45SDavid Howells struct nfs_client *clp = server->nfs_client; 57254ceac45SDavid Howells 573ba9b584cSChuck Lever server->client = rpc_clone_client_set_auth(clp->cl_rpcclient, 574ba9b584cSChuck Lever pseudoflavour); 57554ceac45SDavid Howells if (IS_ERR(server->client)) { 5763110ff80SHarvey Harrison dprintk("%s: couldn't create rpc_client!\n", __func__); 57754ceac45SDavid Howells return PTR_ERR(server->client); 57854ceac45SDavid Howells } 57954ceac45SDavid Howells 58033170233STrond Myklebust memcpy(&server->client->cl_timeout_default, 58133170233STrond Myklebust timeo, 58233170233STrond Myklebust sizeof(server->client->cl_timeout_default)); 58333170233STrond Myklebust server->client->cl_timeout = &server->client->cl_timeout_default; 58454ceac45SDavid Howells server->client->cl_softrtry = 0; 58554ceac45SDavid Howells if (server->flags & NFS_MOUNT_SOFT) 58654ceac45SDavid Howells server->client->cl_softrtry = 1; 58754ceac45SDavid Howells 58854ceac45SDavid Howells return 0; 58954ceac45SDavid Howells } 59089d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_init_server_rpcclient); 59154ceac45SDavid Howells 5928cab4c39SChuck Lever /** 5938cab4c39SChuck Lever * nfs_init_client - Initialise an NFS2 or NFS3 client 5948cab4c39SChuck Lever * 5958cab4c39SChuck Lever * @clp: nfs_client to initialise 5965c6e5b60STrond Myklebust * @cl_init: Initialisation parameters 5978cab4c39SChuck Lever * 5988cab4c39SChuck Lever * Returns pointer to an NFS client, or an ERR_PTR value. 59954ceac45SDavid Howells */ 6008cab4c39SChuck Lever struct nfs_client *nfs_init_client(struct nfs_client *clp, 6015c6e5b60STrond Myklebust const struct nfs_client_initdata *cl_init) 60254ceac45SDavid Howells { 60354ceac45SDavid Howells int error; 60454ceac45SDavid Howells 60554ceac45SDavid Howells if (clp->cl_cons_state == NFS_CS_READY) { 60654ceac45SDavid Howells /* the client is already initialised */ 60754ceac45SDavid Howells dprintk("<-- nfs_init_client() = 0 [already %p]\n", clp); 6088cab4c39SChuck Lever return clp; 60954ceac45SDavid Howells } 61054ceac45SDavid Howells 61154ceac45SDavid Howells /* 61254ceac45SDavid Howells * Create a client RPC handle for doing FSSTAT with UNIX auth only 61354ceac45SDavid Howells * - RFC 2623, sec 2.3.2 61454ceac45SDavid Howells */ 6155c6e5b60STrond Myklebust error = nfs_create_rpc_client(clp, cl_init, RPC_AUTH_UNIX); 61654ceac45SDavid Howells if (error < 0) 61754ceac45SDavid Howells goto error; 61854ceac45SDavid Howells nfs_mark_client_ready(clp, NFS_CS_READY); 6198cab4c39SChuck Lever return clp; 62054ceac45SDavid Howells 62154ceac45SDavid Howells error: 62254ceac45SDavid Howells nfs_mark_client_ready(clp, error); 6238cab4c39SChuck Lever nfs_put_client(clp); 62454ceac45SDavid Howells dprintk("<-- nfs_init_client() = xerror %d\n", error); 6258cab4c39SChuck Lever return ERR_PTR(error); 62654ceac45SDavid Howells } 627ddda8e0aSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_init_client); 62854ceac45SDavid Howells 62954ceac45SDavid Howells /* 63054ceac45SDavid Howells * Create a version 2 or 3 client 63154ceac45SDavid Howells */ 6322283f8d6S\"Talpey, Thomas\ static int nfs_init_server(struct nfs_server *server, 633ab7017a3SBryan Schumaker const struct nfs_parsed_mount_data *data, 634ab7017a3SBryan Schumaker struct nfs_subversion *nfs_mod) 63554ceac45SDavid Howells { 6365c6e5b60STrond Myklebust struct rpc_timeout timeparms; 6373a498026STrond Myklebust struct nfs_client_initdata cl_init = { 6383a498026STrond Myklebust .hostname = data->nfs_server.hostname, 639d7422c47SChuck Lever .addr = (const struct sockaddr *)&data->nfs_server.address, 6404c568017SChuck Lever .addrlen = data->nfs_server.addrlen, 641ab7017a3SBryan Schumaker .nfs_mod = nfs_mod, 64259dca3b2STrond Myklebust .proto = data->nfs_server.protocol, 643e50a7a1aSStanislav Kinsbursky .net = data->net, 6445c6e5b60STrond Myklebust .timeparms = &timeparms, 6453a498026STrond Myklebust }; 64654ceac45SDavid Howells struct nfs_client *clp; 6473a498026STrond Myklebust int error; 64854ceac45SDavid Howells 64954ceac45SDavid Howells dprintk("--> nfs_init_server()\n"); 65054ceac45SDavid Howells 65145a52a02SAndy Adamson nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, 65245a52a02SAndy Adamson data->timeo, data->retrans); 6534bf590e0SChuck Lever if (data->flags & NFS_MOUNT_NORESVPORT) 6544bf590e0SChuck Lever set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags); 65545a52a02SAndy Adamson 65654ceac45SDavid Howells /* Allocate or find a client reference we can use */ 6575c6e5b60STrond Myklebust clp = nfs_get_client(&cl_init, RPC_AUTH_UNIX); 65854ceac45SDavid Howells if (IS_ERR(clp)) { 65954ceac45SDavid Howells dprintk("<-- nfs_init_server() = error %ld\n", PTR_ERR(clp)); 66054ceac45SDavid Howells return PTR_ERR(clp); 66154ceac45SDavid Howells } 66254ceac45SDavid Howells 66354ceac45SDavid Howells server->nfs_client = clp; 66454ceac45SDavid Howells 66554ceac45SDavid Howells /* Initialise the client representation from the mount data */ 666ff3525a5STrond Myklebust server->flags = data->flags; 667b797cac7SDavid Howells server->options = data->options; 66862ab460cSTrond Myklebust server->caps |= NFS_CAP_HARDLINKS|NFS_CAP_SYMLINKS|NFS_CAP_FILEID| 66962ab460cSTrond Myklebust NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|NFS_CAP_OWNER_GROUP| 670cd812599STrond Myklebust NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME; 67154ceac45SDavid Howells 67254ceac45SDavid Howells if (data->rsize) 67354ceac45SDavid Howells server->rsize = nfs_block_size(data->rsize, NULL); 67454ceac45SDavid Howells if (data->wsize) 67554ceac45SDavid Howells server->wsize = nfs_block_size(data->wsize, NULL); 67654ceac45SDavid Howells 67754ceac45SDavid Howells server->acregmin = data->acregmin * HZ; 67854ceac45SDavid Howells server->acregmax = data->acregmax * HZ; 67954ceac45SDavid Howells server->acdirmin = data->acdirmin * HZ; 68054ceac45SDavid Howells server->acdirmax = data->acdirmax * HZ; 68154ceac45SDavid Howells 68254ceac45SDavid Howells /* Start lockd here, before we might error out */ 68354ceac45SDavid Howells error = nfs_start_lockd(server); 68454ceac45SDavid Howells if (error < 0) 68554ceac45SDavid Howells goto error; 68654ceac45SDavid Howells 687f22d6d79SChuck Lever server->port = data->nfs_server.port; 6880f5f49b8SWeston Andros Adamson server->auth_info = data->auth_info; 689f22d6d79SChuck Lever 690a3f73c27SWeston Andros Adamson error = nfs_init_server_rpcclient(server, &timeparms, 691a3f73c27SWeston Andros Adamson data->selected_flavor); 69254ceac45SDavid Howells if (error < 0) 69354ceac45SDavid Howells goto error; 69454ceac45SDavid Howells 6953f8400d1SChuck Lever /* Preserve the values of mount_server-related mount options */ 6963f8400d1SChuck Lever if (data->mount_server.addrlen) { 6973f8400d1SChuck Lever memcpy(&server->mountd_address, &data->mount_server.address, 6983f8400d1SChuck Lever data->mount_server.addrlen); 6993f8400d1SChuck Lever server->mountd_addrlen = data->mount_server.addrlen; 7003f8400d1SChuck Lever } 7013f8400d1SChuck Lever server->mountd_version = data->mount_server.version; 7023f8400d1SChuck Lever server->mountd_port = data->mount_server.port; 7033f8400d1SChuck Lever server->mountd_protocol = data->mount_server.protocol; 7043f8400d1SChuck Lever 70554ceac45SDavid Howells server->namelen = data->namlen; 70654ceac45SDavid Howells dprintk("<-- nfs_init_server() = 0 [new %p]\n", clp); 70754ceac45SDavid Howells return 0; 70854ceac45SDavid Howells 70954ceac45SDavid Howells error: 71054ceac45SDavid Howells server->nfs_client = NULL; 71154ceac45SDavid Howells nfs_put_client(clp); 71254ceac45SDavid Howells dprintk("<-- nfs_init_server() = xerror %d\n", error); 71354ceac45SDavid Howells return error; 71454ceac45SDavid Howells } 71554ceac45SDavid Howells 71654ceac45SDavid Howells /* 71754ceac45SDavid Howells * Load up the server record from information gained in an fsinfo record 71854ceac45SDavid Howells */ 719738fd0f3SBenny Halevy static void nfs_server_set_fsinfo(struct nfs_server *server, 720738fd0f3SBenny Halevy struct nfs_fsinfo *fsinfo) 72154ceac45SDavid Howells { 72254ceac45SDavid Howells unsigned long max_rpc_payload; 72354ceac45SDavid Howells 72454ceac45SDavid Howells /* Work out a lot of parameters */ 72554ceac45SDavid Howells if (server->rsize == 0) 72654ceac45SDavid Howells server->rsize = nfs_block_size(fsinfo->rtpref, NULL); 72754ceac45SDavid Howells if (server->wsize == 0) 72854ceac45SDavid Howells server->wsize = nfs_block_size(fsinfo->wtpref, NULL); 72954ceac45SDavid Howells 73054ceac45SDavid Howells if (fsinfo->rtmax >= 512 && server->rsize > fsinfo->rtmax) 73154ceac45SDavid Howells server->rsize = nfs_block_size(fsinfo->rtmax, NULL); 73254ceac45SDavid Howells if (fsinfo->wtmax >= 512 && server->wsize > fsinfo->wtmax) 73354ceac45SDavid Howells server->wsize = nfs_block_size(fsinfo->wtmax, NULL); 73454ceac45SDavid Howells 73554ceac45SDavid Howells max_rpc_payload = nfs_block_size(rpc_max_payload(server->client), NULL); 73654ceac45SDavid Howells if (server->rsize > max_rpc_payload) 73754ceac45SDavid Howells server->rsize = max_rpc_payload; 73854ceac45SDavid Howells if (server->rsize > NFS_MAX_FILE_IO_SIZE) 73954ceac45SDavid Howells server->rsize = NFS_MAX_FILE_IO_SIZE; 74009cbfeafSKirill A. Shutemov server->rpages = (server->rsize + PAGE_SIZE - 1) >> PAGE_SHIFT; 741e0bf68ddSPeter Zijlstra 742d993831fSJens Axboe server->backing_dev_info.name = "nfs"; 74354ceac45SDavid Howells server->backing_dev_info.ra_pages = server->rpages * NFS_MAX_READAHEAD; 74454ceac45SDavid Howells 74554ceac45SDavid Howells if (server->wsize > max_rpc_payload) 74654ceac45SDavid Howells server->wsize = max_rpc_payload; 74754ceac45SDavid Howells if (server->wsize > NFS_MAX_FILE_IO_SIZE) 74854ceac45SDavid Howells server->wsize = NFS_MAX_FILE_IO_SIZE; 74909cbfeafSKirill A. Shutemov server->wpages = (server->wsize + PAGE_SIZE - 1) >> PAGE_SHIFT; 75085e174baSRicardo Labiaga 75154ceac45SDavid Howells server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL); 75254ceac45SDavid Howells 75354ceac45SDavid Howells server->dtsize = nfs_block_size(fsinfo->dtpref, NULL); 75409cbfeafSKirill A. Shutemov if (server->dtsize > PAGE_SIZE * NFS_MAX_READDIR_PAGES) 75509cbfeafSKirill A. Shutemov server->dtsize = PAGE_SIZE * NFS_MAX_READDIR_PAGES; 75654ceac45SDavid Howells if (server->dtsize > server->rsize) 75754ceac45SDavid Howells server->dtsize = server->rsize; 75854ceac45SDavid Howells 75954ceac45SDavid Howells if (server->flags & NFS_MOUNT_NOAC) { 76054ceac45SDavid Howells server->acregmin = server->acregmax = 0; 76154ceac45SDavid Howells server->acdirmin = server->acdirmax = 0; 76254ceac45SDavid Howells } 76354ceac45SDavid Howells 76454ceac45SDavid Howells server->maxfilesize = fsinfo->maxfilesize; 76554ceac45SDavid Howells 7666b96724eSRicardo Labiaga server->time_delta = fsinfo->time_delta; 7676b96724eSRicardo Labiaga 7682a92ee92SPeng Tao server->clone_blksize = fsinfo->clone_blksize; 76954ceac45SDavid Howells /* We're airborne Set socket buffersize */ 77054ceac45SDavid Howells rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100); 77154ceac45SDavid Howells } 77254ceac45SDavid Howells 77354ceac45SDavid Howells /* 77454ceac45SDavid Howells * Probe filesystem information, including the FSID on v2/v3 77554ceac45SDavid Howells */ 776fcf10398SBryan Schumaker int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr) 77754ceac45SDavid Howells { 77854ceac45SDavid Howells struct nfs_fsinfo fsinfo; 77954ceac45SDavid Howells struct nfs_client *clp = server->nfs_client; 78054ceac45SDavid Howells int error; 78154ceac45SDavid Howells 78254ceac45SDavid Howells dprintk("--> nfs_probe_fsinfo()\n"); 78354ceac45SDavid Howells 78454ceac45SDavid Howells if (clp->rpc_ops->set_capabilities != NULL) { 78554ceac45SDavid Howells error = clp->rpc_ops->set_capabilities(server, mntfh); 78654ceac45SDavid Howells if (error < 0) 78754ceac45SDavid Howells goto out_error; 78854ceac45SDavid Howells } 78954ceac45SDavid Howells 79054ceac45SDavid Howells fsinfo.fattr = fattr; 791ca440c38SJeff Layton fsinfo.nlayouttypes = 0; 7923132e49eSJeff Layton memset(fsinfo.layouttype, 0, sizeof(fsinfo.layouttype)); 79354ceac45SDavid Howells error = clp->rpc_ops->fsinfo(server, mntfh, &fsinfo); 79454ceac45SDavid Howells if (error < 0) 79554ceac45SDavid Howells goto out_error; 79654ceac45SDavid Howells 79771f81e51SKinglong Mee nfs_server_set_fsinfo(server, &fsinfo); 79854ceac45SDavid Howells 79954ceac45SDavid Howells /* Get some general file system info */ 80054ceac45SDavid Howells if (server->namelen == 0) { 80154ceac45SDavid Howells struct nfs_pathconf pathinfo; 80254ceac45SDavid Howells 80354ceac45SDavid Howells pathinfo.fattr = fattr; 80454ceac45SDavid Howells nfs_fattr_init(fattr); 80554ceac45SDavid Howells 80654ceac45SDavid Howells if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0) 80754ceac45SDavid Howells server->namelen = pathinfo.max_namelen; 80854ceac45SDavid Howells } 80954ceac45SDavid Howells 81054ceac45SDavid Howells dprintk("<-- nfs_probe_fsinfo() = 0\n"); 81154ceac45SDavid Howells return 0; 81254ceac45SDavid Howells 81354ceac45SDavid Howells out_error: 81454ceac45SDavid Howells dprintk("nfs_probe_fsinfo: error = %d\n", -error); 81554ceac45SDavid Howells return error; 81654ceac45SDavid Howells } 81789d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_probe_fsinfo); 81854ceac45SDavid Howells 81954ceac45SDavid Howells /* 82054ceac45SDavid Howells * Copy useful information when duplicating a server record 82154ceac45SDavid Howells */ 822fcf10398SBryan Schumaker void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source) 82354ceac45SDavid Howells { 82454ceac45SDavid Howells target->flags = source->flags; 825356e76b8SChuck Lever target->rsize = source->rsize; 826356e76b8SChuck Lever target->wsize = source->wsize; 82754ceac45SDavid Howells target->acregmin = source->acregmin; 82854ceac45SDavid Howells target->acregmax = source->acregmax; 82954ceac45SDavid Howells target->acdirmin = source->acdirmin; 83054ceac45SDavid Howells target->acdirmax = source->acdirmax; 83154ceac45SDavid Howells target->caps = source->caps; 8322df54806SDavid Howells target->options = source->options; 8330f5f49b8SWeston Andros Adamson target->auth_info = source->auth_info; 83454ceac45SDavid Howells } 83589d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_server_copy_userdata); 83654ceac45SDavid Howells 837fcf10398SBryan Schumaker void nfs_server_insert_lists(struct nfs_server *server) 838fca5238eSChuck Lever { 839fca5238eSChuck Lever struct nfs_client *clp = server->nfs_client; 84073ea666cSChuck Lever struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); 841fca5238eSChuck Lever 842dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 843fca5238eSChuck Lever list_add_tail_rcu(&server->client_link, &clp->cl_superblocks); 844c25d32b2SStanislav Kinsbursky list_add_tail(&server->master_link, &nn->nfs_volume_list); 845d3b4c9d7SAndy Adamson clear_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state); 846dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 847fca5238eSChuck Lever 848fca5238eSChuck Lever } 84989d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_server_insert_lists); 850fca5238eSChuck Lever 85132e62b7cSChuck Lever void nfs_server_remove_lists(struct nfs_server *server) 852fca5238eSChuck Lever { 853d3b4c9d7SAndy Adamson struct nfs_client *clp = server->nfs_client; 8544c03ae4aSTrond Myklebust struct nfs_net *nn; 855d3b4c9d7SAndy Adamson 8564c03ae4aSTrond Myklebust if (clp == NULL) 8574c03ae4aSTrond Myklebust return; 85873ea666cSChuck Lever nn = net_generic(clp->cl_net, nfs_net_id); 859dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 860fca5238eSChuck Lever list_del_rcu(&server->client_link); 8614c03ae4aSTrond Myklebust if (list_empty(&clp->cl_superblocks)) 862d3b4c9d7SAndy Adamson set_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state); 863fca5238eSChuck Lever list_del(&server->master_link); 864dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 865fca5238eSChuck Lever 866fca5238eSChuck Lever synchronize_rcu(); 867fca5238eSChuck Lever } 86832e62b7cSChuck Lever EXPORT_SYMBOL_GPL(nfs_server_remove_lists); 869fca5238eSChuck Lever 87054ceac45SDavid Howells /* 87154ceac45SDavid Howells * Allocate and initialise a server record 87254ceac45SDavid Howells */ 873fcf10398SBryan Schumaker struct nfs_server *nfs_alloc_server(void) 87454ceac45SDavid Howells { 87554ceac45SDavid Howells struct nfs_server *server; 87654ceac45SDavid Howells 87754ceac45SDavid Howells server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL); 87854ceac45SDavid Howells if (!server) 87954ceac45SDavid Howells return NULL; 88054ceac45SDavid Howells 88154ceac45SDavid Howells server->client = server->client_acl = ERR_PTR(-EINVAL); 88254ceac45SDavid Howells 88354ceac45SDavid Howells /* Zero out the NFS state stuff */ 88454ceac45SDavid Howells INIT_LIST_HEAD(&server->client_link); 88554ceac45SDavid Howells INIT_LIST_HEAD(&server->master_link); 886d3978bb3SChuck Lever INIT_LIST_HEAD(&server->delegations); 8876382a441SWeston Andros Adamson INIT_LIST_HEAD(&server->layouts); 8880aaaf5c4SChuck Lever INIT_LIST_HEAD(&server->state_owners_lru); 88954ceac45SDavid Howells 890ef818a28SSteve Dickson atomic_set(&server->active, 0); 891ef818a28SSteve Dickson 89254ceac45SDavid Howells server->io_stats = nfs_alloc_iostats(); 89354ceac45SDavid Howells if (!server->io_stats) { 89454ceac45SDavid Howells kfree(server); 89554ceac45SDavid Howells return NULL; 89654ceac45SDavid Howells } 89754ceac45SDavid Howells 89848d07649SJens Axboe if (bdi_init(&server->backing_dev_info)) { 89948d07649SJens Axboe nfs_free_iostats(server->io_stats); 90048d07649SJens Axboe kfree(server); 90148d07649SJens Axboe return NULL; 90248d07649SJens Axboe } 90348d07649SJens Axboe 9049157c31dSTrond Myklebust ida_init(&server->openowner_id); 905d2d7ce28STrond Myklebust ida_init(&server->lockowner_id); 906f7e8917aSFred Isaman pnfs_init_server(server); 907f7e8917aSFred Isaman 90854ceac45SDavid Howells return server; 90954ceac45SDavid Howells } 91089d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_alloc_server); 91154ceac45SDavid Howells 91254ceac45SDavid Howells /* 91354ceac45SDavid Howells * Free up a server record 91454ceac45SDavid Howells */ 91554ceac45SDavid Howells void nfs_free_server(struct nfs_server *server) 91654ceac45SDavid Howells { 91754ceac45SDavid Howells dprintk("--> nfs_free_server()\n"); 91854ceac45SDavid Howells 919fca5238eSChuck Lever nfs_server_remove_lists(server); 92054ceac45SDavid Howells 92154ceac45SDavid Howells if (server->destroy != NULL) 92254ceac45SDavid Howells server->destroy(server); 9235cef338bSTrond Myklebust 9245cef338bSTrond Myklebust if (!IS_ERR(server->client_acl)) 9255cef338bSTrond Myklebust rpc_shutdown_client(server->client_acl); 92654ceac45SDavid Howells if (!IS_ERR(server->client)) 92754ceac45SDavid Howells rpc_shutdown_client(server->client); 92854ceac45SDavid Howells 92954ceac45SDavid Howells nfs_put_client(server->nfs_client); 93054ceac45SDavid Howells 931d2d7ce28STrond Myklebust ida_destroy(&server->lockowner_id); 9329157c31dSTrond Myklebust ida_destroy(&server->openowner_id); 93354ceac45SDavid Howells nfs_free_iostats(server->io_stats); 934e0bf68ddSPeter Zijlstra bdi_destroy(&server->backing_dev_info); 93554ceac45SDavid Howells kfree(server); 93654ceac45SDavid Howells nfs_release_automount_timer(); 93754ceac45SDavid Howells dprintk("<-- nfs_free_server()\n"); 93854ceac45SDavid Howells } 93989d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_free_server); 94054ceac45SDavid Howells 94154ceac45SDavid Howells /* 94254ceac45SDavid Howells * Create a version 2 or 3 volume record 94354ceac45SDavid Howells * - keyed on server and FSID 94454ceac45SDavid Howells */ 9451179acc6SBryan Schumaker struct nfs_server *nfs_create_server(struct nfs_mount_info *mount_info, 946ab7017a3SBryan Schumaker struct nfs_subversion *nfs_mod) 94754ceac45SDavid Howells { 94854ceac45SDavid Howells struct nfs_server *server; 949fbca779aSTrond Myklebust struct nfs_fattr *fattr; 95054ceac45SDavid Howells int error; 95154ceac45SDavid Howells 95254ceac45SDavid Howells server = nfs_alloc_server(); 95354ceac45SDavid Howells if (!server) 95454ceac45SDavid Howells return ERR_PTR(-ENOMEM); 95554ceac45SDavid Howells 956fbca779aSTrond Myklebust error = -ENOMEM; 957fbca779aSTrond Myklebust fattr = nfs_alloc_fattr(); 958fbca779aSTrond Myklebust if (fattr == NULL) 959fbca779aSTrond Myklebust goto error; 960fbca779aSTrond Myklebust 96154ceac45SDavid Howells /* Get a client representation */ 9621179acc6SBryan Schumaker error = nfs_init_server(server, mount_info->parsed, nfs_mod); 96354ceac45SDavid Howells if (error < 0) 96454ceac45SDavid Howells goto error; 96554ceac45SDavid Howells 96654ceac45SDavid Howells /* Probe the root fh to retrieve its FSID */ 9671179acc6SBryan Schumaker error = nfs_probe_fsinfo(server, mount_info->mntfh, fattr); 96854ceac45SDavid Howells if (error < 0) 96954ceac45SDavid Howells goto error; 97054af3bb5STrond Myklebust if (server->nfs_client->rpc_ops->version == 3) { 97154af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) 97254af3bb5STrond Myklebust server->namelen = NFS3_MAXNAMLEN; 9731179acc6SBryan Schumaker if (!(mount_info->parsed->flags & NFS_MOUNT_NORDIRPLUS)) 97454af3bb5STrond Myklebust server->caps |= NFS_CAP_READDIRPLUS; 97554af3bb5STrond Myklebust } else { 97654af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN) 97754af3bb5STrond Myklebust server->namelen = NFS2_MAXNAMLEN; 97854af3bb5STrond Myklebust } 97954af3bb5STrond Myklebust 980fbca779aSTrond Myklebust if (!(fattr->valid & NFS_ATTR_FATTR)) { 9811775fd3eSDavid Quigley error = nfs_mod->rpc_ops->getattr(server, mount_info->mntfh, fattr, NULL); 98254ceac45SDavid Howells if (error < 0) { 98354ceac45SDavid Howells dprintk("nfs_create_server: getattr error = %d\n", -error); 98454ceac45SDavid Howells goto error; 98554ceac45SDavid Howells } 98654ceac45SDavid Howells } 987fbca779aSTrond Myklebust memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid)); 98854ceac45SDavid Howells 9896daabf1bSDavid Howells dprintk("Server FSID: %llx:%llx\n", 9906daabf1bSDavid Howells (unsigned long long) server->fsid.major, 9916daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 99254ceac45SDavid Howells 993fca5238eSChuck Lever nfs_server_insert_lists(server); 99454ceac45SDavid Howells server->mount_time = jiffies; 995fbca779aSTrond Myklebust nfs_free_fattr(fattr); 99654ceac45SDavid Howells return server; 99754ceac45SDavid Howells 99854ceac45SDavid Howells error: 999fbca779aSTrond Myklebust nfs_free_fattr(fattr); 100054ceac45SDavid Howells nfs_free_server(server); 100154ceac45SDavid Howells return ERR_PTR(error); 100254ceac45SDavid Howells } 1003ddda8e0aSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_create_server); 100454ceac45SDavid Howells 100554ceac45SDavid Howells /* 100654ceac45SDavid Howells * Clone an NFS2, NFS3 or NFS4 server record 100754ceac45SDavid Howells */ 100854ceac45SDavid Howells struct nfs_server *nfs_clone_server(struct nfs_server *source, 100954ceac45SDavid Howells struct nfs_fh *fh, 10107e6eb683SBryan Schumaker struct nfs_fattr *fattr, 10117e6eb683SBryan Schumaker rpc_authflavor_t flavor) 101254ceac45SDavid Howells { 101354ceac45SDavid Howells struct nfs_server *server; 1014fbca779aSTrond Myklebust struct nfs_fattr *fattr_fsinfo; 101554ceac45SDavid Howells int error; 101654ceac45SDavid Howells 101754ceac45SDavid Howells dprintk("--> nfs_clone_server(,%llx:%llx,)\n", 10186daabf1bSDavid Howells (unsigned long long) fattr->fsid.major, 10196daabf1bSDavid Howells (unsigned long long) fattr->fsid.minor); 102054ceac45SDavid Howells 102154ceac45SDavid Howells server = nfs_alloc_server(); 102254ceac45SDavid Howells if (!server) 102354ceac45SDavid Howells return ERR_PTR(-ENOMEM); 102454ceac45SDavid Howells 1025fbca779aSTrond Myklebust error = -ENOMEM; 1026fbca779aSTrond Myklebust fattr_fsinfo = nfs_alloc_fattr(); 1027fbca779aSTrond Myklebust if (fattr_fsinfo == NULL) 1028fbca779aSTrond Myklebust goto out_free_server; 1029fbca779aSTrond Myklebust 103054ceac45SDavid Howells /* Copy data from the source */ 103154ceac45SDavid Howells server->nfs_client = source->nfs_client; 10320aaaf5c4SChuck Lever server->destroy = source->destroy; 103354ceac45SDavid Howells atomic_inc(&server->nfs_client->cl_count); 103454ceac45SDavid Howells nfs_server_copy_userdata(server, source); 103554ceac45SDavid Howells 103654ceac45SDavid Howells server->fsid = fattr->fsid; 103754ceac45SDavid Howells 103833170233STrond Myklebust error = nfs_init_server_rpcclient(server, 103933170233STrond Myklebust source->client->cl_timeout, 10407e6eb683SBryan Schumaker flavor); 104154ceac45SDavid Howells if (error < 0) 104254ceac45SDavid Howells goto out_free_server; 104354ceac45SDavid Howells 104454ceac45SDavid Howells /* probe the filesystem info for this server filesystem */ 1045fbca779aSTrond Myklebust error = nfs_probe_fsinfo(server, fh, fattr_fsinfo); 104654ceac45SDavid Howells if (error < 0) 104754ceac45SDavid Howells goto out_free_server; 104854ceac45SDavid Howells 104954af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 105054af3bb5STrond Myklebust server->namelen = NFS4_MAXNAMLEN; 105154af3bb5STrond Myklebust 105254ceac45SDavid Howells dprintk("Cloned FSID: %llx:%llx\n", 10536daabf1bSDavid Howells (unsigned long long) server->fsid.major, 10546daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 105554ceac45SDavid Howells 105654ceac45SDavid Howells error = nfs_start_lockd(server); 105754ceac45SDavid Howells if (error < 0) 105854ceac45SDavid Howells goto out_free_server; 105954ceac45SDavid Howells 1060fca5238eSChuck Lever nfs_server_insert_lists(server); 106154ceac45SDavid Howells server->mount_time = jiffies; 106254ceac45SDavid Howells 1063fbca779aSTrond Myklebust nfs_free_fattr(fattr_fsinfo); 106454ceac45SDavid Howells dprintk("<-- nfs_clone_server() = %p\n", server); 106554ceac45SDavid Howells return server; 106654ceac45SDavid Howells 106754ceac45SDavid Howells out_free_server: 1068fbca779aSTrond Myklebust nfs_free_fattr(fattr_fsinfo); 106954ceac45SDavid Howells nfs_free_server(server); 107054ceac45SDavid Howells dprintk("<-- nfs_clone_server() = error %d\n", error); 107154ceac45SDavid Howells return ERR_PTR(error); 107254ceac45SDavid Howells } 1073ddda8e0aSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_clone_server); 10746aaca566SDavid Howells 10756b13168bSStanislav Kinsbursky void nfs_clients_init(struct net *net) 10766b13168bSStanislav Kinsbursky { 10776b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(net, nfs_net_id); 10786b13168bSStanislav Kinsbursky 10796b13168bSStanislav Kinsbursky INIT_LIST_HEAD(&nn->nfs_client_list); 1080c25d32b2SStanislav Kinsbursky INIT_LIST_HEAD(&nn->nfs_volume_list); 108189d77c8fSBryan Schumaker #if IS_ENABLED(CONFIG_NFS_V4) 108228cd1b3fSStanislav Kinsbursky idr_init(&nn->cb_ident_idr); 108328cd1b3fSStanislav Kinsbursky #endif 10844c03ae4aSTrond Myklebust spin_lock_init(&nn->nfs_client_lock); 1085*2f86e091SDeepa Dinamani nn->boot_time = ktime_get_real(); 10866b13168bSStanislav Kinsbursky } 10876b13168bSStanislav Kinsbursky 10886aaca566SDavid Howells #ifdef CONFIG_PROC_FS 10896aaca566SDavid Howells static int nfs_server_list_open(struct inode *inode, struct file *file); 10906aaca566SDavid Howells static void *nfs_server_list_start(struct seq_file *p, loff_t *pos); 10916aaca566SDavid Howells static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos); 10926aaca566SDavid Howells static void nfs_server_list_stop(struct seq_file *p, void *v); 10936aaca566SDavid Howells static int nfs_server_list_show(struct seq_file *m, void *v); 10946aaca566SDavid Howells 109588e9d34cSJames Morris static const struct seq_operations nfs_server_list_ops = { 10966aaca566SDavid Howells .start = nfs_server_list_start, 10976aaca566SDavid Howells .next = nfs_server_list_next, 10986aaca566SDavid Howells .stop = nfs_server_list_stop, 10996aaca566SDavid Howells .show = nfs_server_list_show, 11006aaca566SDavid Howells }; 11016aaca566SDavid Howells 110200977a59SArjan van de Ven static const struct file_operations nfs_server_list_fops = { 11036aaca566SDavid Howells .open = nfs_server_list_open, 11046aaca566SDavid Howells .read = seq_read, 11056aaca566SDavid Howells .llseek = seq_lseek, 110665b38851SEric W. Biederman .release = seq_release_net, 11076aaca566SDavid Howells }; 11086aaca566SDavid Howells 11096aaca566SDavid Howells static int nfs_volume_list_open(struct inode *inode, struct file *file); 11106aaca566SDavid Howells static void *nfs_volume_list_start(struct seq_file *p, loff_t *pos); 11116aaca566SDavid Howells static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos); 11126aaca566SDavid Howells static void nfs_volume_list_stop(struct seq_file *p, void *v); 11136aaca566SDavid Howells static int nfs_volume_list_show(struct seq_file *m, void *v); 11146aaca566SDavid Howells 111588e9d34cSJames Morris static const struct seq_operations nfs_volume_list_ops = { 11166aaca566SDavid Howells .start = nfs_volume_list_start, 11176aaca566SDavid Howells .next = nfs_volume_list_next, 11186aaca566SDavid Howells .stop = nfs_volume_list_stop, 11196aaca566SDavid Howells .show = nfs_volume_list_show, 11206aaca566SDavid Howells }; 11216aaca566SDavid Howells 112200977a59SArjan van de Ven static const struct file_operations nfs_volume_list_fops = { 11236aaca566SDavid Howells .open = nfs_volume_list_open, 11246aaca566SDavid Howells .read = seq_read, 11256aaca566SDavid Howells .llseek = seq_lseek, 112665b38851SEric W. Biederman .release = seq_release_net, 11276aaca566SDavid Howells }; 11286aaca566SDavid Howells 11296aaca566SDavid Howells /* 11306aaca566SDavid Howells * open "/proc/fs/nfsfs/servers" which provides a summary of servers with which 11316aaca566SDavid Howells * we're dealing 11326aaca566SDavid Howells */ 11336aaca566SDavid Howells static int nfs_server_list_open(struct inode *inode, struct file *file) 11346aaca566SDavid Howells { 113565b38851SEric W. Biederman return seq_open_net(inode, file, &nfs_server_list_ops, 113665b38851SEric W. Biederman sizeof(struct seq_net_private)); 11376aaca566SDavid Howells } 11386aaca566SDavid Howells 11396aaca566SDavid Howells /* 11406aaca566SDavid Howells * set up the iterator to start reading from the server list and return the first item 11416aaca566SDavid Howells */ 11426aaca566SDavid Howells static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos) 11438d11620eSJeff Layton __acquires(&nn->nfs_client_lock) 11446aaca566SDavid Howells { 114565b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); 11466b13168bSStanislav Kinsbursky 11476aaca566SDavid Howells /* lock the list against modification */ 1148dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 11496b13168bSStanislav Kinsbursky return seq_list_start_head(&nn->nfs_client_list, *_pos); 11506aaca566SDavid Howells } 11516aaca566SDavid Howells 11526aaca566SDavid Howells /* 11536aaca566SDavid Howells * move to next server 11546aaca566SDavid Howells */ 11556aaca566SDavid Howells static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos) 11566aaca566SDavid Howells { 115765b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); 11586b13168bSStanislav Kinsbursky 11596b13168bSStanislav Kinsbursky return seq_list_next(v, &nn->nfs_client_list, pos); 11606aaca566SDavid Howells } 11616aaca566SDavid Howells 11626aaca566SDavid Howells /* 11636aaca566SDavid Howells * clean up after reading from the transports list 11646aaca566SDavid Howells */ 11656aaca566SDavid Howells static void nfs_server_list_stop(struct seq_file *p, void *v) 11668d11620eSJeff Layton __releases(&nn->nfs_client_lock) 11676aaca566SDavid Howells { 116865b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); 1169dc030858SStanislav Kinsbursky 1170dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 11716aaca566SDavid Howells } 11726aaca566SDavid Howells 11736aaca566SDavid Howells /* 11746aaca566SDavid Howells * display a header line followed by a load of call lines 11756aaca566SDavid Howells */ 11766aaca566SDavid Howells static int nfs_server_list_show(struct seq_file *m, void *v) 11776aaca566SDavid Howells { 11786aaca566SDavid Howells struct nfs_client *clp; 117965b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); 11806aaca566SDavid Howells 11816aaca566SDavid Howells /* display header on line 1 */ 11826b13168bSStanislav Kinsbursky if (v == &nn->nfs_client_list) { 11836aaca566SDavid Howells seq_puts(m, "NV SERVER PORT USE HOSTNAME\n"); 11846aaca566SDavid Howells return 0; 11856aaca566SDavid Howells } 11866aaca566SDavid Howells 11876aaca566SDavid Howells /* display one transport per line on subsequent lines */ 11886aaca566SDavid Howells clp = list_entry(v, struct nfs_client, cl_share_link); 11896aaca566SDavid Howells 1190940aab49SMalahal Naineni /* Check if the client is initialized */ 1191940aab49SMalahal Naineni if (clp->cl_cons_state != NFS_CS_READY) 1192940aab49SMalahal Naineni return 0; 1193940aab49SMalahal Naineni 11942446ab60STrond Myklebust rcu_read_lock(); 11955d8515caSChuck Lever seq_printf(m, "v%u %s %s %3d %s\n", 119640c55319STrond Myklebust clp->rpc_ops->version, 11975d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), 11985d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), 11996aaca566SDavid Howells atomic_read(&clp->cl_count), 12006aaca566SDavid Howells clp->cl_hostname); 12012446ab60STrond Myklebust rcu_read_unlock(); 12026aaca566SDavid Howells 12036aaca566SDavid Howells return 0; 12046aaca566SDavid Howells } 12056aaca566SDavid Howells 12066aaca566SDavid Howells /* 12076aaca566SDavid Howells * open "/proc/fs/nfsfs/volumes" which provides a summary of extant volumes 12086aaca566SDavid Howells */ 12096aaca566SDavid Howells static int nfs_volume_list_open(struct inode *inode, struct file *file) 12106aaca566SDavid Howells { 12112f3169fbSFabian Frederick return seq_open_net(inode, file, &nfs_volume_list_ops, 121265b38851SEric W. Biederman sizeof(struct seq_net_private)); 12136aaca566SDavid Howells } 12146aaca566SDavid Howells 12156aaca566SDavid Howells /* 12166aaca566SDavid Howells * set up the iterator to start reading from the volume list and return the first item 12176aaca566SDavid Howells */ 12186aaca566SDavid Howells static void *nfs_volume_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); 1222c25d32b2SStanislav Kinsbursky 12236aaca566SDavid Howells /* lock the list against modification */ 1224dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 1225c25d32b2SStanislav Kinsbursky return seq_list_start_head(&nn->nfs_volume_list, *_pos); 12266aaca566SDavid Howells } 12276aaca566SDavid Howells 12286aaca566SDavid Howells /* 12296aaca566SDavid Howells * move to next volume 12306aaca566SDavid Howells */ 12316aaca566SDavid Howells static void *nfs_volume_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); 1234c25d32b2SStanislav Kinsbursky 1235c25d32b2SStanislav Kinsbursky return seq_list_next(v, &nn->nfs_volume_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_volume_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_volume_list_show(struct seq_file *m, void *v) 12536aaca566SDavid Howells { 12546aaca566SDavid Howells struct nfs_server *server; 12556aaca566SDavid Howells struct nfs_client *clp; 1256df05a49fSKinglong Mee char dev[13]; // 8 for 2^24, 1 for ':', 3 for 2^8, 1 for '\0' 1257df05a49fSKinglong Mee char fsid[34]; // 2 * 16 for %llx, 1 for ':', 1 for '\0' 125865b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); 12596aaca566SDavid Howells 12606aaca566SDavid Howells /* display header on line 1 */ 1261c25d32b2SStanislav Kinsbursky if (v == &nn->nfs_volume_list) { 1262df05a49fSKinglong Mee seq_puts(m, "NV SERVER PORT DEV FSID" 1263df05a49fSKinglong Mee " FSC\n"); 12646aaca566SDavid Howells return 0; 12656aaca566SDavid Howells } 12666aaca566SDavid Howells /* display one transport per line on subsequent lines */ 12676aaca566SDavid Howells server = list_entry(v, struct nfs_server, master_link); 12686aaca566SDavid Howells clp = server->nfs_client; 12696aaca566SDavid Howells 1270df05a49fSKinglong Mee snprintf(dev, sizeof(dev), "%u:%u", 12716aaca566SDavid Howells MAJOR(server->s_dev), MINOR(server->s_dev)); 12726aaca566SDavid Howells 1273df05a49fSKinglong Mee snprintf(fsid, sizeof(fsid), "%llx:%llx", 12746daabf1bSDavid Howells (unsigned long long) server->fsid.major, 12756daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 12766aaca566SDavid Howells 12772446ab60STrond Myklebust rcu_read_lock(); 1278df05a49fSKinglong Mee seq_printf(m, "v%u %s %s %-12s %-33s %s\n", 127940c55319STrond Myklebust clp->rpc_ops->version, 12805d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), 12815d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), 12826aaca566SDavid Howells dev, 12835d1acff1SDavid Howells fsid, 12845d1acff1SDavid Howells nfs_server_fscache_state(server)); 12852446ab60STrond Myklebust rcu_read_unlock(); 12866aaca566SDavid Howells 12876aaca566SDavid Howells return 0; 12886aaca566SDavid Howells } 12896aaca566SDavid Howells 129065b38851SEric W. Biederman int nfs_fs_proc_net_init(struct net *net) 129165b38851SEric W. Biederman { 129265b38851SEric W. Biederman struct nfs_net *nn = net_generic(net, nfs_net_id); 129365b38851SEric W. Biederman struct proc_dir_entry *p; 129465b38851SEric W. Biederman 129565b38851SEric W. Biederman nn->proc_nfsfs = proc_net_mkdir(net, "nfsfs", net->proc_net); 129665b38851SEric W. Biederman if (!nn->proc_nfsfs) 129765b38851SEric W. Biederman goto error_0; 129865b38851SEric W. Biederman 129965b38851SEric W. Biederman /* a file of servers with which we're dealing */ 130065b38851SEric W. Biederman p = proc_create("servers", S_IFREG|S_IRUGO, 130165b38851SEric W. Biederman nn->proc_nfsfs, &nfs_server_list_fops); 130265b38851SEric W. Biederman if (!p) 130365b38851SEric W. Biederman goto error_1; 130465b38851SEric W. Biederman 130565b38851SEric W. Biederman /* a file of volumes that we have mounted */ 130665b38851SEric W. Biederman p = proc_create("volumes", S_IFREG|S_IRUGO, 130765b38851SEric W. Biederman nn->proc_nfsfs, &nfs_volume_list_fops); 130865b38851SEric W. Biederman if (!p) 130921e81002SCong Wang goto error_1; 131065b38851SEric W. Biederman return 0; 131165b38851SEric W. Biederman 131265b38851SEric W. Biederman error_1: 131321e81002SCong Wang remove_proc_subtree("nfsfs", net->proc_net); 131465b38851SEric W. Biederman error_0: 131565b38851SEric W. Biederman return -ENOMEM; 131665b38851SEric W. Biederman } 131765b38851SEric W. Biederman 131865b38851SEric W. Biederman void nfs_fs_proc_net_exit(struct net *net) 131965b38851SEric W. Biederman { 132021e81002SCong Wang remove_proc_subtree("nfsfs", net->proc_net); 132165b38851SEric W. Biederman } 132265b38851SEric W. Biederman 13236aaca566SDavid Howells /* 13246aaca566SDavid Howells * initialise the /proc/fs/nfsfs/ directory 13256aaca566SDavid Howells */ 13266aaca566SDavid Howells int __init nfs_fs_proc_init(void) 13276aaca566SDavid Howells { 13286a062a36SKinglong Mee if (!proc_mkdir("fs/nfsfs", NULL)) 13296aaca566SDavid Howells goto error_0; 13306aaca566SDavid Howells 13316aaca566SDavid Howells /* a file of servers with which we're dealing */ 13326a062a36SKinglong Mee if (!proc_symlink("fs/nfsfs/servers", NULL, "../../net/nfsfs/servers")) 13336aaca566SDavid Howells goto error_1; 13346aaca566SDavid Howells 13356aaca566SDavid Howells /* a file of volumes that we have mounted */ 13366a062a36SKinglong Mee if (!proc_symlink("fs/nfsfs/volumes", NULL, "../../net/nfsfs/volumes")) 13376a062a36SKinglong Mee goto error_1; 13386aaca566SDavid Howells 13396a062a36SKinglong Mee return 0; 13406aaca566SDavid Howells error_1: 13416a062a36SKinglong Mee remove_proc_subtree("fs/nfsfs", NULL); 13426aaca566SDavid Howells error_0: 13436aaca566SDavid Howells return -ENOMEM; 13446aaca566SDavid Howells } 13456aaca566SDavid Howells 13466aaca566SDavid Howells /* 13476aaca566SDavid Howells * clean up the /proc/fs/nfsfs/ directory 13486aaca566SDavid Howells */ 13496aaca566SDavid Howells void nfs_fs_proc_exit(void) 13506aaca566SDavid Howells { 13516a062a36SKinglong Mee remove_proc_subtree("fs/nfsfs", NULL); 13526aaca566SDavid Howells } 13536aaca566SDavid Howells 13546aaca566SDavid Howells #endif /* CONFIG_PROC_FS */ 1355