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" 5124c8dbbbSDavid Howells 5224c8dbbbSDavid Howells #define NFSDBG_FACILITY NFSDBG_CLIENT 5324c8dbbbSDavid Howells 5424c8dbbbSDavid Howells static DEFINE_SPINLOCK(nfs_client_lock); 5524c8dbbbSDavid Howells static LIST_HEAD(nfs_client_list); 5654ceac45SDavid Howells static LIST_HEAD(nfs_volume_list); 5724c8dbbbSDavid Howells static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq); 5824c8dbbbSDavid Howells 5924c8dbbbSDavid Howells /* 605006a76cSDavid Howells * RPC cruft for NFS 615006a76cSDavid Howells */ 625006a76cSDavid Howells static struct rpc_version *nfs_version[5] = { 635006a76cSDavid Howells [2] = &nfs_version2, 645006a76cSDavid Howells #ifdef CONFIG_NFS_V3 655006a76cSDavid Howells [3] = &nfs_version3, 665006a76cSDavid Howells #endif 675006a76cSDavid Howells #ifdef CONFIG_NFS_V4 685006a76cSDavid Howells [4] = &nfs_version4, 695006a76cSDavid Howells #endif 705006a76cSDavid Howells }; 715006a76cSDavid Howells 725006a76cSDavid Howells struct rpc_program nfs_program = { 735006a76cSDavid Howells .name = "nfs", 745006a76cSDavid Howells .number = NFS_PROGRAM, 755006a76cSDavid Howells .nrvers = ARRAY_SIZE(nfs_version), 765006a76cSDavid Howells .version = nfs_version, 775006a76cSDavid Howells .stats = &nfs_rpcstat, 785006a76cSDavid Howells .pipe_dir_name = "/nfs", 795006a76cSDavid Howells }; 805006a76cSDavid Howells 815006a76cSDavid Howells struct rpc_stat nfs_rpcstat = { 825006a76cSDavid Howells .program = &nfs_program 835006a76cSDavid Howells }; 845006a76cSDavid Howells 855006a76cSDavid Howells 865006a76cSDavid Howells #ifdef CONFIG_NFS_V3_ACL 875006a76cSDavid Howells static struct rpc_stat nfsacl_rpcstat = { &nfsacl_program }; 885006a76cSDavid Howells static struct rpc_version * nfsacl_version[] = { 895006a76cSDavid Howells [3] = &nfsacl_version3, 905006a76cSDavid Howells }; 915006a76cSDavid Howells 925006a76cSDavid Howells struct rpc_program nfsacl_program = { 935006a76cSDavid Howells .name = "nfsacl", 945006a76cSDavid Howells .number = NFS_ACL_PROGRAM, 955006a76cSDavid Howells .nrvers = ARRAY_SIZE(nfsacl_version), 965006a76cSDavid Howells .version = nfsacl_version, 975006a76cSDavid Howells .stats = &nfsacl_rpcstat, 985006a76cSDavid Howells }; 995006a76cSDavid Howells #endif /* CONFIG_NFS_V3_ACL */ 1005006a76cSDavid Howells 1013a498026STrond Myklebust struct nfs_client_initdata { 1023a498026STrond Myklebust const char *hostname; 103d7422c47SChuck Lever const struct sockaddr *addr; 1046e4cffd7SChuck Lever size_t addrlen; 10540c55319STrond Myklebust const struct nfs_rpc_ops *rpc_ops; 10659dca3b2STrond Myklebust int proto; 1075aae4a9aSBenny Halevy u32 minorversion; 1083a498026STrond Myklebust }; 1093a498026STrond Myklebust 1105006a76cSDavid Howells /* 11124c8dbbbSDavid Howells * Allocate a shared client record 11224c8dbbbSDavid Howells * 11324c8dbbbSDavid Howells * Since these are allocated/deallocated very rarely, we don't 11424c8dbbbSDavid Howells * bother putting them in a slab cache... 11524c8dbbbSDavid Howells */ 1163a498026STrond Myklebust static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) 11724c8dbbbSDavid Howells { 11824c8dbbbSDavid Howells struct nfs_client *clp; 1197c67db3aSTrond Myklebust struct rpc_cred *cred; 120a21bdd9bSChuck Lever int err = -ENOMEM; 12124c8dbbbSDavid Howells 12224c8dbbbSDavid Howells if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) 12324c8dbbbSDavid Howells goto error_0; 12424c8dbbbSDavid Howells 12540c55319STrond Myklebust clp->rpc_ops = cl_init->rpc_ops; 12640c55319STrond Myklebust 12724c8dbbbSDavid Howells atomic_set(&clp->cl_count, 1); 12824c8dbbbSDavid Howells clp->cl_cons_state = NFS_CS_INITING; 12924c8dbbbSDavid Howells 1306e4cffd7SChuck Lever memcpy(&clp->cl_addr, cl_init->addr, cl_init->addrlen); 1316e4cffd7SChuck Lever clp->cl_addrlen = cl_init->addrlen; 13224c8dbbbSDavid Howells 1333a498026STrond Myklebust if (cl_init->hostname) { 134a21bdd9bSChuck Lever err = -ENOMEM; 1353a498026STrond Myklebust clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL); 13624c8dbbbSDavid Howells if (!clp->cl_hostname) 13771468513SBenny Halevy goto error_cleanup; 13824c8dbbbSDavid Howells } 13924c8dbbbSDavid Howells 14024c8dbbbSDavid Howells INIT_LIST_HEAD(&clp->cl_superblocks); 14124c8dbbbSDavid Howells clp->cl_rpcclient = ERR_PTR(-EINVAL); 14224c8dbbbSDavid Howells 14359dca3b2STrond Myklebust clp->cl_proto = cl_init->proto; 14459dca3b2STrond Myklebust 14524c8dbbbSDavid Howells #ifdef CONFIG_NFS_V4 14624c8dbbbSDavid Howells INIT_LIST_HEAD(&clp->cl_delegations); 14724c8dbbbSDavid Howells spin_lock_init(&clp->cl_lock); 14865f27f38SDavid Howells INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state); 14924c8dbbbSDavid Howells rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); 15024c8dbbbSDavid Howells clp->cl_boot_time = CURRENT_TIME; 15124c8dbbbSDavid Howells clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED; 1525aae4a9aSBenny Halevy clp->cl_minorversion = cl_init->minorversion; 15324c8dbbbSDavid Howells #endif 1547c67db3aSTrond Myklebust cred = rpc_lookup_machine_cred(); 1557c67db3aSTrond Myklebust if (!IS_ERR(cred)) 1567c67db3aSTrond Myklebust clp->cl_machine_cred = cred; 15724c8dbbbSDavid Howells 15814727281SDavid Howells nfs_fscache_get_client_cookie(clp); 15914727281SDavid Howells 16024c8dbbbSDavid Howells return clp; 16124c8dbbbSDavid Howells 16271468513SBenny Halevy error_cleanup: 16324c8dbbbSDavid Howells kfree(clp); 16424c8dbbbSDavid Howells error_0: 165a21bdd9bSChuck Lever return ERR_PTR(err); 16624c8dbbbSDavid Howells } 16724c8dbbbSDavid Howells 16824c8dbbbSDavid Howells #ifdef CONFIG_NFS_V4 1699bdaa86dSBenny Halevy /* 170557134a3SAndy Adamson * Clears/puts all minor version specific parts from an nfs_client struct 171557134a3SAndy Adamson * reverting it to minorversion 0. 172557134a3SAndy Adamson */ 173557134a3SAndy Adamson static void nfs4_clear_client_minor_version(struct nfs_client *clp) 174557134a3SAndy Adamson { 175557134a3SAndy Adamson #ifdef CONFIG_NFS_V4_1 176557134a3SAndy Adamson if (nfs4_has_session(clp)) { 177557134a3SAndy Adamson nfs4_destroy_session(clp->cl_session); 178557134a3SAndy Adamson clp->cl_session = NULL; 179557134a3SAndy Adamson } 180cccef3b9SAndy Adamson 181cccef3b9SAndy Adamson clp->cl_call_sync = _nfs4_call_sync; 182557134a3SAndy Adamson #endif /* CONFIG_NFS_V4_1 */ 183557134a3SAndy Adamson } 184557134a3SAndy Adamson 185557134a3SAndy Adamson /* 186888ef2e3SAlexandros Batsakis * Destroy the NFS4 callback service 187888ef2e3SAlexandros Batsakis */ 188888ef2e3SAlexandros Batsakis static void nfs4_destroy_callback(struct nfs_client *clp) 189888ef2e3SAlexandros Batsakis { 190888ef2e3SAlexandros Batsakis if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) 191888ef2e3SAlexandros Batsakis nfs_callback_down(clp->cl_minorversion); 192888ef2e3SAlexandros Batsakis } 193888ef2e3SAlexandros Batsakis 194888ef2e3SAlexandros Batsakis static void nfs4_shutdown_client(struct nfs_client *clp) 195888ef2e3SAlexandros Batsakis { 196888ef2e3SAlexandros Batsakis if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state)) 197888ef2e3SAlexandros Batsakis nfs4_kill_renewd(clp); 198888ef2e3SAlexandros Batsakis nfs4_clear_client_minor_version(clp); 199888ef2e3SAlexandros Batsakis nfs4_destroy_callback(clp); 200888ef2e3SAlexandros Batsakis if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state)) 201888ef2e3SAlexandros Batsakis nfs_idmap_delete(clp); 202888ef2e3SAlexandros Batsakis 203888ef2e3SAlexandros Batsakis rpc_destroy_wait_queue(&clp->cl_rpcwaitq); 204888ef2e3SAlexandros Batsakis } 205888ef2e3SAlexandros Batsakis #else 206888ef2e3SAlexandros Batsakis static void nfs4_shutdown_client(struct nfs_client *clp) 207888ef2e3SAlexandros Batsakis { 208888ef2e3SAlexandros Batsakis } 209888ef2e3SAlexandros Batsakis #endif /* CONFIG_NFS_V4 */ 210888ef2e3SAlexandros Batsakis 211888ef2e3SAlexandros Batsakis /* 2125dd3177aSTrond Myklebust * Destroy a shared client record 2135dd3177aSTrond Myklebust */ 2145dd3177aSTrond Myklebust static void nfs_free_client(struct nfs_client *clp) 2155dd3177aSTrond Myklebust { 21640c55319STrond Myklebust dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version); 2175dd3177aSTrond Myklebust 2185dd3177aSTrond Myklebust nfs4_shutdown_client(clp); 21924c8dbbbSDavid Howells 22014727281SDavid Howells nfs_fscache_release_client_cookie(clp); 22114727281SDavid Howells 22224c8dbbbSDavid Howells /* -EIO all pending I/O */ 22324c8dbbbSDavid Howells if (!IS_ERR(clp->cl_rpcclient)) 22424c8dbbbSDavid Howells rpc_shutdown_client(clp->cl_rpcclient); 22524c8dbbbSDavid Howells 2267c67db3aSTrond Myklebust if (clp->cl_machine_cred != NULL) 2277c67db3aSTrond Myklebust put_rpccred(clp->cl_machine_cred); 2287c67db3aSTrond Myklebust 22924c8dbbbSDavid Howells kfree(clp->cl_hostname); 23024c8dbbbSDavid Howells kfree(clp); 23124c8dbbbSDavid Howells 23224c8dbbbSDavid Howells dprintk("<-- nfs_free_client()\n"); 23324c8dbbbSDavid Howells } 23424c8dbbbSDavid Howells 23524c8dbbbSDavid Howells /* 23624c8dbbbSDavid Howells * Release a reference to a shared client record 23724c8dbbbSDavid Howells */ 23824c8dbbbSDavid Howells void nfs_put_client(struct nfs_client *clp) 23924c8dbbbSDavid Howells { 24027ba8512SDavid Howells if (!clp) 24127ba8512SDavid Howells return; 24227ba8512SDavid Howells 24324c8dbbbSDavid Howells dprintk("--> nfs_put_client({%d})\n", atomic_read(&clp->cl_count)); 24424c8dbbbSDavid Howells 24524c8dbbbSDavid Howells if (atomic_dec_and_lock(&clp->cl_count, &nfs_client_lock)) { 24624c8dbbbSDavid Howells list_del(&clp->cl_share_link); 24724c8dbbbSDavid Howells spin_unlock(&nfs_client_lock); 24824c8dbbbSDavid Howells 24924c8dbbbSDavid Howells BUG_ON(!list_empty(&clp->cl_superblocks)); 25024c8dbbbSDavid Howells 25124c8dbbbSDavid Howells nfs_free_client(clp); 25224c8dbbbSDavid Howells } 25324c8dbbbSDavid Howells } 25424c8dbbbSDavid Howells 2559082a5ccSTrond Myklebust #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 2569f4c899cSTrond Myklebust /* 2579f4c899cSTrond Myklebust * Test if two ip6 socket addresses refer to the same socket by 2589f4c899cSTrond Myklebust * comparing relevant fields. The padding bytes specifically, are not 2599f4c899cSTrond Myklebust * compared. sin6_flowinfo is not compared because it only affects QoS 2609f4c899cSTrond Myklebust * and sin6_scope_id is only compared if the address is "link local" 2619f4c899cSTrond Myklebust * because "link local" addresses need only be unique to a specific 2629f4c899cSTrond Myklebust * link. Conversely, ordinary unicast addresses might have different 2639f4c899cSTrond Myklebust * sin6_scope_id. 2649f4c899cSTrond Myklebust * 2659f4c899cSTrond Myklebust * The caller should ensure both socket addresses are AF_INET6. 2669f4c899cSTrond Myklebust */ 2673c8c45dfSChuck Lever static int nfs_sockaddr_match_ipaddr6(const struct sockaddr *sa1, 2689f4c899cSTrond Myklebust const struct sockaddr *sa2) 2699f4c899cSTrond Myklebust { 2703c8c45dfSChuck Lever const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sa1; 2713c8c45dfSChuck Lever const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2; 2729f4c899cSTrond Myklebust 2733c8c45dfSChuck Lever if (ipv6_addr_scope(&sin1->sin6_addr) == IPV6_ADDR_SCOPE_LINKLOCAL && 2743c8c45dfSChuck Lever sin1->sin6_scope_id != sin2->sin6_scope_id) 2759f4c899cSTrond Myklebust return 0; 2763b0d3f93STrond Myklebust 2773c8c45dfSChuck Lever return ipv6_addr_equal(&sin1->sin6_addr, &sin1->sin6_addr); 2783b0d3f93STrond Myklebust } 2793c8c45dfSChuck Lever #else /* !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE) */ 2803c8c45dfSChuck Lever static int nfs_sockaddr_match_ipaddr6(const struct sockaddr *sa1, 2819f4c899cSTrond Myklebust const struct sockaddr *sa2) 2829f4c899cSTrond Myklebust { 2839f4c899cSTrond Myklebust return 0; 2849f4c899cSTrond Myklebust } 2859082a5ccSTrond Myklebust #endif 2863b0d3f93STrond Myklebust 28724c8dbbbSDavid Howells /* 288d7371c41SIan Dall * Test if two ip4 socket addresses refer to the same socket, by 289d7371c41SIan Dall * comparing relevant fields. The padding bytes specifically, are 290d7371c41SIan Dall * not compared. 291d7371c41SIan Dall * 292d7371c41SIan Dall * The caller should ensure both socket addresses are AF_INET. 293d7371c41SIan Dall */ 2943c8c45dfSChuck Lever static int nfs_sockaddr_match_ipaddr4(const struct sockaddr *sa1, 2953c8c45dfSChuck Lever const struct sockaddr *sa2) 2963c8c45dfSChuck Lever { 2973c8c45dfSChuck Lever const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sa1; 2983c8c45dfSChuck Lever const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sa2; 2993c8c45dfSChuck Lever 3003c8c45dfSChuck Lever return sin1->sin_addr.s_addr == sin2->sin_addr.s_addr; 3013c8c45dfSChuck Lever } 3023c8c45dfSChuck Lever 3033c8c45dfSChuck Lever static int nfs_sockaddr_cmp_ip6(const struct sockaddr *sa1, 3043c8c45dfSChuck Lever const struct sockaddr *sa2) 3053c8c45dfSChuck Lever { 3063c8c45dfSChuck Lever const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sa1; 3073c8c45dfSChuck Lever const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2; 3083c8c45dfSChuck Lever 3093c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr6(sa1, sa2) && 3103c8c45dfSChuck Lever (sin1->sin6_port == sin2->sin6_port); 3113c8c45dfSChuck Lever } 3123c8c45dfSChuck Lever 3139f4c899cSTrond Myklebust static int nfs_sockaddr_cmp_ip4(const struct sockaddr *sa1, 3149f4c899cSTrond Myklebust const struct sockaddr *sa2) 315d7371c41SIan Dall { 3163c8c45dfSChuck Lever const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sa1; 3173c8c45dfSChuck Lever const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sa2; 3189f4c899cSTrond Myklebust 3193c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr4(sa1, sa2) && 3203c8c45dfSChuck Lever (sin1->sin_port == sin2->sin_port); 321d7371c41SIan Dall } 322d7371c41SIan Dall 323d7371c41SIan Dall /* 324d7371c41SIan Dall * Test if two socket addresses represent the same actual socket, 3253c8c45dfSChuck Lever * by comparing (only) relevant fields, excluding the port number. 3263c8c45dfSChuck Lever */ 3273c8c45dfSChuck Lever static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1, 3283c8c45dfSChuck Lever const struct sockaddr *sa2) 3293c8c45dfSChuck Lever { 3303c8c45dfSChuck Lever if (sa1->sa_family != sa2->sa_family) 3313c8c45dfSChuck Lever return 0; 3323c8c45dfSChuck Lever 3333c8c45dfSChuck Lever switch (sa1->sa_family) { 3343c8c45dfSChuck Lever case AF_INET: 3353c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr4(sa1, sa2); 3363c8c45dfSChuck Lever case AF_INET6: 3373c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr6(sa1, sa2); 3383c8c45dfSChuck Lever } 3393c8c45dfSChuck Lever return 0; 3403c8c45dfSChuck Lever } 3413c8c45dfSChuck Lever 3423c8c45dfSChuck Lever /* 3433c8c45dfSChuck Lever * Test if two socket addresses represent the same actual socket, 3443c8c45dfSChuck Lever * by comparing (only) relevant fields, including the port number. 345d7371c41SIan Dall */ 346d7371c41SIan Dall static int nfs_sockaddr_cmp(const struct sockaddr *sa1, 347d7371c41SIan Dall const struct sockaddr *sa2) 348d7371c41SIan Dall { 349d7371c41SIan Dall if (sa1->sa_family != sa2->sa_family) 350d7371c41SIan Dall return 0; 351d7371c41SIan Dall 352d7371c41SIan Dall switch (sa1->sa_family) { 353d7371c41SIan Dall case AF_INET: 3549f4c899cSTrond Myklebust return nfs_sockaddr_cmp_ip4(sa1, sa2); 355d7371c41SIan Dall case AF_INET6: 3569f4c899cSTrond Myklebust return nfs_sockaddr_cmp_ip6(sa1, sa2); 357d7371c41SIan Dall } 358d7371c41SIan Dall return 0; 359d7371c41SIan Dall } 360d7371c41SIan Dall 361d7371c41SIan Dall /* 362c81468a1STrond Myklebust * Find a client by IP address and protocol version 363c81468a1STrond Myklebust * - returns NULL if no such client 36424c8dbbbSDavid Howells */ 365ff052645SChuck Lever struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion) 366c81468a1STrond Myklebust { 367c81468a1STrond Myklebust struct nfs_client *clp; 368c81468a1STrond Myklebust 369c81468a1STrond Myklebust spin_lock(&nfs_client_lock); 370c81468a1STrond Myklebust list_for_each_entry(clp, &nfs_client_list, cl_share_link) { 3713b0d3f93STrond Myklebust struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; 3723b0d3f93STrond Myklebust 373c81468a1STrond Myklebust /* Don't match clients that failed to initialise properly */ 37476db6d95SAndy Adamson if (!(clp->cl_cons_state == NFS_CS_READY || 37576db6d95SAndy Adamson clp->cl_cons_state == NFS_CS_SESSION_INITING)) 376c81468a1STrond Myklebust continue; 377c81468a1STrond Myklebust 378c81468a1STrond Myklebust /* Different NFS versions cannot share the same nfs_client */ 37940c55319STrond Myklebust if (clp->rpc_ops->version != nfsversion) 380c81468a1STrond Myklebust continue; 381c81468a1STrond Myklebust 382c81468a1STrond Myklebust /* Match only the IP address, not the port number */ 3833b0d3f93STrond Myklebust if (!nfs_sockaddr_match_ipaddr(addr, clap)) 384c81468a1STrond Myklebust continue; 385c81468a1STrond Myklebust 386c81468a1STrond Myklebust atomic_inc(&clp->cl_count); 387c81468a1STrond Myklebust spin_unlock(&nfs_client_lock); 388c81468a1STrond Myklebust return clp; 389c81468a1STrond Myklebust } 390c81468a1STrond Myklebust spin_unlock(&nfs_client_lock); 391c81468a1STrond Myklebust return NULL; 392c81468a1STrond Myklebust } 393c81468a1STrond Myklebust 394c81468a1STrond Myklebust /* 3953fbd67adSTrond Myklebust * Find a client by IP address and protocol version 3963fbd67adSTrond Myklebust * - returns NULL if no such client 3973fbd67adSTrond Myklebust */ 3983fbd67adSTrond Myklebust struct nfs_client *nfs_find_client_next(struct nfs_client *clp) 3993fbd67adSTrond Myklebust { 4003fbd67adSTrond Myklebust struct sockaddr *sap = (struct sockaddr *)&clp->cl_addr; 4013fbd67adSTrond Myklebust u32 nfsvers = clp->rpc_ops->version; 4023fbd67adSTrond Myklebust 4033fbd67adSTrond Myklebust spin_lock(&nfs_client_lock); 4043fbd67adSTrond Myklebust list_for_each_entry_continue(clp, &nfs_client_list, cl_share_link) { 4053fbd67adSTrond Myklebust struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; 4063fbd67adSTrond Myklebust 4073fbd67adSTrond Myklebust /* Don't match clients that failed to initialise properly */ 4083fbd67adSTrond Myklebust if (clp->cl_cons_state != NFS_CS_READY) 4093fbd67adSTrond Myklebust continue; 4103fbd67adSTrond Myklebust 4113fbd67adSTrond Myklebust /* Different NFS versions cannot share the same nfs_client */ 4123fbd67adSTrond Myklebust if (clp->rpc_ops->version != nfsvers) 4133fbd67adSTrond Myklebust continue; 4143fbd67adSTrond Myklebust 4153fbd67adSTrond Myklebust /* Match only the IP address, not the port number */ 4163fbd67adSTrond Myklebust if (!nfs_sockaddr_match_ipaddr(sap, clap)) 4173fbd67adSTrond Myklebust continue; 4183fbd67adSTrond Myklebust 4193fbd67adSTrond Myklebust atomic_inc(&clp->cl_count); 4203fbd67adSTrond Myklebust spin_unlock(&nfs_client_lock); 4213fbd67adSTrond Myklebust return clp; 4223fbd67adSTrond Myklebust } 4233fbd67adSTrond Myklebust spin_unlock(&nfs_client_lock); 4243fbd67adSTrond Myklebust return NULL; 4253fbd67adSTrond Myklebust } 4263fbd67adSTrond Myklebust 4273fbd67adSTrond Myklebust /* 428c81468a1STrond Myklebust * Find an nfs_client on the list that matches the initialisation data 429c81468a1STrond Myklebust * that is supplied. 430c81468a1STrond Myklebust */ 431c81468a1STrond Myklebust static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *data) 43224c8dbbbSDavid Howells { 43324c8dbbbSDavid Howells struct nfs_client *clp; 434d7371c41SIan Dall const struct sockaddr *sap = data->addr; 43524c8dbbbSDavid Howells 43624c8dbbbSDavid Howells list_for_each_entry(clp, &nfs_client_list, cl_share_link) { 437d7371c41SIan Dall const struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; 43813bbc06aSTrond Myklebust /* Don't match clients that failed to initialise properly */ 43913bbc06aSTrond Myklebust if (clp->cl_cons_state < 0) 44013bbc06aSTrond Myklebust continue; 44113bbc06aSTrond Myklebust 44224c8dbbbSDavid Howells /* Different NFS versions cannot share the same nfs_client */ 44340c55319STrond Myklebust if (clp->rpc_ops != data->rpc_ops) 44424c8dbbbSDavid Howells continue; 44524c8dbbbSDavid Howells 44659dca3b2STrond Myklebust if (clp->cl_proto != data->proto) 44759dca3b2STrond Myklebust continue; 4485aae4a9aSBenny Halevy /* Match nfsv4 minorversion */ 4495aae4a9aSBenny Halevy if (clp->cl_minorversion != data->minorversion) 4505aae4a9aSBenny Halevy continue; 451c81468a1STrond Myklebust /* Match the full socket address */ 452d7371c41SIan Dall if (!nfs_sockaddr_cmp(sap, clap)) 45324c8dbbbSDavid Howells continue; 45424c8dbbbSDavid Howells 45524c8dbbbSDavid Howells atomic_inc(&clp->cl_count); 45624c8dbbbSDavid Howells return clp; 45724c8dbbbSDavid Howells } 458c81468a1STrond Myklebust return NULL; 45924c8dbbbSDavid Howells } 46024c8dbbbSDavid Howells 46124c8dbbbSDavid Howells /* 46224c8dbbbSDavid Howells * Look up a client by IP address and protocol version 46324c8dbbbSDavid Howells * - creates a new record if one doesn't yet exist 46424c8dbbbSDavid Howells */ 4653a498026STrond Myklebust static struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_init) 46624c8dbbbSDavid Howells { 46724c8dbbbSDavid Howells struct nfs_client *clp, *new = NULL; 46824c8dbbbSDavid Howells int error; 46924c8dbbbSDavid Howells 470d7422c47SChuck Lever dprintk("--> nfs_get_client(%s,v%u)\n", 471d7422c47SChuck Lever cl_init->hostname ?: "", cl_init->rpc_ops->version); 47224c8dbbbSDavid Howells 47324c8dbbbSDavid Howells /* see if the client already exists */ 47424c8dbbbSDavid Howells do { 47524c8dbbbSDavid Howells spin_lock(&nfs_client_lock); 47624c8dbbbSDavid Howells 477c81468a1STrond Myklebust clp = nfs_match_client(cl_init); 47824c8dbbbSDavid Howells if (clp) 47924c8dbbbSDavid Howells goto found_client; 48024c8dbbbSDavid Howells if (new) 48124c8dbbbSDavid Howells goto install_client; 48224c8dbbbSDavid Howells 48324c8dbbbSDavid Howells spin_unlock(&nfs_client_lock); 48424c8dbbbSDavid Howells 4853a498026STrond Myklebust new = nfs_alloc_client(cl_init); 486a21bdd9bSChuck Lever } while (!IS_ERR(new)); 48724c8dbbbSDavid Howells 488a21bdd9bSChuck Lever dprintk("--> nfs_get_client() = %ld [failed]\n", PTR_ERR(new)); 489a21bdd9bSChuck Lever return new; 49024c8dbbbSDavid Howells 49124c8dbbbSDavid Howells /* install a new client and return with it unready */ 49224c8dbbbSDavid Howells install_client: 49324c8dbbbSDavid Howells clp = new; 49424c8dbbbSDavid Howells list_add(&clp->cl_share_link, &nfs_client_list); 49524c8dbbbSDavid Howells spin_unlock(&nfs_client_lock); 49624c8dbbbSDavid Howells dprintk("--> nfs_get_client() = %p [new]\n", clp); 49724c8dbbbSDavid Howells return clp; 49824c8dbbbSDavid Howells 49924c8dbbbSDavid Howells /* found an existing client 50024c8dbbbSDavid Howells * - make sure it's ready before returning 50124c8dbbbSDavid Howells */ 50224c8dbbbSDavid Howells found_client: 50324c8dbbbSDavid Howells spin_unlock(&nfs_client_lock); 50424c8dbbbSDavid Howells 50524c8dbbbSDavid Howells if (new) 50624c8dbbbSDavid Howells nfs_free_client(new); 50724c8dbbbSDavid Howells 508150030b7SMatthew Wilcox error = wait_event_killable(nfs_client_active_wq, 50976db6d95SAndy Adamson clp->cl_cons_state < NFS_CS_INITING); 5100bae89ecSTrond Myklebust if (error < 0) { 51124c8dbbbSDavid Howells nfs_put_client(clp); 51224c8dbbbSDavid Howells return ERR_PTR(-ERESTARTSYS); 51324c8dbbbSDavid Howells } 51424c8dbbbSDavid Howells 51524c8dbbbSDavid Howells if (clp->cl_cons_state < NFS_CS_READY) { 51624c8dbbbSDavid Howells error = clp->cl_cons_state; 51724c8dbbbSDavid Howells nfs_put_client(clp); 51824c8dbbbSDavid Howells return ERR_PTR(error); 51924c8dbbbSDavid Howells } 52024c8dbbbSDavid Howells 52154ceac45SDavid Howells BUG_ON(clp->cl_cons_state != NFS_CS_READY); 52254ceac45SDavid Howells 52324c8dbbbSDavid Howells dprintk("--> nfs_get_client() = %p [share]\n", clp); 52424c8dbbbSDavid Howells return clp; 52524c8dbbbSDavid Howells } 52624c8dbbbSDavid Howells 52724c8dbbbSDavid Howells /* 52824c8dbbbSDavid Howells * Mark a server as ready or failed 52924c8dbbbSDavid Howells */ 53076db6d95SAndy Adamson void nfs_mark_client_ready(struct nfs_client *clp, int state) 53124c8dbbbSDavid Howells { 53224c8dbbbSDavid Howells clp->cl_cons_state = state; 53324c8dbbbSDavid Howells wake_up_all(&nfs_client_active_wq); 53424c8dbbbSDavid Howells } 5355006a76cSDavid Howells 5365006a76cSDavid Howells /* 537008f55d0SBenny Halevy * With sessions, the client is not marked ready until after a 538008f55d0SBenny Halevy * successful EXCHANGE_ID and CREATE_SESSION. 539008f55d0SBenny Halevy * 540008f55d0SBenny Halevy * Map errors cl_cons_state errors to EPROTONOSUPPORT to indicate 541008f55d0SBenny Halevy * other versions of NFS can be tried. 542008f55d0SBenny Halevy */ 543008f55d0SBenny Halevy int nfs4_check_client_ready(struct nfs_client *clp) 544008f55d0SBenny Halevy { 545008f55d0SBenny Halevy if (!nfs4_has_session(clp)) 546008f55d0SBenny Halevy return 0; 547008f55d0SBenny Halevy if (clp->cl_cons_state < NFS_CS_READY) 548008f55d0SBenny Halevy return -EPROTONOSUPPORT; 549008f55d0SBenny Halevy return 0; 550008f55d0SBenny Halevy } 551008f55d0SBenny Halevy 552008f55d0SBenny Halevy /* 5535006a76cSDavid Howells * Initialise the timeout values for a connection 5545006a76cSDavid Howells */ 5555006a76cSDavid Howells static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, 5565006a76cSDavid Howells unsigned int timeo, unsigned int retrans) 5575006a76cSDavid Howells { 5585006a76cSDavid Howells to->to_initval = timeo * HZ / 10; 5595006a76cSDavid Howells to->to_retries = retrans; 5605006a76cSDavid Howells 5615006a76cSDavid Howells switch (proto) { 5620896a725S\"Talpey, Thomas\ case XPRT_TRANSPORT_TCP: 5632cf7ff7aS\"Talpey, Thomas\ case XPRT_TRANSPORT_RDMA: 564259875efSTrond Myklebust if (to->to_retries == 0) 565259875efSTrond Myklebust to->to_retries = NFS_DEF_TCP_RETRANS; 5667a3e3e18STrond Myklebust if (to->to_initval == 0) 567259875efSTrond Myklebust to->to_initval = NFS_DEF_TCP_TIMEO * HZ / 10; 5685006a76cSDavid Howells if (to->to_initval > NFS_MAX_TCP_TIMEOUT) 5695006a76cSDavid Howells to->to_initval = NFS_MAX_TCP_TIMEOUT; 5705006a76cSDavid Howells to->to_increment = to->to_initval; 5715006a76cSDavid Howells to->to_maxval = to->to_initval + (to->to_increment * to->to_retries); 5727a3e3e18STrond Myklebust if (to->to_maxval > NFS_MAX_TCP_TIMEOUT) 5737a3e3e18STrond Myklebust to->to_maxval = NFS_MAX_TCP_TIMEOUT; 5747a3e3e18STrond Myklebust if (to->to_maxval < to->to_initval) 5757a3e3e18STrond Myklebust to->to_maxval = to->to_initval; 5765006a76cSDavid Howells to->to_exponential = 0; 5775006a76cSDavid Howells break; 5780896a725S\"Talpey, Thomas\ case XPRT_TRANSPORT_UDP: 579259875efSTrond Myklebust if (to->to_retries == 0) 580259875efSTrond Myklebust to->to_retries = NFS_DEF_UDP_RETRANS; 5815006a76cSDavid Howells if (!to->to_initval) 582259875efSTrond Myklebust to->to_initval = NFS_DEF_UDP_TIMEO * HZ / 10; 5835006a76cSDavid Howells if (to->to_initval > NFS_MAX_UDP_TIMEOUT) 5845006a76cSDavid Howells to->to_initval = NFS_MAX_UDP_TIMEOUT; 5855006a76cSDavid Howells to->to_maxval = NFS_MAX_UDP_TIMEOUT; 5865006a76cSDavid Howells to->to_exponential = 1; 5875006a76cSDavid Howells break; 588259875efSTrond Myklebust default: 589259875efSTrond Myklebust BUG(); 5905006a76cSDavid Howells } 5915006a76cSDavid Howells } 5925006a76cSDavid Howells 5935006a76cSDavid Howells /* 5945006a76cSDavid Howells * Create an RPC client handle 5955006a76cSDavid Howells */ 59659dca3b2STrond Myklebust static int nfs_create_rpc_client(struct nfs_client *clp, 59733170233STrond Myklebust const struct rpc_timeout *timeparms, 59843d78ef2SChuck Lever rpc_authflavor_t flavor, 5994a01b8a4SChuck Lever int discrtry, int noresvport) 6005006a76cSDavid Howells { 6015006a76cSDavid Howells struct rpc_clnt *clnt = NULL; 60241877d20SChuck Lever struct rpc_create_args args = { 60359dca3b2STrond Myklebust .protocol = clp->cl_proto, 60441877d20SChuck Lever .address = (struct sockaddr *)&clp->cl_addr, 6056e4cffd7SChuck Lever .addrsize = clp->cl_addrlen, 60633170233STrond Myklebust .timeout = timeparms, 60741877d20SChuck Lever .servername = clp->cl_hostname, 60841877d20SChuck Lever .program = &nfs_program, 60941877d20SChuck Lever .version = clp->rpc_ops->version, 61041877d20SChuck Lever .authflavor = flavor, 61141877d20SChuck Lever }; 6125006a76cSDavid Howells 6134a01b8a4SChuck Lever if (discrtry) 6144a01b8a4SChuck Lever args.flags |= RPC_CLNT_CREATE_DISCRTRY; 6154a01b8a4SChuck Lever if (noresvport) 6164a01b8a4SChuck Lever args.flags |= RPC_CLNT_CREATE_NONPRIVPORT; 6174a01b8a4SChuck Lever 6185006a76cSDavid Howells if (!IS_ERR(clp->cl_rpcclient)) 6195006a76cSDavid Howells return 0; 6205006a76cSDavid Howells 62141877d20SChuck Lever clnt = rpc_create(&args); 6225006a76cSDavid Howells if (IS_ERR(clnt)) { 6235006a76cSDavid Howells dprintk("%s: cannot create RPC client. Error = %ld\n", 6243110ff80SHarvey Harrison __func__, PTR_ERR(clnt)); 6255006a76cSDavid Howells return PTR_ERR(clnt); 6265006a76cSDavid Howells } 6275006a76cSDavid Howells 6285006a76cSDavid Howells clp->cl_rpcclient = clnt; 6295006a76cSDavid Howells return 0; 6305006a76cSDavid Howells } 63154ceac45SDavid Howells 63254ceac45SDavid Howells /* 63354ceac45SDavid Howells * Version 2 or 3 client destruction 63454ceac45SDavid Howells */ 63554ceac45SDavid Howells static void nfs_destroy_server(struct nfs_server *server) 63654ceac45SDavid Howells { 63754ceac45SDavid Howells if (!(server->flags & NFS_MOUNT_NONLM)) 6389289e7f9SChuck Lever nlmclnt_done(server->nlm_host); 63954ceac45SDavid Howells } 64054ceac45SDavid Howells 64154ceac45SDavid Howells /* 64254ceac45SDavid Howells * Version 2 or 3 lockd setup 64354ceac45SDavid Howells */ 64454ceac45SDavid Howells static int nfs_start_lockd(struct nfs_server *server) 64554ceac45SDavid Howells { 6469289e7f9SChuck Lever struct nlm_host *host; 6479289e7f9SChuck Lever struct nfs_client *clp = server->nfs_client; 648883bb163SChuck Lever struct nlmclnt_initdata nlm_init = { 649883bb163SChuck Lever .hostname = clp->cl_hostname, 650883bb163SChuck Lever .address = (struct sockaddr *)&clp->cl_addr, 651883bb163SChuck Lever .addrlen = clp->cl_addrlen, 652883bb163SChuck Lever .nfs_version = clp->rpc_ops->version, 6530cb2659bSChuck Lever .noresvport = server->flags & NFS_MOUNT_NORESVPORT ? 6540cb2659bSChuck Lever 1 : 0, 655883bb163SChuck Lever }; 65654ceac45SDavid Howells 657883bb163SChuck Lever if (nlm_init.nfs_version > 3) 6589289e7f9SChuck Lever return 0; 65954ceac45SDavid Howells if (server->flags & NFS_MOUNT_NONLM) 6609289e7f9SChuck Lever return 0; 6619289e7f9SChuck Lever 6628a6e5debSTrond Myklebust switch (clp->cl_proto) { 6638a6e5debSTrond Myklebust default: 6648a6e5debSTrond Myklebust nlm_init.protocol = IPPROTO_TCP; 6658a6e5debSTrond Myklebust break; 6668a6e5debSTrond Myklebust case XPRT_TRANSPORT_UDP: 6678a6e5debSTrond Myklebust nlm_init.protocol = IPPROTO_UDP; 6688a6e5debSTrond Myklebust } 6698a6e5debSTrond Myklebust 670883bb163SChuck Lever host = nlmclnt_init(&nlm_init); 6719289e7f9SChuck Lever if (IS_ERR(host)) 6729289e7f9SChuck Lever return PTR_ERR(host); 6739289e7f9SChuck Lever 6749289e7f9SChuck Lever server->nlm_host = host; 67554ceac45SDavid Howells server->destroy = nfs_destroy_server; 6769289e7f9SChuck Lever return 0; 67754ceac45SDavid Howells } 67854ceac45SDavid Howells 67954ceac45SDavid Howells /* 68054ceac45SDavid Howells * Initialise an NFSv3 ACL client connection 68154ceac45SDavid Howells */ 68254ceac45SDavid Howells #ifdef CONFIG_NFS_V3_ACL 68354ceac45SDavid Howells static void nfs_init_server_aclclient(struct nfs_server *server) 68454ceac45SDavid Howells { 68540c55319STrond Myklebust if (server->nfs_client->rpc_ops->version != 3) 68654ceac45SDavid Howells goto out_noacl; 68754ceac45SDavid Howells if (server->flags & NFS_MOUNT_NOACL) 68854ceac45SDavid Howells goto out_noacl; 68954ceac45SDavid Howells 69054ceac45SDavid Howells server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3); 69154ceac45SDavid Howells if (IS_ERR(server->client_acl)) 69254ceac45SDavid Howells goto out_noacl; 69354ceac45SDavid Howells 69454ceac45SDavid Howells /* No errors! Assume that Sun nfsacls are supported */ 69554ceac45SDavid Howells server->caps |= NFS_CAP_ACLS; 69654ceac45SDavid Howells return; 69754ceac45SDavid Howells 69854ceac45SDavid Howells out_noacl: 69954ceac45SDavid Howells server->caps &= ~NFS_CAP_ACLS; 70054ceac45SDavid Howells } 70154ceac45SDavid Howells #else 70254ceac45SDavid Howells static inline void nfs_init_server_aclclient(struct nfs_server *server) 70354ceac45SDavid Howells { 70454ceac45SDavid Howells server->flags &= ~NFS_MOUNT_NOACL; 70554ceac45SDavid Howells server->caps &= ~NFS_CAP_ACLS; 70654ceac45SDavid Howells } 70754ceac45SDavid Howells #endif 70854ceac45SDavid Howells 70954ceac45SDavid Howells /* 71054ceac45SDavid Howells * Create a general RPC client 71154ceac45SDavid Howells */ 71233170233STrond Myklebust static int nfs_init_server_rpcclient(struct nfs_server *server, 71333170233STrond Myklebust const struct rpc_timeout *timeo, 71433170233STrond Myklebust rpc_authflavor_t pseudoflavour) 71554ceac45SDavid Howells { 71654ceac45SDavid Howells struct nfs_client *clp = server->nfs_client; 71754ceac45SDavid Howells 71854ceac45SDavid Howells server->client = rpc_clone_client(clp->cl_rpcclient); 71954ceac45SDavid Howells if (IS_ERR(server->client)) { 7203110ff80SHarvey Harrison dprintk("%s: couldn't create rpc_client!\n", __func__); 72154ceac45SDavid Howells return PTR_ERR(server->client); 72254ceac45SDavid Howells } 72354ceac45SDavid Howells 72433170233STrond Myklebust memcpy(&server->client->cl_timeout_default, 72533170233STrond Myklebust timeo, 72633170233STrond Myklebust sizeof(server->client->cl_timeout_default)); 72733170233STrond Myklebust server->client->cl_timeout = &server->client->cl_timeout_default; 72833170233STrond Myklebust 72954ceac45SDavid Howells if (pseudoflavour != clp->cl_rpcclient->cl_auth->au_flavor) { 73054ceac45SDavid Howells struct rpc_auth *auth; 73154ceac45SDavid Howells 73254ceac45SDavid Howells auth = rpcauth_create(pseudoflavour, server->client); 73354ceac45SDavid Howells if (IS_ERR(auth)) { 7343110ff80SHarvey Harrison dprintk("%s: couldn't create credcache!\n", __func__); 73554ceac45SDavid Howells return PTR_ERR(auth); 73654ceac45SDavid Howells } 73754ceac45SDavid Howells } 73854ceac45SDavid Howells server->client->cl_softrtry = 0; 73954ceac45SDavid Howells if (server->flags & NFS_MOUNT_SOFT) 74054ceac45SDavid Howells server->client->cl_softrtry = 1; 74154ceac45SDavid Howells 74254ceac45SDavid Howells return 0; 74354ceac45SDavid Howells } 74454ceac45SDavid Howells 74554ceac45SDavid Howells /* 74654ceac45SDavid Howells * Initialise an NFS2 or NFS3 client 74754ceac45SDavid Howells */ 7482283f8d6S\"Talpey, Thomas\ static int nfs_init_client(struct nfs_client *clp, 74933170233STrond Myklebust const struct rpc_timeout *timeparms, 7502283f8d6S\"Talpey, Thomas\ const struct nfs_parsed_mount_data *data) 75154ceac45SDavid Howells { 75254ceac45SDavid Howells int error; 75354ceac45SDavid Howells 75454ceac45SDavid Howells if (clp->cl_cons_state == NFS_CS_READY) { 75554ceac45SDavid Howells /* the client is already initialised */ 75654ceac45SDavid Howells dprintk("<-- nfs_init_client() = 0 [already %p]\n", clp); 75754ceac45SDavid Howells return 0; 75854ceac45SDavid Howells } 75954ceac45SDavid Howells 76054ceac45SDavid Howells /* 76154ceac45SDavid Howells * Create a client RPC handle for doing FSSTAT with UNIX auth only 76254ceac45SDavid Howells * - RFC 2623, sec 2.3.2 76354ceac45SDavid Howells */ 764d740351bSChuck Lever error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX, 765d740351bSChuck Lever 0, data->flags & NFS_MOUNT_NORESVPORT); 76654ceac45SDavid Howells if (error < 0) 76754ceac45SDavid Howells goto error; 76854ceac45SDavid Howells nfs_mark_client_ready(clp, NFS_CS_READY); 76954ceac45SDavid Howells return 0; 77054ceac45SDavid Howells 77154ceac45SDavid Howells error: 77254ceac45SDavid Howells nfs_mark_client_ready(clp, error); 77354ceac45SDavid Howells dprintk("<-- nfs_init_client() = xerror %d\n", error); 77454ceac45SDavid Howells return error; 77554ceac45SDavid Howells } 77654ceac45SDavid Howells 77754ceac45SDavid Howells /* 77854ceac45SDavid Howells * Create a version 2 or 3 client 77954ceac45SDavid Howells */ 7802283f8d6S\"Talpey, Thomas\ static int nfs_init_server(struct nfs_server *server, 7812283f8d6S\"Talpey, Thomas\ const struct nfs_parsed_mount_data *data) 78254ceac45SDavid Howells { 7833a498026STrond Myklebust struct nfs_client_initdata cl_init = { 7843a498026STrond Myklebust .hostname = data->nfs_server.hostname, 785d7422c47SChuck Lever .addr = (const struct sockaddr *)&data->nfs_server.address, 7864c568017SChuck Lever .addrlen = data->nfs_server.addrlen, 78740c55319STrond Myklebust .rpc_ops = &nfs_v2_clientops, 78859dca3b2STrond Myklebust .proto = data->nfs_server.protocol, 7893a498026STrond Myklebust }; 79033170233STrond Myklebust struct rpc_timeout timeparms; 79154ceac45SDavid Howells struct nfs_client *clp; 7923a498026STrond Myklebust int error; 79354ceac45SDavid Howells 79454ceac45SDavid Howells dprintk("--> nfs_init_server()\n"); 79554ceac45SDavid Howells 79654ceac45SDavid Howells #ifdef CONFIG_NFS_V3 7978a6e5debSTrond Myklebust if (data->version == 3) 79840c55319STrond Myklebust cl_init.rpc_ops = &nfs_v3_clientops; 79954ceac45SDavid Howells #endif 80054ceac45SDavid Howells 80154ceac45SDavid Howells /* Allocate or find a client reference we can use */ 8023a498026STrond Myklebust clp = nfs_get_client(&cl_init); 80354ceac45SDavid Howells if (IS_ERR(clp)) { 80454ceac45SDavid Howells dprintk("<-- nfs_init_server() = error %ld\n", PTR_ERR(clp)); 80554ceac45SDavid Howells return PTR_ERR(clp); 80654ceac45SDavid Howells } 80754ceac45SDavid Howells 80833170233STrond Myklebust nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, 80933170233STrond Myklebust data->timeo, data->retrans); 81033170233STrond Myklebust error = nfs_init_client(clp, &timeparms, data); 81154ceac45SDavid Howells if (error < 0) 81254ceac45SDavid Howells goto error; 81354ceac45SDavid Howells 81454ceac45SDavid Howells server->nfs_client = clp; 81554ceac45SDavid Howells 81654ceac45SDavid Howells /* Initialise the client representation from the mount data */ 817ff3525a5STrond Myklebust server->flags = data->flags; 818b797cac7SDavid Howells server->options = data->options; 81962ab460cSTrond Myklebust server->caps |= NFS_CAP_HARDLINKS|NFS_CAP_SYMLINKS|NFS_CAP_FILEID| 82062ab460cSTrond Myklebust NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|NFS_CAP_OWNER_GROUP| 82162ab460cSTrond Myklebust NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME; 82254ceac45SDavid Howells 82354ceac45SDavid Howells if (data->rsize) 82454ceac45SDavid Howells server->rsize = nfs_block_size(data->rsize, NULL); 82554ceac45SDavid Howells if (data->wsize) 82654ceac45SDavid Howells server->wsize = nfs_block_size(data->wsize, NULL); 82754ceac45SDavid Howells 82854ceac45SDavid Howells server->acregmin = data->acregmin * HZ; 82954ceac45SDavid Howells server->acregmax = data->acregmax * HZ; 83054ceac45SDavid Howells server->acdirmin = data->acdirmin * HZ; 83154ceac45SDavid Howells server->acdirmax = data->acdirmax * HZ; 83254ceac45SDavid Howells 83354ceac45SDavid Howells /* Start lockd here, before we might error out */ 83454ceac45SDavid Howells error = nfs_start_lockd(server); 83554ceac45SDavid Howells if (error < 0) 83654ceac45SDavid Howells goto error; 83754ceac45SDavid Howells 838f22d6d79SChuck Lever server->port = data->nfs_server.port; 839f22d6d79SChuck Lever 84033170233STrond Myklebust error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]); 84154ceac45SDavid Howells if (error < 0) 84254ceac45SDavid Howells goto error; 84354ceac45SDavid Howells 8443f8400d1SChuck Lever /* Preserve the values of mount_server-related mount options */ 8453f8400d1SChuck Lever if (data->mount_server.addrlen) { 8463f8400d1SChuck Lever memcpy(&server->mountd_address, &data->mount_server.address, 8473f8400d1SChuck Lever data->mount_server.addrlen); 8483f8400d1SChuck Lever server->mountd_addrlen = data->mount_server.addrlen; 8493f8400d1SChuck Lever } 8503f8400d1SChuck Lever server->mountd_version = data->mount_server.version; 8513f8400d1SChuck Lever server->mountd_port = data->mount_server.port; 8523f8400d1SChuck Lever server->mountd_protocol = data->mount_server.protocol; 8533f8400d1SChuck Lever 85454ceac45SDavid Howells server->namelen = data->namlen; 85554ceac45SDavid Howells /* Create a client RPC handle for the NFSv3 ACL management interface */ 85654ceac45SDavid Howells nfs_init_server_aclclient(server); 85754ceac45SDavid Howells dprintk("<-- nfs_init_server() = 0 [new %p]\n", clp); 85854ceac45SDavid Howells return 0; 85954ceac45SDavid Howells 86054ceac45SDavid Howells error: 86154ceac45SDavid Howells server->nfs_client = NULL; 86254ceac45SDavid Howells nfs_put_client(clp); 86354ceac45SDavid Howells dprintk("<-- nfs_init_server() = xerror %d\n", error); 86454ceac45SDavid Howells return error; 86554ceac45SDavid Howells } 86654ceac45SDavid Howells 86754ceac45SDavid Howells /* 86854ceac45SDavid Howells * Load up the server record from information gained in an fsinfo record 86954ceac45SDavid Howells */ 87054ceac45SDavid Howells static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo *fsinfo) 87154ceac45SDavid Howells { 87254ceac45SDavid Howells unsigned long max_rpc_payload; 87354ceac45SDavid Howells 87454ceac45SDavid Howells /* Work out a lot of parameters */ 87554ceac45SDavid Howells if (server->rsize == 0) 87654ceac45SDavid Howells server->rsize = nfs_block_size(fsinfo->rtpref, NULL); 87754ceac45SDavid Howells if (server->wsize == 0) 87854ceac45SDavid Howells server->wsize = nfs_block_size(fsinfo->wtpref, NULL); 87954ceac45SDavid Howells 88054ceac45SDavid Howells if (fsinfo->rtmax >= 512 && server->rsize > fsinfo->rtmax) 88154ceac45SDavid Howells server->rsize = nfs_block_size(fsinfo->rtmax, NULL); 88254ceac45SDavid Howells if (fsinfo->wtmax >= 512 && server->wsize > fsinfo->wtmax) 88354ceac45SDavid Howells server->wsize = nfs_block_size(fsinfo->wtmax, NULL); 88454ceac45SDavid Howells 88554ceac45SDavid Howells max_rpc_payload = nfs_block_size(rpc_max_payload(server->client), NULL); 88654ceac45SDavid Howells if (server->rsize > max_rpc_payload) 88754ceac45SDavid Howells server->rsize = max_rpc_payload; 88854ceac45SDavid Howells if (server->rsize > NFS_MAX_FILE_IO_SIZE) 88954ceac45SDavid Howells server->rsize = NFS_MAX_FILE_IO_SIZE; 89054ceac45SDavid Howells server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 891e0bf68ddSPeter Zijlstra 892d993831fSJens Axboe server->backing_dev_info.name = "nfs"; 89354ceac45SDavid Howells server->backing_dev_info.ra_pages = server->rpages * NFS_MAX_READAHEAD; 89454ceac45SDavid Howells 89554ceac45SDavid Howells if (server->wsize > max_rpc_payload) 89654ceac45SDavid Howells server->wsize = max_rpc_payload; 89754ceac45SDavid Howells if (server->wsize > NFS_MAX_FILE_IO_SIZE) 89854ceac45SDavid Howells server->wsize = NFS_MAX_FILE_IO_SIZE; 89954ceac45SDavid Howells server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 90054ceac45SDavid Howells server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL); 90154ceac45SDavid Howells 90254ceac45SDavid Howells server->dtsize = nfs_block_size(fsinfo->dtpref, NULL); 90354ceac45SDavid Howells if (server->dtsize > PAGE_CACHE_SIZE) 90454ceac45SDavid Howells server->dtsize = PAGE_CACHE_SIZE; 90554ceac45SDavid Howells if (server->dtsize > server->rsize) 90654ceac45SDavid Howells server->dtsize = server->rsize; 90754ceac45SDavid Howells 90854ceac45SDavid Howells if (server->flags & NFS_MOUNT_NOAC) { 90954ceac45SDavid Howells server->acregmin = server->acregmax = 0; 91054ceac45SDavid Howells server->acdirmin = server->acdirmax = 0; 91154ceac45SDavid Howells } 91254ceac45SDavid Howells 91354ceac45SDavid Howells server->maxfilesize = fsinfo->maxfilesize; 91454ceac45SDavid Howells 91554ceac45SDavid Howells /* We're airborne Set socket buffersize */ 91654ceac45SDavid Howells rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100); 91754ceac45SDavid Howells } 91854ceac45SDavid Howells 91954ceac45SDavid Howells /* 92054ceac45SDavid Howells * Probe filesystem information, including the FSID on v2/v3 92154ceac45SDavid Howells */ 92254ceac45SDavid Howells static int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr) 92354ceac45SDavid Howells { 92454ceac45SDavid Howells struct nfs_fsinfo fsinfo; 92554ceac45SDavid Howells struct nfs_client *clp = server->nfs_client; 92654ceac45SDavid Howells int error; 92754ceac45SDavid Howells 92854ceac45SDavid Howells dprintk("--> nfs_probe_fsinfo()\n"); 92954ceac45SDavid Howells 93054ceac45SDavid Howells if (clp->rpc_ops->set_capabilities != NULL) { 93154ceac45SDavid Howells error = clp->rpc_ops->set_capabilities(server, mntfh); 93254ceac45SDavid Howells if (error < 0) 93354ceac45SDavid Howells goto out_error; 93454ceac45SDavid Howells } 93554ceac45SDavid Howells 93654ceac45SDavid Howells fsinfo.fattr = fattr; 93754ceac45SDavid Howells error = clp->rpc_ops->fsinfo(server, mntfh, &fsinfo); 93854ceac45SDavid Howells if (error < 0) 93954ceac45SDavid Howells goto out_error; 94054ceac45SDavid Howells 94154ceac45SDavid Howells nfs_server_set_fsinfo(server, &fsinfo); 94254ceac45SDavid Howells 94354ceac45SDavid Howells /* Get some general file system info */ 94454ceac45SDavid Howells if (server->namelen == 0) { 94554ceac45SDavid Howells struct nfs_pathconf pathinfo; 94654ceac45SDavid Howells 94754ceac45SDavid Howells pathinfo.fattr = fattr; 94854ceac45SDavid Howells nfs_fattr_init(fattr); 94954ceac45SDavid Howells 95054ceac45SDavid Howells if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0) 95154ceac45SDavid Howells server->namelen = pathinfo.max_namelen; 95254ceac45SDavid Howells } 95354ceac45SDavid Howells 95454ceac45SDavid Howells dprintk("<-- nfs_probe_fsinfo() = 0\n"); 95554ceac45SDavid Howells return 0; 95654ceac45SDavid Howells 95754ceac45SDavid Howells out_error: 95854ceac45SDavid Howells dprintk("nfs_probe_fsinfo: error = %d\n", -error); 95954ceac45SDavid Howells return error; 96054ceac45SDavid Howells } 96154ceac45SDavid Howells 96254ceac45SDavid Howells /* 96354ceac45SDavid Howells * Copy useful information when duplicating a server record 96454ceac45SDavid Howells */ 96554ceac45SDavid Howells static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source) 96654ceac45SDavid Howells { 96754ceac45SDavid Howells target->flags = source->flags; 968356e76b8SChuck Lever target->rsize = source->rsize; 969356e76b8SChuck Lever target->wsize = source->wsize; 97054ceac45SDavid Howells target->acregmin = source->acregmin; 97154ceac45SDavid Howells target->acregmax = source->acregmax; 97254ceac45SDavid Howells target->acdirmin = source->acdirmin; 97354ceac45SDavid Howells target->acdirmax = source->acdirmax; 97454ceac45SDavid Howells target->caps = source->caps; 9752df54806SDavid Howells target->options = source->options; 97654ceac45SDavid Howells } 97754ceac45SDavid Howells 97854ceac45SDavid Howells /* 97954ceac45SDavid Howells * Allocate and initialise a server record 98054ceac45SDavid Howells */ 98154ceac45SDavid Howells static struct nfs_server *nfs_alloc_server(void) 98254ceac45SDavid Howells { 98354ceac45SDavid Howells struct nfs_server *server; 98454ceac45SDavid Howells 98554ceac45SDavid Howells server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL); 98654ceac45SDavid Howells if (!server) 98754ceac45SDavid Howells return NULL; 98854ceac45SDavid Howells 98954ceac45SDavid Howells server->client = server->client_acl = ERR_PTR(-EINVAL); 99054ceac45SDavid Howells 99154ceac45SDavid Howells /* Zero out the NFS state stuff */ 99254ceac45SDavid Howells INIT_LIST_HEAD(&server->client_link); 99354ceac45SDavid Howells INIT_LIST_HEAD(&server->master_link); 99454ceac45SDavid Howells 995ef818a28SSteve Dickson atomic_set(&server->active, 0); 996ef818a28SSteve Dickson 99754ceac45SDavid Howells server->io_stats = nfs_alloc_iostats(); 99854ceac45SDavid Howells if (!server->io_stats) { 99954ceac45SDavid Howells kfree(server); 100054ceac45SDavid Howells return NULL; 100154ceac45SDavid Howells } 100254ceac45SDavid Howells 100348d07649SJens Axboe if (bdi_init(&server->backing_dev_info)) { 100448d07649SJens Axboe nfs_free_iostats(server->io_stats); 100548d07649SJens Axboe kfree(server); 100648d07649SJens Axboe return NULL; 100748d07649SJens Axboe } 100848d07649SJens Axboe 100954ceac45SDavid Howells return server; 101054ceac45SDavid Howells } 101154ceac45SDavid Howells 101254ceac45SDavid Howells /* 101354ceac45SDavid Howells * Free up a server record 101454ceac45SDavid Howells */ 101554ceac45SDavid Howells void nfs_free_server(struct nfs_server *server) 101654ceac45SDavid Howells { 101754ceac45SDavid Howells dprintk("--> nfs_free_server()\n"); 101854ceac45SDavid Howells 101954ceac45SDavid Howells spin_lock(&nfs_client_lock); 102054ceac45SDavid Howells list_del(&server->client_link); 102154ceac45SDavid Howells list_del(&server->master_link); 102254ceac45SDavid Howells spin_unlock(&nfs_client_lock); 102354ceac45SDavid Howells 102454ceac45SDavid Howells if (server->destroy != NULL) 102554ceac45SDavid Howells server->destroy(server); 10265cef338bSTrond Myklebust 10275cef338bSTrond Myklebust if (!IS_ERR(server->client_acl)) 10285cef338bSTrond Myklebust rpc_shutdown_client(server->client_acl); 102954ceac45SDavid Howells if (!IS_ERR(server->client)) 103054ceac45SDavid Howells rpc_shutdown_client(server->client); 103154ceac45SDavid Howells 103254ceac45SDavid Howells nfs_put_client(server->nfs_client); 103354ceac45SDavid Howells 103454ceac45SDavid Howells nfs_free_iostats(server->io_stats); 1035e0bf68ddSPeter Zijlstra bdi_destroy(&server->backing_dev_info); 103654ceac45SDavid Howells kfree(server); 103754ceac45SDavid Howells nfs_release_automount_timer(); 103854ceac45SDavid Howells dprintk("<-- nfs_free_server()\n"); 103954ceac45SDavid Howells } 104054ceac45SDavid Howells 104154ceac45SDavid Howells /* 104254ceac45SDavid Howells * Create a version 2 or 3 volume record 104354ceac45SDavid Howells * - keyed on server and FSID 104454ceac45SDavid Howells */ 10452283f8d6S\"Talpey, Thomas\ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, 104654ceac45SDavid Howells struct nfs_fh *mntfh) 104754ceac45SDavid Howells { 104854ceac45SDavid Howells struct nfs_server *server; 1049*fbca779aSTrond Myklebust struct nfs_fattr *fattr; 105054ceac45SDavid Howells int error; 105154ceac45SDavid Howells 105254ceac45SDavid Howells server = nfs_alloc_server(); 105354ceac45SDavid Howells if (!server) 105454ceac45SDavid Howells return ERR_PTR(-ENOMEM); 105554ceac45SDavid Howells 1056*fbca779aSTrond Myklebust error = -ENOMEM; 1057*fbca779aSTrond Myklebust fattr = nfs_alloc_fattr(); 1058*fbca779aSTrond Myklebust if (fattr == NULL) 1059*fbca779aSTrond Myklebust goto error; 1060*fbca779aSTrond Myklebust 106154ceac45SDavid Howells /* Get a client representation */ 106254ceac45SDavid Howells error = nfs_init_server(server, data); 106354ceac45SDavid Howells if (error < 0) 106454ceac45SDavid Howells goto error; 106554ceac45SDavid Howells 106654ceac45SDavid Howells BUG_ON(!server->nfs_client); 106754ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops); 106854ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 106954ceac45SDavid Howells 107054ceac45SDavid Howells /* Probe the root fh to retrieve its FSID */ 1071*fbca779aSTrond Myklebust error = nfs_probe_fsinfo(server, mntfh, fattr); 107254ceac45SDavid Howells if (error < 0) 107354ceac45SDavid Howells goto error; 107454af3bb5STrond Myklebust if (server->nfs_client->rpc_ops->version == 3) { 107554af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) 107654af3bb5STrond Myklebust server->namelen = NFS3_MAXNAMLEN; 107754af3bb5STrond Myklebust if (!(data->flags & NFS_MOUNT_NORDIRPLUS)) 107854af3bb5STrond Myklebust server->caps |= NFS_CAP_READDIRPLUS; 107954af3bb5STrond Myklebust } else { 108054af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN) 108154af3bb5STrond Myklebust server->namelen = NFS2_MAXNAMLEN; 108254af3bb5STrond Myklebust } 108354af3bb5STrond Myklebust 1084*fbca779aSTrond Myklebust if (!(fattr->valid & NFS_ATTR_FATTR)) { 1085*fbca779aSTrond Myklebust error = server->nfs_client->rpc_ops->getattr(server, mntfh, fattr); 108654ceac45SDavid Howells if (error < 0) { 108754ceac45SDavid Howells dprintk("nfs_create_server: getattr error = %d\n", -error); 108854ceac45SDavid Howells goto error; 108954ceac45SDavid Howells } 109054ceac45SDavid Howells } 1091*fbca779aSTrond Myklebust memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid)); 109254ceac45SDavid Howells 10936daabf1bSDavid Howells dprintk("Server FSID: %llx:%llx\n", 10946daabf1bSDavid Howells (unsigned long long) server->fsid.major, 10956daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 109654ceac45SDavid Howells 109754ceac45SDavid Howells spin_lock(&nfs_client_lock); 109854ceac45SDavid Howells list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); 109954ceac45SDavid Howells list_add_tail(&server->master_link, &nfs_volume_list); 110054ceac45SDavid Howells spin_unlock(&nfs_client_lock); 110154ceac45SDavid Howells 110254ceac45SDavid Howells server->mount_time = jiffies; 1103*fbca779aSTrond Myklebust nfs_free_fattr(fattr); 110454ceac45SDavid Howells return server; 110554ceac45SDavid Howells 110654ceac45SDavid Howells error: 1107*fbca779aSTrond Myklebust nfs_free_fattr(fattr); 110854ceac45SDavid Howells nfs_free_server(server); 110954ceac45SDavid Howells return ERR_PTR(error); 111054ceac45SDavid Howells } 111154ceac45SDavid Howells 111254ceac45SDavid Howells #ifdef CONFIG_NFS_V4 111354ceac45SDavid Howells /* 11149bdaa86dSBenny Halevy * Initialize the NFS4 callback service 11159bdaa86dSBenny Halevy */ 11169bdaa86dSBenny Halevy static int nfs4_init_callback(struct nfs_client *clp) 11179bdaa86dSBenny Halevy { 11189bdaa86dSBenny Halevy int error; 11199bdaa86dSBenny Halevy 11209bdaa86dSBenny Halevy if (clp->rpc_ops->version == 4) { 11210b5b7ae0SAndy Adamson if (nfs4_has_session(clp)) { 11220b5b7ae0SAndy Adamson error = xprt_setup_backchannel( 11230b5b7ae0SAndy Adamson clp->cl_rpcclient->cl_xprt, 11240b5b7ae0SAndy Adamson NFS41_BC_MIN_CALLBACKS); 11250b5b7ae0SAndy Adamson if (error < 0) 11260b5b7ae0SAndy Adamson return error; 11270b5b7ae0SAndy Adamson } 11280b5b7ae0SAndy Adamson 112971468513SBenny Halevy error = nfs_callback_up(clp->cl_minorversion, 113071468513SBenny Halevy clp->cl_rpcclient->cl_xprt); 11319bdaa86dSBenny Halevy if (error < 0) { 11329bdaa86dSBenny Halevy dprintk("%s: failed to start callback. Error = %d\n", 11339bdaa86dSBenny Halevy __func__, error); 11349bdaa86dSBenny Halevy return error; 11359bdaa86dSBenny Halevy } 11369bdaa86dSBenny Halevy __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state); 11379bdaa86dSBenny Halevy } 11389bdaa86dSBenny Halevy return 0; 11399bdaa86dSBenny Halevy } 11409bdaa86dSBenny Halevy 11419bdaa86dSBenny Halevy /* 1142557134a3SAndy Adamson * Initialize the minor version specific parts of an NFS4 client record 1143557134a3SAndy Adamson */ 1144557134a3SAndy Adamson static int nfs4_init_client_minor_version(struct nfs_client *clp) 1145557134a3SAndy Adamson { 1146cccef3b9SAndy Adamson clp->cl_call_sync = _nfs4_call_sync; 1147cccef3b9SAndy Adamson 1148557134a3SAndy Adamson #if defined(CONFIG_NFS_V4_1) 1149557134a3SAndy Adamson if (clp->cl_minorversion) { 1150557134a3SAndy Adamson struct nfs4_session *session = NULL; 1151557134a3SAndy Adamson /* 1152557134a3SAndy Adamson * Create the session and mark it expired. 1153557134a3SAndy Adamson * When a SEQUENCE operation encounters the expired session 1154557134a3SAndy Adamson * it will do session recovery to initialize it. 1155557134a3SAndy Adamson */ 1156557134a3SAndy Adamson session = nfs4_alloc_session(clp); 1157557134a3SAndy Adamson if (!session) 1158557134a3SAndy Adamson return -ENOMEM; 1159557134a3SAndy Adamson 1160557134a3SAndy Adamson clp->cl_session = session; 1161cccef3b9SAndy Adamson clp->cl_call_sync = _nfs4_call_sync_session; 1162557134a3SAndy Adamson } 1163557134a3SAndy Adamson #endif /* CONFIG_NFS_V4_1 */ 1164557134a3SAndy Adamson 116571468513SBenny Halevy return nfs4_init_callback(clp); 1166557134a3SAndy Adamson } 1167557134a3SAndy Adamson 1168557134a3SAndy Adamson /* 116954ceac45SDavid Howells * Initialise an NFS4 client record 117054ceac45SDavid Howells */ 117154ceac45SDavid Howells static int nfs4_init_client(struct nfs_client *clp, 117233170233STrond Myklebust const struct rpc_timeout *timeparms, 11737d9ac06fSJ. Bruce Fields const char *ip_addr, 1174d740351bSChuck Lever rpc_authflavor_t authflavour, 1175d740351bSChuck Lever int flags) 117654ceac45SDavid Howells { 117754ceac45SDavid Howells int error; 117854ceac45SDavid Howells 117954ceac45SDavid Howells if (clp->cl_cons_state == NFS_CS_READY) { 118054ceac45SDavid Howells /* the client is initialised already */ 118154ceac45SDavid Howells dprintk("<-- nfs4_init_client() = 0 [already %p]\n", clp); 118254ceac45SDavid Howells return 0; 118354ceac45SDavid Howells } 118454ceac45SDavid Howells 118554ceac45SDavid Howells /* Check NFS protocol revision and initialize RPC op vector */ 118654ceac45SDavid Howells clp->rpc_ops = &nfs_v4_clientops; 118754ceac45SDavid Howells 118859dca3b2STrond Myklebust error = nfs_create_rpc_client(clp, timeparms, authflavour, 1189d740351bSChuck Lever 1, flags & NFS_MOUNT_NORESVPORT); 119054ceac45SDavid Howells if (error < 0) 119154ceac45SDavid Howells goto error; 1192f4373bf9SBen Hutchings strlcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr)); 119354ceac45SDavid Howells 119454ceac45SDavid Howells error = nfs_idmap_new(clp); 119554ceac45SDavid Howells if (error < 0) { 119654ceac45SDavid Howells dprintk("%s: failed to create idmapper. Error = %d\n", 11973110ff80SHarvey Harrison __func__, error); 119854ceac45SDavid Howells goto error; 119954ceac45SDavid Howells } 12009c5bf38dSTrond Myklebust __set_bit(NFS_CS_IDMAP, &clp->cl_res_state); 120154ceac45SDavid Howells 1202557134a3SAndy Adamson error = nfs4_init_client_minor_version(clp); 1203557134a3SAndy Adamson if (error < 0) 1204557134a3SAndy Adamson goto error; 1205557134a3SAndy Adamson 120676db6d95SAndy Adamson if (!nfs4_has_session(clp)) 120754ceac45SDavid Howells nfs_mark_client_ready(clp, NFS_CS_READY); 120854ceac45SDavid Howells return 0; 120954ceac45SDavid Howells 121054ceac45SDavid Howells error: 121154ceac45SDavid Howells nfs_mark_client_ready(clp, error); 121254ceac45SDavid Howells dprintk("<-- nfs4_init_client() = xerror %d\n", error); 121354ceac45SDavid Howells return error; 121454ceac45SDavid Howells } 121554ceac45SDavid Howells 121654ceac45SDavid Howells /* 121754ceac45SDavid Howells * Set up an NFS4 client 121854ceac45SDavid Howells */ 121954ceac45SDavid Howells static int nfs4_set_client(struct nfs_server *server, 1220dcecae0fSChuck Lever const char *hostname, 1221dcecae0fSChuck Lever const struct sockaddr *addr, 1222dcecae0fSChuck Lever const size_t addrlen, 12237d9ac06fSJ. Bruce Fields const char *ip_addr, 122454ceac45SDavid Howells rpc_authflavor_t authflavour, 122594a417f3SBenny Halevy int proto, const struct rpc_timeout *timeparms, 122694a417f3SBenny Halevy u32 minorversion) 122754ceac45SDavid Howells { 12283a498026STrond Myklebust struct nfs_client_initdata cl_init = { 12293a498026STrond Myklebust .hostname = hostname, 1230dcecae0fSChuck Lever .addr = addr, 1231dcecae0fSChuck Lever .addrlen = addrlen, 123240c55319STrond Myklebust .rpc_ops = &nfs_v4_clientops, 123359dca3b2STrond Myklebust .proto = proto, 12345aae4a9aSBenny Halevy .minorversion = minorversion, 12353a498026STrond Myklebust }; 123654ceac45SDavid Howells struct nfs_client *clp; 123754ceac45SDavid Howells int error; 123854ceac45SDavid Howells 123954ceac45SDavid Howells dprintk("--> nfs4_set_client()\n"); 124054ceac45SDavid Howells 124154ceac45SDavid Howells /* Allocate or find a client reference we can use */ 12423a498026STrond Myklebust clp = nfs_get_client(&cl_init); 124354ceac45SDavid Howells if (IS_ERR(clp)) { 124454ceac45SDavid Howells error = PTR_ERR(clp); 124554ceac45SDavid Howells goto error; 124654ceac45SDavid Howells } 1247d740351bSChuck Lever error = nfs4_init_client(clp, timeparms, ip_addr, authflavour, 1248d740351bSChuck Lever server->flags); 124954ceac45SDavid Howells if (error < 0) 125054ceac45SDavid Howells goto error_put; 125154ceac45SDavid Howells 125254ceac45SDavid Howells server->nfs_client = clp; 125354ceac45SDavid Howells dprintk("<-- nfs4_set_client() = 0 [new %p]\n", clp); 125454ceac45SDavid Howells return 0; 125554ceac45SDavid Howells 125654ceac45SDavid Howells error_put: 125754ceac45SDavid Howells nfs_put_client(clp); 125854ceac45SDavid Howells error: 125954ceac45SDavid Howells dprintk("<-- nfs4_set_client() = xerror %d\n", error); 126054ceac45SDavid Howells return error; 126154ceac45SDavid Howells } 126254ceac45SDavid Howells 1263557134a3SAndy Adamson 1264557134a3SAndy Adamson /* 126596b09e02SAndy Adamson * Session has been established, and the client marked ready. 126696b09e02SAndy Adamson * Set the mount rsize and wsize with negotiated fore channel 126796b09e02SAndy Adamson * attributes which will be bound checked in nfs_server_set_fsinfo. 126896b09e02SAndy Adamson */ 126996b09e02SAndy Adamson static void nfs4_session_set_rwsize(struct nfs_server *server) 127096b09e02SAndy Adamson { 127196b09e02SAndy Adamson #ifdef CONFIG_NFS_V4_1 12722449ea2eSAlexandros Batsakis struct nfs4_session *sess; 12732449ea2eSAlexandros Batsakis u32 server_resp_sz; 12742449ea2eSAlexandros Batsakis u32 server_rqst_sz; 12752449ea2eSAlexandros Batsakis 127696b09e02SAndy Adamson if (!nfs4_has_session(server->nfs_client)) 127796b09e02SAndy Adamson return; 12782449ea2eSAlexandros Batsakis sess = server->nfs_client->cl_session; 12792449ea2eSAlexandros Batsakis server_resp_sz = sess->fc_attrs.max_resp_sz - nfs41_maxread_overhead; 12802449ea2eSAlexandros Batsakis server_rqst_sz = sess->fc_attrs.max_rqst_sz - nfs41_maxwrite_overhead; 12812449ea2eSAlexandros Batsakis 12822449ea2eSAlexandros Batsakis if (server->rsize > server_resp_sz) 12832449ea2eSAlexandros Batsakis server->rsize = server_resp_sz; 12842449ea2eSAlexandros Batsakis if (server->wsize > server_rqst_sz) 12852449ea2eSAlexandros Batsakis server->wsize = server_rqst_sz; 128696b09e02SAndy Adamson #endif /* CONFIG_NFS_V4_1 */ 128796b09e02SAndy Adamson } 128896b09e02SAndy Adamson 128996b09e02SAndy Adamson /* 129054ceac45SDavid Howells * Create a version 4 volume record 129154ceac45SDavid Howells */ 129254ceac45SDavid Howells static int nfs4_init_server(struct nfs_server *server, 129391ea40b9S\"Talpey, Thomas\ const struct nfs_parsed_mount_data *data) 129454ceac45SDavid Howells { 129533170233STrond Myklebust struct rpc_timeout timeparms; 129654ceac45SDavid Howells int error; 129754ceac45SDavid Howells 129854ceac45SDavid Howells dprintk("--> nfs4_init_server()\n"); 129954ceac45SDavid Howells 130033170233STrond Myklebust nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, 130133170233STrond Myklebust data->timeo, data->retrans); 130233170233STrond Myklebust 1303542fcc33SChuck Lever /* Initialise the client representation from the mount data */ 1304542fcc33SChuck Lever server->flags = data->flags; 13050df5dd4aSTrond Myklebust server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR| 13060df5dd4aSTrond Myklebust NFS_CAP_POSIX_LOCK; 1307b797cac7SDavid Howells server->options = data->options; 1308542fcc33SChuck Lever 130933170233STrond Myklebust /* Get a client record */ 131033170233STrond Myklebust error = nfs4_set_client(server, 131133170233STrond Myklebust data->nfs_server.hostname, 131233170233STrond Myklebust (const struct sockaddr *)&data->nfs_server.address, 131333170233STrond Myklebust data->nfs_server.addrlen, 131433170233STrond Myklebust data->client_address, 131533170233STrond Myklebust data->auth_flavors[0], 131633170233STrond Myklebust data->nfs_server.protocol, 131794a417f3SBenny Halevy &timeparms, 131894a417f3SBenny Halevy data->minorversion); 131933170233STrond Myklebust if (error < 0) 132033170233STrond Myklebust goto error; 132133170233STrond Myklebust 132254ceac45SDavid Howells if (data->rsize) 132354ceac45SDavid Howells server->rsize = nfs_block_size(data->rsize, NULL); 132454ceac45SDavid Howells if (data->wsize) 132554ceac45SDavid Howells server->wsize = nfs_block_size(data->wsize, NULL); 132654ceac45SDavid Howells 132754ceac45SDavid Howells server->acregmin = data->acregmin * HZ; 132854ceac45SDavid Howells server->acregmax = data->acregmax * HZ; 132954ceac45SDavid Howells server->acdirmin = data->acdirmin * HZ; 133054ceac45SDavid Howells server->acdirmax = data->acdirmax * HZ; 133154ceac45SDavid Howells 1332f22d6d79SChuck Lever server->port = data->nfs_server.port; 1333f22d6d79SChuck Lever 133433170233STrond Myklebust error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]); 133554ceac45SDavid Howells 133633170233STrond Myklebust error: 133754ceac45SDavid Howells /* Done */ 133854ceac45SDavid Howells dprintk("<-- nfs4_init_server() = %d\n", error); 133954ceac45SDavid Howells return error; 134054ceac45SDavid Howells } 134154ceac45SDavid Howells 134254ceac45SDavid Howells /* 134354ceac45SDavid Howells * Create a version 4 volume record 134454ceac45SDavid Howells * - keyed on server and FSID 134554ceac45SDavid Howells */ 134691ea40b9S\"Talpey, Thomas\ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, 134754ceac45SDavid Howells struct nfs_fh *mntfh) 134854ceac45SDavid Howells { 1349*fbca779aSTrond Myklebust struct nfs_fattr *fattr; 135054ceac45SDavid Howells struct nfs_server *server; 135154ceac45SDavid Howells int error; 135254ceac45SDavid Howells 135354ceac45SDavid Howells dprintk("--> nfs4_create_server()\n"); 135454ceac45SDavid Howells 135554ceac45SDavid Howells server = nfs_alloc_server(); 135654ceac45SDavid Howells if (!server) 135754ceac45SDavid Howells return ERR_PTR(-ENOMEM); 135854ceac45SDavid Howells 1359*fbca779aSTrond Myklebust error = -ENOMEM; 1360*fbca779aSTrond Myklebust fattr = nfs_alloc_fattr(); 1361*fbca779aSTrond Myklebust if (fattr == NULL) 1362*fbca779aSTrond Myklebust goto error; 1363*fbca779aSTrond Myklebust 136454ceac45SDavid Howells /* set up the general RPC client */ 136591ea40b9S\"Talpey, Thomas\ error = nfs4_init_server(server, data); 136654ceac45SDavid Howells if (error < 0) 136754ceac45SDavid Howells goto error; 136854ceac45SDavid Howells 136954ceac45SDavid Howells BUG_ON(!server->nfs_client); 137054ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops); 137154ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 137254ceac45SDavid Howells 1373fccba804STrond Myklebust error = nfs4_init_session(server); 1374fccba804STrond Myklebust if (error < 0) 1375fccba804STrond Myklebust goto error; 1376557134a3SAndy Adamson 137754ceac45SDavid Howells /* Probe the root fh to retrieve its FSID */ 1378815409d2STrond Myklebust error = nfs4_get_rootfh(server, mntfh); 137954ceac45SDavid Howells if (error < 0) 138054ceac45SDavid Howells goto error; 138154ceac45SDavid Howells 13826daabf1bSDavid Howells dprintk("Server FSID: %llx:%llx\n", 13836daabf1bSDavid Howells (unsigned long long) server->fsid.major, 13846daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 138554ceac45SDavid Howells dprintk("Mount FH: %d\n", mntfh->size); 138654ceac45SDavid Howells 138796b09e02SAndy Adamson nfs4_session_set_rwsize(server); 138896b09e02SAndy Adamson 1389*fbca779aSTrond Myklebust error = nfs_probe_fsinfo(server, mntfh, fattr); 139054ceac45SDavid Howells if (error < 0) 139154ceac45SDavid Howells goto error; 139254ceac45SDavid Howells 139354af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 139454af3bb5STrond Myklebust server->namelen = NFS4_MAXNAMLEN; 139554af3bb5STrond Myklebust 139654ceac45SDavid Howells spin_lock(&nfs_client_lock); 139754ceac45SDavid Howells list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); 139854ceac45SDavid Howells list_add_tail(&server->master_link, &nfs_volume_list); 139954ceac45SDavid Howells spin_unlock(&nfs_client_lock); 140054ceac45SDavid Howells 140154ceac45SDavid Howells server->mount_time = jiffies; 140254ceac45SDavid Howells dprintk("<-- nfs4_create_server() = %p\n", server); 1403*fbca779aSTrond Myklebust nfs_free_fattr(fattr); 140454ceac45SDavid Howells return server; 140554ceac45SDavid Howells 140654ceac45SDavid Howells error: 1407*fbca779aSTrond Myklebust nfs_free_fattr(fattr); 140854ceac45SDavid Howells nfs_free_server(server); 140954ceac45SDavid Howells dprintk("<-- nfs4_create_server() = error %d\n", error); 141054ceac45SDavid Howells return ERR_PTR(error); 141154ceac45SDavid Howells } 141254ceac45SDavid Howells 141354ceac45SDavid Howells /* 141454ceac45SDavid Howells * Create an NFS4 referral server record 141554ceac45SDavid Howells */ 141654ceac45SDavid Howells struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, 1417f2d0d85eSTrond Myklebust struct nfs_fh *mntfh) 141854ceac45SDavid Howells { 141954ceac45SDavid Howells struct nfs_client *parent_client; 142054ceac45SDavid Howells struct nfs_server *server, *parent_server; 1421*fbca779aSTrond Myklebust struct nfs_fattr *fattr; 142254ceac45SDavid Howells int error; 142354ceac45SDavid Howells 142454ceac45SDavid Howells dprintk("--> nfs4_create_referral_server()\n"); 142554ceac45SDavid Howells 142654ceac45SDavid Howells server = nfs_alloc_server(); 142754ceac45SDavid Howells if (!server) 142854ceac45SDavid Howells return ERR_PTR(-ENOMEM); 142954ceac45SDavid Howells 1430*fbca779aSTrond Myklebust error = -ENOMEM; 1431*fbca779aSTrond Myklebust fattr = nfs_alloc_fattr(); 1432*fbca779aSTrond Myklebust if (fattr == NULL) 1433*fbca779aSTrond Myklebust goto error; 1434*fbca779aSTrond Myklebust 143554ceac45SDavid Howells parent_server = NFS_SB(data->sb); 143654ceac45SDavid Howells parent_client = parent_server->nfs_client; 143754ceac45SDavid Howells 1438542fcc33SChuck Lever /* Initialise the client representation from the parent server */ 1439542fcc33SChuck Lever nfs_server_copy_userdata(server, parent_server); 144062ab460cSTrond Myklebust server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR; 1441542fcc33SChuck Lever 144254ceac45SDavid Howells /* Get a client representation. 144354ceac45SDavid Howells * Note: NFSv4 always uses TCP, */ 1444dcecae0fSChuck Lever error = nfs4_set_client(server, data->hostname, 14456677d095SChuck Lever data->addr, 14466677d095SChuck Lever data->addrlen, 14477d9ac06fSJ. Bruce Fields parent_client->cl_ipaddr, 144854ceac45SDavid Howells data->authflavor, 144954ceac45SDavid Howells parent_server->client->cl_xprt->prot, 145094a417f3SBenny Halevy parent_server->client->cl_timeout, 145194a417f3SBenny Halevy parent_client->cl_minorversion); 1452297de4f6Sandros@citi.umich.edu if (error < 0) 1453297de4f6Sandros@citi.umich.edu goto error; 145454ceac45SDavid Howells 145533170233STrond Myklebust error = nfs_init_server_rpcclient(server, parent_server->client->cl_timeout, data->authflavor); 145654ceac45SDavid Howells if (error < 0) 145754ceac45SDavid Howells goto error; 145854ceac45SDavid Howells 145954ceac45SDavid Howells BUG_ON(!server->nfs_client); 146054ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops); 146154ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 146254ceac45SDavid Howells 1463f2d0d85eSTrond Myklebust /* Probe the root fh to retrieve its FSID and filehandle */ 1464815409d2STrond Myklebust error = nfs4_get_rootfh(server, mntfh); 1465f2d0d85eSTrond Myklebust if (error < 0) 1466f2d0d85eSTrond Myklebust goto error; 1467f2d0d85eSTrond Myklebust 146854ceac45SDavid Howells /* probe the filesystem info for this server filesystem */ 1469*fbca779aSTrond Myklebust error = nfs_probe_fsinfo(server, mntfh, fattr); 147054ceac45SDavid Howells if (error < 0) 147154ceac45SDavid Howells goto error; 147254ceac45SDavid Howells 147354af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 147454af3bb5STrond Myklebust server->namelen = NFS4_MAXNAMLEN; 147554af3bb5STrond Myklebust 147654ceac45SDavid Howells dprintk("Referral FSID: %llx:%llx\n", 14776daabf1bSDavid Howells (unsigned long long) server->fsid.major, 14786daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 147954ceac45SDavid Howells 148054ceac45SDavid Howells spin_lock(&nfs_client_lock); 148154ceac45SDavid Howells list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); 148254ceac45SDavid Howells list_add_tail(&server->master_link, &nfs_volume_list); 148354ceac45SDavid Howells spin_unlock(&nfs_client_lock); 148454ceac45SDavid Howells 148554ceac45SDavid Howells server->mount_time = jiffies; 148654ceac45SDavid Howells 1487*fbca779aSTrond Myklebust nfs_free_fattr(fattr); 148854ceac45SDavid Howells dprintk("<-- nfs_create_referral_server() = %p\n", server); 148954ceac45SDavid Howells return server; 149054ceac45SDavid Howells 149154ceac45SDavid Howells error: 1492*fbca779aSTrond Myklebust nfs_free_fattr(fattr); 149354ceac45SDavid Howells nfs_free_server(server); 149454ceac45SDavid Howells dprintk("<-- nfs4_create_referral_server() = error %d\n", error); 149554ceac45SDavid Howells return ERR_PTR(error); 149654ceac45SDavid Howells } 149754ceac45SDavid Howells 149854ceac45SDavid Howells #endif /* CONFIG_NFS_V4 */ 149954ceac45SDavid Howells 150054ceac45SDavid Howells /* 150154ceac45SDavid Howells * Clone an NFS2, NFS3 or NFS4 server record 150254ceac45SDavid Howells */ 150354ceac45SDavid Howells struct nfs_server *nfs_clone_server(struct nfs_server *source, 150454ceac45SDavid Howells struct nfs_fh *fh, 150554ceac45SDavid Howells struct nfs_fattr *fattr) 150654ceac45SDavid Howells { 150754ceac45SDavid Howells struct nfs_server *server; 1508*fbca779aSTrond Myklebust struct nfs_fattr *fattr_fsinfo; 150954ceac45SDavid Howells int error; 151054ceac45SDavid Howells 151154ceac45SDavid Howells dprintk("--> nfs_clone_server(,%llx:%llx,)\n", 15126daabf1bSDavid Howells (unsigned long long) fattr->fsid.major, 15136daabf1bSDavid Howells (unsigned long long) fattr->fsid.minor); 151454ceac45SDavid Howells 151554ceac45SDavid Howells server = nfs_alloc_server(); 151654ceac45SDavid Howells if (!server) 151754ceac45SDavid Howells return ERR_PTR(-ENOMEM); 151854ceac45SDavid Howells 1519*fbca779aSTrond Myklebust error = -ENOMEM; 1520*fbca779aSTrond Myklebust fattr_fsinfo = nfs_alloc_fattr(); 1521*fbca779aSTrond Myklebust if (fattr_fsinfo == NULL) 1522*fbca779aSTrond Myklebust goto out_free_server; 1523*fbca779aSTrond Myklebust 152454ceac45SDavid Howells /* Copy data from the source */ 152554ceac45SDavid Howells server->nfs_client = source->nfs_client; 152654ceac45SDavid Howells atomic_inc(&server->nfs_client->cl_count); 152754ceac45SDavid Howells nfs_server_copy_userdata(server, source); 152854ceac45SDavid Howells 152954ceac45SDavid Howells server->fsid = fattr->fsid; 153054ceac45SDavid Howells 153133170233STrond Myklebust error = nfs_init_server_rpcclient(server, 153233170233STrond Myklebust source->client->cl_timeout, 153333170233STrond Myklebust source->client->cl_auth->au_flavor); 153454ceac45SDavid Howells if (error < 0) 153554ceac45SDavid Howells goto out_free_server; 153654ceac45SDavid Howells if (!IS_ERR(source->client_acl)) 153754ceac45SDavid Howells nfs_init_server_aclclient(server); 153854ceac45SDavid Howells 153954ceac45SDavid Howells /* probe the filesystem info for this server filesystem */ 1540*fbca779aSTrond Myklebust error = nfs_probe_fsinfo(server, fh, fattr_fsinfo); 154154ceac45SDavid Howells if (error < 0) 154254ceac45SDavid Howells goto out_free_server; 154354ceac45SDavid Howells 154454af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 154554af3bb5STrond Myklebust server->namelen = NFS4_MAXNAMLEN; 154654af3bb5STrond Myklebust 154754ceac45SDavid Howells dprintk("Cloned FSID: %llx:%llx\n", 15486daabf1bSDavid Howells (unsigned long long) server->fsid.major, 15496daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 155054ceac45SDavid Howells 155154ceac45SDavid Howells error = nfs_start_lockd(server); 155254ceac45SDavid Howells if (error < 0) 155354ceac45SDavid Howells goto out_free_server; 155454ceac45SDavid Howells 155554ceac45SDavid Howells spin_lock(&nfs_client_lock); 155654ceac45SDavid Howells list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); 155754ceac45SDavid Howells list_add_tail(&server->master_link, &nfs_volume_list); 155854ceac45SDavid Howells spin_unlock(&nfs_client_lock); 155954ceac45SDavid Howells 156054ceac45SDavid Howells server->mount_time = jiffies; 156154ceac45SDavid Howells 1562*fbca779aSTrond Myklebust nfs_free_fattr(fattr_fsinfo); 156354ceac45SDavid Howells dprintk("<-- nfs_clone_server() = %p\n", server); 156454ceac45SDavid Howells return server; 156554ceac45SDavid Howells 156654ceac45SDavid Howells out_free_server: 1567*fbca779aSTrond Myklebust nfs_free_fattr(fattr_fsinfo); 156854ceac45SDavid Howells nfs_free_server(server); 156954ceac45SDavid Howells dprintk("<-- nfs_clone_server() = error %d\n", error); 157054ceac45SDavid Howells return ERR_PTR(error); 157154ceac45SDavid Howells } 15726aaca566SDavid Howells 15736aaca566SDavid Howells #ifdef CONFIG_PROC_FS 15746aaca566SDavid Howells static struct proc_dir_entry *proc_fs_nfs; 15756aaca566SDavid Howells 15766aaca566SDavid Howells static int nfs_server_list_open(struct inode *inode, struct file *file); 15776aaca566SDavid Howells static void *nfs_server_list_start(struct seq_file *p, loff_t *pos); 15786aaca566SDavid Howells static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos); 15796aaca566SDavid Howells static void nfs_server_list_stop(struct seq_file *p, void *v); 15806aaca566SDavid Howells static int nfs_server_list_show(struct seq_file *m, void *v); 15816aaca566SDavid Howells 158288e9d34cSJames Morris static const struct seq_operations nfs_server_list_ops = { 15836aaca566SDavid Howells .start = nfs_server_list_start, 15846aaca566SDavid Howells .next = nfs_server_list_next, 15856aaca566SDavid Howells .stop = nfs_server_list_stop, 15866aaca566SDavid Howells .show = nfs_server_list_show, 15876aaca566SDavid Howells }; 15886aaca566SDavid Howells 158900977a59SArjan van de Ven static const struct file_operations nfs_server_list_fops = { 15906aaca566SDavid Howells .open = nfs_server_list_open, 15916aaca566SDavid Howells .read = seq_read, 15926aaca566SDavid Howells .llseek = seq_lseek, 15936aaca566SDavid Howells .release = seq_release, 159434b37235SDenis V. Lunev .owner = THIS_MODULE, 15956aaca566SDavid Howells }; 15966aaca566SDavid Howells 15976aaca566SDavid Howells static int nfs_volume_list_open(struct inode *inode, struct file *file); 15986aaca566SDavid Howells static void *nfs_volume_list_start(struct seq_file *p, loff_t *pos); 15996aaca566SDavid Howells static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos); 16006aaca566SDavid Howells static void nfs_volume_list_stop(struct seq_file *p, void *v); 16016aaca566SDavid Howells static int nfs_volume_list_show(struct seq_file *m, void *v); 16026aaca566SDavid Howells 160388e9d34cSJames Morris static const struct seq_operations nfs_volume_list_ops = { 16046aaca566SDavid Howells .start = nfs_volume_list_start, 16056aaca566SDavid Howells .next = nfs_volume_list_next, 16066aaca566SDavid Howells .stop = nfs_volume_list_stop, 16076aaca566SDavid Howells .show = nfs_volume_list_show, 16086aaca566SDavid Howells }; 16096aaca566SDavid Howells 161000977a59SArjan van de Ven static const struct file_operations nfs_volume_list_fops = { 16116aaca566SDavid Howells .open = nfs_volume_list_open, 16126aaca566SDavid Howells .read = seq_read, 16136aaca566SDavid Howells .llseek = seq_lseek, 16146aaca566SDavid Howells .release = seq_release, 161534b37235SDenis V. Lunev .owner = THIS_MODULE, 16166aaca566SDavid Howells }; 16176aaca566SDavid Howells 16186aaca566SDavid Howells /* 16196aaca566SDavid Howells * open "/proc/fs/nfsfs/servers" which provides a summary of servers with which 16206aaca566SDavid Howells * we're dealing 16216aaca566SDavid Howells */ 16226aaca566SDavid Howells static int nfs_server_list_open(struct inode *inode, struct file *file) 16236aaca566SDavid Howells { 16246aaca566SDavid Howells struct seq_file *m; 16256aaca566SDavid Howells int ret; 16266aaca566SDavid Howells 16276aaca566SDavid Howells ret = seq_open(file, &nfs_server_list_ops); 16286aaca566SDavid Howells if (ret < 0) 16296aaca566SDavid Howells return ret; 16306aaca566SDavid Howells 16316aaca566SDavid Howells m = file->private_data; 16326aaca566SDavid Howells m->private = PDE(inode)->data; 16336aaca566SDavid Howells 16346aaca566SDavid Howells return 0; 16356aaca566SDavid Howells } 16366aaca566SDavid Howells 16376aaca566SDavid Howells /* 16386aaca566SDavid Howells * set up the iterator to start reading from the server list and return the first item 16396aaca566SDavid Howells */ 16406aaca566SDavid Howells static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos) 16416aaca566SDavid Howells { 16426aaca566SDavid Howells /* lock the list against modification */ 16436aaca566SDavid Howells spin_lock(&nfs_client_lock); 1644259902eaSPavel Emelianov return seq_list_start_head(&nfs_client_list, *_pos); 16456aaca566SDavid Howells } 16466aaca566SDavid Howells 16476aaca566SDavid Howells /* 16486aaca566SDavid Howells * move to next server 16496aaca566SDavid Howells */ 16506aaca566SDavid Howells static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos) 16516aaca566SDavid Howells { 1652259902eaSPavel Emelianov return seq_list_next(v, &nfs_client_list, pos); 16536aaca566SDavid Howells } 16546aaca566SDavid Howells 16556aaca566SDavid Howells /* 16566aaca566SDavid Howells * clean up after reading from the transports list 16576aaca566SDavid Howells */ 16586aaca566SDavid Howells static void nfs_server_list_stop(struct seq_file *p, void *v) 16596aaca566SDavid Howells { 16606aaca566SDavid Howells spin_unlock(&nfs_client_lock); 16616aaca566SDavid Howells } 16626aaca566SDavid Howells 16636aaca566SDavid Howells /* 16646aaca566SDavid Howells * display a header line followed by a load of call lines 16656aaca566SDavid Howells */ 16666aaca566SDavid Howells static int nfs_server_list_show(struct seq_file *m, void *v) 16676aaca566SDavid Howells { 16686aaca566SDavid Howells struct nfs_client *clp; 16696aaca566SDavid Howells 16706aaca566SDavid Howells /* display header on line 1 */ 1671259902eaSPavel Emelianov if (v == &nfs_client_list) { 16726aaca566SDavid Howells seq_puts(m, "NV SERVER PORT USE HOSTNAME\n"); 16736aaca566SDavid Howells return 0; 16746aaca566SDavid Howells } 16756aaca566SDavid Howells 16766aaca566SDavid Howells /* display one transport per line on subsequent lines */ 16776aaca566SDavid Howells clp = list_entry(v, struct nfs_client, cl_share_link); 16786aaca566SDavid Howells 16795d8515caSChuck Lever seq_printf(m, "v%u %s %s %3d %s\n", 168040c55319STrond Myklebust clp->rpc_ops->version, 16815d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), 16825d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), 16836aaca566SDavid Howells atomic_read(&clp->cl_count), 16846aaca566SDavid Howells clp->cl_hostname); 16856aaca566SDavid Howells 16866aaca566SDavid Howells return 0; 16876aaca566SDavid Howells } 16886aaca566SDavid Howells 16896aaca566SDavid Howells /* 16906aaca566SDavid Howells * open "/proc/fs/nfsfs/volumes" which provides a summary of extant volumes 16916aaca566SDavid Howells */ 16926aaca566SDavid Howells static int nfs_volume_list_open(struct inode *inode, struct file *file) 16936aaca566SDavid Howells { 16946aaca566SDavid Howells struct seq_file *m; 16956aaca566SDavid Howells int ret; 16966aaca566SDavid Howells 16976aaca566SDavid Howells ret = seq_open(file, &nfs_volume_list_ops); 16986aaca566SDavid Howells if (ret < 0) 16996aaca566SDavid Howells return ret; 17006aaca566SDavid Howells 17016aaca566SDavid Howells m = file->private_data; 17026aaca566SDavid Howells m->private = PDE(inode)->data; 17036aaca566SDavid Howells 17046aaca566SDavid Howells return 0; 17056aaca566SDavid Howells } 17066aaca566SDavid Howells 17076aaca566SDavid Howells /* 17086aaca566SDavid Howells * set up the iterator to start reading from the volume list and return the first item 17096aaca566SDavid Howells */ 17106aaca566SDavid Howells static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos) 17116aaca566SDavid Howells { 17126aaca566SDavid Howells /* lock the list against modification */ 17136aaca566SDavid Howells spin_lock(&nfs_client_lock); 1714259902eaSPavel Emelianov return seq_list_start_head(&nfs_volume_list, *_pos); 17156aaca566SDavid Howells } 17166aaca566SDavid Howells 17176aaca566SDavid Howells /* 17186aaca566SDavid Howells * move to next volume 17196aaca566SDavid Howells */ 17206aaca566SDavid Howells static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos) 17216aaca566SDavid Howells { 1722259902eaSPavel Emelianov return seq_list_next(v, &nfs_volume_list, pos); 17236aaca566SDavid Howells } 17246aaca566SDavid Howells 17256aaca566SDavid Howells /* 17266aaca566SDavid Howells * clean up after reading from the transports list 17276aaca566SDavid Howells */ 17286aaca566SDavid Howells static void nfs_volume_list_stop(struct seq_file *p, void *v) 17296aaca566SDavid Howells { 17306aaca566SDavid Howells spin_unlock(&nfs_client_lock); 17316aaca566SDavid Howells } 17326aaca566SDavid Howells 17336aaca566SDavid Howells /* 17346aaca566SDavid Howells * display a header line followed by a load of call lines 17356aaca566SDavid Howells */ 17366aaca566SDavid Howells static int nfs_volume_list_show(struct seq_file *m, void *v) 17376aaca566SDavid Howells { 17386aaca566SDavid Howells struct nfs_server *server; 17396aaca566SDavid Howells struct nfs_client *clp; 17406aaca566SDavid Howells char dev[8], fsid[17]; 17416aaca566SDavid Howells 17426aaca566SDavid Howells /* display header on line 1 */ 1743259902eaSPavel Emelianov if (v == &nfs_volume_list) { 17445d1acff1SDavid Howells seq_puts(m, "NV SERVER PORT DEV FSID FSC\n"); 17456aaca566SDavid Howells return 0; 17466aaca566SDavid Howells } 17476aaca566SDavid Howells /* display one transport per line on subsequent lines */ 17486aaca566SDavid Howells server = list_entry(v, struct nfs_server, master_link); 17496aaca566SDavid Howells clp = server->nfs_client; 17506aaca566SDavid Howells 17516aaca566SDavid Howells snprintf(dev, 8, "%u:%u", 17526aaca566SDavid Howells MAJOR(server->s_dev), MINOR(server->s_dev)); 17536aaca566SDavid Howells 17546aaca566SDavid Howells snprintf(fsid, 17, "%llx:%llx", 17556daabf1bSDavid Howells (unsigned long long) server->fsid.major, 17566daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 17576aaca566SDavid Howells 17585d1acff1SDavid Howells seq_printf(m, "v%u %s %s %-7s %-17s %s\n", 175940c55319STrond Myklebust clp->rpc_ops->version, 17605d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), 17615d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), 17626aaca566SDavid Howells dev, 17635d1acff1SDavid Howells fsid, 17645d1acff1SDavid Howells nfs_server_fscache_state(server)); 17656aaca566SDavid Howells 17666aaca566SDavid Howells return 0; 17676aaca566SDavid Howells } 17686aaca566SDavid Howells 17696aaca566SDavid Howells /* 17706aaca566SDavid Howells * initialise the /proc/fs/nfsfs/ directory 17716aaca566SDavid Howells */ 17726aaca566SDavid Howells int __init nfs_fs_proc_init(void) 17736aaca566SDavid Howells { 17746aaca566SDavid Howells struct proc_dir_entry *p; 17756aaca566SDavid Howells 177636a5aeb8SAlexey Dobriyan proc_fs_nfs = proc_mkdir("fs/nfsfs", NULL); 17776aaca566SDavid Howells if (!proc_fs_nfs) 17786aaca566SDavid Howells goto error_0; 17796aaca566SDavid Howells 17806aaca566SDavid Howells /* a file of servers with which we're dealing */ 178134b37235SDenis V. Lunev p = proc_create("servers", S_IFREG|S_IRUGO, 178234b37235SDenis V. Lunev proc_fs_nfs, &nfs_server_list_fops); 17836aaca566SDavid Howells if (!p) 17846aaca566SDavid Howells goto error_1; 17856aaca566SDavid Howells 17866aaca566SDavid Howells /* a file of volumes that we have mounted */ 178734b37235SDenis V. Lunev p = proc_create("volumes", S_IFREG|S_IRUGO, 178834b37235SDenis V. Lunev proc_fs_nfs, &nfs_volume_list_fops); 17896aaca566SDavid Howells if (!p) 17906aaca566SDavid Howells goto error_2; 17916aaca566SDavid Howells return 0; 17926aaca566SDavid Howells 17936aaca566SDavid Howells error_2: 17946aaca566SDavid Howells remove_proc_entry("servers", proc_fs_nfs); 17956aaca566SDavid Howells error_1: 179636a5aeb8SAlexey Dobriyan remove_proc_entry("fs/nfsfs", NULL); 17976aaca566SDavid Howells error_0: 17986aaca566SDavid Howells return -ENOMEM; 17996aaca566SDavid Howells } 18006aaca566SDavid Howells 18016aaca566SDavid Howells /* 18026aaca566SDavid Howells * clean up the /proc/fs/nfsfs/ directory 18036aaca566SDavid Howells */ 18046aaca566SDavid Howells void nfs_fs_proc_exit(void) 18056aaca566SDavid Howells { 18066aaca566SDavid Howells remove_proc_entry("volumes", proc_fs_nfs); 18076aaca566SDavid Howells remove_proc_entry("servers", proc_fs_nfs); 180836a5aeb8SAlexey Dobriyan remove_proc_entry("fs/nfsfs", NULL); 18096aaca566SDavid Howells } 18106aaca566SDavid Howells 18116aaca566SDavid Howells #endif /* CONFIG_PROC_FS */ 1812