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; 6873ea666cSChuck Lever struct nfs_net *nn = net_generic(clp->cl_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] = { 932ba68002SBryan Schumaker #ifdef CONFIG_NFS_V2 945006a76cSDavid Howells [2] = &nfs_version2, 952ba68002SBryan Schumaker #endif 965006a76cSDavid Howells #ifdef CONFIG_NFS_V3 975006a76cSDavid Howells [3] = &nfs_version3, 985006a76cSDavid Howells #endif 995006a76cSDavid Howells #ifdef CONFIG_NFS_V4 1005006a76cSDavid Howells [4] = &nfs_version4, 1015006a76cSDavid Howells #endif 1025006a76cSDavid Howells }; 1035006a76cSDavid Howells 104a613fa16STrond Myklebust const struct rpc_program nfs_program = { 1055006a76cSDavid Howells .name = "nfs", 1065006a76cSDavid Howells .number = NFS_PROGRAM, 1075006a76cSDavid Howells .nrvers = ARRAY_SIZE(nfs_version), 1085006a76cSDavid Howells .version = nfs_version, 1095006a76cSDavid Howells .stats = &nfs_rpcstat, 110fe0a9b74SJim Rees .pipe_dir_name = NFS_PIPE_DIRNAME, 1115006a76cSDavid Howells }; 1125006a76cSDavid Howells 1135006a76cSDavid Howells struct rpc_stat nfs_rpcstat = { 1145006a76cSDavid Howells .program = &nfs_program 1155006a76cSDavid Howells }; 1165006a76cSDavid Howells 1175006a76cSDavid Howells 1185006a76cSDavid Howells #ifdef CONFIG_NFS_V3_ACL 1195006a76cSDavid Howells static struct rpc_stat nfsacl_rpcstat = { &nfsacl_program }; 120a613fa16STrond Myklebust static const struct rpc_version *nfsacl_version[] = { 1215006a76cSDavid Howells [3] = &nfsacl_version3, 1225006a76cSDavid Howells }; 1235006a76cSDavid Howells 124a613fa16STrond Myklebust const struct rpc_program nfsacl_program = { 1255006a76cSDavid Howells .name = "nfsacl", 1265006a76cSDavid Howells .number = NFS_ACL_PROGRAM, 1275006a76cSDavid Howells .nrvers = ARRAY_SIZE(nfsacl_version), 1285006a76cSDavid Howells .version = nfsacl_version, 1295006a76cSDavid Howells .stats = &nfsacl_rpcstat, 1305006a76cSDavid Howells }; 1315006a76cSDavid Howells #endif /* CONFIG_NFS_V3_ACL */ 1325006a76cSDavid Howells 1333a498026STrond Myklebust struct nfs_client_initdata { 1344bf590e0SChuck Lever unsigned long init_flags; 1353a498026STrond Myklebust const char *hostname; 136d7422c47SChuck Lever const struct sockaddr *addr; 1376e4cffd7SChuck Lever size_t addrlen; 13840c55319STrond Myklebust const struct nfs_rpc_ops *rpc_ops; 13959dca3b2STrond Myklebust int proto; 1405aae4a9aSBenny Halevy u32 minorversion; 141e50a7a1aSStanislav Kinsbursky struct net *net; 1423a498026STrond Myklebust }; 1433a498026STrond Myklebust 1445006a76cSDavid Howells /* 14524c8dbbbSDavid Howells * Allocate a shared client record 14624c8dbbbSDavid Howells * 14724c8dbbbSDavid Howells * Since these are allocated/deallocated very rarely, we don't 14824c8dbbbSDavid Howells * bother putting them in a slab cache... 14924c8dbbbSDavid Howells */ 1506663ee7fSBryan Schumaker struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) 15124c8dbbbSDavid Howells { 15224c8dbbbSDavid Howells struct nfs_client *clp; 1537c67db3aSTrond Myklebust struct rpc_cred *cred; 154a21bdd9bSChuck Lever int err = -ENOMEM; 15524c8dbbbSDavid Howells 15624c8dbbbSDavid Howells if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) 15724c8dbbbSDavid Howells goto error_0; 15824c8dbbbSDavid Howells 15940c55319STrond Myklebust clp->rpc_ops = cl_init->rpc_ops; 16040c55319STrond Myklebust 16124c8dbbbSDavid Howells atomic_set(&clp->cl_count, 1); 16224c8dbbbSDavid Howells clp->cl_cons_state = NFS_CS_INITING; 16324c8dbbbSDavid Howells 1646e4cffd7SChuck Lever memcpy(&clp->cl_addr, cl_init->addr, cl_init->addrlen); 1656e4cffd7SChuck Lever clp->cl_addrlen = cl_init->addrlen; 16624c8dbbbSDavid Howells 1673a498026STrond Myklebust if (cl_init->hostname) { 168a21bdd9bSChuck Lever err = -ENOMEM; 1693a498026STrond Myklebust clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL); 17024c8dbbbSDavid Howells if (!clp->cl_hostname) 17171468513SBenny Halevy goto error_cleanup; 17224c8dbbbSDavid Howells } 17324c8dbbbSDavid Howells 17424c8dbbbSDavid Howells INIT_LIST_HEAD(&clp->cl_superblocks); 17524c8dbbbSDavid Howells clp->cl_rpcclient = ERR_PTR(-EINVAL); 17624c8dbbbSDavid Howells 17759dca3b2STrond Myklebust clp->cl_proto = cl_init->proto; 17873ea666cSChuck Lever clp->cl_net = get_net(cl_init->net); 17959dca3b2STrond Myklebust 18068c97153STrond Myklebust cred = rpc_lookup_machine_cred("*"); 1817c67db3aSTrond Myklebust if (!IS_ERR(cred)) 1827c67db3aSTrond Myklebust clp->cl_machine_cred = cred; 18314727281SDavid Howells nfs_fscache_get_client_cookie(clp); 18414727281SDavid Howells 18524c8dbbbSDavid Howells return clp; 18624c8dbbbSDavid Howells 18771468513SBenny Halevy error_cleanup: 18824c8dbbbSDavid Howells kfree(clp); 18924c8dbbbSDavid Howells error_0: 190a21bdd9bSChuck Lever return ERR_PTR(err); 19124c8dbbbSDavid Howells } 19224c8dbbbSDavid Howells 19324c8dbbbSDavid Howells #ifdef CONFIG_NFS_V4 194557134a3SAndy Adamson #ifdef CONFIG_NFS_V4_1 195ea005281SAndy Adamson static void nfs4_shutdown_session(struct nfs_client *clp) 196ea005281SAndy Adamson { 1977df529afSTrond Myklebust if (nfs4_has_session(clp)) { 198557134a3SAndy Adamson nfs4_destroy_session(clp->cl_session); 19966245539STrond Myklebust nfs4_destroy_clientid(clp); 200557134a3SAndy Adamson } 2017df529afSTrond Myklebust 2027df529afSTrond Myklebust } 203ea005281SAndy Adamson #else /* CONFIG_NFS_V4_1 */ 204ea005281SAndy Adamson static void nfs4_shutdown_session(struct nfs_client *clp) 205ea005281SAndy Adamson { 206ea005281SAndy Adamson } 207557134a3SAndy Adamson #endif /* CONFIG_NFS_V4_1 */ 208557134a3SAndy Adamson 2096663ee7fSBryan Schumaker struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init) 2106663ee7fSBryan Schumaker { 2116663ee7fSBryan Schumaker int err; 2126663ee7fSBryan Schumaker struct nfs_client *clp = nfs_alloc_client(cl_init); 2136663ee7fSBryan Schumaker if (IS_ERR(clp)) 2146663ee7fSBryan Schumaker return clp; 2156663ee7fSBryan Schumaker 2166663ee7fSBryan Schumaker err = nfs_get_cb_ident_idr(clp, cl_init->minorversion); 2176663ee7fSBryan Schumaker if (err) 2186663ee7fSBryan Schumaker goto error; 2196663ee7fSBryan Schumaker 2206663ee7fSBryan Schumaker spin_lock_init(&clp->cl_lock); 2216663ee7fSBryan Schumaker INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state); 2226663ee7fSBryan Schumaker rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); 2236663ee7fSBryan Schumaker clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED; 2246663ee7fSBryan Schumaker clp->cl_minorversion = cl_init->minorversion; 2256663ee7fSBryan Schumaker clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion]; 2266663ee7fSBryan Schumaker return clp; 2276663ee7fSBryan Schumaker 2286663ee7fSBryan Schumaker error: 2296663ee7fSBryan Schumaker kfree(clp); 2306663ee7fSBryan Schumaker return ERR_PTR(err); 2316663ee7fSBryan Schumaker } 2326663ee7fSBryan Schumaker 233557134a3SAndy Adamson /* 234888ef2e3SAlexandros Batsakis * Destroy the NFS4 callback service 235888ef2e3SAlexandros Batsakis */ 236888ef2e3SAlexandros Batsakis static void nfs4_destroy_callback(struct nfs_client *clp) 237888ef2e3SAlexandros Batsakis { 238888ef2e3SAlexandros Batsakis if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) 23997dc1359STrond Myklebust nfs_callback_down(clp->cl_mvops->minor_version); 240888ef2e3SAlexandros Batsakis } 241888ef2e3SAlexandros Batsakis 242888ef2e3SAlexandros Batsakis static void nfs4_shutdown_client(struct nfs_client *clp) 243888ef2e3SAlexandros Batsakis { 244888ef2e3SAlexandros Batsakis if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state)) 245888ef2e3SAlexandros Batsakis nfs4_kill_renewd(clp); 246ea005281SAndy Adamson nfs4_shutdown_session(clp); 247888ef2e3SAlexandros Batsakis nfs4_destroy_callback(clp); 248888ef2e3SAlexandros Batsakis if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state)) 249888ef2e3SAlexandros Batsakis nfs_idmap_delete(clp); 250888ef2e3SAlexandros Batsakis 251888ef2e3SAlexandros Batsakis rpc_destroy_wait_queue(&clp->cl_rpcwaitq); 252acdeb69dSChuck Lever kfree(clp->cl_serverowner); 25379d4e1f0SChuck Lever kfree(clp->cl_serverscope); 25459155546SChuck Lever kfree(clp->cl_implid); 255888ef2e3SAlexandros Batsakis } 256f4eecd5dSAndy Adamson 257cdb7ecedSBryan Schumaker void nfs4_free_client(struct nfs_client *clp) 258cdb7ecedSBryan Schumaker { 259cdb7ecedSBryan Schumaker nfs4_shutdown_client(clp); 260cdb7ecedSBryan Schumaker nfs_free_client(clp); 261cdb7ecedSBryan Schumaker } 262cdb7ecedSBryan Schumaker 263f4eecd5dSAndy Adamson /* idr_remove_all is not needed as all id's are removed by nfs_put_client */ 26428cd1b3fSStanislav Kinsbursky void nfs_cleanup_cb_ident_idr(struct net *net) 265f4eecd5dSAndy Adamson { 26628cd1b3fSStanislav Kinsbursky struct nfs_net *nn = net_generic(net, nfs_net_id); 26728cd1b3fSStanislav Kinsbursky 26828cd1b3fSStanislav Kinsbursky idr_destroy(&nn->cb_ident_idr); 269f4eecd5dSAndy Adamson } 270f4eecd5dSAndy Adamson 271f4eecd5dSAndy Adamson /* nfs_client_lock held */ 272f4eecd5dSAndy Adamson static void nfs_cb_idr_remove_locked(struct nfs_client *clp) 273f4eecd5dSAndy Adamson { 27473ea666cSChuck Lever struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); 27528cd1b3fSStanislav Kinsbursky 276f4eecd5dSAndy Adamson if (clp->cl_cb_ident) 27728cd1b3fSStanislav Kinsbursky idr_remove(&nn->cb_ident_idr, clp->cl_cb_ident); 278f4eecd5dSAndy Adamson } 279f4eecd5dSAndy Adamson 280f7e8917aSFred Isaman static void pnfs_init_server(struct nfs_server *server) 281f7e8917aSFred Isaman { 282f7e8917aSFred Isaman rpc_init_wait_queue(&server->roc_rpcwaitq, "pNFS ROC"); 283f7e8917aSFred Isaman } 284f7e8917aSFred Isaman 2850aaaf5c4SChuck Lever static void nfs4_destroy_server(struct nfs_server *server) 2860aaaf5c4SChuck Lever { 287eeebf916SBryan Schumaker nfs_server_return_all_delegations(server); 288eeebf916SBryan Schumaker unset_pnfs_layoutdriver(server); 2890aaaf5c4SChuck Lever nfs4_purge_state_owners(server); 2900aaaf5c4SChuck Lever } 2910aaaf5c4SChuck Lever 292888ef2e3SAlexandros Batsakis #else 29328cd1b3fSStanislav Kinsbursky void nfs_cleanup_cb_ident_idr(struct net *net) 294f4eecd5dSAndy Adamson { 295f4eecd5dSAndy Adamson } 296f4eecd5dSAndy Adamson 297f4eecd5dSAndy Adamson static void nfs_cb_idr_remove_locked(struct nfs_client *clp) 298f4eecd5dSAndy Adamson { 299f4eecd5dSAndy Adamson } 300f7e8917aSFred Isaman 301f7e8917aSFred Isaman static void pnfs_init_server(struct nfs_server *server) 302f7e8917aSFred Isaman { 303f7e8917aSFred Isaman } 304f7e8917aSFred Isaman 305888ef2e3SAlexandros Batsakis #endif /* CONFIG_NFS_V4 */ 306888ef2e3SAlexandros Batsakis 307888ef2e3SAlexandros Batsakis /* 3085dd3177aSTrond Myklebust * Destroy a shared client record 3095dd3177aSTrond Myklebust */ 310cdb7ecedSBryan Schumaker void nfs_free_client(struct nfs_client *clp) 3115dd3177aSTrond Myklebust { 31240c55319STrond Myklebust dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version); 3135dd3177aSTrond Myklebust 31414727281SDavid Howells nfs_fscache_release_client_cookie(clp); 31514727281SDavid Howells 31624c8dbbbSDavid Howells /* -EIO all pending I/O */ 31724c8dbbbSDavid Howells if (!IS_ERR(clp->cl_rpcclient)) 31824c8dbbbSDavid Howells rpc_shutdown_client(clp->cl_rpcclient); 31924c8dbbbSDavid Howells 3207c67db3aSTrond Myklebust if (clp->cl_machine_cred != NULL) 3217c67db3aSTrond Myklebust put_rpccred(clp->cl_machine_cred); 3227c67db3aSTrond Myklebust 32373ea666cSChuck Lever put_net(clp->cl_net); 32424c8dbbbSDavid Howells kfree(clp->cl_hostname); 32524c8dbbbSDavid Howells kfree(clp); 32624c8dbbbSDavid Howells 32724c8dbbbSDavid Howells dprintk("<-- nfs_free_client()\n"); 32824c8dbbbSDavid Howells } 32924c8dbbbSDavid Howells 33024c8dbbbSDavid Howells /* 33124c8dbbbSDavid Howells * Release a reference to a shared client record 33224c8dbbbSDavid Howells */ 33324c8dbbbSDavid Howells void nfs_put_client(struct nfs_client *clp) 33424c8dbbbSDavid Howells { 335dc030858SStanislav Kinsbursky struct nfs_net *nn; 336dc030858SStanislav Kinsbursky 33727ba8512SDavid Howells if (!clp) 33827ba8512SDavid Howells return; 33927ba8512SDavid Howells 34024c8dbbbSDavid Howells dprintk("--> nfs_put_client({%d})\n", atomic_read(&clp->cl_count)); 34173ea666cSChuck Lever nn = net_generic(clp->cl_net, nfs_net_id); 34224c8dbbbSDavid Howells 343dc030858SStanislav Kinsbursky if (atomic_dec_and_lock(&clp->cl_count, &nn->nfs_client_lock)) { 34424c8dbbbSDavid Howells list_del(&clp->cl_share_link); 345f4eecd5dSAndy Adamson nfs_cb_idr_remove_locked(clp); 346dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 34724c8dbbbSDavid Howells 34824c8dbbbSDavid Howells BUG_ON(!list_empty(&clp->cl_superblocks)); 34924c8dbbbSDavid Howells 350cdb7ecedSBryan Schumaker clp->rpc_ops->free_client(clp); 35124c8dbbbSDavid Howells } 35224c8dbbbSDavid Howells } 35316b374caSAndy Adamson EXPORT_SYMBOL_GPL(nfs_put_client); 35424c8dbbbSDavid Howells 3559082a5ccSTrond Myklebust #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 3569f4c899cSTrond Myklebust /* 3579f4c899cSTrond Myklebust * Test if two ip6 socket addresses refer to the same socket by 3589f4c899cSTrond Myklebust * comparing relevant fields. The padding bytes specifically, are not 3599f4c899cSTrond Myklebust * compared. sin6_flowinfo is not compared because it only affects QoS 3609f4c899cSTrond Myklebust * and sin6_scope_id is only compared if the address is "link local" 3619f4c899cSTrond Myklebust * because "link local" addresses need only be unique to a specific 3629f4c899cSTrond Myklebust * link. Conversely, ordinary unicast addresses might have different 3639f4c899cSTrond Myklebust * sin6_scope_id. 3649f4c899cSTrond Myklebust * 3659f4c899cSTrond Myklebust * The caller should ensure both socket addresses are AF_INET6. 3669f4c899cSTrond Myklebust */ 3673c8c45dfSChuck Lever static int nfs_sockaddr_match_ipaddr6(const struct sockaddr *sa1, 3689f4c899cSTrond Myklebust const struct sockaddr *sa2) 3699f4c899cSTrond Myklebust { 3703c8c45dfSChuck Lever const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sa1; 3713c8c45dfSChuck Lever const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2; 3729f4c899cSTrond Myklebust 373b9dd3abbSMi Jinlong if (!ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr)) 3749f4c899cSTrond Myklebust return 0; 375b9dd3abbSMi Jinlong else if (ipv6_addr_type(&sin1->sin6_addr) & IPV6_ADDR_LINKLOCAL) 376b9dd3abbSMi Jinlong return sin1->sin6_scope_id == sin2->sin6_scope_id; 3773b0d3f93STrond Myklebust 378b9dd3abbSMi Jinlong return 1; 3793b0d3f93STrond Myklebust } 3803c8c45dfSChuck Lever #else /* !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE) */ 3813c8c45dfSChuck Lever static int nfs_sockaddr_match_ipaddr6(const struct sockaddr *sa1, 3829f4c899cSTrond Myklebust const struct sockaddr *sa2) 3839f4c899cSTrond Myklebust { 3849f4c899cSTrond Myklebust return 0; 3859f4c899cSTrond Myklebust } 3869082a5ccSTrond Myklebust #endif 3873b0d3f93STrond Myklebust 38824c8dbbbSDavid Howells /* 389d7371c41SIan Dall * Test if two ip4 socket addresses refer to the same socket, by 390d7371c41SIan Dall * comparing relevant fields. The padding bytes specifically, are 391d7371c41SIan Dall * not compared. 392d7371c41SIan Dall * 393d7371c41SIan Dall * The caller should ensure both socket addresses are AF_INET. 394d7371c41SIan Dall */ 3953c8c45dfSChuck Lever static int nfs_sockaddr_match_ipaddr4(const struct sockaddr *sa1, 3963c8c45dfSChuck Lever const struct sockaddr *sa2) 3973c8c45dfSChuck Lever { 3983c8c45dfSChuck Lever const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sa1; 3993c8c45dfSChuck Lever const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sa2; 4003c8c45dfSChuck Lever 4013c8c45dfSChuck Lever return sin1->sin_addr.s_addr == sin2->sin_addr.s_addr; 4023c8c45dfSChuck Lever } 4033c8c45dfSChuck Lever 4043c8c45dfSChuck Lever static int nfs_sockaddr_cmp_ip6(const struct sockaddr *sa1, 4053c8c45dfSChuck Lever const struct sockaddr *sa2) 4063c8c45dfSChuck Lever { 4073c8c45dfSChuck Lever const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sa1; 4083c8c45dfSChuck Lever const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2; 4093c8c45dfSChuck Lever 4103c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr6(sa1, sa2) && 4113c8c45dfSChuck Lever (sin1->sin6_port == sin2->sin6_port); 4123c8c45dfSChuck Lever } 4133c8c45dfSChuck Lever 4149f4c899cSTrond Myklebust static int nfs_sockaddr_cmp_ip4(const struct sockaddr *sa1, 4159f4c899cSTrond Myklebust const struct sockaddr *sa2) 416d7371c41SIan Dall { 4173c8c45dfSChuck Lever const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sa1; 4183c8c45dfSChuck Lever const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sa2; 4199f4c899cSTrond Myklebust 4203c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr4(sa1, sa2) && 4213c8c45dfSChuck Lever (sin1->sin_port == sin2->sin_port); 422d7371c41SIan Dall } 423d7371c41SIan Dall 4244b7c8dd2SBryan Schumaker #if defined(CONFIG_NFS_V4_1) 425d7371c41SIan Dall /* 426d7371c41SIan Dall * Test if two socket addresses represent the same actual socket, 4273c8c45dfSChuck Lever * by comparing (only) relevant fields, excluding the port number. 4283c8c45dfSChuck Lever */ 4293c8c45dfSChuck Lever static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1, 4303c8c45dfSChuck Lever const struct sockaddr *sa2) 4313c8c45dfSChuck Lever { 4323c8c45dfSChuck Lever if (sa1->sa_family != sa2->sa_family) 4333c8c45dfSChuck Lever return 0; 4343c8c45dfSChuck Lever 4353c8c45dfSChuck Lever switch (sa1->sa_family) { 4363c8c45dfSChuck Lever case AF_INET: 4373c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr4(sa1, sa2); 4383c8c45dfSChuck Lever case AF_INET6: 4393c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr6(sa1, sa2); 4403c8c45dfSChuck Lever } 4413c8c45dfSChuck Lever return 0; 4423c8c45dfSChuck Lever } 4434b7c8dd2SBryan Schumaker #endif /* CONFIG_NFS_V4_1 */ 4443c8c45dfSChuck Lever 4453c8c45dfSChuck Lever /* 4463c8c45dfSChuck Lever * Test if two socket addresses represent the same actual socket, 4473c8c45dfSChuck Lever * by comparing (only) relevant fields, including the port number. 448d7371c41SIan Dall */ 449d7371c41SIan Dall static int nfs_sockaddr_cmp(const struct sockaddr *sa1, 450d7371c41SIan Dall const struct sockaddr *sa2) 451d7371c41SIan Dall { 452d7371c41SIan Dall if (sa1->sa_family != sa2->sa_family) 453d7371c41SIan Dall return 0; 454d7371c41SIan Dall 455d7371c41SIan Dall switch (sa1->sa_family) { 456d7371c41SIan Dall case AF_INET: 4579f4c899cSTrond Myklebust return nfs_sockaddr_cmp_ip4(sa1, sa2); 458d7371c41SIan Dall case AF_INET6: 4599f4c899cSTrond Myklebust return nfs_sockaddr_cmp_ip6(sa1, sa2); 460d7371c41SIan Dall } 461d7371c41SIan Dall return 0; 462d7371c41SIan Dall } 463d7371c41SIan Dall 4644b7c8dd2SBryan Schumaker #if defined(CONFIG_NFS_V4_1) 465c36fca52SAndy Adamson /* Common match routine for v4.0 and v4.1 callback services */ 46617280175STrond Myklebust static bool nfs4_cb_match_client(const struct sockaddr *addr, 46717280175STrond Myklebust struct nfs_client *clp, u32 minorversion) 468c81468a1STrond Myklebust { 4693b0d3f93STrond Myklebust struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; 4703b0d3f93STrond Myklebust 471c36fca52SAndy Adamson /* Don't match clients that failed to initialise */ 47276db6d95SAndy Adamson if (!(clp->cl_cons_state == NFS_CS_READY || 47376db6d95SAndy Adamson clp->cl_cons_state == NFS_CS_SESSION_INITING)) 474c36fca52SAndy Adamson return false; 475c81468a1STrond Myklebust 47654ac471cSTrond Myklebust smp_rmb(); 47754ac471cSTrond Myklebust 478c36fca52SAndy Adamson /* Match the version and minorversion */ 479c36fca52SAndy Adamson if (clp->rpc_ops->version != 4 || 480c36fca52SAndy Adamson clp->cl_minorversion != minorversion) 481c36fca52SAndy Adamson return false; 482c81468a1STrond Myklebust 483c81468a1STrond Myklebust /* Match only the IP address, not the port number */ 4843b0d3f93STrond Myklebust if (!nfs_sockaddr_match_ipaddr(addr, clap)) 485c36fca52SAndy Adamson return false; 486c81468a1STrond Myklebust 487c36fca52SAndy Adamson return true; 4883fbd67adSTrond Myklebust } 4894b7c8dd2SBryan Schumaker #endif /* CONFIG_NFS_V4_1 */ 4903fbd67adSTrond Myklebust 4913fbd67adSTrond Myklebust /* 492c81468a1STrond Myklebust * Find an nfs_client on the list that matches the initialisation data 493c81468a1STrond Myklebust * that is supplied. 494c81468a1STrond Myklebust */ 495c81468a1STrond Myklebust static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *data) 49624c8dbbbSDavid Howells { 49724c8dbbbSDavid Howells struct nfs_client *clp; 498d7371c41SIan Dall const struct sockaddr *sap = data->addr; 4996b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(data->net, nfs_net_id); 50024c8dbbbSDavid Howells 5016b13168bSStanislav Kinsbursky list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) { 502d7371c41SIan Dall const struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; 50313bbc06aSTrond Myklebust /* Don't match clients that failed to initialise properly */ 50413bbc06aSTrond Myklebust if (clp->cl_cons_state < 0) 50513bbc06aSTrond Myklebust continue; 50613bbc06aSTrond Myklebust 50724c8dbbbSDavid Howells /* Different NFS versions cannot share the same nfs_client */ 50840c55319STrond Myklebust if (clp->rpc_ops != data->rpc_ops) 50924c8dbbbSDavid Howells continue; 51024c8dbbbSDavid Howells 51159dca3b2STrond Myklebust if (clp->cl_proto != data->proto) 51259dca3b2STrond Myklebust continue; 5135aae4a9aSBenny Halevy /* Match nfsv4 minorversion */ 5145aae4a9aSBenny Halevy if (clp->cl_minorversion != data->minorversion) 5155aae4a9aSBenny Halevy continue; 516c81468a1STrond Myklebust /* Match the full socket address */ 517d7371c41SIan Dall if (!nfs_sockaddr_cmp(sap, clap)) 51824c8dbbbSDavid Howells continue; 51924c8dbbbSDavid Howells 52024c8dbbbSDavid Howells atomic_inc(&clp->cl_count); 52124c8dbbbSDavid Howells return clp; 52224c8dbbbSDavid Howells } 523c81468a1STrond Myklebust return NULL; 52424c8dbbbSDavid Howells } 52524c8dbbbSDavid Howells 5264697bd5eSTrond Myklebust static bool nfs_client_init_is_complete(const struct nfs_client *clp) 5274697bd5eSTrond Myklebust { 5284697bd5eSTrond Myklebust return clp->cl_cons_state != NFS_CS_INITING; 5294697bd5eSTrond Myklebust } 5304697bd5eSTrond Myklebust 5314697bd5eSTrond Myklebust int nfs_wait_client_init_complete(const struct nfs_client *clp) 5324697bd5eSTrond Myklebust { 5334697bd5eSTrond Myklebust return wait_event_killable(nfs_client_active_wq, 5344697bd5eSTrond Myklebust nfs_client_init_is_complete(clp)); 5354697bd5eSTrond Myklebust } 5364697bd5eSTrond Myklebust 53724c8dbbbSDavid Howells /* 538f411703aSChuck Lever * Found an existing client. Make sure it's ready before returning. 539f411703aSChuck Lever */ 540f411703aSChuck Lever static struct nfs_client * 541f411703aSChuck Lever nfs_found_client(const struct nfs_client_initdata *cl_init, 542f411703aSChuck Lever struct nfs_client *clp) 543f411703aSChuck Lever { 544f411703aSChuck Lever int error; 545f411703aSChuck Lever 5464697bd5eSTrond Myklebust error = nfs_wait_client_init_complete(clp); 547f411703aSChuck Lever if (error < 0) { 548f411703aSChuck Lever nfs_put_client(clp); 549f411703aSChuck Lever return ERR_PTR(-ERESTARTSYS); 550f411703aSChuck Lever } 551f411703aSChuck Lever 552f411703aSChuck Lever if (clp->cl_cons_state < NFS_CS_READY) { 553f411703aSChuck Lever error = clp->cl_cons_state; 554f411703aSChuck Lever nfs_put_client(clp); 555f411703aSChuck Lever return ERR_PTR(error); 556f411703aSChuck Lever } 557f411703aSChuck Lever 55854ac471cSTrond Myklebust smp_rmb(); 55954ac471cSTrond Myklebust 560f411703aSChuck Lever dprintk("<-- %s found nfs_client %p for %s\n", 561f411703aSChuck Lever __func__, clp, cl_init->hostname ?: ""); 562f411703aSChuck Lever return clp; 563f411703aSChuck Lever } 564f411703aSChuck Lever 565f411703aSChuck Lever /* 56624c8dbbbSDavid Howells * Look up a client by IP address and protocol version 56724c8dbbbSDavid Howells * - creates a new record if one doesn't yet exist 56824c8dbbbSDavid Howells */ 56945a52a02SAndy Adamson static struct nfs_client * 57045a52a02SAndy Adamson nfs_get_client(const struct nfs_client_initdata *cl_init, 57145a52a02SAndy Adamson const struct rpc_timeout *timeparms, 57245a52a02SAndy Adamson const char *ip_addr, 5734bf590e0SChuck Lever rpc_authflavor_t authflavour) 57424c8dbbbSDavid Howells { 57524c8dbbbSDavid Howells struct nfs_client *clp, *new = NULL; 5766b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(cl_init->net, nfs_net_id); 57724c8dbbbSDavid Howells 578d7422c47SChuck Lever dprintk("--> nfs_get_client(%s,v%u)\n", 579d7422c47SChuck Lever cl_init->hostname ?: "", cl_init->rpc_ops->version); 58024c8dbbbSDavid Howells 58124c8dbbbSDavid Howells /* see if the client already exists */ 58224c8dbbbSDavid Howells do { 583dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 58424c8dbbbSDavid Howells 585c81468a1STrond Myklebust clp = nfs_match_client(cl_init); 586f411703aSChuck Lever if (clp) { 587f411703aSChuck Lever spin_unlock(&nn->nfs_client_lock); 588f411703aSChuck Lever if (new) 589cdb7ecedSBryan Schumaker new->rpc_ops->free_client(new); 590f411703aSChuck Lever return nfs_found_client(cl_init, clp); 591f411703aSChuck Lever } 5928cab4c39SChuck Lever if (new) { 5938cab4c39SChuck Lever list_add(&new->cl_share_link, &nn->nfs_client_list); 5948cab4c39SChuck Lever spin_unlock(&nn->nfs_client_lock); 5954bf590e0SChuck Lever new->cl_flags = cl_init->init_flags; 5968cab4c39SChuck Lever return cl_init->rpc_ops->init_client(new, 5978cab4c39SChuck Lever timeparms, ip_addr, 5984bf590e0SChuck Lever authflavour); 5998cab4c39SChuck Lever } 60024c8dbbbSDavid Howells 601dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 60224c8dbbbSDavid Howells 6036663ee7fSBryan Schumaker new = cl_init->rpc_ops->alloc_client(cl_init); 604a21bdd9bSChuck Lever } while (!IS_ERR(new)); 60524c8dbbbSDavid Howells 606f411703aSChuck Lever dprintk("<-- nfs_get_client() Failed to find %s (%ld)\n", 607f411703aSChuck Lever cl_init->hostname ?: "", PTR_ERR(new)); 608a21bdd9bSChuck Lever return new; 60924c8dbbbSDavid Howells } 61024c8dbbbSDavid Howells 61124c8dbbbSDavid Howells /* 61224c8dbbbSDavid Howells * Mark a server as ready or failed 61324c8dbbbSDavid Howells */ 61476db6d95SAndy Adamson void nfs_mark_client_ready(struct nfs_client *clp, int state) 61524c8dbbbSDavid Howells { 61654ac471cSTrond Myklebust smp_wmb(); 61724c8dbbbSDavid Howells clp->cl_cons_state = state; 61824c8dbbbSDavid Howells wake_up_all(&nfs_client_active_wq); 61924c8dbbbSDavid Howells } 6205006a76cSDavid Howells 6215006a76cSDavid Howells /* 6225006a76cSDavid Howells * Initialise the timeout values for a connection 6235006a76cSDavid Howells */ 6245006a76cSDavid Howells static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, 6255006a76cSDavid Howells unsigned int timeo, unsigned int retrans) 6265006a76cSDavid Howells { 6275006a76cSDavid Howells to->to_initval = timeo * HZ / 10; 6285006a76cSDavid Howells to->to_retries = retrans; 6295006a76cSDavid Howells 6305006a76cSDavid Howells switch (proto) { 6310896a725S\"Talpey, Thomas\ case XPRT_TRANSPORT_TCP: 6322cf7ff7aS\"Talpey, Thomas\ case XPRT_TRANSPORT_RDMA: 633259875efSTrond Myklebust if (to->to_retries == 0) 634259875efSTrond Myklebust to->to_retries = NFS_DEF_TCP_RETRANS; 6357a3e3e18STrond Myklebust if (to->to_initval == 0) 636259875efSTrond Myklebust to->to_initval = NFS_DEF_TCP_TIMEO * HZ / 10; 6375006a76cSDavid Howells if (to->to_initval > NFS_MAX_TCP_TIMEOUT) 6385006a76cSDavid Howells to->to_initval = NFS_MAX_TCP_TIMEOUT; 6395006a76cSDavid Howells to->to_increment = to->to_initval; 6405006a76cSDavid Howells to->to_maxval = to->to_initval + (to->to_increment * to->to_retries); 6417a3e3e18STrond Myklebust if (to->to_maxval > NFS_MAX_TCP_TIMEOUT) 6427a3e3e18STrond Myklebust to->to_maxval = NFS_MAX_TCP_TIMEOUT; 6437a3e3e18STrond Myklebust if (to->to_maxval < to->to_initval) 6447a3e3e18STrond Myklebust to->to_maxval = to->to_initval; 6455006a76cSDavid Howells to->to_exponential = 0; 6465006a76cSDavid Howells break; 6470896a725S\"Talpey, Thomas\ case XPRT_TRANSPORT_UDP: 648259875efSTrond Myklebust if (to->to_retries == 0) 649259875efSTrond Myklebust to->to_retries = NFS_DEF_UDP_RETRANS; 6505006a76cSDavid Howells if (!to->to_initval) 651259875efSTrond Myklebust to->to_initval = NFS_DEF_UDP_TIMEO * HZ / 10; 6525006a76cSDavid Howells if (to->to_initval > NFS_MAX_UDP_TIMEOUT) 6535006a76cSDavid Howells to->to_initval = NFS_MAX_UDP_TIMEOUT; 6545006a76cSDavid Howells to->to_maxval = NFS_MAX_UDP_TIMEOUT; 6555006a76cSDavid Howells to->to_exponential = 1; 6565006a76cSDavid Howells break; 657259875efSTrond Myklebust default: 658259875efSTrond Myklebust BUG(); 6595006a76cSDavid Howells } 6605006a76cSDavid Howells } 6615006a76cSDavid Howells 6625006a76cSDavid Howells /* 6635006a76cSDavid Howells * Create an RPC client handle 6645006a76cSDavid Howells */ 665*428360d7SBryan Schumaker int nfs_create_rpc_client(struct nfs_client *clp, 66633170233STrond Myklebust const struct rpc_timeout *timeparms, 6674bf590e0SChuck Lever rpc_authflavor_t flavor) 6685006a76cSDavid Howells { 6695006a76cSDavid Howells struct rpc_clnt *clnt = NULL; 67041877d20SChuck Lever struct rpc_create_args args = { 67173ea666cSChuck Lever .net = clp->cl_net, 67259dca3b2STrond Myklebust .protocol = clp->cl_proto, 67341877d20SChuck Lever .address = (struct sockaddr *)&clp->cl_addr, 6746e4cffd7SChuck Lever .addrsize = clp->cl_addrlen, 67533170233STrond Myklebust .timeout = timeparms, 67641877d20SChuck Lever .servername = clp->cl_hostname, 67741877d20SChuck Lever .program = &nfs_program, 67841877d20SChuck Lever .version = clp->rpc_ops->version, 67941877d20SChuck Lever .authflavor = flavor, 68041877d20SChuck Lever }; 6815006a76cSDavid Howells 6824bf590e0SChuck Lever if (test_bit(NFS_CS_DISCRTRY, &clp->cl_flags)) 6834a01b8a4SChuck Lever args.flags |= RPC_CLNT_CREATE_DISCRTRY; 6844bf590e0SChuck Lever if (test_bit(NFS_CS_NORESVPORT, &clp->cl_flags)) 6854a01b8a4SChuck Lever args.flags |= RPC_CLNT_CREATE_NONPRIVPORT; 6864a01b8a4SChuck Lever 6875006a76cSDavid Howells if (!IS_ERR(clp->cl_rpcclient)) 6885006a76cSDavid Howells return 0; 6895006a76cSDavid Howells 69041877d20SChuck Lever clnt = rpc_create(&args); 6915006a76cSDavid Howells if (IS_ERR(clnt)) { 6925006a76cSDavid Howells dprintk("%s: cannot create RPC client. Error = %ld\n", 6933110ff80SHarvey Harrison __func__, PTR_ERR(clnt)); 6945006a76cSDavid Howells return PTR_ERR(clnt); 6955006a76cSDavid Howells } 6965006a76cSDavid Howells 6975006a76cSDavid Howells clp->cl_rpcclient = clnt; 6985006a76cSDavid Howells return 0; 6995006a76cSDavid Howells } 70054ceac45SDavid Howells 70154ceac45SDavid Howells /* 70254ceac45SDavid Howells * Version 2 or 3 client destruction 70354ceac45SDavid Howells */ 70454ceac45SDavid Howells static void nfs_destroy_server(struct nfs_server *server) 70554ceac45SDavid Howells { 7065eebde23SSuresh Jayaraman if (!(server->flags & NFS_MOUNT_LOCAL_FLOCK) || 7075eebde23SSuresh Jayaraman !(server->flags & NFS_MOUNT_LOCAL_FCNTL)) 7089289e7f9SChuck Lever nlmclnt_done(server->nlm_host); 70954ceac45SDavid Howells } 71054ceac45SDavid Howells 71154ceac45SDavid Howells /* 71254ceac45SDavid Howells * Version 2 or 3 lockd setup 71354ceac45SDavid Howells */ 71454ceac45SDavid Howells static int nfs_start_lockd(struct nfs_server *server) 71554ceac45SDavid Howells { 7169289e7f9SChuck Lever struct nlm_host *host; 7179289e7f9SChuck Lever struct nfs_client *clp = server->nfs_client; 718883bb163SChuck Lever struct nlmclnt_initdata nlm_init = { 719883bb163SChuck Lever .hostname = clp->cl_hostname, 720883bb163SChuck Lever .address = (struct sockaddr *)&clp->cl_addr, 721883bb163SChuck Lever .addrlen = clp->cl_addrlen, 722883bb163SChuck Lever .nfs_version = clp->rpc_ops->version, 7230cb2659bSChuck Lever .noresvport = server->flags & NFS_MOUNT_NORESVPORT ? 7240cb2659bSChuck Lever 1 : 0, 72573ea666cSChuck Lever .net = clp->cl_net, 726883bb163SChuck Lever }; 72754ceac45SDavid Howells 728883bb163SChuck Lever if (nlm_init.nfs_version > 3) 7299289e7f9SChuck Lever return 0; 7305eebde23SSuresh Jayaraman if ((server->flags & NFS_MOUNT_LOCAL_FLOCK) && 7315eebde23SSuresh Jayaraman (server->flags & NFS_MOUNT_LOCAL_FCNTL)) 7329289e7f9SChuck Lever return 0; 7339289e7f9SChuck Lever 7348a6e5debSTrond Myklebust switch (clp->cl_proto) { 7358a6e5debSTrond Myklebust default: 7368a6e5debSTrond Myklebust nlm_init.protocol = IPPROTO_TCP; 7378a6e5debSTrond Myklebust break; 7388a6e5debSTrond Myklebust case XPRT_TRANSPORT_UDP: 7398a6e5debSTrond Myklebust nlm_init.protocol = IPPROTO_UDP; 7408a6e5debSTrond Myklebust } 7418a6e5debSTrond Myklebust 742883bb163SChuck Lever host = nlmclnt_init(&nlm_init); 7439289e7f9SChuck Lever if (IS_ERR(host)) 7449289e7f9SChuck Lever return PTR_ERR(host); 7459289e7f9SChuck Lever 7469289e7f9SChuck Lever server->nlm_host = host; 74754ceac45SDavid Howells server->destroy = nfs_destroy_server; 7489289e7f9SChuck Lever return 0; 74954ceac45SDavid Howells } 75054ceac45SDavid Howells 75154ceac45SDavid Howells /* 75254ceac45SDavid Howells * Initialise an NFSv3 ACL client connection 75354ceac45SDavid Howells */ 75454ceac45SDavid Howells #ifdef CONFIG_NFS_V3_ACL 75554ceac45SDavid Howells static void nfs_init_server_aclclient(struct nfs_server *server) 75654ceac45SDavid Howells { 75740c55319STrond Myklebust if (server->nfs_client->rpc_ops->version != 3) 75854ceac45SDavid Howells goto out_noacl; 75954ceac45SDavid Howells if (server->flags & NFS_MOUNT_NOACL) 76054ceac45SDavid Howells goto out_noacl; 76154ceac45SDavid Howells 76254ceac45SDavid Howells server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3); 76354ceac45SDavid Howells if (IS_ERR(server->client_acl)) 76454ceac45SDavid Howells goto out_noacl; 76554ceac45SDavid Howells 76654ceac45SDavid Howells /* No errors! Assume that Sun nfsacls are supported */ 76754ceac45SDavid Howells server->caps |= NFS_CAP_ACLS; 76854ceac45SDavid Howells return; 76954ceac45SDavid Howells 77054ceac45SDavid Howells out_noacl: 77154ceac45SDavid Howells server->caps &= ~NFS_CAP_ACLS; 77254ceac45SDavid Howells } 77354ceac45SDavid Howells #else 77454ceac45SDavid Howells static inline void nfs_init_server_aclclient(struct nfs_server *server) 77554ceac45SDavid Howells { 77654ceac45SDavid Howells server->flags &= ~NFS_MOUNT_NOACL; 77754ceac45SDavid Howells server->caps &= ~NFS_CAP_ACLS; 77854ceac45SDavid Howells } 77954ceac45SDavid Howells #endif 78054ceac45SDavid Howells 78154ceac45SDavid Howells /* 78254ceac45SDavid Howells * Create a general RPC client 78354ceac45SDavid Howells */ 78433170233STrond Myklebust static int nfs_init_server_rpcclient(struct nfs_server *server, 78533170233STrond Myklebust const struct rpc_timeout *timeo, 78633170233STrond Myklebust rpc_authflavor_t pseudoflavour) 78754ceac45SDavid Howells { 78854ceac45SDavid Howells struct nfs_client *clp = server->nfs_client; 78954ceac45SDavid Howells 79054ceac45SDavid Howells server->client = rpc_clone_client(clp->cl_rpcclient); 79154ceac45SDavid Howells if (IS_ERR(server->client)) { 7923110ff80SHarvey Harrison dprintk("%s: couldn't create rpc_client!\n", __func__); 79354ceac45SDavid Howells return PTR_ERR(server->client); 79454ceac45SDavid Howells } 79554ceac45SDavid Howells 79633170233STrond Myklebust memcpy(&server->client->cl_timeout_default, 79733170233STrond Myklebust timeo, 79833170233STrond Myklebust sizeof(server->client->cl_timeout_default)); 79933170233STrond Myklebust server->client->cl_timeout = &server->client->cl_timeout_default; 80033170233STrond Myklebust 80154ceac45SDavid Howells if (pseudoflavour != clp->cl_rpcclient->cl_auth->au_flavor) { 80254ceac45SDavid Howells struct rpc_auth *auth; 80354ceac45SDavid Howells 80454ceac45SDavid Howells auth = rpcauth_create(pseudoflavour, server->client); 80554ceac45SDavid Howells if (IS_ERR(auth)) { 8063110ff80SHarvey Harrison dprintk("%s: couldn't create credcache!\n", __func__); 80754ceac45SDavid Howells return PTR_ERR(auth); 80854ceac45SDavid Howells } 80954ceac45SDavid Howells } 81054ceac45SDavid Howells server->client->cl_softrtry = 0; 81154ceac45SDavid Howells if (server->flags & NFS_MOUNT_SOFT) 81254ceac45SDavid Howells server->client->cl_softrtry = 1; 81354ceac45SDavid Howells 81454ceac45SDavid Howells return 0; 81554ceac45SDavid Howells } 81654ceac45SDavid Howells 8178cab4c39SChuck Lever /** 8188cab4c39SChuck Lever * nfs_init_client - Initialise an NFS2 or NFS3 client 8198cab4c39SChuck Lever * 8208cab4c39SChuck Lever * @clp: nfs_client to initialise 8218cab4c39SChuck Lever * @timeparms: timeout parameters for underlying RPC transport 8228cab4c39SChuck Lever * @ip_addr: IP presentation address (not used) 8238cab4c39SChuck Lever * @authflavor: authentication flavor for underlying RPC transport 8248cab4c39SChuck Lever * 8258cab4c39SChuck Lever * Returns pointer to an NFS client, or an ERR_PTR value. 82654ceac45SDavid Howells */ 8278cab4c39SChuck Lever struct nfs_client *nfs_init_client(struct nfs_client *clp, 8288cab4c39SChuck Lever const struct rpc_timeout *timeparms, 8294bf590e0SChuck Lever const char *ip_addr, rpc_authflavor_t authflavour) 83054ceac45SDavid Howells { 83154ceac45SDavid Howells int error; 83254ceac45SDavid Howells 83354ceac45SDavid Howells if (clp->cl_cons_state == NFS_CS_READY) { 83454ceac45SDavid Howells /* the client is already initialised */ 83554ceac45SDavid Howells dprintk("<-- nfs_init_client() = 0 [already %p]\n", clp); 8368cab4c39SChuck Lever return clp; 83754ceac45SDavid Howells } 83854ceac45SDavid Howells 83954ceac45SDavid Howells /* 84054ceac45SDavid Howells * Create a client RPC handle for doing FSSTAT with UNIX auth only 84154ceac45SDavid Howells * - RFC 2623, sec 2.3.2 84254ceac45SDavid Howells */ 8434bf590e0SChuck Lever error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX); 84454ceac45SDavid Howells if (error < 0) 84554ceac45SDavid Howells goto error; 84654ceac45SDavid Howells nfs_mark_client_ready(clp, NFS_CS_READY); 8478cab4c39SChuck Lever return clp; 84854ceac45SDavid Howells 84954ceac45SDavid Howells error: 85054ceac45SDavid Howells nfs_mark_client_ready(clp, error); 8518cab4c39SChuck Lever nfs_put_client(clp); 85254ceac45SDavid Howells dprintk("<-- nfs_init_client() = xerror %d\n", error); 8538cab4c39SChuck Lever return ERR_PTR(error); 85454ceac45SDavid Howells } 85554ceac45SDavid Howells 85654ceac45SDavid Howells /* 85754ceac45SDavid Howells * Create a version 2 or 3 client 85854ceac45SDavid Howells */ 8592283f8d6S\"Talpey, Thomas\ static int nfs_init_server(struct nfs_server *server, 8602283f8d6S\"Talpey, Thomas\ const struct nfs_parsed_mount_data *data) 86154ceac45SDavid Howells { 8623a498026STrond Myklebust struct nfs_client_initdata cl_init = { 8633a498026STrond Myklebust .hostname = data->nfs_server.hostname, 864d7422c47SChuck Lever .addr = (const struct sockaddr *)&data->nfs_server.address, 8654c568017SChuck Lever .addrlen = data->nfs_server.addrlen, 8662ba68002SBryan Schumaker .rpc_ops = NULL, 86759dca3b2STrond Myklebust .proto = data->nfs_server.protocol, 868e50a7a1aSStanislav Kinsbursky .net = data->net, 8693a498026STrond Myklebust }; 87033170233STrond Myklebust struct rpc_timeout timeparms; 87154ceac45SDavid Howells struct nfs_client *clp; 8723a498026STrond Myklebust int error; 87354ceac45SDavid Howells 87454ceac45SDavid Howells dprintk("--> nfs_init_server()\n"); 87554ceac45SDavid Howells 8762ba68002SBryan Schumaker switch (data->version) { 8772ba68002SBryan Schumaker #ifdef CONFIG_NFS_V2 8782ba68002SBryan Schumaker case 2: 8792ba68002SBryan Schumaker cl_init.rpc_ops = &nfs_v2_clientops; 8802ba68002SBryan Schumaker break; 88154ceac45SDavid Howells #endif 8822ba68002SBryan Schumaker #ifdef CONFIG_NFS_V3 8832ba68002SBryan Schumaker case 3: 8842ba68002SBryan Schumaker cl_init.rpc_ops = &nfs_v3_clientops; 8852ba68002SBryan Schumaker break; 8862ba68002SBryan Schumaker #endif 8872ba68002SBryan Schumaker default: 8882ba68002SBryan Schumaker return -EPROTONOSUPPORT; 8892ba68002SBryan Schumaker } 89054ceac45SDavid Howells 89145a52a02SAndy Adamson nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, 89245a52a02SAndy Adamson data->timeo, data->retrans); 8934bf590e0SChuck Lever if (data->flags & NFS_MOUNT_NORESVPORT) 8944bf590e0SChuck Lever set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags); 89545a52a02SAndy Adamson 89654ceac45SDavid Howells /* Allocate or find a client reference we can use */ 8974bf590e0SChuck Lever clp = nfs_get_client(&cl_init, &timeparms, NULL, RPC_AUTH_UNIX); 89854ceac45SDavid Howells if (IS_ERR(clp)) { 89954ceac45SDavid Howells dprintk("<-- nfs_init_server() = error %ld\n", PTR_ERR(clp)); 90054ceac45SDavid Howells return PTR_ERR(clp); 90154ceac45SDavid Howells } 90254ceac45SDavid Howells 90354ceac45SDavid Howells server->nfs_client = clp; 90454ceac45SDavid Howells 90554ceac45SDavid Howells /* Initialise the client representation from the mount data */ 906ff3525a5STrond Myklebust server->flags = data->flags; 907b797cac7SDavid Howells server->options = data->options; 90862ab460cSTrond Myklebust server->caps |= NFS_CAP_HARDLINKS|NFS_CAP_SYMLINKS|NFS_CAP_FILEID| 90962ab460cSTrond Myklebust NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|NFS_CAP_OWNER_GROUP| 9103a1556e8STrond Myklebust NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME|NFS_CAP_CHANGE_ATTR; 91154ceac45SDavid Howells 91254ceac45SDavid Howells if (data->rsize) 91354ceac45SDavid Howells server->rsize = nfs_block_size(data->rsize, NULL); 91454ceac45SDavid Howells if (data->wsize) 91554ceac45SDavid Howells server->wsize = nfs_block_size(data->wsize, NULL); 91654ceac45SDavid Howells 91754ceac45SDavid Howells server->acregmin = data->acregmin * HZ; 91854ceac45SDavid Howells server->acregmax = data->acregmax * HZ; 91954ceac45SDavid Howells server->acdirmin = data->acdirmin * HZ; 92054ceac45SDavid Howells server->acdirmax = data->acdirmax * HZ; 92154ceac45SDavid Howells 92254ceac45SDavid Howells /* Start lockd here, before we might error out */ 92354ceac45SDavid Howells error = nfs_start_lockd(server); 92454ceac45SDavid Howells if (error < 0) 92554ceac45SDavid Howells goto error; 92654ceac45SDavid Howells 927f22d6d79SChuck Lever server->port = data->nfs_server.port; 928f22d6d79SChuck Lever 92933170233STrond Myklebust error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]); 93054ceac45SDavid Howells if (error < 0) 93154ceac45SDavid Howells goto error; 93254ceac45SDavid Howells 9333f8400d1SChuck Lever /* Preserve the values of mount_server-related mount options */ 9343f8400d1SChuck Lever if (data->mount_server.addrlen) { 9353f8400d1SChuck Lever memcpy(&server->mountd_address, &data->mount_server.address, 9363f8400d1SChuck Lever data->mount_server.addrlen); 9373f8400d1SChuck Lever server->mountd_addrlen = data->mount_server.addrlen; 9383f8400d1SChuck Lever } 9393f8400d1SChuck Lever server->mountd_version = data->mount_server.version; 9403f8400d1SChuck Lever server->mountd_port = data->mount_server.port; 9413f8400d1SChuck Lever server->mountd_protocol = data->mount_server.protocol; 9423f8400d1SChuck Lever 94354ceac45SDavid Howells server->namelen = data->namlen; 94454ceac45SDavid Howells /* Create a client RPC handle for the NFSv3 ACL management interface */ 94554ceac45SDavid Howells nfs_init_server_aclclient(server); 94654ceac45SDavid Howells dprintk("<-- nfs_init_server() = 0 [new %p]\n", clp); 94754ceac45SDavid Howells return 0; 94854ceac45SDavid Howells 94954ceac45SDavid Howells error: 95054ceac45SDavid Howells server->nfs_client = NULL; 95154ceac45SDavid Howells nfs_put_client(clp); 95254ceac45SDavid Howells dprintk("<-- nfs_init_server() = xerror %d\n", error); 95354ceac45SDavid Howells return error; 95454ceac45SDavid Howells } 95554ceac45SDavid Howells 95654ceac45SDavid Howells /* 95754ceac45SDavid Howells * Load up the server record from information gained in an fsinfo record 95854ceac45SDavid Howells */ 959738fd0f3SBenny Halevy static void nfs_server_set_fsinfo(struct nfs_server *server, 960738fd0f3SBenny Halevy struct nfs_fh *mntfh, 961738fd0f3SBenny Halevy struct nfs_fsinfo *fsinfo) 96254ceac45SDavid Howells { 96354ceac45SDavid Howells unsigned long max_rpc_payload; 96454ceac45SDavid Howells 96554ceac45SDavid Howells /* Work out a lot of parameters */ 96654ceac45SDavid Howells if (server->rsize == 0) 96754ceac45SDavid Howells server->rsize = nfs_block_size(fsinfo->rtpref, NULL); 96854ceac45SDavid Howells if (server->wsize == 0) 96954ceac45SDavid Howells server->wsize = nfs_block_size(fsinfo->wtpref, NULL); 97054ceac45SDavid Howells 97154ceac45SDavid Howells if (fsinfo->rtmax >= 512 && server->rsize > fsinfo->rtmax) 97254ceac45SDavid Howells server->rsize = nfs_block_size(fsinfo->rtmax, NULL); 97354ceac45SDavid Howells if (fsinfo->wtmax >= 512 && server->wsize > fsinfo->wtmax) 97454ceac45SDavid Howells server->wsize = nfs_block_size(fsinfo->wtmax, NULL); 97554ceac45SDavid Howells 97654ceac45SDavid Howells max_rpc_payload = nfs_block_size(rpc_max_payload(server->client), NULL); 97754ceac45SDavid Howells if (server->rsize > max_rpc_payload) 97854ceac45SDavid Howells server->rsize = max_rpc_payload; 97954ceac45SDavid Howells if (server->rsize > NFS_MAX_FILE_IO_SIZE) 98054ceac45SDavid Howells server->rsize = NFS_MAX_FILE_IO_SIZE; 98154ceac45SDavid Howells server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 982e0bf68ddSPeter Zijlstra 983d993831fSJens Axboe server->backing_dev_info.name = "nfs"; 98454ceac45SDavid Howells server->backing_dev_info.ra_pages = server->rpages * NFS_MAX_READAHEAD; 98554ceac45SDavid Howells 98654ceac45SDavid Howells if (server->wsize > max_rpc_payload) 98754ceac45SDavid Howells server->wsize = max_rpc_payload; 98854ceac45SDavid Howells if (server->wsize > NFS_MAX_FILE_IO_SIZE) 98954ceac45SDavid Howells server->wsize = NFS_MAX_FILE_IO_SIZE; 99054ceac45SDavid Howells server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 991dae100c2SFred Isaman server->pnfs_blksize = fsinfo->blksize; 99285e174baSRicardo Labiaga 99354ceac45SDavid Howells server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL); 99454ceac45SDavid Howells 99554ceac45SDavid Howells server->dtsize = nfs_block_size(fsinfo->dtpref, NULL); 99656e4ebf8SBryan Schumaker if (server->dtsize > PAGE_CACHE_SIZE * NFS_MAX_READDIR_PAGES) 99756e4ebf8SBryan Schumaker server->dtsize = PAGE_CACHE_SIZE * NFS_MAX_READDIR_PAGES; 99854ceac45SDavid Howells if (server->dtsize > server->rsize) 99954ceac45SDavid Howells server->dtsize = server->rsize; 100054ceac45SDavid Howells 100154ceac45SDavid Howells if (server->flags & NFS_MOUNT_NOAC) { 100254ceac45SDavid Howells server->acregmin = server->acregmax = 0; 100354ceac45SDavid Howells server->acdirmin = server->acdirmax = 0; 100454ceac45SDavid Howells } 100554ceac45SDavid Howells 100654ceac45SDavid Howells server->maxfilesize = fsinfo->maxfilesize; 100754ceac45SDavid Howells 10086b96724eSRicardo Labiaga server->time_delta = fsinfo->time_delta; 10096b96724eSRicardo Labiaga 101054ceac45SDavid Howells /* We're airborne Set socket buffersize */ 101154ceac45SDavid Howells rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100); 101254ceac45SDavid Howells } 101354ceac45SDavid Howells 101454ceac45SDavid Howells /* 101554ceac45SDavid Howells * Probe filesystem information, including the FSID on v2/v3 101654ceac45SDavid Howells */ 101754ceac45SDavid Howells static int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr) 101854ceac45SDavid Howells { 101954ceac45SDavid Howells struct nfs_fsinfo fsinfo; 102054ceac45SDavid Howells struct nfs_client *clp = server->nfs_client; 102154ceac45SDavid Howells int error; 102254ceac45SDavid Howells 102354ceac45SDavid Howells dprintk("--> nfs_probe_fsinfo()\n"); 102454ceac45SDavid Howells 102554ceac45SDavid Howells if (clp->rpc_ops->set_capabilities != NULL) { 102654ceac45SDavid Howells error = clp->rpc_ops->set_capabilities(server, mntfh); 102754ceac45SDavid Howells if (error < 0) 102854ceac45SDavid Howells goto out_error; 102954ceac45SDavid Howells } 103054ceac45SDavid Howells 103154ceac45SDavid Howells fsinfo.fattr = fattr; 103285e174baSRicardo Labiaga fsinfo.layouttype = 0; 103354ceac45SDavid Howells error = clp->rpc_ops->fsinfo(server, mntfh, &fsinfo); 103454ceac45SDavid Howells if (error < 0) 103554ceac45SDavid Howells goto out_error; 103654ceac45SDavid Howells 1037738fd0f3SBenny Halevy nfs_server_set_fsinfo(server, mntfh, &fsinfo); 103854ceac45SDavid Howells 103954ceac45SDavid Howells /* Get some general file system info */ 104054ceac45SDavid Howells if (server->namelen == 0) { 104154ceac45SDavid Howells struct nfs_pathconf pathinfo; 104254ceac45SDavid Howells 104354ceac45SDavid Howells pathinfo.fattr = fattr; 104454ceac45SDavid Howells nfs_fattr_init(fattr); 104554ceac45SDavid Howells 104654ceac45SDavid Howells if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0) 104754ceac45SDavid Howells server->namelen = pathinfo.max_namelen; 104854ceac45SDavid Howells } 104954ceac45SDavid Howells 105054ceac45SDavid Howells dprintk("<-- nfs_probe_fsinfo() = 0\n"); 105154ceac45SDavid Howells return 0; 105254ceac45SDavid Howells 105354ceac45SDavid Howells out_error: 105454ceac45SDavid Howells dprintk("nfs_probe_fsinfo: error = %d\n", -error); 105554ceac45SDavid Howells return error; 105654ceac45SDavid Howells } 105754ceac45SDavid Howells 105854ceac45SDavid Howells /* 105954ceac45SDavid Howells * Copy useful information when duplicating a server record 106054ceac45SDavid Howells */ 106154ceac45SDavid Howells static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source) 106254ceac45SDavid Howells { 106354ceac45SDavid Howells target->flags = source->flags; 1064356e76b8SChuck Lever target->rsize = source->rsize; 1065356e76b8SChuck Lever target->wsize = source->wsize; 106654ceac45SDavid Howells target->acregmin = source->acregmin; 106754ceac45SDavid Howells target->acregmax = source->acregmax; 106854ceac45SDavid Howells target->acdirmin = source->acdirmin; 106954ceac45SDavid Howells target->acdirmax = source->acdirmax; 107054ceac45SDavid Howells target->caps = source->caps; 10712df54806SDavid Howells target->options = source->options; 107254ceac45SDavid Howells } 107354ceac45SDavid Howells 1074fca5238eSChuck Lever static void nfs_server_insert_lists(struct nfs_server *server) 1075fca5238eSChuck Lever { 1076fca5238eSChuck Lever struct nfs_client *clp = server->nfs_client; 107773ea666cSChuck Lever struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); 1078fca5238eSChuck Lever 1079dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 1080fca5238eSChuck Lever list_add_tail_rcu(&server->client_link, &clp->cl_superblocks); 1081c25d32b2SStanislav Kinsbursky list_add_tail(&server->master_link, &nn->nfs_volume_list); 1082d3b4c9d7SAndy Adamson clear_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state); 1083dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 1084fca5238eSChuck Lever 1085fca5238eSChuck Lever } 1086fca5238eSChuck Lever 1087fca5238eSChuck Lever static void nfs_server_remove_lists(struct nfs_server *server) 1088fca5238eSChuck Lever { 1089d3b4c9d7SAndy Adamson struct nfs_client *clp = server->nfs_client; 10904c03ae4aSTrond Myklebust struct nfs_net *nn; 1091d3b4c9d7SAndy Adamson 10924c03ae4aSTrond Myklebust if (clp == NULL) 10934c03ae4aSTrond Myklebust return; 109473ea666cSChuck Lever nn = net_generic(clp->cl_net, nfs_net_id); 1095dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 1096fca5238eSChuck Lever list_del_rcu(&server->client_link); 10974c03ae4aSTrond Myklebust if (list_empty(&clp->cl_superblocks)) 1098d3b4c9d7SAndy Adamson set_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state); 1099fca5238eSChuck Lever list_del(&server->master_link); 1100dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 1101fca5238eSChuck Lever 1102fca5238eSChuck Lever synchronize_rcu(); 1103fca5238eSChuck Lever } 1104fca5238eSChuck Lever 110554ceac45SDavid Howells /* 110654ceac45SDavid Howells * Allocate and initialise a server record 110754ceac45SDavid Howells */ 110854ceac45SDavid Howells static struct nfs_server *nfs_alloc_server(void) 110954ceac45SDavid Howells { 111054ceac45SDavid Howells struct nfs_server *server; 111154ceac45SDavid Howells 111254ceac45SDavid Howells server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL); 111354ceac45SDavid Howells if (!server) 111454ceac45SDavid Howells return NULL; 111554ceac45SDavid Howells 111654ceac45SDavid Howells server->client = server->client_acl = ERR_PTR(-EINVAL); 111754ceac45SDavid Howells 111854ceac45SDavid Howells /* Zero out the NFS state stuff */ 111954ceac45SDavid Howells INIT_LIST_HEAD(&server->client_link); 112054ceac45SDavid Howells INIT_LIST_HEAD(&server->master_link); 1121d3978bb3SChuck Lever INIT_LIST_HEAD(&server->delegations); 11226382a441SWeston Andros Adamson INIT_LIST_HEAD(&server->layouts); 11230aaaf5c4SChuck Lever INIT_LIST_HEAD(&server->state_owners_lru); 112454ceac45SDavid Howells 1125ef818a28SSteve Dickson atomic_set(&server->active, 0); 1126ef818a28SSteve Dickson 112754ceac45SDavid Howells server->io_stats = nfs_alloc_iostats(); 112854ceac45SDavid Howells if (!server->io_stats) { 112954ceac45SDavid Howells kfree(server); 113054ceac45SDavid Howells return NULL; 113154ceac45SDavid Howells } 113254ceac45SDavid Howells 113348d07649SJens Axboe if (bdi_init(&server->backing_dev_info)) { 113448d07649SJens Axboe nfs_free_iostats(server->io_stats); 113548d07649SJens Axboe kfree(server); 113648d07649SJens Axboe return NULL; 113748d07649SJens Axboe } 113848d07649SJens Axboe 11399157c31dSTrond Myklebust ida_init(&server->openowner_id); 1140d2d7ce28STrond Myklebust ida_init(&server->lockowner_id); 1141f7e8917aSFred Isaman pnfs_init_server(server); 1142f7e8917aSFred Isaman 114354ceac45SDavid Howells return server; 114454ceac45SDavid Howells } 114554ceac45SDavid Howells 114654ceac45SDavid Howells /* 114754ceac45SDavid Howells * Free up a server record 114854ceac45SDavid Howells */ 114954ceac45SDavid Howells void nfs_free_server(struct nfs_server *server) 115054ceac45SDavid Howells { 115154ceac45SDavid Howells dprintk("--> nfs_free_server()\n"); 115254ceac45SDavid Howells 1153fca5238eSChuck Lever nfs_server_remove_lists(server); 115454ceac45SDavid Howells 115554ceac45SDavid Howells if (server->destroy != NULL) 115654ceac45SDavid Howells server->destroy(server); 11575cef338bSTrond Myklebust 11585cef338bSTrond Myklebust if (!IS_ERR(server->client_acl)) 11595cef338bSTrond Myklebust rpc_shutdown_client(server->client_acl); 116054ceac45SDavid Howells if (!IS_ERR(server->client)) 116154ceac45SDavid Howells rpc_shutdown_client(server->client); 116254ceac45SDavid Howells 116354ceac45SDavid Howells nfs_put_client(server->nfs_client); 116454ceac45SDavid Howells 1165d2d7ce28STrond Myklebust ida_destroy(&server->lockowner_id); 11669157c31dSTrond Myklebust ida_destroy(&server->openowner_id); 116754ceac45SDavid Howells nfs_free_iostats(server->io_stats); 1168e0bf68ddSPeter Zijlstra bdi_destroy(&server->backing_dev_info); 116954ceac45SDavid Howells kfree(server); 117054ceac45SDavid Howells nfs_release_automount_timer(); 117154ceac45SDavid Howells dprintk("<-- nfs_free_server()\n"); 117254ceac45SDavid Howells } 117354ceac45SDavid Howells 117454ceac45SDavid Howells /* 117554ceac45SDavid Howells * Create a version 2 or 3 volume record 117654ceac45SDavid Howells * - keyed on server and FSID 117754ceac45SDavid Howells */ 11782283f8d6S\"Talpey, Thomas\ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, 117954ceac45SDavid Howells struct nfs_fh *mntfh) 118054ceac45SDavid Howells { 118154ceac45SDavid Howells struct nfs_server *server; 1182fbca779aSTrond Myklebust struct nfs_fattr *fattr; 118354ceac45SDavid Howells int error; 118454ceac45SDavid Howells 118554ceac45SDavid Howells server = nfs_alloc_server(); 118654ceac45SDavid Howells if (!server) 118754ceac45SDavid Howells return ERR_PTR(-ENOMEM); 118854ceac45SDavid Howells 1189fbca779aSTrond Myklebust error = -ENOMEM; 1190fbca779aSTrond Myklebust fattr = nfs_alloc_fattr(); 1191fbca779aSTrond Myklebust if (fattr == NULL) 1192fbca779aSTrond Myklebust goto error; 1193fbca779aSTrond Myklebust 119454ceac45SDavid Howells /* Get a client representation */ 119554ceac45SDavid Howells error = nfs_init_server(server, data); 119654ceac45SDavid Howells if (error < 0) 119754ceac45SDavid Howells goto error; 119854ceac45SDavid Howells 119954ceac45SDavid Howells BUG_ON(!server->nfs_client); 120054ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops); 120154ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 120254ceac45SDavid Howells 120354ceac45SDavid Howells /* Probe the root fh to retrieve its FSID */ 1204fbca779aSTrond Myklebust error = nfs_probe_fsinfo(server, mntfh, fattr); 120554ceac45SDavid Howells if (error < 0) 120654ceac45SDavid Howells goto error; 120754af3bb5STrond Myklebust if (server->nfs_client->rpc_ops->version == 3) { 120854af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) 120954af3bb5STrond Myklebust server->namelen = NFS3_MAXNAMLEN; 121054af3bb5STrond Myklebust if (!(data->flags & NFS_MOUNT_NORDIRPLUS)) 121154af3bb5STrond Myklebust server->caps |= NFS_CAP_READDIRPLUS; 121254af3bb5STrond Myklebust } else { 121354af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN) 121454af3bb5STrond Myklebust server->namelen = NFS2_MAXNAMLEN; 121554af3bb5STrond Myklebust } 121654af3bb5STrond Myklebust 1217fbca779aSTrond Myklebust if (!(fattr->valid & NFS_ATTR_FATTR)) { 1218fbca779aSTrond Myklebust error = server->nfs_client->rpc_ops->getattr(server, mntfh, fattr); 121954ceac45SDavid Howells if (error < 0) { 122054ceac45SDavid Howells dprintk("nfs_create_server: getattr error = %d\n", -error); 122154ceac45SDavid Howells goto error; 122254ceac45SDavid Howells } 122354ceac45SDavid Howells } 1224fbca779aSTrond Myklebust memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid)); 122554ceac45SDavid Howells 12266daabf1bSDavid Howells dprintk("Server FSID: %llx:%llx\n", 12276daabf1bSDavid Howells (unsigned long long) server->fsid.major, 12286daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 122954ceac45SDavid Howells 1230fca5238eSChuck Lever nfs_server_insert_lists(server); 123154ceac45SDavid Howells server->mount_time = jiffies; 1232fbca779aSTrond Myklebust nfs_free_fattr(fattr); 123354ceac45SDavid Howells return server; 123454ceac45SDavid Howells 123554ceac45SDavid Howells error: 1236fbca779aSTrond Myklebust nfs_free_fattr(fattr); 123754ceac45SDavid Howells nfs_free_server(server); 123854ceac45SDavid Howells return ERR_PTR(error); 123954ceac45SDavid Howells } 124054ceac45SDavid Howells 124154ceac45SDavid Howells #ifdef CONFIG_NFS_V4 124254ceac45SDavid Howells /* 1243c36fca52SAndy Adamson * NFSv4.0 callback thread helper 1244c36fca52SAndy Adamson * 1245c36fca52SAndy Adamson * Find a client by callback identifier 1246c36fca52SAndy Adamson */ 1247c36fca52SAndy Adamson struct nfs_client * 124828cd1b3fSStanislav Kinsbursky nfs4_find_client_ident(struct net *net, int cb_ident) 1249c36fca52SAndy Adamson { 1250c36fca52SAndy Adamson struct nfs_client *clp; 125128cd1b3fSStanislav Kinsbursky struct nfs_net *nn = net_generic(net, nfs_net_id); 1252c36fca52SAndy Adamson 1253dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 125428cd1b3fSStanislav Kinsbursky clp = idr_find(&nn->cb_ident_idr, cb_ident); 1255c36fca52SAndy Adamson if (clp) 1256c36fca52SAndy Adamson atomic_inc(&clp->cl_count); 1257dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 1258c36fca52SAndy Adamson return clp; 1259c36fca52SAndy Adamson } 1260c36fca52SAndy Adamson 1261c36fca52SAndy Adamson #if defined(CONFIG_NFS_V4_1) 1262c36fca52SAndy Adamson /* 1263c36fca52SAndy Adamson * NFSv4.1 callback thread helper 1264c36fca52SAndy Adamson * For CB_COMPOUND calls, find a client by IP address, protocol version, 1265c36fca52SAndy Adamson * minorversion, and sessionID 1266c36fca52SAndy Adamson * 1267c36fca52SAndy Adamson * Returns NULL if no such client 1268c36fca52SAndy Adamson */ 1269c36fca52SAndy Adamson struct nfs_client * 1270c7add9a9SStanislav Kinsbursky nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr, 1271778be232SAndy Adamson struct nfs4_sessionid *sid) 1272c36fca52SAndy Adamson { 1273c36fca52SAndy Adamson struct nfs_client *clp; 1274c7add9a9SStanislav Kinsbursky struct nfs_net *nn = net_generic(net, nfs_net_id); 1275c36fca52SAndy Adamson 1276dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 12776b13168bSStanislav Kinsbursky list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) { 1278c36fca52SAndy Adamson if (nfs4_cb_match_client(addr, clp, 1) == false) 1279c36fca52SAndy Adamson continue; 1280c36fca52SAndy Adamson 1281c36fca52SAndy Adamson if (!nfs4_has_session(clp)) 1282c36fca52SAndy Adamson continue; 1283c36fca52SAndy Adamson 1284778be232SAndy Adamson /* Match sessionid*/ 1285778be232SAndy Adamson if (memcmp(clp->cl_session->sess_id.data, 1286778be232SAndy Adamson sid->data, NFS4_MAX_SESSIONID_LEN) != 0) 1287c36fca52SAndy Adamson continue; 1288c36fca52SAndy Adamson 1289c36fca52SAndy Adamson atomic_inc(&clp->cl_count); 1290dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 1291c36fca52SAndy Adamson return clp; 1292c36fca52SAndy Adamson } 1293dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 1294c36fca52SAndy Adamson return NULL; 1295c36fca52SAndy Adamson } 1296c36fca52SAndy Adamson 1297c36fca52SAndy Adamson #else /* CONFIG_NFS_V4_1 */ 1298c36fca52SAndy Adamson 1299c36fca52SAndy Adamson struct nfs_client * 1300b6d1e83bSStanislav Kinsbursky nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr, 1301778be232SAndy Adamson struct nfs4_sessionid *sid) 1302c36fca52SAndy Adamson { 1303c36fca52SAndy Adamson return NULL; 1304c36fca52SAndy Adamson } 1305c36fca52SAndy Adamson #endif /* CONFIG_NFS_V4_1 */ 1306c36fca52SAndy Adamson 1307c36fca52SAndy Adamson /* 130854ceac45SDavid Howells * Set up an NFS4 client 130954ceac45SDavid Howells */ 131054ceac45SDavid Howells static int nfs4_set_client(struct nfs_server *server, 1311dcecae0fSChuck Lever const char *hostname, 1312dcecae0fSChuck Lever const struct sockaddr *addr, 1313dcecae0fSChuck Lever const size_t addrlen, 13147d9ac06fSJ. Bruce Fields const char *ip_addr, 131554ceac45SDavid Howells rpc_authflavor_t authflavour, 131694a417f3SBenny Halevy int proto, const struct rpc_timeout *timeparms, 1317e50a7a1aSStanislav Kinsbursky u32 minorversion, struct net *net) 131854ceac45SDavid Howells { 13193a498026STrond Myklebust struct nfs_client_initdata cl_init = { 13203a498026STrond Myklebust .hostname = hostname, 1321dcecae0fSChuck Lever .addr = addr, 1322dcecae0fSChuck Lever .addrlen = addrlen, 132340c55319STrond Myklebust .rpc_ops = &nfs_v4_clientops, 132459dca3b2STrond Myklebust .proto = proto, 13255aae4a9aSBenny Halevy .minorversion = minorversion, 1326e50a7a1aSStanislav Kinsbursky .net = net, 13273a498026STrond Myklebust }; 132854ceac45SDavid Howells struct nfs_client *clp; 132954ceac45SDavid Howells int error; 133054ceac45SDavid Howells 133154ceac45SDavid Howells dprintk("--> nfs4_set_client()\n"); 133254ceac45SDavid Howells 13334bf590e0SChuck Lever if (server->flags & NFS_MOUNT_NORESVPORT) 13344bf590e0SChuck Lever set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags); 13354bf590e0SChuck Lever 133654ceac45SDavid Howells /* Allocate or find a client reference we can use */ 13374bf590e0SChuck Lever clp = nfs_get_client(&cl_init, timeparms, ip_addr, authflavour); 133854ceac45SDavid Howells if (IS_ERR(clp)) { 133954ceac45SDavid Howells error = PTR_ERR(clp); 134054ceac45SDavid Howells goto error; 134154ceac45SDavid Howells } 134254ceac45SDavid Howells 1343d6fb79d4SAndy Adamson /* 1344d6fb79d4SAndy Adamson * Query for the lease time on clientid setup or renewal 1345d6fb79d4SAndy Adamson * 1346d6fb79d4SAndy Adamson * Note that this will be set on nfs_clients that were created 1347d6fb79d4SAndy Adamson * only for the DS role and did not set this bit, but now will 1348d6fb79d4SAndy Adamson * serve a dual role. 1349d6fb79d4SAndy Adamson */ 1350d6fb79d4SAndy Adamson set_bit(NFS_CS_CHECK_LEASE_TIME, &clp->cl_res_state); 1351d6fb79d4SAndy Adamson 135254ceac45SDavid Howells server->nfs_client = clp; 135354ceac45SDavid Howells dprintk("<-- nfs4_set_client() = 0 [new %p]\n", clp); 135454ceac45SDavid Howells return 0; 135554ceac45SDavid Howells error: 135654ceac45SDavid Howells dprintk("<-- nfs4_set_client() = xerror %d\n", error); 135754ceac45SDavid Howells return error; 135854ceac45SDavid Howells } 135954ceac45SDavid Howells 1360d83217c1SAndy Adamson /* 1361d83217c1SAndy Adamson * Set up a pNFS Data Server client. 1362d83217c1SAndy Adamson * 1363d83217c1SAndy Adamson * Return any existing nfs_client that matches server address,port,version 1364d83217c1SAndy Adamson * and minorversion. 1365d83217c1SAndy Adamson * 1366d83217c1SAndy Adamson * For a new nfs_client, use a soft mount (default), a low retrans and a 1367d83217c1SAndy Adamson * low timeout interval so that if a connection is lost, we retry through 1368d83217c1SAndy Adamson * the MDS. 1369d83217c1SAndy Adamson */ 1370d83217c1SAndy Adamson struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp, 137198fc685aSAndy Adamson const struct sockaddr *ds_addr, int ds_addrlen, 137298fc685aSAndy Adamson int ds_proto, unsigned int ds_timeo, unsigned int ds_retrans) 1373d83217c1SAndy Adamson { 1374d83217c1SAndy Adamson struct nfs_client_initdata cl_init = { 1375d83217c1SAndy Adamson .addr = ds_addr, 1376d83217c1SAndy Adamson .addrlen = ds_addrlen, 1377d83217c1SAndy Adamson .rpc_ops = &nfs_v4_clientops, 1378d83217c1SAndy Adamson .proto = ds_proto, 1379d83217c1SAndy Adamson .minorversion = mds_clp->cl_minorversion, 138073ea666cSChuck Lever .net = mds_clp->cl_net, 1381d83217c1SAndy Adamson }; 138298fc685aSAndy Adamson struct rpc_timeout ds_timeout; 1383d83217c1SAndy Adamson struct nfs_client *clp; 1384d83217c1SAndy Adamson 1385d83217c1SAndy Adamson /* 1386d83217c1SAndy Adamson * Set an authflavor equual to the MDS value. Use the MDS nfs_client 1387d83217c1SAndy Adamson * cl_ipaddr so as to use the same EXCHANGE_ID co_ownerid as the MDS 1388d83217c1SAndy Adamson * (section 13.1 RFC 5661). 1389d83217c1SAndy Adamson */ 139098fc685aSAndy Adamson nfs_init_timeout_values(&ds_timeout, ds_proto, ds_timeo, ds_retrans); 1391d83217c1SAndy Adamson clp = nfs_get_client(&cl_init, &ds_timeout, mds_clp->cl_ipaddr, 13924bf590e0SChuck Lever mds_clp->cl_rpcclient->cl_auth->au_flavor); 1393d83217c1SAndy Adamson 1394d83217c1SAndy Adamson dprintk("<-- %s %p\n", __func__, clp); 1395d83217c1SAndy Adamson return clp; 1396d83217c1SAndy Adamson } 139794b134acSTrond Myklebust EXPORT_SYMBOL_GPL(nfs4_set_ds_client); 1398557134a3SAndy Adamson 1399557134a3SAndy Adamson /* 140096b09e02SAndy Adamson * Session has been established, and the client marked ready. 140196b09e02SAndy Adamson * Set the mount rsize and wsize with negotiated fore channel 140296b09e02SAndy Adamson * attributes which will be bound checked in nfs_server_set_fsinfo. 140396b09e02SAndy Adamson */ 140496b09e02SAndy Adamson static void nfs4_session_set_rwsize(struct nfs_server *server) 140596b09e02SAndy Adamson { 140696b09e02SAndy Adamson #ifdef CONFIG_NFS_V4_1 14072449ea2eSAlexandros Batsakis struct nfs4_session *sess; 14082449ea2eSAlexandros Batsakis u32 server_resp_sz; 14092449ea2eSAlexandros Batsakis u32 server_rqst_sz; 14102449ea2eSAlexandros Batsakis 141196b09e02SAndy Adamson if (!nfs4_has_session(server->nfs_client)) 141296b09e02SAndy Adamson return; 14132449ea2eSAlexandros Batsakis sess = server->nfs_client->cl_session; 14142449ea2eSAlexandros Batsakis server_resp_sz = sess->fc_attrs.max_resp_sz - nfs41_maxread_overhead; 14152449ea2eSAlexandros Batsakis server_rqst_sz = sess->fc_attrs.max_rqst_sz - nfs41_maxwrite_overhead; 14162449ea2eSAlexandros Batsakis 14172449ea2eSAlexandros Batsakis if (server->rsize > server_resp_sz) 14182449ea2eSAlexandros Batsakis server->rsize = server_resp_sz; 14192449ea2eSAlexandros Batsakis if (server->wsize > server_rqst_sz) 14202449ea2eSAlexandros Batsakis server->wsize = server_rqst_sz; 142196b09e02SAndy Adamson #endif /* CONFIG_NFS_V4_1 */ 142296b09e02SAndy Adamson } 142396b09e02SAndy Adamson 142444950b67STrond Myklebust static int nfs4_server_common_setup(struct nfs_server *server, 142544950b67STrond Myklebust struct nfs_fh *mntfh) 142644950b67STrond Myklebust { 142744950b67STrond Myklebust struct nfs_fattr *fattr; 142844950b67STrond Myklebust int error; 142944950b67STrond Myklebust 143044950b67STrond Myklebust BUG_ON(!server->nfs_client); 143144950b67STrond Myklebust BUG_ON(!server->nfs_client->rpc_ops); 143244950b67STrond Myklebust BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 143344950b67STrond Myklebust 143494de8b27SAndy Adamson /* data servers support only a subset of NFSv4.1 */ 143594de8b27SAndy Adamson if (is_ds_only_client(server->nfs_client)) 143694de8b27SAndy Adamson return -EPROTONOSUPPORT; 143794de8b27SAndy Adamson 143844950b67STrond Myklebust fattr = nfs_alloc_fattr(); 143944950b67STrond Myklebust if (fattr == NULL) 144044950b67STrond Myklebust return -ENOMEM; 144144950b67STrond Myklebust 144244950b67STrond Myklebust /* We must ensure the session is initialised first */ 144344950b67STrond Myklebust error = nfs4_init_session(server); 144444950b67STrond Myklebust if (error < 0) 144544950b67STrond Myklebust goto out; 144644950b67STrond Myklebust 144744950b67STrond Myklebust /* Probe the root fh to retrieve its FSID and filehandle */ 144844950b67STrond Myklebust error = nfs4_get_rootfh(server, mntfh); 144944950b67STrond Myklebust if (error < 0) 145044950b67STrond Myklebust goto out; 145144950b67STrond Myklebust 145244950b67STrond Myklebust dprintk("Server FSID: %llx:%llx\n", 145344950b67STrond Myklebust (unsigned long long) server->fsid.major, 145444950b67STrond Myklebust (unsigned long long) server->fsid.minor); 145544950b67STrond Myklebust dprintk("Mount FH: %d\n", mntfh->size); 145644950b67STrond Myklebust 145744950b67STrond Myklebust nfs4_session_set_rwsize(server); 145844950b67STrond Myklebust 145944950b67STrond Myklebust error = nfs_probe_fsinfo(server, mntfh, fattr); 146044950b67STrond Myklebust if (error < 0) 146144950b67STrond Myklebust goto out; 146244950b67STrond Myklebust 146344950b67STrond Myklebust if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 146444950b67STrond Myklebust server->namelen = NFS4_MAXNAMLEN; 146544950b67STrond Myklebust 1466fca5238eSChuck Lever nfs_server_insert_lists(server); 146744950b67STrond Myklebust server->mount_time = jiffies; 14680aaaf5c4SChuck Lever server->destroy = nfs4_destroy_server; 146944950b67STrond Myklebust out: 147044950b67STrond Myklebust nfs_free_fattr(fattr); 147144950b67STrond Myklebust return error; 147244950b67STrond Myklebust } 147344950b67STrond Myklebust 147496b09e02SAndy Adamson /* 147554ceac45SDavid Howells * Create a version 4 volume record 147654ceac45SDavid Howells */ 147754ceac45SDavid Howells static int nfs4_init_server(struct nfs_server *server, 147891ea40b9S\"Talpey, Thomas\ const struct nfs_parsed_mount_data *data) 147954ceac45SDavid Howells { 148033170233STrond Myklebust struct rpc_timeout timeparms; 148154ceac45SDavid Howells int error; 148254ceac45SDavid Howells 148354ceac45SDavid Howells dprintk("--> nfs4_init_server()\n"); 148454ceac45SDavid Howells 148533170233STrond Myklebust nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, 148633170233STrond Myklebust data->timeo, data->retrans); 148733170233STrond Myklebust 1488542fcc33SChuck Lever /* Initialise the client representation from the mount data */ 1489542fcc33SChuck Lever server->flags = data->flags; 149082f2e547SBryan Schumaker server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR|NFS_CAP_POSIX_LOCK; 149182f2e547SBryan Schumaker if (!(data->flags & NFS_MOUNT_NORDIRPLUS)) 149282f2e547SBryan Schumaker server->caps |= NFS_CAP_READDIRPLUS; 1493b797cac7SDavid Howells server->options = data->options; 1494542fcc33SChuck Lever 149533170233STrond Myklebust /* Get a client record */ 149633170233STrond Myklebust error = nfs4_set_client(server, 149733170233STrond Myklebust data->nfs_server.hostname, 149833170233STrond Myklebust (const struct sockaddr *)&data->nfs_server.address, 149933170233STrond Myklebust data->nfs_server.addrlen, 150033170233STrond Myklebust data->client_address, 150133170233STrond Myklebust data->auth_flavors[0], 150233170233STrond Myklebust data->nfs_server.protocol, 150394a417f3SBenny Halevy &timeparms, 1504e50a7a1aSStanislav Kinsbursky data->minorversion, 1505e50a7a1aSStanislav Kinsbursky data->net); 150633170233STrond Myklebust if (error < 0) 150733170233STrond Myklebust goto error; 150833170233STrond Myklebust 1509b064eca2STrond Myklebust /* 1510b064eca2STrond Myklebust * Don't use NFS uid/gid mapping if we're using AUTH_SYS or lower 1511b064eca2STrond Myklebust * authentication. 1512b064eca2STrond Myklebust */ 1513b064eca2STrond Myklebust if (nfs4_disable_idmapping && data->auth_flavors[0] == RPC_AUTH_UNIX) 1514b064eca2STrond Myklebust server->caps |= NFS_CAP_UIDGID_NOMAP; 1515b064eca2STrond Myklebust 151654ceac45SDavid Howells if (data->rsize) 151754ceac45SDavid Howells server->rsize = nfs_block_size(data->rsize, NULL); 151854ceac45SDavid Howells if (data->wsize) 151954ceac45SDavid Howells server->wsize = nfs_block_size(data->wsize, NULL); 152054ceac45SDavid Howells 152154ceac45SDavid Howells server->acregmin = data->acregmin * HZ; 152254ceac45SDavid Howells server->acregmax = data->acregmax * HZ; 152354ceac45SDavid Howells server->acdirmin = data->acdirmin * HZ; 152454ceac45SDavid Howells server->acdirmax = data->acdirmax * HZ; 152554ceac45SDavid Howells 1526f22d6d79SChuck Lever server->port = data->nfs_server.port; 1527f22d6d79SChuck Lever 152833170233STrond Myklebust error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]); 152954ceac45SDavid Howells 153033170233STrond Myklebust error: 153154ceac45SDavid Howells /* Done */ 153254ceac45SDavid Howells dprintk("<-- nfs4_init_server() = %d\n", error); 153354ceac45SDavid Howells return error; 153454ceac45SDavid Howells } 153554ceac45SDavid Howells 153654ceac45SDavid Howells /* 153754ceac45SDavid Howells * Create a version 4 volume record 153854ceac45SDavid Howells * - keyed on server and FSID 153954ceac45SDavid Howells */ 154091ea40b9S\"Talpey, Thomas\ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, 154154ceac45SDavid Howells struct nfs_fh *mntfh) 154254ceac45SDavid Howells { 154354ceac45SDavid Howells struct nfs_server *server; 154454ceac45SDavid Howells int error; 154554ceac45SDavid Howells 154654ceac45SDavid Howells dprintk("--> nfs4_create_server()\n"); 154754ceac45SDavid Howells 154854ceac45SDavid Howells server = nfs_alloc_server(); 154954ceac45SDavid Howells if (!server) 155054ceac45SDavid Howells return ERR_PTR(-ENOMEM); 155154ceac45SDavid Howells 155254ceac45SDavid Howells /* set up the general RPC client */ 155391ea40b9S\"Talpey, Thomas\ error = nfs4_init_server(server, data); 155454ceac45SDavid Howells if (error < 0) 155554ceac45SDavid Howells goto error; 155654ceac45SDavid Howells 155744950b67STrond Myklebust error = nfs4_server_common_setup(server, mntfh); 1558fccba804STrond Myklebust if (error < 0) 1559fccba804STrond Myklebust goto error; 1560557134a3SAndy Adamson 156154ceac45SDavid Howells dprintk("<-- nfs4_create_server() = %p\n", server); 156254ceac45SDavid Howells return server; 156354ceac45SDavid Howells 156454ceac45SDavid Howells error: 156554ceac45SDavid Howells nfs_free_server(server); 156654ceac45SDavid Howells dprintk("<-- nfs4_create_server() = error %d\n", error); 156754ceac45SDavid Howells return ERR_PTR(error); 156854ceac45SDavid Howells } 156954ceac45SDavid Howells 157054ceac45SDavid Howells /* 157154ceac45SDavid Howells * Create an NFS4 referral server record 157254ceac45SDavid Howells */ 157354ceac45SDavid Howells struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, 1574f2d0d85eSTrond Myklebust struct nfs_fh *mntfh) 157554ceac45SDavid Howells { 157654ceac45SDavid Howells struct nfs_client *parent_client; 157754ceac45SDavid Howells struct nfs_server *server, *parent_server; 157854ceac45SDavid Howells int error; 157954ceac45SDavid Howells 158054ceac45SDavid Howells dprintk("--> nfs4_create_referral_server()\n"); 158154ceac45SDavid Howells 158254ceac45SDavid Howells server = nfs_alloc_server(); 158354ceac45SDavid Howells if (!server) 158454ceac45SDavid Howells return ERR_PTR(-ENOMEM); 158554ceac45SDavid Howells 158654ceac45SDavid Howells parent_server = NFS_SB(data->sb); 158754ceac45SDavid Howells parent_client = parent_server->nfs_client; 158854ceac45SDavid Howells 1589542fcc33SChuck Lever /* Initialise the client representation from the parent server */ 1590542fcc33SChuck Lever nfs_server_copy_userdata(server, parent_server); 159162ab460cSTrond Myklebust server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR; 1592542fcc33SChuck Lever 159354ceac45SDavid Howells /* Get a client representation. 159454ceac45SDavid Howells * Note: NFSv4 always uses TCP, */ 1595dcecae0fSChuck Lever error = nfs4_set_client(server, data->hostname, 15966677d095SChuck Lever data->addr, 15976677d095SChuck Lever data->addrlen, 15987d9ac06fSJ. Bruce Fields parent_client->cl_ipaddr, 159954ceac45SDavid Howells data->authflavor, 16002446ab60STrond Myklebust rpc_protocol(parent_server->client), 160194a417f3SBenny Halevy parent_server->client->cl_timeout, 1602e50a7a1aSStanislav Kinsbursky parent_client->cl_mvops->minor_version, 160373ea666cSChuck Lever parent_client->cl_net); 1604297de4f6Sandros@citi.umich.edu if (error < 0) 1605297de4f6Sandros@citi.umich.edu goto error; 160654ceac45SDavid Howells 160733170233STrond Myklebust error = nfs_init_server_rpcclient(server, parent_server->client->cl_timeout, data->authflavor); 160854ceac45SDavid Howells if (error < 0) 160954ceac45SDavid Howells goto error; 161054ceac45SDavid Howells 161144950b67STrond Myklebust error = nfs4_server_common_setup(server, mntfh); 1612f2d0d85eSTrond Myklebust if (error < 0) 1613f2d0d85eSTrond Myklebust goto error; 1614f2d0d85eSTrond Myklebust 161554ceac45SDavid Howells dprintk("<-- nfs_create_referral_server() = %p\n", server); 161654ceac45SDavid Howells return server; 161754ceac45SDavid Howells 161854ceac45SDavid Howells error: 161954ceac45SDavid Howells nfs_free_server(server); 162054ceac45SDavid Howells dprintk("<-- nfs4_create_referral_server() = error %d\n", error); 162154ceac45SDavid Howells return ERR_PTR(error); 162254ceac45SDavid Howells } 162354ceac45SDavid Howells 162454ceac45SDavid Howells #endif /* CONFIG_NFS_V4 */ 162554ceac45SDavid Howells 162654ceac45SDavid Howells /* 162754ceac45SDavid Howells * Clone an NFS2, NFS3 or NFS4 server record 162854ceac45SDavid Howells */ 162954ceac45SDavid Howells struct nfs_server *nfs_clone_server(struct nfs_server *source, 163054ceac45SDavid Howells struct nfs_fh *fh, 16317e6eb683SBryan Schumaker struct nfs_fattr *fattr, 16327e6eb683SBryan Schumaker rpc_authflavor_t flavor) 163354ceac45SDavid Howells { 163454ceac45SDavid Howells struct nfs_server *server; 1635fbca779aSTrond Myklebust struct nfs_fattr *fattr_fsinfo; 163654ceac45SDavid Howells int error; 163754ceac45SDavid Howells 163854ceac45SDavid Howells dprintk("--> nfs_clone_server(,%llx:%llx,)\n", 16396daabf1bSDavid Howells (unsigned long long) fattr->fsid.major, 16406daabf1bSDavid Howells (unsigned long long) fattr->fsid.minor); 164154ceac45SDavid Howells 164254ceac45SDavid Howells server = nfs_alloc_server(); 164354ceac45SDavid Howells if (!server) 164454ceac45SDavid Howells return ERR_PTR(-ENOMEM); 164554ceac45SDavid Howells 1646fbca779aSTrond Myklebust error = -ENOMEM; 1647fbca779aSTrond Myklebust fattr_fsinfo = nfs_alloc_fattr(); 1648fbca779aSTrond Myklebust if (fattr_fsinfo == NULL) 1649fbca779aSTrond Myklebust goto out_free_server; 1650fbca779aSTrond Myklebust 165154ceac45SDavid Howells /* Copy data from the source */ 165254ceac45SDavid Howells server->nfs_client = source->nfs_client; 16530aaaf5c4SChuck Lever server->destroy = source->destroy; 165454ceac45SDavid Howells atomic_inc(&server->nfs_client->cl_count); 165554ceac45SDavid Howells nfs_server_copy_userdata(server, source); 165654ceac45SDavid Howells 165754ceac45SDavid Howells server->fsid = fattr->fsid; 165854ceac45SDavid Howells 165933170233STrond Myklebust error = nfs_init_server_rpcclient(server, 166033170233STrond Myklebust source->client->cl_timeout, 16617e6eb683SBryan Schumaker flavor); 166254ceac45SDavid Howells if (error < 0) 166354ceac45SDavid Howells goto out_free_server; 166454ceac45SDavid Howells if (!IS_ERR(source->client_acl)) 166554ceac45SDavid Howells nfs_init_server_aclclient(server); 166654ceac45SDavid Howells 166754ceac45SDavid Howells /* probe the filesystem info for this server filesystem */ 1668fbca779aSTrond Myklebust error = nfs_probe_fsinfo(server, fh, fattr_fsinfo); 166954ceac45SDavid Howells if (error < 0) 167054ceac45SDavid Howells goto out_free_server; 167154ceac45SDavid Howells 167254af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 167354af3bb5STrond Myklebust server->namelen = NFS4_MAXNAMLEN; 167454af3bb5STrond Myklebust 167554ceac45SDavid Howells dprintk("Cloned FSID: %llx:%llx\n", 16766daabf1bSDavid Howells (unsigned long long) server->fsid.major, 16776daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 167854ceac45SDavid Howells 167954ceac45SDavid Howells error = nfs_start_lockd(server); 168054ceac45SDavid Howells if (error < 0) 168154ceac45SDavid Howells goto out_free_server; 168254ceac45SDavid Howells 1683fca5238eSChuck Lever nfs_server_insert_lists(server); 168454ceac45SDavid Howells server->mount_time = jiffies; 168554ceac45SDavid Howells 1686fbca779aSTrond Myklebust nfs_free_fattr(fattr_fsinfo); 168754ceac45SDavid Howells dprintk("<-- nfs_clone_server() = %p\n", server); 168854ceac45SDavid Howells return server; 168954ceac45SDavid Howells 169054ceac45SDavid Howells out_free_server: 1691fbca779aSTrond Myklebust nfs_free_fattr(fattr_fsinfo); 169254ceac45SDavid Howells nfs_free_server(server); 169354ceac45SDavid Howells dprintk("<-- nfs_clone_server() = error %d\n", error); 169454ceac45SDavid Howells return ERR_PTR(error); 169554ceac45SDavid Howells } 16966aaca566SDavid Howells 16976b13168bSStanislav Kinsbursky void nfs_clients_init(struct net *net) 16986b13168bSStanislav Kinsbursky { 16996b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(net, nfs_net_id); 17006b13168bSStanislav Kinsbursky 17016b13168bSStanislav Kinsbursky INIT_LIST_HEAD(&nn->nfs_client_list); 1702c25d32b2SStanislav Kinsbursky INIT_LIST_HEAD(&nn->nfs_volume_list); 170328cd1b3fSStanislav Kinsbursky #ifdef CONFIG_NFS_V4 170428cd1b3fSStanislav Kinsbursky idr_init(&nn->cb_ident_idr); 170528cd1b3fSStanislav Kinsbursky #endif 17064c03ae4aSTrond Myklebust spin_lock_init(&nn->nfs_client_lock); 1707f092075dSChuck Lever nn->boot_time = CURRENT_TIME; 17086b13168bSStanislav Kinsbursky } 17096b13168bSStanislav Kinsbursky 17106aaca566SDavid Howells #ifdef CONFIG_PROC_FS 17116aaca566SDavid Howells static struct proc_dir_entry *proc_fs_nfs; 17126aaca566SDavid Howells 17136aaca566SDavid Howells static int nfs_server_list_open(struct inode *inode, struct file *file); 17146aaca566SDavid Howells static void *nfs_server_list_start(struct seq_file *p, loff_t *pos); 17156aaca566SDavid Howells static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos); 17166aaca566SDavid Howells static void nfs_server_list_stop(struct seq_file *p, void *v); 17176aaca566SDavid Howells static int nfs_server_list_show(struct seq_file *m, void *v); 17186aaca566SDavid Howells 171988e9d34cSJames Morris static const struct seq_operations nfs_server_list_ops = { 17206aaca566SDavid Howells .start = nfs_server_list_start, 17216aaca566SDavid Howells .next = nfs_server_list_next, 17226aaca566SDavid Howells .stop = nfs_server_list_stop, 17236aaca566SDavid Howells .show = nfs_server_list_show, 17246aaca566SDavid Howells }; 17256aaca566SDavid Howells 172600977a59SArjan van de Ven static const struct file_operations nfs_server_list_fops = { 17276aaca566SDavid Howells .open = nfs_server_list_open, 17286aaca566SDavid Howells .read = seq_read, 17296aaca566SDavid Howells .llseek = seq_lseek, 17306aaca566SDavid Howells .release = seq_release, 173134b37235SDenis V. Lunev .owner = THIS_MODULE, 17326aaca566SDavid Howells }; 17336aaca566SDavid Howells 17346aaca566SDavid Howells static int nfs_volume_list_open(struct inode *inode, struct file *file); 17356aaca566SDavid Howells static void *nfs_volume_list_start(struct seq_file *p, loff_t *pos); 17366aaca566SDavid Howells static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos); 17376aaca566SDavid Howells static void nfs_volume_list_stop(struct seq_file *p, void *v); 17386aaca566SDavid Howells static int nfs_volume_list_show(struct seq_file *m, void *v); 17396aaca566SDavid Howells 174088e9d34cSJames Morris static const struct seq_operations nfs_volume_list_ops = { 17416aaca566SDavid Howells .start = nfs_volume_list_start, 17426aaca566SDavid Howells .next = nfs_volume_list_next, 17436aaca566SDavid Howells .stop = nfs_volume_list_stop, 17446aaca566SDavid Howells .show = nfs_volume_list_show, 17456aaca566SDavid Howells }; 17466aaca566SDavid Howells 174700977a59SArjan van de Ven static const struct file_operations nfs_volume_list_fops = { 17486aaca566SDavid Howells .open = nfs_volume_list_open, 17496aaca566SDavid Howells .read = seq_read, 17506aaca566SDavid Howells .llseek = seq_lseek, 17516aaca566SDavid Howells .release = seq_release, 175234b37235SDenis V. Lunev .owner = THIS_MODULE, 17536aaca566SDavid Howells }; 17546aaca566SDavid Howells 17556aaca566SDavid Howells /* 17566aaca566SDavid Howells * open "/proc/fs/nfsfs/servers" which provides a summary of servers with which 17576aaca566SDavid Howells * we're dealing 17586aaca566SDavid Howells */ 17596aaca566SDavid Howells static int nfs_server_list_open(struct inode *inode, struct file *file) 17606aaca566SDavid Howells { 17616aaca566SDavid Howells struct seq_file *m; 17626aaca566SDavid Howells int ret; 17636b13168bSStanislav Kinsbursky struct pid_namespace *pid_ns = file->f_dentry->d_sb->s_fs_info; 17646b13168bSStanislav Kinsbursky struct net *net = pid_ns->child_reaper->nsproxy->net_ns; 17656aaca566SDavid Howells 17666aaca566SDavid Howells ret = seq_open(file, &nfs_server_list_ops); 17676aaca566SDavid Howells if (ret < 0) 17686aaca566SDavid Howells return ret; 17696aaca566SDavid Howells 17706aaca566SDavid Howells m = file->private_data; 17716b13168bSStanislav Kinsbursky m->private = net; 17726aaca566SDavid Howells 17736aaca566SDavid Howells return 0; 17746aaca566SDavid Howells } 17756aaca566SDavid Howells 17766aaca566SDavid Howells /* 17776aaca566SDavid Howells * set up the iterator to start reading from the server list and return the first item 17786aaca566SDavid Howells */ 17796aaca566SDavid Howells static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos) 17806aaca566SDavid Howells { 17816b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(m->private, nfs_net_id); 17826b13168bSStanislav Kinsbursky 17836aaca566SDavid Howells /* lock the list against modification */ 1784dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 17856b13168bSStanislav Kinsbursky return seq_list_start_head(&nn->nfs_client_list, *_pos); 17866aaca566SDavid Howells } 17876aaca566SDavid Howells 17886aaca566SDavid Howells /* 17896aaca566SDavid Howells * move to next server 17906aaca566SDavid Howells */ 17916aaca566SDavid Howells static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos) 17926aaca566SDavid Howells { 17936b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(p->private, nfs_net_id); 17946b13168bSStanislav Kinsbursky 17956b13168bSStanislav Kinsbursky return seq_list_next(v, &nn->nfs_client_list, pos); 17966aaca566SDavid Howells } 17976aaca566SDavid Howells 17986aaca566SDavid Howells /* 17996aaca566SDavid Howells * clean up after reading from the transports list 18006aaca566SDavid Howells */ 18016aaca566SDavid Howells static void nfs_server_list_stop(struct seq_file *p, void *v) 18026aaca566SDavid Howells { 1803dc030858SStanislav Kinsbursky struct nfs_net *nn = net_generic(p->private, nfs_net_id); 1804dc030858SStanislav Kinsbursky 1805dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 18066aaca566SDavid Howells } 18076aaca566SDavid Howells 18086aaca566SDavid Howells /* 18096aaca566SDavid Howells * display a header line followed by a load of call lines 18106aaca566SDavid Howells */ 18116aaca566SDavid Howells static int nfs_server_list_show(struct seq_file *m, void *v) 18126aaca566SDavid Howells { 18136aaca566SDavid Howells struct nfs_client *clp; 18146b13168bSStanislav Kinsbursky struct nfs_net *nn = net_generic(m->private, nfs_net_id); 18156aaca566SDavid Howells 18166aaca566SDavid Howells /* display header on line 1 */ 18176b13168bSStanislav Kinsbursky if (v == &nn->nfs_client_list) { 18186aaca566SDavid Howells seq_puts(m, "NV SERVER PORT USE HOSTNAME\n"); 18196aaca566SDavid Howells return 0; 18206aaca566SDavid Howells } 18216aaca566SDavid Howells 18226aaca566SDavid Howells /* display one transport per line on subsequent lines */ 18236aaca566SDavid Howells clp = list_entry(v, struct nfs_client, cl_share_link); 18246aaca566SDavid Howells 1825940aab49SMalahal Naineni /* Check if the client is initialized */ 1826940aab49SMalahal Naineni if (clp->cl_cons_state != NFS_CS_READY) 1827940aab49SMalahal Naineni return 0; 1828940aab49SMalahal Naineni 18292446ab60STrond Myklebust rcu_read_lock(); 18305d8515caSChuck Lever seq_printf(m, "v%u %s %s %3d %s\n", 183140c55319STrond Myklebust clp->rpc_ops->version, 18325d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), 18335d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), 18346aaca566SDavid Howells atomic_read(&clp->cl_count), 18356aaca566SDavid Howells clp->cl_hostname); 18362446ab60STrond Myklebust rcu_read_unlock(); 18376aaca566SDavid Howells 18386aaca566SDavid Howells return 0; 18396aaca566SDavid Howells } 18406aaca566SDavid Howells 18416aaca566SDavid Howells /* 18426aaca566SDavid Howells * open "/proc/fs/nfsfs/volumes" which provides a summary of extant volumes 18436aaca566SDavid Howells */ 18446aaca566SDavid Howells static int nfs_volume_list_open(struct inode *inode, struct file *file) 18456aaca566SDavid Howells { 18466aaca566SDavid Howells struct seq_file *m; 18476aaca566SDavid Howells int ret; 1848c25d32b2SStanislav Kinsbursky struct pid_namespace *pid_ns = file->f_dentry->d_sb->s_fs_info; 1849c25d32b2SStanislav Kinsbursky struct net *net = pid_ns->child_reaper->nsproxy->net_ns; 18506aaca566SDavid Howells 18516aaca566SDavid Howells ret = seq_open(file, &nfs_volume_list_ops); 18526aaca566SDavid Howells if (ret < 0) 18536aaca566SDavid Howells return ret; 18546aaca566SDavid Howells 18556aaca566SDavid Howells m = file->private_data; 1856c25d32b2SStanislav Kinsbursky m->private = net; 18576aaca566SDavid Howells 18586aaca566SDavid Howells return 0; 18596aaca566SDavid Howells } 18606aaca566SDavid Howells 18616aaca566SDavid Howells /* 18626aaca566SDavid Howells * set up the iterator to start reading from the volume list and return the first item 18636aaca566SDavid Howells */ 18646aaca566SDavid Howells static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos) 18656aaca566SDavid Howells { 1866c25d32b2SStanislav Kinsbursky struct nfs_net *nn = net_generic(m->private, nfs_net_id); 1867c25d32b2SStanislav Kinsbursky 18686aaca566SDavid Howells /* lock the list against modification */ 1869dc030858SStanislav Kinsbursky spin_lock(&nn->nfs_client_lock); 1870c25d32b2SStanislav Kinsbursky return seq_list_start_head(&nn->nfs_volume_list, *_pos); 18716aaca566SDavid Howells } 18726aaca566SDavid Howells 18736aaca566SDavid Howells /* 18746aaca566SDavid Howells * move to next volume 18756aaca566SDavid Howells */ 18766aaca566SDavid Howells static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos) 18776aaca566SDavid Howells { 1878c25d32b2SStanislav Kinsbursky struct nfs_net *nn = net_generic(p->private, nfs_net_id); 1879c25d32b2SStanislav Kinsbursky 1880c25d32b2SStanislav Kinsbursky return seq_list_next(v, &nn->nfs_volume_list, pos); 18816aaca566SDavid Howells } 18826aaca566SDavid Howells 18836aaca566SDavid Howells /* 18846aaca566SDavid Howells * clean up after reading from the transports list 18856aaca566SDavid Howells */ 18866aaca566SDavid Howells static void nfs_volume_list_stop(struct seq_file *p, void *v) 18876aaca566SDavid Howells { 1888dc030858SStanislav Kinsbursky struct nfs_net *nn = net_generic(p->private, nfs_net_id); 1889dc030858SStanislav Kinsbursky 1890dc030858SStanislav Kinsbursky spin_unlock(&nn->nfs_client_lock); 18916aaca566SDavid Howells } 18926aaca566SDavid Howells 18936aaca566SDavid Howells /* 18946aaca566SDavid Howells * display a header line followed by a load of call lines 18956aaca566SDavid Howells */ 18966aaca566SDavid Howells static int nfs_volume_list_show(struct seq_file *m, void *v) 18976aaca566SDavid Howells { 18986aaca566SDavid Howells struct nfs_server *server; 18996aaca566SDavid Howells struct nfs_client *clp; 19006aaca566SDavid Howells char dev[8], fsid[17]; 1901c25d32b2SStanislav Kinsbursky struct nfs_net *nn = net_generic(m->private, nfs_net_id); 19026aaca566SDavid Howells 19036aaca566SDavid Howells /* display header on line 1 */ 1904c25d32b2SStanislav Kinsbursky if (v == &nn->nfs_volume_list) { 19055d1acff1SDavid Howells seq_puts(m, "NV SERVER PORT DEV FSID FSC\n"); 19066aaca566SDavid Howells return 0; 19076aaca566SDavid Howells } 19086aaca566SDavid Howells /* display one transport per line on subsequent lines */ 19096aaca566SDavid Howells server = list_entry(v, struct nfs_server, master_link); 19106aaca566SDavid Howells clp = server->nfs_client; 19116aaca566SDavid Howells 19126aaca566SDavid Howells snprintf(dev, 8, "%u:%u", 19136aaca566SDavid Howells MAJOR(server->s_dev), MINOR(server->s_dev)); 19146aaca566SDavid Howells 19156aaca566SDavid Howells snprintf(fsid, 17, "%llx:%llx", 19166daabf1bSDavid Howells (unsigned long long) server->fsid.major, 19176daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 19186aaca566SDavid Howells 19192446ab60STrond Myklebust rcu_read_lock(); 19205d1acff1SDavid Howells seq_printf(m, "v%u %s %s %-7s %-17s %s\n", 192140c55319STrond Myklebust clp->rpc_ops->version, 19225d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), 19235d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), 19246aaca566SDavid Howells dev, 19255d1acff1SDavid Howells fsid, 19265d1acff1SDavid Howells nfs_server_fscache_state(server)); 19272446ab60STrond Myklebust rcu_read_unlock(); 19286aaca566SDavid Howells 19296aaca566SDavid Howells return 0; 19306aaca566SDavid Howells } 19316aaca566SDavid Howells 19326aaca566SDavid Howells /* 19336aaca566SDavid Howells * initialise the /proc/fs/nfsfs/ directory 19346aaca566SDavid Howells */ 19356aaca566SDavid Howells int __init nfs_fs_proc_init(void) 19366aaca566SDavid Howells { 19376aaca566SDavid Howells struct proc_dir_entry *p; 19386aaca566SDavid Howells 193936a5aeb8SAlexey Dobriyan proc_fs_nfs = proc_mkdir("fs/nfsfs", NULL); 19406aaca566SDavid Howells if (!proc_fs_nfs) 19416aaca566SDavid Howells goto error_0; 19426aaca566SDavid Howells 19436aaca566SDavid Howells /* a file of servers with which we're dealing */ 194434b37235SDenis V. Lunev p = proc_create("servers", S_IFREG|S_IRUGO, 194534b37235SDenis V. Lunev proc_fs_nfs, &nfs_server_list_fops); 19466aaca566SDavid Howells if (!p) 19476aaca566SDavid Howells goto error_1; 19486aaca566SDavid Howells 19496aaca566SDavid Howells /* a file of volumes that we have mounted */ 195034b37235SDenis V. Lunev p = proc_create("volumes", S_IFREG|S_IRUGO, 195134b37235SDenis V. Lunev proc_fs_nfs, &nfs_volume_list_fops); 19526aaca566SDavid Howells if (!p) 19536aaca566SDavid Howells goto error_2; 19546aaca566SDavid Howells return 0; 19556aaca566SDavid Howells 19566aaca566SDavid Howells error_2: 19576aaca566SDavid Howells remove_proc_entry("servers", proc_fs_nfs); 19586aaca566SDavid Howells error_1: 195936a5aeb8SAlexey Dobriyan remove_proc_entry("fs/nfsfs", NULL); 19606aaca566SDavid Howells error_0: 19616aaca566SDavid Howells return -ENOMEM; 19626aaca566SDavid Howells } 19636aaca566SDavid Howells 19646aaca566SDavid Howells /* 19656aaca566SDavid Howells * clean up the /proc/fs/nfsfs/ directory 19666aaca566SDavid Howells */ 19676aaca566SDavid Howells void nfs_fs_proc_exit(void) 19686aaca566SDavid Howells { 19696aaca566SDavid Howells remove_proc_entry("volumes", proc_fs_nfs); 19706aaca566SDavid Howells remove_proc_entry("servers", proc_fs_nfs); 197136a5aeb8SAlexey Dobriyan remove_proc_entry("fs/nfsfs", NULL); 19726aaca566SDavid Howells } 19736aaca566SDavid Howells 19746aaca566SDavid Howells #endif /* CONFIG_PROC_FS */ 1975b064eca2STrond Myklebust 1976b064eca2STrond Myklebust module_param(nfs4_disable_idmapping, bool, 0644); 1977b064eca2STrond Myklebust MODULE_PARM_DESC(nfs4_disable_idmapping, 1978b064eca2STrond Myklebust "Turn off NFSv4 idmapping when using 'sec=sys'"); 1979