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> 3724c8dbbbSDavid Howells #include <linux/nfs_xdr.h> 3824c8dbbbSDavid Howells 3924c8dbbbSDavid Howells #include <asm/system.h> 4024c8dbbbSDavid Howells 4124c8dbbbSDavid Howells #include "nfs4_fs.h" 4224c8dbbbSDavid Howells #include "callback.h" 4324c8dbbbSDavid Howells #include "delegation.h" 4424c8dbbbSDavid Howells #include "iostat.h" 4524c8dbbbSDavid Howells #include "internal.h" 4624c8dbbbSDavid Howells 4724c8dbbbSDavid Howells #define NFSDBG_FACILITY NFSDBG_CLIENT 4824c8dbbbSDavid Howells 4924c8dbbbSDavid Howells static DEFINE_SPINLOCK(nfs_client_lock); 5024c8dbbbSDavid Howells static LIST_HEAD(nfs_client_list); 5154ceac45SDavid Howells static LIST_HEAD(nfs_volume_list); 5224c8dbbbSDavid Howells static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq); 5324c8dbbbSDavid Howells 5424c8dbbbSDavid Howells /* 555006a76cSDavid Howells * RPC cruft for NFS 565006a76cSDavid Howells */ 575006a76cSDavid Howells static struct rpc_version *nfs_version[5] = { 585006a76cSDavid Howells [2] = &nfs_version2, 595006a76cSDavid Howells #ifdef CONFIG_NFS_V3 605006a76cSDavid Howells [3] = &nfs_version3, 615006a76cSDavid Howells #endif 625006a76cSDavid Howells #ifdef CONFIG_NFS_V4 635006a76cSDavid Howells [4] = &nfs_version4, 645006a76cSDavid Howells #endif 655006a76cSDavid Howells }; 665006a76cSDavid Howells 675006a76cSDavid Howells struct rpc_program nfs_program = { 685006a76cSDavid Howells .name = "nfs", 695006a76cSDavid Howells .number = NFS_PROGRAM, 705006a76cSDavid Howells .nrvers = ARRAY_SIZE(nfs_version), 715006a76cSDavid Howells .version = nfs_version, 725006a76cSDavid Howells .stats = &nfs_rpcstat, 735006a76cSDavid Howells .pipe_dir_name = "/nfs", 745006a76cSDavid Howells }; 755006a76cSDavid Howells 765006a76cSDavid Howells struct rpc_stat nfs_rpcstat = { 775006a76cSDavid Howells .program = &nfs_program 785006a76cSDavid Howells }; 795006a76cSDavid Howells 805006a76cSDavid Howells 815006a76cSDavid Howells #ifdef CONFIG_NFS_V3_ACL 825006a76cSDavid Howells static struct rpc_stat nfsacl_rpcstat = { &nfsacl_program }; 835006a76cSDavid Howells static struct rpc_version * nfsacl_version[] = { 845006a76cSDavid Howells [3] = &nfsacl_version3, 855006a76cSDavid Howells }; 865006a76cSDavid Howells 875006a76cSDavid Howells struct rpc_program nfsacl_program = { 885006a76cSDavid Howells .name = "nfsacl", 895006a76cSDavid Howells .number = NFS_ACL_PROGRAM, 905006a76cSDavid Howells .nrvers = ARRAY_SIZE(nfsacl_version), 915006a76cSDavid Howells .version = nfsacl_version, 925006a76cSDavid Howells .stats = &nfsacl_rpcstat, 935006a76cSDavid Howells }; 945006a76cSDavid Howells #endif /* CONFIG_NFS_V3_ACL */ 955006a76cSDavid Howells 96*3a498026STrond Myklebust struct nfs_client_initdata { 97*3a498026STrond Myklebust const char *hostname; 98*3a498026STrond Myklebust const struct sockaddr_in *addr; 99*3a498026STrond Myklebust int version; 100*3a498026STrond Myklebust }; 101*3a498026STrond Myklebust 1025006a76cSDavid Howells /* 10324c8dbbbSDavid Howells * Allocate a shared client record 10424c8dbbbSDavid Howells * 10524c8dbbbSDavid Howells * Since these are allocated/deallocated very rarely, we don't 10624c8dbbbSDavid Howells * bother putting them in a slab cache... 10724c8dbbbSDavid Howells */ 108*3a498026STrond Myklebust static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) 10924c8dbbbSDavid Howells { 11024c8dbbbSDavid Howells struct nfs_client *clp; 11124c8dbbbSDavid Howells 11224c8dbbbSDavid Howells if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) 11324c8dbbbSDavid Howells goto error_0; 11424c8dbbbSDavid Howells 115*3a498026STrond Myklebust if (cl_init->version == 4) { 11624c8dbbbSDavid Howells if (nfs_callback_up() < 0) 11724c8dbbbSDavid Howells goto error_2; 11824c8dbbbSDavid Howells __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state); 11924c8dbbbSDavid Howells } 12024c8dbbbSDavid Howells 12124c8dbbbSDavid Howells atomic_set(&clp->cl_count, 1); 12224c8dbbbSDavid Howells clp->cl_cons_state = NFS_CS_INITING; 12324c8dbbbSDavid Howells 124*3a498026STrond Myklebust clp->cl_nfsversion = cl_init->version; 125*3a498026STrond Myklebust memcpy(&clp->cl_addr, cl_init->addr, sizeof(clp->cl_addr)); 12624c8dbbbSDavid Howells 127*3a498026STrond Myklebust if (cl_init->hostname) { 128*3a498026STrond Myklebust clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL); 12924c8dbbbSDavid Howells if (!clp->cl_hostname) 13024c8dbbbSDavid Howells goto error_3; 13124c8dbbbSDavid Howells } 13224c8dbbbSDavid Howells 13324c8dbbbSDavid Howells INIT_LIST_HEAD(&clp->cl_superblocks); 13424c8dbbbSDavid Howells clp->cl_rpcclient = ERR_PTR(-EINVAL); 13524c8dbbbSDavid Howells 13624c8dbbbSDavid Howells #ifdef CONFIG_NFS_V4 13724c8dbbbSDavid Howells init_rwsem(&clp->cl_sem); 13824c8dbbbSDavid Howells INIT_LIST_HEAD(&clp->cl_delegations); 13924c8dbbbSDavid Howells spin_lock_init(&clp->cl_lock); 14065f27f38SDavid Howells INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state); 14124c8dbbbSDavid Howells rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); 14224c8dbbbSDavid Howells clp->cl_boot_time = CURRENT_TIME; 14324c8dbbbSDavid Howells clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED; 14424c8dbbbSDavid Howells #endif 14524c8dbbbSDavid Howells 14624c8dbbbSDavid Howells return clp; 14724c8dbbbSDavid Howells 14824c8dbbbSDavid Howells error_3: 1499c5bf38dSTrond Myklebust if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) 15024c8dbbbSDavid Howells nfs_callback_down(); 15124c8dbbbSDavid Howells error_2: 15224c8dbbbSDavid Howells kfree(clp); 15324c8dbbbSDavid Howells error_0: 15424c8dbbbSDavid Howells return NULL; 15524c8dbbbSDavid Howells } 15624c8dbbbSDavid Howells 1575dd3177aSTrond Myklebust static void nfs4_shutdown_client(struct nfs_client *clp) 15824c8dbbbSDavid Howells { 15924c8dbbbSDavid Howells #ifdef CONFIG_NFS_V4 1605dd3177aSTrond Myklebust if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state)) 1615dd3177aSTrond Myklebust nfs4_kill_renewd(clp); 1629f958ab8STrond Myklebust BUG_ON(!RB_EMPTY_ROOT(&clp->cl_state_owners)); 1635dd3177aSTrond Myklebust if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state)) 16424c8dbbbSDavid Howells nfs_idmap_delete(clp); 16524c8dbbbSDavid Howells #endif 1665dd3177aSTrond Myklebust } 1675dd3177aSTrond Myklebust 1685dd3177aSTrond Myklebust /* 1695dd3177aSTrond Myklebust * Destroy a shared client record 1705dd3177aSTrond Myklebust */ 1715dd3177aSTrond Myklebust static void nfs_free_client(struct nfs_client *clp) 1725dd3177aSTrond Myklebust { 1735dd3177aSTrond Myklebust dprintk("--> nfs_free_client(%d)\n", clp->cl_nfsversion); 1745dd3177aSTrond Myklebust 1755dd3177aSTrond Myklebust nfs4_shutdown_client(clp); 17624c8dbbbSDavid Howells 17724c8dbbbSDavid Howells /* -EIO all pending I/O */ 17824c8dbbbSDavid Howells if (!IS_ERR(clp->cl_rpcclient)) 17924c8dbbbSDavid Howells rpc_shutdown_client(clp->cl_rpcclient); 18024c8dbbbSDavid Howells 18124c8dbbbSDavid Howells if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) 18224c8dbbbSDavid Howells nfs_callback_down(); 18324c8dbbbSDavid Howells 18424c8dbbbSDavid Howells kfree(clp->cl_hostname); 18524c8dbbbSDavid Howells kfree(clp); 18624c8dbbbSDavid Howells 18724c8dbbbSDavid Howells dprintk("<-- nfs_free_client()\n"); 18824c8dbbbSDavid Howells } 18924c8dbbbSDavid Howells 19024c8dbbbSDavid Howells /* 19124c8dbbbSDavid Howells * Release a reference to a shared client record 19224c8dbbbSDavid Howells */ 19324c8dbbbSDavid Howells void nfs_put_client(struct nfs_client *clp) 19424c8dbbbSDavid Howells { 19527ba8512SDavid Howells if (!clp) 19627ba8512SDavid Howells return; 19727ba8512SDavid Howells 19824c8dbbbSDavid Howells dprintk("--> nfs_put_client({%d})\n", atomic_read(&clp->cl_count)); 19924c8dbbbSDavid Howells 20024c8dbbbSDavid Howells if (atomic_dec_and_lock(&clp->cl_count, &nfs_client_lock)) { 20124c8dbbbSDavid Howells list_del(&clp->cl_share_link); 20224c8dbbbSDavid Howells spin_unlock(&nfs_client_lock); 20324c8dbbbSDavid Howells 20424c8dbbbSDavid Howells BUG_ON(!list_empty(&clp->cl_superblocks)); 20524c8dbbbSDavid Howells 20624c8dbbbSDavid Howells nfs_free_client(clp); 20724c8dbbbSDavid Howells } 20824c8dbbbSDavid Howells } 20924c8dbbbSDavid Howells 21024c8dbbbSDavid Howells /* 21124c8dbbbSDavid Howells * Find a client by address 21224c8dbbbSDavid Howells * - caller must hold nfs_client_lock 21324c8dbbbSDavid Howells */ 21413bbc06aSTrond Myklebust static struct nfs_client *__nfs_find_client(const struct sockaddr_in *addr, int nfsversion, int match_port) 21524c8dbbbSDavid Howells { 21624c8dbbbSDavid Howells struct nfs_client *clp; 21724c8dbbbSDavid Howells 21824c8dbbbSDavid Howells list_for_each_entry(clp, &nfs_client_list, cl_share_link) { 21913bbc06aSTrond Myklebust /* Don't match clients that failed to initialise properly */ 22013bbc06aSTrond Myklebust if (clp->cl_cons_state < 0) 22113bbc06aSTrond Myklebust continue; 22213bbc06aSTrond Myklebust 22324c8dbbbSDavid Howells /* Different NFS versions cannot share the same nfs_client */ 22424c8dbbbSDavid Howells if (clp->cl_nfsversion != nfsversion) 22524c8dbbbSDavid Howells continue; 22624c8dbbbSDavid Howells 227d45273edSChuck Lever if (clp->cl_addr.sin_addr.s_addr != addr->sin_addr.s_addr) 22824c8dbbbSDavid Howells continue; 22924c8dbbbSDavid Howells 23013bbc06aSTrond Myklebust if (!match_port || clp->cl_addr.sin_port == addr->sin_port) 23124c8dbbbSDavid Howells goto found; 23224c8dbbbSDavid Howells } 23324c8dbbbSDavid Howells 23424c8dbbbSDavid Howells return NULL; 23524c8dbbbSDavid Howells 23624c8dbbbSDavid Howells found: 23724c8dbbbSDavid Howells atomic_inc(&clp->cl_count); 23824c8dbbbSDavid Howells return clp; 23924c8dbbbSDavid Howells } 24024c8dbbbSDavid Howells 24124c8dbbbSDavid Howells /* 24224c8dbbbSDavid Howells * Find a client by IP address and protocol version 24324c8dbbbSDavid Howells * - returns NULL if no such client 24424c8dbbbSDavid Howells */ 24524c8dbbbSDavid Howells struct nfs_client *nfs_find_client(const struct sockaddr_in *addr, int nfsversion) 24624c8dbbbSDavid Howells { 24724c8dbbbSDavid Howells struct nfs_client *clp; 24824c8dbbbSDavid Howells 24924c8dbbbSDavid Howells spin_lock(&nfs_client_lock); 25013bbc06aSTrond Myklebust clp = __nfs_find_client(addr, nfsversion, 0); 25124c8dbbbSDavid Howells spin_unlock(&nfs_client_lock); 25213bbc06aSTrond Myklebust if (clp != NULL && clp->cl_cons_state != NFS_CS_READY) { 25313bbc06aSTrond Myklebust nfs_put_client(clp); 25413bbc06aSTrond Myklebust clp = NULL; 25513bbc06aSTrond Myklebust } 25624c8dbbbSDavid Howells return clp; 25724c8dbbbSDavid Howells } 25824c8dbbbSDavid Howells 25924c8dbbbSDavid Howells /* 26024c8dbbbSDavid Howells * Look up a client by IP address and protocol version 26124c8dbbbSDavid Howells * - creates a new record if one doesn't yet exist 26224c8dbbbSDavid Howells */ 263*3a498026STrond Myklebust static struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_init) 26424c8dbbbSDavid Howells { 26524c8dbbbSDavid Howells struct nfs_client *clp, *new = NULL; 26624c8dbbbSDavid Howells int error; 26724c8dbbbSDavid Howells 26824c8dbbbSDavid Howells dprintk("--> nfs_get_client(%s,"NIPQUAD_FMT":%d,%d)\n", 269*3a498026STrond Myklebust cl_init->hostname ?: "", NIPQUAD(cl_init->addr->sin_addr), 270*3a498026STrond Myklebust cl_init->addr->sin_port, cl_init->version); 27124c8dbbbSDavid Howells 27224c8dbbbSDavid Howells /* see if the client already exists */ 27324c8dbbbSDavid Howells do { 27424c8dbbbSDavid Howells spin_lock(&nfs_client_lock); 27524c8dbbbSDavid Howells 276*3a498026STrond Myklebust clp = __nfs_find_client(cl_init->addr, cl_init->version, 1); 27724c8dbbbSDavid Howells if (clp) 27824c8dbbbSDavid Howells goto found_client; 27924c8dbbbSDavid Howells if (new) 28024c8dbbbSDavid Howells goto install_client; 28124c8dbbbSDavid Howells 28224c8dbbbSDavid Howells spin_unlock(&nfs_client_lock); 28324c8dbbbSDavid Howells 284*3a498026STrond Myklebust new = nfs_alloc_client(cl_init); 28524c8dbbbSDavid Howells } while (new); 28624c8dbbbSDavid Howells 28724c8dbbbSDavid Howells return ERR_PTR(-ENOMEM); 28824c8dbbbSDavid Howells 28924c8dbbbSDavid Howells /* install a new client and return with it unready */ 29024c8dbbbSDavid Howells install_client: 29124c8dbbbSDavid Howells clp = new; 29224c8dbbbSDavid Howells list_add(&clp->cl_share_link, &nfs_client_list); 29324c8dbbbSDavid Howells spin_unlock(&nfs_client_lock); 29424c8dbbbSDavid Howells dprintk("--> nfs_get_client() = %p [new]\n", clp); 29524c8dbbbSDavid Howells return clp; 29624c8dbbbSDavid Howells 29724c8dbbbSDavid Howells /* found an existing client 29824c8dbbbSDavid Howells * - make sure it's ready before returning 29924c8dbbbSDavid Howells */ 30024c8dbbbSDavid Howells found_client: 30124c8dbbbSDavid Howells spin_unlock(&nfs_client_lock); 30224c8dbbbSDavid Howells 30324c8dbbbSDavid Howells if (new) 30424c8dbbbSDavid Howells nfs_free_client(new); 30524c8dbbbSDavid Howells 30683250493SLinus Torvalds error = wait_event_interruptible(nfs_client_active_wq, 3070bae89ecSTrond Myklebust clp->cl_cons_state != NFS_CS_INITING); 3080bae89ecSTrond Myklebust if (error < 0) { 30924c8dbbbSDavid Howells nfs_put_client(clp); 31024c8dbbbSDavid Howells return ERR_PTR(-ERESTARTSYS); 31124c8dbbbSDavid Howells } 31224c8dbbbSDavid Howells 31324c8dbbbSDavid Howells if (clp->cl_cons_state < NFS_CS_READY) { 31424c8dbbbSDavid Howells error = clp->cl_cons_state; 31524c8dbbbSDavid Howells nfs_put_client(clp); 31624c8dbbbSDavid Howells return ERR_PTR(error); 31724c8dbbbSDavid Howells } 31824c8dbbbSDavid Howells 31954ceac45SDavid Howells BUG_ON(clp->cl_cons_state != NFS_CS_READY); 32054ceac45SDavid Howells 32124c8dbbbSDavid Howells dprintk("--> nfs_get_client() = %p [share]\n", clp); 32224c8dbbbSDavid Howells return clp; 32324c8dbbbSDavid Howells } 32424c8dbbbSDavid Howells 32524c8dbbbSDavid Howells /* 32624c8dbbbSDavid Howells * Mark a server as ready or failed 32724c8dbbbSDavid Howells */ 32854ceac45SDavid Howells static void nfs_mark_client_ready(struct nfs_client *clp, int state) 32924c8dbbbSDavid Howells { 33024c8dbbbSDavid Howells clp->cl_cons_state = state; 33124c8dbbbSDavid Howells wake_up_all(&nfs_client_active_wq); 33224c8dbbbSDavid Howells } 3335006a76cSDavid Howells 3345006a76cSDavid Howells /* 3355006a76cSDavid Howells * Initialise the timeout values for a connection 3365006a76cSDavid Howells */ 3375006a76cSDavid Howells static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, 3385006a76cSDavid Howells unsigned int timeo, unsigned int retrans) 3395006a76cSDavid Howells { 3405006a76cSDavid Howells to->to_initval = timeo * HZ / 10; 3415006a76cSDavid Howells to->to_retries = retrans; 3425006a76cSDavid Howells if (!to->to_retries) 3435006a76cSDavid Howells to->to_retries = 2; 3445006a76cSDavid Howells 3455006a76cSDavid Howells switch (proto) { 3460896a725S\"Talpey, Thomas\ case XPRT_TRANSPORT_TCP: 3472cf7ff7aS\"Talpey, Thomas\ case XPRT_TRANSPORT_RDMA: 3485006a76cSDavid Howells if (!to->to_initval) 3495006a76cSDavid Howells to->to_initval = 60 * HZ; 3505006a76cSDavid Howells if (to->to_initval > NFS_MAX_TCP_TIMEOUT) 3515006a76cSDavid Howells to->to_initval = NFS_MAX_TCP_TIMEOUT; 3525006a76cSDavid Howells to->to_increment = to->to_initval; 3535006a76cSDavid Howells to->to_maxval = to->to_initval + (to->to_increment * to->to_retries); 3545006a76cSDavid Howells to->to_exponential = 0; 3555006a76cSDavid Howells break; 3560896a725S\"Talpey, Thomas\ case XPRT_TRANSPORT_UDP: 3575006a76cSDavid Howells default: 3585006a76cSDavid Howells if (!to->to_initval) 3595006a76cSDavid Howells to->to_initval = 11 * HZ / 10; 3605006a76cSDavid Howells if (to->to_initval > NFS_MAX_UDP_TIMEOUT) 3615006a76cSDavid Howells to->to_initval = NFS_MAX_UDP_TIMEOUT; 3625006a76cSDavid Howells to->to_maxval = NFS_MAX_UDP_TIMEOUT; 3635006a76cSDavid Howells to->to_exponential = 1; 3645006a76cSDavid Howells break; 3655006a76cSDavid Howells } 3665006a76cSDavid Howells } 3675006a76cSDavid Howells 3685006a76cSDavid Howells /* 3695006a76cSDavid Howells * Create an RPC client handle 3705006a76cSDavid Howells */ 37154ceac45SDavid Howells static int nfs_create_rpc_client(struct nfs_client *clp, int proto, 3725006a76cSDavid Howells unsigned int timeo, 3735006a76cSDavid Howells unsigned int retrans, 37443d78ef2SChuck Lever rpc_authflavor_t flavor, 37543d78ef2SChuck Lever int flags) 3765006a76cSDavid Howells { 3775006a76cSDavid Howells struct rpc_timeout timeparms; 3785006a76cSDavid Howells struct rpc_clnt *clnt = NULL; 37941877d20SChuck Lever struct rpc_create_args args = { 38041877d20SChuck Lever .protocol = proto, 38141877d20SChuck Lever .address = (struct sockaddr *)&clp->cl_addr, 38241877d20SChuck Lever .addrsize = sizeof(clp->cl_addr), 38341877d20SChuck Lever .timeout = &timeparms, 38441877d20SChuck Lever .servername = clp->cl_hostname, 38541877d20SChuck Lever .program = &nfs_program, 38641877d20SChuck Lever .version = clp->rpc_ops->version, 38741877d20SChuck Lever .authflavor = flavor, 38843d78ef2SChuck Lever .flags = flags, 38941877d20SChuck Lever }; 3905006a76cSDavid Howells 3915006a76cSDavid Howells if (!IS_ERR(clp->cl_rpcclient)) 3925006a76cSDavid Howells return 0; 3935006a76cSDavid Howells 3945006a76cSDavid Howells nfs_init_timeout_values(&timeparms, proto, timeo, retrans); 3955006a76cSDavid Howells clp->retrans_timeo = timeparms.to_initval; 3965006a76cSDavid Howells clp->retrans_count = timeparms.to_retries; 3975006a76cSDavid Howells 39841877d20SChuck Lever clnt = rpc_create(&args); 3995006a76cSDavid Howells if (IS_ERR(clnt)) { 4005006a76cSDavid Howells dprintk("%s: cannot create RPC client. Error = %ld\n", 4015006a76cSDavid Howells __FUNCTION__, PTR_ERR(clnt)); 4025006a76cSDavid Howells return PTR_ERR(clnt); 4035006a76cSDavid Howells } 4045006a76cSDavid Howells 4055006a76cSDavid Howells clp->cl_rpcclient = clnt; 4065006a76cSDavid Howells return 0; 4075006a76cSDavid Howells } 40854ceac45SDavid Howells 40954ceac45SDavid Howells /* 41054ceac45SDavid Howells * Version 2 or 3 client destruction 41154ceac45SDavid Howells */ 41254ceac45SDavid Howells static void nfs_destroy_server(struct nfs_server *server) 41354ceac45SDavid Howells { 41454ceac45SDavid Howells if (!(server->flags & NFS_MOUNT_NONLM)) 41554ceac45SDavid Howells lockd_down(); /* release rpc.lockd */ 41654ceac45SDavid Howells } 41754ceac45SDavid Howells 41854ceac45SDavid Howells /* 41954ceac45SDavid Howells * Version 2 or 3 lockd setup 42054ceac45SDavid Howells */ 42154ceac45SDavid Howells static int nfs_start_lockd(struct nfs_server *server) 42254ceac45SDavid Howells { 42354ceac45SDavid Howells int error = 0; 42454ceac45SDavid Howells 42554ceac45SDavid Howells if (server->nfs_client->cl_nfsversion > 3) 42654ceac45SDavid Howells goto out; 42754ceac45SDavid Howells if (server->flags & NFS_MOUNT_NONLM) 42854ceac45SDavid Howells goto out; 42924e36663SNeilBrown error = lockd_up((server->flags & NFS_MOUNT_TCP) ? 43024e36663SNeilBrown IPPROTO_TCP : IPPROTO_UDP); 43154ceac45SDavid Howells if (error < 0) 43254ceac45SDavid Howells server->flags |= NFS_MOUNT_NONLM; 43354ceac45SDavid Howells else 43454ceac45SDavid Howells server->destroy = nfs_destroy_server; 43554ceac45SDavid Howells out: 43654ceac45SDavid Howells return error; 43754ceac45SDavid Howells } 43854ceac45SDavid Howells 43954ceac45SDavid Howells /* 44054ceac45SDavid Howells * Initialise an NFSv3 ACL client connection 44154ceac45SDavid Howells */ 44254ceac45SDavid Howells #ifdef CONFIG_NFS_V3_ACL 44354ceac45SDavid Howells static void nfs_init_server_aclclient(struct nfs_server *server) 44454ceac45SDavid Howells { 44554ceac45SDavid Howells if (server->nfs_client->cl_nfsversion != 3) 44654ceac45SDavid Howells goto out_noacl; 44754ceac45SDavid Howells if (server->flags & NFS_MOUNT_NOACL) 44854ceac45SDavid Howells goto out_noacl; 44954ceac45SDavid Howells 45054ceac45SDavid Howells server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3); 45154ceac45SDavid Howells if (IS_ERR(server->client_acl)) 45254ceac45SDavid Howells goto out_noacl; 45354ceac45SDavid Howells 45454ceac45SDavid Howells /* No errors! Assume that Sun nfsacls are supported */ 45554ceac45SDavid Howells server->caps |= NFS_CAP_ACLS; 45654ceac45SDavid Howells return; 45754ceac45SDavid Howells 45854ceac45SDavid Howells out_noacl: 45954ceac45SDavid Howells server->caps &= ~NFS_CAP_ACLS; 46054ceac45SDavid Howells } 46154ceac45SDavid Howells #else 46254ceac45SDavid Howells static inline void nfs_init_server_aclclient(struct nfs_server *server) 46354ceac45SDavid Howells { 46454ceac45SDavid Howells server->flags &= ~NFS_MOUNT_NOACL; 46554ceac45SDavid Howells server->caps &= ~NFS_CAP_ACLS; 46654ceac45SDavid Howells } 46754ceac45SDavid Howells #endif 46854ceac45SDavid Howells 46954ceac45SDavid Howells /* 47054ceac45SDavid Howells * Create a general RPC client 47154ceac45SDavid Howells */ 47254ceac45SDavid Howells static int nfs_init_server_rpcclient(struct nfs_server *server, rpc_authflavor_t pseudoflavour) 47354ceac45SDavid Howells { 47454ceac45SDavid Howells struct nfs_client *clp = server->nfs_client; 47554ceac45SDavid Howells 47654ceac45SDavid Howells server->client = rpc_clone_client(clp->cl_rpcclient); 47754ceac45SDavid Howells if (IS_ERR(server->client)) { 47854ceac45SDavid Howells dprintk("%s: couldn't create rpc_client!\n", __FUNCTION__); 47954ceac45SDavid Howells return PTR_ERR(server->client); 48054ceac45SDavid Howells } 48154ceac45SDavid Howells 48254ceac45SDavid Howells if (pseudoflavour != clp->cl_rpcclient->cl_auth->au_flavor) { 48354ceac45SDavid Howells struct rpc_auth *auth; 48454ceac45SDavid Howells 48554ceac45SDavid Howells auth = rpcauth_create(pseudoflavour, server->client); 48654ceac45SDavid Howells if (IS_ERR(auth)) { 48754ceac45SDavid Howells dprintk("%s: couldn't create credcache!\n", __FUNCTION__); 48854ceac45SDavid Howells return PTR_ERR(auth); 48954ceac45SDavid Howells } 49054ceac45SDavid Howells } 49154ceac45SDavid Howells server->client->cl_softrtry = 0; 49254ceac45SDavid Howells if (server->flags & NFS_MOUNT_SOFT) 49354ceac45SDavid Howells server->client->cl_softrtry = 1; 49454ceac45SDavid Howells 49554ceac45SDavid Howells server->client->cl_intr = 0; 49654ceac45SDavid Howells if (server->flags & NFS4_MOUNT_INTR) 49754ceac45SDavid Howells server->client->cl_intr = 1; 49854ceac45SDavid Howells 49954ceac45SDavid Howells return 0; 50054ceac45SDavid Howells } 50154ceac45SDavid Howells 50254ceac45SDavid Howells /* 50354ceac45SDavid Howells * Initialise an NFS2 or NFS3 client 50454ceac45SDavid Howells */ 5052283f8d6S\"Talpey, Thomas\ static int nfs_init_client(struct nfs_client *clp, 5062283f8d6S\"Talpey, Thomas\ const struct nfs_parsed_mount_data *data) 50754ceac45SDavid Howells { 50854ceac45SDavid Howells int error; 50954ceac45SDavid Howells 51054ceac45SDavid Howells if (clp->cl_cons_state == NFS_CS_READY) { 51154ceac45SDavid Howells /* the client is already initialised */ 51254ceac45SDavid Howells dprintk("<-- nfs_init_client() = 0 [already %p]\n", clp); 51354ceac45SDavid Howells return 0; 51454ceac45SDavid Howells } 51554ceac45SDavid Howells 51654ceac45SDavid Howells /* Check NFS protocol revision and initialize RPC op vector */ 51754ceac45SDavid Howells clp->rpc_ops = &nfs_v2_clientops; 51854ceac45SDavid Howells #ifdef CONFIG_NFS_V3 51954ceac45SDavid Howells if (clp->cl_nfsversion == 3) 52054ceac45SDavid Howells clp->rpc_ops = &nfs_v3_clientops; 52154ceac45SDavid Howells #endif 52254ceac45SDavid Howells /* 52354ceac45SDavid Howells * Create a client RPC handle for doing FSSTAT with UNIX auth only 52454ceac45SDavid Howells * - RFC 2623, sec 2.3.2 52554ceac45SDavid Howells */ 5262283f8d6S\"Talpey, Thomas\ error = nfs_create_rpc_client(clp, data->nfs_server.protocol, 5272283f8d6S\"Talpey, Thomas\ data->timeo, data->retrans, RPC_AUTH_UNIX, 0); 52854ceac45SDavid Howells if (error < 0) 52954ceac45SDavid Howells goto error; 53054ceac45SDavid Howells nfs_mark_client_ready(clp, NFS_CS_READY); 53154ceac45SDavid Howells return 0; 53254ceac45SDavid Howells 53354ceac45SDavid Howells error: 53454ceac45SDavid Howells nfs_mark_client_ready(clp, error); 53554ceac45SDavid Howells dprintk("<-- nfs_init_client() = xerror %d\n", error); 53654ceac45SDavid Howells return error; 53754ceac45SDavid Howells } 53854ceac45SDavid Howells 53954ceac45SDavid Howells /* 54054ceac45SDavid Howells * Create a version 2 or 3 client 54154ceac45SDavid Howells */ 5422283f8d6S\"Talpey, Thomas\ static int nfs_init_server(struct nfs_server *server, 5432283f8d6S\"Talpey, Thomas\ const struct nfs_parsed_mount_data *data) 54454ceac45SDavid Howells { 545*3a498026STrond Myklebust struct nfs_client_initdata cl_init = { 546*3a498026STrond Myklebust .hostname = data->nfs_server.hostname, 547*3a498026STrond Myklebust .addr = &data->nfs_server.address, 548*3a498026STrond Myklebust .version = 2, 549*3a498026STrond Myklebust }; 55054ceac45SDavid Howells struct nfs_client *clp; 551*3a498026STrond Myklebust int error; 55254ceac45SDavid Howells 55354ceac45SDavid Howells dprintk("--> nfs_init_server()\n"); 55454ceac45SDavid Howells 55554ceac45SDavid Howells #ifdef CONFIG_NFS_V3 55654ceac45SDavid Howells if (data->flags & NFS_MOUNT_VER3) 557*3a498026STrond Myklebust cl_init.version = 3; 55854ceac45SDavid Howells #endif 55954ceac45SDavid Howells 56054ceac45SDavid Howells /* Allocate or find a client reference we can use */ 561*3a498026STrond Myklebust clp = nfs_get_client(&cl_init); 56254ceac45SDavid Howells if (IS_ERR(clp)) { 56354ceac45SDavid Howells dprintk("<-- nfs_init_server() = error %ld\n", PTR_ERR(clp)); 56454ceac45SDavid Howells return PTR_ERR(clp); 56554ceac45SDavid Howells } 56654ceac45SDavid Howells 56754ceac45SDavid Howells error = nfs_init_client(clp, data); 56854ceac45SDavid Howells if (error < 0) 56954ceac45SDavid Howells goto error; 57054ceac45SDavid Howells 57154ceac45SDavid Howells server->nfs_client = clp; 57254ceac45SDavid Howells 57354ceac45SDavid Howells /* Initialise the client representation from the mount data */ 57454ceac45SDavid Howells server->flags = data->flags & NFS_MOUNT_FLAGMASK; 57554ceac45SDavid Howells 57654ceac45SDavid Howells if (data->rsize) 57754ceac45SDavid Howells server->rsize = nfs_block_size(data->rsize, NULL); 57854ceac45SDavid Howells if (data->wsize) 57954ceac45SDavid Howells server->wsize = nfs_block_size(data->wsize, NULL); 58054ceac45SDavid Howells 58154ceac45SDavid Howells server->acregmin = data->acregmin * HZ; 58254ceac45SDavid Howells server->acregmax = data->acregmax * HZ; 58354ceac45SDavid Howells server->acdirmin = data->acdirmin * HZ; 58454ceac45SDavid Howells server->acdirmax = data->acdirmax * HZ; 58554ceac45SDavid Howells 58654ceac45SDavid Howells /* Start lockd here, before we might error out */ 58754ceac45SDavid Howells error = nfs_start_lockd(server); 58854ceac45SDavid Howells if (error < 0) 58954ceac45SDavid Howells goto error; 59054ceac45SDavid Howells 5912283f8d6S\"Talpey, Thomas\ error = nfs_init_server_rpcclient(server, data->auth_flavors[0]); 59254ceac45SDavid Howells if (error < 0) 59354ceac45SDavid Howells goto error; 59454ceac45SDavid Howells 59554ceac45SDavid Howells server->namelen = data->namlen; 59654ceac45SDavid Howells /* Create a client RPC handle for the NFSv3 ACL management interface */ 59754ceac45SDavid Howells nfs_init_server_aclclient(server); 59854ceac45SDavid Howells dprintk("<-- nfs_init_server() = 0 [new %p]\n", clp); 59954ceac45SDavid Howells return 0; 60054ceac45SDavid Howells 60154ceac45SDavid Howells error: 60254ceac45SDavid Howells server->nfs_client = NULL; 60354ceac45SDavid Howells nfs_put_client(clp); 60454ceac45SDavid Howells dprintk("<-- nfs_init_server() = xerror %d\n", error); 60554ceac45SDavid Howells return error; 60654ceac45SDavid Howells } 60754ceac45SDavid Howells 60854ceac45SDavid Howells /* 60954ceac45SDavid Howells * Load up the server record from information gained in an fsinfo record 61054ceac45SDavid Howells */ 61154ceac45SDavid Howells static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo *fsinfo) 61254ceac45SDavid Howells { 61354ceac45SDavid Howells unsigned long max_rpc_payload; 61454ceac45SDavid Howells 61554ceac45SDavid Howells /* Work out a lot of parameters */ 61654ceac45SDavid Howells if (server->rsize == 0) 61754ceac45SDavid Howells server->rsize = nfs_block_size(fsinfo->rtpref, NULL); 61854ceac45SDavid Howells if (server->wsize == 0) 61954ceac45SDavid Howells server->wsize = nfs_block_size(fsinfo->wtpref, NULL); 62054ceac45SDavid Howells 62154ceac45SDavid Howells if (fsinfo->rtmax >= 512 && server->rsize > fsinfo->rtmax) 62254ceac45SDavid Howells server->rsize = nfs_block_size(fsinfo->rtmax, NULL); 62354ceac45SDavid Howells if (fsinfo->wtmax >= 512 && server->wsize > fsinfo->wtmax) 62454ceac45SDavid Howells server->wsize = nfs_block_size(fsinfo->wtmax, NULL); 62554ceac45SDavid Howells 62654ceac45SDavid Howells max_rpc_payload = nfs_block_size(rpc_max_payload(server->client), NULL); 62754ceac45SDavid Howells if (server->rsize > max_rpc_payload) 62854ceac45SDavid Howells server->rsize = max_rpc_payload; 62954ceac45SDavid Howells if (server->rsize > NFS_MAX_FILE_IO_SIZE) 63054ceac45SDavid Howells server->rsize = NFS_MAX_FILE_IO_SIZE; 63154ceac45SDavid Howells server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 632e0bf68ddSPeter Zijlstra 63354ceac45SDavid Howells server->backing_dev_info.ra_pages = server->rpages * NFS_MAX_READAHEAD; 63454ceac45SDavid Howells 63554ceac45SDavid Howells if (server->wsize > max_rpc_payload) 63654ceac45SDavid Howells server->wsize = max_rpc_payload; 63754ceac45SDavid Howells if (server->wsize > NFS_MAX_FILE_IO_SIZE) 63854ceac45SDavid Howells server->wsize = NFS_MAX_FILE_IO_SIZE; 63954ceac45SDavid Howells server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 64054ceac45SDavid Howells server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL); 64154ceac45SDavid Howells 64254ceac45SDavid Howells server->dtsize = nfs_block_size(fsinfo->dtpref, NULL); 64354ceac45SDavid Howells if (server->dtsize > PAGE_CACHE_SIZE) 64454ceac45SDavid Howells server->dtsize = PAGE_CACHE_SIZE; 64554ceac45SDavid Howells if (server->dtsize > server->rsize) 64654ceac45SDavid Howells server->dtsize = server->rsize; 64754ceac45SDavid Howells 64854ceac45SDavid Howells if (server->flags & NFS_MOUNT_NOAC) { 64954ceac45SDavid Howells server->acregmin = server->acregmax = 0; 65054ceac45SDavid Howells server->acdirmin = server->acdirmax = 0; 65154ceac45SDavid Howells } 65254ceac45SDavid Howells 65354ceac45SDavid Howells server->maxfilesize = fsinfo->maxfilesize; 65454ceac45SDavid Howells 65554ceac45SDavid Howells /* We're airborne Set socket buffersize */ 65654ceac45SDavid Howells rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100); 65754ceac45SDavid Howells } 65854ceac45SDavid Howells 65954ceac45SDavid Howells /* 66054ceac45SDavid Howells * Probe filesystem information, including the FSID on v2/v3 66154ceac45SDavid Howells */ 66254ceac45SDavid Howells static int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr) 66354ceac45SDavid Howells { 66454ceac45SDavid Howells struct nfs_fsinfo fsinfo; 66554ceac45SDavid Howells struct nfs_client *clp = server->nfs_client; 66654ceac45SDavid Howells int error; 66754ceac45SDavid Howells 66854ceac45SDavid Howells dprintk("--> nfs_probe_fsinfo()\n"); 66954ceac45SDavid Howells 67054ceac45SDavid Howells if (clp->rpc_ops->set_capabilities != NULL) { 67154ceac45SDavid Howells error = clp->rpc_ops->set_capabilities(server, mntfh); 67254ceac45SDavid Howells if (error < 0) 67354ceac45SDavid Howells goto out_error; 67454ceac45SDavid Howells } 67554ceac45SDavid Howells 67654ceac45SDavid Howells fsinfo.fattr = fattr; 67754ceac45SDavid Howells nfs_fattr_init(fattr); 67854ceac45SDavid Howells error = clp->rpc_ops->fsinfo(server, mntfh, &fsinfo); 67954ceac45SDavid Howells if (error < 0) 68054ceac45SDavid Howells goto out_error; 68154ceac45SDavid Howells 68254ceac45SDavid Howells nfs_server_set_fsinfo(server, &fsinfo); 683e0bf68ddSPeter Zijlstra error = bdi_init(&server->backing_dev_info); 684e0bf68ddSPeter Zijlstra if (error) 685e0bf68ddSPeter Zijlstra goto out_error; 686e0bf68ddSPeter Zijlstra 68754ceac45SDavid Howells 68854ceac45SDavid Howells /* Get some general file system info */ 68954ceac45SDavid Howells if (server->namelen == 0) { 69054ceac45SDavid Howells struct nfs_pathconf pathinfo; 69154ceac45SDavid Howells 69254ceac45SDavid Howells pathinfo.fattr = fattr; 69354ceac45SDavid Howells nfs_fattr_init(fattr); 69454ceac45SDavid Howells 69554ceac45SDavid Howells if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0) 69654ceac45SDavid Howells server->namelen = pathinfo.max_namelen; 69754ceac45SDavid Howells } 69854ceac45SDavid Howells 69954ceac45SDavid Howells dprintk("<-- nfs_probe_fsinfo() = 0\n"); 70054ceac45SDavid Howells return 0; 70154ceac45SDavid Howells 70254ceac45SDavid Howells out_error: 70354ceac45SDavid Howells dprintk("nfs_probe_fsinfo: error = %d\n", -error); 70454ceac45SDavid Howells return error; 70554ceac45SDavid Howells } 70654ceac45SDavid Howells 70754ceac45SDavid Howells /* 70854ceac45SDavid Howells * Copy useful information when duplicating a server record 70954ceac45SDavid Howells */ 71054ceac45SDavid Howells static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source) 71154ceac45SDavid Howells { 71254ceac45SDavid Howells target->flags = source->flags; 71354ceac45SDavid Howells target->acregmin = source->acregmin; 71454ceac45SDavid Howells target->acregmax = source->acregmax; 71554ceac45SDavid Howells target->acdirmin = source->acdirmin; 71654ceac45SDavid Howells target->acdirmax = source->acdirmax; 71754ceac45SDavid Howells target->caps = source->caps; 71854ceac45SDavid Howells } 71954ceac45SDavid Howells 72054ceac45SDavid Howells /* 72154ceac45SDavid Howells * Allocate and initialise a server record 72254ceac45SDavid Howells */ 72354ceac45SDavid Howells static struct nfs_server *nfs_alloc_server(void) 72454ceac45SDavid Howells { 72554ceac45SDavid Howells struct nfs_server *server; 72654ceac45SDavid Howells 72754ceac45SDavid Howells server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL); 72854ceac45SDavid Howells if (!server) 72954ceac45SDavid Howells return NULL; 73054ceac45SDavid Howells 73154ceac45SDavid Howells server->client = server->client_acl = ERR_PTR(-EINVAL); 73254ceac45SDavid Howells 73354ceac45SDavid Howells /* Zero out the NFS state stuff */ 73454ceac45SDavid Howells INIT_LIST_HEAD(&server->client_link); 73554ceac45SDavid Howells INIT_LIST_HEAD(&server->master_link); 73654ceac45SDavid Howells 737ef818a28SSteve Dickson init_waitqueue_head(&server->active_wq); 738ef818a28SSteve Dickson atomic_set(&server->active, 0); 739ef818a28SSteve Dickson 74054ceac45SDavid Howells server->io_stats = nfs_alloc_iostats(); 74154ceac45SDavid Howells if (!server->io_stats) { 74254ceac45SDavid Howells kfree(server); 74354ceac45SDavid Howells return NULL; 74454ceac45SDavid Howells } 74554ceac45SDavid Howells 74654ceac45SDavid Howells return server; 74754ceac45SDavid Howells } 74854ceac45SDavid Howells 74954ceac45SDavid Howells /* 75054ceac45SDavid Howells * Free up a server record 75154ceac45SDavid Howells */ 75254ceac45SDavid Howells void nfs_free_server(struct nfs_server *server) 75354ceac45SDavid Howells { 75454ceac45SDavid Howells dprintk("--> nfs_free_server()\n"); 75554ceac45SDavid Howells 75654ceac45SDavid Howells spin_lock(&nfs_client_lock); 75754ceac45SDavid Howells list_del(&server->client_link); 75854ceac45SDavid Howells list_del(&server->master_link); 75954ceac45SDavid Howells spin_unlock(&nfs_client_lock); 76054ceac45SDavid Howells 76154ceac45SDavid Howells if (server->destroy != NULL) 76254ceac45SDavid Howells server->destroy(server); 7635cef338bSTrond Myklebust 7645cef338bSTrond Myklebust if (!IS_ERR(server->client_acl)) 7655cef338bSTrond Myklebust rpc_shutdown_client(server->client_acl); 76654ceac45SDavid Howells if (!IS_ERR(server->client)) 76754ceac45SDavid Howells rpc_shutdown_client(server->client); 76854ceac45SDavid Howells 76954ceac45SDavid Howells nfs_put_client(server->nfs_client); 77054ceac45SDavid Howells 77154ceac45SDavid Howells nfs_free_iostats(server->io_stats); 772e0bf68ddSPeter Zijlstra bdi_destroy(&server->backing_dev_info); 77354ceac45SDavid Howells kfree(server); 77454ceac45SDavid Howells nfs_release_automount_timer(); 77554ceac45SDavid Howells dprintk("<-- nfs_free_server()\n"); 77654ceac45SDavid Howells } 77754ceac45SDavid Howells 77854ceac45SDavid Howells /* 77954ceac45SDavid Howells * Create a version 2 or 3 volume record 78054ceac45SDavid Howells * - keyed on server and FSID 78154ceac45SDavid Howells */ 7822283f8d6S\"Talpey, Thomas\ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, 78354ceac45SDavid Howells struct nfs_fh *mntfh) 78454ceac45SDavid Howells { 78554ceac45SDavid Howells struct nfs_server *server; 78654ceac45SDavid Howells struct nfs_fattr fattr; 78754ceac45SDavid Howells int error; 78854ceac45SDavid Howells 78954ceac45SDavid Howells server = nfs_alloc_server(); 79054ceac45SDavid Howells if (!server) 79154ceac45SDavid Howells return ERR_PTR(-ENOMEM); 79254ceac45SDavid Howells 79354ceac45SDavid Howells /* Get a client representation */ 79454ceac45SDavid Howells error = nfs_init_server(server, data); 79554ceac45SDavid Howells if (error < 0) 79654ceac45SDavid Howells goto error; 79754ceac45SDavid Howells 79854ceac45SDavid Howells BUG_ON(!server->nfs_client); 79954ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops); 80054ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 80154ceac45SDavid Howells 80254ceac45SDavid Howells /* Probe the root fh to retrieve its FSID */ 80354ceac45SDavid Howells error = nfs_probe_fsinfo(server, mntfh, &fattr); 80454ceac45SDavid Howells if (error < 0) 80554ceac45SDavid Howells goto error; 80654af3bb5STrond Myklebust if (server->nfs_client->rpc_ops->version == 3) { 80754af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) 80854af3bb5STrond Myklebust server->namelen = NFS3_MAXNAMLEN; 80954af3bb5STrond Myklebust if (!(data->flags & NFS_MOUNT_NORDIRPLUS)) 81054af3bb5STrond Myklebust server->caps |= NFS_CAP_READDIRPLUS; 81154af3bb5STrond Myklebust } else { 81254af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN) 81354af3bb5STrond Myklebust server->namelen = NFS2_MAXNAMLEN; 81454af3bb5STrond Myklebust } 81554af3bb5STrond Myklebust 81654ceac45SDavid Howells if (!(fattr.valid & NFS_ATTR_FATTR)) { 81754ceac45SDavid Howells error = server->nfs_client->rpc_ops->getattr(server, mntfh, &fattr); 81854ceac45SDavid Howells if (error < 0) { 81954ceac45SDavid Howells dprintk("nfs_create_server: getattr error = %d\n", -error); 82054ceac45SDavid Howells goto error; 82154ceac45SDavid Howells } 82254ceac45SDavid Howells } 82354ceac45SDavid Howells memcpy(&server->fsid, &fattr.fsid, sizeof(server->fsid)); 82454ceac45SDavid Howells 8256daabf1bSDavid Howells dprintk("Server FSID: %llx:%llx\n", 8266daabf1bSDavid Howells (unsigned long long) server->fsid.major, 8276daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 82854ceac45SDavid Howells 82954ceac45SDavid Howells BUG_ON(!server->nfs_client); 83054ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops); 83154ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 83254ceac45SDavid Howells 83354ceac45SDavid Howells spin_lock(&nfs_client_lock); 83454ceac45SDavid Howells list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); 83554ceac45SDavid Howells list_add_tail(&server->master_link, &nfs_volume_list); 83654ceac45SDavid Howells spin_unlock(&nfs_client_lock); 83754ceac45SDavid Howells 83854ceac45SDavid Howells server->mount_time = jiffies; 83954ceac45SDavid Howells return server; 84054ceac45SDavid Howells 84154ceac45SDavid Howells error: 84254ceac45SDavid Howells nfs_free_server(server); 84354ceac45SDavid Howells return ERR_PTR(error); 84454ceac45SDavid Howells } 84554ceac45SDavid Howells 84654ceac45SDavid Howells #ifdef CONFIG_NFS_V4 84754ceac45SDavid Howells /* 84854ceac45SDavid Howells * Initialise an NFS4 client record 84954ceac45SDavid Howells */ 85054ceac45SDavid Howells static int nfs4_init_client(struct nfs_client *clp, 85154ceac45SDavid Howells int proto, int timeo, int retrans, 8527d9ac06fSJ. Bruce Fields const char *ip_addr, 85354ceac45SDavid Howells rpc_authflavor_t authflavour) 85454ceac45SDavid Howells { 85554ceac45SDavid Howells int error; 85654ceac45SDavid Howells 85754ceac45SDavid Howells if (clp->cl_cons_state == NFS_CS_READY) { 85854ceac45SDavid Howells /* the client is initialised already */ 85954ceac45SDavid Howells dprintk("<-- nfs4_init_client() = 0 [already %p]\n", clp); 86054ceac45SDavid Howells return 0; 86154ceac45SDavid Howells } 86254ceac45SDavid Howells 86354ceac45SDavid Howells /* Check NFS protocol revision and initialize RPC op vector */ 86454ceac45SDavid Howells clp->rpc_ops = &nfs_v4_clientops; 86554ceac45SDavid Howells 86643d78ef2SChuck Lever error = nfs_create_rpc_client(clp, proto, timeo, retrans, authflavour, 86743d78ef2SChuck Lever RPC_CLNT_CREATE_DISCRTRY); 86854ceac45SDavid Howells if (error < 0) 86954ceac45SDavid Howells goto error; 8707d9ac06fSJ. Bruce Fields memcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr)); 87154ceac45SDavid Howells 87254ceac45SDavid Howells error = nfs_idmap_new(clp); 87354ceac45SDavid Howells if (error < 0) { 87454ceac45SDavid Howells dprintk("%s: failed to create idmapper. Error = %d\n", 87554ceac45SDavid Howells __FUNCTION__, error); 87654ceac45SDavid Howells goto error; 87754ceac45SDavid Howells } 8789c5bf38dSTrond Myklebust __set_bit(NFS_CS_IDMAP, &clp->cl_res_state); 87954ceac45SDavid Howells 88054ceac45SDavid Howells nfs_mark_client_ready(clp, NFS_CS_READY); 88154ceac45SDavid Howells return 0; 88254ceac45SDavid Howells 88354ceac45SDavid Howells error: 88454ceac45SDavid Howells nfs_mark_client_ready(clp, error); 88554ceac45SDavid Howells dprintk("<-- nfs4_init_client() = xerror %d\n", error); 88654ceac45SDavid Howells return error; 88754ceac45SDavid Howells } 88854ceac45SDavid Howells 88954ceac45SDavid Howells /* 89054ceac45SDavid Howells * Set up an NFS4 client 89154ceac45SDavid Howells */ 89254ceac45SDavid Howells static int nfs4_set_client(struct nfs_server *server, 89354ceac45SDavid Howells const char *hostname, const struct sockaddr_in *addr, 8947d9ac06fSJ. Bruce Fields const char *ip_addr, 89554ceac45SDavid Howells rpc_authflavor_t authflavour, 89654ceac45SDavid Howells int proto, int timeo, int retrans) 89754ceac45SDavid Howells { 898*3a498026STrond Myklebust struct nfs_client_initdata cl_init = { 899*3a498026STrond Myklebust .hostname = hostname, 900*3a498026STrond Myklebust .addr = addr, 901*3a498026STrond Myklebust .version = 4, 902*3a498026STrond Myklebust }; 90354ceac45SDavid Howells struct nfs_client *clp; 90454ceac45SDavid Howells int error; 90554ceac45SDavid Howells 90654ceac45SDavid Howells dprintk("--> nfs4_set_client()\n"); 90754ceac45SDavid Howells 90854ceac45SDavid Howells /* Allocate or find a client reference we can use */ 909*3a498026STrond Myklebust clp = nfs_get_client(&cl_init); 91054ceac45SDavid Howells if (IS_ERR(clp)) { 91154ceac45SDavid Howells error = PTR_ERR(clp); 91254ceac45SDavid Howells goto error; 91354ceac45SDavid Howells } 9147d9ac06fSJ. Bruce Fields error = nfs4_init_client(clp, proto, timeo, retrans, ip_addr, authflavour); 91554ceac45SDavid Howells if (error < 0) 91654ceac45SDavid Howells goto error_put; 91754ceac45SDavid Howells 91854ceac45SDavid Howells server->nfs_client = clp; 91954ceac45SDavid Howells dprintk("<-- nfs4_set_client() = 0 [new %p]\n", clp); 92054ceac45SDavid Howells return 0; 92154ceac45SDavid Howells 92254ceac45SDavid Howells error_put: 92354ceac45SDavid Howells nfs_put_client(clp); 92454ceac45SDavid Howells error: 92554ceac45SDavid Howells dprintk("<-- nfs4_set_client() = xerror %d\n", error); 92654ceac45SDavid Howells return error; 92754ceac45SDavid Howells } 92854ceac45SDavid Howells 92954ceac45SDavid Howells /* 93054ceac45SDavid Howells * Create a version 4 volume record 93154ceac45SDavid Howells */ 93254ceac45SDavid Howells static int nfs4_init_server(struct nfs_server *server, 93391ea40b9S\"Talpey, Thomas\ const struct nfs_parsed_mount_data *data) 93454ceac45SDavid Howells { 93554ceac45SDavid Howells int error; 93654ceac45SDavid Howells 93754ceac45SDavid Howells dprintk("--> nfs4_init_server()\n"); 93854ceac45SDavid Howells 93954ceac45SDavid Howells /* Initialise the client representation from the mount data */ 94054ceac45SDavid Howells server->flags = data->flags & NFS_MOUNT_FLAGMASK; 94154ceac45SDavid Howells server->caps |= NFS_CAP_ATOMIC_OPEN; 94254ceac45SDavid Howells 94354ceac45SDavid Howells if (data->rsize) 94454ceac45SDavid Howells server->rsize = nfs_block_size(data->rsize, NULL); 94554ceac45SDavid Howells if (data->wsize) 94654ceac45SDavid Howells server->wsize = nfs_block_size(data->wsize, NULL); 94754ceac45SDavid Howells 94854ceac45SDavid Howells server->acregmin = data->acregmin * HZ; 94954ceac45SDavid Howells server->acregmax = data->acregmax * HZ; 95054ceac45SDavid Howells server->acdirmin = data->acdirmin * HZ; 95154ceac45SDavid Howells server->acdirmax = data->acdirmax * HZ; 95254ceac45SDavid Howells 95391ea40b9S\"Talpey, Thomas\ error = nfs_init_server_rpcclient(server, data->auth_flavors[0]); 95454ceac45SDavid Howells 95554ceac45SDavid Howells /* Done */ 95654ceac45SDavid Howells dprintk("<-- nfs4_init_server() = %d\n", error); 95754ceac45SDavid Howells return error; 95854ceac45SDavid Howells } 95954ceac45SDavid Howells 96054ceac45SDavid Howells /* 96154ceac45SDavid Howells * Create a version 4 volume record 96254ceac45SDavid Howells * - keyed on server and FSID 96354ceac45SDavid Howells */ 96491ea40b9S\"Talpey, Thomas\ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, 96554ceac45SDavid Howells struct nfs_fh *mntfh) 96654ceac45SDavid Howells { 96754ceac45SDavid Howells struct nfs_fattr fattr; 96854ceac45SDavid Howells struct nfs_server *server; 96954ceac45SDavid Howells int error; 97054ceac45SDavid Howells 97154ceac45SDavid Howells dprintk("--> nfs4_create_server()\n"); 97254ceac45SDavid Howells 97354ceac45SDavid Howells server = nfs_alloc_server(); 97454ceac45SDavid Howells if (!server) 97554ceac45SDavid Howells return ERR_PTR(-ENOMEM); 97654ceac45SDavid Howells 97754ceac45SDavid Howells /* Get a client record */ 97891ea40b9S\"Talpey, Thomas\ error = nfs4_set_client(server, 97991ea40b9S\"Talpey, Thomas\ data->nfs_server.hostname, 98091ea40b9S\"Talpey, Thomas\ &data->nfs_server.address, 98191ea40b9S\"Talpey, Thomas\ data->client_address, 98291ea40b9S\"Talpey, Thomas\ data->auth_flavors[0], 98391ea40b9S\"Talpey, Thomas\ data->nfs_server.protocol, 98491ea40b9S\"Talpey, Thomas\ data->timeo, data->retrans); 98554ceac45SDavid Howells if (error < 0) 98654ceac45SDavid Howells goto error; 98754ceac45SDavid Howells 98854ceac45SDavid Howells /* set up the general RPC client */ 98991ea40b9S\"Talpey, Thomas\ error = nfs4_init_server(server, data); 99054ceac45SDavid Howells if (error < 0) 99154ceac45SDavid Howells goto error; 99254ceac45SDavid Howells 99354ceac45SDavid Howells BUG_ON(!server->nfs_client); 99454ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops); 99554ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 99654ceac45SDavid Howells 99754ceac45SDavid Howells /* Probe the root fh to retrieve its FSID */ 99891ea40b9S\"Talpey, Thomas\ error = nfs4_path_walk(server, mntfh, data->nfs_server.export_path); 99954ceac45SDavid Howells if (error < 0) 100054ceac45SDavid Howells goto error; 100154ceac45SDavid Howells 10026daabf1bSDavid Howells dprintk("Server FSID: %llx:%llx\n", 10036daabf1bSDavid Howells (unsigned long long) server->fsid.major, 10046daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 100554ceac45SDavid Howells dprintk("Mount FH: %d\n", mntfh->size); 100654ceac45SDavid Howells 100754ceac45SDavid Howells error = nfs_probe_fsinfo(server, mntfh, &fattr); 100854ceac45SDavid Howells if (error < 0) 100954ceac45SDavid Howells goto error; 101054ceac45SDavid Howells 101154af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 101254af3bb5STrond Myklebust server->namelen = NFS4_MAXNAMLEN; 101354af3bb5STrond Myklebust 101454ceac45SDavid Howells BUG_ON(!server->nfs_client); 101554ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops); 101654ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 101754ceac45SDavid Howells 101854ceac45SDavid Howells spin_lock(&nfs_client_lock); 101954ceac45SDavid Howells list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); 102054ceac45SDavid Howells list_add_tail(&server->master_link, &nfs_volume_list); 102154ceac45SDavid Howells spin_unlock(&nfs_client_lock); 102254ceac45SDavid Howells 102354ceac45SDavid Howells server->mount_time = jiffies; 102454ceac45SDavid Howells dprintk("<-- nfs4_create_server() = %p\n", server); 102554ceac45SDavid Howells return server; 102654ceac45SDavid Howells 102754ceac45SDavid Howells error: 102854ceac45SDavid Howells nfs_free_server(server); 102954ceac45SDavid Howells dprintk("<-- nfs4_create_server() = error %d\n", error); 103054ceac45SDavid Howells return ERR_PTR(error); 103154ceac45SDavid Howells } 103254ceac45SDavid Howells 103354ceac45SDavid Howells /* 103454ceac45SDavid Howells * Create an NFS4 referral server record 103554ceac45SDavid Howells */ 103654ceac45SDavid Howells struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, 1037f2d0d85eSTrond Myklebust struct nfs_fh *mntfh) 103854ceac45SDavid Howells { 103954ceac45SDavid Howells struct nfs_client *parent_client; 104054ceac45SDavid Howells struct nfs_server *server, *parent_server; 104154ceac45SDavid Howells struct nfs_fattr fattr; 104254ceac45SDavid Howells int error; 104354ceac45SDavid Howells 104454ceac45SDavid Howells dprintk("--> nfs4_create_referral_server()\n"); 104554ceac45SDavid Howells 104654ceac45SDavid Howells server = nfs_alloc_server(); 104754ceac45SDavid Howells if (!server) 104854ceac45SDavid Howells return ERR_PTR(-ENOMEM); 104954ceac45SDavid Howells 105054ceac45SDavid Howells parent_server = NFS_SB(data->sb); 105154ceac45SDavid Howells parent_client = parent_server->nfs_client; 105254ceac45SDavid Howells 105354ceac45SDavid Howells /* Get a client representation. 105454ceac45SDavid Howells * Note: NFSv4 always uses TCP, */ 105554ceac45SDavid Howells error = nfs4_set_client(server, data->hostname, data->addr, 10567d9ac06fSJ. Bruce Fields parent_client->cl_ipaddr, 105754ceac45SDavid Howells data->authflavor, 105854ceac45SDavid Howells parent_server->client->cl_xprt->prot, 105954ceac45SDavid Howells parent_client->retrans_timeo, 106054ceac45SDavid Howells parent_client->retrans_count); 1061297de4f6Sandros@citi.umich.edu if (error < 0) 1062297de4f6Sandros@citi.umich.edu goto error; 106354ceac45SDavid Howells 106454ceac45SDavid Howells /* Initialise the client representation from the parent server */ 106554ceac45SDavid Howells nfs_server_copy_userdata(server, parent_server); 106654ceac45SDavid Howells server->caps |= NFS_CAP_ATOMIC_OPEN; 106754ceac45SDavid Howells 106854ceac45SDavid Howells error = nfs_init_server_rpcclient(server, data->authflavor); 106954ceac45SDavid Howells if (error < 0) 107054ceac45SDavid Howells goto error; 107154ceac45SDavid Howells 107254ceac45SDavid Howells BUG_ON(!server->nfs_client); 107354ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops); 107454ceac45SDavid Howells BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); 107554ceac45SDavid Howells 1076f2d0d85eSTrond Myklebust /* Probe the root fh to retrieve its FSID and filehandle */ 1077f2d0d85eSTrond Myklebust error = nfs4_path_walk(server, mntfh, data->mnt_path); 1078f2d0d85eSTrond Myklebust if (error < 0) 1079f2d0d85eSTrond Myklebust goto error; 1080f2d0d85eSTrond Myklebust 108154ceac45SDavid Howells /* probe the filesystem info for this server filesystem */ 1082f2d0d85eSTrond Myklebust error = nfs_probe_fsinfo(server, mntfh, &fattr); 108354ceac45SDavid Howells if (error < 0) 108454ceac45SDavid Howells goto error; 108554ceac45SDavid Howells 108654af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 108754af3bb5STrond Myklebust server->namelen = NFS4_MAXNAMLEN; 108854af3bb5STrond Myklebust 108954ceac45SDavid Howells dprintk("Referral FSID: %llx:%llx\n", 10906daabf1bSDavid Howells (unsigned long long) server->fsid.major, 10916daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 109254ceac45SDavid Howells 109354ceac45SDavid Howells spin_lock(&nfs_client_lock); 109454ceac45SDavid Howells list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); 109554ceac45SDavid Howells list_add_tail(&server->master_link, &nfs_volume_list); 109654ceac45SDavid Howells spin_unlock(&nfs_client_lock); 109754ceac45SDavid Howells 109854ceac45SDavid Howells server->mount_time = jiffies; 109954ceac45SDavid Howells 110054ceac45SDavid Howells dprintk("<-- nfs_create_referral_server() = %p\n", server); 110154ceac45SDavid Howells return server; 110254ceac45SDavid Howells 110354ceac45SDavid Howells error: 110454ceac45SDavid Howells nfs_free_server(server); 110554ceac45SDavid Howells dprintk("<-- nfs4_create_referral_server() = error %d\n", error); 110654ceac45SDavid Howells return ERR_PTR(error); 110754ceac45SDavid Howells } 110854ceac45SDavid Howells 110954ceac45SDavid Howells #endif /* CONFIG_NFS_V4 */ 111054ceac45SDavid Howells 111154ceac45SDavid Howells /* 111254ceac45SDavid Howells * Clone an NFS2, NFS3 or NFS4 server record 111354ceac45SDavid Howells */ 111454ceac45SDavid Howells struct nfs_server *nfs_clone_server(struct nfs_server *source, 111554ceac45SDavid Howells struct nfs_fh *fh, 111654ceac45SDavid Howells struct nfs_fattr *fattr) 111754ceac45SDavid Howells { 111854ceac45SDavid Howells struct nfs_server *server; 111954ceac45SDavid Howells struct nfs_fattr fattr_fsinfo; 112054ceac45SDavid Howells int error; 112154ceac45SDavid Howells 112254ceac45SDavid Howells dprintk("--> nfs_clone_server(,%llx:%llx,)\n", 11236daabf1bSDavid Howells (unsigned long long) fattr->fsid.major, 11246daabf1bSDavid Howells (unsigned long long) fattr->fsid.minor); 112554ceac45SDavid Howells 112654ceac45SDavid Howells server = nfs_alloc_server(); 112754ceac45SDavid Howells if (!server) 112854ceac45SDavid Howells return ERR_PTR(-ENOMEM); 112954ceac45SDavid Howells 113054ceac45SDavid Howells /* Copy data from the source */ 113154ceac45SDavid Howells server->nfs_client = source->nfs_client; 113254ceac45SDavid Howells atomic_inc(&server->nfs_client->cl_count); 113354ceac45SDavid Howells nfs_server_copy_userdata(server, source); 113454ceac45SDavid Howells 113554ceac45SDavid Howells server->fsid = fattr->fsid; 113654ceac45SDavid Howells 113754ceac45SDavid Howells error = nfs_init_server_rpcclient(server, source->client->cl_auth->au_flavor); 113854ceac45SDavid Howells if (error < 0) 113954ceac45SDavid Howells goto out_free_server; 114054ceac45SDavid Howells if (!IS_ERR(source->client_acl)) 114154ceac45SDavid Howells nfs_init_server_aclclient(server); 114254ceac45SDavid Howells 114354ceac45SDavid Howells /* probe the filesystem info for this server filesystem */ 114454ceac45SDavid Howells error = nfs_probe_fsinfo(server, fh, &fattr_fsinfo); 114554ceac45SDavid Howells if (error < 0) 114654ceac45SDavid Howells goto out_free_server; 114754ceac45SDavid Howells 114854af3bb5STrond Myklebust if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) 114954af3bb5STrond Myklebust server->namelen = NFS4_MAXNAMLEN; 115054af3bb5STrond Myklebust 115154ceac45SDavid Howells dprintk("Cloned FSID: %llx:%llx\n", 11526daabf1bSDavid Howells (unsigned long long) server->fsid.major, 11536daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 115454ceac45SDavid Howells 115554ceac45SDavid Howells error = nfs_start_lockd(server); 115654ceac45SDavid Howells if (error < 0) 115754ceac45SDavid Howells goto out_free_server; 115854ceac45SDavid Howells 115954ceac45SDavid Howells spin_lock(&nfs_client_lock); 116054ceac45SDavid Howells list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); 116154ceac45SDavid Howells list_add_tail(&server->master_link, &nfs_volume_list); 116254ceac45SDavid Howells spin_unlock(&nfs_client_lock); 116354ceac45SDavid Howells 116454ceac45SDavid Howells server->mount_time = jiffies; 116554ceac45SDavid Howells 116654ceac45SDavid Howells dprintk("<-- nfs_clone_server() = %p\n", server); 116754ceac45SDavid Howells return server; 116854ceac45SDavid Howells 116954ceac45SDavid Howells out_free_server: 117054ceac45SDavid Howells nfs_free_server(server); 117154ceac45SDavid Howells dprintk("<-- nfs_clone_server() = error %d\n", error); 117254ceac45SDavid Howells return ERR_PTR(error); 117354ceac45SDavid Howells } 11746aaca566SDavid Howells 11756aaca566SDavid Howells #ifdef CONFIG_PROC_FS 11766aaca566SDavid Howells static struct proc_dir_entry *proc_fs_nfs; 11776aaca566SDavid Howells 11786aaca566SDavid Howells static int nfs_server_list_open(struct inode *inode, struct file *file); 11796aaca566SDavid Howells static void *nfs_server_list_start(struct seq_file *p, loff_t *pos); 11806aaca566SDavid Howells static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos); 11816aaca566SDavid Howells static void nfs_server_list_stop(struct seq_file *p, void *v); 11826aaca566SDavid Howells static int nfs_server_list_show(struct seq_file *m, void *v); 11836aaca566SDavid Howells 11846aaca566SDavid Howells static struct seq_operations nfs_server_list_ops = { 11856aaca566SDavid Howells .start = nfs_server_list_start, 11866aaca566SDavid Howells .next = nfs_server_list_next, 11876aaca566SDavid Howells .stop = nfs_server_list_stop, 11886aaca566SDavid Howells .show = nfs_server_list_show, 11896aaca566SDavid Howells }; 11906aaca566SDavid Howells 119100977a59SArjan van de Ven static const struct file_operations nfs_server_list_fops = { 11926aaca566SDavid Howells .open = nfs_server_list_open, 11936aaca566SDavid Howells .read = seq_read, 11946aaca566SDavid Howells .llseek = seq_lseek, 11956aaca566SDavid Howells .release = seq_release, 11966aaca566SDavid Howells }; 11976aaca566SDavid Howells 11986aaca566SDavid Howells static int nfs_volume_list_open(struct inode *inode, struct file *file); 11996aaca566SDavid Howells static void *nfs_volume_list_start(struct seq_file *p, loff_t *pos); 12006aaca566SDavid Howells static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos); 12016aaca566SDavid Howells static void nfs_volume_list_stop(struct seq_file *p, void *v); 12026aaca566SDavid Howells static int nfs_volume_list_show(struct seq_file *m, void *v); 12036aaca566SDavid Howells 12046aaca566SDavid Howells static struct seq_operations nfs_volume_list_ops = { 12056aaca566SDavid Howells .start = nfs_volume_list_start, 12066aaca566SDavid Howells .next = nfs_volume_list_next, 12076aaca566SDavid Howells .stop = nfs_volume_list_stop, 12086aaca566SDavid Howells .show = nfs_volume_list_show, 12096aaca566SDavid Howells }; 12106aaca566SDavid Howells 121100977a59SArjan van de Ven static const struct file_operations nfs_volume_list_fops = { 12126aaca566SDavid Howells .open = nfs_volume_list_open, 12136aaca566SDavid Howells .read = seq_read, 12146aaca566SDavid Howells .llseek = seq_lseek, 12156aaca566SDavid Howells .release = seq_release, 12166aaca566SDavid Howells }; 12176aaca566SDavid Howells 12186aaca566SDavid Howells /* 12196aaca566SDavid Howells * open "/proc/fs/nfsfs/servers" which provides a summary of servers with which 12206aaca566SDavid Howells * we're dealing 12216aaca566SDavid Howells */ 12226aaca566SDavid Howells static int nfs_server_list_open(struct inode *inode, struct file *file) 12236aaca566SDavid Howells { 12246aaca566SDavid Howells struct seq_file *m; 12256aaca566SDavid Howells int ret; 12266aaca566SDavid Howells 12276aaca566SDavid Howells ret = seq_open(file, &nfs_server_list_ops); 12286aaca566SDavid Howells if (ret < 0) 12296aaca566SDavid Howells return ret; 12306aaca566SDavid Howells 12316aaca566SDavid Howells m = file->private_data; 12326aaca566SDavid Howells m->private = PDE(inode)->data; 12336aaca566SDavid Howells 12346aaca566SDavid Howells return 0; 12356aaca566SDavid Howells } 12366aaca566SDavid Howells 12376aaca566SDavid Howells /* 12386aaca566SDavid Howells * set up the iterator to start reading from the server list and return the first item 12396aaca566SDavid Howells */ 12406aaca566SDavid Howells static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos) 12416aaca566SDavid Howells { 12426aaca566SDavid Howells /* lock the list against modification */ 12436aaca566SDavid Howells spin_lock(&nfs_client_lock); 1244259902eaSPavel Emelianov return seq_list_start_head(&nfs_client_list, *_pos); 12456aaca566SDavid Howells } 12466aaca566SDavid Howells 12476aaca566SDavid Howells /* 12486aaca566SDavid Howells * move to next server 12496aaca566SDavid Howells */ 12506aaca566SDavid Howells static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos) 12516aaca566SDavid Howells { 1252259902eaSPavel Emelianov return seq_list_next(v, &nfs_client_list, pos); 12536aaca566SDavid Howells } 12546aaca566SDavid Howells 12556aaca566SDavid Howells /* 12566aaca566SDavid Howells * clean up after reading from the transports list 12576aaca566SDavid Howells */ 12586aaca566SDavid Howells static void nfs_server_list_stop(struct seq_file *p, void *v) 12596aaca566SDavid Howells { 12606aaca566SDavid Howells spin_unlock(&nfs_client_lock); 12616aaca566SDavid Howells } 12626aaca566SDavid Howells 12636aaca566SDavid Howells /* 12646aaca566SDavid Howells * display a header line followed by a load of call lines 12656aaca566SDavid Howells */ 12666aaca566SDavid Howells static int nfs_server_list_show(struct seq_file *m, void *v) 12676aaca566SDavid Howells { 12686aaca566SDavid Howells struct nfs_client *clp; 12696aaca566SDavid Howells 12706aaca566SDavid Howells /* display header on line 1 */ 1271259902eaSPavel Emelianov if (v == &nfs_client_list) { 12726aaca566SDavid Howells seq_puts(m, "NV SERVER PORT USE HOSTNAME\n"); 12736aaca566SDavid Howells return 0; 12746aaca566SDavid Howells } 12756aaca566SDavid Howells 12766aaca566SDavid Howells /* display one transport per line on subsequent lines */ 12776aaca566SDavid Howells clp = list_entry(v, struct nfs_client, cl_share_link); 12786aaca566SDavid Howells 12796aaca566SDavid Howells seq_printf(m, "v%d %02x%02x%02x%02x %4hx %3d %s\n", 12806aaca566SDavid Howells clp->cl_nfsversion, 12816aaca566SDavid Howells NIPQUAD(clp->cl_addr.sin_addr), 12826aaca566SDavid Howells ntohs(clp->cl_addr.sin_port), 12836aaca566SDavid Howells atomic_read(&clp->cl_count), 12846aaca566SDavid Howells clp->cl_hostname); 12856aaca566SDavid Howells 12866aaca566SDavid Howells return 0; 12876aaca566SDavid Howells } 12886aaca566SDavid Howells 12896aaca566SDavid Howells /* 12906aaca566SDavid Howells * open "/proc/fs/nfsfs/volumes" which provides a summary of extant volumes 12916aaca566SDavid Howells */ 12926aaca566SDavid Howells static int nfs_volume_list_open(struct inode *inode, struct file *file) 12936aaca566SDavid Howells { 12946aaca566SDavid Howells struct seq_file *m; 12956aaca566SDavid Howells int ret; 12966aaca566SDavid Howells 12976aaca566SDavid Howells ret = seq_open(file, &nfs_volume_list_ops); 12986aaca566SDavid Howells if (ret < 0) 12996aaca566SDavid Howells return ret; 13006aaca566SDavid Howells 13016aaca566SDavid Howells m = file->private_data; 13026aaca566SDavid Howells m->private = PDE(inode)->data; 13036aaca566SDavid Howells 13046aaca566SDavid Howells return 0; 13056aaca566SDavid Howells } 13066aaca566SDavid Howells 13076aaca566SDavid Howells /* 13086aaca566SDavid Howells * set up the iterator to start reading from the volume list and return the first item 13096aaca566SDavid Howells */ 13106aaca566SDavid Howells static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos) 13116aaca566SDavid Howells { 13126aaca566SDavid Howells /* lock the list against modification */ 13136aaca566SDavid Howells spin_lock(&nfs_client_lock); 1314259902eaSPavel Emelianov return seq_list_start_head(&nfs_volume_list, *_pos); 13156aaca566SDavid Howells } 13166aaca566SDavid Howells 13176aaca566SDavid Howells /* 13186aaca566SDavid Howells * move to next volume 13196aaca566SDavid Howells */ 13206aaca566SDavid Howells static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos) 13216aaca566SDavid Howells { 1322259902eaSPavel Emelianov return seq_list_next(v, &nfs_volume_list, pos); 13236aaca566SDavid Howells } 13246aaca566SDavid Howells 13256aaca566SDavid Howells /* 13266aaca566SDavid Howells * clean up after reading from the transports list 13276aaca566SDavid Howells */ 13286aaca566SDavid Howells static void nfs_volume_list_stop(struct seq_file *p, void *v) 13296aaca566SDavid Howells { 13306aaca566SDavid Howells spin_unlock(&nfs_client_lock); 13316aaca566SDavid Howells } 13326aaca566SDavid Howells 13336aaca566SDavid Howells /* 13346aaca566SDavid Howells * display a header line followed by a load of call lines 13356aaca566SDavid Howells */ 13366aaca566SDavid Howells static int nfs_volume_list_show(struct seq_file *m, void *v) 13376aaca566SDavid Howells { 13386aaca566SDavid Howells struct nfs_server *server; 13396aaca566SDavid Howells struct nfs_client *clp; 13406aaca566SDavid Howells char dev[8], fsid[17]; 13416aaca566SDavid Howells 13426aaca566SDavid Howells /* display header on line 1 */ 1343259902eaSPavel Emelianov if (v == &nfs_volume_list) { 13446aaca566SDavid Howells seq_puts(m, "NV SERVER PORT DEV FSID\n"); 13456aaca566SDavid Howells return 0; 13466aaca566SDavid Howells } 13476aaca566SDavid Howells /* display one transport per line on subsequent lines */ 13486aaca566SDavid Howells server = list_entry(v, struct nfs_server, master_link); 13496aaca566SDavid Howells clp = server->nfs_client; 13506aaca566SDavid Howells 13516aaca566SDavid Howells snprintf(dev, 8, "%u:%u", 13526aaca566SDavid Howells MAJOR(server->s_dev), MINOR(server->s_dev)); 13536aaca566SDavid Howells 13546aaca566SDavid Howells snprintf(fsid, 17, "%llx:%llx", 13556daabf1bSDavid Howells (unsigned long long) server->fsid.major, 13566daabf1bSDavid Howells (unsigned long long) server->fsid.minor); 13576aaca566SDavid Howells 13586aaca566SDavid Howells seq_printf(m, "v%d %02x%02x%02x%02x %4hx %-7s %-17s\n", 13596aaca566SDavid Howells clp->cl_nfsversion, 13606aaca566SDavid Howells NIPQUAD(clp->cl_addr.sin_addr), 13616aaca566SDavid Howells ntohs(clp->cl_addr.sin_port), 13626aaca566SDavid Howells dev, 13636aaca566SDavid Howells fsid); 13646aaca566SDavid Howells 13656aaca566SDavid Howells return 0; 13666aaca566SDavid Howells } 13676aaca566SDavid Howells 13686aaca566SDavid Howells /* 13696aaca566SDavid Howells * initialise the /proc/fs/nfsfs/ directory 13706aaca566SDavid Howells */ 13716aaca566SDavid Howells int __init nfs_fs_proc_init(void) 13726aaca566SDavid Howells { 13736aaca566SDavid Howells struct proc_dir_entry *p; 13746aaca566SDavid Howells 13756aaca566SDavid Howells proc_fs_nfs = proc_mkdir("nfsfs", proc_root_fs); 13766aaca566SDavid Howells if (!proc_fs_nfs) 13776aaca566SDavid Howells goto error_0; 13786aaca566SDavid Howells 13796aaca566SDavid Howells proc_fs_nfs->owner = THIS_MODULE; 13806aaca566SDavid Howells 13816aaca566SDavid Howells /* a file of servers with which we're dealing */ 13826aaca566SDavid Howells p = create_proc_entry("servers", S_IFREG|S_IRUGO, proc_fs_nfs); 13836aaca566SDavid Howells if (!p) 13846aaca566SDavid Howells goto error_1; 13856aaca566SDavid Howells 13866aaca566SDavid Howells p->proc_fops = &nfs_server_list_fops; 13876aaca566SDavid Howells p->owner = THIS_MODULE; 13886aaca566SDavid Howells 13896aaca566SDavid Howells /* a file of volumes that we have mounted */ 13906aaca566SDavid Howells p = create_proc_entry("volumes", S_IFREG|S_IRUGO, proc_fs_nfs); 13916aaca566SDavid Howells if (!p) 13926aaca566SDavid Howells goto error_2; 13936aaca566SDavid Howells 13946aaca566SDavid Howells p->proc_fops = &nfs_volume_list_fops; 13956aaca566SDavid Howells p->owner = THIS_MODULE; 13966aaca566SDavid Howells return 0; 13976aaca566SDavid Howells 13986aaca566SDavid Howells error_2: 13996aaca566SDavid Howells remove_proc_entry("servers", proc_fs_nfs); 14006aaca566SDavid Howells error_1: 14016aaca566SDavid Howells remove_proc_entry("nfsfs", proc_root_fs); 14026aaca566SDavid Howells error_0: 14036aaca566SDavid Howells return -ENOMEM; 14046aaca566SDavid Howells } 14056aaca566SDavid Howells 14066aaca566SDavid Howells /* 14076aaca566SDavid Howells * clean up the /proc/fs/nfsfs/ directory 14086aaca566SDavid Howells */ 14096aaca566SDavid Howells void nfs_fs_proc_exit(void) 14106aaca566SDavid Howells { 14116aaca566SDavid Howells remove_proc_entry("volumes", proc_fs_nfs); 14126aaca566SDavid Howells remove_proc_entry("servers", proc_fs_nfs); 14136aaca566SDavid Howells remove_proc_entry("nfsfs", proc_root_fs); 14146aaca566SDavid Howells } 14156aaca566SDavid Howells 14166aaca566SDavid Howells #endif /* CONFIG_PROC_FS */ 1417