📄 client.c
字号:
/* client.c: NFS client sharing and management code * * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */#include <linux/module.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/time.h>#include <linux/kernel.h>#include <linux/mm.h>#include <linux/string.h>#include <linux/stat.h>#include <linux/errno.h>#include <linux/unistd.h>#include <linux/sunrpc/clnt.h>#include <linux/sunrpc/stats.h>#include <linux/sunrpc/metrics.h>#include <linux/sunrpc/xprtsock.h>#include <linux/sunrpc/xprtrdma.h>#include <linux/nfs_fs.h>#include <linux/nfs_mount.h>#include <linux/nfs4_mount.h>#include <linux/lockd/bind.h>#include <linux/seq_file.h>#include <linux/mount.h>#include <linux/nfs_idmap.h>#include <linux/vfs.h>#include <linux/inet.h>#include <linux/nfs_xdr.h>#include <asm/system.h>#include "nfs4_fs.h"#include "callback.h"#include "delegation.h"#include "iostat.h"#include "internal.h"#define NFSDBG_FACILITY NFSDBG_CLIENTstatic DEFINE_SPINLOCK(nfs_client_lock);static LIST_HEAD(nfs_client_list);static LIST_HEAD(nfs_volume_list);static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq);/* * RPC cruft for NFS */static struct rpc_version *nfs_version[5] = { [2] = &nfs_version2,#ifdef CONFIG_NFS_V3 [3] = &nfs_version3,#endif#ifdef CONFIG_NFS_V4 [4] = &nfs_version4,#endif};struct rpc_program nfs_program = { .name = "nfs", .number = NFS_PROGRAM, .nrvers = ARRAY_SIZE(nfs_version), .version = nfs_version, .stats = &nfs_rpcstat, .pipe_dir_name = "/nfs",};struct rpc_stat nfs_rpcstat = { .program = &nfs_program};#ifdef CONFIG_NFS_V3_ACLstatic struct rpc_stat nfsacl_rpcstat = { &nfsacl_program };static struct rpc_version * nfsacl_version[] = { [3] = &nfsacl_version3,};struct rpc_program nfsacl_program = { .name = "nfsacl", .number = NFS_ACL_PROGRAM, .nrvers = ARRAY_SIZE(nfsacl_version), .version = nfsacl_version, .stats = &nfsacl_rpcstat,};#endif /* CONFIG_NFS_V3_ACL *//* * Allocate a shared client record * * Since these are allocated/deallocated very rarely, we don't * bother putting them in a slab cache... */static struct nfs_client *nfs_alloc_client(const char *hostname, const struct sockaddr_in *addr, int nfsversion){ struct nfs_client *clp; if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) goto error_0; if (nfsversion == 4) { if (nfs_callback_up() < 0) goto error_2; __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state); } atomic_set(&clp->cl_count, 1); clp->cl_cons_state = NFS_CS_INITING; clp->cl_nfsversion = nfsversion; memcpy(&clp->cl_addr, addr, sizeof(clp->cl_addr)); if (hostname) { clp->cl_hostname = kstrdup(hostname, GFP_KERNEL); if (!clp->cl_hostname) goto error_3; } INIT_LIST_HEAD(&clp->cl_superblocks); clp->cl_rpcclient = ERR_PTR(-EINVAL);#ifdef CONFIG_NFS_V4 init_rwsem(&clp->cl_sem); INIT_LIST_HEAD(&clp->cl_delegations); spin_lock_init(&clp->cl_lock); INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state); rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); clp->cl_boot_time = CURRENT_TIME; clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;#endif return clp;error_3: if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) nfs_callback_down();error_2: kfree(clp);error_0: return NULL;}static void nfs4_shutdown_client(struct nfs_client *clp){#ifdef CONFIG_NFS_V4 if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state)) nfs4_kill_renewd(clp); BUG_ON(!RB_EMPTY_ROOT(&clp->cl_state_owners)); if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state)) nfs_idmap_delete(clp);#endif}/* * Destroy a shared client record */static void nfs_free_client(struct nfs_client *clp){ dprintk("--> nfs_free_client(%d)\n", clp->cl_nfsversion); nfs4_shutdown_client(clp); /* -EIO all pending I/O */ if (!IS_ERR(clp->cl_rpcclient)) rpc_shutdown_client(clp->cl_rpcclient); if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) nfs_callback_down(); kfree(clp->cl_hostname); kfree(clp); dprintk("<-- nfs_free_client()\n");}/* * Release a reference to a shared client record */void nfs_put_client(struct nfs_client *clp){ if (!clp) return; dprintk("--> nfs_put_client({%d})\n", atomic_read(&clp->cl_count)); if (atomic_dec_and_lock(&clp->cl_count, &nfs_client_lock)) { list_del(&clp->cl_share_link); spin_unlock(&nfs_client_lock); BUG_ON(!list_empty(&clp->cl_superblocks)); nfs_free_client(clp); }}/* * Find a client by address * - caller must hold nfs_client_lock */static struct nfs_client *__nfs_find_client(const struct sockaddr_in *addr, int nfsversion, int match_port){ struct nfs_client *clp; list_for_each_entry(clp, &nfs_client_list, cl_share_link) { /* Don't match clients that failed to initialise properly */ if (clp->cl_cons_state < 0) continue; /* Different NFS versions cannot share the same nfs_client */ if (clp->cl_nfsversion != nfsversion) continue; if (memcmp(&clp->cl_addr.sin_addr, &addr->sin_addr, sizeof(clp->cl_addr.sin_addr)) != 0) continue; if (!match_port || clp->cl_addr.sin_port == addr->sin_port) goto found; } return NULL;found: atomic_inc(&clp->cl_count); return clp;}/* * Find a client by IP address and protocol version * - returns NULL if no such client */struct nfs_client *nfs_find_client(const struct sockaddr_in *addr, int nfsversion){ struct nfs_client *clp; spin_lock(&nfs_client_lock); clp = __nfs_find_client(addr, nfsversion, 0); spin_unlock(&nfs_client_lock); if (clp != NULL && clp->cl_cons_state != NFS_CS_READY) { nfs_put_client(clp); clp = NULL; } return clp;}/* * Look up a client by IP address and protocol version * - creates a new record if one doesn't yet exist */static struct nfs_client *nfs_get_client(const char *hostname, const struct sockaddr_in *addr, int nfsversion){ struct nfs_client *clp, *new = NULL; int error; dprintk("--> nfs_get_client(%s,"NIPQUAD_FMT":%d,%d)\n", hostname ?: "", NIPQUAD(addr->sin_addr), addr->sin_port, nfsversion); /* see if the client already exists */ do { spin_lock(&nfs_client_lock); clp = __nfs_find_client(addr, nfsversion, 1); if (clp) goto found_client; if (new) goto install_client; spin_unlock(&nfs_client_lock); new = nfs_alloc_client(hostname, addr, nfsversion); } while (new); return ERR_PTR(-ENOMEM); /* install a new client and return with it unready */install_client: clp = new; list_add(&clp->cl_share_link, &nfs_client_list); spin_unlock(&nfs_client_lock); dprintk("--> nfs_get_client() = %p [new]\n", clp); return clp; /* found an existing client * - make sure it's ready before returning */found_client: spin_unlock(&nfs_client_lock); if (new) nfs_free_client(new); error = wait_event_interruptible(nfs_client_active_wq, clp->cl_cons_state != NFS_CS_INITING); if (error < 0) { nfs_put_client(clp); return ERR_PTR(-ERESTARTSYS); } if (clp->cl_cons_state < NFS_CS_READY) { error = clp->cl_cons_state; nfs_put_client(clp); return ERR_PTR(error); } BUG_ON(clp->cl_cons_state != NFS_CS_READY); dprintk("--> nfs_get_client() = %p [share]\n", clp); return clp;}/* * Mark a server as ready or failed */static void nfs_mark_client_ready(struct nfs_client *clp, int state){ clp->cl_cons_state = state; wake_up_all(&nfs_client_active_wq);}/* * Initialise the timeout values for a connection */static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, unsigned int timeo, unsigned int retrans){ to->to_initval = timeo * HZ / 10; to->to_retries = retrans; if (!to->to_retries) to->to_retries = 2; switch (proto) { case XPRT_TRANSPORT_TCP: case XPRT_TRANSPORT_RDMA: if (!to->to_initval) to->to_initval = 60 * HZ; if (to->to_initval > NFS_MAX_TCP_TIMEOUT) to->to_initval = NFS_MAX_TCP_TIMEOUT; to->to_increment = to->to_initval; to->to_maxval = to->to_initval + (to->to_increment * to->to_retries); to->to_exponential = 0; break; case XPRT_TRANSPORT_UDP: default: if (!to->to_initval) to->to_initval = 11 * HZ / 10; if (to->to_initval > NFS_MAX_UDP_TIMEOUT) to->to_initval = NFS_MAX_UDP_TIMEOUT; to->to_maxval = NFS_MAX_UDP_TIMEOUT; to->to_exponential = 1; break; }}/* * Create an RPC client handle */static int nfs_create_rpc_client(struct nfs_client *clp, int proto, unsigned int timeo, unsigned int retrans, rpc_authflavor_t flavor, int flags){ struct rpc_timeout timeparms; struct rpc_clnt *clnt = NULL; struct rpc_create_args args = { .protocol = proto, .address = (struct sockaddr *)&clp->cl_addr, .addrsize = sizeof(clp->cl_addr), .timeout = &timeparms, .servername = clp->cl_hostname, .program = &nfs_program, .version = clp->rpc_ops->version, .authflavor = flavor, .flags = flags, }; if (!IS_ERR(clp->cl_rpcclient)) return 0; nfs_init_timeout_values(&timeparms, proto, timeo, retrans); clp->retrans_timeo = timeparms.to_initval; clp->retrans_count = timeparms.to_retries; clnt = rpc_create(&args); if (IS_ERR(clnt)) { dprintk("%s: cannot create RPC client. Error = %ld\n", __FUNCTION__, PTR_ERR(clnt)); return PTR_ERR(clnt); } clp->cl_rpcclient = clnt; return 0;}/* * Version 2 or 3 client destruction */static void nfs_destroy_server(struct nfs_server *server){ if (!(server->flags & NFS_MOUNT_NONLM)) lockd_down(); /* release rpc.lockd */}/* * Version 2 or 3 lockd setup */static int nfs_start_lockd(struct nfs_server *server){ int error = 0; if (server->nfs_client->cl_nfsversion > 3) goto out; if (server->flags & NFS_MOUNT_NONLM) goto out; error = lockd_up((server->flags & NFS_MOUNT_TCP) ? IPPROTO_TCP : IPPROTO_UDP); if (error < 0) server->flags |= NFS_MOUNT_NONLM; else server->destroy = nfs_destroy_server;out: return error;}/* * Initialise an NFSv3 ACL client connection */#ifdef CONFIG_NFS_V3_ACLstatic void nfs_init_server_aclclient(struct nfs_server *server){ if (server->nfs_client->cl_nfsversion != 3) goto out_noacl; if (server->flags & NFS_MOUNT_NOACL) goto out_noacl; server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3); if (IS_ERR(server->client_acl)) goto out_noacl; /* No errors! Assume that Sun nfsacls are supported */ server->caps |= NFS_CAP_ACLS; return;out_noacl: server->caps &= ~NFS_CAP_ACLS;}#elsestatic inline void nfs_init_server_aclclient(struct nfs_server *server){ server->flags &= ~NFS_MOUNT_NOACL; server->caps &= ~NFS_CAP_ACLS;}#endif/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -