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> 393b0d3f93STrond Myklebust #include <net/ipv6.h> 4024c8dbbbSDavid Howells #include <linux/nfs_xdr.h> 410b5b7ae0SAndy Adamson #include <linux/sunrpc/bc_xprt.h> 4224c8dbbbSDavid Howells 4324c8dbbbSDavid Howells #include <asm/system.h> 4424c8dbbbSDavid Howells 4524c8dbbbSDavid Howells #include "nfs4_fs.h" 4624c8dbbbSDavid Howells #include "callback.h" 4724c8dbbbSDavid Howells #include "delegation.h" 4824c8dbbbSDavid Howells #include "iostat.h" 4924c8dbbbSDavid Howells #include "internal.h" 5014727281SDavid Howells #include "fscache.h" 5185e174baSRicardo Labiaga #include "pnfs.h" 5224c8dbbbSDavid Howells 5324c8dbbbSDavid Howells #define NFSDBG_FACILITY NFSDBG_CLIENT 5424c8dbbbSDavid Howells 5524c8dbbbSDavid Howells static DEFINE_SPINLOCK(nfs_client_lock); 5624c8dbbbSDavid Howells static LIST_HEAD(nfs_client_list); 5754ceac45SDavid Howells static LIST_HEAD(nfs_volume_list); 5824c8dbbbSDavid Howells static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq); 59f4eecd5dSAndy Adamson #ifdef CONFIG_NFS_V4 60f4eecd5dSAndy Adamson static DEFINE_IDR(cb_ident_idr); /* Protected by nfs_client_lock */ 61f4eecd5dSAndy Adamson 62f4eecd5dSAndy Adamson /* 63f4eecd5dSAndy Adamson * Get a unique NFSv4.0 callback identifier which will be used 64f4eecd5dSAndy Adamson * by the V4.0 callback service to lookup the nfs_client struct 65f4eecd5dSAndy Adamson */ 66f4eecd5dSAndy Adamson static int nfs_get_cb_ident_idr(struct nfs_client *clp, int minorversion) 67f4eecd5dSAndy Adamson { 68f4eecd5dSAndy Adamson int ret = 0; 69f4eecd5dSAndy Adamson 70f4eecd5dSAndy Adamson if (clp->rpc_ops->version != 4 || minorversion != 0) 71f4eecd5dSAndy Adamson return ret; 72f4eecd5dSAndy Adamson retry: 73f4eecd5dSAndy Adamson if (!idr_pre_get(&cb_ident_idr, GFP_KERNEL)) 74f4eecd5dSAndy Adamson return -ENOMEM; 75f4eecd5dSAndy Adamson spin_lock(&nfs_client_lock); 76f4eecd5dSAndy Adamson ret = idr_get_new(&cb_ident_idr, clp, &clp->cl_cb_ident); 77f4eecd5dSAndy Adamson spin_unlock(&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 /* 855006a76cSDavid Howells * RPC cruft for NFS 865006a76cSDavid Howells */ 875006a76cSDavid Howells static struct rpc_version *nfs_version[5] = { 885006a76cSDavid Howells [2] = &nfs_version2, 895006a76cSDavid Howells #ifdef CONFIG_NFS_V3 905006a76cSDavid Howells [3] = &nfs_version3, 915006a76cSDavid Howells #endif 925006a76cSDavid Howells #ifdef CONFIG_NFS_V4 935006a76cSDavid Howells [4] = &nfs_version4, 945006a76cSDavid Howells #endif 955006a76cSDavid Howells }; 965006a76cSDavid Howells 975006a76cSDavid Howells struct rpc_program nfs_program = { 985006a76cSDavid Howells .name = "nfs", 995006a76cSDavid Howells .number = NFS_PROGRAM, 1005006a76cSDavid Howells .nrvers = ARRAY_SIZE(nfs_version), 1015006a76cSDavid Howells .version = nfs_version, 1025006a76cSDavid Howells .stats = &nfs_rpcstat, 1035006a76cSDavid Howells .pipe_dir_name = "/nfs", 1045006a76cSDavid Howells }; 1055006a76cSDavid Howells 1065006a76cSDavid Howells struct rpc_stat nfs_rpcstat = { 1075006a76cSDavid Howells .program = &nfs_program 1085006a76cSDavid Howells }; 1095006a76cSDavid Howells 1105006a76cSDavid Howells 1115006a76cSDavid Howells #ifdef CONFIG_NFS_V3_ACL 1125006a76cSDavid Howells static struct rpc_stat nfsacl_rpcstat = { &nfsacl_program }; 1135006a76cSDavid Howells static struct rpc_version * nfsacl_version[] = { 1145006a76cSDavid Howells [3] = &nfsacl_version3, 1155006a76cSDavid Howells }; 1165006a76cSDavid Howells 1175006a76cSDavid Howells struct rpc_program nfsacl_program = { 1185006a76cSDavid Howells .name = "nfsacl", 1195006a76cSDavid Howells .number = NFS_ACL_PROGRAM, 1205006a76cSDavid Howells .nrvers = ARRAY_SIZE(nfsacl_version), 1215006a76cSDavid Howells .version = nfsacl_version, 1225006a76cSDavid Howells .stats = &nfsacl_rpcstat, 1235006a76cSDavid Howells }; 1245006a76cSDavid Howells #endif /* CONFIG_NFS_V3_ACL */ 1255006a76cSDavid Howells 1263a498026STrond Myklebust struct nfs_client_initdata { 1273a498026STrond Myklebust const char *hostname; 128d7422c47SChuck Lever const struct sockaddr *addr; 1296e4cffd7SChuck Lever size_t addrlen; 13040c55319STrond Myklebust const struct nfs_rpc_ops *rpc_ops; 13159dca3b2STrond Myklebust int proto; 1325aae4a9aSBenny Halevy u32 minorversion; 1333a498026STrond Myklebust }; 1343a498026STrond Myklebust 1355006a76cSDavid Howells /* 13624c8dbbbSDavid Howells * Allocate a shared client record 13724c8dbbbSDavid Howells * 13824c8dbbbSDavid Howells * Since these are allocated/deallocated very rarely, we don't 13924c8dbbbSDavid Howells * bother putting them in a slab cache... 14024c8dbbbSDavid Howells */ 1413a498026STrond Myklebust static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) 14224c8dbbbSDavid Howells { 14324c8dbbbSDavid Howells struct nfs_client *clp; 1447c67db3aSTrond Myklebust struct rpc_cred *cred; 145a21bdd9bSChuck Lever int err = -ENOMEM; 14624c8dbbbSDavid Howells 14724c8dbbbSDavid Howells if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) 14824c8dbbbSDavid Howells goto error_0; 14924c8dbbbSDavid Howells 15040c55319STrond Myklebust clp->rpc_ops = cl_init->rpc_ops; 15140c55319STrond Myklebust 15224c8dbbbSDavid Howells atomic_set(&clp->cl_count, 1); 15324c8dbbbSDavid Howells clp->cl_cons_state = NFS_CS_INITING; 15424c8dbbbSDavid Howells 1556e4cffd7SChuck Lever memcpy(&clp->cl_addr, cl_init->addr, cl_init->addrlen); 1566e4cffd7SChuck Lever clp->cl_addrlen = cl_init->addrlen; 15724c8dbbbSDavid Howells 1583a498026STrond Myklebust if (cl_init->hostname) { 159a21bdd9bSChuck Lever err = -ENOMEM; 1603a498026STrond Myklebust clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL); 16124c8dbbbSDavid Howells if (!clp->cl_hostname) 16271468513SBenny Halevy goto error_cleanup; 16324c8dbbbSDavid Howells } 16424c8dbbbSDavid Howells 16524c8dbbbSDavid Howells INIT_LIST_HEAD(&clp->cl_superblocks); 16624c8dbbbSDavid Howells clp->cl_rpcclient = ERR_PTR(-EINVAL); 16724c8dbbbSDavid Howells 16859dca3b2STrond Myklebust clp->cl_proto = cl_init->proto; 16959dca3b2STrond Myklebust 17024c8dbbbSDavid Howells #ifdef CONFIG_NFS_V4 171f4eecd5dSAndy Adamson err = nfs_get_cb_ident_idr(clp, cl_init->minorversion); 172f4eecd5dSAndy Adamson if (err) 173f4eecd5dSAndy Adamson goto error_cleanup; 174f4eecd5dSAndy Adamson 17524c8dbbbSDavid Howells spin_lock_init(&clp->cl_lock); 17665f27f38SDavid Howells INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state); 17724c8dbbbSDavid Howells rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); 17824c8dbbbSDavid Howells clp->cl_boot_time = CURRENT_TIME; 17924c8dbbbSDavid Howells clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED; 1805aae4a9aSBenny Halevy clp->cl_minorversion = cl_init->minorversion; 18197dc1359STrond Myklebust clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion]; 18224c8dbbbSDavid Howells #endif 1837c67db3aSTrond Myklebust cred = rpc_lookup_machine_cred(); 1847c67db3aSTrond Myklebust if (!IS_ERR(cred)) 1857c67db3aSTrond Myklebust clp->cl_machine_cred = cred; 186974cec8cSAndy Adamson #if defined(CONFIG_NFS_V4_1) 187974cec8cSAndy Adamson INIT_LIST_HEAD(&clp->cl_layouts); 188974cec8cSAndy Adamson #endif 18914727281SDavid Howells nfs_fscache_get_client_cookie(clp); 19014727281SDavid Howells 19124c8dbbbSDavid Howells return clp; 19224c8dbbbSDavid Howells 19371468513SBenny Halevy error_cleanup: 19424c8dbbbSDavid Howells kfree(clp); 19524c8dbbbSDavid Howells error_0: 196a21bdd9bSChuck Lever return ERR_PTR(err); 19724c8dbbbSDavid Howells } 19824c8dbbbSDavid Howells 19924c8dbbbSDavid Howells #ifdef CONFIG_NFS_V4 200557134a3SAndy Adamson #ifdef CONFIG_NFS_V4_1 201ea005281SAndy Adamson static void nfs4_shutdown_session(struct nfs_client *clp) 202ea005281SAndy Adamson { 203ea005281SAndy Adamson if (nfs4_has_session(clp)) 204557134a3SAndy Adamson nfs4_destroy_session(clp->cl_session); 205557134a3SAndy Adamson } 206ea005281SAndy Adamson #else /* CONFIG_NFS_V4_1 */ 207ea005281SAndy Adamson static void nfs4_shutdown_session(struct nfs_client *clp) 208ea005281SAndy Adamson { 209ea005281SAndy Adamson } 210557134a3SAndy Adamson #endif /* CONFIG_NFS_V4_1 */ 211557134a3SAndy Adamson 212557134a3SAndy Adamson /* 213888ef2e3SAlexandros Batsakis * Destroy the NFS4 callback service 214888ef2e3SAlexandros Batsakis */ 215888ef2e3SAlexandros Batsakis static void nfs4_destroy_callback(struct nfs_client *clp) 216888ef2e3SAlexandros Batsakis { 217888ef2e3SAlexandros Batsakis if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) 21897dc1359STrond Myklebust nfs_callback_down(clp->cl_mvops->minor_version); 219888ef2e3SAlexandros Batsakis } 220888ef2e3SAlexandros Batsakis 221888ef2e3SAlexandros Batsakis static void nfs4_shutdown_client(struct nfs_client *clp) 222888ef2e3SAlexandros Batsakis { 223888ef2e3SAlexandros Batsakis if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state)) 224888ef2e3SAlexandros Batsakis nfs4_kill_renewd(clp); 225ea005281SAndy Adamson nfs4_shutdown_session(clp); 226888ef2e3SAlexandros Batsakis nfs4_destroy_callback(clp); 227888ef2e3SAlexandros Batsakis if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state)) 228888ef2e3SAlexandros Batsakis nfs_idmap_delete(clp); 229888ef2e3SAlexandros Batsakis 230888ef2e3SAlexandros Batsakis rpc_destroy_wait_queue(&clp->cl_rpcwaitq); 231888ef2e3SAlexandros Batsakis } 232f4eecd5dSAndy Adamson 233f4eecd5dSAndy Adamson /* idr_remove_all is not needed as all id's are removed by nfs_put_client */ 234f4eecd5dSAndy Adamson void nfs_cleanup_cb_ident_idr(void) 235f4eecd5dSAndy Adamson { 236f4eecd5dSAndy Adamson idr_destroy(&cb_ident_idr); 237f4eecd5dSAndy Adamson } 238f4eecd5dSAndy Adamson 239f4eecd5dSAndy Adamson /* nfs_client_lock held */ 240f4eecd5dSAndy Adamson static void nfs_cb_idr_remove_locked(struct nfs_client *clp) 241f4eecd5dSAndy Adamson { 242f4eecd5dSAndy Adamson if (clp->cl_cb_ident) 243f4eecd5dSAndy Adamson idr_remove(&cb_ident_idr, clp->cl_cb_ident); 244f4eecd5dSAndy Adamson } 245f4eecd5dSAndy Adamson 246f7e8917aSFred Isaman static void pnfs_init_server(struct nfs_server *server) 247f7e8917aSFred Isaman { 248f7e8917aSFred Isaman rpc_init_wait_queue(&server->roc_rpcwaitq, "pNFS ROC"); 249f7e8917aSFred Isaman } 250f7e8917aSFred Isaman 251888ef2e3SAlexandros Batsakis #else 252888ef2e3SAlexandros Batsakis static void nfs4_shutdown_client(struct nfs_client *clp) 253888ef2e3SAlexandros Batsakis { 254888ef2e3SAlexandros Batsakis } 255f4eecd5dSAndy Adamson 256f4eecd5dSAndy Adamson void nfs_cleanup_cb_ident_idr(void) 257f4eecd5dSAndy Adamson { 258f4eecd5dSAndy Adamson } 259f4eecd5dSAndy Adamson 260f4eecd5dSAndy Adamson static void nfs_cb_idr_remove_locked(struct nfs_client *clp) 261f4eecd5dSAndy Adamson { 262f4eecd5dSAndy Adamson } 263f7e8917aSFred Isaman 264f7e8917aSFred Isaman static void pnfs_init_server(struct nfs_server *server) 265f7e8917aSFred Isaman { 266f7e8917aSFred Isaman } 267f7e8917aSFred Isaman 268888ef2e3SAlexandros Batsakis #endif /* CONFIG_NFS_V4 */ 269888ef2e3SAlexandros Batsakis 270888ef2e3SAlexandros Batsakis /* 2715dd3177aSTrond Myklebust * Destroy a shared client record 2725dd3177aSTrond Myklebust */ 2735dd3177aSTrond Myklebust static void nfs_free_client(struct nfs_client *clp) 2745dd3177aSTrond Myklebust { 27540c55319STrond Myklebust dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version); 2765dd3177aSTrond Myklebust 2775dd3177aSTrond Myklebust nfs4_shutdown_client(clp); 27824c8dbbbSDavid Howells 27914727281SDavid Howells nfs_fscache_release_client_cookie(clp); 28014727281SDavid Howells 28124c8dbbbSDavid Howells /* -EIO all pending I/O */ 28224c8dbbbSDavid Howells if (!IS_ERR(clp->cl_rpcclient)) 28324c8dbbbSDavid Howells rpc_shutdown_client(clp->cl_rpcclient); 28424c8dbbbSDavid Howells 2857c67db3aSTrond Myklebust if (clp->cl_machine_cred != NULL) 2867c67db3aSTrond Myklebust put_rpccred(clp->cl_machine_cred); 2877c67db3aSTrond Myklebust 28824c8dbbbSDavid Howells kfree(clp->cl_hostname); 28924c8dbbbSDavid Howells kfree(clp); 29024c8dbbbSDavid Howells 29124c8dbbbSDavid Howells dprintk("<-- nfs_free_client()\n"); 29224c8dbbbSDavid Howells } 29324c8dbbbSDavid Howells 29424c8dbbbSDavid Howells /* 29524c8dbbbSDavid Howells * Release a reference to a shared client record 29624c8dbbbSDavid Howells */ 29724c8dbbbSDavid Howells void nfs_put_client(struct nfs_client *clp) 29824c8dbbbSDavid Howells { 29927ba8512SDavid Howells if (!clp) 30027ba8512SDavid Howells return; 30127ba8512SDavid Howells 30224c8dbbbSDavid Howells dprintk("--> nfs_put_client({%d})\n", atomic_read(&clp->cl_count)); 30324c8dbbbSDavid Howells 30424c8dbbbSDavid Howells if (atomic_dec_and_lock(&clp->cl_count, &nfs_client_lock)) { 30524c8dbbbSDavid Howells list_del(&clp->cl_share_link); 306f4eecd5dSAndy Adamson nfs_cb_idr_remove_locked(clp); 30724c8dbbbSDavid Howells spin_unlock(&nfs_client_lock); 30824c8dbbbSDavid Howells 30924c8dbbbSDavid Howells BUG_ON(!list_empty(&clp->cl_superblocks)); 31024c8dbbbSDavid Howells 31124c8dbbbSDavid Howells nfs_free_client(clp); 31224c8dbbbSDavid Howells } 31324c8dbbbSDavid Howells } 31416b374caSAndy Adamson EXPORT_SYMBOL_GPL(nfs_put_client); 31524c8dbbbSDavid Howells 3169082a5ccSTrond Myklebust #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 3179f4c899cSTrond Myklebust /* 3189f4c899cSTrond Myklebust * Test if two ip6 socket addresses refer to the same socket by 3199f4c899cSTrond Myklebust * comparing relevant fields. The padding bytes specifically, are not 3209f4c899cSTrond Myklebust * compared. sin6_flowinfo is not compared because it only affects QoS 3219f4c899cSTrond Myklebust * and sin6_scope_id is only compared if the address is "link local" 3229f4c899cSTrond Myklebust * because "link local" addresses need only be unique to a specific 3239f4c899cSTrond Myklebust * link. Conversely, ordinary unicast addresses might have different 3249f4c899cSTrond Myklebust * sin6_scope_id. 3259f4c899cSTrond Myklebust * 3269f4c899cSTrond Myklebust * The caller should ensure both socket addresses are AF_INET6. 3279f4c899cSTrond Myklebust */ 3283c8c45dfSChuck Lever static int nfs_sockaddr_match_ipaddr6(const struct sockaddr *sa1, 3299f4c899cSTrond Myklebust const struct sockaddr *sa2) 3309f4c899cSTrond Myklebust { 3313c8c45dfSChuck Lever const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sa1; 3323c8c45dfSChuck Lever const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2; 3339f4c899cSTrond Myklebust 3343c8c45dfSChuck Lever if (ipv6_addr_scope(&sin1->sin6_addr) == IPV6_ADDR_SCOPE_LINKLOCAL && 3353c8c45dfSChuck Lever sin1->sin6_scope_id != sin2->sin6_scope_id) 3369f4c899cSTrond Myklebust return 0; 3373b0d3f93STrond Myklebust 338b20d37caSTrond Myklebust return ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr); 3393b0d3f93STrond Myklebust } 3403c8c45dfSChuck Lever #else /* !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE) */ 3413c8c45dfSChuck Lever static int nfs_sockaddr_match_ipaddr6(const struct sockaddr *sa1, 3429f4c899cSTrond Myklebust const struct sockaddr *sa2) 3439f4c899cSTrond Myklebust { 3449f4c899cSTrond Myklebust return 0; 3459f4c899cSTrond Myklebust } 3469082a5ccSTrond Myklebust #endif 3473b0d3f93STrond Myklebust 34824c8dbbbSDavid Howells /* 349d7371c41SIan Dall * Test if two ip4 socket addresses refer to the same socket, by 350d7371c41SIan Dall * comparing relevant fields. The padding bytes specifically, are 351d7371c41SIan Dall * not compared. 352d7371c41SIan Dall * 353d7371c41SIan Dall * The caller should ensure both socket addresses are AF_INET. 354d7371c41SIan Dall */ 3553c8c45dfSChuck Lever static int nfs_sockaddr_match_ipaddr4(const struct sockaddr *sa1, 3563c8c45dfSChuck Lever const struct sockaddr *sa2) 3573c8c45dfSChuck Lever { 3583c8c45dfSChuck Lever const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sa1; 3593c8c45dfSChuck Lever const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sa2; 3603c8c45dfSChuck Lever 3613c8c45dfSChuck Lever return sin1->sin_addr.s_addr == sin2->sin_addr.s_addr; 3623c8c45dfSChuck Lever } 3633c8c45dfSChuck Lever 3643c8c45dfSChuck Lever static int nfs_sockaddr_cmp_ip6(const struct sockaddr *sa1, 3653c8c45dfSChuck Lever const struct sockaddr *sa2) 3663c8c45dfSChuck Lever { 3673c8c45dfSChuck Lever const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sa1; 3683c8c45dfSChuck Lever const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2; 3693c8c45dfSChuck Lever 3703c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr6(sa1, sa2) && 3713c8c45dfSChuck Lever (sin1->sin6_port == sin2->sin6_port); 3723c8c45dfSChuck Lever } 3733c8c45dfSChuck Lever 3749f4c899cSTrond Myklebust static int nfs_sockaddr_cmp_ip4(const struct sockaddr *sa1, 3759f4c899cSTrond Myklebust const struct sockaddr *sa2) 376d7371c41SIan Dall { 3773c8c45dfSChuck Lever const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sa1; 3783c8c45dfSChuck Lever const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sa2; 3799f4c899cSTrond Myklebust 3803c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr4(sa1, sa2) && 3813c8c45dfSChuck Lever (sin1->sin_port == sin2->sin_port); 382d7371c41SIan Dall } 383d7371c41SIan Dall 384d7371c41SIan Dall /* 385d7371c41SIan Dall * Test if two socket addresses represent the same actual socket, 3863c8c45dfSChuck Lever * by comparing (only) relevant fields, excluding the port number. 3873c8c45dfSChuck Lever */ 3883c8c45dfSChuck Lever static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1, 3893c8c45dfSChuck Lever const struct sockaddr *sa2) 3903c8c45dfSChuck Lever { 3913c8c45dfSChuck Lever if (sa1->sa_family != sa2->sa_family) 3923c8c45dfSChuck Lever return 0; 3933c8c45dfSChuck Lever 3943c8c45dfSChuck Lever switch (sa1->sa_family) { 3953c8c45dfSChuck Lever case AF_INET: 3963c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr4(sa1, sa2); 3973c8c45dfSChuck Lever case AF_INET6: 3983c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr6(sa1, sa2); 3993c8c45dfSChuck Lever } 4003c8c45dfSChuck Lever return 0; 4013c8c45dfSChuck Lever } 4023c8c45dfSChuck Lever 4033c8c45dfSChuck Lever /* 4043c8c45dfSChuck Lever * Test if two socket addresses represent the same actual socket, 4053c8c45dfSChuck Lever * by comparing (only) relevant fields, including the port number. 406d7371c41SIan Dall */ 407d7371c41SIan Dall static int nfs_sockaddr_cmp(const struct sockaddr *sa1, 408d7371c41SIan Dall const struct sockaddr *sa2) 409d7371c41SIan Dall { 410d7371c41SIan Dall if (sa1->sa_family != sa2->sa_family) 411d7371c41SIan Dall return 0; 412d7371c41SIan Dall 413d7371c41SIan Dall switch (sa1->sa_family) { 414d7371c41SIan Dall case AF_INET: 4159f4c899cSTrond Myklebust return nfs_sockaddr_cmp_ip4(sa1, sa2); 416d7371c41SIan Dall case AF_INET6: 4179f4c899cSTrond Myklebust return nfs_sockaddr_cmp_ip6(sa1, sa2); 418d7371c41SIan Dall } 419d7371c41SIan Dall return 0; 420d7371c41SIan Dall } 421d7371c41SIan Dall 422c36fca52SAndy Adamson /* Common match routine for v4.0 and v4.1 callback services */ 423c36fca52SAndy Adamson bool 424c36fca52SAndy Adamson nfs4_cb_match_client(const struct sockaddr *addr, struct nfs_client *clp, 425c36fca52SAndy Adamson u32 minorversion) 426c81468a1STrond Myklebust { 4273b0d3f93STrond Myklebust struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; 4283b0d3f93STrond Myklebust 429c36fca52SAndy Adamson /* Don't match clients that failed to initialise */ 43076db6d95SAndy Adamson if (!(clp->cl_cons_state == NFS_CS_READY || 43176db6d95SAndy Adamson clp->cl_cons_state == NFS_CS_SESSION_INITING)) 432c36fca52SAndy Adamson return false; 433c81468a1STrond Myklebust 434c36fca52SAndy Adamson /* Match the version and minorversion */ 435c36fca52SAndy Adamson if (clp->rpc_ops->version != 4 || 436c36fca52SAndy Adamson clp->cl_minorversion != minorversion) 437c36fca52SAndy Adamson return false; 438c81468a1STrond Myklebust 439c81468a1STrond Myklebust /* Match only the IP address, not the port number */ 4403b0d3f93STrond Myklebust if (!nfs_sockaddr_match_ipaddr(addr, clap)) 441c36fca52SAndy Adamson return false; 442c81468a1STrond Myklebust 443c36fca52SAndy Adamson return true; 4443fbd67adSTrond Myklebust } 4453fbd67adSTrond Myklebust 4463fbd67adSTrond Myklebust /* 447c81468a1STrond Myklebust * Find an nfs_client on the list that matches the initialisation data 448c81468a1STrond Myklebust * that is supplied. 449c81468a1STrond Myklebust */ 450c81468a1STrond Myklebust static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *data) 45124c8dbbbSDavid Howells { 45224c8dbbbSDavid Howells struct nfs_client *clp; 453d7371c41SIan Dall const struct sockaddr *sap = data->addr; 45424c8dbbbSDavid Howells 45524c8dbbbSDavid Howells list_for_each_entry(clp, &nfs_client_list, cl_share_link) { 456d7371c41SIan Dall const struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; 45713bbc06aSTrond Myklebust /* Don't match clients that failed to initialise properly */ 45813bbc06aSTrond Myklebust if (clp->cl_cons_state < 0) 45913bbc06aSTrond Myklebust continue; 46013bbc06aSTrond Myklebust 46124c8dbbbSDavid Howells /* Different NFS versions cannot share the same nfs_client */ 46240c55319STrond Myklebust if (clp->rpc_ops != data->rpc_ops) 46324c8dbbbSDavid Howells continue; 46424c8dbbbSDavid Howells 46559dca3b2STrond Myklebust if (clp->cl_proto != data->proto) 46659dca3b2STrond Myklebust continue; 4675aae4a9aSBenny Halevy /* Match nfsv4 minorversion */ 4685aae4a9aSBenny Halevy if (clp->cl_minorversion != data->minorversion) 4695aae4a9aSBenny Halevy continue; 470c81468a1STrond Myklebust /* Match the full socket address */ 471d7371c41SIan Dall if (!nfs_sockaddr_cmp(sap, clap)) 47224c8dbbbSDavid Howells continue; 47324c8dbbbSDavid Howells 47424c8dbbbSDavid Howells atomic_inc(&clp->cl_count); 47524c8dbbbSDavid Howells return clp; 47624c8dbbbSDavid Howells } 477c81468a1STrond Myklebust return NULL; 47824c8dbbbSDavid Howells } 47924c8dbbbSDavid Howells 48024c8dbbbSDavid Howells /* 48124c8dbbbSDavid Howells * Look up a client by IP address and protocol version 48224c8dbbbSDavid Howells * - creates a new record if one doesn't yet exist 48324c8dbbbSDavid Howells */ 48445a52a02SAndy Adamson static struct nfs_client * 48545a52a02SAndy Adamson nfs_get_client(const struct nfs_client_initdata *cl_init, 48645a52a02SAndy Adamson const struct rpc_timeout *timeparms, 48745a52a02SAndy Adamson const char *ip_addr, 48845a52a02SAndy Adamson rpc_authflavor_t authflavour, 48945a52a02SAndy Adamson int noresvport) 49024c8dbbbSDavid Howells { 49124c8dbbbSDavid Howells struct nfs_client *clp, *new = NULL; 49224c8dbbbSDavid Howells int error; 49324c8dbbbSDavid Howells 494d7422c47SChuck Lever dprintk("--> nfs_get_client(%s,v%u)\n", 495d7422c47SChuck Lever cl_init->hostname ?: "", cl_init->rpc_ops->version); 49624c8dbbbSDavid Howells 49724c8dbbbSDavid Howells /* see if the client already exists */ 49824c8dbbbSDavid Howells do { 49924c8dbbbSDavid Howells spin_lock(&nfs_client_lock); 50024c8dbbbSDavid Howells 501c81468a1STrond Myklebust clp = nfs_match_client(cl_init); 50224c8dbbbSDavid Howells if (clp) 50324c8dbbbSDavid Howells goto found_client; 50424c8dbbbSDavid Howells if (new) 50524c8dbbbSDavid Howells goto install_client; 50624c8dbbbSDavid Howells 50724c8dbbbSDavid Howells spin_unlock(&nfs_client_lock); 50824c8dbbbSDavid Howells 5093a498026STrond Myklebust new = nfs_alloc_client(cl_init); 510a21bdd9bSChuck Lever } while (!IS_ERR(new)); 51124c8dbbbSDavid Howells 512a21bdd9bSChuck Lever dprintk("--> nfs_get_client() = %ld [failed]\n", PTR_ERR(new)); 513a21bdd9bSChuck Lever return new; 51424c8dbbbSDavid Howells 51524c8dbbbSDavid Howells /* install a new client and return with it unready */ 51624c8dbbbSDavid Howells install_client: 51724c8dbbbSDavid Howells clp = new; 51824c8dbbbSDavid Howells list_add(&clp->cl_share_link, &nfs_client_list); 51924c8dbbbSDavid Howells spin_unlock(&nfs_client_lock); 52045a52a02SAndy Adamson 52145a52a02SAndy Adamson error = cl_init->rpc_ops->init_client(clp, timeparms, ip_addr, 52245a52a02SAndy Adamson authflavour, noresvport); 52345a52a02SAndy Adamson if (error < 0) { 52445a52a02SAndy Adamson nfs_put_client(clp); 52545a52a02SAndy Adamson return ERR_PTR(error); 52645a52a02SAndy Adamson } 52724c8dbbbSDavid Howells dprintk("--> nfs_get_client() = %p [new]\n", clp); 52824c8dbbbSDavid Howells return clp; 52924c8dbbbSDavid Howells 53024c8dbbbSDavid Howells /* found an existing client 53124c8dbbbSDavid Howells * - make sure it's ready before returning 53224c8dbbbSDavid Howells */ 53324c8dbbbSDavid Howells found_client: 53424c8dbbbSDavid Howells spin_unlock(&nfs_client_lock); 53524c8dbbbSDavid Howells 53624c8dbbbSDavid Howells if (new) 53724c8dbbbSDavid Howells nfs_free_client(new); 53824c8dbbbSDavid Howells 539150030b7SMatthew Wilcox error = wait_event_killable(nfs_client_active_wq, 54076db6d95SAndy Adamson clp->cl_cons_state < NFS_CS_INITING); 5410bae89ecSTrond Myklebust if (error < 0) { 54224c8dbbbSDavid Howells nfs_put_client(clp); 54324c8dbbbSDavid Howells return ERR_PTR(-ERESTARTSYS); 54424c8dbbbSDavid Howells } 54524c8dbbbSDavid Howells 54624c8dbbbSDavid Howells if (clp->cl_cons_state < NFS_CS_READY) { 54724c8dbbbSDavid Howells error = clp->cl_cons_state; 54824c8dbbbSDavid Howells nfs_put_client(clp); 54924c8dbbbSDavid Howells return ERR_PTR(error); 55024c8dbbbSDavid Howells } 55124c8dbbbSDavid Howells 55254ceac45SDavid Howells BUG_ON(clp->cl_cons_state != NFS_CS_READY); 55354ceac45SDavid Howells 55424c8dbbbSDavid Howells dprintk("--> nfs_get_client() = %p [share]\n", clp); 55524c8dbbbSDavid Howells return clp; 55624c8dbbbSDavid Howells } 55724c8dbbbSDavid Howells 55824c8dbbbSDavid Howells /* 55924c8dbbbSDavid Howells * Mark a server as ready or failed 56024c8dbbbSDavid Howells */ 56176db6d95SAndy Adamson void nfs_mark_client_ready(struct nfs_client *clp, int state) 56224c8dbbbSDavid Howells { 56324c8dbbbSDavid Howells clp->cl_cons_state = state; 56424c8dbbbSDavid Howells wake_up_all(&nfs_client_active_wq); 56524c8dbbbSDavid Howells } 5665006a76cSDavid Howells 5675006a76cSDavid Howells /* 568008f55d0SBenny Halevy * With sessions, the client is not marked ready until after a 569008f55d0SBenny Halevy * successful EXCHANGE_ID and CREATE_SESSION. 570008f55d0SBenny Halevy * 571008f55d0SBenny Halevy * Map errors cl_cons_state errors to EPROTONOSUPPORT to indicate 572008f55d0SBenny Halevy * other versions of NFS can be tried. 573008f55d0SBenny Halevy */ 574008f55d0SBenny Halevy int nfs4_check_client_ready(struct nfs_client *clp) 575008f55d0SBenny Halevy { 576008f55d0SBenny Halevy if (!nfs4_has_session(clp)) 577008f55d0SBenny Halevy return 0; 578008f55d0SBenny Halevy if (clp->cl_cons_state < NFS_CS_READY) 579008f55d0SBenny Halevy return -EPROTONOSUPPORT; 580008f55d0SBenny Halevy return 0; 581008f55d0SBenny Halevy } 582008f55d0SBenny Halevy 583008f55d0SBenny Halevy /* 5845006a76cSDavid Howells * Initialise the timeout values for a connection 5855006a76cSDavid Howells */ 5865006a76cSDavid Howells static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, 5875006a76cSDavid Howells unsigned int timeo, unsigned int retrans) 5885006a76cSDavid Howells { 5895006a76cSDavid Howells to->to_initval = timeo * HZ / 10; 5905006a76cSDavid Howells to->to_retries = retrans; 5915006a76cSDavid Howells 5925006a76cSDavid Howells switch (proto) { 5930896a725S\"Talpey, Thomas\ case XPRT_TRANSPORT_TCP: 5942cf7ff7aS\"Talpey, Thomas\ case XPRT_TRANSPORT_RDMA: 595259875efSTrond Myklebust if (to->to_retries == 0) 596259875efSTrond Myklebust to->to_retries = NFS_DEF_TCP_RETRANS; 5977a3e3e18STrond Myklebust if (to->to_initval == 0) 598259875efSTrond Myklebust to->to_initval = NFS_DEF_TCP_TIMEO * HZ / 10; 5995006a76cSDavid Howells if (to->to_initval > NFS_MAX_TCP_TIMEOUT) 6005006a76cSDavid Howells to->to_initval = NFS_MAX_TCP_TIMEOUT; 6015006a76cSDavid Howells to->to_increment = to->to_initval; 6025006a76cSDavid Howells to->to_maxval = to->to_initval + (to->to_increment * to->to_retries); 6037a3e3e18STrond Myklebust if (to->to_maxval > NFS_MAX_TCP_TIMEOUT) 6047a3e3e18STrond Myklebust to->to_maxval = NFS_MAX_TCP_TIMEOUT; 6057a3e3e18STrond Myklebust if (to->to_maxval < to->to_initval) 6067a3e3e18STrond Myklebust to->to_maxval = to->to_initval; 6075006a76cSDavid Howells to->to_exponential = 0; 6085006a76cSDavid Howells break; 6090896a725S\"Talpey, Thomas\ case XPRT_TRANSPORT_UDP: 610259875efSTrond Myklebust if (to->to_retries == 0) 611259875efSTrond Myklebust to->to_retries = NFS_DEF_UDP_RETRANS; 6125006a76cSDavid Howells if (!to->to_initval) 613259875efSTrond Myklebust to->to_initval = NFS_DEF_UDP_TIMEO * HZ / 10; 6145006a76cSDavid Howells if (to->to_initval > NFS_MAX_UDP_TIMEOUT) 6155006a76cSDavid Howells to->to_initval = NFS_MAX_UDP_TIMEOUT; 6165006a76cSDavid Howells to->to_maxval = NFS_MAX_UDP_TIMEOUT; 6175006a76cSDavid Howells to->to_exponential = 1; 6185006a76cSDavid Howells break; 619259875efSTrond Myklebust default: 620259875efSTrond Myklebust BUG(); 6215006a76cSDavid Howells } 6225006a76cSDavid Howells } 6235006a76cSDavid Howells 6245006a76cSDavid Howells /* 6255006a76cSDavid Howells * Create an RPC client handle 6265006a76cSDavid Howells */ 62759dca3b2STrond Myklebust static int nfs_create_rpc_client(struct nfs_client *clp, 62833170233STrond Myklebust const struct rpc_timeout *timeparms, 62943d78ef2SChuck Lever rpc_authflavor_t flavor, 6304a01b8a4SChuck Lever int discrtry, int noresvport) 6315006a76cSDavid Howells { 6325006a76cSDavid Howells struct rpc_clnt *clnt = NULL; 63341877d20SChuck Lever struct rpc_create_args args = { 634c653ce3fSPavel Emelyanov .net = &init_net, 63559dca3b2STrond Myklebust .protocol = clp->cl_proto, 63641877d20SChuck Lever .address = (struct sockaddr *)&clp->cl_addr, 6376e4cffd7SChuck Lever .addrsize = clp->cl_addrlen, 63833170233STrond Myklebust .timeout = timeparms, 63941877d20SChuck Lever .servername = clp->cl_hostname, 64041877d20SChuck Lever .program = &nfs_program, 64141877d20SChuck Lever .version = clp->rpc_ops->version, 64241877d20SChuck Lever .authflavor = flavor, 64341877d20SChuck Lever }; 6445006a76cSDavid Howells 6454a01b8a4SChuck Lever if (discrtry) 6464a01b8a4SChuck Lever args.flags |= RPC_CLNT_CREATE_DISCRTRY; 6474a01b8a4SChuck Lever if (noresvport) 6484a01b8a4SChuck Lever args.flags |= RPC_CLNT_CREATE_NONPRIVPORT; 6494a01b8a4SChuck Lever 6505006a76cSDavid Howells if (!IS_ERR(clp->cl_rpcclient)) 6515006a76cSDavid Howells return 0; 6525006a76cSDavid Howells 65341877d20SChuck Lever clnt = rpc_create(&args); 6545006a76cSDavid Howells if (IS_ERR(clnt)) { 6555006a76cSDavid Howells dprintk("%s: cannot create RPC client. Error = %ld\n", 6563110ff80SHarvey Harrison __func__, PTR_ERR(clnt)); 6575006a76cSDavid Howells return PTR_ERR(clnt); 6585006a76cSDavid Howells } 6595006a76cSDavid Howells 6605006a76cSDavid Howells clp->cl_rpcclient = clnt; 6615006a76cSDavid Howells return 0; 6625006a76cSDavid Howells } 66354ceac45SDavid Howells 66454ceac45SDavid Howells /* 66554ceac45SDavid Howells * Version 2 or 3 client destruction 66654ceac45SDavid Howells */ 66754ceac45SDavid Howells static void nfs_destroy_server(struct nfs_server *server) 66854ceac45SDavid Howells { 6695eebde23SSuresh Jayaraman if (!(server->flags & NFS_MOUNT_LOCAL_FLOCK) || 6705eebde23SSuresh Jayaraman !(server->flags & NFS_MOUNT_LOCAL_FCNTL)) 6719289e7f9SChuck Lever nlmclnt_done(server->nlm_host); 67254ceac45SDavid Howells } 67354ceac45SDavid Howells 67454ceac45SDavid Howells /* 67554ceac45SDavid Howells * Version 2 or 3 lockd setup 67654ceac45SDavid Howells */ 67754ceac45SDavid Howells static int nfs_start_lockd(struct nfs_server *server) 67854ceac45SDavid Howells { 6799289e7f9SChuck Lever struct nlm_host *host; 6809289e7f9SChuck Lever struct nfs_client *clp = server->nfs_client; 681883bb163SChuck Lever struct nlmclnt_initdata nlm_init = { 682883bb163SChuck Lever .hostname = clp->cl_hostname, 683883bb163SChuck Lever .address = (struct sockaddr *)&clp->cl_addr, 684883bb163SChuck Lever .addrlen = clp->cl_addrlen, 685883bb163SChuck Lever .nfs_version = clp->rpc_ops->version, 6860cb2659bSChuck Lever .noresvport = server->flags & NFS_MOUNT_NORESVPORT ? 6870cb2659bSChuck Lever 1 : 0, 688883bb163SChuck Lever }; 68954ceac45SDavid Howells 690883bb163SChuck Lever if (nlm_init.nfs_version > 3) 6919289e7f9SChuck Lever return 0; 6925eebde23SSuresh Jayaraman if ((server->flags & NFS_MOUNT_LOCAL_FLOCK) && 6935eebde23SSuresh Jayaraman (server->flags & NFS_MOUNT_LOCAL_FCNTL)) 6949289e7f9SChuck Lever return 0; 6959289e7f9SChuck Lever 6968a6e5debSTrond Myklebust switch (clp->cl_proto) { 6978a6e5debSTrond Myklebust default: 6988a6e5debSTrond Myklebust nlm_init.protocol = IPPROTO_TCP; 6998a6e5debSTrond Myklebust break; 7008a6e5debSTrond Myklebust case XPRT_TRANSPORT_UDP: 7018a6e5debSTrond Myklebust nlm_init.protocol = IPPROTO_UDP; 7028a6e5debSTrond Myklebust } 7038a6e5debSTrond Myklebust 704883bb163SChuck Lever host = nlmclnt_init(&nlm_init); 7059289e7f9SChuck Lever if (IS_ERR(host)) 7069289e7f9SChuck Lever return PTR_ERR(host); 7079289e7f9SChuck Lever 7089289e7f9SChuck Lever server->nlm_host = host; 70954ceac45SDavid Howells server->destroy = nfs_destroy_server; 7109289e7f9SChuck Lever return 0; 71154ceac45SDavid Howells } 71254ceac45SDavid Howells 71354ceac45SDavid Howells /* 71454ceac45SDavid Howells * Initialise an NFSv3 ACL client connection 71554ceac45SDavid Howells */ 71654ceac45SDavid Howells #ifdef CONFIG_NFS_V3_ACL 71754ceac45SDavid Howells static void nfs_init_server_aclclient(struct nfs_server *server) 71854ceac45SDavid Howells { 71940c55319STrond Myklebust if (server->nfs_client->rpc_ops->version != 3) 72054ceac45SDavid Howells goto out_noacl; 72154ceac45SDavid Howells if (server->flags & NFS_MOUNT_NOACL) 72254ceac45SDavid Howells goto out_noacl; 72354ceac45SDavid Howells 72454ceac45SDavid Howells server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3); 72554ceac45SDavid Howells if (IS_ERR(server->client_acl)) 72654ceac45SDavid Howells goto out_noacl; 72754ceac45SDavid Howells 72854ceac45SDavid Howells /* No errors! Assume that Sun nfsacls are supported */ 72954ceac45SDavid Howells server->caps |= NFS_CAP_ACLS; 73054ceac45SDavid Howells return; 73154ceac45SDavid Howells 73254ceac45SDavid Howells out_noacl: 73354ceac45SDavid Howells server->caps &= ~NFS_CAP_ACLS; 73454ceac45SDavid Howells } 73554ceac45SDavid Howells #else 73654ceac45SDavid Howells static inline void nfs_init_server_aclclient(struct nfs_server *server) 73754ceac45SDavid Howells { 73854ceac45SDavid Howells server->flags &= ~NFS_MOUNT_NOACL; 73954ceac45SDavid Howells server->caps &= ~NFS_CAP_ACLS; 74054ceac45SDavid Howells } 74154ceac45SDavid Howells #endif 74254ceac45SDavid Howells 74354ceac45SDavid Howells /* 74454ceac45SDavid Howells * Create a general RPC client 74554ceac45SDavid Howells */ 74633170233STrond Myklebust static int nfs_init_server_rpcclient(struct nfs_server *server, 74733170233STrond Myklebust const struct rpc_timeout *timeo, 74833170233STrond Myklebust rpc_authflavor_t pseudoflavour) 74954ceac45SDavid Howells { 75054ceac45SDavid Howells struct nfs_client *clp = server->nfs_client; 75154ceac45SDavid Howells 75254ceac45SDavid Howells server->client = rpc_clone_client(clp->cl_rpcclient); 75354ceac45SDavid Howells if (IS_ERR(server->client)) { 7543110ff80SHarvey Harrison dprintk("%s: couldn't create rpc_client!\n", __func__); 75554ceac45SDavid Howells return PTR_ERR(server->client); 75654ceac45SDavid Howells } 75754ceac45SDavid Howells 75833170233STrond Myklebust memcpy(&server->client->cl_timeout_default, 75933170233STrond Myklebust timeo, 76033170233STrond Myklebust sizeof(server->client->cl_timeout_default)); 76133170233STrond Myklebust server->client->cl_timeout = &server->client->cl_timeout_default; 76233170233STrond Myklebust 76354ceac45SDavid Howells if (pseudoflavour != clp->cl_rpcclient->cl_auth->au_flavor) { 76454ceac45SDavid Howells struct rpc_auth *auth; 76554ceac45SDavid Howells 76654ceac45SDavid Howells auth = rpcauth_create(pseudoflavour, server->client); 76754ceac45SDavid Howells if (IS_ERR(auth)) { 7683110ff80SHarvey Harrison dprintk("%s: couldn't create credcache!\n", __func__); 76954ceac45SDavid Howells return PTR_ERR(auth); 77054ceac45SDavid Howells } 77154ceac45SDavid Howells } 77254ceac45SDavid Howells server->client->cl_softrtry = 0; 77354ceac45SDavid Howells if (server->flags & NFS_MOUNT_SOFT) 77454ceac45SDavid Howells server->client->cl_softrtry = 1; 77554ceac45SDavid Howells 77654ceac45SDavid Howells return 0; 77754ceac45SDavid Howells } 77854ceac45SDavid Howells 77954ceac45SDavid Howells /* 78054ceac45SDavid Howells * Initialise an NFS2 or NFS3 client 78154ceac45SDavid Howells */ 78245a52a02SAndy Adamson int nfs_init_client(struct nfs_client *clp, const struct rpc_timeout *timeparms, 78345a52a02SAndy Adamson const char *ip_addr, rpc_authflavor_t authflavour, 78445a52a02SAndy Adamson int noresvport) 78554ceac45SDavid Howells { 78654ceac45SDavid Howells int error; 78754ceac45SDavid Howells 78854ceac45SDavid Howells if (clp->cl_cons_state == NFS_CS_READY) { 78954ceac45SDavid Howells /* the client is already initialised */ 79054ceac45SDavid Howells dprintk("<-- nfs_init_client() = 0 [already %p]\n", clp); 79154ceac45SDavid Howells return 0; 79254ceac45SDavid Howells } 79354ceac45SDavid Howells 79454ceac45SDavid Howells /* 79554ceac45SDavid Howells * Create a client RPC handle for doing FSSTAT with UNIX auth only 79654ceac45SDavid Howells * - RFC 2623, sec 2.3.2 79754ceac45SDavid Howells */ 798d740351bSChuck Lever error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX, 79945a52a02SAndy Adamson 0, noresvport); 80054ceac45SDavid Howells if (error < 0) 80154ceac45SDavid Howells goto error; 80254ceac45SDavid Howells nfs_mark_client_ready(clp, NFS_CS_READY); 80354ceac45SDavid Howells return 0; 80454ceac45SDavid Howells 80554ceac45SDavid Howells error: 80654ceac45SDavid Howells nfs_mark_client_ready(clp, error); 80754ceac45SDavid Howells dprintk("<-- nfs_init_client() = xerror %d\n", error); 80854ceac45SDavid Howells return error; 80954ceac45SDavid Howells } 81054ceac45SDavid Howells 81154ceac45SDavid Howells /* 81254ceac45SDavid Howells * Create a version 2 or 3 client 81354ceac45SDavid Howells */ 8142283f8d6S\"Talpey, Thomas\ static int nfs_init_server(struct nfs_server *server, 8152283f8d6S\"Talpey, Thomas\ const struct nfs_parsed_mount_data *data) 81654ceac45SDavid Howells { 8173a498026STrond Myklebust struct nfs_client_initdata cl_init = { 8183a498026STrond Myklebust .hostname = data->nfs_server.hostname, 819d7422c47SChuck Lever .addr = (const struct sockaddr *)&data->nfs_server.address, 8204c568017SChuck Lever .addrlen = data->nfs_server.addrlen, 82140c55319STrond Myklebust .rpc_ops = &nfs_v2_clientops, 82259dca3b2STrond Myklebust .proto = data->nfs_server.protocol, 8233a498026STrond Myklebust }; 82433170233STrond Myklebust struct rpc_timeout timeparms; 82554ceac45SDavid Howells struct nfs_client *clp; 8263a498026STrond Myklebust int error; 82754ceac45SDavid Howells 82854ceac45SDavid Howells dprintk("--> nfs_init_server()\n"); 82954ceac45SDavid Howells 83054ceac45SDavid Howells #ifdef CONFIG_NFS_V3 8318a6e5debSTrond Myklebust if (data->version == 3) 83240c55319STrond Myklebust cl_init.rpc_ops = &nfs_v3_clientops; 83354ceac45SDavid Howells #endif 83454ceac45SDavid Howells 83545a52a02SAndy Adamson nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, 83645a52a02SAndy Adamson data->timeo, data->retrans); 83745a52a02SAndy Adamson 83854ceac45SDavid Howells /* Allocate or find a client reference we can use */ 83945a52a02SAndy Adamson clp = nfs_get_client(&cl_init, &timeparms, NULL, RPC_AUTH_UNIX, 84045a52a02SAndy Adamson data->flags & NFS_MOUNT_NORESVPORT); 84154ceac45SDavid Howells if (IS_ERR(clp)) { 84254ceac45SDavid Howells dprintk("<-- nfs_init_server() = error %ld\n", PTR_ERR(clp)); 84354ceac45SDavid Howells return PTR_ERR(clp); 84454ceac45SDavid Howells } 84554ceac45SDavid Howells 84654ceac45SDavid Howells server->nfs_client = clp; 84754ceac45SDavid Howells 84854ceac45SDavid Howells /* Initialise the client representation from the mount data */ 849ff3525a5STrond Myklebust server->flags = data->flags; 850b797cac7SDavid Howells server->options = data->options; 85162ab460cSTrond Myklebust server->caps |= NFS_CAP_HARDLINKS|NFS_CAP_SYMLINKS|NFS_CAP_FILEID| 85262ab460cSTrond Myklebust NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|NFS_CAP_OWNER_GROUP| 85362ab460cSTrond Myklebust NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME; 85454ceac45SDavid Howells 85554ceac45SDavid Howells if (data->rsize) 85654ceac45SDavid Howells server->rsize = nfs_block_size(data->rsize, NULL); 85754ceac45SDavid Howells if (data->wsize) 85854ceac45SDavid Howells server->wsize = nfs_block_size(data->wsize, NULL); 85954ceac45SDavid Howells 86054ceac45SDavid Howells server->acregmin = data->acregmin * HZ; 86154ceac45SDavid Howells server->acregmax = data->acregmax * HZ; 86254ceac45SDavid Howells server->acdirmin = data->acdirmin * HZ; 86354ceac45SDavid Howells server->acdirmax = data->acdirmax * HZ; 86454ceac45SDavid Howells 86554ceac45SDavid Howells /* Start lockd here, before we might error out */ 86654ceac45SDavid Howells error = nfs_start_lockd(server); 86754ceac45SDavid Howells if (error < 0) 86854ceac45SDavid Howells goto error; 86954ceac45SDavid Howells 870f22d6d79SChuck Lever server->port = data->nfs_server.port; 871f22d6d79SChuck Lever 87233170233STrond Myklebust error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]); 87354ceac45SDavid Howells if (error < 0) 87454ceac45SDavid Howells goto error; 87554ceac45SDavid Howells 8763f8400d1SChuck Lever /* Preserve the values of mount_server-related mount options */ 8773f8400d1SChuck Lever if (data->mount_server.addrlen) { 8783f8400d1SChuck Lever memcpy(&server->mountd_address, &data->mount_server.address, 8793f8400d1SChuck Lever data->mount_server.addrlen); 8803f8400d1SChuck Lever server->mountd_addrlen = data->mount_server.addrlen; 8813f8400d1SChuck Lever } 8823f8400d1SChuck Lever server->mountd_version = data->mount_server.version; 8833f8400d1SChuck Lever server->mountd_port = data->mount_server.port; 8843f8400d1SChuck Lever server->mountd_protocol = data->mount_server.protocol; 8853f8400d1SChuck Lever 88654ceac45SDavid Howells server->namelen = data->namlen; 88754ceac45SDavid Howells /* Create a client RPC handle for the NFSv3 ACL management interface */ 88854ceac45SDavid Howells nfs_init_server_aclclient(server); 88954ceac45SDavid Howells dprintk("<-- nfs_init_server() = 0 [new %p]\n", clp); 89054ceac45SDavid Howells return 0; 89154ceac45SDavid Howells 89254ceac45SDavid Howells error: 89354ceac45SDavid Howells server->nfs_client = NULL; 89454ceac45SDavid Howells nfs_put_client(clp); 89554ceac45SDavid Howells dprintk("<-- nfs_init_server() = xerror %d\n", error); 89654ceac45SDavid Howells return error; 89754ceac45SDavid Howells } 89854ceac45SDavid Howells 89954ceac45SDavid Howells /* 90054ceac45SDavid Howells * Load up the server record from information gained in an fsinfo record 90154ceac45SDavid Howells */ 90254ceac45SDavid Howells static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo *fsinfo) 90354ceac45SDavid Howells { 90454ceac45SDavid Howells unsigned long max_rpc_payload; 90554ceac45SDavid Howells 90654ceac45SDavid Howells /* Work out a lot of parameters */ 90754ceac45SDavid Howells if (server->rsize == 0) 90854ceac45SDavid Howells server->rsize = nfs_block_size(fsinfo->rtpref, NULL); 90954ceac45SDavid Howells if (server->wsize == 0) 91054ceac45SDavid Howells server->wsize = nfs_block_size(fsinfo->wtpref, NULL); 91154ceac45SDavid Howells 91254ceac45SDavid Howells if (fsinfo->rtmax >= 512 && server->rsize > fsinfo->rtmax) 91354ceac45SDavid Howells server->rsize = nfs_block_size(fsinfo->rtmax, NULL); 91454ceac45SDavid Howells if (fsinfo->wtmax >= 512 && server->wsize > fsinfo->wtmax) 91554ceac45SDavid Howells server->wsize = nfs_block_size(fsinfo->wtmax, NULL); 91654ceac45SDavid Howells 91754ceac45SDavid Howells max_rpc_payload = nfs_block_size(rpc_max_payload(server->client), NULL); 91854ceac45SDavid Howells if (server->rsize > max_rpc_payload) 91954ceac45SDavid Howells server->rsize = max_rpc_payload; 92054ceac45SDavid Howells if (server->rsize > NFS_MAX_FILE_IO_SIZE) 92154ceac45SDavid Howells server->rsize = NFS_MAX_FILE_IO_SIZE; 92254ceac45SDavid Howells server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 923e0bf68ddSPeter Zijlstra 924d993831fSJens Axboe server->backing_dev_info.name = "nfs"; 92554ceac45SDavid Howells server->backing_dev_info.ra_pages = server->rpages * NFS_MAX_READAHEAD; 92654ceac45SDavid Howells 92754ceac45SDavid Howells if (server->wsize > max_rpc_payload) 92854ceac45SDavid Howells server->wsize = max_rpc_payload; 92954ceac45SDavid Howells if (server->wsize > NFS_MAX_FILE_IO_SIZE) 93054ceac45SDavid Howells server->wsize = NFS_MAX_FILE_IO_SIZE; 93154ceac45SDavid Howells server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 93285e174baSRicardo Labiaga set_pnfs_layoutdriver(server, fsinfo->layouttype); 93385e174baSRicardo Labiaga 93454ceac45SDavid Howells server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL); 93554ceac45SDavid Howells 93654ceac45SDavid Howells server->dtsize = nfs_block_size(fsinfo->dtpref, NULL); 93756e4ebf8SBryan Schumaker if (server->dtsize > PAGE_CACHE_SIZE * NFS_MAX_READDIR_PAGES) 93856e4ebf8SBryan Schumaker server->dtsize = PAGE_CACHE_SIZE * NFS_MAX_READDIR_PAGES; 93954ceac45SDavid Howells if (server->dtsize > server->rsize) 94054ceac45SDavid Howells server->dtsize = server->rsize; 94154ceac45SDavid Howells 94254ceac45SDavid Howells if (server->flags & NFS_MOUNT_NOAC) { 94354ceac45SDavid Howells server->acregmin = server->acregmax = 0; 94454ceac45SDavid Howells server->acdirmin = server->acdirmax = 0; 94554ceac45SDavid Howells } 94654ceac45SDavid Howells 94754ceac45SDavid Howells server->maxfilesize = fsinfo->maxfilesize; 94854ceac45SDavid Howells 9496b96724eSRicardo Labiaga server->time_delta = fsinfo->time_delta; 9506b96724eSRicardo Labiaga 95154ceac45SDavid Howells /* We're airborne Set socket buffersize */ 95254ceac45SDavid Howells rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100); 95354ceac45SDavid Howells } 95454ceac45SDavid Howells 95554ceac45SDavid Howells /* 95654ceac45SDavid Howells * Probe filesystem information, including the FSID on v2/v3 95754ceac45SDavid Howells */ 95854ceac45SDavid Howells static int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr) 95954ceac45SDavid Howells { 96054ceac45SDavid Howells struct nfs_fsinfo fsinfo; 96154ceac45SDavid Howells struct nfs_client *clp = server->nfs_client; 96254ceac45SDavid Howells int error; 96354ceac45SDavid Howells 96454ceac45SDavid Howells dprintk("--> nfs_probe_fsinfo()\n"); 96554ceac45SDavid Howells 96654ceac45SDavid Howells if (clp->rpc_ops->set_capabilities != NULL) { 96754ceac45SDavid Howells error = clp->rpc_ops->set_capabilities(server, mntfh); 96854ceac45SDavid Howells if (error < 0) 96954ceac45SDavid Howells goto out_error; 97054ceac45SDavid Howells } 97154ceac45SDavid Howells 97254ceac45SDavid Howells fsinfo.fattr = fattr; 97385e174baSRicardo Labiaga fsinfo.layouttype = 0; 97454ceac45SDavid Howells error = clp->rpc_ops->fsinfo(server, mntfh, &fsinfo); 97554ceac45SDavid Howells if (error < 0) 97654ceac45SDavid Howells goto out_error; 97754ceac45SDavid Howells 97854ceac45SDavid Howells nfs_server_set_fsinfo(server, &fsinfo); 97954ceac45SDavid Howells 98054ceac45SDavid Howells /* Get some general file system info */ 98154ceac45SDavid Howells if (server->namelen == 0) { 98254ceac45SDavid Howells struct nfs_pathconf pathinfo; 98354ceac45SDavid Howells 98454ceac45SDavid Howells pathinfo.fattr = fattr; 98554ceac45SDavid Howells nfs_fattr_init(fattr); 98654ceac45SDavid Howells 98754ceac45SDavid Howells if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0) 98854ceac45SDavid Howells server->namelen = pathinfo.max_namelen; 98954ceac45SDavid Howells } 99054ceac45SDavid Howells 99154ceac45SDavid Howells dprintk("<-- nfs_probe_fsinfo() = 0\n"); 99254ceac45SDavid Howells return 0; 99354ceac45SDavid Howells 99454ceac45SDavid Howells out_error: 99554ceac45SDavid Howells dprintk("nfs_probe_fsinfo: error = %d\n", -error); 99654ceac45SDavid Howells return error; 99754ceac45SDavid Howells } 99854ceac45SDavid Howells 99954ceac45SDavid Howells /* 100054ceac45SDavid Howells * Copy useful information when duplicating a server record 100154ceac45SDavid Howells */ 100254ceac45SDavid Howells static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source) 100354ceac45SDavid Howells { 100454ceac45SDavid Howells target->flags = source->flags; 1005356e76b8SChuck Lever target->rsize = source->rsize; 1006356e76b8SChuck Lever target->wsize = source->wsize; 100754ceac45SDavid Howells target->acregmin = source->acregmin; 100854ceac45SDavid Howells target->acregmax = source->acregmax; 100954ceac45SDavid Howells target->acdirmin = source->acdirmin; 101054ceac45SDavid Howells target->acdirmax = source->acdirmax; 101154ceac45SDavid Howells target->caps = source->caps; 10122df54806SDavid Howells target->options = source->options; 101354ceac45SDavid Howells } 101454ceac45SDavid Howells 1015fca5238eSChuck Lever static void nfs_server_insert_lists(struct nfs_server *server) 1016fca5238eSChuck Lever { 1017fca5238eSChuck Lever struct nfs_client *clp = server->nfs_client; 1018fca5238eSChuck Lever 1019fca5238eSChuck Lever spin_lock(&nfs_client_lock); 1020fca5238eSChuck Lever list_add_tail_rcu(&server->client_link, &clp->cl_superblocks); 1021fca5238eSChuck Lever list_add_tail(&server->master_link, &nfs_volume_list); 1022d3b4c9d7SAndy Adamson clear_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state); 1023fca5238eSChuck Lever spin_unlock(&nfs_client_lock); 1024fca5238eSChuck Lever 1025fca5238eSChuck Lever } 1026fca5238eSChuck Lever 1027fca5238eSChuck Lever static void nfs_server_remove_lists(struct nfs_server *server) 1028fca5238eSChuck Lever { 1029d3b4c9d7SAndy Adamson struct nfs_client *clp = server->nfs_client; 1030d3b4c9d7SAndy Adamson 1031fca5238eSChuck Lever spin_lock(&nfs_client_lock); 1032fca5238eSChuck Lever list_del_rcu(&server->client_link); 1033d3b4c9d7SAndy Adamson if (clp && list_empty(&clp->cl_superblocks)) 1034d3b4c9d7SAndy Adamson set_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state); 1035fca5238eSChuck Lever list_del(&server->master_link); 1036fca5238eSChuck Lever spin_unlock(&nfs_client_lock); 1037fca5238eSChuck Lever 1038fca5238eSChuck Lever synchronize_rcu(); 1039fca5238eSChuck Lever } 1040fca5238eSChuck Lever 104154ceac45SDavid Howells /* 104254ceac45SDavid Howells * Allocate and initialise a server record 104354ceac45SDavid Howells */ 104454ceac45SDavid Howells static struct nfs_server *nfs_alloc_server(void) 104554ceac45SDavid Howells { 104654ceac45SDavid Howells struct nfs_server *server; 104754ceac45SDavid Howells 104854ceac45SDavid Howells server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL); 104954ceac45SDavid Howells if (!server) 105054ceac45SDavid Howells return NULL; 105154ceac45SDavid Howells 105254ceac45SDavid Howells server->client = server->client_acl = ERR_PTR(-EINVAL); 105354ceac45SDavid Howells 105454ceac45SDavid Howells /* Zero out the NFS state stuff */ 105554ceac45SDavid Howells INIT_LIST_HEAD(&server->client_link); 105654ceac45SDavid Howells INIT_LIST_HEAD(&server->master_link); 1057d3978bb3SChuck Lever INIT_LIST_HEAD(&server->delegations); 105854ceac45SDavid Howells 1059ef818a28SSteve Dickson atomic_set(&server->active, 0); 1060ef818a28SSteve Dickson 106154ceac45SDavid Howells server->io_stats = nfs_alloc_iostats(); 106254ceac45SDavid Howells if (!server->io_stats) { 106354ceac45SDavid Howells kfree(server); 106454ceac45SDavid Howells return NULL; 106554ceac45SDavid Howells } 106654ceac45SDavid Howells 106748d07649SJens Axboe if (bdi_init(&server->backing_dev_info)) { 106848d07649SJens Axboe nfs_free_iostats(server->io_stats); 106948d07649SJens Axboe kfree(server); 107048d07649SJens Axboe return NULL; 107148d07649SJens Axboe } 107248d07649SJens Axboe 1073f7e8917aSFred Isaman pnfs_init_server(server); 1074f7e8917aSFred Isaman 107554ceac45SDavid Howells return server; 107654ceac45SDavid Howells } 107754ceac45SDavid Howells 107854ceac45SDavid Howells /* 107954ceac45SDavid Howells * Free up a server record 108054ceac45SDavid Howells */ 108154ceac45SDavid Howells void nfs_free_server(struct nfs_server *server) 108254ceac45SDavid Howells { 108354ceac45SDavid Howells dprintk("--> nfs_free_server()\n"); 108454ceac45SDavid Howells 1085fca5238eSChuck Lever nfs_server_remove_lists(server); 108685e174baSRicardo Labiaga unset_pnfs_layoutdriver(server); 108754ceac45SDavid Howells 108854ceac45SDavid Howells if (server->destroy != NULL) 108954ceac45SDavid Howells server->destroy(server); 10905cef338bSTrond Myklebust 10915cef338bSTrond Myklebust if (!IS_ERR(server->client_acl)) 10925cef338bSTrond Myklebust rpc_shutdown_client(server->client_acl); 109354ceac45SDavid Howells if (!IS_ERR(server->client)) 109454ceac45SDavid Howells rpc_shutdown_client(server->client); 109554ceac45SDavid Howells 109654ceac45SDavid Howells nfs_put_client(server->nfs_client); 109754ceac45SDavid Howells 109854ceac45SDavid Howells nfs_free_iostats(server->io_stats); 1099e0bf68ddSPeter Zijlstra bdi_destroy(&server->backing_dev_info); 110054ceac45SDavid Howells kfree(server); 110154ceac45SDavid Howells nfs_release_automount_timer(); 110254ceac45SDavid Howells dprintk("<-- nfs_free_server()\n"); 110354ceac45SDavid Howells } 110454ceac45SDavid Howells 110554ceac45SDavid Howells /* 110654ceac45SDavid Howells * Create a version 2 or 3 volume record 110754ceac45SDavid Howells * - keyed on server and FSID 110854ceac45SDavid Howells */ 11092283f8d6S\"Talpey, Thomas\ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, 111054ceac45SDavid Howells struct nfs_fh *mntfh) 111154ceac45SDavid Howells { 111254ceac45SDavid Howells struct nfs_server *server; 1113fbca779aSTrond Myklebust struct nfs_fattr *fattr; 111454ceac45SDavid Howells int error; 111554ceac45SDavid Howells 111654ceac45SDavid Howells server = nfs_alloc_server(); 111754ceac45SDavid Howells if (!server) 111854ceac45SDavid Howells return ERR_PTR(-ENOMEM); 111954ceac45SDavid Howells 1120fbca779aSTrond Myklebust error = -ENOMEM; 1121fbca779aSTrond Myklebust fattr = nfs_alloc_fattr(); 1122fbca779aSTrond Myklebust if (fattr == NULL) 1123fbca779aSTrond Myklebust goto error; 1124fbca779aSTrond Myklebust 112554ceac45SDavid Howells /* Get a client representation */ 112654ceac45SDavid Howells error = nfs_init_server(server, data); 112754ceac45SDavid Howells if (error < 0) 112854ceac45SDavid Howells goto error; 112954ceac45SDavid Howells 113054ceac45SDavid Howells BUG_ON(!server->nfs_client); 113154ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops); 113254ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 113354ceac45SDavid Howells 113454ceac45SDavid Howells /* Probe the root fh to retrieve its FSID */ 1135fbca779aSTrond Myklebust error = nfs_probe_fsinfo(server, mntfh, fattr); 113654ceac45SDavid Howells if (error < 0) 113754ceac45SDavid Howells goto error; 113854af3bb5STrond Myklebust if (server->nfs_client->rpc_ops->version == 3) { 113954af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) 114054af3bb5STrond Myklebust server->namelen = NFS3_MAXNAMLEN; 114154af3bb5STrond Myklebust if (!(data->flags & NFS_MOUNT_NORDIRPLUS)) 114254af3bb5STrond Myklebust server->caps |= NFS_CAP_READDIRPLUS; 114354af3bb5STrond Myklebust } else { 114454af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN) 114554af3bb5STrond Myklebust server->namelen = NFS2_MAXNAMLEN; 114654af3bb5STrond Myklebust } 114754af3bb5STrond Myklebust 1148fbca779aSTrond Myklebust if (!(fattr->valid & NFS_ATTR_FATTR)) { 1149fbca779aSTrond Myklebust error = server->nfs_client->rpc_ops->getattr(server, mntfh, fattr); 115054ceac45SDavid Howells if (error < 0) { 115154ceac45SDavid Howells dprintk("nfs_create_server: getattr error = %d\n", -error); 115254ceac45SDavid Howells goto error; 115354ceac45SDavid Howells } 115454ceac45SDavid Howells } 1155fbca779aSTrond Myklebust memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid)); 115654ceac45SDavid Howells 11576daabf1bSDavid Howells dprintk("Server FSID: %llx:%llx\n", 11586daabf1bSDavid Howells (unsigned long long) server->fsid.major, 11596daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 116054ceac45SDavid Howells 1161fca5238eSChuck Lever nfs_server_insert_lists(server); 116254ceac45SDavid Howells server->mount_time = jiffies; 1163fbca779aSTrond Myklebust nfs_free_fattr(fattr); 116454ceac45SDavid Howells return server; 116554ceac45SDavid Howells 116654ceac45SDavid Howells error: 1167fbca779aSTrond Myklebust nfs_free_fattr(fattr); 116854ceac45SDavid Howells nfs_free_server(server); 116954ceac45SDavid Howells return ERR_PTR(error); 117054ceac45SDavid Howells } 117154ceac45SDavid Howells 117254ceac45SDavid Howells #ifdef CONFIG_NFS_V4 117354ceac45SDavid Howells /* 1174c36fca52SAndy Adamson * NFSv4.0 callback thread helper 1175c36fca52SAndy Adamson * 1176c36fca52SAndy Adamson * Find a client by IP address, protocol version, and minorversion 1177c36fca52SAndy Adamson * 1178c36fca52SAndy Adamson * Called from the pg_authenticate method. The callback identifier 1179c36fca52SAndy Adamson * is not used as it has not been decoded. 1180c36fca52SAndy Adamson * 1181c36fca52SAndy Adamson * Returns NULL if no such client 1182c36fca52SAndy Adamson */ 1183c36fca52SAndy Adamson struct nfs_client * 1184c36fca52SAndy Adamson nfs4_find_client_no_ident(const struct sockaddr *addr) 1185c36fca52SAndy Adamson { 1186c36fca52SAndy Adamson struct nfs_client *clp; 1187c36fca52SAndy Adamson 1188c36fca52SAndy Adamson spin_lock(&nfs_client_lock); 1189c36fca52SAndy Adamson list_for_each_entry(clp, &nfs_client_list, cl_share_link) { 1190c36fca52SAndy Adamson if (nfs4_cb_match_client(addr, clp, 0) == false) 1191c36fca52SAndy Adamson continue; 1192c36fca52SAndy Adamson atomic_inc(&clp->cl_count); 1193c36fca52SAndy Adamson spin_unlock(&nfs_client_lock); 1194c36fca52SAndy Adamson return clp; 1195c36fca52SAndy Adamson } 1196c36fca52SAndy Adamson spin_unlock(&nfs_client_lock); 1197c36fca52SAndy Adamson return NULL; 1198c36fca52SAndy Adamson } 1199c36fca52SAndy Adamson 1200c36fca52SAndy Adamson /* 1201c36fca52SAndy Adamson * NFSv4.0 callback thread helper 1202c36fca52SAndy Adamson * 1203c36fca52SAndy Adamson * Find a client by callback identifier 1204c36fca52SAndy Adamson */ 1205c36fca52SAndy Adamson struct nfs_client * 1206c36fca52SAndy Adamson nfs4_find_client_ident(int cb_ident) 1207c36fca52SAndy Adamson { 1208c36fca52SAndy Adamson struct nfs_client *clp; 1209c36fca52SAndy Adamson 1210c36fca52SAndy Adamson spin_lock(&nfs_client_lock); 1211c36fca52SAndy Adamson clp = idr_find(&cb_ident_idr, cb_ident); 1212c36fca52SAndy Adamson if (clp) 1213c36fca52SAndy Adamson atomic_inc(&clp->cl_count); 1214c36fca52SAndy Adamson spin_unlock(&nfs_client_lock); 1215c36fca52SAndy Adamson return clp; 1216c36fca52SAndy Adamson } 1217c36fca52SAndy Adamson 1218c36fca52SAndy Adamson #if defined(CONFIG_NFS_V4_1) 1219c36fca52SAndy Adamson /* 1220c36fca52SAndy Adamson * NFSv4.1 callback thread helper 1221c36fca52SAndy Adamson * For CB_COMPOUND calls, find a client by IP address, protocol version, 1222c36fca52SAndy Adamson * minorversion, and sessionID 1223c36fca52SAndy Adamson * 1224c36fca52SAndy Adamson * Returns NULL if no such client 1225c36fca52SAndy Adamson */ 1226c36fca52SAndy Adamson struct nfs_client * 1227c36fca52SAndy Adamson nfs4_find_client_sessionid(const struct sockaddr *addr, 1228778be232SAndy Adamson struct nfs4_sessionid *sid) 1229c36fca52SAndy Adamson { 1230c36fca52SAndy Adamson struct nfs_client *clp; 1231c36fca52SAndy Adamson 1232c36fca52SAndy Adamson spin_lock(&nfs_client_lock); 1233c36fca52SAndy Adamson list_for_each_entry(clp, &nfs_client_list, cl_share_link) { 1234c36fca52SAndy Adamson if (nfs4_cb_match_client(addr, clp, 1) == false) 1235c36fca52SAndy Adamson continue; 1236c36fca52SAndy Adamson 1237c36fca52SAndy Adamson if (!nfs4_has_session(clp)) 1238c36fca52SAndy Adamson continue; 1239c36fca52SAndy Adamson 1240778be232SAndy Adamson /* Match sessionid*/ 1241778be232SAndy Adamson if (memcmp(clp->cl_session->sess_id.data, 1242778be232SAndy Adamson sid->data, NFS4_MAX_SESSIONID_LEN) != 0) 1243c36fca52SAndy Adamson continue; 1244c36fca52SAndy Adamson 1245c36fca52SAndy Adamson atomic_inc(&clp->cl_count); 1246c36fca52SAndy Adamson spin_unlock(&nfs_client_lock); 1247c36fca52SAndy Adamson return clp; 1248c36fca52SAndy Adamson } 1249c36fca52SAndy Adamson spin_unlock(&nfs_client_lock); 1250c36fca52SAndy Adamson return NULL; 1251c36fca52SAndy Adamson } 1252c36fca52SAndy Adamson 1253c36fca52SAndy Adamson #else /* CONFIG_NFS_V4_1 */ 1254c36fca52SAndy Adamson 1255c36fca52SAndy Adamson struct nfs_client * 1256c36fca52SAndy Adamson nfs4_find_client_sessionid(const struct sockaddr *addr, 1257778be232SAndy Adamson struct nfs4_sessionid *sid) 1258c36fca52SAndy Adamson { 1259c36fca52SAndy Adamson return NULL; 1260c36fca52SAndy Adamson } 1261c36fca52SAndy Adamson #endif /* CONFIG_NFS_V4_1 */ 1262c36fca52SAndy Adamson 1263c36fca52SAndy Adamson /* 12649bdaa86dSBenny Halevy * Initialize the NFS4 callback service 12659bdaa86dSBenny Halevy */ 12669bdaa86dSBenny Halevy static int nfs4_init_callback(struct nfs_client *clp) 12679bdaa86dSBenny Halevy { 12689bdaa86dSBenny Halevy int error; 12699bdaa86dSBenny Halevy 12709bdaa86dSBenny Halevy if (clp->rpc_ops->version == 4) { 12710b5b7ae0SAndy Adamson if (nfs4_has_session(clp)) { 12720b5b7ae0SAndy Adamson error = xprt_setup_backchannel( 12730b5b7ae0SAndy Adamson clp->cl_rpcclient->cl_xprt, 12740b5b7ae0SAndy Adamson NFS41_BC_MIN_CALLBACKS); 12750b5b7ae0SAndy Adamson if (error < 0) 12760b5b7ae0SAndy Adamson return error; 12770b5b7ae0SAndy Adamson } 12780b5b7ae0SAndy Adamson 127997dc1359STrond Myklebust error = nfs_callback_up(clp->cl_mvops->minor_version, 128071468513SBenny Halevy clp->cl_rpcclient->cl_xprt); 12819bdaa86dSBenny Halevy if (error < 0) { 12829bdaa86dSBenny Halevy dprintk("%s: failed to start callback. Error = %d\n", 12839bdaa86dSBenny Halevy __func__, error); 12849bdaa86dSBenny Halevy return error; 12859bdaa86dSBenny Halevy } 12869bdaa86dSBenny Halevy __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state); 12879bdaa86dSBenny Halevy } 12889bdaa86dSBenny Halevy return 0; 12899bdaa86dSBenny Halevy } 12909bdaa86dSBenny Halevy 12919bdaa86dSBenny Halevy /* 1292557134a3SAndy Adamson * Initialize the minor version specific parts of an NFS4 client record 1293557134a3SAndy Adamson */ 1294557134a3SAndy Adamson static int nfs4_init_client_minor_version(struct nfs_client *clp) 1295557134a3SAndy Adamson { 1296557134a3SAndy Adamson #if defined(CONFIG_NFS_V4_1) 129797dc1359STrond Myklebust if (clp->cl_mvops->minor_version) { 1298557134a3SAndy Adamson struct nfs4_session *session = NULL; 1299557134a3SAndy Adamson /* 1300557134a3SAndy Adamson * Create the session and mark it expired. 1301557134a3SAndy Adamson * When a SEQUENCE operation encounters the expired session 1302557134a3SAndy Adamson * it will do session recovery to initialize it. 1303557134a3SAndy Adamson */ 1304557134a3SAndy Adamson session = nfs4_alloc_session(clp); 1305557134a3SAndy Adamson if (!session) 1306557134a3SAndy Adamson return -ENOMEM; 1307557134a3SAndy Adamson 1308557134a3SAndy Adamson clp->cl_session = session; 1309fe74ba3aSTrond Myklebust /* 1310fe74ba3aSTrond Myklebust * The create session reply races with the server back 1311fe74ba3aSTrond Myklebust * channel probe. Mark the client NFS_CS_SESSION_INITING 1312fe74ba3aSTrond Myklebust * so that the client back channel can find the 1313fe74ba3aSTrond Myklebust * nfs_client struct 1314fe74ba3aSTrond Myklebust */ 1315fe74ba3aSTrond Myklebust clp->cl_cons_state = NFS_CS_SESSION_INITING; 1316557134a3SAndy Adamson } 1317557134a3SAndy Adamson #endif /* CONFIG_NFS_V4_1 */ 1318557134a3SAndy Adamson 131971468513SBenny Halevy return nfs4_init_callback(clp); 1320557134a3SAndy Adamson } 1321557134a3SAndy Adamson 1322557134a3SAndy Adamson /* 132354ceac45SDavid Howells * Initialise an NFS4 client record 132454ceac45SDavid Howells */ 132545a52a02SAndy Adamson int nfs4_init_client(struct nfs_client *clp, 132633170233STrond Myklebust const struct rpc_timeout *timeparms, 13277d9ac06fSJ. Bruce Fields const char *ip_addr, 1328d740351bSChuck Lever rpc_authflavor_t authflavour, 132945a52a02SAndy Adamson int noresvport) 133054ceac45SDavid Howells { 133154ceac45SDavid Howells int error; 133254ceac45SDavid Howells 133354ceac45SDavid Howells if (clp->cl_cons_state == NFS_CS_READY) { 133454ceac45SDavid Howells /* the client is initialised already */ 133554ceac45SDavid Howells dprintk("<-- nfs4_init_client() = 0 [already %p]\n", clp); 133654ceac45SDavid Howells return 0; 133754ceac45SDavid Howells } 133854ceac45SDavid Howells 133954ceac45SDavid Howells /* Check NFS protocol revision and initialize RPC op vector */ 134054ceac45SDavid Howells clp->rpc_ops = &nfs_v4_clientops; 134154ceac45SDavid Howells 134259dca3b2STrond Myklebust error = nfs_create_rpc_client(clp, timeparms, authflavour, 134345a52a02SAndy Adamson 1, noresvport); 134454ceac45SDavid Howells if (error < 0) 134554ceac45SDavid Howells goto error; 1346f4373bf9SBen Hutchings strlcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr)); 134754ceac45SDavid Howells 134854ceac45SDavid Howells error = nfs_idmap_new(clp); 134954ceac45SDavid Howells if (error < 0) { 135054ceac45SDavid Howells dprintk("%s: failed to create idmapper. Error = %d\n", 13513110ff80SHarvey Harrison __func__, error); 135254ceac45SDavid Howells goto error; 135354ceac45SDavid Howells } 13549c5bf38dSTrond Myklebust __set_bit(NFS_CS_IDMAP, &clp->cl_res_state); 135554ceac45SDavid Howells 1356557134a3SAndy Adamson error = nfs4_init_client_minor_version(clp); 1357557134a3SAndy Adamson if (error < 0) 1358557134a3SAndy Adamson goto error; 1359557134a3SAndy Adamson 136076db6d95SAndy Adamson if (!nfs4_has_session(clp)) 136154ceac45SDavid Howells nfs_mark_client_ready(clp, NFS_CS_READY); 136254ceac45SDavid Howells return 0; 136354ceac45SDavid Howells 136454ceac45SDavid Howells error: 136554ceac45SDavid Howells nfs_mark_client_ready(clp, error); 136654ceac45SDavid Howells dprintk("<-- nfs4_init_client() = xerror %d\n", error); 136754ceac45SDavid Howells return error; 136854ceac45SDavid Howells } 136954ceac45SDavid Howells 137054ceac45SDavid Howells /* 137154ceac45SDavid Howells * Set up an NFS4 client 137254ceac45SDavid Howells */ 137354ceac45SDavid Howells static int nfs4_set_client(struct nfs_server *server, 1374dcecae0fSChuck Lever const char *hostname, 1375dcecae0fSChuck Lever const struct sockaddr *addr, 1376dcecae0fSChuck Lever const size_t addrlen, 13777d9ac06fSJ. Bruce Fields const char *ip_addr, 137854ceac45SDavid Howells rpc_authflavor_t authflavour, 137994a417f3SBenny Halevy int proto, const struct rpc_timeout *timeparms, 138094a417f3SBenny Halevy u32 minorversion) 138154ceac45SDavid Howells { 13823a498026STrond Myklebust struct nfs_client_initdata cl_init = { 13833a498026STrond Myklebust .hostname = hostname, 1384dcecae0fSChuck Lever .addr = addr, 1385dcecae0fSChuck Lever .addrlen = addrlen, 138640c55319STrond Myklebust .rpc_ops = &nfs_v4_clientops, 138759dca3b2STrond Myklebust .proto = proto, 13885aae4a9aSBenny Halevy .minorversion = minorversion, 13893a498026STrond Myklebust }; 139054ceac45SDavid Howells struct nfs_client *clp; 139154ceac45SDavid Howells int error; 139254ceac45SDavid Howells 139354ceac45SDavid Howells dprintk("--> nfs4_set_client()\n"); 139454ceac45SDavid Howells 139554ceac45SDavid Howells /* Allocate or find a client reference we can use */ 139645a52a02SAndy Adamson clp = nfs_get_client(&cl_init, timeparms, ip_addr, authflavour, 139745a52a02SAndy Adamson server->flags & NFS_MOUNT_NORESVPORT); 139854ceac45SDavid Howells if (IS_ERR(clp)) { 139954ceac45SDavid Howells error = PTR_ERR(clp); 140054ceac45SDavid Howells goto error; 140154ceac45SDavid Howells } 140254ceac45SDavid Howells 1403*d6fb79d4SAndy Adamson /* 1404*d6fb79d4SAndy Adamson * Query for the lease time on clientid setup or renewal 1405*d6fb79d4SAndy Adamson * 1406*d6fb79d4SAndy Adamson * Note that this will be set on nfs_clients that were created 1407*d6fb79d4SAndy Adamson * only for the DS role and did not set this bit, but now will 1408*d6fb79d4SAndy Adamson * serve a dual role. 1409*d6fb79d4SAndy Adamson */ 1410*d6fb79d4SAndy Adamson set_bit(NFS_CS_CHECK_LEASE_TIME, &clp->cl_res_state); 1411*d6fb79d4SAndy Adamson 141254ceac45SDavid Howells server->nfs_client = clp; 141354ceac45SDavid Howells dprintk("<-- nfs4_set_client() = 0 [new %p]\n", clp); 141454ceac45SDavid Howells return 0; 141554ceac45SDavid Howells error: 141654ceac45SDavid Howells dprintk("<-- nfs4_set_client() = xerror %d\n", error); 141754ceac45SDavid Howells return error; 141854ceac45SDavid Howells } 141954ceac45SDavid Howells 1420557134a3SAndy Adamson 1421557134a3SAndy Adamson /* 142296b09e02SAndy Adamson * Session has been established, and the client marked ready. 142396b09e02SAndy Adamson * Set the mount rsize and wsize with negotiated fore channel 142496b09e02SAndy Adamson * attributes which will be bound checked in nfs_server_set_fsinfo. 142596b09e02SAndy Adamson */ 142696b09e02SAndy Adamson static void nfs4_session_set_rwsize(struct nfs_server *server) 142796b09e02SAndy Adamson { 142896b09e02SAndy Adamson #ifdef CONFIG_NFS_V4_1 14292449ea2eSAlexandros Batsakis struct nfs4_session *sess; 14302449ea2eSAlexandros Batsakis u32 server_resp_sz; 14312449ea2eSAlexandros Batsakis u32 server_rqst_sz; 14322449ea2eSAlexandros Batsakis 143396b09e02SAndy Adamson if (!nfs4_has_session(server->nfs_client)) 143496b09e02SAndy Adamson return; 14352449ea2eSAlexandros Batsakis sess = server->nfs_client->cl_session; 14362449ea2eSAlexandros Batsakis server_resp_sz = sess->fc_attrs.max_resp_sz - nfs41_maxread_overhead; 14372449ea2eSAlexandros Batsakis server_rqst_sz = sess->fc_attrs.max_rqst_sz - nfs41_maxwrite_overhead; 14382449ea2eSAlexandros Batsakis 14392449ea2eSAlexandros Batsakis if (server->rsize > server_resp_sz) 14402449ea2eSAlexandros Batsakis server->rsize = server_resp_sz; 14412449ea2eSAlexandros Batsakis if (server->wsize > server_rqst_sz) 14422449ea2eSAlexandros Batsakis server->wsize = server_rqst_sz; 144396b09e02SAndy Adamson #endif /* CONFIG_NFS_V4_1 */ 144496b09e02SAndy Adamson } 144596b09e02SAndy Adamson 144644950b67STrond Myklebust static int nfs4_server_common_setup(struct nfs_server *server, 144744950b67STrond Myklebust struct nfs_fh *mntfh) 144844950b67STrond Myklebust { 144944950b67STrond Myklebust struct nfs_fattr *fattr; 145044950b67STrond Myklebust int error; 145144950b67STrond Myklebust 145244950b67STrond Myklebust BUG_ON(!server->nfs_client); 145344950b67STrond Myklebust BUG_ON(!server->nfs_client->rpc_ops); 145444950b67STrond Myklebust BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 145544950b67STrond Myklebust 145644950b67STrond Myklebust fattr = nfs_alloc_fattr(); 145744950b67STrond Myklebust if (fattr == NULL) 145844950b67STrond Myklebust return -ENOMEM; 145944950b67STrond Myklebust 146044950b67STrond Myklebust /* We must ensure the session is initialised first */ 146144950b67STrond Myklebust error = nfs4_init_session(server); 146244950b67STrond Myklebust if (error < 0) 146344950b67STrond Myklebust goto out; 146444950b67STrond Myklebust 146544950b67STrond Myklebust /* Probe the root fh to retrieve its FSID and filehandle */ 146644950b67STrond Myklebust error = nfs4_get_rootfh(server, mntfh); 146744950b67STrond Myklebust if (error < 0) 146844950b67STrond Myklebust goto out; 146944950b67STrond Myklebust 147044950b67STrond Myklebust dprintk("Server FSID: %llx:%llx\n", 147144950b67STrond Myklebust (unsigned long long) server->fsid.major, 147244950b67STrond Myklebust (unsigned long long) server->fsid.minor); 147344950b67STrond Myklebust dprintk("Mount FH: %d\n", mntfh->size); 147444950b67STrond Myklebust 147544950b67STrond Myklebust nfs4_session_set_rwsize(server); 147644950b67STrond Myklebust 147744950b67STrond Myklebust error = nfs_probe_fsinfo(server, mntfh, fattr); 147844950b67STrond Myklebust if (error < 0) 147944950b67STrond Myklebust goto out; 148044950b67STrond Myklebust 148144950b67STrond Myklebust if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 148244950b67STrond Myklebust server->namelen = NFS4_MAXNAMLEN; 148344950b67STrond Myklebust 1484fca5238eSChuck Lever nfs_server_insert_lists(server); 148544950b67STrond Myklebust server->mount_time = jiffies; 148644950b67STrond Myklebust out: 148744950b67STrond Myklebust nfs_free_fattr(fattr); 148844950b67STrond Myklebust return error; 148944950b67STrond Myklebust } 149044950b67STrond Myklebust 149196b09e02SAndy Adamson /* 149254ceac45SDavid Howells * Create a version 4 volume record 149354ceac45SDavid Howells */ 149454ceac45SDavid Howells static int nfs4_init_server(struct nfs_server *server, 149591ea40b9S\"Talpey, Thomas\ const struct nfs_parsed_mount_data *data) 149654ceac45SDavid Howells { 149733170233STrond Myklebust struct rpc_timeout timeparms; 149854ceac45SDavid Howells int error; 149954ceac45SDavid Howells 150054ceac45SDavid Howells dprintk("--> nfs4_init_server()\n"); 150154ceac45SDavid Howells 150233170233STrond Myklebust nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, 150333170233STrond Myklebust data->timeo, data->retrans); 150433170233STrond Myklebust 1505542fcc33SChuck Lever /* Initialise the client representation from the mount data */ 1506542fcc33SChuck Lever server->flags = data->flags; 150782f2e547SBryan Schumaker server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR|NFS_CAP_POSIX_LOCK; 150882f2e547SBryan Schumaker if (!(data->flags & NFS_MOUNT_NORDIRPLUS)) 150982f2e547SBryan Schumaker server->caps |= NFS_CAP_READDIRPLUS; 1510b797cac7SDavid Howells server->options = data->options; 1511542fcc33SChuck Lever 151233170233STrond Myklebust /* Get a client record */ 151333170233STrond Myklebust error = nfs4_set_client(server, 151433170233STrond Myklebust data->nfs_server.hostname, 151533170233STrond Myklebust (const struct sockaddr *)&data->nfs_server.address, 151633170233STrond Myklebust data->nfs_server.addrlen, 151733170233STrond Myklebust data->client_address, 151833170233STrond Myklebust data->auth_flavors[0], 151933170233STrond Myklebust data->nfs_server.protocol, 152094a417f3SBenny Halevy &timeparms, 152194a417f3SBenny Halevy data->minorversion); 152233170233STrond Myklebust if (error < 0) 152333170233STrond Myklebust goto error; 152433170233STrond Myklebust 152554ceac45SDavid Howells if (data->rsize) 152654ceac45SDavid Howells server->rsize = nfs_block_size(data->rsize, NULL); 152754ceac45SDavid Howells if (data->wsize) 152854ceac45SDavid Howells server->wsize = nfs_block_size(data->wsize, NULL); 152954ceac45SDavid Howells 153054ceac45SDavid Howells server->acregmin = data->acregmin * HZ; 153154ceac45SDavid Howells server->acregmax = data->acregmax * HZ; 153254ceac45SDavid Howells server->acdirmin = data->acdirmin * HZ; 153354ceac45SDavid Howells server->acdirmax = data->acdirmax * HZ; 153454ceac45SDavid Howells 1535f22d6d79SChuck Lever server->port = data->nfs_server.port; 1536f22d6d79SChuck Lever 153733170233STrond Myklebust error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]); 153854ceac45SDavid Howells 153933170233STrond Myklebust error: 154054ceac45SDavid Howells /* Done */ 154154ceac45SDavid Howells dprintk("<-- nfs4_init_server() = %d\n", error); 154254ceac45SDavid Howells return error; 154354ceac45SDavid Howells } 154454ceac45SDavid Howells 154554ceac45SDavid Howells /* 154654ceac45SDavid Howells * Create a version 4 volume record 154754ceac45SDavid Howells * - keyed on server and FSID 154854ceac45SDavid Howells */ 154991ea40b9S\"Talpey, Thomas\ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, 155054ceac45SDavid Howells struct nfs_fh *mntfh) 155154ceac45SDavid Howells { 155254ceac45SDavid Howells struct nfs_server *server; 155354ceac45SDavid Howells int error; 155454ceac45SDavid Howells 155554ceac45SDavid Howells dprintk("--> nfs4_create_server()\n"); 155654ceac45SDavid Howells 155754ceac45SDavid Howells server = nfs_alloc_server(); 155854ceac45SDavid Howells if (!server) 155954ceac45SDavid Howells return ERR_PTR(-ENOMEM); 156054ceac45SDavid Howells 156154ceac45SDavid Howells /* set up the general RPC client */ 156291ea40b9S\"Talpey, Thomas\ error = nfs4_init_server(server, data); 156354ceac45SDavid Howells if (error < 0) 156454ceac45SDavid Howells goto error; 156554ceac45SDavid Howells 156644950b67STrond Myklebust error = nfs4_server_common_setup(server, mntfh); 1567fccba804STrond Myklebust if (error < 0) 1568fccba804STrond Myklebust goto error; 1569557134a3SAndy Adamson 157054ceac45SDavid Howells dprintk("<-- nfs4_create_server() = %p\n", server); 157154ceac45SDavid Howells return server; 157254ceac45SDavid Howells 157354ceac45SDavid Howells error: 157454ceac45SDavid Howells nfs_free_server(server); 157554ceac45SDavid Howells dprintk("<-- nfs4_create_server() = error %d\n", error); 157654ceac45SDavid Howells return ERR_PTR(error); 157754ceac45SDavid Howells } 157854ceac45SDavid Howells 157954ceac45SDavid Howells /* 158054ceac45SDavid Howells * Create an NFS4 referral server record 158154ceac45SDavid Howells */ 158254ceac45SDavid Howells struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, 1583f2d0d85eSTrond Myklebust struct nfs_fh *mntfh) 158454ceac45SDavid Howells { 158554ceac45SDavid Howells struct nfs_client *parent_client; 158654ceac45SDavid Howells struct nfs_server *server, *parent_server; 158754ceac45SDavid Howells int error; 158854ceac45SDavid Howells 158954ceac45SDavid Howells dprintk("--> nfs4_create_referral_server()\n"); 159054ceac45SDavid Howells 159154ceac45SDavid Howells server = nfs_alloc_server(); 159254ceac45SDavid Howells if (!server) 159354ceac45SDavid Howells return ERR_PTR(-ENOMEM); 159454ceac45SDavid Howells 159554ceac45SDavid Howells parent_server = NFS_SB(data->sb); 159654ceac45SDavid Howells parent_client = parent_server->nfs_client; 159754ceac45SDavid Howells 1598542fcc33SChuck Lever /* Initialise the client representation from the parent server */ 1599542fcc33SChuck Lever nfs_server_copy_userdata(server, parent_server); 160062ab460cSTrond Myklebust server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR; 1601542fcc33SChuck Lever 160254ceac45SDavid Howells /* Get a client representation. 160354ceac45SDavid Howells * Note: NFSv4 always uses TCP, */ 1604dcecae0fSChuck Lever error = nfs4_set_client(server, data->hostname, 16056677d095SChuck Lever data->addr, 16066677d095SChuck Lever data->addrlen, 16077d9ac06fSJ. Bruce Fields parent_client->cl_ipaddr, 160854ceac45SDavid Howells data->authflavor, 160954ceac45SDavid Howells parent_server->client->cl_xprt->prot, 161094a417f3SBenny Halevy parent_server->client->cl_timeout, 161197dc1359STrond Myklebust parent_client->cl_mvops->minor_version); 1612297de4f6Sandros@citi.umich.edu if (error < 0) 1613297de4f6Sandros@citi.umich.edu goto error; 161454ceac45SDavid Howells 161533170233STrond Myklebust error = nfs_init_server_rpcclient(server, parent_server->client->cl_timeout, data->authflavor); 161654ceac45SDavid Howells if (error < 0) 161754ceac45SDavid Howells goto error; 161854ceac45SDavid Howells 161944950b67STrond Myklebust error = nfs4_server_common_setup(server, mntfh); 1620f2d0d85eSTrond Myklebust if (error < 0) 1621f2d0d85eSTrond Myklebust goto error; 1622f2d0d85eSTrond Myklebust 162354ceac45SDavid Howells dprintk("<-- nfs_create_referral_server() = %p\n", server); 162454ceac45SDavid Howells return server; 162554ceac45SDavid Howells 162654ceac45SDavid Howells error: 162754ceac45SDavid Howells nfs_free_server(server); 162854ceac45SDavid Howells dprintk("<-- nfs4_create_referral_server() = error %d\n", error); 162954ceac45SDavid Howells return ERR_PTR(error); 163054ceac45SDavid Howells } 163154ceac45SDavid Howells 163254ceac45SDavid Howells #endif /* CONFIG_NFS_V4 */ 163354ceac45SDavid Howells 163454ceac45SDavid Howells /* 163554ceac45SDavid Howells * Clone an NFS2, NFS3 or NFS4 server record 163654ceac45SDavid Howells */ 163754ceac45SDavid Howells struct nfs_server *nfs_clone_server(struct nfs_server *source, 163854ceac45SDavid Howells struct nfs_fh *fh, 163954ceac45SDavid Howells struct nfs_fattr *fattr) 164054ceac45SDavid Howells { 164154ceac45SDavid Howells struct nfs_server *server; 1642fbca779aSTrond Myklebust struct nfs_fattr *fattr_fsinfo; 164354ceac45SDavid Howells int error; 164454ceac45SDavid Howells 164554ceac45SDavid Howells dprintk("--> nfs_clone_server(,%llx:%llx,)\n", 16466daabf1bSDavid Howells (unsigned long long) fattr->fsid.major, 16476daabf1bSDavid Howells (unsigned long long) fattr->fsid.minor); 164854ceac45SDavid Howells 164954ceac45SDavid Howells server = nfs_alloc_server(); 165054ceac45SDavid Howells if (!server) 165154ceac45SDavid Howells return ERR_PTR(-ENOMEM); 165254ceac45SDavid Howells 1653fbca779aSTrond Myklebust error = -ENOMEM; 1654fbca779aSTrond Myklebust fattr_fsinfo = nfs_alloc_fattr(); 1655fbca779aSTrond Myklebust if (fattr_fsinfo == NULL) 1656fbca779aSTrond Myklebust goto out_free_server; 1657fbca779aSTrond Myklebust 165854ceac45SDavid Howells /* Copy data from the source */ 165954ceac45SDavid Howells server->nfs_client = source->nfs_client; 166054ceac45SDavid Howells atomic_inc(&server->nfs_client->cl_count); 166154ceac45SDavid Howells nfs_server_copy_userdata(server, source); 166254ceac45SDavid Howells 166354ceac45SDavid Howells server->fsid = fattr->fsid; 166454ceac45SDavid Howells 166533170233STrond Myklebust error = nfs_init_server_rpcclient(server, 166633170233STrond Myklebust source->client->cl_timeout, 166733170233STrond Myklebust source->client->cl_auth->au_flavor); 166854ceac45SDavid Howells if (error < 0) 166954ceac45SDavid Howells goto out_free_server; 167054ceac45SDavid Howells if (!IS_ERR(source->client_acl)) 167154ceac45SDavid Howells nfs_init_server_aclclient(server); 167254ceac45SDavid Howells 167354ceac45SDavid Howells /* probe the filesystem info for this server filesystem */ 1674fbca779aSTrond Myklebust error = nfs_probe_fsinfo(server, fh, fattr_fsinfo); 167554ceac45SDavid Howells if (error < 0) 167654ceac45SDavid Howells goto out_free_server; 167754ceac45SDavid Howells 167854af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 167954af3bb5STrond Myklebust server->namelen = NFS4_MAXNAMLEN; 168054af3bb5STrond Myklebust 168154ceac45SDavid Howells dprintk("Cloned FSID: %llx:%llx\n", 16826daabf1bSDavid Howells (unsigned long long) server->fsid.major, 16836daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 168454ceac45SDavid Howells 168554ceac45SDavid Howells error = nfs_start_lockd(server); 168654ceac45SDavid Howells if (error < 0) 168754ceac45SDavid Howells goto out_free_server; 168854ceac45SDavid Howells 1689fca5238eSChuck Lever nfs_server_insert_lists(server); 169054ceac45SDavid Howells server->mount_time = jiffies; 169154ceac45SDavid Howells 1692fbca779aSTrond Myklebust nfs_free_fattr(fattr_fsinfo); 169354ceac45SDavid Howells dprintk("<-- nfs_clone_server() = %p\n", server); 169454ceac45SDavid Howells return server; 169554ceac45SDavid Howells 169654ceac45SDavid Howells out_free_server: 1697fbca779aSTrond Myklebust nfs_free_fattr(fattr_fsinfo); 169854ceac45SDavid Howells nfs_free_server(server); 169954ceac45SDavid Howells dprintk("<-- nfs_clone_server() = error %d\n", error); 170054ceac45SDavid Howells return ERR_PTR(error); 170154ceac45SDavid Howells } 17026aaca566SDavid Howells 17036aaca566SDavid Howells #ifdef CONFIG_PROC_FS 17046aaca566SDavid Howells static struct proc_dir_entry *proc_fs_nfs; 17056aaca566SDavid Howells 17066aaca566SDavid Howells static int nfs_server_list_open(struct inode *inode, struct file *file); 17076aaca566SDavid Howells static void *nfs_server_list_start(struct seq_file *p, loff_t *pos); 17086aaca566SDavid Howells static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos); 17096aaca566SDavid Howells static void nfs_server_list_stop(struct seq_file *p, void *v); 17106aaca566SDavid Howells static int nfs_server_list_show(struct seq_file *m, void *v); 17116aaca566SDavid Howells 171288e9d34cSJames Morris static const struct seq_operations nfs_server_list_ops = { 17136aaca566SDavid Howells .start = nfs_server_list_start, 17146aaca566SDavid Howells .next = nfs_server_list_next, 17156aaca566SDavid Howells .stop = nfs_server_list_stop, 17166aaca566SDavid Howells .show = nfs_server_list_show, 17176aaca566SDavid Howells }; 17186aaca566SDavid Howells 171900977a59SArjan van de Ven static const struct file_operations nfs_server_list_fops = { 17206aaca566SDavid Howells .open = nfs_server_list_open, 17216aaca566SDavid Howells .read = seq_read, 17226aaca566SDavid Howells .llseek = seq_lseek, 17236aaca566SDavid Howells .release = seq_release, 172434b37235SDenis V. Lunev .owner = THIS_MODULE, 17256aaca566SDavid Howells }; 17266aaca566SDavid Howells 17276aaca566SDavid Howells static int nfs_volume_list_open(struct inode *inode, struct file *file); 17286aaca566SDavid Howells static void *nfs_volume_list_start(struct seq_file *p, loff_t *pos); 17296aaca566SDavid Howells static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos); 17306aaca566SDavid Howells static void nfs_volume_list_stop(struct seq_file *p, void *v); 17316aaca566SDavid Howells static int nfs_volume_list_show(struct seq_file *m, void *v); 17326aaca566SDavid Howells 173388e9d34cSJames Morris static const struct seq_operations nfs_volume_list_ops = { 17346aaca566SDavid Howells .start = nfs_volume_list_start, 17356aaca566SDavid Howells .next = nfs_volume_list_next, 17366aaca566SDavid Howells .stop = nfs_volume_list_stop, 17376aaca566SDavid Howells .show = nfs_volume_list_show, 17386aaca566SDavid Howells }; 17396aaca566SDavid Howells 174000977a59SArjan van de Ven static const struct file_operations nfs_volume_list_fops = { 17416aaca566SDavid Howells .open = nfs_volume_list_open, 17426aaca566SDavid Howells .read = seq_read, 17436aaca566SDavid Howells .llseek = seq_lseek, 17446aaca566SDavid Howells .release = seq_release, 174534b37235SDenis V. Lunev .owner = THIS_MODULE, 17466aaca566SDavid Howells }; 17476aaca566SDavid Howells 17486aaca566SDavid Howells /* 17496aaca566SDavid Howells * open "/proc/fs/nfsfs/servers" which provides a summary of servers with which 17506aaca566SDavid Howells * we're dealing 17516aaca566SDavid Howells */ 17526aaca566SDavid Howells static int nfs_server_list_open(struct inode *inode, struct file *file) 17536aaca566SDavid Howells { 17546aaca566SDavid Howells struct seq_file *m; 17556aaca566SDavid Howells int ret; 17566aaca566SDavid Howells 17576aaca566SDavid Howells ret = seq_open(file, &nfs_server_list_ops); 17586aaca566SDavid Howells if (ret < 0) 17596aaca566SDavid Howells return ret; 17606aaca566SDavid Howells 17616aaca566SDavid Howells m = file->private_data; 17626aaca566SDavid Howells m->private = PDE(inode)->data; 17636aaca566SDavid Howells 17646aaca566SDavid Howells return 0; 17656aaca566SDavid Howells } 17666aaca566SDavid Howells 17676aaca566SDavid Howells /* 17686aaca566SDavid Howells * set up the iterator to start reading from the server list and return the first item 17696aaca566SDavid Howells */ 17706aaca566SDavid Howells static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos) 17716aaca566SDavid Howells { 17726aaca566SDavid Howells /* lock the list against modification */ 17736aaca566SDavid Howells spin_lock(&nfs_client_lock); 1774259902eaSPavel Emelianov return seq_list_start_head(&nfs_client_list, *_pos); 17756aaca566SDavid Howells } 17766aaca566SDavid Howells 17776aaca566SDavid Howells /* 17786aaca566SDavid Howells * move to next server 17796aaca566SDavid Howells */ 17806aaca566SDavid Howells static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos) 17816aaca566SDavid Howells { 1782259902eaSPavel Emelianov return seq_list_next(v, &nfs_client_list, pos); 17836aaca566SDavid Howells } 17846aaca566SDavid Howells 17856aaca566SDavid Howells /* 17866aaca566SDavid Howells * clean up after reading from the transports list 17876aaca566SDavid Howells */ 17886aaca566SDavid Howells static void nfs_server_list_stop(struct seq_file *p, void *v) 17896aaca566SDavid Howells { 17906aaca566SDavid Howells spin_unlock(&nfs_client_lock); 17916aaca566SDavid Howells } 17926aaca566SDavid Howells 17936aaca566SDavid Howells /* 17946aaca566SDavid Howells * display a header line followed by a load of call lines 17956aaca566SDavid Howells */ 17966aaca566SDavid Howells static int nfs_server_list_show(struct seq_file *m, void *v) 17976aaca566SDavid Howells { 17986aaca566SDavid Howells struct nfs_client *clp; 17996aaca566SDavid Howells 18006aaca566SDavid Howells /* display header on line 1 */ 1801259902eaSPavel Emelianov if (v == &nfs_client_list) { 18026aaca566SDavid Howells seq_puts(m, "NV SERVER PORT USE HOSTNAME\n"); 18036aaca566SDavid Howells return 0; 18046aaca566SDavid Howells } 18056aaca566SDavid Howells 18066aaca566SDavid Howells /* display one transport per line on subsequent lines */ 18076aaca566SDavid Howells clp = list_entry(v, struct nfs_client, cl_share_link); 18086aaca566SDavid Howells 18095d8515caSChuck Lever seq_printf(m, "v%u %s %s %3d %s\n", 181040c55319STrond Myklebust clp->rpc_ops->version, 18115d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), 18125d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), 18136aaca566SDavid Howells atomic_read(&clp->cl_count), 18146aaca566SDavid Howells clp->cl_hostname); 18156aaca566SDavid Howells 18166aaca566SDavid Howells return 0; 18176aaca566SDavid Howells } 18186aaca566SDavid Howells 18196aaca566SDavid Howells /* 18206aaca566SDavid Howells * open "/proc/fs/nfsfs/volumes" which provides a summary of extant volumes 18216aaca566SDavid Howells */ 18226aaca566SDavid Howells static int nfs_volume_list_open(struct inode *inode, struct file *file) 18236aaca566SDavid Howells { 18246aaca566SDavid Howells struct seq_file *m; 18256aaca566SDavid Howells int ret; 18266aaca566SDavid Howells 18276aaca566SDavid Howells ret = seq_open(file, &nfs_volume_list_ops); 18286aaca566SDavid Howells if (ret < 0) 18296aaca566SDavid Howells return ret; 18306aaca566SDavid Howells 18316aaca566SDavid Howells m = file->private_data; 18326aaca566SDavid Howells m->private = PDE(inode)->data; 18336aaca566SDavid Howells 18346aaca566SDavid Howells return 0; 18356aaca566SDavid Howells } 18366aaca566SDavid Howells 18376aaca566SDavid Howells /* 18386aaca566SDavid Howells * set up the iterator to start reading from the volume list and return the first item 18396aaca566SDavid Howells */ 18406aaca566SDavid Howells static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos) 18416aaca566SDavid Howells { 18426aaca566SDavid Howells /* lock the list against modification */ 18436aaca566SDavid Howells spin_lock(&nfs_client_lock); 1844259902eaSPavel Emelianov return seq_list_start_head(&nfs_volume_list, *_pos); 18456aaca566SDavid Howells } 18466aaca566SDavid Howells 18476aaca566SDavid Howells /* 18486aaca566SDavid Howells * move to next volume 18496aaca566SDavid Howells */ 18506aaca566SDavid Howells static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos) 18516aaca566SDavid Howells { 1852259902eaSPavel Emelianov return seq_list_next(v, &nfs_volume_list, pos); 18536aaca566SDavid Howells } 18546aaca566SDavid Howells 18556aaca566SDavid Howells /* 18566aaca566SDavid Howells * clean up after reading from the transports list 18576aaca566SDavid Howells */ 18586aaca566SDavid Howells static void nfs_volume_list_stop(struct seq_file *p, void *v) 18596aaca566SDavid Howells { 18606aaca566SDavid Howells spin_unlock(&nfs_client_lock); 18616aaca566SDavid Howells } 18626aaca566SDavid Howells 18636aaca566SDavid Howells /* 18646aaca566SDavid Howells * display a header line followed by a load of call lines 18656aaca566SDavid Howells */ 18666aaca566SDavid Howells static int nfs_volume_list_show(struct seq_file *m, void *v) 18676aaca566SDavid Howells { 18686aaca566SDavid Howells struct nfs_server *server; 18696aaca566SDavid Howells struct nfs_client *clp; 18706aaca566SDavid Howells char dev[8], fsid[17]; 18716aaca566SDavid Howells 18726aaca566SDavid Howells /* display header on line 1 */ 1873259902eaSPavel Emelianov if (v == &nfs_volume_list) { 18745d1acff1SDavid Howells seq_puts(m, "NV SERVER PORT DEV FSID FSC\n"); 18756aaca566SDavid Howells return 0; 18766aaca566SDavid Howells } 18776aaca566SDavid Howells /* display one transport per line on subsequent lines */ 18786aaca566SDavid Howells server = list_entry(v, struct nfs_server, master_link); 18796aaca566SDavid Howells clp = server->nfs_client; 18806aaca566SDavid Howells 18816aaca566SDavid Howells snprintf(dev, 8, "%u:%u", 18826aaca566SDavid Howells MAJOR(server->s_dev), MINOR(server->s_dev)); 18836aaca566SDavid Howells 18846aaca566SDavid Howells snprintf(fsid, 17, "%llx:%llx", 18856daabf1bSDavid Howells (unsigned long long) server->fsid.major, 18866daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 18876aaca566SDavid Howells 18885d1acff1SDavid Howells seq_printf(m, "v%u %s %s %-7s %-17s %s\n", 188940c55319STrond Myklebust clp->rpc_ops->version, 18905d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), 18915d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), 18926aaca566SDavid Howells dev, 18935d1acff1SDavid Howells fsid, 18945d1acff1SDavid Howells nfs_server_fscache_state(server)); 18956aaca566SDavid Howells 18966aaca566SDavid Howells return 0; 18976aaca566SDavid Howells } 18986aaca566SDavid Howells 18996aaca566SDavid Howells /* 19006aaca566SDavid Howells * initialise the /proc/fs/nfsfs/ directory 19016aaca566SDavid Howells */ 19026aaca566SDavid Howells int __init nfs_fs_proc_init(void) 19036aaca566SDavid Howells { 19046aaca566SDavid Howells struct proc_dir_entry *p; 19056aaca566SDavid Howells 190636a5aeb8SAlexey Dobriyan proc_fs_nfs = proc_mkdir("fs/nfsfs", NULL); 19076aaca566SDavid Howells if (!proc_fs_nfs) 19086aaca566SDavid Howells goto error_0; 19096aaca566SDavid Howells 19106aaca566SDavid Howells /* a file of servers with which we're dealing */ 191134b37235SDenis V. Lunev p = proc_create("servers", S_IFREG|S_IRUGO, 191234b37235SDenis V. Lunev proc_fs_nfs, &nfs_server_list_fops); 19136aaca566SDavid Howells if (!p) 19146aaca566SDavid Howells goto error_1; 19156aaca566SDavid Howells 19166aaca566SDavid Howells /* a file of volumes that we have mounted */ 191734b37235SDenis V. Lunev p = proc_create("volumes", S_IFREG|S_IRUGO, 191834b37235SDenis V. Lunev proc_fs_nfs, &nfs_volume_list_fops); 19196aaca566SDavid Howells if (!p) 19206aaca566SDavid Howells goto error_2; 19216aaca566SDavid Howells return 0; 19226aaca566SDavid Howells 19236aaca566SDavid Howells error_2: 19246aaca566SDavid Howells remove_proc_entry("servers", proc_fs_nfs); 19256aaca566SDavid Howells error_1: 192636a5aeb8SAlexey Dobriyan remove_proc_entry("fs/nfsfs", NULL); 19276aaca566SDavid Howells error_0: 19286aaca566SDavid Howells return -ENOMEM; 19296aaca566SDavid Howells } 19306aaca566SDavid Howells 19316aaca566SDavid Howells /* 19326aaca566SDavid Howells * clean up the /proc/fs/nfsfs/ directory 19336aaca566SDavid Howells */ 19346aaca566SDavid Howells void nfs_fs_proc_exit(void) 19356aaca566SDavid Howells { 19366aaca566SDavid Howells remove_proc_entry("volumes", proc_fs_nfs); 19376aaca566SDavid Howells remove_proc_entry("servers", proc_fs_nfs); 193836a5aeb8SAlexey Dobriyan remove_proc_entry("fs/nfsfs", NULL); 19396aaca566SDavid Howells } 19406aaca566SDavid Howells 19416aaca566SDavid Howells #endif /* CONFIG_PROC_FS */ 1942