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" 546b13168bSStanislav Kinsbursky #include "netns.h" 5524c8dbbbSDavid Howells 5624c8dbbbSDavid Howells #define NFSDBG_FACILITY NFSDBG_CLIENT 5724c8dbbbSDavid Howells 5824c8dbbbSDavid Howells static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq); 59f4eecd5dSAndy Adamson #ifdef CONFIG_NFS_V4 60f4eecd5dSAndy Adamson 61f4eecd5dSAndy Adamson /* 62f4eecd5dSAndy Adamson * Get a unique NFSv4.0 callback identifier which will be used 63f4eecd5dSAndy Adamson * by the V4.0 callback service to lookup the nfs_client struct 64f4eecd5dSAndy Adamson */ 65f4eecd5dSAndy Adamson static int nfs_get_cb_ident_idr(struct nfs_client *clp, int minorversion) 66f4eecd5dSAndy Adamson { 67f4eecd5dSAndy Adamson int ret = 0; 6828cd1b3fSStanislav Kinsbursky struct nfs_net *nn = net_generic(clp->net, nfs_net_id); 69f4eecd5dSAndy Adamson 70f4eecd5dSAndy Adamson if (clp->rpc_ops->version != 4 || minorversion != 0) 71f4eecd5dSAndy Adamson return ret; 72f4eecd5dSAndy Adamson retry: 7328cd1b3fSStanislav Kinsbursky if (!idr_pre_get(&nn->cb_ident_idr, GFP_KERNEL)) 74f4eecd5dSAndy Adamson return -ENOMEM; 75dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 7628cd1b3fSStanislav Kinsbursky ret = idr_get_new(&nn->cb_ident_idr, clp, &clp->cl_cb_ident); 77dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 78f4eecd5dSAndy Adamson if (ret == -EAGAIN) 79f4eecd5dSAndy Adamson goto retry; 80f4eecd5dSAndy Adamson return ret; 81f4eecd5dSAndy Adamson } 82f4eecd5dSAndy Adamson #endif /* CONFIG_NFS_V4 */ 8324c8dbbbSDavid Howells 8424c8dbbbSDavid Howells /* 85b064eca2STrond Myklebust * Turn off NFSv4 uid/gid mapping when using AUTH_SYS 86b064eca2STrond Myklebust */ 8790ab5ee9SRusty Russell static bool nfs4_disable_idmapping = true; 88b064eca2STrond Myklebust 89b064eca2STrond Myklebust /* 905006a76cSDavid Howells * RPC cruft for NFS 915006a76cSDavid Howells */ 92a613fa16STrond Myklebust static const struct rpc_version *nfs_version[5] = { 935006a76cSDavid Howells [2] = &nfs_version2, 945006a76cSDavid Howells #ifdef CONFIG_NFS_V3 955006a76cSDavid Howells [3] = &nfs_version3, 965006a76cSDavid Howells #endif 975006a76cSDavid Howells #ifdef CONFIG_NFS_V4 985006a76cSDavid Howells [4] = &nfs_version4, 995006a76cSDavid Howells #endif 1005006a76cSDavid Howells }; 1015006a76cSDavid Howells 102a613fa16STrond Myklebust const struct rpc_program nfs_program = { 1035006a76cSDavid Howells .name = "nfs", 1045006a76cSDavid Howells .number = NFS_PROGRAM, 1055006a76cSDavid Howells .nrvers = ARRAY_SIZE(nfs_version), 1065006a76cSDavid Howells .version = nfs_version, 1075006a76cSDavid Howells .stats = &nfs_rpcstat, 108fe0a9b74SJim Rees .pipe_dir_name = NFS_PIPE_DIRNAME, 1095006a76cSDavid Howells }; 1105006a76cSDavid Howells 1115006a76cSDavid Howells struct rpc_stat nfs_rpcstat = { 1125006a76cSDavid Howells .program = &nfs_program 1135006a76cSDavid Howells }; 1145006a76cSDavid Howells 1155006a76cSDavid Howells 1165006a76cSDavid Howells #ifdef CONFIG_NFS_V3_ACL 1175006a76cSDavid Howells static struct rpc_stat nfsacl_rpcstat = { &nfsacl_program }; 118a613fa16STrond Myklebust static const struct rpc_version *nfsacl_version[] = { 1195006a76cSDavid Howells [3] = &nfsacl_version3, 1205006a76cSDavid Howells }; 1215006a76cSDavid Howells 122a613fa16STrond Myklebust const struct rpc_program nfsacl_program = { 1235006a76cSDavid Howells .name = "nfsacl", 1245006a76cSDavid Howells .number = NFS_ACL_PROGRAM, 1255006a76cSDavid Howells .nrvers = ARRAY_SIZE(nfsacl_version), 1265006a76cSDavid Howells .version = nfsacl_version, 1275006a76cSDavid Howells .stats = &nfsacl_rpcstat, 1285006a76cSDavid Howells }; 1295006a76cSDavid Howells #endif /* CONFIG_NFS_V3_ACL */ 1305006a76cSDavid Howells 1313a498026STrond Myklebust struct nfs_client_initdata { 1323a498026STrond Myklebust const char *hostname; 133d7422c47SChuck Lever const struct sockaddr *addr; 1346e4cffd7SChuck Lever size_t addrlen; 13540c55319STrond Myklebust const struct nfs_rpc_ops *rpc_ops; 13659dca3b2STrond Myklebust int proto; 1375aae4a9aSBenny Halevy u32 minorversion; 138e50a7a1aSStanislav Kinsbursky struct net *net; 1393a498026STrond Myklebust }; 1403a498026STrond Myklebust 1415006a76cSDavid Howells /* 14224c8dbbbSDavid Howells * Allocate a shared client record 14324c8dbbbSDavid Howells * 14424c8dbbbSDavid Howells * Since these are allocated/deallocated very rarely, we don't 14524c8dbbbSDavid Howells * bother putting them in a slab cache... 14624c8dbbbSDavid Howells */ 1473a498026STrond Myklebust static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) 14824c8dbbbSDavid Howells { 14924c8dbbbSDavid Howells struct nfs_client *clp; 1507c67db3aSTrond Myklebust struct rpc_cred *cred; 151a21bdd9bSChuck Lever int err = -ENOMEM; 15224c8dbbbSDavid Howells 15324c8dbbbSDavid Howells if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) 15424c8dbbbSDavid Howells goto error_0; 15524c8dbbbSDavid Howells 15640c55319STrond Myklebust clp->rpc_ops = cl_init->rpc_ops; 15740c55319STrond Myklebust 15824c8dbbbSDavid Howells atomic_set(&clp->cl_count, 1); 15924c8dbbbSDavid Howells clp->cl_cons_state = NFS_CS_INITING; 16024c8dbbbSDavid Howells 1616e4cffd7SChuck Lever memcpy(&clp->cl_addr, cl_init->addr, cl_init->addrlen); 1626e4cffd7SChuck Lever clp->cl_addrlen = cl_init->addrlen; 16324c8dbbbSDavid Howells 1643a498026STrond Myklebust if (cl_init->hostname) { 165a21bdd9bSChuck Lever err = -ENOMEM; 1663a498026STrond Myklebust clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL); 16724c8dbbbSDavid Howells if (!clp->cl_hostname) 16871468513SBenny Halevy goto error_cleanup; 16924c8dbbbSDavid Howells } 17024c8dbbbSDavid Howells 17124c8dbbbSDavid Howells INIT_LIST_HEAD(&clp->cl_superblocks); 17224c8dbbbSDavid Howells clp->cl_rpcclient = ERR_PTR(-EINVAL); 17324c8dbbbSDavid Howells 17459dca3b2STrond Myklebust clp->cl_proto = cl_init->proto; 175abd96698STrond Myklebust clp->net = get_net(cl_init->net); 17659dca3b2STrond Myklebust 17724c8dbbbSDavid Howells #ifdef CONFIG_NFS_V4 178f4eecd5dSAndy Adamson err = nfs_get_cb_ident_idr(clp, cl_init->minorversion); 179f4eecd5dSAndy Adamson if (err) 180f4eecd5dSAndy Adamson goto error_cleanup; 181f4eecd5dSAndy Adamson 18224c8dbbbSDavid Howells spin_lock_init(&clp->cl_lock); 18365f27f38SDavid Howells INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state); 18424c8dbbbSDavid Howells rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); 18524c8dbbbSDavid Howells clp->cl_boot_time = CURRENT_TIME; 18624c8dbbbSDavid Howells clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED; 1875aae4a9aSBenny Halevy clp->cl_minorversion = cl_init->minorversion; 18897dc1359STrond Myklebust clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion]; 18924c8dbbbSDavid Howells #endif 19068c97153STrond Myklebust cred = rpc_lookup_machine_cred("*"); 1917c67db3aSTrond Myklebust if (!IS_ERR(cred)) 1927c67db3aSTrond Myklebust clp->cl_machine_cred = cred; 19314727281SDavid Howells nfs_fscache_get_client_cookie(clp); 19414727281SDavid Howells 19524c8dbbbSDavid Howells return clp; 19624c8dbbbSDavid Howells 19771468513SBenny Halevy error_cleanup: 19824c8dbbbSDavid Howells kfree(clp); 19924c8dbbbSDavid Howells error_0: 200a21bdd9bSChuck Lever return ERR_PTR(err); 20124c8dbbbSDavid Howells } 20224c8dbbbSDavid Howells 20324c8dbbbSDavid Howells #ifdef CONFIG_NFS_V4 204557134a3SAndy Adamson #ifdef CONFIG_NFS_V4_1 205ea005281SAndy Adamson static void nfs4_shutdown_session(struct nfs_client *clp) 206ea005281SAndy Adamson { 2077df529afSTrond Myklebust if (nfs4_has_session(clp)) { 2087df529afSTrond Myklebust nfs4_deviceid_purge_client(clp); 209557134a3SAndy Adamson nfs4_destroy_session(clp->cl_session); 210557134a3SAndy Adamson } 2117df529afSTrond Myklebust 2127df529afSTrond Myklebust } 213ea005281SAndy Adamson #else /* CONFIG_NFS_V4_1 */ 214ea005281SAndy Adamson static void nfs4_shutdown_session(struct nfs_client *clp) 215ea005281SAndy Adamson { 216ea005281SAndy Adamson } 217557134a3SAndy Adamson #endif /* CONFIG_NFS_V4_1 */ 218557134a3SAndy Adamson 219557134a3SAndy Adamson /* 220888ef2e3SAlexandros Batsakis * Destroy the NFS4 callback service 221888ef2e3SAlexandros Batsakis */ 222888ef2e3SAlexandros Batsakis static void nfs4_destroy_callback(struct nfs_client *clp) 223888ef2e3SAlexandros Batsakis { 224888ef2e3SAlexandros Batsakis if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) 22597dc1359STrond Myklebust nfs_callback_down(clp->cl_mvops->minor_version); 226888ef2e3SAlexandros Batsakis } 227888ef2e3SAlexandros Batsakis 228888ef2e3SAlexandros Batsakis static void nfs4_shutdown_client(struct nfs_client *clp) 229888ef2e3SAlexandros Batsakis { 230888ef2e3SAlexandros Batsakis if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state)) 231888ef2e3SAlexandros Batsakis nfs4_kill_renewd(clp); 232ea005281SAndy Adamson nfs4_shutdown_session(clp); 233888ef2e3SAlexandros Batsakis nfs4_destroy_callback(clp); 234888ef2e3SAlexandros Batsakis if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state)) 235888ef2e3SAlexandros Batsakis nfs_idmap_delete(clp); 236888ef2e3SAlexandros Batsakis 237888ef2e3SAlexandros Batsakis rpc_destroy_wait_queue(&clp->cl_rpcwaitq); 238888ef2e3SAlexandros Batsakis } 239f4eecd5dSAndy Adamson 240f4eecd5dSAndy Adamson /* idr_remove_all is not needed as all id's are removed by nfs_put_client */ 24128cd1b3fSStanislav Kinsbursky void nfs_cleanup_cb_ident_idr(struct net *net) 242f4eecd5dSAndy Adamson { 24328cd1b3fSStanislav Kinsbursky struct nfs_net *nn = net_generic(net, nfs_net_id); 24428cd1b3fSStanislav Kinsbursky 24528cd1b3fSStanislav Kinsbursky idr_destroy(&nn->cb_ident_idr); 246f4eecd5dSAndy Adamson } 247f4eecd5dSAndy Adamson 248f4eecd5dSAndy Adamson /* nfs_client_lock held */ 249f4eecd5dSAndy Adamson static void nfs_cb_idr_remove_locked(struct nfs_client *clp) 250f4eecd5dSAndy Adamson { 25128cd1b3fSStanislav Kinsbursky struct nfs_net *nn = net_generic(clp->net, nfs_net_id); 25228cd1b3fSStanislav Kinsbursky 253f4eecd5dSAndy Adamson if (clp->cl_cb_ident) 25428cd1b3fSStanislav Kinsbursky idr_remove(&nn->cb_ident_idr, clp->cl_cb_ident); 255f4eecd5dSAndy Adamson } 256f4eecd5dSAndy Adamson 257f7e8917aSFred Isaman static void pnfs_init_server(struct nfs_server *server) 258f7e8917aSFred Isaman { 259f7e8917aSFred Isaman rpc_init_wait_queue(&server->roc_rpcwaitq, "pNFS ROC"); 260f7e8917aSFred Isaman } 261f7e8917aSFred Isaman 2620aaaf5c4SChuck Lever static void nfs4_destroy_server(struct nfs_server *server) 2630aaaf5c4SChuck Lever { 2640aaaf5c4SChuck Lever nfs4_purge_state_owners(server); 2650aaaf5c4SChuck Lever } 2660aaaf5c4SChuck Lever 267888ef2e3SAlexandros Batsakis #else 268888ef2e3SAlexandros Batsakis static void nfs4_shutdown_client(struct nfs_client *clp) 269888ef2e3SAlexandros Batsakis { 270888ef2e3SAlexandros Batsakis } 271f4eecd5dSAndy Adamson 27228cd1b3fSStanislav Kinsbursky void nfs_cleanup_cb_ident_idr(struct net *net) 273f4eecd5dSAndy Adamson { 274f4eecd5dSAndy Adamson } 275f4eecd5dSAndy Adamson 276f4eecd5dSAndy Adamson static void nfs_cb_idr_remove_locked(struct nfs_client *clp) 277f4eecd5dSAndy Adamson { 278f4eecd5dSAndy Adamson } 279f7e8917aSFred Isaman 280f7e8917aSFred Isaman static void pnfs_init_server(struct nfs_server *server) 281f7e8917aSFred Isaman { 282f7e8917aSFred Isaman } 283f7e8917aSFred Isaman 284888ef2e3SAlexandros Batsakis #endif /* CONFIG_NFS_V4 */ 285888ef2e3SAlexandros Batsakis 286888ef2e3SAlexandros Batsakis /* 2875dd3177aSTrond Myklebust * Destroy a shared client record 2885dd3177aSTrond Myklebust */ 2895dd3177aSTrond Myklebust static void nfs_free_client(struct nfs_client *clp) 2905dd3177aSTrond Myklebust { 29140c55319STrond Myklebust dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version); 2925dd3177aSTrond Myklebust 2935dd3177aSTrond Myklebust nfs4_shutdown_client(clp); 29424c8dbbbSDavid Howells 29514727281SDavid Howells nfs_fscache_release_client_cookie(clp); 29614727281SDavid Howells 29724c8dbbbSDavid Howells /* -EIO all pending I/O */ 29824c8dbbbSDavid Howells if (!IS_ERR(clp->cl_rpcclient)) 29924c8dbbbSDavid Howells rpc_shutdown_client(clp->cl_rpcclient); 30024c8dbbbSDavid Howells 3017c67db3aSTrond Myklebust if (clp->cl_machine_cred != NULL) 3027c67db3aSTrond Myklebust put_rpccred(clp->cl_machine_cred); 3037c67db3aSTrond Myklebust 304abd96698STrond Myklebust put_net(clp->net); 30524c8dbbbSDavid Howells kfree(clp->cl_hostname); 30678fe0f41SWeston Andros Adamson kfree(clp->server_scope); 3077d2ed9acSWeston Andros Adamson kfree(clp->impl_id); 30824c8dbbbSDavid Howells kfree(clp); 30924c8dbbbSDavid Howells 31024c8dbbbSDavid Howells dprintk("<-- nfs_free_client()\n"); 31124c8dbbbSDavid Howells } 31224c8dbbbSDavid Howells 31324c8dbbbSDavid Howells /* 31424c8dbbbSDavid Howells * Release a reference to a shared client record 31524c8dbbbSDavid Howells */ 31624c8dbbbSDavid Howells void nfs_put_client(struct nfs_client *clp) 31724c8dbbbSDavid Howells { 318dc030858SStanislav Kinsbursky struct nfs_net *nn; 319dc030858SStanislav Kinsbursky 32027ba8512SDavid Howells if (!clp) 32127ba8512SDavid Howells return; 32227ba8512SDavid Howells 32324c8dbbbSDavid Howells dprintk("--> nfs_put_client({%d})\n", atomic_read(&clp->cl_count)); 324dc030858SStanislav Kinsbursky nn = net_generic(clp->net, nfs_net_id); 32524c8dbbbSDavid Howells 326dc030858SStanislav Kinsbursky if (atomic_dec_and_lock(&clp->cl_count, &nn->nfs_client_lock)) { 32724c8dbbbSDavid Howells list_del(&clp->cl_share_link); 328f4eecd5dSAndy Adamson nfs_cb_idr_remove_locked(clp); 329dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 33024c8dbbbSDavid Howells 33124c8dbbbSDavid Howells BUG_ON(!list_empty(&clp->cl_superblocks)); 33224c8dbbbSDavid Howells 33324c8dbbbSDavid Howells nfs_free_client(clp); 33424c8dbbbSDavid Howells } 33524c8dbbbSDavid Howells } 33616b374caSAndy Adamson EXPORT_SYMBOL_GPL(nfs_put_client); 33724c8dbbbSDavid Howells 3389082a5ccSTrond Myklebust #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 3399f4c899cSTrond Myklebust /* 3409f4c899cSTrond Myklebust * Test if two ip6 socket addresses refer to the same socket by 3419f4c899cSTrond Myklebust * comparing relevant fields. The padding bytes specifically, are not 3429f4c899cSTrond Myklebust * compared. sin6_flowinfo is not compared because it only affects QoS 3439f4c899cSTrond Myklebust * and sin6_scope_id is only compared if the address is "link local" 3449f4c899cSTrond Myklebust * because "link local" addresses need only be unique to a specific 3459f4c899cSTrond Myklebust * link. Conversely, ordinary unicast addresses might have different 3469f4c899cSTrond Myklebust * sin6_scope_id. 3479f4c899cSTrond Myklebust * 3489f4c899cSTrond Myklebust * The caller should ensure both socket addresses are AF_INET6. 3499f4c899cSTrond Myklebust */ 3503c8c45dfSChuck Lever static int nfs_sockaddr_match_ipaddr6(const struct sockaddr *sa1, 3519f4c899cSTrond Myklebust const struct sockaddr *sa2) 3529f4c899cSTrond Myklebust { 3533c8c45dfSChuck Lever const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sa1; 3543c8c45dfSChuck Lever const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2; 3559f4c899cSTrond Myklebust 356b9dd3abbSMi Jinlong if (!ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr)) 3579f4c899cSTrond Myklebust return 0; 358b9dd3abbSMi Jinlong else if (ipv6_addr_type(&sin1->sin6_addr) & IPV6_ADDR_LINKLOCAL) 359b9dd3abbSMi Jinlong return sin1->sin6_scope_id == sin2->sin6_scope_id; 3603b0d3f93STrond Myklebust 361b9dd3abbSMi Jinlong return 1; 3623b0d3f93STrond Myklebust } 3633c8c45dfSChuck Lever #else /* !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE) */ 3643c8c45dfSChuck Lever static int nfs_sockaddr_match_ipaddr6(const struct sockaddr *sa1, 3659f4c899cSTrond Myklebust const struct sockaddr *sa2) 3669f4c899cSTrond Myklebust { 3679f4c899cSTrond Myklebust return 0; 3689f4c899cSTrond Myklebust } 3699082a5ccSTrond Myklebust #endif 3703b0d3f93STrond Myklebust 37124c8dbbbSDavid Howells /* 372d7371c41SIan Dall * Test if two ip4 socket addresses refer to the same socket, by 373d7371c41SIan Dall * comparing relevant fields. The padding bytes specifically, are 374d7371c41SIan Dall * not compared. 375d7371c41SIan Dall * 376d7371c41SIan Dall * The caller should ensure both socket addresses are AF_INET. 377d7371c41SIan Dall */ 3783c8c45dfSChuck Lever static int nfs_sockaddr_match_ipaddr4(const struct sockaddr *sa1, 3793c8c45dfSChuck Lever const struct sockaddr *sa2) 3803c8c45dfSChuck Lever { 3813c8c45dfSChuck Lever const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sa1; 3823c8c45dfSChuck Lever const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sa2; 3833c8c45dfSChuck Lever 3843c8c45dfSChuck Lever return sin1->sin_addr.s_addr == sin2->sin_addr.s_addr; 3853c8c45dfSChuck Lever } 3863c8c45dfSChuck Lever 3873c8c45dfSChuck Lever static int nfs_sockaddr_cmp_ip6(const struct sockaddr *sa1, 3883c8c45dfSChuck Lever const struct sockaddr *sa2) 3893c8c45dfSChuck Lever { 3903c8c45dfSChuck Lever const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sa1; 3913c8c45dfSChuck Lever const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2; 3923c8c45dfSChuck Lever 3933c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr6(sa1, sa2) && 3943c8c45dfSChuck Lever (sin1->sin6_port == sin2->sin6_port); 3953c8c45dfSChuck Lever } 3963c8c45dfSChuck Lever 3979f4c899cSTrond Myklebust static int nfs_sockaddr_cmp_ip4(const struct sockaddr *sa1, 3989f4c899cSTrond Myklebust const struct sockaddr *sa2) 399d7371c41SIan Dall { 4003c8c45dfSChuck Lever const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sa1; 4013c8c45dfSChuck Lever const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sa2; 4029f4c899cSTrond Myklebust 4033c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr4(sa1, sa2) && 4043c8c45dfSChuck Lever (sin1->sin_port == sin2->sin_port); 405d7371c41SIan Dall } 406d7371c41SIan Dall 4074b7c8dd2SBryan Schumaker #if defined(CONFIG_NFS_V4_1) 408d7371c41SIan Dall /* 409d7371c41SIan Dall * Test if two socket addresses represent the same actual socket, 4103c8c45dfSChuck Lever * by comparing (only) relevant fields, excluding the port number. 4113c8c45dfSChuck Lever */ 4123c8c45dfSChuck Lever static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1, 4133c8c45dfSChuck Lever const struct sockaddr *sa2) 4143c8c45dfSChuck Lever { 4153c8c45dfSChuck Lever if (sa1->sa_family != sa2->sa_family) 4163c8c45dfSChuck Lever return 0; 4173c8c45dfSChuck Lever 4183c8c45dfSChuck Lever switch (sa1->sa_family) { 4193c8c45dfSChuck Lever case AF_INET: 4203c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr4(sa1, sa2); 4213c8c45dfSChuck Lever case AF_INET6: 4223c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr6(sa1, sa2); 4233c8c45dfSChuck Lever } 4243c8c45dfSChuck Lever return 0; 4253c8c45dfSChuck Lever } 4264b7c8dd2SBryan Schumaker #endif /* CONFIG_NFS_V4_1 */ 4273c8c45dfSChuck Lever 4283c8c45dfSChuck Lever /* 4293c8c45dfSChuck Lever * Test if two socket addresses represent the same actual socket, 4303c8c45dfSChuck Lever * by comparing (only) relevant fields, including the port number. 431d7371c41SIan Dall */ 432d7371c41SIan Dall static int nfs_sockaddr_cmp(const struct sockaddr *sa1, 433d7371c41SIan Dall const struct sockaddr *sa2) 434d7371c41SIan Dall { 435d7371c41SIan Dall if (sa1->sa_family != sa2->sa_family) 436d7371c41SIan Dall return 0; 437d7371c41SIan Dall 438d7371c41SIan Dall switch (sa1->sa_family) { 439d7371c41SIan Dall case AF_INET: 4409f4c899cSTrond Myklebust return nfs_sockaddr_cmp_ip4(sa1, sa2); 441d7371c41SIan Dall case AF_INET6: 4429f4c899cSTrond Myklebust return nfs_sockaddr_cmp_ip6(sa1, sa2); 443d7371c41SIan Dall } 444d7371c41SIan Dall return 0; 445d7371c41SIan Dall } 446d7371c41SIan Dall 4474b7c8dd2SBryan Schumaker #if defined(CONFIG_NFS_V4_1) 448c36fca52SAndy Adamson /* Common match routine for v4.0 and v4.1 callback services */ 44917280175STrond Myklebust static bool nfs4_cb_match_client(const struct sockaddr *addr, 45017280175STrond Myklebust struct nfs_client *clp, u32 minorversion) 451c81468a1STrond Myklebust { 4523b0d3f93STrond Myklebust struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; 4533b0d3f93STrond Myklebust 454c36fca52SAndy Adamson /* Don't match clients that failed to initialise */ 45576db6d95SAndy Adamson if (!(clp->cl_cons_state == NFS_CS_READY || 45676db6d95SAndy Adamson clp->cl_cons_state == NFS_CS_SESSION_INITING)) 457c36fca52SAndy Adamson return false; 458c81468a1STrond Myklebust 459c36fca52SAndy Adamson /* Match the version and minorversion */ 460c36fca52SAndy Adamson if (clp->rpc_ops->version != 4 || 461c36fca52SAndy Adamson clp->cl_minorversion != minorversion) 462c36fca52SAndy Adamson return false; 463c81468a1STrond Myklebust 464c81468a1STrond Myklebust /* Match only the IP address, not the port number */ 4653b0d3f93STrond Myklebust if (!nfs_sockaddr_match_ipaddr(addr, clap)) 466c36fca52SAndy Adamson return false; 467c81468a1STrond Myklebust 468c36fca52SAndy Adamson return true; 4693fbd67adSTrond Myklebust } 4704b7c8dd2SBryan Schumaker #endif /* CONFIG_NFS_V4_1 */ 4713fbd67adSTrond Myklebust 4723fbd67adSTrond Myklebust /* 473c81468a1STrond Myklebust * Find an nfs_client on the list that matches the initialisation data 474c81468a1STrond Myklebust * that is supplied. 475c81468a1STrond Myklebust */ 476c81468a1STrond Myklebust static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *data) 47724c8dbbbSDavid Howells { 47824c8dbbbSDavid Howells struct nfs_client *clp; 479d7371c41SIan Dall const struct sockaddr *sap = data->addr; 4806b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(data->net, nfs_net_id); 48124c8dbbbSDavid Howells 4826b13168bSStanislav Kinsbursky list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) { 483d7371c41SIan Dall const struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; 48413bbc06aSTrond Myklebust /* Don't match clients that failed to initialise properly */ 48513bbc06aSTrond Myklebust if (clp->cl_cons_state < 0) 48613bbc06aSTrond Myklebust continue; 48713bbc06aSTrond Myklebust 48824c8dbbbSDavid Howells /* Different NFS versions cannot share the same nfs_client */ 48940c55319STrond Myklebust if (clp->rpc_ops != data->rpc_ops) 49024c8dbbbSDavid Howells continue; 49124c8dbbbSDavid Howells 49259dca3b2STrond Myklebust if (clp->cl_proto != data->proto) 49359dca3b2STrond Myklebust continue; 4945aae4a9aSBenny Halevy /* Match nfsv4 minorversion */ 4955aae4a9aSBenny Halevy if (clp->cl_minorversion != data->minorversion) 4965aae4a9aSBenny Halevy continue; 497c81468a1STrond Myklebust /* Match the full socket address */ 498d7371c41SIan Dall if (!nfs_sockaddr_cmp(sap, clap)) 49924c8dbbbSDavid Howells continue; 50024c8dbbbSDavid Howells 50124c8dbbbSDavid Howells atomic_inc(&clp->cl_count); 50224c8dbbbSDavid Howells return clp; 50324c8dbbbSDavid Howells } 504c81468a1STrond Myklebust return NULL; 50524c8dbbbSDavid Howells } 50624c8dbbbSDavid Howells 50724c8dbbbSDavid Howells /* 50824c8dbbbSDavid Howells * Look up a client by IP address and protocol version 50924c8dbbbSDavid Howells * - creates a new record if one doesn't yet exist 51024c8dbbbSDavid Howells */ 51145a52a02SAndy Adamson static struct nfs_client * 51245a52a02SAndy Adamson nfs_get_client(const struct nfs_client_initdata *cl_init, 51345a52a02SAndy Adamson const struct rpc_timeout *timeparms, 51445a52a02SAndy Adamson const char *ip_addr, 51545a52a02SAndy Adamson rpc_authflavor_t authflavour, 51645a52a02SAndy Adamson int noresvport) 51724c8dbbbSDavid Howells { 51824c8dbbbSDavid Howells struct nfs_client *clp, *new = NULL; 51924c8dbbbSDavid Howells int error; 5206b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(cl_init->net, nfs_net_id); 52124c8dbbbSDavid Howells 522d7422c47SChuck Lever dprintk("--> nfs_get_client(%s,v%u)\n", 523d7422c47SChuck Lever cl_init->hostname ?: "", cl_init->rpc_ops->version); 52424c8dbbbSDavid Howells 52524c8dbbbSDavid Howells /* see if the client already exists */ 52624c8dbbbSDavid Howells do { 527dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 52824c8dbbbSDavid Howells 529c81468a1STrond Myklebust clp = nfs_match_client(cl_init); 53024c8dbbbSDavid Howells if (clp) 53124c8dbbbSDavid Howells goto found_client; 53224c8dbbbSDavid Howells if (new) 53324c8dbbbSDavid Howells goto install_client; 53424c8dbbbSDavid Howells 535dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 53624c8dbbbSDavid Howells 5373a498026STrond Myklebust new = nfs_alloc_client(cl_init); 538a21bdd9bSChuck Lever } while (!IS_ERR(new)); 53924c8dbbbSDavid Howells 540a21bdd9bSChuck Lever dprintk("--> nfs_get_client() = %ld [failed]\n", PTR_ERR(new)); 541a21bdd9bSChuck Lever return new; 54224c8dbbbSDavid Howells 54324c8dbbbSDavid Howells /* install a new client and return with it unready */ 54424c8dbbbSDavid Howells install_client: 54524c8dbbbSDavid Howells clp = new; 5466b13168bSStanislav Kinsbursky list_add(&clp->cl_share_link, &nn->nfs_client_list); 547dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 54845a52a02SAndy Adamson 54945a52a02SAndy Adamson error = cl_init->rpc_ops->init_client(clp, timeparms, ip_addr, 55045a52a02SAndy Adamson authflavour, noresvport); 55145a52a02SAndy Adamson if (error < 0) { 55245a52a02SAndy Adamson nfs_put_client(clp); 55345a52a02SAndy Adamson return ERR_PTR(error); 55445a52a02SAndy Adamson } 55524c8dbbbSDavid Howells dprintk("--> nfs_get_client() = %p [new]\n", clp); 55624c8dbbbSDavid Howells return clp; 55724c8dbbbSDavid Howells 55824c8dbbbSDavid Howells /* found an existing client 55924c8dbbbSDavid Howells * - make sure it's ready before returning 56024c8dbbbSDavid Howells */ 56124c8dbbbSDavid Howells found_client: 562dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 56324c8dbbbSDavid Howells 56424c8dbbbSDavid Howells if (new) 56524c8dbbbSDavid Howells nfs_free_client(new); 56624c8dbbbSDavid Howells 567150030b7SMatthew Wilcox error = wait_event_killable(nfs_client_active_wq, 56876db6d95SAndy Adamson clp->cl_cons_state < NFS_CS_INITING); 5690bae89ecSTrond Myklebust if (error < 0) { 57024c8dbbbSDavid Howells nfs_put_client(clp); 57124c8dbbbSDavid Howells return ERR_PTR(-ERESTARTSYS); 57224c8dbbbSDavid Howells } 57324c8dbbbSDavid Howells 57424c8dbbbSDavid Howells if (clp->cl_cons_state < NFS_CS_READY) { 57524c8dbbbSDavid Howells error = clp->cl_cons_state; 57624c8dbbbSDavid Howells nfs_put_client(clp); 57724c8dbbbSDavid Howells return ERR_PTR(error); 57824c8dbbbSDavid Howells } 57924c8dbbbSDavid Howells 58054ceac45SDavid Howells BUG_ON(clp->cl_cons_state != NFS_CS_READY); 58154ceac45SDavid Howells 58224c8dbbbSDavid Howells dprintk("--> nfs_get_client() = %p [share]\n", clp); 58324c8dbbbSDavid Howells return clp; 58424c8dbbbSDavid Howells } 58524c8dbbbSDavid Howells 58624c8dbbbSDavid Howells /* 58724c8dbbbSDavid Howells * Mark a server as ready or failed 58824c8dbbbSDavid Howells */ 58976db6d95SAndy Adamson void nfs_mark_client_ready(struct nfs_client *clp, int state) 59024c8dbbbSDavid Howells { 59124c8dbbbSDavid Howells clp->cl_cons_state = state; 59224c8dbbbSDavid Howells wake_up_all(&nfs_client_active_wq); 59324c8dbbbSDavid Howells } 5945006a76cSDavid Howells 5955006a76cSDavid Howells /* 596008f55d0SBenny Halevy * With sessions, the client is not marked ready until after a 597008f55d0SBenny Halevy * successful EXCHANGE_ID and CREATE_SESSION. 598008f55d0SBenny Halevy * 599008f55d0SBenny Halevy * Map errors cl_cons_state errors to EPROTONOSUPPORT to indicate 600008f55d0SBenny Halevy * other versions of NFS can be tried. 601008f55d0SBenny Halevy */ 602008f55d0SBenny Halevy int nfs4_check_client_ready(struct nfs_client *clp) 603008f55d0SBenny Halevy { 604008f55d0SBenny Halevy if (!nfs4_has_session(clp)) 605008f55d0SBenny Halevy return 0; 606008f55d0SBenny Halevy if (clp->cl_cons_state < NFS_CS_READY) 607008f55d0SBenny Halevy return -EPROTONOSUPPORT; 608008f55d0SBenny Halevy return 0; 609008f55d0SBenny Halevy } 610008f55d0SBenny Halevy 611008f55d0SBenny Halevy /* 6125006a76cSDavid Howells * Initialise the timeout values for a connection 6135006a76cSDavid Howells */ 6145006a76cSDavid Howells static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, 6155006a76cSDavid Howells unsigned int timeo, unsigned int retrans) 6165006a76cSDavid Howells { 6175006a76cSDavid Howells to->to_initval = timeo * HZ / 10; 6185006a76cSDavid Howells to->to_retries = retrans; 6195006a76cSDavid Howells 6205006a76cSDavid Howells switch (proto) { 6210896a725S\"Talpey, Thomas\ case XPRT_TRANSPORT_TCP: 6222cf7ff7aS\"Talpey, Thomas\ case XPRT_TRANSPORT_RDMA: 623259875efSTrond Myklebust if (to->to_retries == 0) 624259875efSTrond Myklebust to->to_retries = NFS_DEF_TCP_RETRANS; 6257a3e3e18STrond Myklebust if (to->to_initval == 0) 626259875efSTrond Myklebust to->to_initval = NFS_DEF_TCP_TIMEO * HZ / 10; 6275006a76cSDavid Howells if (to->to_initval > NFS_MAX_TCP_TIMEOUT) 6285006a76cSDavid Howells to->to_initval = NFS_MAX_TCP_TIMEOUT; 6295006a76cSDavid Howells to->to_increment = to->to_initval; 6305006a76cSDavid Howells to->to_maxval = to->to_initval + (to->to_increment * to->to_retries); 6317a3e3e18STrond Myklebust if (to->to_maxval > NFS_MAX_TCP_TIMEOUT) 6327a3e3e18STrond Myklebust to->to_maxval = NFS_MAX_TCP_TIMEOUT; 6337a3e3e18STrond Myklebust if (to->to_maxval < to->to_initval) 6347a3e3e18STrond Myklebust to->to_maxval = to->to_initval; 6355006a76cSDavid Howells to->to_exponential = 0; 6365006a76cSDavid Howells break; 6370896a725S\"Talpey, Thomas\ case XPRT_TRANSPORT_UDP: 638259875efSTrond Myklebust if (to->to_retries == 0) 639259875efSTrond Myklebust to->to_retries = NFS_DEF_UDP_RETRANS; 6405006a76cSDavid Howells if (!to->to_initval) 641259875efSTrond Myklebust to->to_initval = NFS_DEF_UDP_TIMEO * HZ / 10; 6425006a76cSDavid Howells if (to->to_initval > NFS_MAX_UDP_TIMEOUT) 6435006a76cSDavid Howells to->to_initval = NFS_MAX_UDP_TIMEOUT; 6445006a76cSDavid Howells to->to_maxval = NFS_MAX_UDP_TIMEOUT; 6455006a76cSDavid Howells to->to_exponential = 1; 6465006a76cSDavid Howells break; 647259875efSTrond Myklebust default: 648259875efSTrond Myklebust BUG(); 6495006a76cSDavid Howells } 6505006a76cSDavid Howells } 6515006a76cSDavid Howells 6525006a76cSDavid Howells /* 6535006a76cSDavid Howells * Create an RPC client handle 6545006a76cSDavid Howells */ 65559dca3b2STrond Myklebust static int nfs_create_rpc_client(struct nfs_client *clp, 65633170233STrond Myklebust const struct rpc_timeout *timeparms, 65743d78ef2SChuck Lever rpc_authflavor_t flavor, 6584a01b8a4SChuck Lever int discrtry, int noresvport) 6595006a76cSDavid Howells { 6605006a76cSDavid Howells struct rpc_clnt *clnt = NULL; 66141877d20SChuck Lever struct rpc_create_args args = { 6626d59b8d5SStanislav Kinsbursky .net = clp->net, 66359dca3b2STrond Myklebust .protocol = clp->cl_proto, 66441877d20SChuck Lever .address = (struct sockaddr *)&clp->cl_addr, 6656e4cffd7SChuck Lever .addrsize = clp->cl_addrlen, 66633170233STrond Myklebust .timeout = timeparms, 66741877d20SChuck Lever .servername = clp->cl_hostname, 66841877d20SChuck Lever .program = &nfs_program, 66941877d20SChuck Lever .version = clp->rpc_ops->version, 67041877d20SChuck Lever .authflavor = flavor, 67141877d20SChuck Lever }; 6725006a76cSDavid Howells 6734a01b8a4SChuck Lever if (discrtry) 6744a01b8a4SChuck Lever args.flags |= RPC_CLNT_CREATE_DISCRTRY; 6754a01b8a4SChuck Lever if (noresvport) 6764a01b8a4SChuck Lever args.flags |= RPC_CLNT_CREATE_NONPRIVPORT; 6774a01b8a4SChuck Lever 6785006a76cSDavid Howells if (!IS_ERR(clp->cl_rpcclient)) 6795006a76cSDavid Howells return 0; 6805006a76cSDavid Howells 68141877d20SChuck Lever clnt = rpc_create(&args); 6825006a76cSDavid Howells if (IS_ERR(clnt)) { 6835006a76cSDavid Howells dprintk("%s: cannot create RPC client. Error = %ld\n", 6843110ff80SHarvey Harrison __func__, PTR_ERR(clnt)); 6855006a76cSDavid Howells return PTR_ERR(clnt); 6865006a76cSDavid Howells } 6875006a76cSDavid Howells 6885006a76cSDavid Howells clp->cl_rpcclient = clnt; 6895006a76cSDavid Howells return 0; 6905006a76cSDavid Howells } 69154ceac45SDavid Howells 69254ceac45SDavid Howells /* 69354ceac45SDavid Howells * Version 2 or 3 client destruction 69454ceac45SDavid Howells */ 69554ceac45SDavid Howells static void nfs_destroy_server(struct nfs_server *server) 69654ceac45SDavid Howells { 6975eebde23SSuresh Jayaraman if (!(server->flags & NFS_MOUNT_LOCAL_FLOCK) || 6985eebde23SSuresh Jayaraman !(server->flags & NFS_MOUNT_LOCAL_FCNTL)) 6999289e7f9SChuck Lever nlmclnt_done(server->nlm_host); 70054ceac45SDavid Howells } 70154ceac45SDavid Howells 70254ceac45SDavid Howells /* 70354ceac45SDavid Howells * Version 2 or 3 lockd setup 70454ceac45SDavid Howells */ 70554ceac45SDavid Howells static int nfs_start_lockd(struct nfs_server *server) 70654ceac45SDavid Howells { 7079289e7f9SChuck Lever struct nlm_host *host; 7089289e7f9SChuck Lever struct nfs_client *clp = server->nfs_client; 709883bb163SChuck Lever struct nlmclnt_initdata nlm_init = { 710883bb163SChuck Lever .hostname = clp->cl_hostname, 711883bb163SChuck Lever .address = (struct sockaddr *)&clp->cl_addr, 712883bb163SChuck Lever .addrlen = clp->cl_addrlen, 713883bb163SChuck Lever .nfs_version = clp->rpc_ops->version, 7140cb2659bSChuck Lever .noresvport = server->flags & NFS_MOUNT_NORESVPORT ? 7150cb2659bSChuck Lever 1 : 0, 71666697bfdSStanislav Kinsbursky .net = clp->net, 717883bb163SChuck Lever }; 71854ceac45SDavid Howells 719883bb163SChuck Lever if (nlm_init.nfs_version > 3) 7209289e7f9SChuck Lever return 0; 7215eebde23SSuresh Jayaraman if ((server->flags & NFS_MOUNT_LOCAL_FLOCK) && 7225eebde23SSuresh Jayaraman (server->flags & NFS_MOUNT_LOCAL_FCNTL)) 7239289e7f9SChuck Lever return 0; 7249289e7f9SChuck Lever 7258a6e5debSTrond Myklebust switch (clp->cl_proto) { 7268a6e5debSTrond Myklebust default: 7278a6e5debSTrond Myklebust nlm_init.protocol = IPPROTO_TCP; 7288a6e5debSTrond Myklebust break; 7298a6e5debSTrond Myklebust case XPRT_TRANSPORT_UDP: 7308a6e5debSTrond Myklebust nlm_init.protocol = IPPROTO_UDP; 7318a6e5debSTrond Myklebust } 7328a6e5debSTrond Myklebust 733883bb163SChuck Lever host = nlmclnt_init(&nlm_init); 7349289e7f9SChuck Lever if (IS_ERR(host)) 7359289e7f9SChuck Lever return PTR_ERR(host); 7369289e7f9SChuck Lever 7379289e7f9SChuck Lever server->nlm_host = host; 73854ceac45SDavid Howells server->destroy = nfs_destroy_server; 7399289e7f9SChuck Lever return 0; 74054ceac45SDavid Howells } 74154ceac45SDavid Howells 74254ceac45SDavid Howells /* 74354ceac45SDavid Howells * Initialise an NFSv3 ACL client connection 74454ceac45SDavid Howells */ 74554ceac45SDavid Howells #ifdef CONFIG_NFS_V3_ACL 74654ceac45SDavid Howells static void nfs_init_server_aclclient(struct nfs_server *server) 74754ceac45SDavid Howells { 74840c55319STrond Myklebust if (server->nfs_client->rpc_ops->version != 3) 74954ceac45SDavid Howells goto out_noacl; 75054ceac45SDavid Howells if (server->flags & NFS_MOUNT_NOACL) 75154ceac45SDavid Howells goto out_noacl; 75254ceac45SDavid Howells 75354ceac45SDavid Howells server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3); 75454ceac45SDavid Howells if (IS_ERR(server->client_acl)) 75554ceac45SDavid Howells goto out_noacl; 75654ceac45SDavid Howells 75754ceac45SDavid Howells /* No errors! Assume that Sun nfsacls are supported */ 75854ceac45SDavid Howells server->caps |= NFS_CAP_ACLS; 75954ceac45SDavid Howells return; 76054ceac45SDavid Howells 76154ceac45SDavid Howells out_noacl: 76254ceac45SDavid Howells server->caps &= ~NFS_CAP_ACLS; 76354ceac45SDavid Howells } 76454ceac45SDavid Howells #else 76554ceac45SDavid Howells static inline void nfs_init_server_aclclient(struct nfs_server *server) 76654ceac45SDavid Howells { 76754ceac45SDavid Howells server->flags &= ~NFS_MOUNT_NOACL; 76854ceac45SDavid Howells server->caps &= ~NFS_CAP_ACLS; 76954ceac45SDavid Howells } 77054ceac45SDavid Howells #endif 77154ceac45SDavid Howells 77254ceac45SDavid Howells /* 77354ceac45SDavid Howells * Create a general RPC client 77454ceac45SDavid Howells */ 77533170233STrond Myklebust static int nfs_init_server_rpcclient(struct nfs_server *server, 77633170233STrond Myklebust const struct rpc_timeout *timeo, 77733170233STrond Myklebust rpc_authflavor_t pseudoflavour) 77854ceac45SDavid Howells { 77954ceac45SDavid Howells struct nfs_client *clp = server->nfs_client; 78054ceac45SDavid Howells 78154ceac45SDavid Howells server->client = rpc_clone_client(clp->cl_rpcclient); 78254ceac45SDavid Howells if (IS_ERR(server->client)) { 7833110ff80SHarvey Harrison dprintk("%s: couldn't create rpc_client!\n", __func__); 78454ceac45SDavid Howells return PTR_ERR(server->client); 78554ceac45SDavid Howells } 78654ceac45SDavid Howells 78733170233STrond Myklebust memcpy(&server->client->cl_timeout_default, 78833170233STrond Myklebust timeo, 78933170233STrond Myklebust sizeof(server->client->cl_timeout_default)); 79033170233STrond Myklebust server->client->cl_timeout = &server->client->cl_timeout_default; 79133170233STrond Myklebust 79254ceac45SDavid Howells if (pseudoflavour != clp->cl_rpcclient->cl_auth->au_flavor) { 79354ceac45SDavid Howells struct rpc_auth *auth; 79454ceac45SDavid Howells 79554ceac45SDavid Howells auth = rpcauth_create(pseudoflavour, server->client); 79654ceac45SDavid Howells if (IS_ERR(auth)) { 7973110ff80SHarvey Harrison dprintk("%s: couldn't create credcache!\n", __func__); 79854ceac45SDavid Howells return PTR_ERR(auth); 79954ceac45SDavid Howells } 80054ceac45SDavid Howells } 80154ceac45SDavid Howells server->client->cl_softrtry = 0; 80254ceac45SDavid Howells if (server->flags & NFS_MOUNT_SOFT) 80354ceac45SDavid Howells server->client->cl_softrtry = 1; 80454ceac45SDavid Howells 80554ceac45SDavid Howells return 0; 80654ceac45SDavid Howells } 80754ceac45SDavid Howells 80854ceac45SDavid Howells /* 80954ceac45SDavid Howells * Initialise an NFS2 or NFS3 client 81054ceac45SDavid Howells */ 81145a52a02SAndy Adamson int nfs_init_client(struct nfs_client *clp, const struct rpc_timeout *timeparms, 81245a52a02SAndy Adamson const char *ip_addr, rpc_authflavor_t authflavour, 81345a52a02SAndy Adamson int noresvport) 81454ceac45SDavid Howells { 81554ceac45SDavid Howells int error; 81654ceac45SDavid Howells 81754ceac45SDavid Howells if (clp->cl_cons_state == NFS_CS_READY) { 81854ceac45SDavid Howells /* the client is already initialised */ 81954ceac45SDavid Howells dprintk("<-- nfs_init_client() = 0 [already %p]\n", clp); 82054ceac45SDavid Howells return 0; 82154ceac45SDavid Howells } 82254ceac45SDavid Howells 82354ceac45SDavid Howells /* 82454ceac45SDavid Howells * Create a client RPC handle for doing FSSTAT with UNIX auth only 82554ceac45SDavid Howells * - RFC 2623, sec 2.3.2 82654ceac45SDavid Howells */ 827d740351bSChuck Lever error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX, 82845a52a02SAndy Adamson 0, noresvport); 82954ceac45SDavid Howells if (error < 0) 83054ceac45SDavid Howells goto error; 83154ceac45SDavid Howells nfs_mark_client_ready(clp, NFS_CS_READY); 83254ceac45SDavid Howells return 0; 83354ceac45SDavid Howells 83454ceac45SDavid Howells error: 83554ceac45SDavid Howells nfs_mark_client_ready(clp, error); 83654ceac45SDavid Howells dprintk("<-- nfs_init_client() = xerror %d\n", error); 83754ceac45SDavid Howells return error; 83854ceac45SDavid Howells } 83954ceac45SDavid Howells 84054ceac45SDavid Howells /* 84154ceac45SDavid Howells * Create a version 2 or 3 client 84254ceac45SDavid Howells */ 8432283f8d6S\"Talpey, Thomas\ static int nfs_init_server(struct nfs_server *server, 8442283f8d6S\"Talpey, Thomas\ const struct nfs_parsed_mount_data *data) 84554ceac45SDavid Howells { 8463a498026STrond Myklebust struct nfs_client_initdata cl_init = { 8473a498026STrond Myklebust .hostname = data->nfs_server.hostname, 848d7422c47SChuck Lever .addr = (const struct sockaddr *)&data->nfs_server.address, 8494c568017SChuck Lever .addrlen = data->nfs_server.addrlen, 85040c55319STrond Myklebust .rpc_ops = &nfs_v2_clientops, 85159dca3b2STrond Myklebust .proto = data->nfs_server.protocol, 852e50a7a1aSStanislav Kinsbursky .net = data->net, 8533a498026STrond Myklebust }; 85433170233STrond Myklebust struct rpc_timeout timeparms; 85554ceac45SDavid Howells struct nfs_client *clp; 8563a498026STrond Myklebust int error; 85754ceac45SDavid Howells 85854ceac45SDavid Howells dprintk("--> nfs_init_server()\n"); 85954ceac45SDavid Howells 86054ceac45SDavid Howells #ifdef CONFIG_NFS_V3 8618a6e5debSTrond Myklebust if (data->version == 3) 86240c55319STrond Myklebust cl_init.rpc_ops = &nfs_v3_clientops; 86354ceac45SDavid Howells #endif 86454ceac45SDavid Howells 86545a52a02SAndy Adamson nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, 86645a52a02SAndy Adamson data->timeo, data->retrans); 86745a52a02SAndy Adamson 86854ceac45SDavid Howells /* Allocate or find a client reference we can use */ 86945a52a02SAndy Adamson clp = nfs_get_client(&cl_init, &timeparms, NULL, RPC_AUTH_UNIX, 87045a52a02SAndy Adamson data->flags & NFS_MOUNT_NORESVPORT); 87154ceac45SDavid Howells if (IS_ERR(clp)) { 87254ceac45SDavid Howells dprintk("<-- nfs_init_server() = error %ld\n", PTR_ERR(clp)); 87354ceac45SDavid Howells return PTR_ERR(clp); 87454ceac45SDavid Howells } 87554ceac45SDavid Howells 87654ceac45SDavid Howells server->nfs_client = clp; 87754ceac45SDavid Howells 87854ceac45SDavid Howells /* Initialise the client representation from the mount data */ 879ff3525a5STrond Myklebust server->flags = data->flags; 880b797cac7SDavid Howells server->options = data->options; 88162ab460cSTrond Myklebust server->caps |= NFS_CAP_HARDLINKS|NFS_CAP_SYMLINKS|NFS_CAP_FILEID| 88262ab460cSTrond Myklebust NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|NFS_CAP_OWNER_GROUP| 88362ab460cSTrond Myklebust NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME; 88454ceac45SDavid Howells 88554ceac45SDavid Howells if (data->rsize) 88654ceac45SDavid Howells server->rsize = nfs_block_size(data->rsize, NULL); 88754ceac45SDavid Howells if (data->wsize) 88854ceac45SDavid Howells server->wsize = nfs_block_size(data->wsize, NULL); 88954ceac45SDavid Howells 89054ceac45SDavid Howells server->acregmin = data->acregmin * HZ; 89154ceac45SDavid Howells server->acregmax = data->acregmax * HZ; 89254ceac45SDavid Howells server->acdirmin = data->acdirmin * HZ; 89354ceac45SDavid Howells server->acdirmax = data->acdirmax * HZ; 89454ceac45SDavid Howells 89554ceac45SDavid Howells /* Start lockd here, before we might error out */ 89654ceac45SDavid Howells error = nfs_start_lockd(server); 89754ceac45SDavid Howells if (error < 0) 89854ceac45SDavid Howells goto error; 89954ceac45SDavid Howells 900f22d6d79SChuck Lever server->port = data->nfs_server.port; 901f22d6d79SChuck Lever 90233170233STrond Myklebust error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]); 90354ceac45SDavid Howells if (error < 0) 90454ceac45SDavid Howells goto error; 90554ceac45SDavid Howells 9063f8400d1SChuck Lever /* Preserve the values of mount_server-related mount options */ 9073f8400d1SChuck Lever if (data->mount_server.addrlen) { 9083f8400d1SChuck Lever memcpy(&server->mountd_address, &data->mount_server.address, 9093f8400d1SChuck Lever data->mount_server.addrlen); 9103f8400d1SChuck Lever server->mountd_addrlen = data->mount_server.addrlen; 9113f8400d1SChuck Lever } 9123f8400d1SChuck Lever server->mountd_version = data->mount_server.version; 9133f8400d1SChuck Lever server->mountd_port = data->mount_server.port; 9143f8400d1SChuck Lever server->mountd_protocol = data->mount_server.protocol; 9153f8400d1SChuck Lever 91654ceac45SDavid Howells server->namelen = data->namlen; 91754ceac45SDavid Howells /* Create a client RPC handle for the NFSv3 ACL management interface */ 91854ceac45SDavid Howells nfs_init_server_aclclient(server); 91954ceac45SDavid Howells dprintk("<-- nfs_init_server() = 0 [new %p]\n", clp); 92054ceac45SDavid Howells return 0; 92154ceac45SDavid Howells 92254ceac45SDavid Howells error: 92354ceac45SDavid Howells server->nfs_client = NULL; 92454ceac45SDavid Howells nfs_put_client(clp); 92554ceac45SDavid Howells dprintk("<-- nfs_init_server() = xerror %d\n", error); 92654ceac45SDavid Howells return error; 92754ceac45SDavid Howells } 92854ceac45SDavid Howells 92954ceac45SDavid Howells /* 93054ceac45SDavid Howells * Load up the server record from information gained in an fsinfo record 93154ceac45SDavid Howells */ 932738fd0f3SBenny Halevy static void nfs_server_set_fsinfo(struct nfs_server *server, 933738fd0f3SBenny Halevy struct nfs_fh *mntfh, 934738fd0f3SBenny Halevy struct nfs_fsinfo *fsinfo) 93554ceac45SDavid Howells { 93654ceac45SDavid Howells unsigned long max_rpc_payload; 93754ceac45SDavid Howells 93854ceac45SDavid Howells /* Work out a lot of parameters */ 93954ceac45SDavid Howells if (server->rsize == 0) 94054ceac45SDavid Howells server->rsize = nfs_block_size(fsinfo->rtpref, NULL); 94154ceac45SDavid Howells if (server->wsize == 0) 94254ceac45SDavid Howells server->wsize = nfs_block_size(fsinfo->wtpref, NULL); 94354ceac45SDavid Howells 94454ceac45SDavid Howells if (fsinfo->rtmax >= 512 && server->rsize > fsinfo->rtmax) 94554ceac45SDavid Howells server->rsize = nfs_block_size(fsinfo->rtmax, NULL); 94654ceac45SDavid Howells if (fsinfo->wtmax >= 512 && server->wsize > fsinfo->wtmax) 94754ceac45SDavid Howells server->wsize = nfs_block_size(fsinfo->wtmax, NULL); 94854ceac45SDavid Howells 94954ceac45SDavid Howells max_rpc_payload = nfs_block_size(rpc_max_payload(server->client), NULL); 95054ceac45SDavid Howells if (server->rsize > max_rpc_payload) 95154ceac45SDavid Howells server->rsize = max_rpc_payload; 95254ceac45SDavid Howells if (server->rsize > NFS_MAX_FILE_IO_SIZE) 95354ceac45SDavid Howells server->rsize = NFS_MAX_FILE_IO_SIZE; 95454ceac45SDavid Howells server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 955e0bf68ddSPeter Zijlstra 956d993831fSJens Axboe server->backing_dev_info.name = "nfs"; 95754ceac45SDavid Howells server->backing_dev_info.ra_pages = server->rpages * NFS_MAX_READAHEAD; 95854ceac45SDavid Howells 95954ceac45SDavid Howells if (server->wsize > max_rpc_payload) 96054ceac45SDavid Howells server->wsize = max_rpc_payload; 96154ceac45SDavid Howells if (server->wsize > NFS_MAX_FILE_IO_SIZE) 96254ceac45SDavid Howells server->wsize = NFS_MAX_FILE_IO_SIZE; 96354ceac45SDavid Howells server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 964dae100c2SFred Isaman server->pnfs_blksize = fsinfo->blksize; 965738fd0f3SBenny Halevy set_pnfs_layoutdriver(server, mntfh, fsinfo->layouttype); 96685e174baSRicardo Labiaga 96754ceac45SDavid Howells server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL); 96854ceac45SDavid Howells 96954ceac45SDavid Howells server->dtsize = nfs_block_size(fsinfo->dtpref, NULL); 97056e4ebf8SBryan Schumaker if (server->dtsize > PAGE_CACHE_SIZE * NFS_MAX_READDIR_PAGES) 97156e4ebf8SBryan Schumaker server->dtsize = PAGE_CACHE_SIZE * NFS_MAX_READDIR_PAGES; 97254ceac45SDavid Howells if (server->dtsize > server->rsize) 97354ceac45SDavid Howells server->dtsize = server->rsize; 97454ceac45SDavid Howells 97554ceac45SDavid Howells if (server->flags & NFS_MOUNT_NOAC) { 97654ceac45SDavid Howells server->acregmin = server->acregmax = 0; 97754ceac45SDavid Howells server->acdirmin = server->acdirmax = 0; 97854ceac45SDavid Howells } 97954ceac45SDavid Howells 98054ceac45SDavid Howells server->maxfilesize = fsinfo->maxfilesize; 98154ceac45SDavid Howells 9826b96724eSRicardo Labiaga server->time_delta = fsinfo->time_delta; 9836b96724eSRicardo Labiaga 98454ceac45SDavid Howells /* We're airborne Set socket buffersize */ 98554ceac45SDavid Howells rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100); 98654ceac45SDavid Howells } 98754ceac45SDavid Howells 98854ceac45SDavid Howells /* 98954ceac45SDavid Howells * Probe filesystem information, including the FSID on v2/v3 99054ceac45SDavid Howells */ 99154ceac45SDavid Howells static int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr) 99254ceac45SDavid Howells { 99354ceac45SDavid Howells struct nfs_fsinfo fsinfo; 99454ceac45SDavid Howells struct nfs_client *clp = server->nfs_client; 99554ceac45SDavid Howells int error; 99654ceac45SDavid Howells 99754ceac45SDavid Howells dprintk("--> nfs_probe_fsinfo()\n"); 99854ceac45SDavid Howells 99954ceac45SDavid Howells if (clp->rpc_ops->set_capabilities != NULL) { 100054ceac45SDavid Howells error = clp->rpc_ops->set_capabilities(server, mntfh); 100154ceac45SDavid Howells if (error < 0) 100254ceac45SDavid Howells goto out_error; 100354ceac45SDavid Howells } 100454ceac45SDavid Howells 100554ceac45SDavid Howells fsinfo.fattr = fattr; 100685e174baSRicardo Labiaga fsinfo.layouttype = 0; 100754ceac45SDavid Howells error = clp->rpc_ops->fsinfo(server, mntfh, &fsinfo); 100854ceac45SDavid Howells if (error < 0) 100954ceac45SDavid Howells goto out_error; 101054ceac45SDavid Howells 1011738fd0f3SBenny Halevy nfs_server_set_fsinfo(server, mntfh, &fsinfo); 101254ceac45SDavid Howells 101354ceac45SDavid Howells /* Get some general file system info */ 101454ceac45SDavid Howells if (server->namelen == 0) { 101554ceac45SDavid Howells struct nfs_pathconf pathinfo; 101654ceac45SDavid Howells 101754ceac45SDavid Howells pathinfo.fattr = fattr; 101854ceac45SDavid Howells nfs_fattr_init(fattr); 101954ceac45SDavid Howells 102054ceac45SDavid Howells if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0) 102154ceac45SDavid Howells server->namelen = pathinfo.max_namelen; 102254ceac45SDavid Howells } 102354ceac45SDavid Howells 102454ceac45SDavid Howells dprintk("<-- nfs_probe_fsinfo() = 0\n"); 102554ceac45SDavid Howells return 0; 102654ceac45SDavid Howells 102754ceac45SDavid Howells out_error: 102854ceac45SDavid Howells dprintk("nfs_probe_fsinfo: error = %d\n", -error); 102954ceac45SDavid Howells return error; 103054ceac45SDavid Howells } 103154ceac45SDavid Howells 103254ceac45SDavid Howells /* 103354ceac45SDavid Howells * Copy useful information when duplicating a server record 103454ceac45SDavid Howells */ 103554ceac45SDavid Howells static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source) 103654ceac45SDavid Howells { 103754ceac45SDavid Howells target->flags = source->flags; 1038356e76b8SChuck Lever target->rsize = source->rsize; 1039356e76b8SChuck Lever target->wsize = source->wsize; 104054ceac45SDavid Howells target->acregmin = source->acregmin; 104154ceac45SDavid Howells target->acregmax = source->acregmax; 104254ceac45SDavid Howells target->acdirmin = source->acdirmin; 104354ceac45SDavid Howells target->acdirmax = source->acdirmax; 104454ceac45SDavid Howells target->caps = source->caps; 10452df54806SDavid Howells target->options = source->options; 104654ceac45SDavid Howells } 104754ceac45SDavid Howells 1048fca5238eSChuck Lever static void nfs_server_insert_lists(struct nfs_server *server) 1049fca5238eSChuck Lever { 1050fca5238eSChuck Lever struct nfs_client *clp = server->nfs_client; 1051c25d32b2SStanislav Kinsbursky struct nfs_net *nn = net_generic(clp->net, nfs_net_id); 1052fca5238eSChuck Lever 1053dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 1054fca5238eSChuck Lever list_add_tail_rcu(&server->client_link, &clp->cl_superblocks); 1055c25d32b2SStanislav Kinsbursky list_add_tail(&server->master_link, &nn->nfs_volume_list); 1056d3b4c9d7SAndy Adamson clear_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state); 1057dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 1058fca5238eSChuck Lever 1059fca5238eSChuck Lever } 1060fca5238eSChuck Lever 1061fca5238eSChuck Lever static void nfs_server_remove_lists(struct nfs_server *server) 1062fca5238eSChuck Lever { 1063d3b4c9d7SAndy Adamson struct nfs_client *clp = server->nfs_client; 10644c03ae4aSTrond Myklebust struct nfs_net *nn; 1065d3b4c9d7SAndy Adamson 10664c03ae4aSTrond Myklebust if (clp == NULL) 10674c03ae4aSTrond Myklebust return; 10684c03ae4aSTrond Myklebust nn = net_generic(clp->net, nfs_net_id); 1069dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 1070fca5238eSChuck Lever list_del_rcu(&server->client_link); 10714c03ae4aSTrond Myklebust if (list_empty(&clp->cl_superblocks)) 1072d3b4c9d7SAndy Adamson set_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state); 1073fca5238eSChuck Lever list_del(&server->master_link); 1074dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 1075fca5238eSChuck Lever 1076fca5238eSChuck Lever synchronize_rcu(); 1077fca5238eSChuck Lever } 1078fca5238eSChuck Lever 107954ceac45SDavid Howells /* 108054ceac45SDavid Howells * Allocate and initialise a server record 108154ceac45SDavid Howells */ 108254ceac45SDavid Howells static struct nfs_server *nfs_alloc_server(void) 108354ceac45SDavid Howells { 108454ceac45SDavid Howells struct nfs_server *server; 108554ceac45SDavid Howells 108654ceac45SDavid Howells server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL); 108754ceac45SDavid Howells if (!server) 108854ceac45SDavid Howells return NULL; 108954ceac45SDavid Howells 109054ceac45SDavid Howells server->client = server->client_acl = ERR_PTR(-EINVAL); 109154ceac45SDavid Howells 109254ceac45SDavid Howells /* Zero out the NFS state stuff */ 109354ceac45SDavid Howells INIT_LIST_HEAD(&server->client_link); 109454ceac45SDavid Howells INIT_LIST_HEAD(&server->master_link); 1095d3978bb3SChuck Lever INIT_LIST_HEAD(&server->delegations); 10966382a441SWeston Andros Adamson INIT_LIST_HEAD(&server->layouts); 10970aaaf5c4SChuck Lever INIT_LIST_HEAD(&server->state_owners_lru); 109854ceac45SDavid Howells 1099ef818a28SSteve Dickson atomic_set(&server->active, 0); 1100ef818a28SSteve Dickson 110154ceac45SDavid Howells server->io_stats = nfs_alloc_iostats(); 110254ceac45SDavid Howells if (!server->io_stats) { 110354ceac45SDavid Howells kfree(server); 110454ceac45SDavid Howells return NULL; 110554ceac45SDavid Howells } 110654ceac45SDavid Howells 110748d07649SJens Axboe if (bdi_init(&server->backing_dev_info)) { 110848d07649SJens Axboe nfs_free_iostats(server->io_stats); 110948d07649SJens Axboe kfree(server); 111048d07649SJens Axboe return NULL; 111148d07649SJens Axboe } 111248d07649SJens Axboe 11139157c31dSTrond Myklebust ida_init(&server->openowner_id); 1114d2d7ce28STrond Myklebust ida_init(&server->lockowner_id); 1115f7e8917aSFred Isaman pnfs_init_server(server); 1116f7e8917aSFred Isaman 111754ceac45SDavid Howells return server; 111854ceac45SDavid Howells } 111954ceac45SDavid Howells 112054ceac45SDavid Howells /* 112154ceac45SDavid Howells * Free up a server record 112254ceac45SDavid Howells */ 112354ceac45SDavid Howells void nfs_free_server(struct nfs_server *server) 112454ceac45SDavid Howells { 112554ceac45SDavid Howells dprintk("--> nfs_free_server()\n"); 112654ceac45SDavid Howells 1127fca5238eSChuck Lever nfs_server_remove_lists(server); 112885e174baSRicardo Labiaga unset_pnfs_layoutdriver(server); 112954ceac45SDavid Howells 113054ceac45SDavid Howells if (server->destroy != NULL) 113154ceac45SDavid Howells server->destroy(server); 11325cef338bSTrond Myklebust 11335cef338bSTrond Myklebust if (!IS_ERR(server->client_acl)) 11345cef338bSTrond Myklebust rpc_shutdown_client(server->client_acl); 113554ceac45SDavid Howells if (!IS_ERR(server->client)) 113654ceac45SDavid Howells rpc_shutdown_client(server->client); 113754ceac45SDavid Howells 113854ceac45SDavid Howells nfs_put_client(server->nfs_client); 113954ceac45SDavid Howells 1140d2d7ce28STrond Myklebust ida_destroy(&server->lockowner_id); 11419157c31dSTrond Myklebust ida_destroy(&server->openowner_id); 114254ceac45SDavid Howells nfs_free_iostats(server->io_stats); 1143e0bf68ddSPeter Zijlstra bdi_destroy(&server->backing_dev_info); 114454ceac45SDavid Howells kfree(server); 114554ceac45SDavid Howells nfs_release_automount_timer(); 114654ceac45SDavid Howells dprintk("<-- nfs_free_server()\n"); 114754ceac45SDavid Howells } 114854ceac45SDavid Howells 114954ceac45SDavid Howells /* 115054ceac45SDavid Howells * Create a version 2 or 3 volume record 115154ceac45SDavid Howells * - keyed on server and FSID 115254ceac45SDavid Howells */ 11532283f8d6S\"Talpey, Thomas\ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, 115454ceac45SDavid Howells struct nfs_fh *mntfh) 115554ceac45SDavid Howells { 115654ceac45SDavid Howells struct nfs_server *server; 1157fbca779aSTrond Myklebust struct nfs_fattr *fattr; 115854ceac45SDavid Howells int error; 115954ceac45SDavid Howells 116054ceac45SDavid Howells server = nfs_alloc_server(); 116154ceac45SDavid Howells if (!server) 116254ceac45SDavid Howells return ERR_PTR(-ENOMEM); 116354ceac45SDavid Howells 1164fbca779aSTrond Myklebust error = -ENOMEM; 1165fbca779aSTrond Myklebust fattr = nfs_alloc_fattr(); 1166fbca779aSTrond Myklebust if (fattr == NULL) 1167fbca779aSTrond Myklebust goto error; 1168fbca779aSTrond Myklebust 116954ceac45SDavid Howells /* Get a client representation */ 117054ceac45SDavid Howells error = nfs_init_server(server, data); 117154ceac45SDavid Howells if (error < 0) 117254ceac45SDavid Howells goto error; 117354ceac45SDavid Howells 117454ceac45SDavid Howells BUG_ON(!server->nfs_client); 117554ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops); 117654ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 117754ceac45SDavid Howells 117854ceac45SDavid Howells /* Probe the root fh to retrieve its FSID */ 1179fbca779aSTrond Myklebust error = nfs_probe_fsinfo(server, mntfh, fattr); 118054ceac45SDavid Howells if (error < 0) 118154ceac45SDavid Howells goto error; 118254af3bb5STrond Myklebust if (server->nfs_client->rpc_ops->version == 3) { 118354af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) 118454af3bb5STrond Myklebust server->namelen = NFS3_MAXNAMLEN; 118554af3bb5STrond Myklebust if (!(data->flags & NFS_MOUNT_NORDIRPLUS)) 118654af3bb5STrond Myklebust server->caps |= NFS_CAP_READDIRPLUS; 118754af3bb5STrond Myklebust } else { 118854af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN) 118954af3bb5STrond Myklebust server->namelen = NFS2_MAXNAMLEN; 119054af3bb5STrond Myklebust } 119154af3bb5STrond Myklebust 1192fbca779aSTrond Myklebust if (!(fattr->valid & NFS_ATTR_FATTR)) { 1193fbca779aSTrond Myklebust error = server->nfs_client->rpc_ops->getattr(server, mntfh, fattr); 119454ceac45SDavid Howells if (error < 0) { 119554ceac45SDavid Howells dprintk("nfs_create_server: getattr error = %d\n", -error); 119654ceac45SDavid Howells goto error; 119754ceac45SDavid Howells } 119854ceac45SDavid Howells } 1199fbca779aSTrond Myklebust memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid)); 120054ceac45SDavid Howells 12016daabf1bSDavid Howells dprintk("Server FSID: %llx:%llx\n", 12026daabf1bSDavid Howells (unsigned long long) server->fsid.major, 12036daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 120454ceac45SDavid Howells 1205fca5238eSChuck Lever nfs_server_insert_lists(server); 120654ceac45SDavid Howells server->mount_time = jiffies; 1207fbca779aSTrond Myklebust nfs_free_fattr(fattr); 120854ceac45SDavid Howells return server; 120954ceac45SDavid Howells 121054ceac45SDavid Howells error: 1211fbca779aSTrond Myklebust nfs_free_fattr(fattr); 121254ceac45SDavid Howells nfs_free_server(server); 121354ceac45SDavid Howells return ERR_PTR(error); 121454ceac45SDavid Howells } 121554ceac45SDavid Howells 121654ceac45SDavid Howells #ifdef CONFIG_NFS_V4 121754ceac45SDavid Howells /* 1218c36fca52SAndy Adamson * NFSv4.0 callback thread helper 1219c36fca52SAndy Adamson * 1220c36fca52SAndy Adamson * Find a client by callback identifier 1221c36fca52SAndy Adamson */ 1222c36fca52SAndy Adamson struct nfs_client * 122328cd1b3fSStanislav Kinsbursky nfs4_find_client_ident(struct net *net, int cb_ident) 1224c36fca52SAndy Adamson { 1225c36fca52SAndy Adamson struct nfs_client *clp; 122628cd1b3fSStanislav Kinsbursky struct nfs_net *nn = net_generic(net, nfs_net_id); 1227c36fca52SAndy Adamson 1228dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 122928cd1b3fSStanislav Kinsbursky clp = idr_find(&nn->cb_ident_idr, cb_ident); 1230c36fca52SAndy Adamson if (clp) 1231c36fca52SAndy Adamson atomic_inc(&clp->cl_count); 1232dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 1233c36fca52SAndy Adamson return clp; 1234c36fca52SAndy Adamson } 1235c36fca52SAndy Adamson 1236c36fca52SAndy Adamson #if defined(CONFIG_NFS_V4_1) 1237c36fca52SAndy Adamson /* 1238c36fca52SAndy Adamson * NFSv4.1 callback thread helper 1239c36fca52SAndy Adamson * For CB_COMPOUND calls, find a client by IP address, protocol version, 1240c36fca52SAndy Adamson * minorversion, and sessionID 1241c36fca52SAndy Adamson * 1242c36fca52SAndy Adamson * Returns NULL if no such client 1243c36fca52SAndy Adamson */ 1244c36fca52SAndy Adamson struct nfs_client * 1245c7add9a9SStanislav Kinsbursky nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr, 1246778be232SAndy Adamson struct nfs4_sessionid *sid) 1247c36fca52SAndy Adamson { 1248c36fca52SAndy Adamson struct nfs_client *clp; 1249c7add9a9SStanislav Kinsbursky struct nfs_net *nn = net_generic(net, nfs_net_id); 1250c36fca52SAndy Adamson 1251dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 12526b13168bSStanislav Kinsbursky list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) { 1253c36fca52SAndy Adamson if (nfs4_cb_match_client(addr, clp, 1) == false) 1254c36fca52SAndy Adamson continue; 1255c36fca52SAndy Adamson 1256c36fca52SAndy Adamson if (!nfs4_has_session(clp)) 1257c36fca52SAndy Adamson continue; 1258c36fca52SAndy Adamson 1259778be232SAndy Adamson /* Match sessionid*/ 1260778be232SAndy Adamson if (memcmp(clp->cl_session->sess_id.data, 1261778be232SAndy Adamson sid->data, NFS4_MAX_SESSIONID_LEN) != 0) 1262c36fca52SAndy Adamson continue; 1263c36fca52SAndy Adamson 1264c36fca52SAndy Adamson atomic_inc(&clp->cl_count); 1265dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 1266c36fca52SAndy Adamson return clp; 1267c36fca52SAndy Adamson } 1268dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 1269c36fca52SAndy Adamson return NULL; 1270c36fca52SAndy Adamson } 1271c36fca52SAndy Adamson 1272c36fca52SAndy Adamson #else /* CONFIG_NFS_V4_1 */ 1273c36fca52SAndy Adamson 1274c36fca52SAndy Adamson struct nfs_client * 1275b6d1e83bSStanislav Kinsbursky nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr, 1276778be232SAndy Adamson struct nfs4_sessionid *sid) 1277c36fca52SAndy Adamson { 1278c36fca52SAndy Adamson return NULL; 1279c36fca52SAndy Adamson } 1280c36fca52SAndy Adamson #endif /* CONFIG_NFS_V4_1 */ 1281c36fca52SAndy Adamson 1282c36fca52SAndy Adamson /* 12839bdaa86dSBenny Halevy * Initialize the NFS4 callback service 12849bdaa86dSBenny Halevy */ 12859bdaa86dSBenny Halevy static int nfs4_init_callback(struct nfs_client *clp) 12869bdaa86dSBenny Halevy { 12879bdaa86dSBenny Halevy int error; 12889bdaa86dSBenny Halevy 12899bdaa86dSBenny Halevy if (clp->rpc_ops->version == 4) { 12902446ab60STrond Myklebust struct rpc_xprt *xprt; 12912446ab60STrond Myklebust 12922446ab60STrond Myklebust xprt = rcu_dereference_raw(clp->cl_rpcclient->cl_xprt); 12932446ab60STrond Myklebust 12940b5b7ae0SAndy Adamson if (nfs4_has_session(clp)) { 12952446ab60STrond Myklebust error = xprt_setup_backchannel(xprt, 12960b5b7ae0SAndy Adamson NFS41_BC_MIN_CALLBACKS); 12970b5b7ae0SAndy Adamson if (error < 0) 12980b5b7ae0SAndy Adamson return error; 12990b5b7ae0SAndy Adamson } 13000b5b7ae0SAndy Adamson 13012446ab60STrond Myklebust error = nfs_callback_up(clp->cl_mvops->minor_version, xprt); 13029bdaa86dSBenny Halevy if (error < 0) { 13039bdaa86dSBenny Halevy dprintk("%s: failed to start callback. Error = %d\n", 13049bdaa86dSBenny Halevy __func__, error); 13059bdaa86dSBenny Halevy return error; 13069bdaa86dSBenny Halevy } 13079bdaa86dSBenny Halevy __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state); 13089bdaa86dSBenny Halevy } 13099bdaa86dSBenny Halevy return 0; 13109bdaa86dSBenny Halevy } 13119bdaa86dSBenny Halevy 13129bdaa86dSBenny Halevy /* 1313557134a3SAndy Adamson * Initialize the minor version specific parts of an NFS4 client record 1314557134a3SAndy Adamson */ 1315557134a3SAndy Adamson static int nfs4_init_client_minor_version(struct nfs_client *clp) 1316557134a3SAndy Adamson { 1317557134a3SAndy Adamson #if defined(CONFIG_NFS_V4_1) 131897dc1359STrond Myklebust if (clp->cl_mvops->minor_version) { 1319557134a3SAndy Adamson struct nfs4_session *session = NULL; 1320557134a3SAndy Adamson /* 1321557134a3SAndy Adamson * Create the session and mark it expired. 1322557134a3SAndy Adamson * When a SEQUENCE operation encounters the expired session 1323557134a3SAndy Adamson * it will do session recovery to initialize it. 1324557134a3SAndy Adamson */ 1325557134a3SAndy Adamson session = nfs4_alloc_session(clp); 1326557134a3SAndy Adamson if (!session) 1327557134a3SAndy Adamson return -ENOMEM; 1328557134a3SAndy Adamson 1329557134a3SAndy Adamson clp->cl_session = session; 1330fe74ba3aSTrond Myklebust /* 1331fe74ba3aSTrond Myklebust * The create session reply races with the server back 1332fe74ba3aSTrond Myklebust * channel probe. Mark the client NFS_CS_SESSION_INITING 1333fe74ba3aSTrond Myklebust * so that the client back channel can find the 1334fe74ba3aSTrond Myklebust * nfs_client struct 1335fe74ba3aSTrond Myklebust */ 1336fe74ba3aSTrond Myklebust clp->cl_cons_state = NFS_CS_SESSION_INITING; 1337557134a3SAndy Adamson } 1338557134a3SAndy Adamson #endif /* CONFIG_NFS_V4_1 */ 1339557134a3SAndy Adamson 134071468513SBenny Halevy return nfs4_init_callback(clp); 1341557134a3SAndy Adamson } 1342557134a3SAndy Adamson 1343557134a3SAndy Adamson /* 134454ceac45SDavid Howells * Initialise an NFS4 client record 134554ceac45SDavid Howells */ 134645a52a02SAndy Adamson int nfs4_init_client(struct nfs_client *clp, 134733170233STrond Myklebust const struct rpc_timeout *timeparms, 13487d9ac06fSJ. Bruce Fields const char *ip_addr, 1349d740351bSChuck Lever rpc_authflavor_t authflavour, 135045a52a02SAndy Adamson int noresvport) 135154ceac45SDavid Howells { 135231b8e2aeSChuck Lever char buf[INET6_ADDRSTRLEN + 1]; 135354ceac45SDavid Howells int error; 135454ceac45SDavid Howells 135554ceac45SDavid Howells if (clp->cl_cons_state == NFS_CS_READY) { 135654ceac45SDavid Howells /* the client is initialised already */ 135754ceac45SDavid Howells dprintk("<-- nfs4_init_client() = 0 [already %p]\n", clp); 135854ceac45SDavid Howells return 0; 135954ceac45SDavid Howells } 136054ceac45SDavid Howells 136154ceac45SDavid Howells /* Check NFS protocol revision and initialize RPC op vector */ 136254ceac45SDavid Howells clp->rpc_ops = &nfs_v4_clientops; 136354ceac45SDavid Howells 136459dca3b2STrond Myklebust error = nfs_create_rpc_client(clp, timeparms, authflavour, 136545a52a02SAndy Adamson 1, noresvport); 136654ceac45SDavid Howells if (error < 0) 136754ceac45SDavid Howells goto error; 136831b8e2aeSChuck Lever 136931b8e2aeSChuck Lever /* If no clientaddr= option was specified, find a usable cb address */ 137031b8e2aeSChuck Lever if (ip_addr == NULL) { 137131b8e2aeSChuck Lever struct sockaddr_storage cb_addr; 137231b8e2aeSChuck Lever struct sockaddr *sap = (struct sockaddr *)&cb_addr; 137331b8e2aeSChuck Lever 137431b8e2aeSChuck Lever error = rpc_localaddr(clp->cl_rpcclient, sap, sizeof(cb_addr)); 137531b8e2aeSChuck Lever if (error < 0) 137631b8e2aeSChuck Lever goto error; 137731b8e2aeSChuck Lever error = rpc_ntop(sap, buf, sizeof(buf)); 137831b8e2aeSChuck Lever if (error < 0) 137931b8e2aeSChuck Lever goto error; 138031b8e2aeSChuck Lever ip_addr = (const char *)buf; 138131b8e2aeSChuck Lever } 1382f4373bf9SBen Hutchings strlcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr)); 138354ceac45SDavid Howells 138454ceac45SDavid Howells error = nfs_idmap_new(clp); 138554ceac45SDavid Howells if (error < 0) { 138654ceac45SDavid Howells dprintk("%s: failed to create idmapper. Error = %d\n", 13873110ff80SHarvey Harrison __func__, error); 138854ceac45SDavid Howells goto error; 138954ceac45SDavid Howells } 13909c5bf38dSTrond Myklebust __set_bit(NFS_CS_IDMAP, &clp->cl_res_state); 139154ceac45SDavid Howells 1392557134a3SAndy Adamson error = nfs4_init_client_minor_version(clp); 1393557134a3SAndy Adamson if (error < 0) 1394557134a3SAndy Adamson goto error; 1395557134a3SAndy Adamson 139676db6d95SAndy Adamson if (!nfs4_has_session(clp)) 139754ceac45SDavid Howells nfs_mark_client_ready(clp, NFS_CS_READY); 139854ceac45SDavid Howells return 0; 139954ceac45SDavid Howells 140054ceac45SDavid Howells error: 140154ceac45SDavid Howells nfs_mark_client_ready(clp, error); 140254ceac45SDavid Howells dprintk("<-- nfs4_init_client() = xerror %d\n", error); 140354ceac45SDavid Howells return error; 140454ceac45SDavid Howells } 140554ceac45SDavid Howells 140654ceac45SDavid Howells /* 140754ceac45SDavid Howells * Set up an NFS4 client 140854ceac45SDavid Howells */ 140954ceac45SDavid Howells static int nfs4_set_client(struct nfs_server *server, 1410dcecae0fSChuck Lever const char *hostname, 1411dcecae0fSChuck Lever const struct sockaddr *addr, 1412dcecae0fSChuck Lever const size_t addrlen, 14137d9ac06fSJ. Bruce Fields const char *ip_addr, 141454ceac45SDavid Howells rpc_authflavor_t authflavour, 141594a417f3SBenny Halevy int proto, const struct rpc_timeout *timeparms, 1416e50a7a1aSStanislav Kinsbursky u32 minorversion, struct net *net) 141754ceac45SDavid Howells { 14183a498026STrond Myklebust struct nfs_client_initdata cl_init = { 14193a498026STrond Myklebust .hostname = hostname, 1420dcecae0fSChuck Lever .addr = addr, 1421dcecae0fSChuck Lever .addrlen = addrlen, 142240c55319STrond Myklebust .rpc_ops = &nfs_v4_clientops, 142359dca3b2STrond Myklebust .proto = proto, 14245aae4a9aSBenny Halevy .minorversion = minorversion, 1425e50a7a1aSStanislav Kinsbursky .net = net, 14263a498026STrond Myklebust }; 142754ceac45SDavid Howells struct nfs_client *clp; 142854ceac45SDavid Howells int error; 142954ceac45SDavid Howells 143054ceac45SDavid Howells dprintk("--> nfs4_set_client()\n"); 143154ceac45SDavid Howells 143254ceac45SDavid Howells /* Allocate or find a client reference we can use */ 143345a52a02SAndy Adamson clp = nfs_get_client(&cl_init, timeparms, ip_addr, authflavour, 143445a52a02SAndy Adamson server->flags & NFS_MOUNT_NORESVPORT); 143554ceac45SDavid Howells if (IS_ERR(clp)) { 143654ceac45SDavid Howells error = PTR_ERR(clp); 143754ceac45SDavid Howells goto error; 143854ceac45SDavid Howells } 143954ceac45SDavid Howells 1440d6fb79d4SAndy Adamson /* 1441d6fb79d4SAndy Adamson * Query for the lease time on clientid setup or renewal 1442d6fb79d4SAndy Adamson * 1443d6fb79d4SAndy Adamson * Note that this will be set on nfs_clients that were created 1444d6fb79d4SAndy Adamson * only for the DS role and did not set this bit, but now will 1445d6fb79d4SAndy Adamson * serve a dual role. 1446d6fb79d4SAndy Adamson */ 1447d6fb79d4SAndy Adamson set_bit(NFS_CS_CHECK_LEASE_TIME, &clp->cl_res_state); 1448d6fb79d4SAndy Adamson 144954ceac45SDavid Howells server->nfs_client = clp; 145054ceac45SDavid Howells dprintk("<-- nfs4_set_client() = 0 [new %p]\n", clp); 145154ceac45SDavid Howells return 0; 145254ceac45SDavid Howells error: 145354ceac45SDavid Howells dprintk("<-- nfs4_set_client() = xerror %d\n", error); 145454ceac45SDavid Howells return error; 145554ceac45SDavid Howells } 145654ceac45SDavid Howells 1457d83217c1SAndy Adamson /* 1458d83217c1SAndy Adamson * Set up a pNFS Data Server client. 1459d83217c1SAndy Adamson * 1460d83217c1SAndy Adamson * Return any existing nfs_client that matches server address,port,version 1461d83217c1SAndy Adamson * and minorversion. 1462d83217c1SAndy Adamson * 1463d83217c1SAndy Adamson * For a new nfs_client, use a soft mount (default), a low retrans and a 1464d83217c1SAndy Adamson * low timeout interval so that if a connection is lost, we retry through 1465d83217c1SAndy Adamson * the MDS. 1466d83217c1SAndy Adamson */ 1467d83217c1SAndy Adamson struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp, 1468d83217c1SAndy Adamson const struct sockaddr *ds_addr, 1469d83217c1SAndy Adamson int ds_addrlen, int ds_proto) 1470d83217c1SAndy Adamson { 1471d83217c1SAndy Adamson struct nfs_client_initdata cl_init = { 1472d83217c1SAndy Adamson .addr = ds_addr, 1473d83217c1SAndy Adamson .addrlen = ds_addrlen, 1474d83217c1SAndy Adamson .rpc_ops = &nfs_v4_clientops, 1475d83217c1SAndy Adamson .proto = ds_proto, 1476d83217c1SAndy Adamson .minorversion = mds_clp->cl_minorversion, 1477e50a7a1aSStanislav Kinsbursky .net = mds_clp->net, 1478d83217c1SAndy Adamson }; 1479d83217c1SAndy Adamson struct rpc_timeout ds_timeout = { 1480d83217c1SAndy Adamson .to_initval = 15 * HZ, 1481d83217c1SAndy Adamson .to_maxval = 15 * HZ, 1482d83217c1SAndy Adamson .to_retries = 1, 1483d83217c1SAndy Adamson .to_exponential = 1, 1484d83217c1SAndy Adamson }; 1485d83217c1SAndy Adamson struct nfs_client *clp; 1486d83217c1SAndy Adamson 1487d83217c1SAndy Adamson /* 1488d83217c1SAndy Adamson * Set an authflavor equual to the MDS value. Use the MDS nfs_client 1489d83217c1SAndy Adamson * cl_ipaddr so as to use the same EXCHANGE_ID co_ownerid as the MDS 1490d83217c1SAndy Adamson * (section 13.1 RFC 5661). 1491d83217c1SAndy Adamson */ 1492d83217c1SAndy Adamson clp = nfs_get_client(&cl_init, &ds_timeout, mds_clp->cl_ipaddr, 1493d83217c1SAndy Adamson mds_clp->cl_rpcclient->cl_auth->au_flavor, 0); 1494d83217c1SAndy Adamson 1495d83217c1SAndy Adamson dprintk("<-- %s %p\n", __func__, clp); 1496d83217c1SAndy Adamson return clp; 1497d83217c1SAndy Adamson } 149894b134acSTrond Myklebust EXPORT_SYMBOL_GPL(nfs4_set_ds_client); 1499557134a3SAndy Adamson 1500557134a3SAndy Adamson /* 150196b09e02SAndy Adamson * Session has been established, and the client marked ready. 150296b09e02SAndy Adamson * Set the mount rsize and wsize with negotiated fore channel 150396b09e02SAndy Adamson * attributes which will be bound checked in nfs_server_set_fsinfo. 150496b09e02SAndy Adamson */ 150596b09e02SAndy Adamson static void nfs4_session_set_rwsize(struct nfs_server *server) 150696b09e02SAndy Adamson { 150796b09e02SAndy Adamson #ifdef CONFIG_NFS_V4_1 15082449ea2eSAlexandros Batsakis struct nfs4_session *sess; 15092449ea2eSAlexandros Batsakis u32 server_resp_sz; 15102449ea2eSAlexandros Batsakis u32 server_rqst_sz; 15112449ea2eSAlexandros Batsakis 151296b09e02SAndy Adamson if (!nfs4_has_session(server->nfs_client)) 151396b09e02SAndy Adamson return; 15142449ea2eSAlexandros Batsakis sess = server->nfs_client->cl_session; 15152449ea2eSAlexandros Batsakis server_resp_sz = sess->fc_attrs.max_resp_sz - nfs41_maxread_overhead; 15162449ea2eSAlexandros Batsakis server_rqst_sz = sess->fc_attrs.max_rqst_sz - nfs41_maxwrite_overhead; 15172449ea2eSAlexandros Batsakis 15182449ea2eSAlexandros Batsakis if (server->rsize > server_resp_sz) 15192449ea2eSAlexandros Batsakis server->rsize = server_resp_sz; 15202449ea2eSAlexandros Batsakis if (server->wsize > server_rqst_sz) 15212449ea2eSAlexandros Batsakis server->wsize = server_rqst_sz; 152296b09e02SAndy Adamson #endif /* CONFIG_NFS_V4_1 */ 152396b09e02SAndy Adamson } 152496b09e02SAndy Adamson 152544950b67STrond Myklebust static int nfs4_server_common_setup(struct nfs_server *server, 152644950b67STrond Myklebust struct nfs_fh *mntfh) 152744950b67STrond Myklebust { 152844950b67STrond Myklebust struct nfs_fattr *fattr; 152944950b67STrond Myklebust int error; 153044950b67STrond Myklebust 153144950b67STrond Myklebust BUG_ON(!server->nfs_client); 153244950b67STrond Myklebust BUG_ON(!server->nfs_client->rpc_ops); 153344950b67STrond Myklebust BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 153444950b67STrond Myklebust 153594de8b27SAndy Adamson /* data servers support only a subset of NFSv4.1 */ 153694de8b27SAndy Adamson if (is_ds_only_client(server->nfs_client)) 153794de8b27SAndy Adamson return -EPROTONOSUPPORT; 153894de8b27SAndy Adamson 153944950b67STrond Myklebust fattr = nfs_alloc_fattr(); 154044950b67STrond Myklebust if (fattr == NULL) 154144950b67STrond Myklebust return -ENOMEM; 154244950b67STrond Myklebust 154344950b67STrond Myklebust /* We must ensure the session is initialised first */ 154444950b67STrond Myklebust error = nfs4_init_session(server); 154544950b67STrond Myklebust if (error < 0) 154644950b67STrond Myklebust goto out; 154744950b67STrond Myklebust 154844950b67STrond Myklebust /* Probe the root fh to retrieve its FSID and filehandle */ 154944950b67STrond Myklebust error = nfs4_get_rootfh(server, mntfh); 155044950b67STrond Myklebust if (error < 0) 155144950b67STrond Myklebust goto out; 155244950b67STrond Myklebust 155344950b67STrond Myklebust dprintk("Server FSID: %llx:%llx\n", 155444950b67STrond Myklebust (unsigned long long) server->fsid.major, 155544950b67STrond Myklebust (unsigned long long) server->fsid.minor); 155644950b67STrond Myklebust dprintk("Mount FH: %d\n", mntfh->size); 155744950b67STrond Myklebust 155844950b67STrond Myklebust nfs4_session_set_rwsize(server); 155944950b67STrond Myklebust 156044950b67STrond Myklebust error = nfs_probe_fsinfo(server, mntfh, fattr); 156144950b67STrond Myklebust if (error < 0) 156244950b67STrond Myklebust goto out; 156344950b67STrond Myklebust 156444950b67STrond Myklebust if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 156544950b67STrond Myklebust server->namelen = NFS4_MAXNAMLEN; 156644950b67STrond Myklebust 1567fca5238eSChuck Lever nfs_server_insert_lists(server); 156844950b67STrond Myklebust server->mount_time = jiffies; 15690aaaf5c4SChuck Lever server->destroy = nfs4_destroy_server; 157044950b67STrond Myklebust out: 157144950b67STrond Myklebust nfs_free_fattr(fattr); 157244950b67STrond Myklebust return error; 157344950b67STrond Myklebust } 157444950b67STrond Myklebust 157596b09e02SAndy Adamson /* 157654ceac45SDavid Howells * Create a version 4 volume record 157754ceac45SDavid Howells */ 157854ceac45SDavid Howells static int nfs4_init_server(struct nfs_server *server, 157991ea40b9S\"Talpey, Thomas\ const struct nfs_parsed_mount_data *data) 158054ceac45SDavid Howells { 158133170233STrond Myklebust struct rpc_timeout timeparms; 158254ceac45SDavid Howells int error; 158354ceac45SDavid Howells 158454ceac45SDavid Howells dprintk("--> nfs4_init_server()\n"); 158554ceac45SDavid Howells 158633170233STrond Myklebust nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, 158733170233STrond Myklebust data->timeo, data->retrans); 158833170233STrond Myklebust 1589542fcc33SChuck Lever /* Initialise the client representation from the mount data */ 1590542fcc33SChuck Lever server->flags = data->flags; 159182f2e547SBryan Schumaker server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR|NFS_CAP_POSIX_LOCK; 159282f2e547SBryan Schumaker if (!(data->flags & NFS_MOUNT_NORDIRPLUS)) 159382f2e547SBryan Schumaker server->caps |= NFS_CAP_READDIRPLUS; 1594b797cac7SDavid Howells server->options = data->options; 1595542fcc33SChuck Lever 159633170233STrond Myklebust /* Get a client record */ 159733170233STrond Myklebust error = nfs4_set_client(server, 159833170233STrond Myklebust data->nfs_server.hostname, 159933170233STrond Myklebust (const struct sockaddr *)&data->nfs_server.address, 160033170233STrond Myklebust data->nfs_server.addrlen, 160133170233STrond Myklebust data->client_address, 160233170233STrond Myklebust data->auth_flavors[0], 160333170233STrond Myklebust data->nfs_server.protocol, 160494a417f3SBenny Halevy &timeparms, 1605e50a7a1aSStanislav Kinsbursky data->minorversion, 1606e50a7a1aSStanislav Kinsbursky data->net); 160733170233STrond Myklebust if (error < 0) 160833170233STrond Myklebust goto error; 160933170233STrond Myklebust 1610b064eca2STrond Myklebust /* 1611b064eca2STrond Myklebust * Don't use NFS uid/gid mapping if we're using AUTH_SYS or lower 1612b064eca2STrond Myklebust * authentication. 1613b064eca2STrond Myklebust */ 1614b064eca2STrond Myklebust if (nfs4_disable_idmapping && data->auth_flavors[0] == RPC_AUTH_UNIX) 1615b064eca2STrond Myklebust server->caps |= NFS_CAP_UIDGID_NOMAP; 1616b064eca2STrond Myklebust 161754ceac45SDavid Howells if (data->rsize) 161854ceac45SDavid Howells server->rsize = nfs_block_size(data->rsize, NULL); 161954ceac45SDavid Howells if (data->wsize) 162054ceac45SDavid Howells server->wsize = nfs_block_size(data->wsize, NULL); 162154ceac45SDavid Howells 162254ceac45SDavid Howells server->acregmin = data->acregmin * HZ; 162354ceac45SDavid Howells server->acregmax = data->acregmax * HZ; 162454ceac45SDavid Howells server->acdirmin = data->acdirmin * HZ; 162554ceac45SDavid Howells server->acdirmax = data->acdirmax * HZ; 162654ceac45SDavid Howells 1627f22d6d79SChuck Lever server->port = data->nfs_server.port; 1628f22d6d79SChuck Lever 162933170233STrond Myklebust error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]); 163054ceac45SDavid Howells 163133170233STrond Myklebust error: 163254ceac45SDavid Howells /* Done */ 163354ceac45SDavid Howells dprintk("<-- nfs4_init_server() = %d\n", error); 163454ceac45SDavid Howells return error; 163554ceac45SDavid Howells } 163654ceac45SDavid Howells 163754ceac45SDavid Howells /* 163854ceac45SDavid Howells * Create a version 4 volume record 163954ceac45SDavid Howells * - keyed on server and FSID 164054ceac45SDavid Howells */ 164191ea40b9S\"Talpey, Thomas\ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, 164254ceac45SDavid Howells struct nfs_fh *mntfh) 164354ceac45SDavid Howells { 164454ceac45SDavid Howells struct nfs_server *server; 164554ceac45SDavid Howells int error; 164654ceac45SDavid Howells 164754ceac45SDavid Howells dprintk("--> nfs4_create_server()\n"); 164854ceac45SDavid Howells 164954ceac45SDavid Howells server = nfs_alloc_server(); 165054ceac45SDavid Howells if (!server) 165154ceac45SDavid Howells return ERR_PTR(-ENOMEM); 165254ceac45SDavid Howells 165354ceac45SDavid Howells /* set up the general RPC client */ 165491ea40b9S\"Talpey, Thomas\ error = nfs4_init_server(server, data); 165554ceac45SDavid Howells if (error < 0) 165654ceac45SDavid Howells goto error; 165754ceac45SDavid Howells 165844950b67STrond Myklebust error = nfs4_server_common_setup(server, mntfh); 1659fccba804STrond Myklebust if (error < 0) 1660fccba804STrond Myklebust goto error; 1661557134a3SAndy Adamson 166254ceac45SDavid Howells dprintk("<-- nfs4_create_server() = %p\n", server); 166354ceac45SDavid Howells return server; 166454ceac45SDavid Howells 166554ceac45SDavid Howells error: 166654ceac45SDavid Howells nfs_free_server(server); 166754ceac45SDavid Howells dprintk("<-- nfs4_create_server() = error %d\n", error); 166854ceac45SDavid Howells return ERR_PTR(error); 166954ceac45SDavid Howells } 167054ceac45SDavid Howells 167154ceac45SDavid Howells /* 167254ceac45SDavid Howells * Create an NFS4 referral server record 167354ceac45SDavid Howells */ 167454ceac45SDavid Howells struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, 1675f2d0d85eSTrond Myklebust struct nfs_fh *mntfh) 167654ceac45SDavid Howells { 167754ceac45SDavid Howells struct nfs_client *parent_client; 167854ceac45SDavid Howells struct nfs_server *server, *parent_server; 167954ceac45SDavid Howells int error; 168054ceac45SDavid Howells 168154ceac45SDavid Howells dprintk("--> nfs4_create_referral_server()\n"); 168254ceac45SDavid Howells 168354ceac45SDavid Howells server = nfs_alloc_server(); 168454ceac45SDavid Howells if (!server) 168554ceac45SDavid Howells return ERR_PTR(-ENOMEM); 168654ceac45SDavid Howells 168754ceac45SDavid Howells parent_server = NFS_SB(data->sb); 168854ceac45SDavid Howells parent_client = parent_server->nfs_client; 168954ceac45SDavid Howells 1690542fcc33SChuck Lever /* Initialise the client representation from the parent server */ 1691542fcc33SChuck Lever nfs_server_copy_userdata(server, parent_server); 169262ab460cSTrond Myklebust server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR; 1693542fcc33SChuck Lever 169454ceac45SDavid Howells /* Get a client representation. 169554ceac45SDavid Howells * Note: NFSv4 always uses TCP, */ 1696dcecae0fSChuck Lever error = nfs4_set_client(server, data->hostname, 16976677d095SChuck Lever data->addr, 16986677d095SChuck Lever data->addrlen, 16997d9ac06fSJ. Bruce Fields parent_client->cl_ipaddr, 170054ceac45SDavid Howells data->authflavor, 17012446ab60STrond Myklebust rpc_protocol(parent_server->client), 170294a417f3SBenny Halevy parent_server->client->cl_timeout, 1703e50a7a1aSStanislav Kinsbursky parent_client->cl_mvops->minor_version, 1704e50a7a1aSStanislav Kinsbursky parent_client->net); 1705297de4f6Sandros@citi.umich.edu if (error < 0) 1706297de4f6Sandros@citi.umich.edu goto error; 170754ceac45SDavid Howells 170833170233STrond Myklebust error = nfs_init_server_rpcclient(server, parent_server->client->cl_timeout, data->authflavor); 170954ceac45SDavid Howells if (error < 0) 171054ceac45SDavid Howells goto error; 171154ceac45SDavid Howells 171244950b67STrond Myklebust error = nfs4_server_common_setup(server, mntfh); 1713f2d0d85eSTrond Myklebust if (error < 0) 1714f2d0d85eSTrond Myklebust goto error; 1715f2d0d85eSTrond Myklebust 171654ceac45SDavid Howells dprintk("<-- nfs_create_referral_server() = %p\n", server); 171754ceac45SDavid Howells return server; 171854ceac45SDavid Howells 171954ceac45SDavid Howells error: 172054ceac45SDavid Howells nfs_free_server(server); 172154ceac45SDavid Howells dprintk("<-- nfs4_create_referral_server() = error %d\n", error); 172254ceac45SDavid Howells return ERR_PTR(error); 172354ceac45SDavid Howells } 172454ceac45SDavid Howells 172554ceac45SDavid Howells #endif /* CONFIG_NFS_V4 */ 172654ceac45SDavid Howells 172754ceac45SDavid Howells /* 172854ceac45SDavid Howells * Clone an NFS2, NFS3 or NFS4 server record 172954ceac45SDavid Howells */ 173054ceac45SDavid Howells struct nfs_server *nfs_clone_server(struct nfs_server *source, 173154ceac45SDavid Howells struct nfs_fh *fh, 1732*7e6eb683SBryan Schumaker struct nfs_fattr *fattr, 1733*7e6eb683SBryan Schumaker rpc_authflavor_t flavor) 173454ceac45SDavid Howells { 173554ceac45SDavid Howells struct nfs_server *server; 1736fbca779aSTrond Myklebust struct nfs_fattr *fattr_fsinfo; 173754ceac45SDavid Howells int error; 173854ceac45SDavid Howells 173954ceac45SDavid Howells dprintk("--> nfs_clone_server(,%llx:%llx,)\n", 17406daabf1bSDavid Howells (unsigned long long) fattr->fsid.major, 17416daabf1bSDavid Howells (unsigned long long) fattr->fsid.minor); 174254ceac45SDavid Howells 174354ceac45SDavid Howells server = nfs_alloc_server(); 174454ceac45SDavid Howells if (!server) 174554ceac45SDavid Howells return ERR_PTR(-ENOMEM); 174654ceac45SDavid Howells 1747fbca779aSTrond Myklebust error = -ENOMEM; 1748fbca779aSTrond Myklebust fattr_fsinfo = nfs_alloc_fattr(); 1749fbca779aSTrond Myklebust if (fattr_fsinfo == NULL) 1750fbca779aSTrond Myklebust goto out_free_server; 1751fbca779aSTrond Myklebust 175254ceac45SDavid Howells /* Copy data from the source */ 175354ceac45SDavid Howells server->nfs_client = source->nfs_client; 17540aaaf5c4SChuck Lever server->destroy = source->destroy; 175554ceac45SDavid Howells atomic_inc(&server->nfs_client->cl_count); 175654ceac45SDavid Howells nfs_server_copy_userdata(server, source); 175754ceac45SDavid Howells 175854ceac45SDavid Howells server->fsid = fattr->fsid; 175954ceac45SDavid Howells 176033170233STrond Myklebust error = nfs_init_server_rpcclient(server, 176133170233STrond Myklebust source->client->cl_timeout, 1762*7e6eb683SBryan Schumaker flavor); 176354ceac45SDavid Howells if (error < 0) 176454ceac45SDavid Howells goto out_free_server; 176554ceac45SDavid Howells if (!IS_ERR(source->client_acl)) 176654ceac45SDavid Howells nfs_init_server_aclclient(server); 176754ceac45SDavid Howells 176854ceac45SDavid Howells /* probe the filesystem info for this server filesystem */ 1769fbca779aSTrond Myklebust error = nfs_probe_fsinfo(server, fh, fattr_fsinfo); 177054ceac45SDavid Howells if (error < 0) 177154ceac45SDavid Howells goto out_free_server; 177254ceac45SDavid Howells 177354af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 177454af3bb5STrond Myklebust server->namelen = NFS4_MAXNAMLEN; 177554af3bb5STrond Myklebust 177654ceac45SDavid Howells dprintk("Cloned FSID: %llx:%llx\n", 17776daabf1bSDavid Howells (unsigned long long) server->fsid.major, 17786daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 177954ceac45SDavid Howells 178054ceac45SDavid Howells error = nfs_start_lockd(server); 178154ceac45SDavid Howells if (error < 0) 178254ceac45SDavid Howells goto out_free_server; 178354ceac45SDavid Howells 1784fca5238eSChuck Lever nfs_server_insert_lists(server); 178554ceac45SDavid Howells server->mount_time = jiffies; 178654ceac45SDavid Howells 1787fbca779aSTrond Myklebust nfs_free_fattr(fattr_fsinfo); 178854ceac45SDavid Howells dprintk("<-- nfs_clone_server() = %p\n", server); 178954ceac45SDavid Howells return server; 179054ceac45SDavid Howells 179154ceac45SDavid Howells out_free_server: 1792fbca779aSTrond Myklebust nfs_free_fattr(fattr_fsinfo); 179354ceac45SDavid Howells nfs_free_server(server); 179454ceac45SDavid Howells dprintk("<-- nfs_clone_server() = error %d\n", error); 179554ceac45SDavid Howells return ERR_PTR(error); 179654ceac45SDavid Howells } 17976aaca566SDavid Howells 17986b13168bSStanislav Kinsbursky void nfs_clients_init(struct net *net) 17996b13168bSStanislav Kinsbursky { 18006b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(net, nfs_net_id); 18016b13168bSStanislav Kinsbursky 18026b13168bSStanislav Kinsbursky INIT_LIST_HEAD(&nn->nfs_client_list); 1803c25d32b2SStanislav Kinsbursky INIT_LIST_HEAD(&nn->nfs_volume_list); 180428cd1b3fSStanislav Kinsbursky #ifdef CONFIG_NFS_V4 180528cd1b3fSStanislav Kinsbursky idr_init(&nn->cb_ident_idr); 180628cd1b3fSStanislav Kinsbursky #endif 18074c03ae4aSTrond Myklebust spin_lock_init(&nn->nfs_client_lock); 18086b13168bSStanislav Kinsbursky } 18096b13168bSStanislav Kinsbursky 18106aaca566SDavid Howells #ifdef CONFIG_PROC_FS 18116aaca566SDavid Howells static struct proc_dir_entry *proc_fs_nfs; 18126aaca566SDavid Howells 18136aaca566SDavid Howells static int nfs_server_list_open(struct inode *inode, struct file *file); 18146aaca566SDavid Howells static void *nfs_server_list_start(struct seq_file *p, loff_t *pos); 18156aaca566SDavid Howells static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos); 18166aaca566SDavid Howells static void nfs_server_list_stop(struct seq_file *p, void *v); 18176aaca566SDavid Howells static int nfs_server_list_show(struct seq_file *m, void *v); 18186aaca566SDavid Howells 181988e9d34cSJames Morris static const struct seq_operations nfs_server_list_ops = { 18206aaca566SDavid Howells .start = nfs_server_list_start, 18216aaca566SDavid Howells .next = nfs_server_list_next, 18226aaca566SDavid Howells .stop = nfs_server_list_stop, 18236aaca566SDavid Howells .show = nfs_server_list_show, 18246aaca566SDavid Howells }; 18256aaca566SDavid Howells 182600977a59SArjan van de Ven static const struct file_operations nfs_server_list_fops = { 18276aaca566SDavid Howells .open = nfs_server_list_open, 18286aaca566SDavid Howells .read = seq_read, 18296aaca566SDavid Howells .llseek = seq_lseek, 18306aaca566SDavid Howells .release = seq_release, 183134b37235SDenis V. Lunev .owner = THIS_MODULE, 18326aaca566SDavid Howells }; 18336aaca566SDavid Howells 18346aaca566SDavid Howells static int nfs_volume_list_open(struct inode *inode, struct file *file); 18356aaca566SDavid Howells static void *nfs_volume_list_start(struct seq_file *p, loff_t *pos); 18366aaca566SDavid Howells static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos); 18376aaca566SDavid Howells static void nfs_volume_list_stop(struct seq_file *p, void *v); 18386aaca566SDavid Howells static int nfs_volume_list_show(struct seq_file *m, void *v); 18396aaca566SDavid Howells 184088e9d34cSJames Morris static const struct seq_operations nfs_volume_list_ops = { 18416aaca566SDavid Howells .start = nfs_volume_list_start, 18426aaca566SDavid Howells .next = nfs_volume_list_next, 18436aaca566SDavid Howells .stop = nfs_volume_list_stop, 18446aaca566SDavid Howells .show = nfs_volume_list_show, 18456aaca566SDavid Howells }; 18466aaca566SDavid Howells 184700977a59SArjan van de Ven static const struct file_operations nfs_volume_list_fops = { 18486aaca566SDavid Howells .open = nfs_volume_list_open, 18496aaca566SDavid Howells .read = seq_read, 18506aaca566SDavid Howells .llseek = seq_lseek, 18516aaca566SDavid Howells .release = seq_release, 185234b37235SDenis V. Lunev .owner = THIS_MODULE, 18536aaca566SDavid Howells }; 18546aaca566SDavid Howells 18556aaca566SDavid Howells /* 18566aaca566SDavid Howells * open "/proc/fs/nfsfs/servers" which provides a summary of servers with which 18576aaca566SDavid Howells * we're dealing 18586aaca566SDavid Howells */ 18596aaca566SDavid Howells static int nfs_server_list_open(struct inode *inode, struct file *file) 18606aaca566SDavid Howells { 18616aaca566SDavid Howells struct seq_file *m; 18626aaca566SDavid Howells int ret; 18636b13168bSStanislav Kinsbursky struct pid_namespace *pid_ns = file->f_dentry->d_sb->s_fs_info; 18646b13168bSStanislav Kinsbursky struct net *net = pid_ns->child_reaper->nsproxy->net_ns; 18656aaca566SDavid Howells 18666aaca566SDavid Howells ret = seq_open(file, &nfs_server_list_ops); 18676aaca566SDavid Howells if (ret < 0) 18686aaca566SDavid Howells return ret; 18696aaca566SDavid Howells 18706aaca566SDavid Howells m = file->private_data; 18716b13168bSStanislav Kinsbursky m->private = net; 18726aaca566SDavid Howells 18736aaca566SDavid Howells return 0; 18746aaca566SDavid Howells } 18756aaca566SDavid Howells 18766aaca566SDavid Howells /* 18776aaca566SDavid Howells * set up the iterator to start reading from the server list and return the first item 18786aaca566SDavid Howells */ 18796aaca566SDavid Howells static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos) 18806aaca566SDavid Howells { 18816b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(m->private, nfs_net_id); 18826b13168bSStanislav Kinsbursky 18836aaca566SDavid Howells /* lock the list against modification */ 1884dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 18856b13168bSStanislav Kinsbursky return seq_list_start_head(&nn->nfs_client_list, *_pos); 18866aaca566SDavid Howells } 18876aaca566SDavid Howells 18886aaca566SDavid Howells /* 18896aaca566SDavid Howells * move to next server 18906aaca566SDavid Howells */ 18916aaca566SDavid Howells static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos) 18926aaca566SDavid Howells { 18936b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(p->private, nfs_net_id); 18946b13168bSStanislav Kinsbursky 18956b13168bSStanislav Kinsbursky return seq_list_next(v, &nn->nfs_client_list, pos); 18966aaca566SDavid Howells } 18976aaca566SDavid Howells 18986aaca566SDavid Howells /* 18996aaca566SDavid Howells * clean up after reading from the transports list 19006aaca566SDavid Howells */ 19016aaca566SDavid Howells static void nfs_server_list_stop(struct seq_file *p, void *v) 19026aaca566SDavid Howells { 1903dc030858SStanislav Kinsbursky struct nfs_net *nn = net_generic(p->private, nfs_net_id); 1904dc030858SStanislav Kinsbursky 1905dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 19066aaca566SDavid Howells } 19076aaca566SDavid Howells 19086aaca566SDavid Howells /* 19096aaca566SDavid Howells * display a header line followed by a load of call lines 19106aaca566SDavid Howells */ 19116aaca566SDavid Howells static int nfs_server_list_show(struct seq_file *m, void *v) 19126aaca566SDavid Howells { 19136aaca566SDavid Howells struct nfs_client *clp; 19146b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(m->private, nfs_net_id); 19156aaca566SDavid Howells 19166aaca566SDavid Howells /* display header on line 1 */ 19176b13168bSStanislav Kinsbursky if (v == &nn->nfs_client_list) { 19186aaca566SDavid Howells seq_puts(m, "NV SERVER PORT USE HOSTNAME\n"); 19196aaca566SDavid Howells return 0; 19206aaca566SDavid Howells } 19216aaca566SDavid Howells 19226aaca566SDavid Howells /* display one transport per line on subsequent lines */ 19236aaca566SDavid Howells clp = list_entry(v, struct nfs_client, cl_share_link); 19246aaca566SDavid Howells 1925940aab49SMalahal Naineni /* Check if the client is initialized */ 1926940aab49SMalahal Naineni if (clp->cl_cons_state != NFS_CS_READY) 1927940aab49SMalahal Naineni return 0; 1928940aab49SMalahal Naineni 19292446ab60STrond Myklebust rcu_read_lock(); 19305d8515caSChuck Lever seq_printf(m, "v%u %s %s %3d %s\n", 193140c55319STrond Myklebust clp->rpc_ops->version, 19325d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), 19335d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), 19346aaca566SDavid Howells atomic_read(&clp->cl_count), 19356aaca566SDavid Howells clp->cl_hostname); 19362446ab60STrond Myklebust rcu_read_unlock(); 19376aaca566SDavid Howells 19386aaca566SDavid Howells return 0; 19396aaca566SDavid Howells } 19406aaca566SDavid Howells 19416aaca566SDavid Howells /* 19426aaca566SDavid Howells * open "/proc/fs/nfsfs/volumes" which provides a summary of extant volumes 19436aaca566SDavid Howells */ 19446aaca566SDavid Howells static int nfs_volume_list_open(struct inode *inode, struct file *file) 19456aaca566SDavid Howells { 19466aaca566SDavid Howells struct seq_file *m; 19476aaca566SDavid Howells int ret; 1948c25d32b2SStanislav Kinsbursky struct pid_namespace *pid_ns = file->f_dentry->d_sb->s_fs_info; 1949c25d32b2SStanislav Kinsbursky struct net *net = pid_ns->child_reaper->nsproxy->net_ns; 19506aaca566SDavid Howells 19516aaca566SDavid Howells ret = seq_open(file, &nfs_volume_list_ops); 19526aaca566SDavid Howells if (ret < 0) 19536aaca566SDavid Howells return ret; 19546aaca566SDavid Howells 19556aaca566SDavid Howells m = file->private_data; 1956c25d32b2SStanislav Kinsbursky m->private = net; 19576aaca566SDavid Howells 19586aaca566SDavid Howells return 0; 19596aaca566SDavid Howells } 19606aaca566SDavid Howells 19616aaca566SDavid Howells /* 19626aaca566SDavid Howells * set up the iterator to start reading from the volume list and return the first item 19636aaca566SDavid Howells */ 19646aaca566SDavid Howells static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos) 19656aaca566SDavid Howells { 1966c25d32b2SStanislav Kinsbursky struct nfs_net *nn = net_generic(m->private, nfs_net_id); 1967c25d32b2SStanislav Kinsbursky 19686aaca566SDavid Howells /* lock the list against modification */ 1969dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 1970c25d32b2SStanislav Kinsbursky return seq_list_start_head(&nn->nfs_volume_list, *_pos); 19716aaca566SDavid Howells } 19726aaca566SDavid Howells 19736aaca566SDavid Howells /* 19746aaca566SDavid Howells * move to next volume 19756aaca566SDavid Howells */ 19766aaca566SDavid Howells static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos) 19776aaca566SDavid Howells { 1978c25d32b2SStanislav Kinsbursky struct nfs_net *nn = net_generic(p->private, nfs_net_id); 1979c25d32b2SStanislav Kinsbursky 1980c25d32b2SStanislav Kinsbursky return seq_list_next(v, &nn->nfs_volume_list, pos); 19816aaca566SDavid Howells } 19826aaca566SDavid Howells 19836aaca566SDavid Howells /* 19846aaca566SDavid Howells * clean up after reading from the transports list 19856aaca566SDavid Howells */ 19866aaca566SDavid Howells static void nfs_volume_list_stop(struct seq_file *p, void *v) 19876aaca566SDavid Howells { 1988dc030858SStanislav Kinsbursky struct nfs_net *nn = net_generic(p->private, nfs_net_id); 1989dc030858SStanislav Kinsbursky 1990dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 19916aaca566SDavid Howells } 19926aaca566SDavid Howells 19936aaca566SDavid Howells /* 19946aaca566SDavid Howells * display a header line followed by a load of call lines 19956aaca566SDavid Howells */ 19966aaca566SDavid Howells static int nfs_volume_list_show(struct seq_file *m, void *v) 19976aaca566SDavid Howells { 19986aaca566SDavid Howells struct nfs_server *server; 19996aaca566SDavid Howells struct nfs_client *clp; 20006aaca566SDavid Howells char dev[8], fsid[17]; 2001c25d32b2SStanislav Kinsbursky struct nfs_net *nn = net_generic(m->private, nfs_net_id); 20026aaca566SDavid Howells 20036aaca566SDavid Howells /* display header on line 1 */ 2004c25d32b2SStanislav Kinsbursky if (v == &nn->nfs_volume_list) { 20055d1acff1SDavid Howells seq_puts(m, "NV SERVER PORT DEV FSID FSC\n"); 20066aaca566SDavid Howells return 0; 20076aaca566SDavid Howells } 20086aaca566SDavid Howells /* display one transport per line on subsequent lines */ 20096aaca566SDavid Howells server = list_entry(v, struct nfs_server, master_link); 20106aaca566SDavid Howells clp = server->nfs_client; 20116aaca566SDavid Howells 20126aaca566SDavid Howells snprintf(dev, 8, "%u:%u", 20136aaca566SDavid Howells MAJOR(server->s_dev), MINOR(server->s_dev)); 20146aaca566SDavid Howells 20156aaca566SDavid Howells snprintf(fsid, 17, "%llx:%llx", 20166daabf1bSDavid Howells (unsigned long long) server->fsid.major, 20176daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 20186aaca566SDavid Howells 20192446ab60STrond Myklebust rcu_read_lock(); 20205d1acff1SDavid Howells seq_printf(m, "v%u %s %s %-7s %-17s %s\n", 202140c55319STrond Myklebust clp->rpc_ops->version, 20225d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), 20235d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), 20246aaca566SDavid Howells dev, 20255d1acff1SDavid Howells fsid, 20265d1acff1SDavid Howells nfs_server_fscache_state(server)); 20272446ab60STrond Myklebust rcu_read_unlock(); 20286aaca566SDavid Howells 20296aaca566SDavid Howells return 0; 20306aaca566SDavid Howells } 20316aaca566SDavid Howells 20326aaca566SDavid Howells /* 20336aaca566SDavid Howells * initialise the /proc/fs/nfsfs/ directory 20346aaca566SDavid Howells */ 20356aaca566SDavid Howells int __init nfs_fs_proc_init(void) 20366aaca566SDavid Howells { 20376aaca566SDavid Howells struct proc_dir_entry *p; 20386aaca566SDavid Howells 203936a5aeb8SAlexey Dobriyan proc_fs_nfs = proc_mkdir("fs/nfsfs", NULL); 20406aaca566SDavid Howells if (!proc_fs_nfs) 20416aaca566SDavid Howells goto error_0; 20426aaca566SDavid Howells 20436aaca566SDavid Howells /* a file of servers with which we're dealing */ 204434b37235SDenis V. Lunev p = proc_create("servers", S_IFREG|S_IRUGO, 204534b37235SDenis V. Lunev proc_fs_nfs, &nfs_server_list_fops); 20466aaca566SDavid Howells if (!p) 20476aaca566SDavid Howells goto error_1; 20486aaca566SDavid Howells 20496aaca566SDavid Howells /* a file of volumes that we have mounted */ 205034b37235SDenis V. Lunev p = proc_create("volumes", S_IFREG|S_IRUGO, 205134b37235SDenis V. Lunev proc_fs_nfs, &nfs_volume_list_fops); 20526aaca566SDavid Howells if (!p) 20536aaca566SDavid Howells goto error_2; 20546aaca566SDavid Howells return 0; 20556aaca566SDavid Howells 20566aaca566SDavid Howells error_2: 20576aaca566SDavid Howells remove_proc_entry("servers", proc_fs_nfs); 20586aaca566SDavid Howells error_1: 205936a5aeb8SAlexey Dobriyan remove_proc_entry("fs/nfsfs", NULL); 20606aaca566SDavid Howells error_0: 20616aaca566SDavid Howells return -ENOMEM; 20626aaca566SDavid Howells } 20636aaca566SDavid Howells 20646aaca566SDavid Howells /* 20656aaca566SDavid Howells * clean up the /proc/fs/nfsfs/ directory 20666aaca566SDavid Howells */ 20676aaca566SDavid Howells void nfs_fs_proc_exit(void) 20686aaca566SDavid Howells { 20696aaca566SDavid Howells remove_proc_entry("volumes", proc_fs_nfs); 20706aaca566SDavid Howells remove_proc_entry("servers", proc_fs_nfs); 207136a5aeb8SAlexey Dobriyan remove_proc_entry("fs/nfsfs", NULL); 20726aaca566SDavid Howells } 20736aaca566SDavid Howells 20746aaca566SDavid Howells #endif /* CONFIG_PROC_FS */ 2075b064eca2STrond Myklebust 2076b064eca2STrond Myklebust module_param(nfs4_disable_idmapping, bool, 0644); 2077b064eca2STrond Myklebust MODULE_PARM_DESC(nfs4_disable_idmapping, 2078b064eca2STrond Myklebust "Turn off NFSv4 idmapping when using 'sec=sys'"); 2079