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" 4824c8dbbbSDavid Howells 4924c8dbbbSDavid Howells #define NFSDBG_FACILITY NFSDBG_CLIENT 5024c8dbbbSDavid Howells 5124c8dbbbSDavid Howells static DEFINE_SPINLOCK(nfs_client_lock); 5224c8dbbbSDavid Howells static LIST_HEAD(nfs_client_list); 5354ceac45SDavid Howells static LIST_HEAD(nfs_volume_list); 5424c8dbbbSDavid Howells static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq); 5524c8dbbbSDavid Howells 5624c8dbbbSDavid Howells /* 575006a76cSDavid Howells * RPC cruft for NFS 585006a76cSDavid Howells */ 595006a76cSDavid Howells static struct rpc_version *nfs_version[5] = { 605006a76cSDavid Howells [2] = &nfs_version2, 615006a76cSDavid Howells #ifdef CONFIG_NFS_V3 625006a76cSDavid Howells [3] = &nfs_version3, 635006a76cSDavid Howells #endif 645006a76cSDavid Howells #ifdef CONFIG_NFS_V4 655006a76cSDavid Howells [4] = &nfs_version4, 665006a76cSDavid Howells #endif 675006a76cSDavid Howells }; 685006a76cSDavid Howells 695006a76cSDavid Howells struct rpc_program nfs_program = { 705006a76cSDavid Howells .name = "nfs", 715006a76cSDavid Howells .number = NFS_PROGRAM, 725006a76cSDavid Howells .nrvers = ARRAY_SIZE(nfs_version), 735006a76cSDavid Howells .version = nfs_version, 745006a76cSDavid Howells .stats = &nfs_rpcstat, 755006a76cSDavid Howells .pipe_dir_name = "/nfs", 765006a76cSDavid Howells }; 775006a76cSDavid Howells 785006a76cSDavid Howells struct rpc_stat nfs_rpcstat = { 795006a76cSDavid Howells .program = &nfs_program 805006a76cSDavid Howells }; 815006a76cSDavid Howells 825006a76cSDavid Howells 835006a76cSDavid Howells #ifdef CONFIG_NFS_V3_ACL 845006a76cSDavid Howells static struct rpc_stat nfsacl_rpcstat = { &nfsacl_program }; 855006a76cSDavid Howells static struct rpc_version * nfsacl_version[] = { 865006a76cSDavid Howells [3] = &nfsacl_version3, 875006a76cSDavid Howells }; 885006a76cSDavid Howells 895006a76cSDavid Howells struct rpc_program nfsacl_program = { 905006a76cSDavid Howells .name = "nfsacl", 915006a76cSDavid Howells .number = NFS_ACL_PROGRAM, 925006a76cSDavid Howells .nrvers = ARRAY_SIZE(nfsacl_version), 935006a76cSDavid Howells .version = nfsacl_version, 945006a76cSDavid Howells .stats = &nfsacl_rpcstat, 955006a76cSDavid Howells }; 965006a76cSDavid Howells #endif /* CONFIG_NFS_V3_ACL */ 975006a76cSDavid Howells 983a498026STrond Myklebust struct nfs_client_initdata { 993a498026STrond Myklebust const char *hostname; 100d7422c47SChuck Lever const struct sockaddr *addr; 1016e4cffd7SChuck Lever size_t addrlen; 10240c55319STrond Myklebust const struct nfs_rpc_ops *rpc_ops; 10359dca3b2STrond Myklebust int proto; 1043a498026STrond Myklebust }; 1053a498026STrond Myklebust 1065006a76cSDavid Howells /* 10724c8dbbbSDavid Howells * Allocate a shared client record 10824c8dbbbSDavid Howells * 10924c8dbbbSDavid Howells * Since these are allocated/deallocated very rarely, we don't 11024c8dbbbSDavid Howells * bother putting them in a slab cache... 11124c8dbbbSDavid Howells */ 1123a498026STrond Myklebust static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) 11324c8dbbbSDavid Howells { 11424c8dbbbSDavid Howells struct nfs_client *clp; 11524c8dbbbSDavid Howells 11624c8dbbbSDavid Howells if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) 11724c8dbbbSDavid Howells goto error_0; 11824c8dbbbSDavid Howells 11940c55319STrond Myklebust clp->rpc_ops = cl_init->rpc_ops; 12040c55319STrond Myklebust 12140c55319STrond Myklebust if (cl_init->rpc_ops->version == 4) { 12224c8dbbbSDavid Howells if (nfs_callback_up() < 0) 12324c8dbbbSDavid Howells goto error_2; 12424c8dbbbSDavid Howells __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state); 12524c8dbbbSDavid Howells } 12624c8dbbbSDavid Howells 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) { 1343a498026STrond Myklebust clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL); 13524c8dbbbSDavid Howells if (!clp->cl_hostname) 13624c8dbbbSDavid Howells goto error_3; 13724c8dbbbSDavid Howells } 13824c8dbbbSDavid Howells 13924c8dbbbSDavid Howells INIT_LIST_HEAD(&clp->cl_superblocks); 14024c8dbbbSDavid Howells clp->cl_rpcclient = ERR_PTR(-EINVAL); 14124c8dbbbSDavid Howells 14259dca3b2STrond Myklebust clp->cl_proto = cl_init->proto; 14359dca3b2STrond Myklebust 14424c8dbbbSDavid Howells #ifdef CONFIG_NFS_V4 14524c8dbbbSDavid Howells init_rwsem(&clp->cl_sem); 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; 15224c8dbbbSDavid Howells #endif 15324c8dbbbSDavid Howells 15424c8dbbbSDavid Howells return clp; 15524c8dbbbSDavid Howells 15624c8dbbbSDavid Howells error_3: 1579c5bf38dSTrond Myklebust if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) 15824c8dbbbSDavid Howells nfs_callback_down(); 15924c8dbbbSDavid Howells error_2: 16024c8dbbbSDavid Howells kfree(clp); 16124c8dbbbSDavid Howells error_0: 16224c8dbbbSDavid Howells return NULL; 16324c8dbbbSDavid Howells } 16424c8dbbbSDavid Howells 1655dd3177aSTrond Myklebust static void nfs4_shutdown_client(struct nfs_client *clp) 16624c8dbbbSDavid Howells { 16724c8dbbbSDavid Howells #ifdef CONFIG_NFS_V4 1685dd3177aSTrond Myklebust if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state)) 1695dd3177aSTrond Myklebust nfs4_kill_renewd(clp); 1709f958ab8STrond Myklebust BUG_ON(!RB_EMPTY_ROOT(&clp->cl_state_owners)); 1715dd3177aSTrond Myklebust if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state)) 17224c8dbbbSDavid Howells nfs_idmap_delete(clp); 173f6a1cc89STrond Myklebust 174f6a1cc89STrond Myklebust rpc_destroy_wait_queue(&clp->cl_rpcwaitq); 17524c8dbbbSDavid Howells #endif 1765dd3177aSTrond Myklebust } 1775dd3177aSTrond Myklebust 1785dd3177aSTrond Myklebust /* 1795dd3177aSTrond Myklebust * Destroy a shared client record 1805dd3177aSTrond Myklebust */ 1815dd3177aSTrond Myklebust static void nfs_free_client(struct nfs_client *clp) 1825dd3177aSTrond Myklebust { 18340c55319STrond Myklebust dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version); 1845dd3177aSTrond Myklebust 1855dd3177aSTrond Myklebust nfs4_shutdown_client(clp); 18624c8dbbbSDavid Howells 18724c8dbbbSDavid Howells /* -EIO all pending I/O */ 18824c8dbbbSDavid Howells if (!IS_ERR(clp->cl_rpcclient)) 18924c8dbbbSDavid Howells rpc_shutdown_client(clp->cl_rpcclient); 19024c8dbbbSDavid Howells 19124c8dbbbSDavid Howells if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) 19224c8dbbbSDavid Howells nfs_callback_down(); 19324c8dbbbSDavid Howells 19424c8dbbbSDavid Howells kfree(clp->cl_hostname); 19524c8dbbbSDavid Howells kfree(clp); 19624c8dbbbSDavid Howells 19724c8dbbbSDavid Howells dprintk("<-- nfs_free_client()\n"); 19824c8dbbbSDavid Howells } 19924c8dbbbSDavid Howells 20024c8dbbbSDavid Howells /* 20124c8dbbbSDavid Howells * Release a reference to a shared client record 20224c8dbbbSDavid Howells */ 20324c8dbbbSDavid Howells void nfs_put_client(struct nfs_client *clp) 20424c8dbbbSDavid Howells { 20527ba8512SDavid Howells if (!clp) 20627ba8512SDavid Howells return; 20727ba8512SDavid Howells 20824c8dbbbSDavid Howells dprintk("--> nfs_put_client({%d})\n", atomic_read(&clp->cl_count)); 20924c8dbbbSDavid Howells 21024c8dbbbSDavid Howells if (atomic_dec_and_lock(&clp->cl_count, &nfs_client_lock)) { 21124c8dbbbSDavid Howells list_del(&clp->cl_share_link); 21224c8dbbbSDavid Howells spin_unlock(&nfs_client_lock); 21324c8dbbbSDavid Howells 21424c8dbbbSDavid Howells BUG_ON(!list_empty(&clp->cl_superblocks)); 21524c8dbbbSDavid Howells 21624c8dbbbSDavid Howells nfs_free_client(clp); 21724c8dbbbSDavid Howells } 21824c8dbbbSDavid Howells } 21924c8dbbbSDavid Howells 2203b0d3f93STrond Myklebust static int nfs_sockaddr_match_ipaddr4(const struct sockaddr_in *sa1, 2213b0d3f93STrond Myklebust const struct sockaddr_in *sa2) 2223b0d3f93STrond Myklebust { 2233b0d3f93STrond Myklebust return sa1->sin_addr.s_addr == sa2->sin_addr.s_addr; 2243b0d3f93STrond Myklebust } 2253b0d3f93STrond Myklebust 2263b0d3f93STrond Myklebust static int nfs_sockaddr_match_ipaddr6(const struct sockaddr_in6 *sa1, 2273b0d3f93STrond Myklebust const struct sockaddr_in6 *sa2) 2283b0d3f93STrond Myklebust { 2293b0d3f93STrond Myklebust return ipv6_addr_equal(&sa1->sin6_addr, &sa2->sin6_addr); 2303b0d3f93STrond Myklebust } 2313b0d3f93STrond Myklebust 2323b0d3f93STrond Myklebust static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1, 2333b0d3f93STrond Myklebust const struct sockaddr *sa2) 2343b0d3f93STrond Myklebust { 2353b0d3f93STrond Myklebust switch (sa1->sa_family) { 2363b0d3f93STrond Myklebust case AF_INET: 2373b0d3f93STrond Myklebust return nfs_sockaddr_match_ipaddr4((const struct sockaddr_in *)sa1, 2383b0d3f93STrond Myklebust (const struct sockaddr_in *)sa2); 2393b0d3f93STrond Myklebust case AF_INET6: 2403b0d3f93STrond Myklebust return nfs_sockaddr_match_ipaddr6((const struct sockaddr_in6 *)sa1, 2413b0d3f93STrond Myklebust (const struct sockaddr_in6 *)sa2); 2423b0d3f93STrond Myklebust } 2433b0d3f93STrond Myklebust BUG(); 2443b0d3f93STrond Myklebust } 2453b0d3f93STrond Myklebust 24624c8dbbbSDavid Howells /* 247c81468a1STrond Myklebust * Find a client by IP address and protocol version 248c81468a1STrond Myklebust * - returns NULL if no such client 24924c8dbbbSDavid Howells */ 250ff052645SChuck Lever struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion) 251c81468a1STrond Myklebust { 252c81468a1STrond Myklebust struct nfs_client *clp; 253c81468a1STrond Myklebust 254c81468a1STrond Myklebust spin_lock(&nfs_client_lock); 255c81468a1STrond Myklebust list_for_each_entry(clp, &nfs_client_list, cl_share_link) { 2563b0d3f93STrond Myklebust struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; 2573b0d3f93STrond Myklebust 258c81468a1STrond Myklebust /* Don't match clients that failed to initialise properly */ 259c81468a1STrond Myklebust if (clp->cl_cons_state != NFS_CS_READY) 260c81468a1STrond Myklebust continue; 261c81468a1STrond Myklebust 262c81468a1STrond Myklebust /* Different NFS versions cannot share the same nfs_client */ 26340c55319STrond Myklebust if (clp->rpc_ops->version != nfsversion) 264c81468a1STrond Myklebust continue; 265c81468a1STrond Myklebust 2663b0d3f93STrond Myklebust if (addr->sa_family != clap->sa_family) 2673b0d3f93STrond Myklebust continue; 268c81468a1STrond Myklebust /* Match only the IP address, not the port number */ 2693b0d3f93STrond Myklebust if (!nfs_sockaddr_match_ipaddr(addr, clap)) 270c81468a1STrond Myklebust continue; 271c81468a1STrond Myklebust 272c81468a1STrond Myklebust atomic_inc(&clp->cl_count); 273c81468a1STrond Myklebust spin_unlock(&nfs_client_lock); 274c81468a1STrond Myklebust return clp; 275c81468a1STrond Myklebust } 276c81468a1STrond Myklebust spin_unlock(&nfs_client_lock); 277c81468a1STrond Myklebust return NULL; 278c81468a1STrond Myklebust } 279c81468a1STrond Myklebust 280c81468a1STrond Myklebust /* 2813fbd67adSTrond Myklebust * Find a client by IP address and protocol version 2823fbd67adSTrond Myklebust * - returns NULL if no such client 2833fbd67adSTrond Myklebust */ 2843fbd67adSTrond Myklebust struct nfs_client *nfs_find_client_next(struct nfs_client *clp) 2853fbd67adSTrond Myklebust { 2863fbd67adSTrond Myklebust struct sockaddr *sap = (struct sockaddr *)&clp->cl_addr; 2873fbd67adSTrond Myklebust u32 nfsvers = clp->rpc_ops->version; 2883fbd67adSTrond Myklebust 2893fbd67adSTrond Myklebust spin_lock(&nfs_client_lock); 2903fbd67adSTrond Myklebust list_for_each_entry_continue(clp, &nfs_client_list, cl_share_link) { 2913fbd67adSTrond Myklebust struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; 2923fbd67adSTrond Myklebust 2933fbd67adSTrond Myklebust /* Don't match clients that failed to initialise properly */ 2943fbd67adSTrond Myklebust if (clp->cl_cons_state != NFS_CS_READY) 2953fbd67adSTrond Myklebust continue; 2963fbd67adSTrond Myklebust 2973fbd67adSTrond Myklebust /* Different NFS versions cannot share the same nfs_client */ 2983fbd67adSTrond Myklebust if (clp->rpc_ops->version != nfsvers) 2993fbd67adSTrond Myklebust continue; 3003fbd67adSTrond Myklebust 3013fbd67adSTrond Myklebust if (sap->sa_family != clap->sa_family) 3023fbd67adSTrond Myklebust continue; 3033fbd67adSTrond Myklebust /* Match only the IP address, not the port number */ 3043fbd67adSTrond Myklebust if (!nfs_sockaddr_match_ipaddr(sap, clap)) 3053fbd67adSTrond Myklebust continue; 3063fbd67adSTrond Myklebust 3073fbd67adSTrond Myklebust atomic_inc(&clp->cl_count); 3083fbd67adSTrond Myklebust spin_unlock(&nfs_client_lock); 3093fbd67adSTrond Myklebust return clp; 3103fbd67adSTrond Myklebust } 3113fbd67adSTrond Myklebust spin_unlock(&nfs_client_lock); 3123fbd67adSTrond Myklebust return NULL; 3133fbd67adSTrond Myklebust } 3143fbd67adSTrond Myklebust 3153fbd67adSTrond Myklebust /* 316c81468a1STrond Myklebust * Find an nfs_client on the list that matches the initialisation data 317c81468a1STrond Myklebust * that is supplied. 318c81468a1STrond Myklebust */ 319c81468a1STrond Myklebust static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *data) 32024c8dbbbSDavid Howells { 32124c8dbbbSDavid Howells struct nfs_client *clp; 32224c8dbbbSDavid Howells 32324c8dbbbSDavid Howells list_for_each_entry(clp, &nfs_client_list, cl_share_link) { 32413bbc06aSTrond Myklebust /* Don't match clients that failed to initialise properly */ 32513bbc06aSTrond Myklebust if (clp->cl_cons_state < 0) 32613bbc06aSTrond Myklebust continue; 32713bbc06aSTrond Myklebust 32824c8dbbbSDavid Howells /* Different NFS versions cannot share the same nfs_client */ 32940c55319STrond Myklebust if (clp->rpc_ops != data->rpc_ops) 33024c8dbbbSDavid Howells continue; 33124c8dbbbSDavid Howells 33259dca3b2STrond Myklebust if (clp->cl_proto != data->proto) 33359dca3b2STrond Myklebust continue; 33459dca3b2STrond Myklebust 335c81468a1STrond Myklebust /* Match the full socket address */ 336c81468a1STrond Myklebust if (memcmp(&clp->cl_addr, data->addr, sizeof(clp->cl_addr)) != 0) 33724c8dbbbSDavid Howells continue; 33824c8dbbbSDavid Howells 33924c8dbbbSDavid Howells atomic_inc(&clp->cl_count); 34024c8dbbbSDavid Howells return clp; 34124c8dbbbSDavid Howells } 342c81468a1STrond Myklebust return NULL; 34324c8dbbbSDavid Howells } 34424c8dbbbSDavid Howells 34524c8dbbbSDavid Howells /* 34624c8dbbbSDavid Howells * Look up a client by IP address and protocol version 34724c8dbbbSDavid Howells * - creates a new record if one doesn't yet exist 34824c8dbbbSDavid Howells */ 3493a498026STrond Myklebust static struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_init) 35024c8dbbbSDavid Howells { 35124c8dbbbSDavid Howells struct nfs_client *clp, *new = NULL; 35224c8dbbbSDavid Howells int error; 35324c8dbbbSDavid Howells 354d7422c47SChuck Lever dprintk("--> nfs_get_client(%s,v%u)\n", 355d7422c47SChuck Lever cl_init->hostname ?: "", cl_init->rpc_ops->version); 35624c8dbbbSDavid Howells 35724c8dbbbSDavid Howells /* see if the client already exists */ 35824c8dbbbSDavid Howells do { 35924c8dbbbSDavid Howells spin_lock(&nfs_client_lock); 36024c8dbbbSDavid Howells 361c81468a1STrond Myklebust clp = nfs_match_client(cl_init); 36224c8dbbbSDavid Howells if (clp) 36324c8dbbbSDavid Howells goto found_client; 36424c8dbbbSDavid Howells if (new) 36524c8dbbbSDavid Howells goto install_client; 36624c8dbbbSDavid Howells 36724c8dbbbSDavid Howells spin_unlock(&nfs_client_lock); 36824c8dbbbSDavid Howells 3693a498026STrond Myklebust new = nfs_alloc_client(cl_init); 37024c8dbbbSDavid Howells } while (new); 37124c8dbbbSDavid Howells 37224c8dbbbSDavid Howells return ERR_PTR(-ENOMEM); 37324c8dbbbSDavid Howells 37424c8dbbbSDavid Howells /* install a new client and return with it unready */ 37524c8dbbbSDavid Howells install_client: 37624c8dbbbSDavid Howells clp = new; 37724c8dbbbSDavid Howells list_add(&clp->cl_share_link, &nfs_client_list); 37824c8dbbbSDavid Howells spin_unlock(&nfs_client_lock); 37924c8dbbbSDavid Howells dprintk("--> nfs_get_client() = %p [new]\n", clp); 38024c8dbbbSDavid Howells return clp; 38124c8dbbbSDavid Howells 38224c8dbbbSDavid Howells /* found an existing client 38324c8dbbbSDavid Howells * - make sure it's ready before returning 38424c8dbbbSDavid Howells */ 38524c8dbbbSDavid Howells found_client: 38624c8dbbbSDavid Howells spin_unlock(&nfs_client_lock); 38724c8dbbbSDavid Howells 38824c8dbbbSDavid Howells if (new) 38924c8dbbbSDavid Howells nfs_free_client(new); 39024c8dbbbSDavid Howells 391150030b7SMatthew Wilcox error = wait_event_killable(nfs_client_active_wq, 3920bae89ecSTrond Myklebust clp->cl_cons_state != NFS_CS_INITING); 3930bae89ecSTrond Myklebust if (error < 0) { 39424c8dbbbSDavid Howells nfs_put_client(clp); 39524c8dbbbSDavid Howells return ERR_PTR(-ERESTARTSYS); 39624c8dbbbSDavid Howells } 39724c8dbbbSDavid Howells 39824c8dbbbSDavid Howells if (clp->cl_cons_state < NFS_CS_READY) { 39924c8dbbbSDavid Howells error = clp->cl_cons_state; 40024c8dbbbSDavid Howells nfs_put_client(clp); 40124c8dbbbSDavid Howells return ERR_PTR(error); 40224c8dbbbSDavid Howells } 40324c8dbbbSDavid Howells 40454ceac45SDavid Howells BUG_ON(clp->cl_cons_state != NFS_CS_READY); 40554ceac45SDavid Howells 40624c8dbbbSDavid Howells dprintk("--> nfs_get_client() = %p [share]\n", clp); 40724c8dbbbSDavid Howells return clp; 40824c8dbbbSDavid Howells } 40924c8dbbbSDavid Howells 41024c8dbbbSDavid Howells /* 41124c8dbbbSDavid Howells * Mark a server as ready or failed 41224c8dbbbSDavid Howells */ 41354ceac45SDavid Howells static void nfs_mark_client_ready(struct nfs_client *clp, int state) 41424c8dbbbSDavid Howells { 41524c8dbbbSDavid Howells clp->cl_cons_state = state; 41624c8dbbbSDavid Howells wake_up_all(&nfs_client_active_wq); 41724c8dbbbSDavid Howells } 4185006a76cSDavid Howells 4195006a76cSDavid Howells /* 4205006a76cSDavid Howells * Initialise the timeout values for a connection 4215006a76cSDavid Howells */ 4225006a76cSDavid Howells static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, 4235006a76cSDavid Howells unsigned int timeo, unsigned int retrans) 4245006a76cSDavid Howells { 4255006a76cSDavid Howells to->to_initval = timeo * HZ / 10; 4265006a76cSDavid Howells to->to_retries = retrans; 4275006a76cSDavid Howells if (!to->to_retries) 4285006a76cSDavid Howells to->to_retries = 2; 4295006a76cSDavid Howells 4305006a76cSDavid Howells switch (proto) { 4310896a725S\"Talpey, Thomas\ case XPRT_TRANSPORT_TCP: 4322cf7ff7aS\"Talpey, Thomas\ case XPRT_TRANSPORT_RDMA: 4337a3e3e18STrond Myklebust if (to->to_initval == 0) 4345006a76cSDavid Howells to->to_initval = 60 * HZ; 4355006a76cSDavid Howells if (to->to_initval > NFS_MAX_TCP_TIMEOUT) 4365006a76cSDavid Howells to->to_initval = NFS_MAX_TCP_TIMEOUT; 4375006a76cSDavid Howells to->to_increment = to->to_initval; 4385006a76cSDavid Howells to->to_maxval = to->to_initval + (to->to_increment * to->to_retries); 4397a3e3e18STrond Myklebust if (to->to_maxval > NFS_MAX_TCP_TIMEOUT) 4407a3e3e18STrond Myklebust to->to_maxval = NFS_MAX_TCP_TIMEOUT; 4417a3e3e18STrond Myklebust if (to->to_maxval < to->to_initval) 4427a3e3e18STrond Myklebust to->to_maxval = to->to_initval; 4435006a76cSDavid Howells to->to_exponential = 0; 4445006a76cSDavid Howells break; 4450896a725S\"Talpey, Thomas\ case XPRT_TRANSPORT_UDP: 4465006a76cSDavid Howells default: 4475006a76cSDavid Howells if (!to->to_initval) 4485006a76cSDavid Howells to->to_initval = 11 * HZ / 10; 4495006a76cSDavid Howells if (to->to_initval > NFS_MAX_UDP_TIMEOUT) 4505006a76cSDavid Howells to->to_initval = NFS_MAX_UDP_TIMEOUT; 4515006a76cSDavid Howells to->to_maxval = NFS_MAX_UDP_TIMEOUT; 4525006a76cSDavid Howells to->to_exponential = 1; 4535006a76cSDavid Howells break; 4545006a76cSDavid Howells } 4555006a76cSDavid Howells } 4565006a76cSDavid Howells 4575006a76cSDavid Howells /* 4585006a76cSDavid Howells * Create an RPC client handle 4595006a76cSDavid Howells */ 46059dca3b2STrond Myklebust static int nfs_create_rpc_client(struct nfs_client *clp, 46133170233STrond Myklebust const struct rpc_timeout *timeparms, 46243d78ef2SChuck Lever rpc_authflavor_t flavor, 46343d78ef2SChuck Lever int flags) 4645006a76cSDavid Howells { 4655006a76cSDavid Howells struct rpc_clnt *clnt = NULL; 46641877d20SChuck Lever struct rpc_create_args args = { 46759dca3b2STrond Myklebust .protocol = clp->cl_proto, 46841877d20SChuck Lever .address = (struct sockaddr *)&clp->cl_addr, 4696e4cffd7SChuck Lever .addrsize = clp->cl_addrlen, 47033170233STrond Myklebust .timeout = timeparms, 47141877d20SChuck Lever .servername = clp->cl_hostname, 47241877d20SChuck Lever .program = &nfs_program, 47341877d20SChuck Lever .version = clp->rpc_ops->version, 47441877d20SChuck Lever .authflavor = flavor, 47543d78ef2SChuck Lever .flags = flags, 47641877d20SChuck Lever }; 4775006a76cSDavid Howells 4785006a76cSDavid Howells if (!IS_ERR(clp->cl_rpcclient)) 4795006a76cSDavid Howells return 0; 4805006a76cSDavid Howells 48141877d20SChuck Lever clnt = rpc_create(&args); 4825006a76cSDavid Howells if (IS_ERR(clnt)) { 4835006a76cSDavid Howells dprintk("%s: cannot create RPC client. Error = %ld\n", 4845006a76cSDavid Howells __FUNCTION__, PTR_ERR(clnt)); 4855006a76cSDavid Howells return PTR_ERR(clnt); 4865006a76cSDavid Howells } 4875006a76cSDavid Howells 4885006a76cSDavid Howells clp->cl_rpcclient = clnt; 4895006a76cSDavid Howells return 0; 4905006a76cSDavid Howells } 49154ceac45SDavid Howells 49254ceac45SDavid Howells /* 49354ceac45SDavid Howells * Version 2 or 3 client destruction 49454ceac45SDavid Howells */ 49554ceac45SDavid Howells static void nfs_destroy_server(struct nfs_server *server) 49654ceac45SDavid Howells { 49754ceac45SDavid Howells if (!(server->flags & NFS_MOUNT_NONLM)) 4989289e7f9SChuck Lever nlmclnt_done(server->nlm_host); 49954ceac45SDavid Howells } 50054ceac45SDavid Howells 50154ceac45SDavid Howells /* 50254ceac45SDavid Howells * Version 2 or 3 lockd setup 50354ceac45SDavid Howells */ 50454ceac45SDavid Howells static int nfs_start_lockd(struct nfs_server *server) 50554ceac45SDavid Howells { 5069289e7f9SChuck Lever struct nlm_host *host; 5079289e7f9SChuck Lever struct nfs_client *clp = server->nfs_client; 508883bb163SChuck Lever struct nlmclnt_initdata nlm_init = { 509883bb163SChuck Lever .hostname = clp->cl_hostname, 510883bb163SChuck Lever .address = (struct sockaddr *)&clp->cl_addr, 511883bb163SChuck Lever .addrlen = clp->cl_addrlen, 512883bb163SChuck Lever .protocol = server->flags & NFS_MOUNT_TCP ? 513883bb163SChuck Lever IPPROTO_TCP : IPPROTO_UDP, 514883bb163SChuck Lever .nfs_version = clp->rpc_ops->version, 515883bb163SChuck Lever }; 51654ceac45SDavid Howells 517883bb163SChuck Lever if (nlm_init.nfs_version > 3) 5189289e7f9SChuck Lever return 0; 51954ceac45SDavid Howells if (server->flags & NFS_MOUNT_NONLM) 5209289e7f9SChuck Lever return 0; 5219289e7f9SChuck Lever 522883bb163SChuck Lever host = nlmclnt_init(&nlm_init); 5239289e7f9SChuck Lever if (IS_ERR(host)) 5249289e7f9SChuck Lever return PTR_ERR(host); 5259289e7f9SChuck Lever 5269289e7f9SChuck Lever server->nlm_host = host; 52754ceac45SDavid Howells server->destroy = nfs_destroy_server; 5289289e7f9SChuck Lever return 0; 52954ceac45SDavid Howells } 53054ceac45SDavid Howells 53154ceac45SDavid Howells /* 53254ceac45SDavid Howells * Initialise an NFSv3 ACL client connection 53354ceac45SDavid Howells */ 53454ceac45SDavid Howells #ifdef CONFIG_NFS_V3_ACL 53554ceac45SDavid Howells static void nfs_init_server_aclclient(struct nfs_server *server) 53654ceac45SDavid Howells { 53740c55319STrond Myklebust if (server->nfs_client->rpc_ops->version != 3) 53854ceac45SDavid Howells goto out_noacl; 53954ceac45SDavid Howells if (server->flags & NFS_MOUNT_NOACL) 54054ceac45SDavid Howells goto out_noacl; 54154ceac45SDavid Howells 54254ceac45SDavid Howells server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3); 54354ceac45SDavid Howells if (IS_ERR(server->client_acl)) 54454ceac45SDavid Howells goto out_noacl; 54554ceac45SDavid Howells 54654ceac45SDavid Howells /* No errors! Assume that Sun nfsacls are supported */ 54754ceac45SDavid Howells server->caps |= NFS_CAP_ACLS; 54854ceac45SDavid Howells return; 54954ceac45SDavid Howells 55054ceac45SDavid Howells out_noacl: 55154ceac45SDavid Howells server->caps &= ~NFS_CAP_ACLS; 55254ceac45SDavid Howells } 55354ceac45SDavid Howells #else 55454ceac45SDavid Howells static inline void nfs_init_server_aclclient(struct nfs_server *server) 55554ceac45SDavid Howells { 55654ceac45SDavid Howells server->flags &= ~NFS_MOUNT_NOACL; 55754ceac45SDavid Howells server->caps &= ~NFS_CAP_ACLS; 55854ceac45SDavid Howells } 55954ceac45SDavid Howells #endif 56054ceac45SDavid Howells 56154ceac45SDavid Howells /* 56254ceac45SDavid Howells * Create a general RPC client 56354ceac45SDavid Howells */ 56433170233STrond Myklebust static int nfs_init_server_rpcclient(struct nfs_server *server, 56533170233STrond Myklebust const struct rpc_timeout *timeo, 56633170233STrond Myklebust rpc_authflavor_t pseudoflavour) 56754ceac45SDavid Howells { 56854ceac45SDavid Howells struct nfs_client *clp = server->nfs_client; 56954ceac45SDavid Howells 57054ceac45SDavid Howells server->client = rpc_clone_client(clp->cl_rpcclient); 57154ceac45SDavid Howells if (IS_ERR(server->client)) { 57254ceac45SDavid Howells dprintk("%s: couldn't create rpc_client!\n", __FUNCTION__); 57354ceac45SDavid Howells return PTR_ERR(server->client); 57454ceac45SDavid Howells } 57554ceac45SDavid Howells 57633170233STrond Myklebust memcpy(&server->client->cl_timeout_default, 57733170233STrond Myklebust timeo, 57833170233STrond Myklebust sizeof(server->client->cl_timeout_default)); 57933170233STrond Myklebust server->client->cl_timeout = &server->client->cl_timeout_default; 58033170233STrond Myklebust 58154ceac45SDavid Howells if (pseudoflavour != clp->cl_rpcclient->cl_auth->au_flavor) { 58254ceac45SDavid Howells struct rpc_auth *auth; 58354ceac45SDavid Howells 58454ceac45SDavid Howells auth = rpcauth_create(pseudoflavour, server->client); 58554ceac45SDavid Howells if (IS_ERR(auth)) { 58654ceac45SDavid Howells dprintk("%s: couldn't create credcache!\n", __FUNCTION__); 58754ceac45SDavid Howells return PTR_ERR(auth); 58854ceac45SDavid Howells } 58954ceac45SDavid Howells } 59054ceac45SDavid Howells server->client->cl_softrtry = 0; 59154ceac45SDavid Howells if (server->flags & NFS_MOUNT_SOFT) 59254ceac45SDavid Howells server->client->cl_softrtry = 1; 59354ceac45SDavid Howells 59454ceac45SDavid Howells return 0; 59554ceac45SDavid Howells } 59654ceac45SDavid Howells 59754ceac45SDavid Howells /* 59854ceac45SDavid Howells * Initialise an NFS2 or NFS3 client 59954ceac45SDavid Howells */ 6002283f8d6S\"Talpey, Thomas\ static int nfs_init_client(struct nfs_client *clp, 60133170233STrond Myklebust const struct rpc_timeout *timeparms, 6022283f8d6S\"Talpey, Thomas\ const struct nfs_parsed_mount_data *data) 60354ceac45SDavid Howells { 60454ceac45SDavid Howells int error; 60554ceac45SDavid Howells 60654ceac45SDavid Howells if (clp->cl_cons_state == NFS_CS_READY) { 60754ceac45SDavid Howells /* the client is already initialised */ 60854ceac45SDavid Howells dprintk("<-- nfs_init_client() = 0 [already %p]\n", clp); 60954ceac45SDavid Howells return 0; 61054ceac45SDavid Howells } 61154ceac45SDavid Howells 61254ceac45SDavid Howells /* 61354ceac45SDavid Howells * Create a client RPC handle for doing FSSTAT with UNIX auth only 61454ceac45SDavid Howells * - RFC 2623, sec 2.3.2 61554ceac45SDavid Howells */ 61659dca3b2STrond Myklebust error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX, 0); 61754ceac45SDavid Howells if (error < 0) 61854ceac45SDavid Howells goto error; 61954ceac45SDavid Howells nfs_mark_client_ready(clp, NFS_CS_READY); 62054ceac45SDavid Howells return 0; 62154ceac45SDavid Howells 62254ceac45SDavid Howells error: 62354ceac45SDavid Howells nfs_mark_client_ready(clp, error); 62454ceac45SDavid Howells dprintk("<-- nfs_init_client() = xerror %d\n", error); 62554ceac45SDavid Howells return error; 62654ceac45SDavid Howells } 62754ceac45SDavid Howells 62854ceac45SDavid Howells /* 62954ceac45SDavid Howells * Create a version 2 or 3 client 63054ceac45SDavid Howells */ 6312283f8d6S\"Talpey, Thomas\ static int nfs_init_server(struct nfs_server *server, 6322283f8d6S\"Talpey, Thomas\ const struct nfs_parsed_mount_data *data) 63354ceac45SDavid Howells { 6343a498026STrond Myklebust struct nfs_client_initdata cl_init = { 6353a498026STrond Myklebust .hostname = data->nfs_server.hostname, 636d7422c47SChuck Lever .addr = (const struct sockaddr *)&data->nfs_server.address, 6374c568017SChuck Lever .addrlen = data->nfs_server.addrlen, 63840c55319STrond Myklebust .rpc_ops = &nfs_v2_clientops, 63959dca3b2STrond Myklebust .proto = data->nfs_server.protocol, 6403a498026STrond Myklebust }; 64133170233STrond Myklebust struct rpc_timeout timeparms; 64254ceac45SDavid Howells struct nfs_client *clp; 6433a498026STrond Myklebust int error; 64454ceac45SDavid Howells 64554ceac45SDavid Howells dprintk("--> nfs_init_server()\n"); 64654ceac45SDavid Howells 64754ceac45SDavid Howells #ifdef CONFIG_NFS_V3 64854ceac45SDavid Howells if (data->flags & NFS_MOUNT_VER3) 64940c55319STrond Myklebust cl_init.rpc_ops = &nfs_v3_clientops; 65054ceac45SDavid Howells #endif 65154ceac45SDavid Howells 65254ceac45SDavid Howells /* Allocate or find a client reference we can use */ 6533a498026STrond Myklebust clp = nfs_get_client(&cl_init); 65454ceac45SDavid Howells if (IS_ERR(clp)) { 65554ceac45SDavid Howells dprintk("<-- nfs_init_server() = error %ld\n", PTR_ERR(clp)); 65654ceac45SDavid Howells return PTR_ERR(clp); 65754ceac45SDavid Howells } 65854ceac45SDavid Howells 65933170233STrond Myklebust nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, 66033170233STrond Myklebust data->timeo, data->retrans); 66133170233STrond Myklebust error = nfs_init_client(clp, &timeparms, data); 66254ceac45SDavid Howells if (error < 0) 66354ceac45SDavid Howells goto error; 66454ceac45SDavid Howells 66554ceac45SDavid Howells server->nfs_client = clp; 66654ceac45SDavid Howells 66754ceac45SDavid Howells /* Initialise the client representation from the mount data */ 66854ceac45SDavid Howells server->flags = data->flags & NFS_MOUNT_FLAGMASK; 66954ceac45SDavid Howells 67054ceac45SDavid Howells if (data->rsize) 67154ceac45SDavid Howells server->rsize = nfs_block_size(data->rsize, NULL); 67254ceac45SDavid Howells if (data->wsize) 67354ceac45SDavid Howells server->wsize = nfs_block_size(data->wsize, NULL); 67454ceac45SDavid Howells 67554ceac45SDavid Howells server->acregmin = data->acregmin * HZ; 67654ceac45SDavid Howells server->acregmax = data->acregmax * HZ; 67754ceac45SDavid Howells server->acdirmin = data->acdirmin * HZ; 67854ceac45SDavid Howells server->acdirmax = data->acdirmax * HZ; 67954ceac45SDavid Howells 68054ceac45SDavid Howells /* Start lockd here, before we might error out */ 68154ceac45SDavid Howells error = nfs_start_lockd(server); 68254ceac45SDavid Howells if (error < 0) 68354ceac45SDavid Howells goto error; 68454ceac45SDavid Howells 685f22d6d79SChuck Lever server->port = data->nfs_server.port; 686f22d6d79SChuck Lever 68733170233STrond Myklebust error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]); 68854ceac45SDavid Howells if (error < 0) 68954ceac45SDavid Howells goto error; 69054ceac45SDavid Howells 691*3f8400d1SChuck Lever /* Preserve the values of mount_server-related mount options */ 692*3f8400d1SChuck Lever if (data->mount_server.addrlen) { 693*3f8400d1SChuck Lever memcpy(&server->mountd_address, &data->mount_server.address, 694*3f8400d1SChuck Lever data->mount_server.addrlen); 695*3f8400d1SChuck Lever server->mountd_addrlen = data->mount_server.addrlen; 696*3f8400d1SChuck Lever } 697*3f8400d1SChuck Lever server->mountd_version = data->mount_server.version; 698*3f8400d1SChuck Lever server->mountd_port = data->mount_server.port; 699*3f8400d1SChuck Lever server->mountd_protocol = data->mount_server.protocol; 700*3f8400d1SChuck Lever 70154ceac45SDavid Howells server->namelen = data->namlen; 70254ceac45SDavid Howells /* Create a client RPC handle for the NFSv3 ACL management interface */ 70354ceac45SDavid Howells nfs_init_server_aclclient(server); 70454ceac45SDavid Howells dprintk("<-- nfs_init_server() = 0 [new %p]\n", clp); 70554ceac45SDavid Howells return 0; 70654ceac45SDavid Howells 70754ceac45SDavid Howells error: 70854ceac45SDavid Howells server->nfs_client = NULL; 70954ceac45SDavid Howells nfs_put_client(clp); 71054ceac45SDavid Howells dprintk("<-- nfs_init_server() = xerror %d\n", error); 71154ceac45SDavid Howells return error; 71254ceac45SDavid Howells } 71354ceac45SDavid Howells 71454ceac45SDavid Howells /* 71554ceac45SDavid Howells * Load up the server record from information gained in an fsinfo record 71654ceac45SDavid Howells */ 71754ceac45SDavid Howells static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo *fsinfo) 71854ceac45SDavid Howells { 71954ceac45SDavid Howells unsigned long max_rpc_payload; 72054ceac45SDavid Howells 72154ceac45SDavid Howells /* Work out a lot of parameters */ 72254ceac45SDavid Howells if (server->rsize == 0) 72354ceac45SDavid Howells server->rsize = nfs_block_size(fsinfo->rtpref, NULL); 72454ceac45SDavid Howells if (server->wsize == 0) 72554ceac45SDavid Howells server->wsize = nfs_block_size(fsinfo->wtpref, NULL); 72654ceac45SDavid Howells 72754ceac45SDavid Howells if (fsinfo->rtmax >= 512 && server->rsize > fsinfo->rtmax) 72854ceac45SDavid Howells server->rsize = nfs_block_size(fsinfo->rtmax, NULL); 72954ceac45SDavid Howells if (fsinfo->wtmax >= 512 && server->wsize > fsinfo->wtmax) 73054ceac45SDavid Howells server->wsize = nfs_block_size(fsinfo->wtmax, NULL); 73154ceac45SDavid Howells 73254ceac45SDavid Howells max_rpc_payload = nfs_block_size(rpc_max_payload(server->client), NULL); 73354ceac45SDavid Howells if (server->rsize > max_rpc_payload) 73454ceac45SDavid Howells server->rsize = max_rpc_payload; 73554ceac45SDavid Howells if (server->rsize > NFS_MAX_FILE_IO_SIZE) 73654ceac45SDavid Howells server->rsize = NFS_MAX_FILE_IO_SIZE; 73754ceac45SDavid Howells server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 738e0bf68ddSPeter Zijlstra 73954ceac45SDavid Howells server->backing_dev_info.ra_pages = server->rpages * NFS_MAX_READAHEAD; 74054ceac45SDavid Howells 74154ceac45SDavid Howells if (server->wsize > max_rpc_payload) 74254ceac45SDavid Howells server->wsize = max_rpc_payload; 74354ceac45SDavid Howells if (server->wsize > NFS_MAX_FILE_IO_SIZE) 74454ceac45SDavid Howells server->wsize = NFS_MAX_FILE_IO_SIZE; 74554ceac45SDavid Howells server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 74654ceac45SDavid Howells server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL); 74754ceac45SDavid Howells 74854ceac45SDavid Howells server->dtsize = nfs_block_size(fsinfo->dtpref, NULL); 74954ceac45SDavid Howells if (server->dtsize > PAGE_CACHE_SIZE) 75054ceac45SDavid Howells server->dtsize = PAGE_CACHE_SIZE; 75154ceac45SDavid Howells if (server->dtsize > server->rsize) 75254ceac45SDavid Howells server->dtsize = server->rsize; 75354ceac45SDavid Howells 75454ceac45SDavid Howells if (server->flags & NFS_MOUNT_NOAC) { 75554ceac45SDavid Howells server->acregmin = server->acregmax = 0; 75654ceac45SDavid Howells server->acdirmin = server->acdirmax = 0; 75754ceac45SDavid Howells } 75854ceac45SDavid Howells 75954ceac45SDavid Howells server->maxfilesize = fsinfo->maxfilesize; 76054ceac45SDavid Howells 76154ceac45SDavid Howells /* We're airborne Set socket buffersize */ 76254ceac45SDavid Howells rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100); 76354ceac45SDavid Howells } 76454ceac45SDavid Howells 76554ceac45SDavid Howells /* 76654ceac45SDavid Howells * Probe filesystem information, including the FSID on v2/v3 76754ceac45SDavid Howells */ 76854ceac45SDavid Howells static int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr) 76954ceac45SDavid Howells { 77054ceac45SDavid Howells struct nfs_fsinfo fsinfo; 77154ceac45SDavid Howells struct nfs_client *clp = server->nfs_client; 77254ceac45SDavid Howells int error; 77354ceac45SDavid Howells 77454ceac45SDavid Howells dprintk("--> nfs_probe_fsinfo()\n"); 77554ceac45SDavid Howells 77654ceac45SDavid Howells if (clp->rpc_ops->set_capabilities != NULL) { 77754ceac45SDavid Howells error = clp->rpc_ops->set_capabilities(server, mntfh); 77854ceac45SDavid Howells if (error < 0) 77954ceac45SDavid Howells goto out_error; 78054ceac45SDavid Howells } 78154ceac45SDavid Howells 78254ceac45SDavid Howells fsinfo.fattr = fattr; 78354ceac45SDavid Howells nfs_fattr_init(fattr); 78454ceac45SDavid Howells error = clp->rpc_ops->fsinfo(server, mntfh, &fsinfo); 78554ceac45SDavid Howells if (error < 0) 78654ceac45SDavid Howells goto out_error; 78754ceac45SDavid Howells 78854ceac45SDavid Howells nfs_server_set_fsinfo(server, &fsinfo); 789e0bf68ddSPeter Zijlstra error = bdi_init(&server->backing_dev_info); 790e0bf68ddSPeter Zijlstra if (error) 791e0bf68ddSPeter Zijlstra goto out_error; 792e0bf68ddSPeter Zijlstra 79354ceac45SDavid Howells 79454ceac45SDavid Howells /* Get some general file system info */ 79554ceac45SDavid Howells if (server->namelen == 0) { 79654ceac45SDavid Howells struct nfs_pathconf pathinfo; 79754ceac45SDavid Howells 79854ceac45SDavid Howells pathinfo.fattr = fattr; 79954ceac45SDavid Howells nfs_fattr_init(fattr); 80054ceac45SDavid Howells 80154ceac45SDavid Howells if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0) 80254ceac45SDavid Howells server->namelen = pathinfo.max_namelen; 80354ceac45SDavid Howells } 80454ceac45SDavid Howells 80554ceac45SDavid Howells dprintk("<-- nfs_probe_fsinfo() = 0\n"); 80654ceac45SDavid Howells return 0; 80754ceac45SDavid Howells 80854ceac45SDavid Howells out_error: 80954ceac45SDavid Howells dprintk("nfs_probe_fsinfo: error = %d\n", -error); 81054ceac45SDavid Howells return error; 81154ceac45SDavid Howells } 81254ceac45SDavid Howells 81354ceac45SDavid Howells /* 81454ceac45SDavid Howells * Copy useful information when duplicating a server record 81554ceac45SDavid Howells */ 81654ceac45SDavid Howells static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source) 81754ceac45SDavid Howells { 81854ceac45SDavid Howells target->flags = source->flags; 81954ceac45SDavid Howells target->acregmin = source->acregmin; 82054ceac45SDavid Howells target->acregmax = source->acregmax; 82154ceac45SDavid Howells target->acdirmin = source->acdirmin; 82254ceac45SDavid Howells target->acdirmax = source->acdirmax; 82354ceac45SDavid Howells target->caps = source->caps; 82454ceac45SDavid Howells } 82554ceac45SDavid Howells 82654ceac45SDavid Howells /* 82754ceac45SDavid Howells * Allocate and initialise a server record 82854ceac45SDavid Howells */ 82954ceac45SDavid Howells static struct nfs_server *nfs_alloc_server(void) 83054ceac45SDavid Howells { 83154ceac45SDavid Howells struct nfs_server *server; 83254ceac45SDavid Howells 83354ceac45SDavid Howells server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL); 83454ceac45SDavid Howells if (!server) 83554ceac45SDavid Howells return NULL; 83654ceac45SDavid Howells 83754ceac45SDavid Howells server->client = server->client_acl = ERR_PTR(-EINVAL); 83854ceac45SDavid Howells 83954ceac45SDavid Howells /* Zero out the NFS state stuff */ 84054ceac45SDavid Howells INIT_LIST_HEAD(&server->client_link); 84154ceac45SDavid Howells INIT_LIST_HEAD(&server->master_link); 84254ceac45SDavid Howells 843ef818a28SSteve Dickson init_waitqueue_head(&server->active_wq); 844ef818a28SSteve Dickson atomic_set(&server->active, 0); 845ef818a28SSteve Dickson 84654ceac45SDavid Howells server->io_stats = nfs_alloc_iostats(); 84754ceac45SDavid Howells if (!server->io_stats) { 84854ceac45SDavid Howells kfree(server); 84954ceac45SDavid Howells return NULL; 85054ceac45SDavid Howells } 85154ceac45SDavid Howells 85254ceac45SDavid Howells return server; 85354ceac45SDavid Howells } 85454ceac45SDavid Howells 85554ceac45SDavid Howells /* 85654ceac45SDavid Howells * Free up a server record 85754ceac45SDavid Howells */ 85854ceac45SDavid Howells void nfs_free_server(struct nfs_server *server) 85954ceac45SDavid Howells { 86054ceac45SDavid Howells dprintk("--> nfs_free_server()\n"); 86154ceac45SDavid Howells 86254ceac45SDavid Howells spin_lock(&nfs_client_lock); 86354ceac45SDavid Howells list_del(&server->client_link); 86454ceac45SDavid Howells list_del(&server->master_link); 86554ceac45SDavid Howells spin_unlock(&nfs_client_lock); 86654ceac45SDavid Howells 86754ceac45SDavid Howells if (server->destroy != NULL) 86854ceac45SDavid Howells server->destroy(server); 8695cef338bSTrond Myklebust 8705cef338bSTrond Myklebust if (!IS_ERR(server->client_acl)) 8715cef338bSTrond Myklebust rpc_shutdown_client(server->client_acl); 87254ceac45SDavid Howells if (!IS_ERR(server->client)) 87354ceac45SDavid Howells rpc_shutdown_client(server->client); 87454ceac45SDavid Howells 87554ceac45SDavid Howells nfs_put_client(server->nfs_client); 87654ceac45SDavid Howells 87754ceac45SDavid Howells nfs_free_iostats(server->io_stats); 878e0bf68ddSPeter Zijlstra bdi_destroy(&server->backing_dev_info); 87954ceac45SDavid Howells kfree(server); 88054ceac45SDavid Howells nfs_release_automount_timer(); 88154ceac45SDavid Howells dprintk("<-- nfs_free_server()\n"); 88254ceac45SDavid Howells } 88354ceac45SDavid Howells 88454ceac45SDavid Howells /* 88554ceac45SDavid Howells * Create a version 2 or 3 volume record 88654ceac45SDavid Howells * - keyed on server and FSID 88754ceac45SDavid Howells */ 8882283f8d6S\"Talpey, Thomas\ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, 88954ceac45SDavid Howells struct nfs_fh *mntfh) 89054ceac45SDavid Howells { 89154ceac45SDavid Howells struct nfs_server *server; 89254ceac45SDavid Howells struct nfs_fattr fattr; 89354ceac45SDavid Howells int error; 89454ceac45SDavid Howells 89554ceac45SDavid Howells server = nfs_alloc_server(); 89654ceac45SDavid Howells if (!server) 89754ceac45SDavid Howells return ERR_PTR(-ENOMEM); 89854ceac45SDavid Howells 89954ceac45SDavid Howells /* Get a client representation */ 90054ceac45SDavid Howells error = nfs_init_server(server, data); 90154ceac45SDavid Howells if (error < 0) 90254ceac45SDavid Howells goto error; 90354ceac45SDavid Howells 90454ceac45SDavid Howells BUG_ON(!server->nfs_client); 90554ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops); 90654ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 90754ceac45SDavid Howells 90854ceac45SDavid Howells /* Probe the root fh to retrieve its FSID */ 90954ceac45SDavid Howells error = nfs_probe_fsinfo(server, mntfh, &fattr); 91054ceac45SDavid Howells if (error < 0) 91154ceac45SDavid Howells goto error; 91254af3bb5STrond Myklebust if (server->nfs_client->rpc_ops->version == 3) { 91354af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) 91454af3bb5STrond Myklebust server->namelen = NFS3_MAXNAMLEN; 91554af3bb5STrond Myklebust if (!(data->flags & NFS_MOUNT_NORDIRPLUS)) 91654af3bb5STrond Myklebust server->caps |= NFS_CAP_READDIRPLUS; 91754af3bb5STrond Myklebust } else { 91854af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN) 91954af3bb5STrond Myklebust server->namelen = NFS2_MAXNAMLEN; 92054af3bb5STrond Myklebust } 92154af3bb5STrond Myklebust 92254ceac45SDavid Howells if (!(fattr.valid & NFS_ATTR_FATTR)) { 92354ceac45SDavid Howells error = server->nfs_client->rpc_ops->getattr(server, mntfh, &fattr); 92454ceac45SDavid Howells if (error < 0) { 92554ceac45SDavid Howells dprintk("nfs_create_server: getattr error = %d\n", -error); 92654ceac45SDavid Howells goto error; 92754ceac45SDavid Howells } 92854ceac45SDavid Howells } 92954ceac45SDavid Howells memcpy(&server->fsid, &fattr.fsid, sizeof(server->fsid)); 93054ceac45SDavid Howells 9316daabf1bSDavid Howells dprintk("Server FSID: %llx:%llx\n", 9326daabf1bSDavid Howells (unsigned long long) server->fsid.major, 9336daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 93454ceac45SDavid Howells 93554ceac45SDavid Howells BUG_ON(!server->nfs_client); 93654ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops); 93754ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 93854ceac45SDavid Howells 93954ceac45SDavid Howells spin_lock(&nfs_client_lock); 94054ceac45SDavid Howells list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); 94154ceac45SDavid Howells list_add_tail(&server->master_link, &nfs_volume_list); 94254ceac45SDavid Howells spin_unlock(&nfs_client_lock); 94354ceac45SDavid Howells 94454ceac45SDavid Howells server->mount_time = jiffies; 94554ceac45SDavid Howells return server; 94654ceac45SDavid Howells 94754ceac45SDavid Howells error: 94854ceac45SDavid Howells nfs_free_server(server); 94954ceac45SDavid Howells return ERR_PTR(error); 95054ceac45SDavid Howells } 95154ceac45SDavid Howells 95254ceac45SDavid Howells #ifdef CONFIG_NFS_V4 95354ceac45SDavid Howells /* 95454ceac45SDavid Howells * Initialise an NFS4 client record 95554ceac45SDavid Howells */ 95654ceac45SDavid Howells static int nfs4_init_client(struct nfs_client *clp, 95733170233STrond Myklebust const struct rpc_timeout *timeparms, 9587d9ac06fSJ. Bruce Fields const char *ip_addr, 95954ceac45SDavid Howells rpc_authflavor_t authflavour) 96054ceac45SDavid Howells { 96154ceac45SDavid Howells int error; 96254ceac45SDavid Howells 96354ceac45SDavid Howells if (clp->cl_cons_state == NFS_CS_READY) { 96454ceac45SDavid Howells /* the client is initialised already */ 96554ceac45SDavid Howells dprintk("<-- nfs4_init_client() = 0 [already %p]\n", clp); 96654ceac45SDavid Howells return 0; 96754ceac45SDavid Howells } 96854ceac45SDavid Howells 96954ceac45SDavid Howells /* Check NFS protocol revision and initialize RPC op vector */ 97054ceac45SDavid Howells clp->rpc_ops = &nfs_v4_clientops; 97154ceac45SDavid Howells 97259dca3b2STrond Myklebust error = nfs_create_rpc_client(clp, timeparms, authflavour, 97343d78ef2SChuck Lever RPC_CLNT_CREATE_DISCRTRY); 97454ceac45SDavid Howells if (error < 0) 97554ceac45SDavid Howells goto error; 9767d9ac06fSJ. Bruce Fields memcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr)); 97754ceac45SDavid Howells 97854ceac45SDavid Howells error = nfs_idmap_new(clp); 97954ceac45SDavid Howells if (error < 0) { 98054ceac45SDavid Howells dprintk("%s: failed to create idmapper. Error = %d\n", 98154ceac45SDavid Howells __FUNCTION__, error); 98254ceac45SDavid Howells goto error; 98354ceac45SDavid Howells } 9849c5bf38dSTrond Myklebust __set_bit(NFS_CS_IDMAP, &clp->cl_res_state); 98554ceac45SDavid Howells 98654ceac45SDavid Howells nfs_mark_client_ready(clp, NFS_CS_READY); 98754ceac45SDavid Howells return 0; 98854ceac45SDavid Howells 98954ceac45SDavid Howells error: 99054ceac45SDavid Howells nfs_mark_client_ready(clp, error); 99154ceac45SDavid Howells dprintk("<-- nfs4_init_client() = xerror %d\n", error); 99254ceac45SDavid Howells return error; 99354ceac45SDavid Howells } 99454ceac45SDavid Howells 99554ceac45SDavid Howells /* 99654ceac45SDavid Howells * Set up an NFS4 client 99754ceac45SDavid Howells */ 99854ceac45SDavid Howells static int nfs4_set_client(struct nfs_server *server, 999dcecae0fSChuck Lever const char *hostname, 1000dcecae0fSChuck Lever const struct sockaddr *addr, 1001dcecae0fSChuck Lever const size_t addrlen, 10027d9ac06fSJ. Bruce Fields const char *ip_addr, 100354ceac45SDavid Howells rpc_authflavor_t authflavour, 100433170233STrond Myklebust int proto, const struct rpc_timeout *timeparms) 100554ceac45SDavid Howells { 10063a498026STrond Myklebust struct nfs_client_initdata cl_init = { 10073a498026STrond Myklebust .hostname = hostname, 1008dcecae0fSChuck Lever .addr = addr, 1009dcecae0fSChuck Lever .addrlen = addrlen, 101040c55319STrond Myklebust .rpc_ops = &nfs_v4_clientops, 101159dca3b2STrond Myklebust .proto = proto, 10123a498026STrond Myklebust }; 101354ceac45SDavid Howells struct nfs_client *clp; 101454ceac45SDavid Howells int error; 101554ceac45SDavid Howells 101654ceac45SDavid Howells dprintk("--> nfs4_set_client()\n"); 101754ceac45SDavid Howells 101854ceac45SDavid Howells /* Allocate or find a client reference we can use */ 10193a498026STrond Myklebust clp = nfs_get_client(&cl_init); 102054ceac45SDavid Howells if (IS_ERR(clp)) { 102154ceac45SDavid Howells error = PTR_ERR(clp); 102254ceac45SDavid Howells goto error; 102354ceac45SDavid Howells } 102459dca3b2STrond Myklebust error = nfs4_init_client(clp, timeparms, ip_addr, authflavour); 102554ceac45SDavid Howells if (error < 0) 102654ceac45SDavid Howells goto error_put; 102754ceac45SDavid Howells 102854ceac45SDavid Howells server->nfs_client = clp; 102954ceac45SDavid Howells dprintk("<-- nfs4_set_client() = 0 [new %p]\n", clp); 103054ceac45SDavid Howells return 0; 103154ceac45SDavid Howells 103254ceac45SDavid Howells error_put: 103354ceac45SDavid Howells nfs_put_client(clp); 103454ceac45SDavid Howells error: 103554ceac45SDavid Howells dprintk("<-- nfs4_set_client() = xerror %d\n", error); 103654ceac45SDavid Howells return error; 103754ceac45SDavid Howells } 103854ceac45SDavid Howells 103954ceac45SDavid Howells /* 104054ceac45SDavid Howells * Create a version 4 volume record 104154ceac45SDavid Howells */ 104254ceac45SDavid Howells static int nfs4_init_server(struct nfs_server *server, 104391ea40b9S\"Talpey, Thomas\ const struct nfs_parsed_mount_data *data) 104454ceac45SDavid Howells { 104533170233STrond Myklebust struct rpc_timeout timeparms; 104654ceac45SDavid Howells int error; 104754ceac45SDavid Howells 104854ceac45SDavid Howells dprintk("--> nfs4_init_server()\n"); 104954ceac45SDavid Howells 105033170233STrond Myklebust nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, 105133170233STrond Myklebust data->timeo, data->retrans); 105233170233STrond Myklebust 105333170233STrond Myklebust /* Get a client record */ 105433170233STrond Myklebust error = nfs4_set_client(server, 105533170233STrond Myklebust data->nfs_server.hostname, 105633170233STrond Myklebust (const struct sockaddr *)&data->nfs_server.address, 105733170233STrond Myklebust data->nfs_server.addrlen, 105833170233STrond Myklebust data->client_address, 105933170233STrond Myklebust data->auth_flavors[0], 106033170233STrond Myklebust data->nfs_server.protocol, 106133170233STrond Myklebust &timeparms); 106233170233STrond Myklebust if (error < 0) 106333170233STrond Myklebust goto error; 106433170233STrond Myklebust 106554ceac45SDavid Howells /* Initialise the client representation from the mount data */ 106654ceac45SDavid Howells server->flags = data->flags & NFS_MOUNT_FLAGMASK; 106754ceac45SDavid Howells server->caps |= NFS_CAP_ATOMIC_OPEN; 106854ceac45SDavid Howells 106954ceac45SDavid Howells if (data->rsize) 107054ceac45SDavid Howells server->rsize = nfs_block_size(data->rsize, NULL); 107154ceac45SDavid Howells if (data->wsize) 107254ceac45SDavid Howells server->wsize = nfs_block_size(data->wsize, NULL); 107354ceac45SDavid Howells 107454ceac45SDavid Howells server->acregmin = data->acregmin * HZ; 107554ceac45SDavid Howells server->acregmax = data->acregmax * HZ; 107654ceac45SDavid Howells server->acdirmin = data->acdirmin * HZ; 107754ceac45SDavid Howells server->acdirmax = data->acdirmax * HZ; 107854ceac45SDavid Howells 1079f22d6d79SChuck Lever server->port = data->nfs_server.port; 1080f22d6d79SChuck Lever 108133170233STrond Myklebust error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]); 108254ceac45SDavid Howells 108333170233STrond Myklebust error: 108454ceac45SDavid Howells /* Done */ 108554ceac45SDavid Howells dprintk("<-- nfs4_init_server() = %d\n", error); 108654ceac45SDavid Howells return error; 108754ceac45SDavid Howells } 108854ceac45SDavid Howells 108954ceac45SDavid Howells /* 109054ceac45SDavid Howells * Create a version 4 volume record 109154ceac45SDavid Howells * - keyed on server and FSID 109254ceac45SDavid Howells */ 109391ea40b9S\"Talpey, Thomas\ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, 109454ceac45SDavid Howells struct nfs_fh *mntfh) 109554ceac45SDavid Howells { 109654ceac45SDavid Howells struct nfs_fattr fattr; 109754ceac45SDavid Howells struct nfs_server *server; 109854ceac45SDavid Howells int error; 109954ceac45SDavid Howells 110054ceac45SDavid Howells dprintk("--> nfs4_create_server()\n"); 110154ceac45SDavid Howells 110254ceac45SDavid Howells server = nfs_alloc_server(); 110354ceac45SDavid Howells if (!server) 110454ceac45SDavid Howells return ERR_PTR(-ENOMEM); 110554ceac45SDavid Howells 110654ceac45SDavid Howells /* set up the general RPC client */ 110791ea40b9S\"Talpey, Thomas\ error = nfs4_init_server(server, data); 110854ceac45SDavid Howells if (error < 0) 110954ceac45SDavid Howells goto error; 111054ceac45SDavid Howells 111154ceac45SDavid Howells BUG_ON(!server->nfs_client); 111254ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops); 111354ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 111454ceac45SDavid Howells 111554ceac45SDavid Howells /* Probe the root fh to retrieve its FSID */ 111691ea40b9S\"Talpey, Thomas\ error = nfs4_path_walk(server, mntfh, data->nfs_server.export_path); 111754ceac45SDavid Howells if (error < 0) 111854ceac45SDavid Howells goto error; 111954ceac45SDavid Howells 11206daabf1bSDavid Howells dprintk("Server FSID: %llx:%llx\n", 11216daabf1bSDavid Howells (unsigned long long) server->fsid.major, 11226daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 112354ceac45SDavid Howells dprintk("Mount FH: %d\n", mntfh->size); 112454ceac45SDavid Howells 112554ceac45SDavid Howells error = nfs_probe_fsinfo(server, mntfh, &fattr); 112654ceac45SDavid Howells if (error < 0) 112754ceac45SDavid Howells goto error; 112854ceac45SDavid Howells 112954af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 113054af3bb5STrond Myklebust server->namelen = NFS4_MAXNAMLEN; 113154af3bb5STrond Myklebust 113254ceac45SDavid Howells BUG_ON(!server->nfs_client); 113354ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops); 113454ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 113554ceac45SDavid Howells 113654ceac45SDavid Howells spin_lock(&nfs_client_lock); 113754ceac45SDavid Howells list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); 113854ceac45SDavid Howells list_add_tail(&server->master_link, &nfs_volume_list); 113954ceac45SDavid Howells spin_unlock(&nfs_client_lock); 114054ceac45SDavid Howells 114154ceac45SDavid Howells server->mount_time = jiffies; 114254ceac45SDavid Howells dprintk("<-- nfs4_create_server() = %p\n", server); 114354ceac45SDavid Howells return server; 114454ceac45SDavid Howells 114554ceac45SDavid Howells error: 114654ceac45SDavid Howells nfs_free_server(server); 114754ceac45SDavid Howells dprintk("<-- nfs4_create_server() = error %d\n", error); 114854ceac45SDavid Howells return ERR_PTR(error); 114954ceac45SDavid Howells } 115054ceac45SDavid Howells 115154ceac45SDavid Howells /* 115254ceac45SDavid Howells * Create an NFS4 referral server record 115354ceac45SDavid Howells */ 115454ceac45SDavid Howells struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, 1155f2d0d85eSTrond Myklebust struct nfs_fh *mntfh) 115654ceac45SDavid Howells { 115754ceac45SDavid Howells struct nfs_client *parent_client; 115854ceac45SDavid Howells struct nfs_server *server, *parent_server; 115954ceac45SDavid Howells struct nfs_fattr fattr; 116054ceac45SDavid Howells int error; 116154ceac45SDavid Howells 116254ceac45SDavid Howells dprintk("--> nfs4_create_referral_server()\n"); 116354ceac45SDavid Howells 116454ceac45SDavid Howells server = nfs_alloc_server(); 116554ceac45SDavid Howells if (!server) 116654ceac45SDavid Howells return ERR_PTR(-ENOMEM); 116754ceac45SDavid Howells 116854ceac45SDavid Howells parent_server = NFS_SB(data->sb); 116954ceac45SDavid Howells parent_client = parent_server->nfs_client; 117054ceac45SDavid Howells 117154ceac45SDavid Howells /* Get a client representation. 117254ceac45SDavid Howells * Note: NFSv4 always uses TCP, */ 1173dcecae0fSChuck Lever error = nfs4_set_client(server, data->hostname, 11746677d095SChuck Lever data->addr, 11756677d095SChuck Lever data->addrlen, 11767d9ac06fSJ. Bruce Fields parent_client->cl_ipaddr, 117754ceac45SDavid Howells data->authflavor, 117854ceac45SDavid Howells parent_server->client->cl_xprt->prot, 117933170233STrond Myklebust parent_server->client->cl_timeout); 1180297de4f6Sandros@citi.umich.edu if (error < 0) 1181297de4f6Sandros@citi.umich.edu goto error; 118254ceac45SDavid Howells 118354ceac45SDavid Howells /* Initialise the client representation from the parent server */ 118454ceac45SDavid Howells nfs_server_copy_userdata(server, parent_server); 118554ceac45SDavid Howells server->caps |= NFS_CAP_ATOMIC_OPEN; 118654ceac45SDavid Howells 118733170233STrond Myklebust error = nfs_init_server_rpcclient(server, parent_server->client->cl_timeout, data->authflavor); 118854ceac45SDavid Howells if (error < 0) 118954ceac45SDavid Howells goto error; 119054ceac45SDavid Howells 119154ceac45SDavid Howells BUG_ON(!server->nfs_client); 119254ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops); 119354ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 119454ceac45SDavid Howells 1195f2d0d85eSTrond Myklebust /* Probe the root fh to retrieve its FSID and filehandle */ 1196f2d0d85eSTrond Myklebust error = nfs4_path_walk(server, mntfh, data->mnt_path); 1197f2d0d85eSTrond Myklebust if (error < 0) 1198f2d0d85eSTrond Myklebust goto error; 1199f2d0d85eSTrond Myklebust 120054ceac45SDavid Howells /* probe the filesystem info for this server filesystem */ 1201f2d0d85eSTrond Myklebust error = nfs_probe_fsinfo(server, mntfh, &fattr); 120254ceac45SDavid Howells if (error < 0) 120354ceac45SDavid Howells goto error; 120454ceac45SDavid Howells 120554af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 120654af3bb5STrond Myklebust server->namelen = NFS4_MAXNAMLEN; 120754af3bb5STrond Myklebust 120854ceac45SDavid Howells dprintk("Referral FSID: %llx:%llx\n", 12096daabf1bSDavid Howells (unsigned long long) server->fsid.major, 12106daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 121154ceac45SDavid Howells 121254ceac45SDavid Howells spin_lock(&nfs_client_lock); 121354ceac45SDavid Howells list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); 121454ceac45SDavid Howells list_add_tail(&server->master_link, &nfs_volume_list); 121554ceac45SDavid Howells spin_unlock(&nfs_client_lock); 121654ceac45SDavid Howells 121754ceac45SDavid Howells server->mount_time = jiffies; 121854ceac45SDavid Howells 121954ceac45SDavid Howells dprintk("<-- nfs_create_referral_server() = %p\n", server); 122054ceac45SDavid Howells return server; 122154ceac45SDavid Howells 122254ceac45SDavid Howells error: 122354ceac45SDavid Howells nfs_free_server(server); 122454ceac45SDavid Howells dprintk("<-- nfs4_create_referral_server() = error %d\n", error); 122554ceac45SDavid Howells return ERR_PTR(error); 122654ceac45SDavid Howells } 122754ceac45SDavid Howells 122854ceac45SDavid Howells #endif /* CONFIG_NFS_V4 */ 122954ceac45SDavid Howells 123054ceac45SDavid Howells /* 123154ceac45SDavid Howells * Clone an NFS2, NFS3 or NFS4 server record 123254ceac45SDavid Howells */ 123354ceac45SDavid Howells struct nfs_server *nfs_clone_server(struct nfs_server *source, 123454ceac45SDavid Howells struct nfs_fh *fh, 123554ceac45SDavid Howells struct nfs_fattr *fattr) 123654ceac45SDavid Howells { 123754ceac45SDavid Howells struct nfs_server *server; 123854ceac45SDavid Howells struct nfs_fattr fattr_fsinfo; 123954ceac45SDavid Howells int error; 124054ceac45SDavid Howells 124154ceac45SDavid Howells dprintk("--> nfs_clone_server(,%llx:%llx,)\n", 12426daabf1bSDavid Howells (unsigned long long) fattr->fsid.major, 12436daabf1bSDavid Howells (unsigned long long) fattr->fsid.minor); 124454ceac45SDavid Howells 124554ceac45SDavid Howells server = nfs_alloc_server(); 124654ceac45SDavid Howells if (!server) 124754ceac45SDavid Howells return ERR_PTR(-ENOMEM); 124854ceac45SDavid Howells 124954ceac45SDavid Howells /* Copy data from the source */ 125054ceac45SDavid Howells server->nfs_client = source->nfs_client; 125154ceac45SDavid Howells atomic_inc(&server->nfs_client->cl_count); 125254ceac45SDavid Howells nfs_server_copy_userdata(server, source); 125354ceac45SDavid Howells 125454ceac45SDavid Howells server->fsid = fattr->fsid; 125554ceac45SDavid Howells 125633170233STrond Myklebust error = nfs_init_server_rpcclient(server, 125733170233STrond Myklebust source->client->cl_timeout, 125833170233STrond Myklebust source->client->cl_auth->au_flavor); 125954ceac45SDavid Howells if (error < 0) 126054ceac45SDavid Howells goto out_free_server; 126154ceac45SDavid Howells if (!IS_ERR(source->client_acl)) 126254ceac45SDavid Howells nfs_init_server_aclclient(server); 126354ceac45SDavid Howells 126454ceac45SDavid Howells /* probe the filesystem info for this server filesystem */ 126554ceac45SDavid Howells error = nfs_probe_fsinfo(server, fh, &fattr_fsinfo); 126654ceac45SDavid Howells if (error < 0) 126754ceac45SDavid Howells goto out_free_server; 126854ceac45SDavid Howells 126954af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 127054af3bb5STrond Myklebust server->namelen = NFS4_MAXNAMLEN; 127154af3bb5STrond Myklebust 127254ceac45SDavid Howells dprintk("Cloned FSID: %llx:%llx\n", 12736daabf1bSDavid Howells (unsigned long long) server->fsid.major, 12746daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 127554ceac45SDavid Howells 127654ceac45SDavid Howells error = nfs_start_lockd(server); 127754ceac45SDavid Howells if (error < 0) 127854ceac45SDavid Howells goto out_free_server; 127954ceac45SDavid Howells 128054ceac45SDavid Howells spin_lock(&nfs_client_lock); 128154ceac45SDavid Howells list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); 128254ceac45SDavid Howells list_add_tail(&server->master_link, &nfs_volume_list); 128354ceac45SDavid Howells spin_unlock(&nfs_client_lock); 128454ceac45SDavid Howells 128554ceac45SDavid Howells server->mount_time = jiffies; 128654ceac45SDavid Howells 128754ceac45SDavid Howells dprintk("<-- nfs_clone_server() = %p\n", server); 128854ceac45SDavid Howells return server; 128954ceac45SDavid Howells 129054ceac45SDavid Howells out_free_server: 129154ceac45SDavid Howells nfs_free_server(server); 129254ceac45SDavid Howells dprintk("<-- nfs_clone_server() = error %d\n", error); 129354ceac45SDavid Howells return ERR_PTR(error); 129454ceac45SDavid Howells } 12956aaca566SDavid Howells 12966aaca566SDavid Howells #ifdef CONFIG_PROC_FS 12976aaca566SDavid Howells static struct proc_dir_entry *proc_fs_nfs; 12986aaca566SDavid Howells 12996aaca566SDavid Howells static int nfs_server_list_open(struct inode *inode, struct file *file); 13006aaca566SDavid Howells static void *nfs_server_list_start(struct seq_file *p, loff_t *pos); 13016aaca566SDavid Howells static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos); 13026aaca566SDavid Howells static void nfs_server_list_stop(struct seq_file *p, void *v); 13036aaca566SDavid Howells static int nfs_server_list_show(struct seq_file *m, void *v); 13046aaca566SDavid Howells 13056aaca566SDavid Howells static struct seq_operations nfs_server_list_ops = { 13066aaca566SDavid Howells .start = nfs_server_list_start, 13076aaca566SDavid Howells .next = nfs_server_list_next, 13086aaca566SDavid Howells .stop = nfs_server_list_stop, 13096aaca566SDavid Howells .show = nfs_server_list_show, 13106aaca566SDavid Howells }; 13116aaca566SDavid Howells 131200977a59SArjan van de Ven static const struct file_operations nfs_server_list_fops = { 13136aaca566SDavid Howells .open = nfs_server_list_open, 13146aaca566SDavid Howells .read = seq_read, 13156aaca566SDavid Howells .llseek = seq_lseek, 13166aaca566SDavid Howells .release = seq_release, 13176aaca566SDavid Howells }; 13186aaca566SDavid Howells 13196aaca566SDavid Howells static int nfs_volume_list_open(struct inode *inode, struct file *file); 13206aaca566SDavid Howells static void *nfs_volume_list_start(struct seq_file *p, loff_t *pos); 13216aaca566SDavid Howells static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos); 13226aaca566SDavid Howells static void nfs_volume_list_stop(struct seq_file *p, void *v); 13236aaca566SDavid Howells static int nfs_volume_list_show(struct seq_file *m, void *v); 13246aaca566SDavid Howells 13256aaca566SDavid Howells static struct seq_operations nfs_volume_list_ops = { 13266aaca566SDavid Howells .start = nfs_volume_list_start, 13276aaca566SDavid Howells .next = nfs_volume_list_next, 13286aaca566SDavid Howells .stop = nfs_volume_list_stop, 13296aaca566SDavid Howells .show = nfs_volume_list_show, 13306aaca566SDavid Howells }; 13316aaca566SDavid Howells 133200977a59SArjan van de Ven static const struct file_operations nfs_volume_list_fops = { 13336aaca566SDavid Howells .open = nfs_volume_list_open, 13346aaca566SDavid Howells .read = seq_read, 13356aaca566SDavid Howells .llseek = seq_lseek, 13366aaca566SDavid Howells .release = seq_release, 13376aaca566SDavid Howells }; 13386aaca566SDavid Howells 13396aaca566SDavid Howells /* 13406aaca566SDavid Howells * open "/proc/fs/nfsfs/servers" which provides a summary of servers with which 13416aaca566SDavid Howells * we're dealing 13426aaca566SDavid Howells */ 13436aaca566SDavid Howells static int nfs_server_list_open(struct inode *inode, struct file *file) 13446aaca566SDavid Howells { 13456aaca566SDavid Howells struct seq_file *m; 13466aaca566SDavid Howells int ret; 13476aaca566SDavid Howells 13486aaca566SDavid Howells ret = seq_open(file, &nfs_server_list_ops); 13496aaca566SDavid Howells if (ret < 0) 13506aaca566SDavid Howells return ret; 13516aaca566SDavid Howells 13526aaca566SDavid Howells m = file->private_data; 13536aaca566SDavid Howells m->private = PDE(inode)->data; 13546aaca566SDavid Howells 13556aaca566SDavid Howells return 0; 13566aaca566SDavid Howells } 13576aaca566SDavid Howells 13586aaca566SDavid Howells /* 13596aaca566SDavid Howells * set up the iterator to start reading from the server list and return the first item 13606aaca566SDavid Howells */ 13616aaca566SDavid Howells static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos) 13626aaca566SDavid Howells { 13636aaca566SDavid Howells /* lock the list against modification */ 13646aaca566SDavid Howells spin_lock(&nfs_client_lock); 1365259902eaSPavel Emelianov return seq_list_start_head(&nfs_client_list, *_pos); 13666aaca566SDavid Howells } 13676aaca566SDavid Howells 13686aaca566SDavid Howells /* 13696aaca566SDavid Howells * move to next server 13706aaca566SDavid Howells */ 13716aaca566SDavid Howells static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos) 13726aaca566SDavid Howells { 1373259902eaSPavel Emelianov return seq_list_next(v, &nfs_client_list, pos); 13746aaca566SDavid Howells } 13756aaca566SDavid Howells 13766aaca566SDavid Howells /* 13776aaca566SDavid Howells * clean up after reading from the transports list 13786aaca566SDavid Howells */ 13796aaca566SDavid Howells static void nfs_server_list_stop(struct seq_file *p, void *v) 13806aaca566SDavid Howells { 13816aaca566SDavid Howells spin_unlock(&nfs_client_lock); 13826aaca566SDavid Howells } 13836aaca566SDavid Howells 13846aaca566SDavid Howells /* 13856aaca566SDavid Howells * display a header line followed by a load of call lines 13866aaca566SDavid Howells */ 13876aaca566SDavid Howells static int nfs_server_list_show(struct seq_file *m, void *v) 13886aaca566SDavid Howells { 13896aaca566SDavid Howells struct nfs_client *clp; 13906aaca566SDavid Howells 13916aaca566SDavid Howells /* display header on line 1 */ 1392259902eaSPavel Emelianov if (v == &nfs_client_list) { 13936aaca566SDavid Howells seq_puts(m, "NV SERVER PORT USE HOSTNAME\n"); 13946aaca566SDavid Howells return 0; 13956aaca566SDavid Howells } 13966aaca566SDavid Howells 13976aaca566SDavid Howells /* display one transport per line on subsequent lines */ 13986aaca566SDavid Howells clp = list_entry(v, struct nfs_client, cl_share_link); 13996aaca566SDavid Howells 14005d8515caSChuck Lever seq_printf(m, "v%u %s %s %3d %s\n", 140140c55319STrond Myklebust clp->rpc_ops->version, 14025d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), 14035d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), 14046aaca566SDavid Howells atomic_read(&clp->cl_count), 14056aaca566SDavid Howells clp->cl_hostname); 14066aaca566SDavid Howells 14076aaca566SDavid Howells return 0; 14086aaca566SDavid Howells } 14096aaca566SDavid Howells 14106aaca566SDavid Howells /* 14116aaca566SDavid Howells * open "/proc/fs/nfsfs/volumes" which provides a summary of extant volumes 14126aaca566SDavid Howells */ 14136aaca566SDavid Howells static int nfs_volume_list_open(struct inode *inode, struct file *file) 14146aaca566SDavid Howells { 14156aaca566SDavid Howells struct seq_file *m; 14166aaca566SDavid Howells int ret; 14176aaca566SDavid Howells 14186aaca566SDavid Howells ret = seq_open(file, &nfs_volume_list_ops); 14196aaca566SDavid Howells if (ret < 0) 14206aaca566SDavid Howells return ret; 14216aaca566SDavid Howells 14226aaca566SDavid Howells m = file->private_data; 14236aaca566SDavid Howells m->private = PDE(inode)->data; 14246aaca566SDavid Howells 14256aaca566SDavid Howells return 0; 14266aaca566SDavid Howells } 14276aaca566SDavid Howells 14286aaca566SDavid Howells /* 14296aaca566SDavid Howells * set up the iterator to start reading from the volume list and return the first item 14306aaca566SDavid Howells */ 14316aaca566SDavid Howells static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos) 14326aaca566SDavid Howells { 14336aaca566SDavid Howells /* lock the list against modification */ 14346aaca566SDavid Howells spin_lock(&nfs_client_lock); 1435259902eaSPavel Emelianov return seq_list_start_head(&nfs_volume_list, *_pos); 14366aaca566SDavid Howells } 14376aaca566SDavid Howells 14386aaca566SDavid Howells /* 14396aaca566SDavid Howells * move to next volume 14406aaca566SDavid Howells */ 14416aaca566SDavid Howells static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos) 14426aaca566SDavid Howells { 1443259902eaSPavel Emelianov return seq_list_next(v, &nfs_volume_list, pos); 14446aaca566SDavid Howells } 14456aaca566SDavid Howells 14466aaca566SDavid Howells /* 14476aaca566SDavid Howells * clean up after reading from the transports list 14486aaca566SDavid Howells */ 14496aaca566SDavid Howells static void nfs_volume_list_stop(struct seq_file *p, void *v) 14506aaca566SDavid Howells { 14516aaca566SDavid Howells spin_unlock(&nfs_client_lock); 14526aaca566SDavid Howells } 14536aaca566SDavid Howells 14546aaca566SDavid Howells /* 14556aaca566SDavid Howells * display a header line followed by a load of call lines 14566aaca566SDavid Howells */ 14576aaca566SDavid Howells static int nfs_volume_list_show(struct seq_file *m, void *v) 14586aaca566SDavid Howells { 14596aaca566SDavid Howells struct nfs_server *server; 14606aaca566SDavid Howells struct nfs_client *clp; 14616aaca566SDavid Howells char dev[8], fsid[17]; 14626aaca566SDavid Howells 14636aaca566SDavid Howells /* display header on line 1 */ 1464259902eaSPavel Emelianov if (v == &nfs_volume_list) { 14656aaca566SDavid Howells seq_puts(m, "NV SERVER PORT DEV FSID\n"); 14666aaca566SDavid Howells return 0; 14676aaca566SDavid Howells } 14686aaca566SDavid Howells /* display one transport per line on subsequent lines */ 14696aaca566SDavid Howells server = list_entry(v, struct nfs_server, master_link); 14706aaca566SDavid Howells clp = server->nfs_client; 14716aaca566SDavid Howells 14726aaca566SDavid Howells snprintf(dev, 8, "%u:%u", 14736aaca566SDavid Howells MAJOR(server->s_dev), MINOR(server->s_dev)); 14746aaca566SDavid Howells 14756aaca566SDavid Howells snprintf(fsid, 17, "%llx:%llx", 14766daabf1bSDavid Howells (unsigned long long) server->fsid.major, 14776daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 14786aaca566SDavid Howells 14795d8515caSChuck Lever seq_printf(m, "v%u %s %s %-7s %-17s\n", 148040c55319STrond Myklebust clp->rpc_ops->version, 14815d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR), 14825d8515caSChuck Lever rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT), 14836aaca566SDavid Howells dev, 14846aaca566SDavid Howells fsid); 14856aaca566SDavid Howells 14866aaca566SDavid Howells return 0; 14876aaca566SDavid Howells } 14886aaca566SDavid Howells 14896aaca566SDavid Howells /* 14906aaca566SDavid Howells * initialise the /proc/fs/nfsfs/ directory 14916aaca566SDavid Howells */ 14926aaca566SDavid Howells int __init nfs_fs_proc_init(void) 14936aaca566SDavid Howells { 14946aaca566SDavid Howells struct proc_dir_entry *p; 14956aaca566SDavid Howells 14966aaca566SDavid Howells proc_fs_nfs = proc_mkdir("nfsfs", proc_root_fs); 14976aaca566SDavid Howells if (!proc_fs_nfs) 14986aaca566SDavid Howells goto error_0; 14996aaca566SDavid Howells 15006aaca566SDavid Howells proc_fs_nfs->owner = THIS_MODULE; 15016aaca566SDavid Howells 15026aaca566SDavid Howells /* a file of servers with which we're dealing */ 15036aaca566SDavid Howells p = create_proc_entry("servers", S_IFREG|S_IRUGO, proc_fs_nfs); 15046aaca566SDavid Howells if (!p) 15056aaca566SDavid Howells goto error_1; 15066aaca566SDavid Howells 15076aaca566SDavid Howells p->proc_fops = &nfs_server_list_fops; 15086aaca566SDavid Howells p->owner = THIS_MODULE; 15096aaca566SDavid Howells 15106aaca566SDavid Howells /* a file of volumes that we have mounted */ 15116aaca566SDavid Howells p = create_proc_entry("volumes", S_IFREG|S_IRUGO, proc_fs_nfs); 15126aaca566SDavid Howells if (!p) 15136aaca566SDavid Howells goto error_2; 15146aaca566SDavid Howells 15156aaca566SDavid Howells p->proc_fops = &nfs_volume_list_fops; 15166aaca566SDavid Howells p->owner = THIS_MODULE; 15176aaca566SDavid Howells return 0; 15186aaca566SDavid Howells 15196aaca566SDavid Howells error_2: 15206aaca566SDavid Howells remove_proc_entry("servers", proc_fs_nfs); 15216aaca566SDavid Howells error_1: 15226aaca566SDavid Howells remove_proc_entry("nfsfs", proc_root_fs); 15236aaca566SDavid Howells error_0: 15246aaca566SDavid Howells return -ENOMEM; 15256aaca566SDavid Howells } 15266aaca566SDavid Howells 15276aaca566SDavid Howells /* 15286aaca566SDavid Howells * clean up the /proc/fs/nfsfs/ directory 15296aaca566SDavid Howells */ 15306aaca566SDavid Howells void nfs_fs_proc_exit(void) 15316aaca566SDavid Howells { 15326aaca566SDavid Howells remove_proc_entry("volumes", proc_fs_nfs); 15336aaca566SDavid Howells remove_proc_entry("servers", proc_fs_nfs); 15346aaca566SDavid Howells remove_proc_entry("nfsfs", proc_root_fs); 15356aaca566SDavid Howells } 15366aaca566SDavid Howells 15376aaca566SDavid Howells #endif /* CONFIG_PROC_FS */ 1538