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> 383b0d3f93STrond Myklebust #include <net/ipv6.h> 3924c8dbbbSDavid Howells #include <linux/nfs_xdr.h> 4024c8dbbbSDavid Howells 4124c8dbbbSDavid Howells #include <asm/system.h> 4224c8dbbbSDavid Howells 4324c8dbbbSDavid Howells #include "nfs4_fs.h" 4424c8dbbbSDavid Howells #include "callback.h" 4524c8dbbbSDavid Howells #include "delegation.h" 4624c8dbbbSDavid Howells #include "iostat.h" 4724c8dbbbSDavid Howells #include "internal.h" 4814727281SDavid Howells #include "fscache.h" 4924c8dbbbSDavid Howells 5024c8dbbbSDavid Howells #define NFSDBG_FACILITY NFSDBG_CLIENT 5124c8dbbbSDavid Howells 5224c8dbbbSDavid Howells static DEFINE_SPINLOCK(nfs_client_lock); 5324c8dbbbSDavid Howells static LIST_HEAD(nfs_client_list); 5454ceac45SDavid Howells static LIST_HEAD(nfs_volume_list); 5524c8dbbbSDavid Howells static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq); 5624c8dbbbSDavid Howells 5724c8dbbbSDavid Howells /* 585006a76cSDavid Howells * RPC cruft for NFS 595006a76cSDavid Howells */ 605006a76cSDavid Howells static struct rpc_version *nfs_version[5] = { 615006a76cSDavid Howells [2] = &nfs_version2, 625006a76cSDavid Howells #ifdef CONFIG_NFS_V3 635006a76cSDavid Howells [3] = &nfs_version3, 645006a76cSDavid Howells #endif 655006a76cSDavid Howells #ifdef CONFIG_NFS_V4 665006a76cSDavid Howells [4] = &nfs_version4, 675006a76cSDavid Howells #endif 685006a76cSDavid Howells }; 695006a76cSDavid Howells 705006a76cSDavid Howells struct rpc_program nfs_program = { 715006a76cSDavid Howells .name = "nfs", 725006a76cSDavid Howells .number = NFS_PROGRAM, 735006a76cSDavid Howells .nrvers = ARRAY_SIZE(nfs_version), 745006a76cSDavid Howells .version = nfs_version, 755006a76cSDavid Howells .stats = &nfs_rpcstat, 765006a76cSDavid Howells .pipe_dir_name = "/nfs", 775006a76cSDavid Howells }; 785006a76cSDavid Howells 795006a76cSDavid Howells struct rpc_stat nfs_rpcstat = { 805006a76cSDavid Howells .program = &nfs_program 815006a76cSDavid Howells }; 825006a76cSDavid Howells 835006a76cSDavid Howells 845006a76cSDavid Howells #ifdef CONFIG_NFS_V3_ACL 855006a76cSDavid Howells static struct rpc_stat nfsacl_rpcstat = { &nfsacl_program }; 865006a76cSDavid Howells static struct rpc_version * nfsacl_version[] = { 875006a76cSDavid Howells [3] = &nfsacl_version3, 885006a76cSDavid Howells }; 895006a76cSDavid Howells 905006a76cSDavid Howells struct rpc_program nfsacl_program = { 915006a76cSDavid Howells .name = "nfsacl", 925006a76cSDavid Howells .number = NFS_ACL_PROGRAM, 935006a76cSDavid Howells .nrvers = ARRAY_SIZE(nfsacl_version), 945006a76cSDavid Howells .version = nfsacl_version, 955006a76cSDavid Howells .stats = &nfsacl_rpcstat, 965006a76cSDavid Howells }; 975006a76cSDavid Howells #endif /* CONFIG_NFS_V3_ACL */ 985006a76cSDavid Howells 993a498026STrond Myklebust struct nfs_client_initdata { 1003a498026STrond Myklebust const char *hostname; 101d7422c47SChuck Lever const struct sockaddr *addr; 1026e4cffd7SChuck Lever size_t addrlen; 10340c55319STrond Myklebust const struct nfs_rpc_ops *rpc_ops; 10459dca3b2STrond Myklebust int proto; 1055aae4a9aSBenny Halevy u32 minorversion; 1063a498026STrond Myklebust }; 1073a498026STrond Myklebust 1085006a76cSDavid Howells /* 10924c8dbbbSDavid Howells * Allocate a shared client record 11024c8dbbbSDavid Howells * 11124c8dbbbSDavid Howells * Since these are allocated/deallocated very rarely, we don't 11224c8dbbbSDavid Howells * bother putting them in a slab cache... 11324c8dbbbSDavid Howells */ 1143a498026STrond Myklebust static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) 11524c8dbbbSDavid Howells { 11624c8dbbbSDavid Howells struct nfs_client *clp; 1177c67db3aSTrond Myklebust struct rpc_cred *cred; 11824c8dbbbSDavid Howells 11924c8dbbbSDavid Howells if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) 12024c8dbbbSDavid Howells goto error_0; 12124c8dbbbSDavid Howells 12240c55319STrond Myklebust clp->rpc_ops = cl_init->rpc_ops; 12340c55319STrond Myklebust 12440c55319STrond Myklebust if (cl_init->rpc_ops->version == 4) { 12524c8dbbbSDavid Howells if (nfs_callback_up() < 0) 12624c8dbbbSDavid Howells goto error_2; 12724c8dbbbSDavid Howells __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state); 12824c8dbbbSDavid Howells } 12924c8dbbbSDavid Howells 13024c8dbbbSDavid Howells atomic_set(&clp->cl_count, 1); 13124c8dbbbSDavid Howells clp->cl_cons_state = NFS_CS_INITING; 13224c8dbbbSDavid Howells 1336e4cffd7SChuck Lever memcpy(&clp->cl_addr, cl_init->addr, cl_init->addrlen); 1346e4cffd7SChuck Lever clp->cl_addrlen = cl_init->addrlen; 13524c8dbbbSDavid Howells 1363a498026STrond Myklebust if (cl_init->hostname) { 1373a498026STrond Myklebust clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL); 13824c8dbbbSDavid Howells if (!clp->cl_hostname) 13924c8dbbbSDavid Howells goto error_3; 14024c8dbbbSDavid Howells } 14124c8dbbbSDavid Howells 14224c8dbbbSDavid Howells INIT_LIST_HEAD(&clp->cl_superblocks); 14324c8dbbbSDavid Howells clp->cl_rpcclient = ERR_PTR(-EINVAL); 14424c8dbbbSDavid Howells 14559dca3b2STrond Myklebust clp->cl_proto = cl_init->proto; 14659dca3b2STrond Myklebust 14724c8dbbbSDavid Howells #ifdef CONFIG_NFS_V4 14824c8dbbbSDavid Howells INIT_LIST_HEAD(&clp->cl_delegations); 14924c8dbbbSDavid Howells spin_lock_init(&clp->cl_lock); 15065f27f38SDavid Howells INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state); 15124c8dbbbSDavid Howells rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); 15224c8dbbbSDavid Howells clp->cl_boot_time = CURRENT_TIME; 15324c8dbbbSDavid Howells clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED; 1545aae4a9aSBenny Halevy clp->cl_minorversion = cl_init->minorversion; 15524c8dbbbSDavid Howells #endif 1567c67db3aSTrond Myklebust cred = rpc_lookup_machine_cred(); 1577c67db3aSTrond Myklebust if (!IS_ERR(cred)) 1587c67db3aSTrond Myklebust clp->cl_machine_cred = cred; 15924c8dbbbSDavid Howells 16014727281SDavid Howells nfs_fscache_get_client_cookie(clp); 16114727281SDavid Howells 16224c8dbbbSDavid Howells return clp; 16324c8dbbbSDavid Howells 16424c8dbbbSDavid Howells error_3: 1659c5bf38dSTrond Myklebust if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) 16624c8dbbbSDavid Howells nfs_callback_down(); 16724c8dbbbSDavid Howells error_2: 16824c8dbbbSDavid Howells kfree(clp); 16924c8dbbbSDavid Howells error_0: 17024c8dbbbSDavid Howells return NULL; 17124c8dbbbSDavid Howells } 17224c8dbbbSDavid Howells 1735dd3177aSTrond Myklebust static void nfs4_shutdown_client(struct nfs_client *clp) 17424c8dbbbSDavid Howells { 17524c8dbbbSDavid Howells #ifdef CONFIG_NFS_V4 1765dd3177aSTrond Myklebust if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state)) 1775dd3177aSTrond Myklebust nfs4_kill_renewd(clp); 1789f958ab8STrond Myklebust BUG_ON(!RB_EMPTY_ROOT(&clp->cl_state_owners)); 1795dd3177aSTrond Myklebust if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state)) 18024c8dbbbSDavid Howells nfs_idmap_delete(clp); 181f6a1cc89STrond Myklebust 182f6a1cc89STrond Myklebust rpc_destroy_wait_queue(&clp->cl_rpcwaitq); 18324c8dbbbSDavid Howells #endif 1845dd3177aSTrond Myklebust } 1855dd3177aSTrond Myklebust 1865dd3177aSTrond Myklebust /* 187557134a3SAndy Adamson * Clears/puts all minor version specific parts from an nfs_client struct 188557134a3SAndy Adamson * reverting it to minorversion 0. 189557134a3SAndy Adamson */ 190557134a3SAndy Adamson static void nfs4_clear_client_minor_version(struct nfs_client *clp) 191557134a3SAndy Adamson { 192557134a3SAndy Adamson #ifdef CONFIG_NFS_V4_1 193557134a3SAndy Adamson if (nfs4_has_session(clp)) { 194557134a3SAndy Adamson nfs4_destroy_session(clp->cl_session); 195557134a3SAndy Adamson clp->cl_session = NULL; 196557134a3SAndy Adamson } 197*cccef3b9SAndy Adamson 198*cccef3b9SAndy Adamson clp->cl_call_sync = _nfs4_call_sync; 199557134a3SAndy Adamson #endif /* CONFIG_NFS_V4_1 */ 200557134a3SAndy Adamson } 201557134a3SAndy Adamson 202557134a3SAndy Adamson /* 2035dd3177aSTrond Myklebust * Destroy a shared client record 2045dd3177aSTrond Myklebust */ 2055dd3177aSTrond Myklebust static void nfs_free_client(struct nfs_client *clp) 2065dd3177aSTrond Myklebust { 20740c55319STrond Myklebust dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version); 2085dd3177aSTrond Myklebust 209557134a3SAndy Adamson nfs4_clear_client_minor_version(clp); 2105dd3177aSTrond Myklebust nfs4_shutdown_client(clp); 21124c8dbbbSDavid Howells 21214727281SDavid Howells nfs_fscache_release_client_cookie(clp); 21314727281SDavid Howells 21424c8dbbbSDavid Howells /* -EIO all pending I/O */ 21524c8dbbbSDavid Howells if (!IS_ERR(clp->cl_rpcclient)) 21624c8dbbbSDavid Howells rpc_shutdown_client(clp->cl_rpcclient); 21724c8dbbbSDavid Howells 21824c8dbbbSDavid Howells if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) 21924c8dbbbSDavid Howells nfs_callback_down(); 22024c8dbbbSDavid Howells 2217c67db3aSTrond Myklebust if (clp->cl_machine_cred != NULL) 2227c67db3aSTrond Myklebust put_rpccred(clp->cl_machine_cred); 2237c67db3aSTrond Myklebust 22424c8dbbbSDavid Howells kfree(clp->cl_hostname); 22524c8dbbbSDavid Howells kfree(clp); 22624c8dbbbSDavid Howells 22724c8dbbbSDavid Howells dprintk("<-- nfs_free_client()\n"); 22824c8dbbbSDavid Howells } 22924c8dbbbSDavid Howells 23024c8dbbbSDavid Howells /* 23124c8dbbbSDavid Howells * Release a reference to a shared client record 23224c8dbbbSDavid Howells */ 23324c8dbbbSDavid Howells void nfs_put_client(struct nfs_client *clp) 23424c8dbbbSDavid Howells { 23527ba8512SDavid Howells if (!clp) 23627ba8512SDavid Howells return; 23727ba8512SDavid Howells 23824c8dbbbSDavid Howells dprintk("--> nfs_put_client({%d})\n", atomic_read(&clp->cl_count)); 23924c8dbbbSDavid Howells 24024c8dbbbSDavid Howells if (atomic_dec_and_lock(&clp->cl_count, &nfs_client_lock)) { 24124c8dbbbSDavid Howells list_del(&clp->cl_share_link); 24224c8dbbbSDavid Howells spin_unlock(&nfs_client_lock); 24324c8dbbbSDavid Howells 24424c8dbbbSDavid Howells BUG_ON(!list_empty(&clp->cl_superblocks)); 24524c8dbbbSDavid Howells 24624c8dbbbSDavid Howells nfs_free_client(clp); 24724c8dbbbSDavid Howells } 24824c8dbbbSDavid Howells } 24924c8dbbbSDavid Howells 2509082a5ccSTrond Myklebust #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) 2519f4c899cSTrond Myklebust /* 2529f4c899cSTrond Myklebust * Test if two ip6 socket addresses refer to the same socket by 2539f4c899cSTrond Myklebust * comparing relevant fields. The padding bytes specifically, are not 2549f4c899cSTrond Myklebust * compared. sin6_flowinfo is not compared because it only affects QoS 2559f4c899cSTrond Myklebust * and sin6_scope_id is only compared if the address is "link local" 2569f4c899cSTrond Myklebust * because "link local" addresses need only be unique to a specific 2579f4c899cSTrond Myklebust * link. Conversely, ordinary unicast addresses might have different 2589f4c899cSTrond Myklebust * sin6_scope_id. 2599f4c899cSTrond Myklebust * 2609f4c899cSTrond Myklebust * The caller should ensure both socket addresses are AF_INET6. 2619f4c899cSTrond Myklebust */ 2623c8c45dfSChuck Lever static int nfs_sockaddr_match_ipaddr6(const struct sockaddr *sa1, 2639f4c899cSTrond Myklebust const struct sockaddr *sa2) 2649f4c899cSTrond Myklebust { 2653c8c45dfSChuck Lever const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sa1; 2663c8c45dfSChuck Lever const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2; 2679f4c899cSTrond Myklebust 2683c8c45dfSChuck Lever if (ipv6_addr_scope(&sin1->sin6_addr) == IPV6_ADDR_SCOPE_LINKLOCAL && 2693c8c45dfSChuck Lever sin1->sin6_scope_id != sin2->sin6_scope_id) 2709f4c899cSTrond Myklebust return 0; 2713b0d3f93STrond Myklebust 2723c8c45dfSChuck Lever return ipv6_addr_equal(&sin1->sin6_addr, &sin1->sin6_addr); 2733b0d3f93STrond Myklebust } 2743c8c45dfSChuck Lever #else /* !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE) */ 2753c8c45dfSChuck Lever static int nfs_sockaddr_match_ipaddr6(const struct sockaddr *sa1, 2769f4c899cSTrond Myklebust const struct sockaddr *sa2) 2779f4c899cSTrond Myklebust { 2789f4c899cSTrond Myklebust return 0; 2799f4c899cSTrond Myklebust } 2809082a5ccSTrond Myklebust #endif 2813b0d3f93STrond Myklebust 28224c8dbbbSDavid Howells /* 283d7371c41SIan Dall * Test if two ip4 socket addresses refer to the same socket, by 284d7371c41SIan Dall * comparing relevant fields. The padding bytes specifically, are 285d7371c41SIan Dall * not compared. 286d7371c41SIan Dall * 287d7371c41SIan Dall * The caller should ensure both socket addresses are AF_INET. 288d7371c41SIan Dall */ 2893c8c45dfSChuck Lever static int nfs_sockaddr_match_ipaddr4(const struct sockaddr *sa1, 2903c8c45dfSChuck Lever const struct sockaddr *sa2) 2913c8c45dfSChuck Lever { 2923c8c45dfSChuck Lever const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sa1; 2933c8c45dfSChuck Lever const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sa2; 2943c8c45dfSChuck Lever 2953c8c45dfSChuck Lever return sin1->sin_addr.s_addr == sin2->sin_addr.s_addr; 2963c8c45dfSChuck Lever } 2973c8c45dfSChuck Lever 2983c8c45dfSChuck Lever static int nfs_sockaddr_cmp_ip6(const struct sockaddr *sa1, 2993c8c45dfSChuck Lever const struct sockaddr *sa2) 3003c8c45dfSChuck Lever { 3013c8c45dfSChuck Lever const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sa1; 3023c8c45dfSChuck Lever const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2; 3033c8c45dfSChuck Lever 3043c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr6(sa1, sa2) && 3053c8c45dfSChuck Lever (sin1->sin6_port == sin2->sin6_port); 3063c8c45dfSChuck Lever } 3073c8c45dfSChuck Lever 3089f4c899cSTrond Myklebust static int nfs_sockaddr_cmp_ip4(const struct sockaddr *sa1, 3099f4c899cSTrond Myklebust const struct sockaddr *sa2) 310d7371c41SIan Dall { 3113c8c45dfSChuck Lever const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sa1; 3123c8c45dfSChuck Lever const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sa2; 3139f4c899cSTrond Myklebust 3143c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr4(sa1, sa2) && 3153c8c45dfSChuck Lever (sin1->sin_port == sin2->sin_port); 316d7371c41SIan Dall } 317d7371c41SIan Dall 318d7371c41SIan Dall /* 319d7371c41SIan Dall * Test if two socket addresses represent the same actual socket, 3203c8c45dfSChuck Lever * by comparing (only) relevant fields, excluding the port number. 3213c8c45dfSChuck Lever */ 3223c8c45dfSChuck Lever static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1, 3233c8c45dfSChuck Lever const struct sockaddr *sa2) 3243c8c45dfSChuck Lever { 3253c8c45dfSChuck Lever if (sa1->sa_family != sa2->sa_family) 3263c8c45dfSChuck Lever return 0; 3273c8c45dfSChuck Lever 3283c8c45dfSChuck Lever switch (sa1->sa_family) { 3293c8c45dfSChuck Lever case AF_INET: 3303c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr4(sa1, sa2); 3313c8c45dfSChuck Lever case AF_INET6: 3323c8c45dfSChuck Lever return nfs_sockaddr_match_ipaddr6(sa1, sa2); 3333c8c45dfSChuck Lever } 3343c8c45dfSChuck Lever return 0; 3353c8c45dfSChuck Lever } 3363c8c45dfSChuck Lever 3373c8c45dfSChuck Lever /* 3383c8c45dfSChuck Lever * Test if two socket addresses represent the same actual socket, 3393c8c45dfSChuck Lever * by comparing (only) relevant fields, including the port number. 340d7371c41SIan Dall */ 341d7371c41SIan Dall static int nfs_sockaddr_cmp(const struct sockaddr *sa1, 342d7371c41SIan Dall const struct sockaddr *sa2) 343d7371c41SIan Dall { 344d7371c41SIan Dall if (sa1->sa_family != sa2->sa_family) 345d7371c41SIan Dall return 0; 346d7371c41SIan Dall 347d7371c41SIan Dall switch (sa1->sa_family) { 348d7371c41SIan Dall case AF_INET: 3499f4c899cSTrond Myklebust return nfs_sockaddr_cmp_ip4(sa1, sa2); 350d7371c41SIan Dall case AF_INET6: 3519f4c899cSTrond Myklebust return nfs_sockaddr_cmp_ip6(sa1, sa2); 352d7371c41SIan Dall } 353d7371c41SIan Dall return 0; 354d7371c41SIan Dall } 355d7371c41SIan Dall 356d7371c41SIan Dall /* 357c81468a1STrond Myklebust * Find a client by IP address and protocol version 358c81468a1STrond Myklebust * - returns NULL if no such client 35924c8dbbbSDavid Howells */ 360ff052645SChuck Lever struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion) 361c81468a1STrond Myklebust { 362c81468a1STrond Myklebust struct nfs_client *clp; 363c81468a1STrond Myklebust 364c81468a1STrond Myklebust spin_lock(&nfs_client_lock); 365c81468a1STrond Myklebust list_for_each_entry(clp, &nfs_client_list, cl_share_link) { 3663b0d3f93STrond Myklebust struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; 3673b0d3f93STrond Myklebust 368c81468a1STrond Myklebust /* Don't match clients that failed to initialise properly */ 369c81468a1STrond Myklebust if (clp->cl_cons_state != NFS_CS_READY) 370c81468a1STrond Myklebust continue; 371c81468a1STrond Myklebust 372c81468a1STrond Myklebust /* Different NFS versions cannot share the same nfs_client */ 37340c55319STrond Myklebust if (clp->rpc_ops->version != nfsversion) 374c81468a1STrond Myklebust continue; 375c81468a1STrond Myklebust 376c81468a1STrond Myklebust /* Match only the IP address, not the port number */ 3773b0d3f93STrond Myklebust if (!nfs_sockaddr_match_ipaddr(addr, clap)) 378c81468a1STrond Myklebust continue; 379c81468a1STrond Myklebust 380c81468a1STrond Myklebust atomic_inc(&clp->cl_count); 381c81468a1STrond Myklebust spin_unlock(&nfs_client_lock); 382c81468a1STrond Myklebust return clp; 383c81468a1STrond Myklebust } 384c81468a1STrond Myklebust spin_unlock(&nfs_client_lock); 385c81468a1STrond Myklebust return NULL; 386c81468a1STrond Myklebust } 387c81468a1STrond Myklebust 388c81468a1STrond Myklebust /* 3893fbd67adSTrond Myklebust * Find a client by IP address and protocol version 3903fbd67adSTrond Myklebust * - returns NULL if no such client 3913fbd67adSTrond Myklebust */ 3923fbd67adSTrond Myklebust struct nfs_client *nfs_find_client_next(struct nfs_client *clp) 3933fbd67adSTrond Myklebust { 3943fbd67adSTrond Myklebust struct sockaddr *sap = (struct sockaddr *)&clp->cl_addr; 3953fbd67adSTrond Myklebust u32 nfsvers = clp->rpc_ops->version; 3963fbd67adSTrond Myklebust 3973fbd67adSTrond Myklebust spin_lock(&nfs_client_lock); 3983fbd67adSTrond Myklebust list_for_each_entry_continue(clp, &nfs_client_list, cl_share_link) { 3993fbd67adSTrond Myklebust struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; 4003fbd67adSTrond Myklebust 4013fbd67adSTrond Myklebust /* Don't match clients that failed to initialise properly */ 4023fbd67adSTrond Myklebust if (clp->cl_cons_state != NFS_CS_READY) 4033fbd67adSTrond Myklebust continue; 4043fbd67adSTrond Myklebust 4053fbd67adSTrond Myklebust /* Different NFS versions cannot share the same nfs_client */ 4063fbd67adSTrond Myklebust if (clp->rpc_ops->version != nfsvers) 4073fbd67adSTrond Myklebust continue; 4083fbd67adSTrond Myklebust 4093fbd67adSTrond Myklebust /* Match only the IP address, not the port number */ 4103fbd67adSTrond Myklebust if (!nfs_sockaddr_match_ipaddr(sap, clap)) 4113fbd67adSTrond Myklebust continue; 4123fbd67adSTrond Myklebust 4133fbd67adSTrond Myklebust atomic_inc(&clp->cl_count); 4143fbd67adSTrond Myklebust spin_unlock(&nfs_client_lock); 4153fbd67adSTrond Myklebust return clp; 4163fbd67adSTrond Myklebust } 4173fbd67adSTrond Myklebust spin_unlock(&nfs_client_lock); 4183fbd67adSTrond Myklebust return NULL; 4193fbd67adSTrond Myklebust } 4203fbd67adSTrond Myklebust 4213fbd67adSTrond Myklebust /* 422c81468a1STrond Myklebust * Find an nfs_client on the list that matches the initialisation data 423c81468a1STrond Myklebust * that is supplied. 424c81468a1STrond Myklebust */ 425c81468a1STrond Myklebust static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *data) 42624c8dbbbSDavid Howells { 42724c8dbbbSDavid Howells struct nfs_client *clp; 428d7371c41SIan Dall const struct sockaddr *sap = data->addr; 42924c8dbbbSDavid Howells 43024c8dbbbSDavid Howells list_for_each_entry(clp, &nfs_client_list, cl_share_link) { 431d7371c41SIan Dall const struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; 43213bbc06aSTrond Myklebust /* Don't match clients that failed to initialise properly */ 43313bbc06aSTrond Myklebust if (clp->cl_cons_state < 0) 43413bbc06aSTrond Myklebust continue; 43513bbc06aSTrond Myklebust 43624c8dbbbSDavid Howells /* Different NFS versions cannot share the same nfs_client */ 43740c55319STrond Myklebust if (clp->rpc_ops != data->rpc_ops) 43824c8dbbbSDavid Howells continue; 43924c8dbbbSDavid Howells 44059dca3b2STrond Myklebust if (clp->cl_proto != data->proto) 44159dca3b2STrond Myklebust continue; 4425aae4a9aSBenny Halevy /* Match nfsv4 minorversion */ 4435aae4a9aSBenny Halevy if (clp->cl_minorversion != data->minorversion) 4445aae4a9aSBenny Halevy continue; 445c81468a1STrond Myklebust /* Match the full socket address */ 446d7371c41SIan Dall if (!nfs_sockaddr_cmp(sap, clap)) 44724c8dbbbSDavid Howells continue; 44824c8dbbbSDavid Howells 44924c8dbbbSDavid Howells atomic_inc(&clp->cl_count); 45024c8dbbbSDavid Howells return clp; 45124c8dbbbSDavid Howells } 452c81468a1STrond Myklebust return NULL; 45324c8dbbbSDavid Howells } 45424c8dbbbSDavid Howells 45524c8dbbbSDavid Howells /* 45624c8dbbbSDavid Howells * Look up a client by IP address and protocol version 45724c8dbbbSDavid Howells * - creates a new record if one doesn't yet exist 45824c8dbbbSDavid Howells */ 4593a498026STrond Myklebust static struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_init) 46024c8dbbbSDavid Howells { 46124c8dbbbSDavid Howells struct nfs_client *clp, *new = NULL; 46224c8dbbbSDavid Howells int error; 46324c8dbbbSDavid Howells 464d7422c47SChuck Lever dprintk("--> nfs_get_client(%s,v%u)\n", 465d7422c47SChuck Lever cl_init->hostname ?: "", cl_init->rpc_ops->version); 46624c8dbbbSDavid Howells 46724c8dbbbSDavid Howells /* see if the client already exists */ 46824c8dbbbSDavid Howells do { 46924c8dbbbSDavid Howells spin_lock(&nfs_client_lock); 47024c8dbbbSDavid Howells 471c81468a1STrond Myklebust clp = nfs_match_client(cl_init); 47224c8dbbbSDavid Howells if (clp) 47324c8dbbbSDavid Howells goto found_client; 47424c8dbbbSDavid Howells if (new) 47524c8dbbbSDavid Howells goto install_client; 47624c8dbbbSDavid Howells 47724c8dbbbSDavid Howells spin_unlock(&nfs_client_lock); 47824c8dbbbSDavid Howells 4793a498026STrond Myklebust new = nfs_alloc_client(cl_init); 48024c8dbbbSDavid Howells } while (new); 48124c8dbbbSDavid Howells 48224c8dbbbSDavid Howells return ERR_PTR(-ENOMEM); 48324c8dbbbSDavid Howells 48424c8dbbbSDavid Howells /* install a new client and return with it unready */ 48524c8dbbbSDavid Howells install_client: 48624c8dbbbSDavid Howells clp = new; 48724c8dbbbSDavid Howells list_add(&clp->cl_share_link, &nfs_client_list); 48824c8dbbbSDavid Howells spin_unlock(&nfs_client_lock); 48924c8dbbbSDavid Howells dprintk("--> nfs_get_client() = %p [new]\n", clp); 49024c8dbbbSDavid Howells return clp; 49124c8dbbbSDavid Howells 49224c8dbbbSDavid Howells /* found an existing client 49324c8dbbbSDavid Howells * - make sure it's ready before returning 49424c8dbbbSDavid Howells */ 49524c8dbbbSDavid Howells found_client: 49624c8dbbbSDavid Howells spin_unlock(&nfs_client_lock); 49724c8dbbbSDavid Howells 49824c8dbbbSDavid Howells if (new) 49924c8dbbbSDavid Howells nfs_free_client(new); 50024c8dbbbSDavid Howells 501150030b7SMatthew Wilcox error = wait_event_killable(nfs_client_active_wq, 5020bae89ecSTrond Myklebust clp->cl_cons_state != NFS_CS_INITING); 5030bae89ecSTrond Myklebust if (error < 0) { 50424c8dbbbSDavid Howells nfs_put_client(clp); 50524c8dbbbSDavid Howells return ERR_PTR(-ERESTARTSYS); 50624c8dbbbSDavid Howells } 50724c8dbbbSDavid Howells 50824c8dbbbSDavid Howells if (clp->cl_cons_state < NFS_CS_READY) { 50924c8dbbbSDavid Howells error = clp->cl_cons_state; 51024c8dbbbSDavid Howells nfs_put_client(clp); 51124c8dbbbSDavid Howells return ERR_PTR(error); 51224c8dbbbSDavid Howells } 51324c8dbbbSDavid Howells 51454ceac45SDavid Howells BUG_ON(clp->cl_cons_state != NFS_CS_READY); 51554ceac45SDavid Howells 51624c8dbbbSDavid Howells dprintk("--> nfs_get_client() = %p [share]\n", clp); 51724c8dbbbSDavid Howells return clp; 51824c8dbbbSDavid Howells } 51924c8dbbbSDavid Howells 52024c8dbbbSDavid Howells /* 52124c8dbbbSDavid Howells * Mark a server as ready or failed 52224c8dbbbSDavid Howells */ 52354ceac45SDavid Howells static void nfs_mark_client_ready(struct nfs_client *clp, int state) 52424c8dbbbSDavid Howells { 52524c8dbbbSDavid Howells clp->cl_cons_state = state; 52624c8dbbbSDavid Howells wake_up_all(&nfs_client_active_wq); 52724c8dbbbSDavid Howells } 5285006a76cSDavid Howells 5295006a76cSDavid Howells /* 5305006a76cSDavid Howells * Initialise the timeout values for a connection 5315006a76cSDavid Howells */ 5325006a76cSDavid Howells static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, 5335006a76cSDavid Howells unsigned int timeo, unsigned int retrans) 5345006a76cSDavid Howells { 5355006a76cSDavid Howells to->to_initval = timeo * HZ / 10; 5365006a76cSDavid Howells to->to_retries = retrans; 5375006a76cSDavid Howells 5385006a76cSDavid Howells switch (proto) { 5390896a725S\"Talpey, Thomas\ case XPRT_TRANSPORT_TCP: 5402cf7ff7aS\"Talpey, Thomas\ case XPRT_TRANSPORT_RDMA: 541259875efSTrond Myklebust if (to->to_retries == 0) 542259875efSTrond Myklebust to->to_retries = NFS_DEF_TCP_RETRANS; 5437a3e3e18STrond Myklebust if (to->to_initval == 0) 544259875efSTrond Myklebust to->to_initval = NFS_DEF_TCP_TIMEO * HZ / 10; 5455006a76cSDavid Howells if (to->to_initval > NFS_MAX_TCP_TIMEOUT) 5465006a76cSDavid Howells to->to_initval = NFS_MAX_TCP_TIMEOUT; 5475006a76cSDavid Howells to->to_increment = to->to_initval; 5485006a76cSDavid Howells to->to_maxval = to->to_initval + (to->to_increment * to->to_retries); 5497a3e3e18STrond Myklebust if (to->to_maxval > NFS_MAX_TCP_TIMEOUT) 5507a3e3e18STrond Myklebust to->to_maxval = NFS_MAX_TCP_TIMEOUT; 5517a3e3e18STrond Myklebust if (to->to_maxval < to->to_initval) 5527a3e3e18STrond Myklebust to->to_maxval = to->to_initval; 5535006a76cSDavid Howells to->to_exponential = 0; 5545006a76cSDavid Howells break; 5550896a725S\"Talpey, Thomas\ case XPRT_TRANSPORT_UDP: 556259875efSTrond Myklebust if (to->to_retries == 0) 557259875efSTrond Myklebust to->to_retries = NFS_DEF_UDP_RETRANS; 5585006a76cSDavid Howells if (!to->to_initval) 559259875efSTrond Myklebust to->to_initval = NFS_DEF_UDP_TIMEO * HZ / 10; 5605006a76cSDavid Howells if (to->to_initval > NFS_MAX_UDP_TIMEOUT) 5615006a76cSDavid Howells to->to_initval = NFS_MAX_UDP_TIMEOUT; 5625006a76cSDavid Howells to->to_maxval = NFS_MAX_UDP_TIMEOUT; 5635006a76cSDavid Howells to->to_exponential = 1; 5645006a76cSDavid Howells break; 565259875efSTrond Myklebust default: 566259875efSTrond Myklebust BUG(); 5675006a76cSDavid Howells } 5685006a76cSDavid Howells } 5695006a76cSDavid Howells 5705006a76cSDavid Howells /* 5715006a76cSDavid Howells * Create an RPC client handle 5725006a76cSDavid Howells */ 57359dca3b2STrond Myklebust static int nfs_create_rpc_client(struct nfs_client *clp, 57433170233STrond Myklebust const struct rpc_timeout *timeparms, 57543d78ef2SChuck Lever rpc_authflavor_t flavor, 5764a01b8a4SChuck Lever int discrtry, int noresvport) 5775006a76cSDavid Howells { 5785006a76cSDavid Howells struct rpc_clnt *clnt = NULL; 57941877d20SChuck Lever struct rpc_create_args args = { 58059dca3b2STrond Myklebust .protocol = clp->cl_proto, 58141877d20SChuck Lever .address = (struct sockaddr *)&clp->cl_addr, 5826e4cffd7SChuck Lever .addrsize = clp->cl_addrlen, 58333170233STrond Myklebust .timeout = timeparms, 58441877d20SChuck Lever .servername = clp->cl_hostname, 58541877d20SChuck Lever .program = &nfs_program, 58641877d20SChuck Lever .version = clp->rpc_ops->version, 58741877d20SChuck Lever .authflavor = flavor, 58841877d20SChuck Lever }; 5895006a76cSDavid Howells 5904a01b8a4SChuck Lever if (discrtry) 5914a01b8a4SChuck Lever args.flags |= RPC_CLNT_CREATE_DISCRTRY; 5924a01b8a4SChuck Lever if (noresvport) 5934a01b8a4SChuck Lever args.flags |= RPC_CLNT_CREATE_NONPRIVPORT; 5944a01b8a4SChuck Lever 5955006a76cSDavid Howells if (!IS_ERR(clp->cl_rpcclient)) 5965006a76cSDavid Howells return 0; 5975006a76cSDavid Howells 59841877d20SChuck Lever clnt = rpc_create(&args); 5995006a76cSDavid Howells if (IS_ERR(clnt)) { 6005006a76cSDavid Howells dprintk("%s: cannot create RPC client. Error = %ld\n", 6013110ff80SHarvey Harrison __func__, PTR_ERR(clnt)); 6025006a76cSDavid Howells return PTR_ERR(clnt); 6035006a76cSDavid Howells } 6045006a76cSDavid Howells 6055006a76cSDavid Howells clp->cl_rpcclient = clnt; 6065006a76cSDavid Howells return 0; 6075006a76cSDavid Howells } 60854ceac45SDavid Howells 60954ceac45SDavid Howells /* 61054ceac45SDavid Howells * Version 2 or 3 client destruction 61154ceac45SDavid Howells */ 61254ceac45SDavid Howells static void nfs_destroy_server(struct nfs_server *server) 61354ceac45SDavid Howells { 61454ceac45SDavid Howells if (!(server->flags & NFS_MOUNT_NONLM)) 6159289e7f9SChuck Lever nlmclnt_done(server->nlm_host); 61654ceac45SDavid Howells } 61754ceac45SDavid Howells 61854ceac45SDavid Howells /* 61954ceac45SDavid Howells * Version 2 or 3 lockd setup 62054ceac45SDavid Howells */ 62154ceac45SDavid Howells static int nfs_start_lockd(struct nfs_server *server) 62254ceac45SDavid Howells { 6239289e7f9SChuck Lever struct nlm_host *host; 6249289e7f9SChuck Lever struct nfs_client *clp = server->nfs_client; 625883bb163SChuck Lever struct nlmclnt_initdata nlm_init = { 626883bb163SChuck Lever .hostname = clp->cl_hostname, 627883bb163SChuck Lever .address = (struct sockaddr *)&clp->cl_addr, 628883bb163SChuck Lever .addrlen = clp->cl_addrlen, 629883bb163SChuck Lever .protocol = server->flags & NFS_MOUNT_TCP ? 630883bb163SChuck Lever IPPROTO_TCP : IPPROTO_UDP, 631883bb163SChuck Lever .nfs_version = clp->rpc_ops->version, 6320cb2659bSChuck Lever .noresvport = server->flags & NFS_MOUNT_NORESVPORT ? 6330cb2659bSChuck Lever 1 : 0, 634883bb163SChuck Lever }; 63554ceac45SDavid Howells 636883bb163SChuck Lever if (nlm_init.nfs_version > 3) 6379289e7f9SChuck Lever return 0; 63854ceac45SDavid Howells if (server->flags & NFS_MOUNT_NONLM) 6399289e7f9SChuck Lever return 0; 6409289e7f9SChuck Lever 641883bb163SChuck Lever host = nlmclnt_init(&nlm_init); 6429289e7f9SChuck Lever if (IS_ERR(host)) 6439289e7f9SChuck Lever return PTR_ERR(host); 6449289e7f9SChuck Lever 6459289e7f9SChuck Lever server->nlm_host = host; 64654ceac45SDavid Howells server->destroy = nfs_destroy_server; 6479289e7f9SChuck Lever return 0; 64854ceac45SDavid Howells } 64954ceac45SDavid Howells 65054ceac45SDavid Howells /* 65154ceac45SDavid Howells * Initialise an NFSv3 ACL client connection 65254ceac45SDavid Howells */ 65354ceac45SDavid Howells #ifdef CONFIG_NFS_V3_ACL 65454ceac45SDavid Howells static void nfs_init_server_aclclient(struct nfs_server *server) 65554ceac45SDavid Howells { 65640c55319STrond Myklebust if (server->nfs_client->rpc_ops->version != 3) 65754ceac45SDavid Howells goto out_noacl; 65854ceac45SDavid Howells if (server->flags & NFS_MOUNT_NOACL) 65954ceac45SDavid Howells goto out_noacl; 66054ceac45SDavid Howells 66154ceac45SDavid Howells server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3); 66254ceac45SDavid Howells if (IS_ERR(server->client_acl)) 66354ceac45SDavid Howells goto out_noacl; 66454ceac45SDavid Howells 66554ceac45SDavid Howells /* No errors! Assume that Sun nfsacls are supported */ 66654ceac45SDavid Howells server->caps |= NFS_CAP_ACLS; 66754ceac45SDavid Howells return; 66854ceac45SDavid Howells 66954ceac45SDavid Howells out_noacl: 67054ceac45SDavid Howells server->caps &= ~NFS_CAP_ACLS; 67154ceac45SDavid Howells } 67254ceac45SDavid Howells #else 67354ceac45SDavid Howells static inline void nfs_init_server_aclclient(struct nfs_server *server) 67454ceac45SDavid Howells { 67554ceac45SDavid Howells server->flags &= ~NFS_MOUNT_NOACL; 67654ceac45SDavid Howells server->caps &= ~NFS_CAP_ACLS; 67754ceac45SDavid Howells } 67854ceac45SDavid Howells #endif 67954ceac45SDavid Howells 68054ceac45SDavid Howells /* 68154ceac45SDavid Howells * Create a general RPC client 68254ceac45SDavid Howells */ 68333170233STrond Myklebust static int nfs_init_server_rpcclient(struct nfs_server *server, 68433170233STrond Myklebust const struct rpc_timeout *timeo, 68533170233STrond Myklebust rpc_authflavor_t pseudoflavour) 68654ceac45SDavid Howells { 68754ceac45SDavid Howells struct nfs_client *clp = server->nfs_client; 68854ceac45SDavid Howells 68954ceac45SDavid Howells server->client = rpc_clone_client(clp->cl_rpcclient); 69054ceac45SDavid Howells if (IS_ERR(server->client)) { 6913110ff80SHarvey Harrison dprintk("%s: couldn't create rpc_client!\n", __func__); 69254ceac45SDavid Howells return PTR_ERR(server->client); 69354ceac45SDavid Howells } 69454ceac45SDavid Howells 69533170233STrond Myklebust memcpy(&server->client->cl_timeout_default, 69633170233STrond Myklebust timeo, 69733170233STrond Myklebust sizeof(server->client->cl_timeout_default)); 69833170233STrond Myklebust server->client->cl_timeout = &server->client->cl_timeout_default; 69933170233STrond Myklebust 70054ceac45SDavid Howells if (pseudoflavour != clp->cl_rpcclient->cl_auth->au_flavor) { 70154ceac45SDavid Howells struct rpc_auth *auth; 70254ceac45SDavid Howells 70354ceac45SDavid Howells auth = rpcauth_create(pseudoflavour, server->client); 70454ceac45SDavid Howells if (IS_ERR(auth)) { 7053110ff80SHarvey Harrison dprintk("%s: couldn't create credcache!\n", __func__); 70654ceac45SDavid Howells return PTR_ERR(auth); 70754ceac45SDavid Howells } 70854ceac45SDavid Howells } 70954ceac45SDavid Howells server->client->cl_softrtry = 0; 71054ceac45SDavid Howells if (server->flags & NFS_MOUNT_SOFT) 71154ceac45SDavid Howells server->client->cl_softrtry = 1; 71254ceac45SDavid Howells 71354ceac45SDavid Howells return 0; 71454ceac45SDavid Howells } 71554ceac45SDavid Howells 71654ceac45SDavid Howells /* 71754ceac45SDavid Howells * Initialise an NFS2 or NFS3 client 71854ceac45SDavid Howells */ 7192283f8d6S\"Talpey, Thomas\ static int nfs_init_client(struct nfs_client *clp, 72033170233STrond Myklebust const struct rpc_timeout *timeparms, 7212283f8d6S\"Talpey, Thomas\ const struct nfs_parsed_mount_data *data) 72254ceac45SDavid Howells { 72354ceac45SDavid Howells int error; 72454ceac45SDavid Howells 72554ceac45SDavid Howells if (clp->cl_cons_state == NFS_CS_READY) { 72654ceac45SDavid Howells /* the client is already initialised */ 72754ceac45SDavid Howells dprintk("<-- nfs_init_client() = 0 [already %p]\n", clp); 72854ceac45SDavid Howells return 0; 72954ceac45SDavid Howells } 73054ceac45SDavid Howells 73154ceac45SDavid Howells /* 73254ceac45SDavid Howells * Create a client RPC handle for doing FSSTAT with UNIX auth only 73354ceac45SDavid Howells * - RFC 2623, sec 2.3.2 73454ceac45SDavid Howells */ 735d740351bSChuck Lever error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX, 736d740351bSChuck Lever 0, data->flags & NFS_MOUNT_NORESVPORT); 73754ceac45SDavid Howells if (error < 0) 73854ceac45SDavid Howells goto error; 73954ceac45SDavid Howells nfs_mark_client_ready(clp, NFS_CS_READY); 74054ceac45SDavid Howells return 0; 74154ceac45SDavid Howells 74254ceac45SDavid Howells error: 74354ceac45SDavid Howells nfs_mark_client_ready(clp, error); 74454ceac45SDavid Howells dprintk("<-- nfs_init_client() = xerror %d\n", error); 74554ceac45SDavid Howells return error; 74654ceac45SDavid Howells } 74754ceac45SDavid Howells 74854ceac45SDavid Howells /* 74954ceac45SDavid Howells * Create a version 2 or 3 client 75054ceac45SDavid Howells */ 7512283f8d6S\"Talpey, Thomas\ static int nfs_init_server(struct nfs_server *server, 7522283f8d6S\"Talpey, Thomas\ const struct nfs_parsed_mount_data *data) 75354ceac45SDavid Howells { 7543a498026STrond Myklebust struct nfs_client_initdata cl_init = { 7553a498026STrond Myklebust .hostname = data->nfs_server.hostname, 756d7422c47SChuck Lever .addr = (const struct sockaddr *)&data->nfs_server.address, 7574c568017SChuck Lever .addrlen = data->nfs_server.addrlen, 75840c55319STrond Myklebust .rpc_ops = &nfs_v2_clientops, 75959dca3b2STrond Myklebust .proto = data->nfs_server.protocol, 7603a498026STrond Myklebust }; 76133170233STrond Myklebust struct rpc_timeout timeparms; 76254ceac45SDavid Howells struct nfs_client *clp; 7633a498026STrond Myklebust int error; 76454ceac45SDavid Howells 76554ceac45SDavid Howells dprintk("--> nfs_init_server()\n"); 76654ceac45SDavid Howells 76754ceac45SDavid Howells #ifdef CONFIG_NFS_V3 76854ceac45SDavid Howells if (data->flags & NFS_MOUNT_VER3) 76940c55319STrond Myklebust cl_init.rpc_ops = &nfs_v3_clientops; 77054ceac45SDavid Howells #endif 77154ceac45SDavid Howells 77254ceac45SDavid Howells /* Allocate or find a client reference we can use */ 7733a498026STrond Myklebust clp = nfs_get_client(&cl_init); 77454ceac45SDavid Howells if (IS_ERR(clp)) { 77554ceac45SDavid Howells dprintk("<-- nfs_init_server() = error %ld\n", PTR_ERR(clp)); 77654ceac45SDavid Howells return PTR_ERR(clp); 77754ceac45SDavid Howells } 77854ceac45SDavid Howells 77933170233STrond Myklebust nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, 78033170233STrond Myklebust data->timeo, data->retrans); 78133170233STrond Myklebust error = nfs_init_client(clp, &timeparms, data); 78254ceac45SDavid Howells if (error < 0) 78354ceac45SDavid Howells goto error; 78454ceac45SDavid Howells 78554ceac45SDavid Howells server->nfs_client = clp; 78654ceac45SDavid Howells 78754ceac45SDavid Howells /* Initialise the client representation from the mount data */ 788ff3525a5STrond Myklebust server->flags = data->flags; 789b797cac7SDavid Howells server->options = data->options; 79054ceac45SDavid Howells 79154ceac45SDavid Howells if (data->rsize) 79254ceac45SDavid Howells server->rsize = nfs_block_size(data->rsize, NULL); 79354ceac45SDavid Howells if (data->wsize) 79454ceac45SDavid Howells server->wsize = nfs_block_size(data->wsize, NULL); 79554ceac45SDavid Howells 79654ceac45SDavid Howells server->acregmin = data->acregmin * HZ; 79754ceac45SDavid Howells server->acregmax = data->acregmax * HZ; 79854ceac45SDavid Howells server->acdirmin = data->acdirmin * HZ; 79954ceac45SDavid Howells server->acdirmax = data->acdirmax * HZ; 80054ceac45SDavid Howells 80154ceac45SDavid Howells /* Start lockd here, before we might error out */ 80254ceac45SDavid Howells error = nfs_start_lockd(server); 80354ceac45SDavid Howells if (error < 0) 80454ceac45SDavid Howells goto error; 80554ceac45SDavid Howells 806f22d6d79SChuck Lever server->port = data->nfs_server.port; 807f22d6d79SChuck Lever 80833170233STrond Myklebust error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]); 80954ceac45SDavid Howells if (error < 0) 81054ceac45SDavid Howells goto error; 81154ceac45SDavid Howells 8123f8400d1SChuck Lever /* Preserve the values of mount_server-related mount options */ 8133f8400d1SChuck Lever if (data->mount_server.addrlen) { 8143f8400d1SChuck Lever memcpy(&server->mountd_address, &data->mount_server.address, 8153f8400d1SChuck Lever data->mount_server.addrlen); 8163f8400d1SChuck Lever server->mountd_addrlen = data->mount_server.addrlen; 8173f8400d1SChuck Lever } 8183f8400d1SChuck Lever server->mountd_version = data->mount_server.version; 8193f8400d1SChuck Lever server->mountd_port = data->mount_server.port; 8203f8400d1SChuck Lever server->mountd_protocol = data->mount_server.protocol; 8213f8400d1SChuck Lever 82254ceac45SDavid Howells server->namelen = data->namlen; 82354ceac45SDavid Howells /* Create a client RPC handle for the NFSv3 ACL management interface */ 82454ceac45SDavid Howells nfs_init_server_aclclient(server); 82554ceac45SDavid Howells dprintk("<-- nfs_init_server() = 0 [new %p]\n", clp); 82654ceac45SDavid Howells return 0; 82754ceac45SDavid Howells 82854ceac45SDavid Howells error: 82954ceac45SDavid Howells server->nfs_client = NULL; 83054ceac45SDavid Howells nfs_put_client(clp); 83154ceac45SDavid Howells dprintk("<-- nfs_init_server() = xerror %d\n", error); 83254ceac45SDavid Howells return error; 83354ceac45SDavid Howells } 83454ceac45SDavid Howells 83554ceac45SDavid Howells /* 83654ceac45SDavid Howells * Load up the server record from information gained in an fsinfo record 83754ceac45SDavid Howells */ 83854ceac45SDavid Howells static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo *fsinfo) 83954ceac45SDavid Howells { 84054ceac45SDavid Howells unsigned long max_rpc_payload; 84154ceac45SDavid Howells 84254ceac45SDavid Howells /* Work out a lot of parameters */ 84354ceac45SDavid Howells if (server->rsize == 0) 84454ceac45SDavid Howells server->rsize = nfs_block_size(fsinfo->rtpref, NULL); 84554ceac45SDavid Howells if (server->wsize == 0) 84654ceac45SDavid Howells server->wsize = nfs_block_size(fsinfo->wtpref, NULL); 84754ceac45SDavid Howells 84854ceac45SDavid Howells if (fsinfo->rtmax >= 512 && server->rsize > fsinfo->rtmax) 84954ceac45SDavid Howells server->rsize = nfs_block_size(fsinfo->rtmax, NULL); 85054ceac45SDavid Howells if (fsinfo->wtmax >= 512 && server->wsize > fsinfo->wtmax) 85154ceac45SDavid Howells server->wsize = nfs_block_size(fsinfo->wtmax, NULL); 85254ceac45SDavid Howells 85354ceac45SDavid Howells max_rpc_payload = nfs_block_size(rpc_max_payload(server->client), NULL); 85454ceac45SDavid Howells if (server->rsize > max_rpc_payload) 85554ceac45SDavid Howells server->rsize = max_rpc_payload; 85654ceac45SDavid Howells if (server->rsize > NFS_MAX_FILE_IO_SIZE) 85754ceac45SDavid Howells server->rsize = NFS_MAX_FILE_IO_SIZE; 85854ceac45SDavid Howells server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 859e0bf68ddSPeter Zijlstra 86054ceac45SDavid Howells server->backing_dev_info.ra_pages = server->rpages * NFS_MAX_READAHEAD; 86154ceac45SDavid Howells 86254ceac45SDavid Howells if (server->wsize > max_rpc_payload) 86354ceac45SDavid Howells server->wsize = max_rpc_payload; 86454ceac45SDavid Howells if (server->wsize > NFS_MAX_FILE_IO_SIZE) 86554ceac45SDavid Howells server->wsize = NFS_MAX_FILE_IO_SIZE; 86654ceac45SDavid Howells server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 86754ceac45SDavid Howells server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL); 86854ceac45SDavid Howells 86954ceac45SDavid Howells server->dtsize = nfs_block_size(fsinfo->dtpref, NULL); 87054ceac45SDavid Howells if (server->dtsize > PAGE_CACHE_SIZE) 87154ceac45SDavid Howells server->dtsize = PAGE_CACHE_SIZE; 87254ceac45SDavid Howells if (server->dtsize > server->rsize) 87354ceac45SDavid Howells server->dtsize = server->rsize; 87454ceac45SDavid Howells 87554ceac45SDavid Howells if (server->flags & NFS_MOUNT_NOAC) { 87654ceac45SDavid Howells server->acregmin = server->acregmax = 0; 87754ceac45SDavid Howells server->acdirmin = server->acdirmax = 0; 87854ceac45SDavid Howells } 87954ceac45SDavid Howells 88054ceac45SDavid Howells server->maxfilesize = fsinfo->maxfilesize; 88154ceac45SDavid Howells 88254ceac45SDavid Howells /* We're airborne Set socket buffersize */ 88354ceac45SDavid Howells rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100); 88454ceac45SDavid Howells } 88554ceac45SDavid Howells 88654ceac45SDavid Howells /* 88754ceac45SDavid Howells * Probe filesystem information, including the FSID on v2/v3 88854ceac45SDavid Howells */ 88954ceac45SDavid Howells static int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr) 89054ceac45SDavid Howells { 89154ceac45SDavid Howells struct nfs_fsinfo fsinfo; 89254ceac45SDavid Howells struct nfs_client *clp = server->nfs_client; 89354ceac45SDavid Howells int error; 89454ceac45SDavid Howells 89554ceac45SDavid Howells dprintk("--> nfs_probe_fsinfo()\n"); 89654ceac45SDavid Howells 89754ceac45SDavid Howells if (clp->rpc_ops->set_capabilities != NULL) { 89854ceac45SDavid Howells error = clp->rpc_ops->set_capabilities(server, mntfh); 89954ceac45SDavid Howells if (error < 0) 90054ceac45SDavid Howells goto out_error; 90154ceac45SDavid Howells } 90254ceac45SDavid Howells 90354ceac45SDavid Howells fsinfo.fattr = fattr; 90454ceac45SDavid Howells nfs_fattr_init(fattr); 90554ceac45SDavid Howells error = clp->rpc_ops->fsinfo(server, mntfh, &fsinfo); 90654ceac45SDavid Howells if (error < 0) 90754ceac45SDavid Howells goto out_error; 90854ceac45SDavid Howells 90954ceac45SDavid Howells nfs_server_set_fsinfo(server, &fsinfo); 910e0bf68ddSPeter Zijlstra error = bdi_init(&server->backing_dev_info); 911e0bf68ddSPeter Zijlstra if (error) 912e0bf68ddSPeter Zijlstra goto out_error; 913e0bf68ddSPeter Zijlstra 91454ceac45SDavid Howells 91554ceac45SDavid Howells /* Get some general file system info */ 91654ceac45SDavid Howells if (server->namelen == 0) { 91754ceac45SDavid Howells struct nfs_pathconf pathinfo; 91854ceac45SDavid Howells 91954ceac45SDavid Howells pathinfo.fattr = fattr; 92054ceac45SDavid Howells nfs_fattr_init(fattr); 92154ceac45SDavid Howells 92254ceac45SDavid Howells if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0) 92354ceac45SDavid Howells server->namelen = pathinfo.max_namelen; 92454ceac45SDavid Howells } 92554ceac45SDavid Howells 92654ceac45SDavid Howells dprintk("<-- nfs_probe_fsinfo() = 0\n"); 92754ceac45SDavid Howells return 0; 92854ceac45SDavid Howells 92954ceac45SDavid Howells out_error: 93054ceac45SDavid Howells dprintk("nfs_probe_fsinfo: error = %d\n", -error); 93154ceac45SDavid Howells return error; 93254ceac45SDavid Howells } 93354ceac45SDavid Howells 93454ceac45SDavid Howells /* 93554ceac45SDavid Howells * Copy useful information when duplicating a server record 93654ceac45SDavid Howells */ 93754ceac45SDavid Howells static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source) 93854ceac45SDavid Howells { 93954ceac45SDavid Howells target->flags = source->flags; 94054ceac45SDavid Howells target->acregmin = source->acregmin; 94154ceac45SDavid Howells target->acregmax = source->acregmax; 94254ceac45SDavid Howells target->acdirmin = source->acdirmin; 94354ceac45SDavid Howells target->acdirmax = source->acdirmax; 94454ceac45SDavid Howells target->caps = source->caps; 94554ceac45SDavid Howells } 94654ceac45SDavid Howells 94754ceac45SDavid Howells /* 94854ceac45SDavid Howells * Allocate and initialise a server record 94954ceac45SDavid Howells */ 95054ceac45SDavid Howells static struct nfs_server *nfs_alloc_server(void) 95154ceac45SDavid Howells { 95254ceac45SDavid Howells struct nfs_server *server; 95354ceac45SDavid Howells 95454ceac45SDavid Howells server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL); 95554ceac45SDavid Howells if (!server) 95654ceac45SDavid Howells return NULL; 95754ceac45SDavid Howells 95854ceac45SDavid Howells server->client = server->client_acl = ERR_PTR(-EINVAL); 95954ceac45SDavid Howells 96054ceac45SDavid Howells /* Zero out the NFS state stuff */ 96154ceac45SDavid Howells INIT_LIST_HEAD(&server->client_link); 96254ceac45SDavid Howells INIT_LIST_HEAD(&server->master_link); 96354ceac45SDavid Howells 964ef818a28SSteve Dickson atomic_set(&server->active, 0); 965ef818a28SSteve Dickson 96654ceac45SDavid Howells server->io_stats = nfs_alloc_iostats(); 96754ceac45SDavid Howells if (!server->io_stats) { 96854ceac45SDavid Howells kfree(server); 96954ceac45SDavid Howells return NULL; 97054ceac45SDavid Howells } 97154ceac45SDavid Howells 97254ceac45SDavid Howells return server; 97354ceac45SDavid Howells } 97454ceac45SDavid Howells 97554ceac45SDavid Howells /* 97654ceac45SDavid Howells * Free up a server record 97754ceac45SDavid Howells */ 97854ceac45SDavid Howells void nfs_free_server(struct nfs_server *server) 97954ceac45SDavid Howells { 98054ceac45SDavid Howells dprintk("--> nfs_free_server()\n"); 98154ceac45SDavid Howells 98254ceac45SDavid Howells spin_lock(&nfs_client_lock); 98354ceac45SDavid Howells list_del(&server->client_link); 98454ceac45SDavid Howells list_del(&server->master_link); 98554ceac45SDavid Howells spin_unlock(&nfs_client_lock); 98654ceac45SDavid Howells 98754ceac45SDavid Howells if (server->destroy != NULL) 98854ceac45SDavid Howells server->destroy(server); 9895cef338bSTrond Myklebust 9905cef338bSTrond Myklebust if (!IS_ERR(server->client_acl)) 9915cef338bSTrond Myklebust rpc_shutdown_client(server->client_acl); 99254ceac45SDavid Howells if (!IS_ERR(server->client)) 99354ceac45SDavid Howells rpc_shutdown_client(server->client); 99454ceac45SDavid Howells 99554ceac45SDavid Howells nfs_put_client(server->nfs_client); 99654ceac45SDavid Howells 99754ceac45SDavid Howells nfs_free_iostats(server->io_stats); 998e0bf68ddSPeter Zijlstra bdi_destroy(&server->backing_dev_info); 99954ceac45SDavid Howells kfree(server); 100054ceac45SDavid Howells nfs_release_automount_timer(); 100154ceac45SDavid Howells dprintk("<-- nfs_free_server()\n"); 100254ceac45SDavid Howells } 100354ceac45SDavid Howells 100454ceac45SDavid Howells /* 100554ceac45SDavid Howells * Create a version 2 or 3 volume record 100654ceac45SDavid Howells * - keyed on server and FSID 100754ceac45SDavid Howells */ 10082283f8d6S\"Talpey, Thomas\ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, 100954ceac45SDavid Howells struct nfs_fh *mntfh) 101054ceac45SDavid Howells { 101154ceac45SDavid Howells struct nfs_server *server; 101254ceac45SDavid Howells struct nfs_fattr fattr; 101354ceac45SDavid Howells int error; 101454ceac45SDavid Howells 101554ceac45SDavid Howells server = nfs_alloc_server(); 101654ceac45SDavid Howells if (!server) 101754ceac45SDavid Howells return ERR_PTR(-ENOMEM); 101854ceac45SDavid Howells 101954ceac45SDavid Howells /* Get a client representation */ 102054ceac45SDavid Howells error = nfs_init_server(server, data); 102154ceac45SDavid Howells if (error < 0) 102254ceac45SDavid Howells goto error; 102354ceac45SDavid Howells 102454ceac45SDavid Howells BUG_ON(!server->nfs_client); 102554ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops); 102654ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 102754ceac45SDavid Howells 102854ceac45SDavid Howells /* Probe the root fh to retrieve its FSID */ 102954ceac45SDavid Howells error = nfs_probe_fsinfo(server, mntfh, &fattr); 103054ceac45SDavid Howells if (error < 0) 103154ceac45SDavid Howells goto error; 103254af3bb5STrond Myklebust if (server->nfs_client->rpc_ops->version == 3) { 103354af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) 103454af3bb5STrond Myklebust server->namelen = NFS3_MAXNAMLEN; 103554af3bb5STrond Myklebust if (!(data->flags & NFS_MOUNT_NORDIRPLUS)) 103654af3bb5STrond Myklebust server->caps |= NFS_CAP_READDIRPLUS; 103754af3bb5STrond Myklebust } else { 103854af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN) 103954af3bb5STrond Myklebust server->namelen = NFS2_MAXNAMLEN; 104054af3bb5STrond Myklebust } 104154af3bb5STrond Myklebust 104254ceac45SDavid Howells if (!(fattr.valid & NFS_ATTR_FATTR)) { 104354ceac45SDavid Howells error = server->nfs_client->rpc_ops->getattr(server, mntfh, &fattr); 104454ceac45SDavid Howells if (error < 0) { 104554ceac45SDavid Howells dprintk("nfs_create_server: getattr error = %d\n", -error); 104654ceac45SDavid Howells goto error; 104754ceac45SDavid Howells } 104854ceac45SDavid Howells } 104954ceac45SDavid Howells memcpy(&server->fsid, &fattr.fsid, sizeof(server->fsid)); 105054ceac45SDavid Howells 10516daabf1bSDavid Howells dprintk("Server FSID: %llx:%llx\n", 10526daabf1bSDavid Howells (unsigned long long) server->fsid.major, 10536daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 105454ceac45SDavid Howells 105554ceac45SDavid Howells BUG_ON(!server->nfs_client); 105654ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops); 105754ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 105854ceac45SDavid Howells 105954ceac45SDavid Howells spin_lock(&nfs_client_lock); 106054ceac45SDavid Howells list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); 106154ceac45SDavid Howells list_add_tail(&server->master_link, &nfs_volume_list); 106254ceac45SDavid Howells spin_unlock(&nfs_client_lock); 106354ceac45SDavid Howells 106454ceac45SDavid Howells server->mount_time = jiffies; 106554ceac45SDavid Howells return server; 106654ceac45SDavid Howells 106754ceac45SDavid Howells error: 106854ceac45SDavid Howells nfs_free_server(server); 106954ceac45SDavid Howells return ERR_PTR(error); 107054ceac45SDavid Howells } 107154ceac45SDavid Howells 107254ceac45SDavid Howells #ifdef CONFIG_NFS_V4 107354ceac45SDavid Howells /* 1074557134a3SAndy Adamson * Initialize the minor version specific parts of an NFS4 client record 1075557134a3SAndy Adamson */ 1076557134a3SAndy Adamson static int nfs4_init_client_minor_version(struct nfs_client *clp) 1077557134a3SAndy Adamson { 1078*cccef3b9SAndy Adamson clp->cl_call_sync = _nfs4_call_sync; 1079*cccef3b9SAndy Adamson 1080557134a3SAndy Adamson #if defined(CONFIG_NFS_V4_1) 1081557134a3SAndy Adamson if (clp->cl_minorversion) { 1082557134a3SAndy Adamson struct nfs4_session *session = NULL; 1083557134a3SAndy Adamson /* 1084557134a3SAndy Adamson * Create the session and mark it expired. 1085557134a3SAndy Adamson * When a SEQUENCE operation encounters the expired session 1086557134a3SAndy Adamson * it will do session recovery to initialize it. 1087557134a3SAndy Adamson */ 1088557134a3SAndy Adamson session = nfs4_alloc_session(clp); 1089557134a3SAndy Adamson if (!session) 1090557134a3SAndy Adamson return -ENOMEM; 1091557134a3SAndy Adamson 1092557134a3SAndy Adamson clp->cl_session = session; 1093*cccef3b9SAndy Adamson clp->cl_call_sync = _nfs4_call_sync_session; 1094557134a3SAndy Adamson } 1095557134a3SAndy Adamson #endif /* CONFIG_NFS_V4_1 */ 1096557134a3SAndy Adamson 1097557134a3SAndy Adamson return 0; 1098557134a3SAndy Adamson } 1099557134a3SAndy Adamson 1100557134a3SAndy Adamson /* 110154ceac45SDavid Howells * Initialise an NFS4 client record 110254ceac45SDavid Howells */ 110354ceac45SDavid Howells static int nfs4_init_client(struct nfs_client *clp, 110433170233STrond Myklebust const struct rpc_timeout *timeparms, 11057d9ac06fSJ. Bruce Fields const char *ip_addr, 1106d740351bSChuck Lever rpc_authflavor_t authflavour, 1107d740351bSChuck Lever int flags) 110854ceac45SDavid Howells { 110954ceac45SDavid Howells int error; 111054ceac45SDavid Howells 111154ceac45SDavid Howells if (clp->cl_cons_state == NFS_CS_READY) { 111254ceac45SDavid Howells /* the client is initialised already */ 111354ceac45SDavid Howells dprintk("<-- nfs4_init_client() = 0 [already %p]\n", clp); 111454ceac45SDavid Howells return 0; 111554ceac45SDavid Howells } 111654ceac45SDavid Howells 111754ceac45SDavid Howells /* Check NFS protocol revision and initialize RPC op vector */ 111854ceac45SDavid Howells clp->rpc_ops = &nfs_v4_clientops; 111954ceac45SDavid Howells 112059dca3b2STrond Myklebust error = nfs_create_rpc_client(clp, timeparms, authflavour, 1121d740351bSChuck Lever 1, flags & NFS_MOUNT_NORESVPORT); 112254ceac45SDavid Howells if (error < 0) 112354ceac45SDavid Howells goto error; 11247d9ac06fSJ. Bruce Fields memcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr)); 112554ceac45SDavid Howells 112654ceac45SDavid Howells error = nfs_idmap_new(clp); 112754ceac45SDavid Howells if (error < 0) { 112854ceac45SDavid Howells dprintk("%s: failed to create idmapper. Error = %d\n", 11293110ff80SHarvey Harrison __func__, error); 113054ceac45SDavid Howells goto error; 113154ceac45SDavid Howells } 11329c5bf38dSTrond Myklebust __set_bit(NFS_CS_IDMAP, &clp->cl_res_state); 113354ceac45SDavid Howells 1134557134a3SAndy Adamson error = nfs4_init_client_minor_version(clp); 1135557134a3SAndy Adamson if (error < 0) 1136557134a3SAndy Adamson goto error; 1137557134a3SAndy Adamson 113854ceac45SDavid Howells nfs_mark_client_ready(clp, NFS_CS_READY); 113954ceac45SDavid Howells return 0; 114054ceac45SDavid Howells 114154ceac45SDavid Howells error: 114254ceac45SDavid Howells nfs_mark_client_ready(clp, error); 114354ceac45SDavid Howells dprintk("<-- nfs4_init_client() = xerror %d\n", error); 114454ceac45SDavid Howells return error; 114554ceac45SDavid Howells } 114654ceac45SDavid Howells 114754ceac45SDavid Howells /* 114854ceac45SDavid Howells * Set up an NFS4 client 114954ceac45SDavid Howells */ 115054ceac45SDavid Howells static int nfs4_set_client(struct nfs_server *server, 1151dcecae0fSChuck Lever const char *hostname, 1152dcecae0fSChuck Lever const struct sockaddr *addr, 1153dcecae0fSChuck Lever const size_t addrlen, 11547d9ac06fSJ. Bruce Fields const char *ip_addr, 115554ceac45SDavid Howells rpc_authflavor_t authflavour, 115694a417f3SBenny Halevy int proto, const struct rpc_timeout *timeparms, 115794a417f3SBenny Halevy u32 minorversion) 115854ceac45SDavid Howells { 11593a498026STrond Myklebust struct nfs_client_initdata cl_init = { 11603a498026STrond Myklebust .hostname = hostname, 1161dcecae0fSChuck Lever .addr = addr, 1162dcecae0fSChuck Lever .addrlen = addrlen, 116340c55319STrond Myklebust .rpc_ops = &nfs_v4_clientops, 116459dca3b2STrond Myklebust .proto = proto, 11655aae4a9aSBenny Halevy .minorversion = minorversion, 11663a498026STrond Myklebust }; 116754ceac45SDavid Howells struct nfs_client *clp; 116854ceac45SDavid Howells int error; 116954ceac45SDavid Howells 117054ceac45SDavid Howells dprintk("--> nfs4_set_client()\n"); 117154ceac45SDavid Howells 117254ceac45SDavid Howells /* Allocate or find a client reference we can use */ 11733a498026STrond Myklebust clp = nfs_get_client(&cl_init); 117454ceac45SDavid Howells if (IS_ERR(clp)) { 117554ceac45SDavid Howells error = PTR_ERR(clp); 117654ceac45SDavid Howells goto error; 117754ceac45SDavid Howells } 1178d740351bSChuck Lever error = nfs4_init_client(clp, timeparms, ip_addr, authflavour, 1179d740351bSChuck Lever server->flags); 118054ceac45SDavid Howells if (error < 0) 118154ceac45SDavid Howells goto error_put; 118254ceac45SDavid Howells 118354ceac45SDavid Howells server->nfs_client = clp; 118454ceac45SDavid Howells dprintk("<-- nfs4_set_client() = 0 [new %p]\n", clp); 118554ceac45SDavid Howells return 0; 118654ceac45SDavid Howells 118754ceac45SDavid Howells error_put: 118854ceac45SDavid Howells nfs_put_client(clp); 118954ceac45SDavid Howells error: 119054ceac45SDavid Howells dprintk("<-- nfs4_set_client() = xerror %d\n", error); 119154ceac45SDavid Howells return error; 119254ceac45SDavid Howells } 119354ceac45SDavid Howells 119454ceac45SDavid Howells /* 1195557134a3SAndy Adamson * Initialize a session. 1196557134a3SAndy Adamson * Note: save the mount rsize and wsize for create_server negotiation. 1197557134a3SAndy Adamson */ 1198557134a3SAndy Adamson static void nfs4_init_session(struct nfs_client *clp, 1199557134a3SAndy Adamson unsigned int wsize, unsigned int rsize) 1200557134a3SAndy Adamson { 1201557134a3SAndy Adamson #if defined(CONFIG_NFS_V4_1) 1202557134a3SAndy Adamson if (nfs4_has_session(clp)) { 1203557134a3SAndy Adamson clp->cl_session->fc_attrs.max_rqst_sz = wsize; 1204557134a3SAndy Adamson clp->cl_session->fc_attrs.max_resp_sz = rsize; 1205557134a3SAndy Adamson } 1206557134a3SAndy Adamson #endif /* CONFIG_NFS_V4_1 */ 1207557134a3SAndy Adamson } 1208557134a3SAndy Adamson 1209557134a3SAndy Adamson /* 121054ceac45SDavid Howells * Create a version 4 volume record 121154ceac45SDavid Howells */ 121254ceac45SDavid Howells static int nfs4_init_server(struct nfs_server *server, 121391ea40b9S\"Talpey, Thomas\ const struct nfs_parsed_mount_data *data) 121454ceac45SDavid Howells { 121533170233STrond Myklebust struct rpc_timeout timeparms; 121654ceac45SDavid Howells int error; 121754ceac45SDavid Howells 121854ceac45SDavid Howells dprintk("--> nfs4_init_server()\n"); 121954ceac45SDavid Howells 122033170233STrond Myklebust nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, 122133170233STrond Myklebust data->timeo, data->retrans); 122233170233STrond Myklebust 1223542fcc33SChuck Lever /* Initialise the client representation from the mount data */ 1224542fcc33SChuck Lever server->flags = data->flags; 1225542fcc33SChuck Lever server->caps |= NFS_CAP_ATOMIC_OPEN; 1226b797cac7SDavid Howells server->options = data->options; 1227542fcc33SChuck Lever 122833170233STrond Myklebust /* Get a client record */ 122933170233STrond Myklebust error = nfs4_set_client(server, 123033170233STrond Myklebust data->nfs_server.hostname, 123133170233STrond Myklebust (const struct sockaddr *)&data->nfs_server.address, 123233170233STrond Myklebust data->nfs_server.addrlen, 123333170233STrond Myklebust data->client_address, 123433170233STrond Myklebust data->auth_flavors[0], 123533170233STrond Myklebust data->nfs_server.protocol, 123694a417f3SBenny Halevy &timeparms, 123794a417f3SBenny Halevy data->minorversion); 123833170233STrond Myklebust if (error < 0) 123933170233STrond Myklebust goto error; 124033170233STrond Myklebust 124154ceac45SDavid Howells if (data->rsize) 124254ceac45SDavid Howells server->rsize = nfs_block_size(data->rsize, NULL); 124354ceac45SDavid Howells if (data->wsize) 124454ceac45SDavid Howells server->wsize = nfs_block_size(data->wsize, NULL); 124554ceac45SDavid Howells 124654ceac45SDavid Howells server->acregmin = data->acregmin * HZ; 124754ceac45SDavid Howells server->acregmax = data->acregmax * HZ; 124854ceac45SDavid Howells server->acdirmin = data->acdirmin * HZ; 124954ceac45SDavid Howells server->acdirmax = data->acdirmax * HZ; 125054ceac45SDavid Howells 1251f22d6d79SChuck Lever server->port = data->nfs_server.port; 1252f22d6d79SChuck Lever 125333170233STrond Myklebust error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]); 125454ceac45SDavid Howells 125533170233STrond Myklebust error: 125654ceac45SDavid Howells /* Done */ 125754ceac45SDavid Howells dprintk("<-- nfs4_init_server() = %d\n", error); 125854ceac45SDavid Howells return error; 125954ceac45SDavid Howells } 126054ceac45SDavid Howells 126154ceac45SDavid Howells /* 126254ceac45SDavid Howells * Create a version 4 volume record 126354ceac45SDavid Howells * - keyed on server and FSID 126454ceac45SDavid Howells */ 126591ea40b9S\"Talpey, Thomas\ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, 126654ceac45SDavid Howells struct nfs_fh *mntfh) 126754ceac45SDavid Howells { 126854ceac45SDavid Howells struct nfs_fattr fattr; 126954ceac45SDavid Howells struct nfs_server *server; 127054ceac45SDavid Howells int error; 127154ceac45SDavid Howells 127254ceac45SDavid Howells dprintk("--> nfs4_create_server()\n"); 127354ceac45SDavid Howells 127454ceac45SDavid Howells server = nfs_alloc_server(); 127554ceac45SDavid Howells if (!server) 127654ceac45SDavid Howells return ERR_PTR(-ENOMEM); 127754ceac45SDavid Howells 127854ceac45SDavid Howells /* set up the general RPC client */ 127991ea40b9S\"Talpey, Thomas\ error = nfs4_init_server(server, data); 128054ceac45SDavid Howells if (error < 0) 128154ceac45SDavid Howells goto error; 128254ceac45SDavid Howells 128354ceac45SDavid Howells BUG_ON(!server->nfs_client); 128454ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops); 128554ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 128654ceac45SDavid Howells 1287557134a3SAndy Adamson nfs4_init_session(server->nfs_client, server->wsize, server->rsize); 1288557134a3SAndy Adamson 128954ceac45SDavid Howells /* Probe the root fh to retrieve its FSID */ 129091ea40b9S\"Talpey, Thomas\ error = nfs4_path_walk(server, mntfh, data->nfs_server.export_path); 129154ceac45SDavid Howells if (error < 0) 129254ceac45SDavid Howells goto error; 129354ceac45SDavid Howells 12946daabf1bSDavid Howells dprintk("Server FSID: %llx:%llx\n", 12956daabf1bSDavid Howells (unsigned long long) server->fsid.major, 12966daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 129754ceac45SDavid Howells dprintk("Mount FH: %d\n", mntfh->size); 129854ceac45SDavid Howells 129954ceac45SDavid Howells error = nfs_probe_fsinfo(server, mntfh, &fattr); 130054ceac45SDavid Howells if (error < 0) 130154ceac45SDavid Howells goto error; 130254ceac45SDavid Howells 130354af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 130454af3bb5STrond Myklebust server->namelen = NFS4_MAXNAMLEN; 130554af3bb5STrond Myklebust 130654ceac45SDavid Howells BUG_ON(!server->nfs_client); 130754ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops); 130854ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 130954ceac45SDavid Howells 131054ceac45SDavid Howells spin_lock(&nfs_client_lock); 131154ceac45SDavid Howells list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); 131254ceac45SDavid Howells list_add_tail(&server->master_link, &nfs_volume_list); 131354ceac45SDavid Howells spin_unlock(&nfs_client_lock); 131454ceac45SDavid Howells 131554ceac45SDavid Howells server->mount_time = jiffies; 131654ceac45SDavid Howells dprintk("<-- nfs4_create_server() = %p\n", server); 131754ceac45SDavid Howells return server; 131854ceac45SDavid Howells 131954ceac45SDavid Howells error: 132054ceac45SDavid Howells nfs_free_server(server); 132154ceac45SDavid Howells dprintk("<-- nfs4_create_server() = error %d\n", error); 132254ceac45SDavid Howells return ERR_PTR(error); 132354ceac45SDavid Howells } 132454ceac45SDavid Howells 132554ceac45SDavid Howells /* 132654ceac45SDavid Howells * Create an NFS4 referral server record 132754ceac45SDavid Howells */ 132854ceac45SDavid Howells struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, 1329f2d0d85eSTrond Myklebust struct nfs_fh *mntfh) 133054ceac45SDavid Howells { 133154ceac45SDavid Howells struct nfs_client *parent_client; 133254ceac45SDavid Howells struct nfs_server *server, *parent_server; 133354ceac45SDavid Howells struct nfs_fattr fattr; 133454ceac45SDavid Howells int error; 133554ceac45SDavid Howells 133654ceac45SDavid Howells dprintk("--> nfs4_create_referral_server()\n"); 133754ceac45SDavid Howells 133854ceac45SDavid Howells server = nfs_alloc_server(); 133954ceac45SDavid Howells if (!server) 134054ceac45SDavid Howells return ERR_PTR(-ENOMEM); 134154ceac45SDavid Howells 134254ceac45SDavid Howells parent_server = NFS_SB(data->sb); 134354ceac45SDavid Howells parent_client = parent_server->nfs_client; 134454ceac45SDavid Howells 1345542fcc33SChuck Lever /* Initialise the client representation from the parent server */ 1346542fcc33SChuck Lever nfs_server_copy_userdata(server, parent_server); 1347542fcc33SChuck Lever server->caps |= NFS_CAP_ATOMIC_OPEN; 1348542fcc33SChuck Lever 134954ceac45SDavid Howells /* Get a client representation. 135054ceac45SDavid Howells * Note: NFSv4 always uses TCP, */ 1351dcecae0fSChuck Lever error = nfs4_set_client(server, data->hostname, 13526677d095SChuck Lever data->addr, 13536677d095SChuck Lever data->addrlen, 13547d9ac06fSJ. Bruce Fields parent_client->cl_ipaddr, 135554ceac45SDavid Howells data->authflavor, 135654ceac45SDavid Howells parent_server->client->cl_xprt->prot, 135794a417f3SBenny Halevy parent_server->client->cl_timeout, 135894a417f3SBenny Halevy parent_client->cl_minorversion); 1359297de4f6Sandros@citi.umich.edu if (error < 0) 1360297de4f6Sandros@citi.umich.edu goto error; 136154ceac45SDavid Howells 136233170233STrond Myklebust error = nfs_init_server_rpcclient(server, parent_server->client->cl_timeout, data->authflavor); 136354ceac45SDavid Howells if (error < 0) 136454ceac45SDavid Howells goto error; 136554ceac45SDavid Howells 136654ceac45SDavid Howells BUG_ON(!server->nfs_client); 136754ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops); 136854ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 136954ceac45SDavid Howells 1370f2d0d85eSTrond Myklebust /* Probe the root fh to retrieve its FSID and filehandle */ 1371f2d0d85eSTrond Myklebust error = nfs4_path_walk(server, mntfh, data->mnt_path); 1372f2d0d85eSTrond Myklebust if (error < 0) 1373f2d0d85eSTrond Myklebust goto error; 1374f2d0d85eSTrond Myklebust 137554ceac45SDavid Howells /* probe the filesystem info for this server filesystem */ 1376f2d0d85eSTrond Myklebust error = nfs_probe_fsinfo(server, mntfh, &fattr); 137754ceac45SDavid Howells if (error < 0) 137854ceac45SDavid Howells goto error; 137954ceac45SDavid Howells 138054af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 138154af3bb5STrond Myklebust server->namelen = NFS4_MAXNAMLEN; 138254af3bb5STrond Myklebust 138354ceac45SDavid Howells dprintk("Referral FSID: %llx:%llx\n", 13846daabf1bSDavid Howells (unsigned long long) server->fsid.major, 13856daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 138654ceac45SDavid Howells 138754ceac45SDavid Howells spin_lock(&nfs_client_lock); 138854ceac45SDavid Howells list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); 138954ceac45SDavid Howells list_add_tail(&server->master_link, &nfs_volume_list); 139054ceac45SDavid Howells spin_unlock(&nfs_client_lock); 139154ceac45SDavid Howells 139254ceac45SDavid Howells server->mount_time = jiffies; 139354ceac45SDavid Howells 139454ceac45SDavid Howells dprintk("<-- nfs_create_referral_server() = %p\n", server); 139554ceac45SDavid Howells return server; 139654ceac45SDavid Howells 139754ceac45SDavid Howells error: 139854ceac45SDavid Howells nfs_free_server(server); 139954ceac45SDavid Howells dprintk("<-- nfs4_create_referral_server() = error %d\n", error); 140054ceac45SDavid Howells return ERR_PTR(error); 140154ceac45SDavid Howells } 140254ceac45SDavid Howells 140354ceac45SDavid Howells #endif /* CONFIG_NFS_V4 */ 140454ceac45SDavid Howells 140554ceac45SDavid Howells /* 140654ceac45SDavid Howells * Clone an NFS2, NFS3 or NFS4 server record 140754ceac45SDavid Howells */ 140854ceac45SDavid Howells struct nfs_server *nfs_clone_server(struct nfs_server *source, 140954ceac45SDavid Howells struct nfs_fh *fh, 141054ceac45SDavid Howells struct nfs_fattr *fattr) 141154ceac45SDavid Howells { 141254ceac45SDavid Howells struct nfs_server *server; 141354ceac45SDavid Howells struct nfs_fattr fattr_fsinfo; 141454ceac45SDavid Howells int error; 141554ceac45SDavid Howells 141654ceac45SDavid Howells dprintk("--> nfs_clone_server(,%llx:%llx,)\n", 14176daabf1bSDavid Howells (unsigned long long) fattr->fsid.major, 14186daabf1bSDavid Howells (unsigned long long) fattr->fsid.minor); 141954ceac45SDavid Howells 142054ceac45SDavid Howells server = nfs_alloc_server(); 142154ceac45SDavid Howells if (!server) 142254ceac45SDavid Howells return ERR_PTR(-ENOMEM); 142354ceac45SDavid Howells 142454ceac45SDavid Howells /* Copy data from the source */ 142554ceac45SDavid Howells server->nfs_client = source->nfs_client; 142654ceac45SDavid Howells atomic_inc(&server->nfs_client->cl_count); 142754ceac45SDavid Howells nfs_server_copy_userdata(server, source); 142854ceac45SDavid Howells 142954ceac45SDavid Howells server->fsid = fattr->fsid; 143054ceac45SDavid Howells 143133170233STrond Myklebust error = nfs_init_server_rpcclient(server, 143233170233STrond Myklebust source->client->cl_timeout, 143333170233STrond Myklebust source->client->cl_auth->au_flavor); 143454ceac45SDavid Howells if (error < 0) 143554ceac45SDavid Howells goto out_free_server; 143654ceac45SDavid Howells if (!IS_ERR(source->client_acl)) 143754ceac45SDavid Howells nfs_init_server_aclclient(server); 143854ceac45SDavid Howells 143954ceac45SDavid Howells /* probe the filesystem info for this server filesystem */ 144054ceac45SDavid Howells error = nfs_probe_fsinfo(server, fh, &fattr_fsinfo); 144154ceac45SDavid Howells if (error < 0) 144254ceac45SDavid Howells goto out_free_server; 144354ceac45SDavid Howells 144454af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 144554af3bb5STrond Myklebust server->namelen = NFS4_MAXNAMLEN; 144654af3bb5STrond Myklebust 144754ceac45SDavid Howells dprintk("Cloned FSID: %llx:%llx\n", 14486daabf1bSDavid Howells (unsigned long long) server->fsid.major, 14496daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 145054ceac45SDavid Howells 145154ceac45SDavid Howells error = nfs_start_lockd(server); 145254ceac45SDavid Howells if (error < 0) 145354ceac45SDavid Howells goto out_free_server; 145454ceac45SDavid Howells 145554ceac45SDavid Howells spin_lock(&nfs_client_lock); 145654ceac45SDavid Howells list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); 145754ceac45SDavid Howells list_add_tail(&server->master_link, &nfs_volume_list); 145854ceac45SDavid Howells spin_unlock(&nfs_client_lock); 145954ceac45SDavid Howells 146054ceac45SDavid Howells server->mount_time = jiffies; 146154ceac45SDavid Howells 146254ceac45SDavid Howells dprintk("<-- nfs_clone_server() = %p\n", server); 146354ceac45SDavid Howells return server; 146454ceac45SDavid Howells 146554ceac45SDavid Howells out_free_server: 146654ceac45SDavid Howells nfs_free_server(server); 146754ceac45SDavid Howells dprintk("<-- nfs_clone_server() = error %d\n", error); 146854ceac45SDavid Howells return ERR_PTR(error); 146954ceac45SDavid Howells } 14706aaca566SDavid Howells 14716aaca566SDavid Howells #ifdef CONFIG_PROC_FS 14726aaca566SDavid Howells static struct proc_dir_entry *proc_fs_nfs; 14736aaca566SDavid Howells 14746aaca566SDavid Howells static int nfs_server_list_open(struct inode *inode, struct file *file); 14756aaca566SDavid Howells static void *nfs_server_list_start(struct seq_file *p, loff_t *pos); 14766aaca566SDavid Howells static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos); 14776aaca566SDavid Howells static void nfs_server_list_stop(struct seq_file *p, void *v); 14786aaca566SDavid Howells static int nfs_server_list_show(struct seq_file *m, void *v); 14796aaca566SDavid Howells 14806aaca566SDavid Howells static struct seq_operations nfs_server_list_ops = { 14816aaca566SDavid Howells .start = nfs_server_list_start, 14826aaca566SDavid Howells .next = nfs_server_list_next, 14836aaca566SDavid Howells .stop = nfs_server_list_stop, 14846aaca566SDavid Howells .show = nfs_server_list_show, 14856aaca566SDavid Howells }; 14866aaca566SDavid Howells 148700977a59SArjan van de Ven static const struct file_operations nfs_server_list_fops = { 14886aaca566SDavid Howells .open = nfs_server_list_open, 14896aaca566SDavid Howells .read = seq_read, 14906aaca566SDavid Howells .llseek = seq_lseek, 14916aaca566SDavid Howells .release = seq_release, 149234b37235SDenis V. Lunev .owner = THIS_MODULE, 14936aaca566SDavid Howells }; 14946aaca566SDavid Howells 14956aaca566SDavid Howells static int nfs_volume_list_open(struct inode *inode, struct file *file); 14966aaca566SDavid Howells static void *nfs_volume_list_start(struct seq_file *p, loff_t *pos); 14976aaca566SDavid Howells static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos); 14986aaca566SDavid Howells static void nfs_volume_list_stop(struct seq_file *p, void *v); 14996aaca566SDavid Howells static int nfs_volume_list_show(struct seq_file *m, void *v); 15006aaca566SDavid Howells 15016aaca566SDavid Howells static struct seq_operations nfs_volume_list_ops = { 15026aaca566SDavid Howells .start = nfs_volume_list_start, 15036aaca566SDavid Howells .next = nfs_volume_list_next, 15046aaca566SDavid Howells .stop = nfs_volume_list_stop, 15056aaca566SDavid Howells .show = nfs_volume_list_show, 15066aaca566SDavid Howells }; 15076aaca566SDavid Howells 150800977a59SArjan van de Ven static const struct file_operations nfs_volume_list_fops = { 15096aaca566SDavid Howells .open = nfs_volume_list_open, 15106aaca566SDavid Howells .read = seq_read, 15116aaca566SDavid Howells .llseek = seq_lseek, 15126aaca566SDavid Howells .release = seq_release, 151334b37235SDenis V. Lunev .owner = THIS_MODULE, 15146aaca566SDavid Howells }; 15156aaca566SDavid Howells 15166aaca566SDavid Howells /* 15176aaca566SDavid Howells * open "/proc/fs/nfsfs/servers" which provides a summary of servers with which 15186aaca566SDavid Howells * we're dealing 15196aaca566SDavid Howells */ 15206aaca566SDavid Howells static int nfs_server_list_open(struct inode *inode, struct file *file) 15216aaca566SDavid Howells { 15226aaca566SDavid Howells struct seq_file *m; 15236aaca566SDavid Howells int ret; 15246aaca566SDavid Howells 15256aaca566SDavid Howells ret = seq_open(file, &nfs_server_list_ops); 15266aaca566SDavid Howells if (ret < 0) 15276aaca566SDavid Howells return ret; 15286aaca566SDavid Howells 15296aaca566SDavid Howells m = file->private_data; 15306aaca566SDavid Howells m->private = PDE(inode)->data; 15316aaca566SDavid Howells 15326aaca566SDavid Howells return 0; 15336aaca566SDavid Howells } 15346aaca566SDavid Howells 15356aaca566SDavid Howells /* 15366aaca566SDavid Howells * set up the iterator to start reading from the server list and return the first item 15376aaca566SDavid Howells */ 15386aaca566SDavid Howells static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos) 15396aaca566SDavid Howells { 15406aaca566SDavid Howells /* lock the list against modification */ 15416aaca566SDavid Howells spin_lock(&nfs_client_lock); 1542259902eaSPavel Emelianov return seq_list_start_head(&nfs_client_list, *_pos); 15436aaca566SDavid Howells } 15446aaca566SDavid Howells 15456aaca566SDavid Howells /* 15466aaca566SDavid Howells * move to next server 15476aaca566SDavid Howells */ 15486aaca566SDavid Howells static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos) 15496aaca566SDavid Howells { 1550259902eaSPavel Emelianov return seq_list_next(v, &nfs_client_list, pos); 15516aaca566SDavid Howells } 15526aaca566SDavid Howells 15536aaca566SDavid Howells /* 15546aaca566SDavid Howells * clean up after reading from the transports list 15556aaca566SDavid Howells */ 15566aaca566SDavid Howells static void nfs_server_list_stop(struct seq_file *p, void *v) 15576aaca566SDavid Howells { 15586aaca566SDavid Howells spin_unlock(&nfs_client_lock); 15596aaca566SDavid Howells } 15606aaca566SDavid Howells 15616aaca566SDavid Howells /* 15626aaca566SDavid Howells * display a header line followed by a load of call lines 15636aaca566SDavid Howells */ 15646aaca566SDavid Howells static int nfs_server_list_show(struct seq_file *m, void *v) 15656aaca566SDavid Howells { 15666aaca566SDavid Howells struct nfs_client *clp; 15676aaca566SDavid Howells 15686aaca566SDavid Howells /* display header on line 1 */ 1569259902eaSPavel Emelianov if (v == &nfs_client_list) { 15706aaca566SDavid Howells seq_puts(m, "NV SERVER PORT USE HOSTNAME\n"); 15716aaca566SDavid Howells return 0; 15726aaca566SDavid Howells } 15736aaca566SDavid Howells 15746aaca566SDavid Howells /* display one transport per line on subsequent lines */ 15756aaca566SDavid Howells clp = list_entry(v, struct nfs_client, cl_share_link); 15766aaca566SDavid Howells 15775d8515caSChuck Lever seq_printf(m, "v%u %s %s %3d %s\n", 157840c55319STrond Myklebust clp->rpc_ops->version, 15795d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), 15805d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), 15816aaca566SDavid Howells atomic_read(&clp->cl_count), 15826aaca566SDavid Howells clp->cl_hostname); 15836aaca566SDavid Howells 15846aaca566SDavid Howells return 0; 15856aaca566SDavid Howells } 15866aaca566SDavid Howells 15876aaca566SDavid Howells /* 15886aaca566SDavid Howells * open "/proc/fs/nfsfs/volumes" which provides a summary of extant volumes 15896aaca566SDavid Howells */ 15906aaca566SDavid Howells static int nfs_volume_list_open(struct inode *inode, struct file *file) 15916aaca566SDavid Howells { 15926aaca566SDavid Howells struct seq_file *m; 15936aaca566SDavid Howells int ret; 15946aaca566SDavid Howells 15956aaca566SDavid Howells ret = seq_open(file, &nfs_volume_list_ops); 15966aaca566SDavid Howells if (ret < 0) 15976aaca566SDavid Howells return ret; 15986aaca566SDavid Howells 15996aaca566SDavid Howells m = file->private_data; 16006aaca566SDavid Howells m->private = PDE(inode)->data; 16016aaca566SDavid Howells 16026aaca566SDavid Howells return 0; 16036aaca566SDavid Howells } 16046aaca566SDavid Howells 16056aaca566SDavid Howells /* 16066aaca566SDavid Howells * set up the iterator to start reading from the volume list and return the first item 16076aaca566SDavid Howells */ 16086aaca566SDavid Howells static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos) 16096aaca566SDavid Howells { 16106aaca566SDavid Howells /* lock the list against modification */ 16116aaca566SDavid Howells spin_lock(&nfs_client_lock); 1612259902eaSPavel Emelianov return seq_list_start_head(&nfs_volume_list, *_pos); 16136aaca566SDavid Howells } 16146aaca566SDavid Howells 16156aaca566SDavid Howells /* 16166aaca566SDavid Howells * move to next volume 16176aaca566SDavid Howells */ 16186aaca566SDavid Howells static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos) 16196aaca566SDavid Howells { 1620259902eaSPavel Emelianov return seq_list_next(v, &nfs_volume_list, pos); 16216aaca566SDavid Howells } 16226aaca566SDavid Howells 16236aaca566SDavid Howells /* 16246aaca566SDavid Howells * clean up after reading from the transports list 16256aaca566SDavid Howells */ 16266aaca566SDavid Howells static void nfs_volume_list_stop(struct seq_file *p, void *v) 16276aaca566SDavid Howells { 16286aaca566SDavid Howells spin_unlock(&nfs_client_lock); 16296aaca566SDavid Howells } 16306aaca566SDavid Howells 16316aaca566SDavid Howells /* 16326aaca566SDavid Howells * display a header line followed by a load of call lines 16336aaca566SDavid Howells */ 16346aaca566SDavid Howells static int nfs_volume_list_show(struct seq_file *m, void *v) 16356aaca566SDavid Howells { 16366aaca566SDavid Howells struct nfs_server *server; 16376aaca566SDavid Howells struct nfs_client *clp; 16386aaca566SDavid Howells char dev[8], fsid[17]; 16396aaca566SDavid Howells 16406aaca566SDavid Howells /* display header on line 1 */ 1641259902eaSPavel Emelianov if (v == &nfs_volume_list) { 16425d1acff1SDavid Howells seq_puts(m, "NV SERVER PORT DEV FSID FSC\n"); 16436aaca566SDavid Howells return 0; 16446aaca566SDavid Howells } 16456aaca566SDavid Howells /* display one transport per line on subsequent lines */ 16466aaca566SDavid Howells server = list_entry(v, struct nfs_server, master_link); 16476aaca566SDavid Howells clp = server->nfs_client; 16486aaca566SDavid Howells 16496aaca566SDavid Howells snprintf(dev, 8, "%u:%u", 16506aaca566SDavid Howells MAJOR(server->s_dev), MINOR(server->s_dev)); 16516aaca566SDavid Howells 16526aaca566SDavid Howells snprintf(fsid, 17, "%llx:%llx", 16536daabf1bSDavid Howells (unsigned long long) server->fsid.major, 16546daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 16556aaca566SDavid Howells 16565d1acff1SDavid Howells seq_printf(m, "v%u %s %s %-7s %-17s %s\n", 165740c55319STrond Myklebust clp->rpc_ops->version, 16585d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), 16595d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), 16606aaca566SDavid Howells dev, 16615d1acff1SDavid Howells fsid, 16625d1acff1SDavid Howells nfs_server_fscache_state(server)); 16636aaca566SDavid Howells 16646aaca566SDavid Howells return 0; 16656aaca566SDavid Howells } 16666aaca566SDavid Howells 16676aaca566SDavid Howells /* 16686aaca566SDavid Howells * initialise the /proc/fs/nfsfs/ directory 16696aaca566SDavid Howells */ 16706aaca566SDavid Howells int __init nfs_fs_proc_init(void) 16716aaca566SDavid Howells { 16726aaca566SDavid Howells struct proc_dir_entry *p; 16736aaca566SDavid Howells 167436a5aeb8SAlexey Dobriyan proc_fs_nfs = proc_mkdir("fs/nfsfs", NULL); 16756aaca566SDavid Howells if (!proc_fs_nfs) 16766aaca566SDavid Howells goto error_0; 16776aaca566SDavid Howells 16786aaca566SDavid Howells /* a file of servers with which we're dealing */ 167934b37235SDenis V. Lunev p = proc_create("servers", S_IFREG|S_IRUGO, 168034b37235SDenis V. Lunev proc_fs_nfs, &nfs_server_list_fops); 16816aaca566SDavid Howells if (!p) 16826aaca566SDavid Howells goto error_1; 16836aaca566SDavid Howells 16846aaca566SDavid Howells /* a file of volumes that we have mounted */ 168534b37235SDenis V. Lunev p = proc_create("volumes", S_IFREG|S_IRUGO, 168634b37235SDenis V. Lunev proc_fs_nfs, &nfs_volume_list_fops); 16876aaca566SDavid Howells if (!p) 16886aaca566SDavid Howells goto error_2; 16896aaca566SDavid Howells return 0; 16906aaca566SDavid Howells 16916aaca566SDavid Howells error_2: 16926aaca566SDavid Howells remove_proc_entry("servers", proc_fs_nfs); 16936aaca566SDavid Howells error_1: 169436a5aeb8SAlexey Dobriyan remove_proc_entry("fs/nfsfs", NULL); 16956aaca566SDavid Howells error_0: 16966aaca566SDavid Howells return -ENOMEM; 16976aaca566SDavid Howells } 16986aaca566SDavid Howells 16996aaca566SDavid Howells /* 17006aaca566SDavid Howells * clean up the /proc/fs/nfsfs/ directory 17016aaca566SDavid Howells */ 17026aaca566SDavid Howells void nfs_fs_proc_exit(void) 17036aaca566SDavid Howells { 17046aaca566SDavid Howells remove_proc_entry("volumes", proc_fs_nfs); 17056aaca566SDavid Howells remove_proc_entry("servers", proc_fs_nfs); 170636a5aeb8SAlexey Dobriyan remove_proc_entry("fs/nfsfs", NULL); 17076aaca566SDavid Howells } 17086aaca566SDavid Howells 17096aaca566SDavid Howells #endif /* CONFIG_PROC_FS */ 1710