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> 2324c8dbbbSDavid Howells #include <linux/sunrpc/clnt.h> 2424c8dbbbSDavid Howells #include <linux/sunrpc/stats.h> 2524c8dbbbSDavid Howells #include <linux/sunrpc/metrics.h> 260896a725S\"Talpey, Thomas\ #include <linux/sunrpc/xprtsock.h> 272cf7ff7aS\"Talpey, Thomas\ #include <linux/sunrpc/xprtrdma.h> 2824c8dbbbSDavid Howells #include <linux/nfs_fs.h> 2924c8dbbbSDavid Howells #include <linux/nfs_mount.h> 3024c8dbbbSDavid Howells #include <linux/nfs4_mount.h> 3124c8dbbbSDavid Howells #include <linux/lockd/bind.h> 3224c8dbbbSDavid Howells #include <linux/seq_file.h> 3324c8dbbbSDavid Howells #include <linux/mount.h> 3424c8dbbbSDavid Howells #include <linux/nfs_idmap.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 113ab7017a3SBryan Schumaker if (!IS_ERR(nfs)) 114ab7017a3SBryan Schumaker try_module_get(nfs->owner); 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; 161ab7017a3SBryan Schumaker try_module_get(clp->cl_nfs_mod->owner); 162ab7017a3SBryan Schumaker 163ab7017a3SBryan Schumaker clp->rpc_ops = clp->cl_nfs_mod->rpc_ops; 16440c55319STrond Myklebust 16524c8dbbbSDavid Howells atomic_set(&clp->cl_count, 1); 16624c8dbbbSDavid Howells clp->cl_cons_state = NFS_CS_INITING; 16724c8dbbbSDavid Howells 1686e4cffd7SChuck Lever memcpy(&clp->cl_addr, cl_init->addr, cl_init->addrlen); 1696e4cffd7SChuck Lever clp->cl_addrlen = cl_init->addrlen; 17024c8dbbbSDavid Howells 1713a498026STrond Myklebust if (cl_init->hostname) { 172a21bdd9bSChuck Lever err = -ENOMEM; 1733a498026STrond Myklebust clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL); 17424c8dbbbSDavid Howells if (!clp->cl_hostname) 17571468513SBenny Halevy goto error_cleanup; 17624c8dbbbSDavid Howells } 17724c8dbbbSDavid Howells 17824c8dbbbSDavid Howells INIT_LIST_HEAD(&clp->cl_superblocks); 17924c8dbbbSDavid Howells clp->cl_rpcclient = ERR_PTR(-EINVAL); 18024c8dbbbSDavid Howells 18159dca3b2STrond Myklebust clp->cl_proto = cl_init->proto; 18273ea666cSChuck Lever clp->cl_net = get_net(cl_init->net); 18359dca3b2STrond Myklebust 18468c97153STrond Myklebust cred = rpc_lookup_machine_cred("*"); 1857c67db3aSTrond Myklebust if (!IS_ERR(cred)) 1867c67db3aSTrond Myklebust clp->cl_machine_cred = cred; 18714727281SDavid Howells nfs_fscache_get_client_cookie(clp); 18814727281SDavid Howells 18924c8dbbbSDavid Howells return clp; 19024c8dbbbSDavid Howells 19171468513SBenny Halevy error_cleanup: 192ab7017a3SBryan Schumaker put_nfs_version(clp->cl_nfs_mod); 19324c8dbbbSDavid Howells kfree(clp); 19424c8dbbbSDavid Howells error_0: 195a21bdd9bSChuck Lever return ERR_PTR(err); 19624c8dbbbSDavid Howells } 197ddda8e0aSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_alloc_client); 19824c8dbbbSDavid Howells 19989d77c8fSBryan Schumaker #if IS_ENABLED(CONFIG_NFS_V4) 20028cd1b3fSStanislav Kinsbursky void nfs_cleanup_cb_ident_idr(struct net *net) 201f4eecd5dSAndy Adamson { 20228cd1b3fSStanislav Kinsbursky struct nfs_net *nn = net_generic(net, nfs_net_id); 20328cd1b3fSStanislav Kinsbursky 20428cd1b3fSStanislav Kinsbursky idr_destroy(&nn->cb_ident_idr); 205f4eecd5dSAndy Adamson } 206f4eecd5dSAndy Adamson 207f4eecd5dSAndy Adamson /* nfs_client_lock held */ 208f4eecd5dSAndy Adamson static void nfs_cb_idr_remove_locked(struct nfs_client *clp) 209f4eecd5dSAndy Adamson { 21073ea666cSChuck Lever struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); 21128cd1b3fSStanislav Kinsbursky 212f4eecd5dSAndy Adamson if (clp->cl_cb_ident) 21328cd1b3fSStanislav Kinsbursky idr_remove(&nn->cb_ident_idr, clp->cl_cb_ident); 214f4eecd5dSAndy Adamson } 215f4eecd5dSAndy Adamson 216f7e8917aSFred Isaman static void pnfs_init_server(struct nfs_server *server) 217f7e8917aSFred Isaman { 218f7e8917aSFred Isaman rpc_init_wait_queue(&server->roc_rpcwaitq, "pNFS ROC"); 219f7e8917aSFred Isaman } 220f7e8917aSFred Isaman 221888ef2e3SAlexandros Batsakis #else 22228cd1b3fSStanislav Kinsbursky void nfs_cleanup_cb_ident_idr(struct net *net) 223f4eecd5dSAndy Adamson { 224f4eecd5dSAndy Adamson } 225f4eecd5dSAndy Adamson 226f4eecd5dSAndy Adamson static void nfs_cb_idr_remove_locked(struct nfs_client *clp) 227f4eecd5dSAndy Adamson { 228f4eecd5dSAndy Adamson } 229f7e8917aSFred Isaman 230f7e8917aSFred Isaman static void pnfs_init_server(struct nfs_server *server) 231f7e8917aSFred Isaman { 232f7e8917aSFred Isaman } 233f7e8917aSFred Isaman 234888ef2e3SAlexandros Batsakis #endif /* CONFIG_NFS_V4 */ 235888ef2e3SAlexandros Batsakis 236888ef2e3SAlexandros Batsakis /* 2375dd3177aSTrond Myklebust * Destroy a shared client record 2385dd3177aSTrond Myklebust */ 239cdb7ecedSBryan Schumaker void nfs_free_client(struct nfs_client *clp) 2405dd3177aSTrond Myklebust { 24140c55319STrond Myklebust dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version); 2425dd3177aSTrond Myklebust 24314727281SDavid Howells nfs_fscache_release_client_cookie(clp); 24414727281SDavid Howells 24524c8dbbbSDavid Howells /* -EIO all pending I/O */ 24624c8dbbbSDavid Howells if (!IS_ERR(clp->cl_rpcclient)) 24724c8dbbbSDavid Howells rpc_shutdown_client(clp->cl_rpcclient); 24824c8dbbbSDavid Howells 2497c67db3aSTrond Myklebust if (clp->cl_machine_cred != NULL) 2507c67db3aSTrond Myklebust put_rpccred(clp->cl_machine_cred); 2517c67db3aSTrond Myklebust 25273ea666cSChuck Lever put_net(clp->cl_net); 253ab7017a3SBryan Schumaker put_nfs_version(clp->cl_nfs_mod); 25424c8dbbbSDavid Howells kfree(clp->cl_hostname); 25524c8dbbbSDavid Howells kfree(clp); 25624c8dbbbSDavid Howells 25724c8dbbbSDavid Howells dprintk("<-- nfs_free_client()\n"); 25824c8dbbbSDavid Howells } 259ddda8e0aSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_free_client); 26024c8dbbbSDavid Howells 26124c8dbbbSDavid Howells /* 26224c8dbbbSDavid Howells * Release a reference to a shared client record 26324c8dbbbSDavid Howells */ 26424c8dbbbSDavid Howells void nfs_put_client(struct nfs_client *clp) 26524c8dbbbSDavid Howells { 266dc030858SStanislav Kinsbursky struct nfs_net *nn; 267dc030858SStanislav Kinsbursky 26827ba8512SDavid Howells if (!clp) 26927ba8512SDavid Howells return; 27027ba8512SDavid Howells 27124c8dbbbSDavid Howells dprintk("--> nfs_put_client({%d})\n", atomic_read(&clp->cl_count)); 27273ea666cSChuck Lever nn = net_generic(clp->cl_net, nfs_net_id); 27324c8dbbbSDavid Howells 274dc030858SStanislav Kinsbursky if (atomic_dec_and_lock(&clp->cl_count, &nn->nfs_client_lock)) { 27524c8dbbbSDavid Howells list_del(&clp->cl_share_link); 276f4eecd5dSAndy Adamson nfs_cb_idr_remove_locked(clp); 277dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 27824c8dbbbSDavid Howells 2791fea73a8STrond Myklebust WARN_ON_ONCE(!list_empty(&clp->cl_superblocks)); 28024c8dbbbSDavid Howells 281cdb7ecedSBryan Schumaker clp->rpc_ops->free_client(clp); 28224c8dbbbSDavid Howells } 28324c8dbbbSDavid Howells } 28416b374caSAndy Adamson EXPORT_SYMBOL_GPL(nfs_put_client); 28524c8dbbbSDavid Howells 2869082a5ccSTrond Myklebust #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 2879f4c899cSTrond Myklebust /* 2889f4c899cSTrond Myklebust * Test if two ip6 socket addresses refer to the same socket by 2899f4c899cSTrond Myklebust * comparing relevant fields. The padding bytes specifically, are not 2909f4c899cSTrond Myklebust * compared. sin6_flowinfo is not compared because it only affects QoS 2919f4c899cSTrond Myklebust * and sin6_scope_id is only compared if the address is "link local" 2929f4c899cSTrond Myklebust * because "link local" addresses need only be unique to a specific 2939f4c899cSTrond Myklebust * link. Conversely, ordinary unicast addresses might have different 2949f4c899cSTrond Myklebust * sin6_scope_id. 2959f4c899cSTrond Myklebust * 2969f4c899cSTrond Myklebust * The caller should ensure both socket addresses are AF_INET6. 2979f4c899cSTrond Myklebust */ 2983c8c45dfSChuck Lever static int nfs_sockaddr_match_ipaddr6(const struct sockaddr *sa1, 2999f4c899cSTrond Myklebust const struct sockaddr *sa2) 3009f4c899cSTrond Myklebust { 3013c8c45dfSChuck Lever const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sa1; 3023c8c45dfSChuck Lever const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2; 3039f4c899cSTrond Myklebust 304b9dd3abbSMi Jinlong if (!ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr)) 3059f4c899cSTrond Myklebust return 0; 306b9dd3abbSMi Jinlong else if (ipv6_addr_type(&sin1->sin6_addr) & IPV6_ADDR_LINKLOCAL) 307b9dd3abbSMi Jinlong return sin1->sin6_scope_id == sin2->sin6_scope_id; 3083b0d3f93STrond Myklebust 309b9dd3abbSMi Jinlong return 1; 3103b0d3f93STrond Myklebust } 3113c8c45dfSChuck Lever #else /* !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE) */ 3123c8c45dfSChuck Lever static int nfs_sockaddr_match_ipaddr6(const struct sockaddr *sa1, 3139f4c899cSTrond Myklebust const struct sockaddr *sa2) 3149f4c899cSTrond Myklebust { 3159f4c899cSTrond Myklebust return 0; 3169f4c899cSTrond Myklebust } 3179082a5ccSTrond Myklebust #endif 3183b0d3f93STrond Myklebust 31924c8dbbbSDavid Howells /* 320d7371c41SIan Dall * Test if two ip4 socket addresses refer to the same socket, by 321d7371c41SIan Dall * comparing relevant fields. The padding bytes specifically, are 322d7371c41SIan Dall * not compared. 323d7371c41SIan Dall * 324d7371c41SIan Dall * The caller should ensure both socket addresses are AF_INET. 325d7371c41SIan Dall */ 3263c8c45dfSChuck Lever static int nfs_sockaddr_match_ipaddr4(const struct sockaddr *sa1, 3273c8c45dfSChuck Lever const struct sockaddr *sa2) 3283c8c45dfSChuck Lever { 3293c8c45dfSChuck Lever const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sa1; 3303c8c45dfSChuck Lever const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sa2; 3313c8c45dfSChuck Lever 3323c8c45dfSChuck Lever return sin1->sin_addr.s_addr == sin2->sin_addr.s_addr; 3333c8c45dfSChuck Lever } 3343c8c45dfSChuck Lever 3353c8c45dfSChuck Lever static int nfs_sockaddr_cmp_ip6(const struct sockaddr *sa1, 3363c8c45dfSChuck Lever const struct sockaddr *sa2) 3373c8c45dfSChuck Lever { 3383c8c45dfSChuck Lever const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sa1; 3393c8c45dfSChuck Lever const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2; 3403c8c45dfSChuck Lever 3413c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr6(sa1, sa2) && 3423c8c45dfSChuck Lever (sin1->sin6_port == sin2->sin6_port); 3433c8c45dfSChuck Lever } 3443c8c45dfSChuck Lever 3459f4c899cSTrond Myklebust static int nfs_sockaddr_cmp_ip4(const struct sockaddr *sa1, 3469f4c899cSTrond Myklebust const struct sockaddr *sa2) 347d7371c41SIan Dall { 3483c8c45dfSChuck Lever const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sa1; 3493c8c45dfSChuck Lever const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sa2; 3509f4c899cSTrond Myklebust 3513c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr4(sa1, sa2) && 3523c8c45dfSChuck Lever (sin1->sin_port == sin2->sin_port); 353d7371c41SIan Dall } 354d7371c41SIan Dall 3554b7c8dd2SBryan Schumaker #if defined(CONFIG_NFS_V4_1) 356d7371c41SIan Dall /* 357d7371c41SIan Dall * Test if two socket addresses represent the same actual socket, 3583c8c45dfSChuck Lever * by comparing (only) relevant fields, excluding the port number. 3593c8c45dfSChuck Lever */ 360fcf10398SBryan Schumaker int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1, 3613c8c45dfSChuck Lever const struct sockaddr *sa2) 3623c8c45dfSChuck Lever { 3633c8c45dfSChuck Lever if (sa1->sa_family != sa2->sa_family) 3643c8c45dfSChuck Lever return 0; 3653c8c45dfSChuck Lever 3663c8c45dfSChuck Lever switch (sa1->sa_family) { 3673c8c45dfSChuck Lever case AF_INET: 3683c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr4(sa1, sa2); 3693c8c45dfSChuck Lever case AF_INET6: 3703c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr6(sa1, sa2); 3713c8c45dfSChuck Lever } 3723c8c45dfSChuck Lever return 0; 3733c8c45dfSChuck Lever } 37489d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_sockaddr_match_ipaddr); 3754b7c8dd2SBryan Schumaker #endif /* CONFIG_NFS_V4_1 */ 3763c8c45dfSChuck Lever 3773c8c45dfSChuck Lever /* 3783c8c45dfSChuck Lever * Test if two socket addresses represent the same actual socket, 3793c8c45dfSChuck Lever * by comparing (only) relevant fields, including the port number. 380d7371c41SIan Dall */ 381d7371c41SIan Dall static int nfs_sockaddr_cmp(const struct sockaddr *sa1, 382d7371c41SIan Dall const struct sockaddr *sa2) 383d7371c41SIan Dall { 384d7371c41SIan Dall if (sa1->sa_family != sa2->sa_family) 385d7371c41SIan Dall return 0; 386d7371c41SIan Dall 387d7371c41SIan Dall switch (sa1->sa_family) { 388d7371c41SIan Dall case AF_INET: 3899f4c899cSTrond Myklebust return nfs_sockaddr_cmp_ip4(sa1, sa2); 390d7371c41SIan Dall case AF_INET6: 3919f4c899cSTrond Myklebust return nfs_sockaddr_cmp_ip6(sa1, sa2); 392d7371c41SIan Dall } 393d7371c41SIan Dall return 0; 394d7371c41SIan Dall } 395d7371c41SIan Dall 3963fbd67adSTrond Myklebust /* 397c81468a1STrond Myklebust * Find an nfs_client on the list that matches the initialisation data 398c81468a1STrond Myklebust * that is supplied. 399c81468a1STrond Myklebust */ 400c81468a1STrond Myklebust static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *data) 40124c8dbbbSDavid Howells { 40224c8dbbbSDavid Howells struct nfs_client *clp; 403d7371c41SIan Dall const struct sockaddr *sap = data->addr; 4046b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(data->net, nfs_net_id); 40524c8dbbbSDavid Howells 4066b13168bSStanislav Kinsbursky list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) { 407d7371c41SIan Dall const struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; 40813bbc06aSTrond Myklebust /* Don't match clients that failed to initialise properly */ 40913bbc06aSTrond Myklebust if (clp->cl_cons_state < 0) 41013bbc06aSTrond Myklebust continue; 41113bbc06aSTrond Myklebust 41224c8dbbbSDavid Howells /* Different NFS versions cannot share the same nfs_client */ 413ab7017a3SBryan Schumaker if (clp->rpc_ops != data->nfs_mod->rpc_ops) 41424c8dbbbSDavid Howells continue; 41524c8dbbbSDavid Howells 41659dca3b2STrond Myklebust if (clp->cl_proto != data->proto) 41759dca3b2STrond Myklebust continue; 4185aae4a9aSBenny Halevy /* Match nfsv4 minorversion */ 4195aae4a9aSBenny Halevy if (clp->cl_minorversion != data->minorversion) 4205aae4a9aSBenny Halevy continue; 421c81468a1STrond Myklebust /* Match the full socket address */ 422d7371c41SIan Dall if (!nfs_sockaddr_cmp(sap, clap)) 42324c8dbbbSDavid Howells continue; 42424c8dbbbSDavid Howells 42524c8dbbbSDavid Howells atomic_inc(&clp->cl_count); 42624c8dbbbSDavid Howells return clp; 42724c8dbbbSDavid Howells } 428c81468a1STrond Myklebust return NULL; 42924c8dbbbSDavid Howells } 43024c8dbbbSDavid Howells 4314697bd5eSTrond Myklebust static bool nfs_client_init_is_complete(const struct nfs_client *clp) 4324697bd5eSTrond Myklebust { 4334697bd5eSTrond Myklebust return clp->cl_cons_state != NFS_CS_INITING; 4344697bd5eSTrond Myklebust } 4354697bd5eSTrond Myklebust 4364697bd5eSTrond Myklebust int nfs_wait_client_init_complete(const struct nfs_client *clp) 4374697bd5eSTrond Myklebust { 4384697bd5eSTrond Myklebust return wait_event_killable(nfs_client_active_wq, 4394697bd5eSTrond Myklebust nfs_client_init_is_complete(clp)); 4404697bd5eSTrond Myklebust } 44189d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_wait_client_init_complete); 4424697bd5eSTrond Myklebust 44324c8dbbbSDavid Howells /* 444f411703aSChuck Lever * Found an existing client. Make sure it's ready before returning. 445f411703aSChuck Lever */ 446f411703aSChuck Lever static struct nfs_client * 447f411703aSChuck Lever nfs_found_client(const struct nfs_client_initdata *cl_init, 448f411703aSChuck Lever struct nfs_client *clp) 449f411703aSChuck Lever { 450f411703aSChuck Lever int error; 451f411703aSChuck Lever 4524697bd5eSTrond Myklebust error = nfs_wait_client_init_complete(clp); 453f411703aSChuck Lever if (error < 0) { 454f411703aSChuck Lever nfs_put_client(clp); 455f411703aSChuck Lever return ERR_PTR(-ERESTARTSYS); 456f411703aSChuck Lever } 457f411703aSChuck Lever 458f411703aSChuck Lever if (clp->cl_cons_state < NFS_CS_READY) { 459f411703aSChuck Lever error = clp->cl_cons_state; 460f411703aSChuck Lever nfs_put_client(clp); 461f411703aSChuck Lever return ERR_PTR(error); 462f411703aSChuck Lever } 463f411703aSChuck Lever 46454ac471cSTrond Myklebust smp_rmb(); 46554ac471cSTrond Myklebust 466f411703aSChuck Lever dprintk("<-- %s found nfs_client %p for %s\n", 467f411703aSChuck Lever __func__, clp, cl_init->hostname ?: ""); 468f411703aSChuck Lever return clp; 469f411703aSChuck Lever } 470f411703aSChuck Lever 471f411703aSChuck Lever /* 47224c8dbbbSDavid Howells * Look up a client by IP address and protocol version 47324c8dbbbSDavid Howells * - creates a new record if one doesn't yet exist 47424c8dbbbSDavid Howells */ 475fcf10398SBryan Schumaker struct nfs_client * 47645a52a02SAndy Adamson nfs_get_client(const struct nfs_client_initdata *cl_init, 47745a52a02SAndy Adamson const struct rpc_timeout *timeparms, 47845a52a02SAndy Adamson const char *ip_addr, 4794bf590e0SChuck Lever rpc_authflavor_t authflavour) 48024c8dbbbSDavid Howells { 48124c8dbbbSDavid Howells struct nfs_client *clp, *new = NULL; 4826b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(cl_init->net, nfs_net_id); 483ab7017a3SBryan Schumaker const struct nfs_rpc_ops *rpc_ops = cl_init->nfs_mod->rpc_ops; 48424c8dbbbSDavid Howells 485d7422c47SChuck Lever dprintk("--> nfs_get_client(%s,v%u)\n", 486ab7017a3SBryan Schumaker cl_init->hostname ?: "", rpc_ops->version); 48724c8dbbbSDavid Howells 48824c8dbbbSDavid Howells /* see if the client already exists */ 48924c8dbbbSDavid Howells do { 490dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 49124c8dbbbSDavid Howells 492c81468a1STrond Myklebust clp = nfs_match_client(cl_init); 493f411703aSChuck Lever if (clp) { 494f411703aSChuck Lever spin_unlock(&nn->nfs_client_lock); 495f411703aSChuck Lever if (new) 496cdb7ecedSBryan Schumaker new->rpc_ops->free_client(new); 497f411703aSChuck Lever return nfs_found_client(cl_init, clp); 498f411703aSChuck Lever } 4998cab4c39SChuck Lever if (new) { 50005f4c350SChuck Lever list_add_tail(&new->cl_share_link, 50105f4c350SChuck Lever &nn->nfs_client_list); 5028cab4c39SChuck Lever spin_unlock(&nn->nfs_client_lock); 5034bf590e0SChuck Lever new->cl_flags = cl_init->init_flags; 504f8407299SAndy Adamson return rpc_ops->init_client(new, timeparms, ip_addr); 5058cab4c39SChuck Lever } 50624c8dbbbSDavid Howells 507dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 50824c8dbbbSDavid Howells 509ab7017a3SBryan Schumaker new = rpc_ops->alloc_client(cl_init); 510a21bdd9bSChuck Lever } while (!IS_ERR(new)); 51124c8dbbbSDavid Howells 512f411703aSChuck Lever dprintk("<-- nfs_get_client() Failed to find %s (%ld)\n", 513f411703aSChuck Lever cl_init->hostname ?: "", PTR_ERR(new)); 514a21bdd9bSChuck Lever return new; 51524c8dbbbSDavid Howells } 51689d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_get_client); 51724c8dbbbSDavid Howells 51824c8dbbbSDavid Howells /* 51924c8dbbbSDavid Howells * Mark a server as ready or failed 52024c8dbbbSDavid Howells */ 52176db6d95SAndy Adamson void nfs_mark_client_ready(struct nfs_client *clp, int state) 52224c8dbbbSDavid Howells { 52354ac471cSTrond Myklebust smp_wmb(); 52424c8dbbbSDavid Howells clp->cl_cons_state = state; 52524c8dbbbSDavid Howells wake_up_all(&nfs_client_active_wq); 52624c8dbbbSDavid Howells } 52789d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_mark_client_ready); 5285006a76cSDavid Howells 5295006a76cSDavid Howells /* 5305006a76cSDavid Howells * Initialise the timeout values for a connection 5315006a76cSDavid Howells */ 532fcf10398SBryan Schumaker void nfs_init_timeout_values(struct rpc_timeout *to, int proto, 5335006a76cSDavid Howells unsigned int timeo, unsigned int retrans) 5345006a76cSDavid Howells { 5355006a76cSDavid Howells to->to_initval = timeo * HZ / 10; 5365006a76cSDavid Howells to->to_retries = retrans; 5375006a76cSDavid Howells 5385006a76cSDavid Howells switch (proto) { 5390896a725S\"Talpey, Thomas\ case XPRT_TRANSPORT_TCP: 5402cf7ff7aS\"Talpey, Thomas\ case XPRT_TRANSPORT_RDMA: 541259875efSTrond Myklebust if (to->to_retries == 0) 542259875efSTrond Myklebust to->to_retries = NFS_DEF_TCP_RETRANS; 5437a3e3e18STrond Myklebust if (to->to_initval == 0) 544259875efSTrond Myklebust to->to_initval = NFS_DEF_TCP_TIMEO * HZ / 10; 5455006a76cSDavid Howells if (to->to_initval > NFS_MAX_TCP_TIMEOUT) 5465006a76cSDavid Howells to->to_initval = NFS_MAX_TCP_TIMEOUT; 5475006a76cSDavid Howells to->to_increment = to->to_initval; 5485006a76cSDavid Howells to->to_maxval = to->to_initval + (to->to_increment * to->to_retries); 5497a3e3e18STrond Myklebust if (to->to_maxval > NFS_MAX_TCP_TIMEOUT) 5507a3e3e18STrond Myklebust to->to_maxval = NFS_MAX_TCP_TIMEOUT; 5517a3e3e18STrond Myklebust if (to->to_maxval < to->to_initval) 5527a3e3e18STrond Myklebust to->to_maxval = to->to_initval; 5535006a76cSDavid Howells to->to_exponential = 0; 5545006a76cSDavid Howells break; 5550896a725S\"Talpey, Thomas\ case XPRT_TRANSPORT_UDP: 556259875efSTrond Myklebust if (to->to_retries == 0) 557259875efSTrond Myklebust to->to_retries = NFS_DEF_UDP_RETRANS; 5585006a76cSDavid Howells if (!to->to_initval) 559259875efSTrond Myklebust to->to_initval = NFS_DEF_UDP_TIMEO * HZ / 10; 5605006a76cSDavid Howells if (to->to_initval > NFS_MAX_UDP_TIMEOUT) 5615006a76cSDavid Howells to->to_initval = NFS_MAX_UDP_TIMEOUT; 5625006a76cSDavid Howells to->to_maxval = NFS_MAX_UDP_TIMEOUT; 5635006a76cSDavid Howells to->to_exponential = 1; 5645006a76cSDavid Howells break; 565259875efSTrond Myklebust default: 566259875efSTrond Myklebust BUG(); 5675006a76cSDavid Howells } 5685006a76cSDavid Howells } 56989d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_init_timeout_values); 5705006a76cSDavid Howells 5715006a76cSDavid Howells /* 5725006a76cSDavid Howells * Create an RPC client handle 5735006a76cSDavid Howells */ 574428360d7SBryan Schumaker int nfs_create_rpc_client(struct nfs_client *clp, 57533170233STrond Myklebust const struct rpc_timeout *timeparms, 5764bf590e0SChuck Lever rpc_authflavor_t flavor) 5775006a76cSDavid Howells { 5785006a76cSDavid Howells struct rpc_clnt *clnt = NULL; 57941877d20SChuck Lever struct rpc_create_args args = { 58073ea666cSChuck Lever .net = clp->cl_net, 58159dca3b2STrond Myklebust .protocol = clp->cl_proto, 58241877d20SChuck Lever .address = (struct sockaddr *)&clp->cl_addr, 5836e4cffd7SChuck Lever .addrsize = clp->cl_addrlen, 58433170233STrond Myklebust .timeout = timeparms, 58541877d20SChuck Lever .servername = clp->cl_hostname, 58641877d20SChuck Lever .program = &nfs_program, 58741877d20SChuck Lever .version = clp->rpc_ops->version, 58841877d20SChuck Lever .authflavor = flavor, 58941877d20SChuck Lever }; 5905006a76cSDavid Howells 5914bf590e0SChuck Lever if (test_bit(NFS_CS_DISCRTRY, &clp->cl_flags)) 5924a01b8a4SChuck Lever args.flags |= RPC_CLNT_CREATE_DISCRTRY; 59399875249STrond Myklebust if (test_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags)) 59499875249STrond Myklebust args.flags |= RPC_CLNT_CREATE_NO_RETRANS_TIMEOUT; 5954bf590e0SChuck Lever if (test_bit(NFS_CS_NORESVPORT, &clp->cl_flags)) 5964a01b8a4SChuck Lever args.flags |= RPC_CLNT_CREATE_NONPRIVPORT; 59798f98cf5STrond Myklebust if (test_bit(NFS_CS_INFINITE_SLOTS, &clp->cl_flags)) 59898f98cf5STrond Myklebust args.flags |= RPC_CLNT_CREATE_INFINITE_SLOTS; 5994a01b8a4SChuck Lever 6005006a76cSDavid Howells if (!IS_ERR(clp->cl_rpcclient)) 6015006a76cSDavid Howells return 0; 6025006a76cSDavid Howells 60341877d20SChuck Lever clnt = rpc_create(&args); 6045006a76cSDavid Howells if (IS_ERR(clnt)) { 6055006a76cSDavid Howells dprintk("%s: cannot create RPC client. Error = %ld\n", 6063110ff80SHarvey Harrison __func__, PTR_ERR(clnt)); 6075006a76cSDavid Howells return PTR_ERR(clnt); 6085006a76cSDavid Howells } 6095006a76cSDavid Howells 6105006a76cSDavid Howells clp->cl_rpcclient = clnt; 6115006a76cSDavid Howells return 0; 6125006a76cSDavid Howells } 61389d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_create_rpc_client); 61454ceac45SDavid Howells 61554ceac45SDavid Howells /* 61654ceac45SDavid Howells * Version 2 or 3 client destruction 61754ceac45SDavid Howells */ 61854ceac45SDavid Howells static void nfs_destroy_server(struct nfs_server *server) 61954ceac45SDavid Howells { 620f259613aSNeilBrown if (server->nlm_host) 6219289e7f9SChuck Lever nlmclnt_done(server->nlm_host); 62254ceac45SDavid Howells } 62354ceac45SDavid Howells 62454ceac45SDavid Howells /* 62554ceac45SDavid Howells * Version 2 or 3 lockd setup 62654ceac45SDavid Howells */ 62754ceac45SDavid Howells static int nfs_start_lockd(struct nfs_server *server) 62854ceac45SDavid Howells { 6299289e7f9SChuck Lever struct nlm_host *host; 6309289e7f9SChuck Lever struct nfs_client *clp = server->nfs_client; 631883bb163SChuck Lever struct nlmclnt_initdata nlm_init = { 632883bb163SChuck Lever .hostname = clp->cl_hostname, 633883bb163SChuck Lever .address = (struct sockaddr *)&clp->cl_addr, 634883bb163SChuck Lever .addrlen = clp->cl_addrlen, 635883bb163SChuck Lever .nfs_version = clp->rpc_ops->version, 6360cb2659bSChuck Lever .noresvport = server->flags & NFS_MOUNT_NORESVPORT ? 6370cb2659bSChuck Lever 1 : 0, 63873ea666cSChuck Lever .net = clp->cl_net, 639883bb163SChuck Lever }; 64054ceac45SDavid Howells 641883bb163SChuck Lever if (nlm_init.nfs_version > 3) 6429289e7f9SChuck Lever return 0; 6435eebde23SSuresh Jayaraman if ((server->flags & NFS_MOUNT_LOCAL_FLOCK) && 6445eebde23SSuresh Jayaraman (server->flags & NFS_MOUNT_LOCAL_FCNTL)) 6459289e7f9SChuck Lever return 0; 6469289e7f9SChuck Lever 6478a6e5debSTrond Myklebust switch (clp->cl_proto) { 6488a6e5debSTrond Myklebust default: 6498a6e5debSTrond Myklebust nlm_init.protocol = IPPROTO_TCP; 6508a6e5debSTrond Myklebust break; 6518a6e5debSTrond Myklebust case XPRT_TRANSPORT_UDP: 6528a6e5debSTrond Myklebust nlm_init.protocol = IPPROTO_UDP; 6538a6e5debSTrond Myklebust } 6548a6e5debSTrond Myklebust 655883bb163SChuck Lever host = nlmclnt_init(&nlm_init); 6569289e7f9SChuck Lever if (IS_ERR(host)) 6579289e7f9SChuck Lever return PTR_ERR(host); 6589289e7f9SChuck Lever 6599289e7f9SChuck Lever server->nlm_host = host; 66054ceac45SDavid Howells server->destroy = nfs_destroy_server; 6619289e7f9SChuck Lever return 0; 66254ceac45SDavid Howells } 66354ceac45SDavid Howells 66454ceac45SDavid Howells /* 66554ceac45SDavid Howells * Create a general RPC client 66654ceac45SDavid Howells */ 667fcf10398SBryan Schumaker int nfs_init_server_rpcclient(struct nfs_server *server, 66833170233STrond Myklebust const struct rpc_timeout *timeo, 66933170233STrond Myklebust rpc_authflavor_t pseudoflavour) 67054ceac45SDavid Howells { 67154ceac45SDavid Howells struct nfs_client *clp = server->nfs_client; 67254ceac45SDavid Howells 673ba9b584cSChuck Lever server->client = rpc_clone_client_set_auth(clp->cl_rpcclient, 674ba9b584cSChuck Lever pseudoflavour); 67554ceac45SDavid Howells if (IS_ERR(server->client)) { 6763110ff80SHarvey Harrison dprintk("%s: couldn't create rpc_client!\n", __func__); 67754ceac45SDavid Howells return PTR_ERR(server->client); 67854ceac45SDavid Howells } 67954ceac45SDavid Howells 68033170233STrond Myklebust memcpy(&server->client->cl_timeout_default, 68133170233STrond Myklebust timeo, 68233170233STrond Myklebust sizeof(server->client->cl_timeout_default)); 68333170233STrond Myklebust server->client->cl_timeout = &server->client->cl_timeout_default; 68454ceac45SDavid Howells server->client->cl_softrtry = 0; 68554ceac45SDavid Howells if (server->flags & NFS_MOUNT_SOFT) 68654ceac45SDavid Howells server->client->cl_softrtry = 1; 68754ceac45SDavid Howells 68854ceac45SDavid Howells return 0; 68954ceac45SDavid Howells } 69089d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_init_server_rpcclient); 69154ceac45SDavid Howells 6928cab4c39SChuck Lever /** 6938cab4c39SChuck Lever * nfs_init_client - Initialise an NFS2 or NFS3 client 6948cab4c39SChuck Lever * 6958cab4c39SChuck Lever * @clp: nfs_client to initialise 6968cab4c39SChuck Lever * @timeparms: timeout parameters for underlying RPC transport 6978cab4c39SChuck Lever * @ip_addr: IP presentation address (not used) 6988cab4c39SChuck Lever * 6998cab4c39SChuck Lever * Returns pointer to an NFS client, or an ERR_PTR value. 70054ceac45SDavid Howells */ 7018cab4c39SChuck Lever struct nfs_client *nfs_init_client(struct nfs_client *clp, 7028cab4c39SChuck Lever const struct rpc_timeout *timeparms, 703f8407299SAndy Adamson const char *ip_addr) 70454ceac45SDavid Howells { 70554ceac45SDavid Howells int error; 70654ceac45SDavid Howells 70754ceac45SDavid Howells if (clp->cl_cons_state == NFS_CS_READY) { 70854ceac45SDavid Howells /* the client is already initialised */ 70954ceac45SDavid Howells dprintk("<-- nfs_init_client() = 0 [already %p]\n", clp); 7108cab4c39SChuck Lever return clp; 71154ceac45SDavid Howells } 71254ceac45SDavid Howells 71354ceac45SDavid Howells /* 71454ceac45SDavid Howells * Create a client RPC handle for doing FSSTAT with UNIX auth only 71554ceac45SDavid Howells * - RFC 2623, sec 2.3.2 71654ceac45SDavid Howells */ 7174bf590e0SChuck Lever error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX); 71854ceac45SDavid Howells if (error < 0) 71954ceac45SDavid Howells goto error; 72054ceac45SDavid Howells nfs_mark_client_ready(clp, NFS_CS_READY); 7218cab4c39SChuck Lever return clp; 72254ceac45SDavid Howells 72354ceac45SDavid Howells error: 72454ceac45SDavid Howells nfs_mark_client_ready(clp, error); 7258cab4c39SChuck Lever nfs_put_client(clp); 72654ceac45SDavid Howells dprintk("<-- nfs_init_client() = xerror %d\n", error); 7278cab4c39SChuck Lever return ERR_PTR(error); 72854ceac45SDavid Howells } 729ddda8e0aSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_init_client); 73054ceac45SDavid Howells 73154ceac45SDavid Howells /* 73254ceac45SDavid Howells * Create a version 2 or 3 client 73354ceac45SDavid Howells */ 7342283f8d6S\"Talpey, Thomas\ static int nfs_init_server(struct nfs_server *server, 735ab7017a3SBryan Schumaker const struct nfs_parsed_mount_data *data, 736ab7017a3SBryan Schumaker struct nfs_subversion *nfs_mod) 73754ceac45SDavid Howells { 7383a498026STrond Myklebust struct nfs_client_initdata cl_init = { 7393a498026STrond Myklebust .hostname = data->nfs_server.hostname, 740d7422c47SChuck Lever .addr = (const struct sockaddr *)&data->nfs_server.address, 7414c568017SChuck Lever .addrlen = data->nfs_server.addrlen, 742ab7017a3SBryan Schumaker .nfs_mod = nfs_mod, 74359dca3b2STrond Myklebust .proto = data->nfs_server.protocol, 744e50a7a1aSStanislav Kinsbursky .net = data->net, 7453a498026STrond Myklebust }; 74633170233STrond Myklebust struct rpc_timeout timeparms; 74754ceac45SDavid Howells struct nfs_client *clp; 7483a498026STrond Myklebust int error; 74954ceac45SDavid Howells 75054ceac45SDavid Howells dprintk("--> nfs_init_server()\n"); 75154ceac45SDavid Howells 75245a52a02SAndy Adamson nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, 75345a52a02SAndy Adamson data->timeo, data->retrans); 7544bf590e0SChuck Lever if (data->flags & NFS_MOUNT_NORESVPORT) 7554bf590e0SChuck Lever set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags); 75645a52a02SAndy Adamson 75754ceac45SDavid Howells /* Allocate or find a client reference we can use */ 7584bf590e0SChuck Lever clp = nfs_get_client(&cl_init, &timeparms, NULL, RPC_AUTH_UNIX); 75954ceac45SDavid Howells if (IS_ERR(clp)) { 76054ceac45SDavid Howells dprintk("<-- nfs_init_server() = error %ld\n", PTR_ERR(clp)); 76154ceac45SDavid Howells return PTR_ERR(clp); 76254ceac45SDavid Howells } 76354ceac45SDavid Howells 76454ceac45SDavid Howells server->nfs_client = clp; 76554ceac45SDavid Howells 76654ceac45SDavid Howells /* Initialise the client representation from the mount data */ 767ff3525a5STrond Myklebust server->flags = data->flags; 768b797cac7SDavid Howells server->options = data->options; 76962ab460cSTrond Myklebust server->caps |= NFS_CAP_HARDLINKS|NFS_CAP_SYMLINKS|NFS_CAP_FILEID| 77062ab460cSTrond Myklebust NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|NFS_CAP_OWNER_GROUP| 7713a1556e8STrond Myklebust NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME|NFS_CAP_CHANGE_ATTR; 77254ceac45SDavid Howells 77354ceac45SDavid Howells if (data->rsize) 77454ceac45SDavid Howells server->rsize = nfs_block_size(data->rsize, NULL); 77554ceac45SDavid Howells if (data->wsize) 77654ceac45SDavid Howells server->wsize = nfs_block_size(data->wsize, NULL); 77754ceac45SDavid Howells 77854ceac45SDavid Howells server->acregmin = data->acregmin * HZ; 77954ceac45SDavid Howells server->acregmax = data->acregmax * HZ; 78054ceac45SDavid Howells server->acdirmin = data->acdirmin * HZ; 78154ceac45SDavid Howells server->acdirmax = data->acdirmax * HZ; 78254ceac45SDavid Howells 78354ceac45SDavid Howells /* Start lockd here, before we might error out */ 78454ceac45SDavid Howells error = nfs_start_lockd(server); 78554ceac45SDavid Howells if (error < 0) 78654ceac45SDavid Howells goto error; 78754ceac45SDavid Howells 788f22d6d79SChuck Lever server->port = data->nfs_server.port; 7890f5f49b8SWeston Andros Adamson server->auth_info = data->auth_info; 790f22d6d79SChuck Lever 791a3f73c27SWeston Andros Adamson error = nfs_init_server_rpcclient(server, &timeparms, 792a3f73c27SWeston Andros Adamson data->selected_flavor); 79354ceac45SDavid Howells if (error < 0) 79454ceac45SDavid Howells goto error; 79554ceac45SDavid Howells 7963f8400d1SChuck Lever /* Preserve the values of mount_server-related mount options */ 7973f8400d1SChuck Lever if (data->mount_server.addrlen) { 7983f8400d1SChuck Lever memcpy(&server->mountd_address, &data->mount_server.address, 7993f8400d1SChuck Lever data->mount_server.addrlen); 8003f8400d1SChuck Lever server->mountd_addrlen = data->mount_server.addrlen; 8013f8400d1SChuck Lever } 8023f8400d1SChuck Lever server->mountd_version = data->mount_server.version; 8033f8400d1SChuck Lever server->mountd_port = data->mount_server.port; 8043f8400d1SChuck Lever server->mountd_protocol = data->mount_server.protocol; 8053f8400d1SChuck Lever 80654ceac45SDavid Howells server->namelen = data->namlen; 80754ceac45SDavid Howells dprintk("<-- nfs_init_server() = 0 [new %p]\n", clp); 80854ceac45SDavid Howells return 0; 80954ceac45SDavid Howells 81054ceac45SDavid Howells error: 81154ceac45SDavid Howells server->nfs_client = NULL; 81254ceac45SDavid Howells nfs_put_client(clp); 81354ceac45SDavid Howells dprintk("<-- nfs_init_server() = xerror %d\n", error); 81454ceac45SDavid Howells return error; 81554ceac45SDavid Howells } 81654ceac45SDavid Howells 81754ceac45SDavid Howells /* 81854ceac45SDavid Howells * Load up the server record from information gained in an fsinfo record 81954ceac45SDavid Howells */ 820738fd0f3SBenny Halevy static void nfs_server_set_fsinfo(struct nfs_server *server, 821738fd0f3SBenny Halevy struct nfs_fh *mntfh, 822738fd0f3SBenny Halevy struct nfs_fsinfo *fsinfo) 82354ceac45SDavid Howells { 82454ceac45SDavid Howells unsigned long max_rpc_payload; 82554ceac45SDavid Howells 82654ceac45SDavid Howells /* Work out a lot of parameters */ 82754ceac45SDavid Howells if (server->rsize == 0) 82854ceac45SDavid Howells server->rsize = nfs_block_size(fsinfo->rtpref, NULL); 82954ceac45SDavid Howells if (server->wsize == 0) 83054ceac45SDavid Howells server->wsize = nfs_block_size(fsinfo->wtpref, NULL); 83154ceac45SDavid Howells 83254ceac45SDavid Howells if (fsinfo->rtmax >= 512 && server->rsize > fsinfo->rtmax) 83354ceac45SDavid Howells server->rsize = nfs_block_size(fsinfo->rtmax, NULL); 83454ceac45SDavid Howells if (fsinfo->wtmax >= 512 && server->wsize > fsinfo->wtmax) 83554ceac45SDavid Howells server->wsize = nfs_block_size(fsinfo->wtmax, NULL); 83654ceac45SDavid Howells 83754ceac45SDavid Howells max_rpc_payload = nfs_block_size(rpc_max_payload(server->client), NULL); 83854ceac45SDavid Howells if (server->rsize > max_rpc_payload) 83954ceac45SDavid Howells server->rsize = max_rpc_payload; 84054ceac45SDavid Howells if (server->rsize > NFS_MAX_FILE_IO_SIZE) 84154ceac45SDavid Howells server->rsize = NFS_MAX_FILE_IO_SIZE; 84254ceac45SDavid Howells server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 843e0bf68ddSPeter Zijlstra 844d993831fSJens Axboe server->backing_dev_info.name = "nfs"; 84554ceac45SDavid Howells server->backing_dev_info.ra_pages = server->rpages * NFS_MAX_READAHEAD; 84654ceac45SDavid Howells 84754ceac45SDavid Howells if (server->wsize > max_rpc_payload) 84854ceac45SDavid Howells server->wsize = max_rpc_payload; 84954ceac45SDavid Howells if (server->wsize > NFS_MAX_FILE_IO_SIZE) 85054ceac45SDavid Howells server->wsize = NFS_MAX_FILE_IO_SIZE; 85154ceac45SDavid Howells server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 85285e174baSRicardo Labiaga 85354ceac45SDavid Howells server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL); 85454ceac45SDavid Howells 85554ceac45SDavid Howells server->dtsize = nfs_block_size(fsinfo->dtpref, NULL); 85656e4ebf8SBryan Schumaker if (server->dtsize > PAGE_CACHE_SIZE * NFS_MAX_READDIR_PAGES) 85756e4ebf8SBryan Schumaker server->dtsize = PAGE_CACHE_SIZE * NFS_MAX_READDIR_PAGES; 85854ceac45SDavid Howells if (server->dtsize > server->rsize) 85954ceac45SDavid Howells server->dtsize = server->rsize; 86054ceac45SDavid Howells 86154ceac45SDavid Howells if (server->flags & NFS_MOUNT_NOAC) { 86254ceac45SDavid Howells server->acregmin = server->acregmax = 0; 86354ceac45SDavid Howells server->acdirmin = server->acdirmax = 0; 86454ceac45SDavid Howells } 86554ceac45SDavid Howells 86654ceac45SDavid Howells server->maxfilesize = fsinfo->maxfilesize; 86754ceac45SDavid Howells 8686b96724eSRicardo Labiaga server->time_delta = fsinfo->time_delta; 8696b96724eSRicardo Labiaga 87054ceac45SDavid Howells /* We're airborne Set socket buffersize */ 87154ceac45SDavid Howells rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100); 87254ceac45SDavid Howells } 87354ceac45SDavid Howells 87454ceac45SDavid Howells /* 87554ceac45SDavid Howells * Probe filesystem information, including the FSID on v2/v3 87654ceac45SDavid Howells */ 877fcf10398SBryan Schumaker int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr) 87854ceac45SDavid Howells { 87954ceac45SDavid Howells struct nfs_fsinfo fsinfo; 88054ceac45SDavid Howells struct nfs_client *clp = server->nfs_client; 88154ceac45SDavid Howells int error; 88254ceac45SDavid Howells 88354ceac45SDavid Howells dprintk("--> nfs_probe_fsinfo()\n"); 88454ceac45SDavid Howells 88554ceac45SDavid Howells if (clp->rpc_ops->set_capabilities != NULL) { 88654ceac45SDavid Howells error = clp->rpc_ops->set_capabilities(server, mntfh); 88754ceac45SDavid Howells if (error < 0) 88854ceac45SDavid Howells goto out_error; 88954ceac45SDavid Howells } 89054ceac45SDavid Howells 89154ceac45SDavid Howells fsinfo.fattr = fattr; 89285e174baSRicardo Labiaga fsinfo.layouttype = 0; 89354ceac45SDavid Howells error = clp->rpc_ops->fsinfo(server, mntfh, &fsinfo); 89454ceac45SDavid Howells if (error < 0) 89554ceac45SDavid Howells goto out_error; 89654ceac45SDavid Howells 897738fd0f3SBenny Halevy nfs_server_set_fsinfo(server, mntfh, &fsinfo); 89854ceac45SDavid Howells 89954ceac45SDavid Howells /* Get some general file system info */ 90054ceac45SDavid Howells if (server->namelen == 0) { 90154ceac45SDavid Howells struct nfs_pathconf pathinfo; 90254ceac45SDavid Howells 90354ceac45SDavid Howells pathinfo.fattr = fattr; 90454ceac45SDavid Howells nfs_fattr_init(fattr); 90554ceac45SDavid Howells 90654ceac45SDavid Howells if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0) 90754ceac45SDavid Howells server->namelen = pathinfo.max_namelen; 90854ceac45SDavid Howells } 90954ceac45SDavid Howells 91054ceac45SDavid Howells dprintk("<-- nfs_probe_fsinfo() = 0\n"); 91154ceac45SDavid Howells return 0; 91254ceac45SDavid Howells 91354ceac45SDavid Howells out_error: 91454ceac45SDavid Howells dprintk("nfs_probe_fsinfo: error = %d\n", -error); 91554ceac45SDavid Howells return error; 91654ceac45SDavid Howells } 91789d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_probe_fsinfo); 91854ceac45SDavid Howells 91954ceac45SDavid Howells /* 92054ceac45SDavid Howells * Copy useful information when duplicating a server record 92154ceac45SDavid Howells */ 922fcf10398SBryan Schumaker void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source) 92354ceac45SDavid Howells { 92454ceac45SDavid Howells target->flags = source->flags; 925356e76b8SChuck Lever target->rsize = source->rsize; 926356e76b8SChuck Lever target->wsize = source->wsize; 92754ceac45SDavid Howells target->acregmin = source->acregmin; 92854ceac45SDavid Howells target->acregmax = source->acregmax; 92954ceac45SDavid Howells target->acdirmin = source->acdirmin; 93054ceac45SDavid Howells target->acdirmax = source->acdirmax; 93154ceac45SDavid Howells target->caps = source->caps; 9322df54806SDavid Howells target->options = source->options; 9330f5f49b8SWeston Andros Adamson target->auth_info = source->auth_info; 93454ceac45SDavid Howells } 93589d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_server_copy_userdata); 93654ceac45SDavid Howells 937fcf10398SBryan Schumaker void nfs_server_insert_lists(struct nfs_server *server) 938fca5238eSChuck Lever { 939fca5238eSChuck Lever struct nfs_client *clp = server->nfs_client; 94073ea666cSChuck Lever struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); 941fca5238eSChuck Lever 942dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 943fca5238eSChuck Lever list_add_tail_rcu(&server->client_link, &clp->cl_superblocks); 944c25d32b2SStanislav Kinsbursky list_add_tail(&server->master_link, &nn->nfs_volume_list); 945d3b4c9d7SAndy Adamson clear_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state); 946dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 947fca5238eSChuck Lever 948fca5238eSChuck Lever } 94989d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_server_insert_lists); 950fca5238eSChuck Lever 95132e62b7cSChuck Lever void nfs_server_remove_lists(struct nfs_server *server) 952fca5238eSChuck Lever { 953d3b4c9d7SAndy Adamson struct nfs_client *clp = server->nfs_client; 9544c03ae4aSTrond Myklebust struct nfs_net *nn; 955d3b4c9d7SAndy Adamson 9564c03ae4aSTrond Myklebust if (clp == NULL) 9574c03ae4aSTrond Myklebust return; 95873ea666cSChuck Lever nn = net_generic(clp->cl_net, nfs_net_id); 959dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 960fca5238eSChuck Lever list_del_rcu(&server->client_link); 9614c03ae4aSTrond Myklebust if (list_empty(&clp->cl_superblocks)) 962d3b4c9d7SAndy Adamson set_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state); 963fca5238eSChuck Lever list_del(&server->master_link); 964dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 965fca5238eSChuck Lever 966fca5238eSChuck Lever synchronize_rcu(); 967fca5238eSChuck Lever } 96832e62b7cSChuck Lever EXPORT_SYMBOL_GPL(nfs_server_remove_lists); 969fca5238eSChuck Lever 97054ceac45SDavid Howells /* 97154ceac45SDavid Howells * Allocate and initialise a server record 97254ceac45SDavid Howells */ 973fcf10398SBryan Schumaker struct nfs_server *nfs_alloc_server(void) 97454ceac45SDavid Howells { 97554ceac45SDavid Howells struct nfs_server *server; 97654ceac45SDavid Howells 97754ceac45SDavid Howells server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL); 97854ceac45SDavid Howells if (!server) 97954ceac45SDavid Howells return NULL; 98054ceac45SDavid Howells 98154ceac45SDavid Howells server->client = server->client_acl = ERR_PTR(-EINVAL); 98254ceac45SDavid Howells 98354ceac45SDavid Howells /* Zero out the NFS state stuff */ 98454ceac45SDavid Howells INIT_LIST_HEAD(&server->client_link); 98554ceac45SDavid Howells INIT_LIST_HEAD(&server->master_link); 986d3978bb3SChuck Lever INIT_LIST_HEAD(&server->delegations); 9876382a441SWeston Andros Adamson INIT_LIST_HEAD(&server->layouts); 9880aaaf5c4SChuck Lever INIT_LIST_HEAD(&server->state_owners_lru); 98954ceac45SDavid Howells 990ef818a28SSteve Dickson atomic_set(&server->active, 0); 991ef818a28SSteve Dickson 99254ceac45SDavid Howells server->io_stats = nfs_alloc_iostats(); 99354ceac45SDavid Howells if (!server->io_stats) { 99454ceac45SDavid Howells kfree(server); 99554ceac45SDavid Howells return NULL; 99654ceac45SDavid Howells } 99754ceac45SDavid Howells 99848d07649SJens Axboe if (bdi_init(&server->backing_dev_info)) { 99948d07649SJens Axboe nfs_free_iostats(server->io_stats); 100048d07649SJens Axboe kfree(server); 100148d07649SJens Axboe return NULL; 100248d07649SJens Axboe } 100348d07649SJens Axboe 10049157c31dSTrond Myklebust ida_init(&server->openowner_id); 1005d2d7ce28STrond Myklebust ida_init(&server->lockowner_id); 1006f7e8917aSFred Isaman pnfs_init_server(server); 1007f7e8917aSFred Isaman 100854ceac45SDavid Howells return server; 100954ceac45SDavid Howells } 101089d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_alloc_server); 101154ceac45SDavid Howells 101254ceac45SDavid Howells /* 101354ceac45SDavid Howells * Free up a server record 101454ceac45SDavid Howells */ 101554ceac45SDavid Howells void nfs_free_server(struct nfs_server *server) 101654ceac45SDavid Howells { 101754ceac45SDavid Howells dprintk("--> nfs_free_server()\n"); 101854ceac45SDavid Howells 1019fca5238eSChuck Lever nfs_server_remove_lists(server); 102054ceac45SDavid Howells 102154ceac45SDavid Howells if (server->destroy != NULL) 102254ceac45SDavid Howells server->destroy(server); 10235cef338bSTrond Myklebust 10245cef338bSTrond Myklebust if (!IS_ERR(server->client_acl)) 10255cef338bSTrond Myklebust rpc_shutdown_client(server->client_acl); 102654ceac45SDavid Howells if (!IS_ERR(server->client)) 102754ceac45SDavid Howells rpc_shutdown_client(server->client); 102854ceac45SDavid Howells 102954ceac45SDavid Howells nfs_put_client(server->nfs_client); 103054ceac45SDavid Howells 1031d2d7ce28STrond Myklebust ida_destroy(&server->lockowner_id); 10329157c31dSTrond Myklebust ida_destroy(&server->openowner_id); 103354ceac45SDavid Howells nfs_free_iostats(server->io_stats); 1034e0bf68ddSPeter Zijlstra bdi_destroy(&server->backing_dev_info); 103554ceac45SDavid Howells kfree(server); 103654ceac45SDavid Howells nfs_release_automount_timer(); 103754ceac45SDavid Howells dprintk("<-- nfs_free_server()\n"); 103854ceac45SDavid Howells } 103989d77c8fSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_free_server); 104054ceac45SDavid Howells 104154ceac45SDavid Howells /* 104254ceac45SDavid Howells * Create a version 2 or 3 volume record 104354ceac45SDavid Howells * - keyed on server and FSID 104454ceac45SDavid Howells */ 10451179acc6SBryan Schumaker struct nfs_server *nfs_create_server(struct nfs_mount_info *mount_info, 1046ab7017a3SBryan Schumaker struct nfs_subversion *nfs_mod) 104754ceac45SDavid Howells { 104854ceac45SDavid Howells struct nfs_server *server; 1049fbca779aSTrond Myklebust struct nfs_fattr *fattr; 105054ceac45SDavid Howells int error; 105154ceac45SDavid Howells 105254ceac45SDavid Howells server = nfs_alloc_server(); 105354ceac45SDavid Howells if (!server) 105454ceac45SDavid Howells return ERR_PTR(-ENOMEM); 105554ceac45SDavid Howells 1056fbca779aSTrond Myklebust error = -ENOMEM; 1057fbca779aSTrond Myklebust fattr = nfs_alloc_fattr(); 1058fbca779aSTrond Myklebust if (fattr == NULL) 1059fbca779aSTrond Myklebust goto error; 1060fbca779aSTrond Myklebust 106154ceac45SDavid Howells /* Get a client representation */ 10621179acc6SBryan Schumaker error = nfs_init_server(server, mount_info->parsed, nfs_mod); 106354ceac45SDavid Howells if (error < 0) 106454ceac45SDavid Howells goto error; 106554ceac45SDavid Howells 106654ceac45SDavid Howells /* Probe the root fh to retrieve its FSID */ 10671179acc6SBryan Schumaker error = nfs_probe_fsinfo(server, mount_info->mntfh, fattr); 106854ceac45SDavid Howells if (error < 0) 106954ceac45SDavid Howells goto error; 107054af3bb5STrond Myklebust if (server->nfs_client->rpc_ops->version == 3) { 107154af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) 107254af3bb5STrond Myklebust server->namelen = NFS3_MAXNAMLEN; 10731179acc6SBryan Schumaker if (!(mount_info->parsed->flags & NFS_MOUNT_NORDIRPLUS)) 107454af3bb5STrond Myklebust server->caps |= NFS_CAP_READDIRPLUS; 107554af3bb5STrond Myklebust } else { 107654af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN) 107754af3bb5STrond Myklebust server->namelen = NFS2_MAXNAMLEN; 107854af3bb5STrond Myklebust } 107954af3bb5STrond Myklebust 1080fbca779aSTrond Myklebust if (!(fattr->valid & NFS_ATTR_FATTR)) { 10811775fd3eSDavid Quigley error = nfs_mod->rpc_ops->getattr(server, mount_info->mntfh, fattr, NULL); 108254ceac45SDavid Howells if (error < 0) { 108354ceac45SDavid Howells dprintk("nfs_create_server: getattr error = %d\n", -error); 108454ceac45SDavid Howells goto error; 108554ceac45SDavid Howells } 108654ceac45SDavid Howells } 1087fbca779aSTrond Myklebust memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid)); 108854ceac45SDavid Howells 10896daabf1bSDavid Howells dprintk("Server FSID: %llx:%llx\n", 10906daabf1bSDavid Howells (unsigned long long) server->fsid.major, 10916daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 109254ceac45SDavid Howells 1093fca5238eSChuck Lever nfs_server_insert_lists(server); 109454ceac45SDavid Howells server->mount_time = jiffies; 1095fbca779aSTrond Myklebust nfs_free_fattr(fattr); 109654ceac45SDavid Howells return server; 109754ceac45SDavid Howells 109854ceac45SDavid Howells error: 1099fbca779aSTrond Myklebust nfs_free_fattr(fattr); 110054ceac45SDavid Howells nfs_free_server(server); 110154ceac45SDavid Howells return ERR_PTR(error); 110254ceac45SDavid Howells } 1103ddda8e0aSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_create_server); 110454ceac45SDavid Howells 110554ceac45SDavid Howells /* 110654ceac45SDavid Howells * Clone an NFS2, NFS3 or NFS4 server record 110754ceac45SDavid Howells */ 110854ceac45SDavid Howells struct nfs_server *nfs_clone_server(struct nfs_server *source, 110954ceac45SDavid Howells struct nfs_fh *fh, 11107e6eb683SBryan Schumaker struct nfs_fattr *fattr, 11117e6eb683SBryan Schumaker rpc_authflavor_t flavor) 111254ceac45SDavid Howells { 111354ceac45SDavid Howells struct nfs_server *server; 1114fbca779aSTrond Myklebust struct nfs_fattr *fattr_fsinfo; 111554ceac45SDavid Howells int error; 111654ceac45SDavid Howells 111754ceac45SDavid Howells dprintk("--> nfs_clone_server(,%llx:%llx,)\n", 11186daabf1bSDavid Howells (unsigned long long) fattr->fsid.major, 11196daabf1bSDavid Howells (unsigned long long) fattr->fsid.minor); 112054ceac45SDavid Howells 112154ceac45SDavid Howells server = nfs_alloc_server(); 112254ceac45SDavid Howells if (!server) 112354ceac45SDavid Howells return ERR_PTR(-ENOMEM); 112454ceac45SDavid Howells 1125fbca779aSTrond Myklebust error = -ENOMEM; 1126fbca779aSTrond Myklebust fattr_fsinfo = nfs_alloc_fattr(); 1127fbca779aSTrond Myklebust if (fattr_fsinfo == NULL) 1128fbca779aSTrond Myklebust goto out_free_server; 1129fbca779aSTrond Myklebust 113054ceac45SDavid Howells /* Copy data from the source */ 113154ceac45SDavid Howells server->nfs_client = source->nfs_client; 11320aaaf5c4SChuck Lever server->destroy = source->destroy; 113354ceac45SDavid Howells atomic_inc(&server->nfs_client->cl_count); 113454ceac45SDavid Howells nfs_server_copy_userdata(server, source); 113554ceac45SDavid Howells 113654ceac45SDavid Howells server->fsid = fattr->fsid; 113754ceac45SDavid Howells 113833170233STrond Myklebust error = nfs_init_server_rpcclient(server, 113933170233STrond Myklebust source->client->cl_timeout, 11407e6eb683SBryan Schumaker flavor); 114154ceac45SDavid Howells if (error < 0) 114254ceac45SDavid Howells goto out_free_server; 114354ceac45SDavid Howells 114454ceac45SDavid Howells /* probe the filesystem info for this server filesystem */ 1145fbca779aSTrond Myklebust error = nfs_probe_fsinfo(server, fh, fattr_fsinfo); 114654ceac45SDavid Howells if (error < 0) 114754ceac45SDavid Howells goto out_free_server; 114854ceac45SDavid Howells 114954af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 115054af3bb5STrond Myklebust server->namelen = NFS4_MAXNAMLEN; 115154af3bb5STrond Myklebust 115254ceac45SDavid Howells dprintk("Cloned FSID: %llx:%llx\n", 11536daabf1bSDavid Howells (unsigned long long) server->fsid.major, 11546daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 115554ceac45SDavid Howells 115654ceac45SDavid Howells error = nfs_start_lockd(server); 115754ceac45SDavid Howells if (error < 0) 115854ceac45SDavid Howells goto out_free_server; 115954ceac45SDavid Howells 1160fca5238eSChuck Lever nfs_server_insert_lists(server); 116154ceac45SDavid Howells server->mount_time = jiffies; 116254ceac45SDavid Howells 1163fbca779aSTrond Myklebust nfs_free_fattr(fattr_fsinfo); 116454ceac45SDavid Howells dprintk("<-- nfs_clone_server() = %p\n", server); 116554ceac45SDavid Howells return server; 116654ceac45SDavid Howells 116754ceac45SDavid Howells out_free_server: 1168fbca779aSTrond Myklebust nfs_free_fattr(fattr_fsinfo); 116954ceac45SDavid Howells nfs_free_server(server); 117054ceac45SDavid Howells dprintk("<-- nfs_clone_server() = error %d\n", error); 117154ceac45SDavid Howells return ERR_PTR(error); 117254ceac45SDavid Howells } 1173ddda8e0aSBryan Schumaker EXPORT_SYMBOL_GPL(nfs_clone_server); 11746aaca566SDavid Howells 11756b13168bSStanislav Kinsbursky void nfs_clients_init(struct net *net) 11766b13168bSStanislav Kinsbursky { 11776b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(net, nfs_net_id); 11786b13168bSStanislav Kinsbursky 11796b13168bSStanislav Kinsbursky INIT_LIST_HEAD(&nn->nfs_client_list); 1180c25d32b2SStanislav Kinsbursky INIT_LIST_HEAD(&nn->nfs_volume_list); 118189d77c8fSBryan Schumaker #if IS_ENABLED(CONFIG_NFS_V4) 118228cd1b3fSStanislav Kinsbursky idr_init(&nn->cb_ident_idr); 118328cd1b3fSStanislav Kinsbursky #endif 11844c03ae4aSTrond Myklebust spin_lock_init(&nn->nfs_client_lock); 1185f092075dSChuck Lever nn->boot_time = CURRENT_TIME; 11866b13168bSStanislav Kinsbursky } 11876b13168bSStanislav Kinsbursky 11886aaca566SDavid Howells #ifdef CONFIG_PROC_FS 11896aaca566SDavid Howells static struct proc_dir_entry *proc_fs_nfs; 11906aaca566SDavid Howells 11916aaca566SDavid Howells static int nfs_server_list_open(struct inode *inode, struct file *file); 11926aaca566SDavid Howells static void *nfs_server_list_start(struct seq_file *p, loff_t *pos); 11936aaca566SDavid Howells static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos); 11946aaca566SDavid Howells static void nfs_server_list_stop(struct seq_file *p, void *v); 11956aaca566SDavid Howells static int nfs_server_list_show(struct seq_file *m, void *v); 11966aaca566SDavid Howells 119788e9d34cSJames Morris static const struct seq_operations nfs_server_list_ops = { 11986aaca566SDavid Howells .start = nfs_server_list_start, 11996aaca566SDavid Howells .next = nfs_server_list_next, 12006aaca566SDavid Howells .stop = nfs_server_list_stop, 12016aaca566SDavid Howells .show = nfs_server_list_show, 12026aaca566SDavid Howells }; 12036aaca566SDavid Howells 120400977a59SArjan van de Ven static const struct file_operations nfs_server_list_fops = { 12056aaca566SDavid Howells .open = nfs_server_list_open, 12066aaca566SDavid Howells .read = seq_read, 12076aaca566SDavid Howells .llseek = seq_lseek, 1208*65b38851SEric W. Biederman .release = seq_release_net, 120934b37235SDenis V. Lunev .owner = THIS_MODULE, 12106aaca566SDavid Howells }; 12116aaca566SDavid Howells 12126aaca566SDavid Howells static int nfs_volume_list_open(struct inode *inode, struct file *file); 12136aaca566SDavid Howells static void *nfs_volume_list_start(struct seq_file *p, loff_t *pos); 12146aaca566SDavid Howells static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos); 12156aaca566SDavid Howells static void nfs_volume_list_stop(struct seq_file *p, void *v); 12166aaca566SDavid Howells static int nfs_volume_list_show(struct seq_file *m, void *v); 12176aaca566SDavid Howells 121888e9d34cSJames Morris static const struct seq_operations nfs_volume_list_ops = { 12196aaca566SDavid Howells .start = nfs_volume_list_start, 12206aaca566SDavid Howells .next = nfs_volume_list_next, 12216aaca566SDavid Howells .stop = nfs_volume_list_stop, 12226aaca566SDavid Howells .show = nfs_volume_list_show, 12236aaca566SDavid Howells }; 12246aaca566SDavid Howells 122500977a59SArjan van de Ven static const struct file_operations nfs_volume_list_fops = { 12266aaca566SDavid Howells .open = nfs_volume_list_open, 12276aaca566SDavid Howells .read = seq_read, 12286aaca566SDavid Howells .llseek = seq_lseek, 1229*65b38851SEric W. Biederman .release = seq_release_net, 123034b37235SDenis V. Lunev .owner = THIS_MODULE, 12316aaca566SDavid Howells }; 12326aaca566SDavid Howells 12336aaca566SDavid Howells /* 12346aaca566SDavid Howells * open "/proc/fs/nfsfs/servers" which provides a summary of servers with which 12356aaca566SDavid Howells * we're dealing 12366aaca566SDavid Howells */ 12376aaca566SDavid Howells static int nfs_server_list_open(struct inode *inode, struct file *file) 12386aaca566SDavid Howells { 1239*65b38851SEric W. Biederman return seq_open_net(inode, file, &nfs_server_list_ops, 1240*65b38851SEric W. Biederman sizeof(struct seq_net_private)); 12416aaca566SDavid Howells } 12426aaca566SDavid Howells 12436aaca566SDavid Howells /* 12446aaca566SDavid Howells * set up the iterator to start reading from the server list and return the first item 12456aaca566SDavid Howells */ 12466aaca566SDavid Howells static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos) 12476aaca566SDavid Howells { 1248*65b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); 12496b13168bSStanislav Kinsbursky 12506aaca566SDavid Howells /* lock the list against modification */ 1251dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 12526b13168bSStanislav Kinsbursky return seq_list_start_head(&nn->nfs_client_list, *_pos); 12536aaca566SDavid Howells } 12546aaca566SDavid Howells 12556aaca566SDavid Howells /* 12566aaca566SDavid Howells * move to next server 12576aaca566SDavid Howells */ 12586aaca566SDavid Howells static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos) 12596aaca566SDavid Howells { 1260*65b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); 12616b13168bSStanislav Kinsbursky 12626b13168bSStanislav Kinsbursky return seq_list_next(v, &nn->nfs_client_list, pos); 12636aaca566SDavid Howells } 12646aaca566SDavid Howells 12656aaca566SDavid Howells /* 12666aaca566SDavid Howells * clean up after reading from the transports list 12676aaca566SDavid Howells */ 12686aaca566SDavid Howells static void nfs_server_list_stop(struct seq_file *p, void *v) 12696aaca566SDavid Howells { 1270*65b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); 1271dc030858SStanislav Kinsbursky 1272dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 12736aaca566SDavid Howells } 12746aaca566SDavid Howells 12756aaca566SDavid Howells /* 12766aaca566SDavid Howells * display a header line followed by a load of call lines 12776aaca566SDavid Howells */ 12786aaca566SDavid Howells static int nfs_server_list_show(struct seq_file *m, void *v) 12796aaca566SDavid Howells { 12806aaca566SDavid Howells struct nfs_client *clp; 1281*65b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); 12826aaca566SDavid Howells 12836aaca566SDavid Howells /* display header on line 1 */ 12846b13168bSStanislav Kinsbursky if (v == &nn->nfs_client_list) { 12856aaca566SDavid Howells seq_puts(m, "NV SERVER PORT USE HOSTNAME\n"); 12866aaca566SDavid Howells return 0; 12876aaca566SDavid Howells } 12886aaca566SDavid Howells 12896aaca566SDavid Howells /* display one transport per line on subsequent lines */ 12906aaca566SDavid Howells clp = list_entry(v, struct nfs_client, cl_share_link); 12916aaca566SDavid Howells 1292940aab49SMalahal Naineni /* Check if the client is initialized */ 1293940aab49SMalahal Naineni if (clp->cl_cons_state != NFS_CS_READY) 1294940aab49SMalahal Naineni return 0; 1295940aab49SMalahal Naineni 12962446ab60STrond Myklebust rcu_read_lock(); 12975d8515caSChuck Lever seq_printf(m, "v%u %s %s %3d %s\n", 129840c55319STrond Myklebust clp->rpc_ops->version, 12995d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), 13005d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), 13016aaca566SDavid Howells atomic_read(&clp->cl_count), 13026aaca566SDavid Howells clp->cl_hostname); 13032446ab60STrond Myklebust rcu_read_unlock(); 13046aaca566SDavid Howells 13056aaca566SDavid Howells return 0; 13066aaca566SDavid Howells } 13076aaca566SDavid Howells 13086aaca566SDavid Howells /* 13096aaca566SDavid Howells * open "/proc/fs/nfsfs/volumes" which provides a summary of extant volumes 13106aaca566SDavid Howells */ 13116aaca566SDavid Howells static int nfs_volume_list_open(struct inode *inode, struct file *file) 13126aaca566SDavid Howells { 1313*65b38851SEric W. Biederman return seq_open_net(inode, file, &nfs_server_list_ops, 1314*65b38851SEric W. Biederman sizeof(struct seq_net_private)); 13156aaca566SDavid Howells } 13166aaca566SDavid Howells 13176aaca566SDavid Howells /* 13186aaca566SDavid Howells * set up the iterator to start reading from the volume list and return the first item 13196aaca566SDavid Howells */ 13206aaca566SDavid Howells static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos) 13216aaca566SDavid Howells { 1322*65b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); 1323c25d32b2SStanislav Kinsbursky 13246aaca566SDavid Howells /* lock the list against modification */ 1325dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 1326c25d32b2SStanislav Kinsbursky return seq_list_start_head(&nn->nfs_volume_list, *_pos); 13276aaca566SDavid Howells } 13286aaca566SDavid Howells 13296aaca566SDavid Howells /* 13306aaca566SDavid Howells * move to next volume 13316aaca566SDavid Howells */ 13326aaca566SDavid Howells static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos) 13336aaca566SDavid Howells { 1334*65b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); 1335c25d32b2SStanislav Kinsbursky 1336c25d32b2SStanislav Kinsbursky return seq_list_next(v, &nn->nfs_volume_list, pos); 13376aaca566SDavid Howells } 13386aaca566SDavid Howells 13396aaca566SDavid Howells /* 13406aaca566SDavid Howells * clean up after reading from the transports list 13416aaca566SDavid Howells */ 13426aaca566SDavid Howells static void nfs_volume_list_stop(struct seq_file *p, void *v) 13436aaca566SDavid Howells { 1344*65b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); 1345dc030858SStanislav Kinsbursky 1346dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 13476aaca566SDavid Howells } 13486aaca566SDavid Howells 13496aaca566SDavid Howells /* 13506aaca566SDavid Howells * display a header line followed by a load of call lines 13516aaca566SDavid Howells */ 13526aaca566SDavid Howells static int nfs_volume_list_show(struct seq_file *m, void *v) 13536aaca566SDavid Howells { 13546aaca566SDavid Howells struct nfs_server *server; 13556aaca566SDavid Howells struct nfs_client *clp; 13566aaca566SDavid Howells char dev[8], fsid[17]; 1357*65b38851SEric W. Biederman struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); 13586aaca566SDavid Howells 13596aaca566SDavid Howells /* display header on line 1 */ 1360c25d32b2SStanislav Kinsbursky if (v == &nn->nfs_volume_list) { 13615d1acff1SDavid Howells seq_puts(m, "NV SERVER PORT DEV FSID FSC\n"); 13626aaca566SDavid Howells return 0; 13636aaca566SDavid Howells } 13646aaca566SDavid Howells /* display one transport per line on subsequent lines */ 13656aaca566SDavid Howells server = list_entry(v, struct nfs_server, master_link); 13666aaca566SDavid Howells clp = server->nfs_client; 13676aaca566SDavid Howells 13686aaca566SDavid Howells snprintf(dev, 8, "%u:%u", 13696aaca566SDavid Howells MAJOR(server->s_dev), MINOR(server->s_dev)); 13706aaca566SDavid Howells 13716aaca566SDavid Howells snprintf(fsid, 17, "%llx:%llx", 13726daabf1bSDavid Howells (unsigned long long) server->fsid.major, 13736daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 13746aaca566SDavid Howells 13752446ab60STrond Myklebust rcu_read_lock(); 13765d1acff1SDavid Howells seq_printf(m, "v%u %s %s %-7s %-17s %s\n", 137740c55319STrond Myklebust clp->rpc_ops->version, 13785d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), 13795d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), 13806aaca566SDavid Howells dev, 13815d1acff1SDavid Howells fsid, 13825d1acff1SDavid Howells nfs_server_fscache_state(server)); 13832446ab60STrond Myklebust rcu_read_unlock(); 13846aaca566SDavid Howells 13856aaca566SDavid Howells return 0; 13866aaca566SDavid Howells } 13876aaca566SDavid Howells 1388*65b38851SEric W. Biederman int nfs_fs_proc_net_init(struct net *net) 1389*65b38851SEric W. Biederman { 1390*65b38851SEric W. Biederman struct nfs_net *nn = net_generic(net, nfs_net_id); 1391*65b38851SEric W. Biederman struct proc_dir_entry *p; 1392*65b38851SEric W. Biederman 1393*65b38851SEric W. Biederman nn->proc_nfsfs = proc_net_mkdir(net, "nfsfs", net->proc_net); 1394*65b38851SEric W. Biederman if (!nn->proc_nfsfs) 1395*65b38851SEric W. Biederman goto error_0; 1396*65b38851SEric W. Biederman 1397*65b38851SEric W. Biederman /* a file of servers with which we're dealing */ 1398*65b38851SEric W. Biederman p = proc_create("servers", S_IFREG|S_IRUGO, 1399*65b38851SEric W. Biederman nn->proc_nfsfs, &nfs_server_list_fops); 1400*65b38851SEric W. Biederman if (!p) 1401*65b38851SEric W. Biederman goto error_1; 1402*65b38851SEric W. Biederman 1403*65b38851SEric W. Biederman /* a file of volumes that we have mounted */ 1404*65b38851SEric W. Biederman p = proc_create("volumes", S_IFREG|S_IRUGO, 1405*65b38851SEric W. Biederman nn->proc_nfsfs, &nfs_volume_list_fops); 1406*65b38851SEric W. Biederman if (!p) 1407*65b38851SEric W. Biederman goto error_2; 1408*65b38851SEric W. Biederman return 0; 1409*65b38851SEric W. Biederman 1410*65b38851SEric W. Biederman error_2: 1411*65b38851SEric W. Biederman remove_proc_entry("servers", nn->proc_nfsfs); 1412*65b38851SEric W. Biederman error_1: 1413*65b38851SEric W. Biederman remove_proc_entry("fs/nfsfs", NULL); 1414*65b38851SEric W. Biederman error_0: 1415*65b38851SEric W. Biederman return -ENOMEM; 1416*65b38851SEric W. Biederman } 1417*65b38851SEric W. Biederman 1418*65b38851SEric W. Biederman void nfs_fs_proc_net_exit(struct net *net) 1419*65b38851SEric W. Biederman { 1420*65b38851SEric W. Biederman struct nfs_net *nn = net_generic(net, nfs_net_id); 1421*65b38851SEric W. Biederman 1422*65b38851SEric W. Biederman remove_proc_entry("volumes", nn->proc_nfsfs); 1423*65b38851SEric W. Biederman remove_proc_entry("servers", nn->proc_nfsfs); 1424*65b38851SEric W. Biederman remove_proc_entry("fs/nfsfs", NULL); 1425*65b38851SEric W. Biederman } 1426*65b38851SEric W. Biederman 14276aaca566SDavid Howells /* 14286aaca566SDavid Howells * initialise the /proc/fs/nfsfs/ directory 14296aaca566SDavid Howells */ 14306aaca566SDavid Howells int __init nfs_fs_proc_init(void) 14316aaca566SDavid Howells { 14326aaca566SDavid Howells struct proc_dir_entry *p; 14336aaca566SDavid Howells 143436a5aeb8SAlexey Dobriyan proc_fs_nfs = proc_mkdir("fs/nfsfs", NULL); 14356aaca566SDavid Howells if (!proc_fs_nfs) 14366aaca566SDavid Howells goto error_0; 14376aaca566SDavid Howells 14386aaca566SDavid Howells /* a file of servers with which we're dealing */ 1439*65b38851SEric W. Biederman p = proc_symlink("servers", proc_fs_nfs, "../../net/nfsfs/servers"); 14406aaca566SDavid Howells if (!p) 14416aaca566SDavid Howells goto error_1; 14426aaca566SDavid Howells 14436aaca566SDavid Howells /* a file of volumes that we have mounted */ 1444*65b38851SEric W. Biederman p = proc_symlink("volumes", proc_fs_nfs, "../../net/nfsfs/volumes"); 14456aaca566SDavid Howells if (!p) 14466aaca566SDavid Howells goto error_2; 14476aaca566SDavid Howells return 0; 14486aaca566SDavid Howells 14496aaca566SDavid Howells error_2: 14506aaca566SDavid Howells remove_proc_entry("servers", proc_fs_nfs); 14516aaca566SDavid Howells error_1: 145236a5aeb8SAlexey Dobriyan remove_proc_entry("fs/nfsfs", NULL); 14536aaca566SDavid Howells error_0: 14546aaca566SDavid Howells return -ENOMEM; 14556aaca566SDavid Howells } 14566aaca566SDavid Howells 14576aaca566SDavid Howells /* 14586aaca566SDavid Howells * clean up the /proc/fs/nfsfs/ directory 14596aaca566SDavid Howells */ 14606aaca566SDavid Howells void nfs_fs_proc_exit(void) 14616aaca566SDavid Howells { 14626aaca566SDavid Howells remove_proc_entry("volumes", proc_fs_nfs); 14636aaca566SDavid Howells remove_proc_entry("servers", proc_fs_nfs); 146436a5aeb8SAlexey Dobriyan remove_proc_entry("fs/nfsfs", NULL); 14656aaca566SDavid Howells } 14666aaca566SDavid Howells 14676aaca566SDavid Howells #endif /* CONFIG_PROC_FS */ 1468