NFS: Generalise the nfs_client structure
Generalise the nfs_client structure by: (1) Moving nfs_client to a more general place (nfs_fs_sb.h). (2) Renaming its maintenance routines to be non-NFS4 specific. (3) Move those maintenance routines to a new non-NFS4 specific file (client.c) and move the declarations to internal.h. (4) Make nfs_find/get_client() take a full sockaddr_in to include the port number (will be required for NFS2/3). (5) Make nfs_find/get_client() take the NFS protocol version (again will be required to differentiate NFS2, 3 & 4 client records). Also: (6) Make nfs_client construction proceed akin to inodes, marking them as under construction and providing a function to indicate completion. (7) Make nfs_get_client() wait interruptibly if it finds a client that it can share, but that client is currently being constructed. (8) Make nfs4_create_client() use (6) and (7) instead of locking cl_sem. Signed-Off-By: David Howells <dhowells@redhat.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
This commit is contained in:
parent
e9326dcab4
commit
24c8dbbb5f
@ -4,9 +4,9 @@
|
||||
|
||||
obj-$(CONFIG_NFS_FS) += nfs.o
|
||||
|
||||
nfs-y := dir.o file.o inode.o super.o nfs2xdr.o pagelist.o \
|
||||
proc.o read.o symlink.o unlink.o write.o \
|
||||
namespace.o
|
||||
nfs-y := client.o dir.o file.o inode.o super.o nfs2xdr.o \
|
||||
pagelist.o proc.o read.o symlink.o unlink.o \
|
||||
write.o namespace.o
|
||||
nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o
|
||||
nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o
|
||||
nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include "nfs4_fs.h"
|
||||
#include "callback.h"
|
||||
#include "internal.h"
|
||||
|
||||
#define NFSDBG_FACILITY NFSDBG_CALLBACK
|
||||
|
||||
@ -166,15 +167,15 @@ void nfs_callback_down(void)
|
||||
|
||||
static int nfs_callback_authenticate(struct svc_rqst *rqstp)
|
||||
{
|
||||
struct in_addr *addr = &rqstp->rq_addr.sin_addr;
|
||||
struct sockaddr_in *addr = &rqstp->rq_addr;
|
||||
struct nfs_client *clp;
|
||||
|
||||
/* Don't talk to strangers */
|
||||
clp = nfs4_find_client(addr);
|
||||
clp = nfs_find_client(addr, 4);
|
||||
if (clp == NULL)
|
||||
return SVC_DROP;
|
||||
dprintk("%s: %u.%u.%u.%u NFSv4 callback!\n", __FUNCTION__, NIPQUAD(addr));
|
||||
nfs4_put_client(clp);
|
||||
dprintk("%s: %u.%u.%u.%u NFSv4 callback!\n", __FUNCTION__, NIPQUAD(addr->sin_addr));
|
||||
nfs_put_client(clp);
|
||||
switch (rqstp->rq_authop->flavour) {
|
||||
case RPC_AUTH_NULL:
|
||||
if (rqstp->rq_proc != CB_NULL)
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "nfs4_fs.h"
|
||||
#include "callback.h"
|
||||
#include "delegation.h"
|
||||
#include "internal.h"
|
||||
|
||||
#define NFSDBG_FACILITY NFSDBG_CALLBACK
|
||||
|
||||
@ -22,7 +23,7 @@ unsigned nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres
|
||||
|
||||
res->bitmap[0] = res->bitmap[1] = 0;
|
||||
res->status = htonl(NFS4ERR_BADHANDLE);
|
||||
clp = nfs4_find_client(&args->addr->sin_addr);
|
||||
clp = nfs_find_client(args->addr, 4);
|
||||
if (clp == NULL)
|
||||
goto out;
|
||||
inode = nfs_delegation_find_inode(clp, &args->fh);
|
||||
@ -48,7 +49,7 @@ out_iput:
|
||||
up_read(&nfsi->rwsem);
|
||||
iput(inode);
|
||||
out_putclient:
|
||||
nfs4_put_client(clp);
|
||||
nfs_put_client(clp);
|
||||
out:
|
||||
dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(res->status));
|
||||
return res->status;
|
||||
@ -61,7 +62,7 @@ unsigned nfs4_callback_recall(struct cb_recallargs *args, void *dummy)
|
||||
unsigned res;
|
||||
|
||||
res = htonl(NFS4ERR_BADHANDLE);
|
||||
clp = nfs4_find_client(&args->addr->sin_addr);
|
||||
clp = nfs_find_client(args->addr, 4);
|
||||
if (clp == NULL)
|
||||
goto out;
|
||||
inode = nfs_delegation_find_inode(clp, &args->fh);
|
||||
@ -80,7 +81,7 @@ unsigned nfs4_callback_recall(struct cb_recallargs *args, void *dummy)
|
||||
}
|
||||
iput(inode);
|
||||
out_putclient:
|
||||
nfs4_put_client(clp);
|
||||
nfs_put_client(clp);
|
||||
out:
|
||||
dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(res));
|
||||
return res;
|
||||
|
312
fs/nfs/client.c
Normal file
312
fs/nfs/client.c
Normal file
@ -0,0 +1,312 @@
|
||||
/* 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/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.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/nfs_fs.h>
|
||||
#include <linux/nfs_mount.h>
|
||||
#include <linux/nfs4_mount.h>
|
||||
#include <linux/lockd/bind.h>
|
||||
#include <linux/smp_lock.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_CLIENT
|
||||
|
||||
static DEFINE_SPINLOCK(nfs_client_lock);
|
||||
static LIST_HEAD(nfs_client_list);
|
||||
static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq);
|
||||
|
||||
/*
|
||||
* 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;
|
||||
int error;
|
||||
|
||||
if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL)
|
||||
goto error_0;
|
||||
|
||||
error = rpciod_up();
|
||||
if (error < 0) {
|
||||
dprintk("%s: couldn't start rpciod! Error = %d\n",
|
||||
__FUNCTION__, error);
|
||||
__set_bit(NFS_CS_RPCIOD, &clp->cl_res_state);
|
||||
goto error_1;
|
||||
}
|
||||
|
||||
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);
|
||||
INIT_LIST_HEAD(&clp->cl_state_owners);
|
||||
INIT_LIST_HEAD(&clp->cl_unused);
|
||||
spin_lock_init(&clp->cl_lock);
|
||||
INIT_WORK(&clp->cl_renewd, nfs4_renew_state, clp);
|
||||
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:
|
||||
nfs_callback_down();
|
||||
__clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state);
|
||||
error_2:
|
||||
rpciod_down();
|
||||
__clear_bit(NFS_CS_RPCIOD, &clp->cl_res_state);
|
||||
error_1:
|
||||
kfree(clp);
|
||||
error_0:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy a shared client record
|
||||
*/
|
||||
static void nfs_free_client(struct nfs_client *clp)
|
||||
{
|
||||
dprintk("--> nfs_free_client(%d)\n", clp->cl_nfsversion);
|
||||
|
||||
#ifdef CONFIG_NFS_V4
|
||||
if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state)) {
|
||||
while (!list_empty(&clp->cl_unused)) {
|
||||
struct nfs4_state_owner *sp;
|
||||
|
||||
sp = list_entry(clp->cl_unused.next,
|
||||
struct nfs4_state_owner,
|
||||
so_list);
|
||||
list_del(&sp->so_list);
|
||||
kfree(sp);
|
||||
}
|
||||
BUG_ON(!list_empty(&clp->cl_state_owners));
|
||||
nfs_idmap_delete(clp);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* -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();
|
||||
|
||||
if (__test_and_clear_bit(NFS_CS_RPCIOD, &clp->cl_res_state))
|
||||
rpciod_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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
struct nfs_client *clp;
|
||||
|
||||
list_for_each_entry(clp, &nfs_client_list, cl_share_link) {
|
||||
/* 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 (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);
|
||||
spin_unlock(&nfs_client_lock);
|
||||
|
||||
BUG_ON(clp->cl_cons_state == 0);
|
||||
|
||||
return clp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up a client by IP address and protocol version
|
||||
* - creates a new record if one doesn't yet exist
|
||||
*/
|
||||
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);
|
||||
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);
|
||||
|
||||
if (clp->cl_cons_state == NFS_CS_INITING) {
|
||||
DECLARE_WAITQUEUE(myself, current);
|
||||
|
||||
add_wait_queue(&nfs_client_active_wq, &myself);
|
||||
|
||||
for (;;) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if (signal_pending(current) ||
|
||||
clp->cl_cons_state > NFS_CS_READY)
|
||||
break;
|
||||
schedule();
|
||||
}
|
||||
|
||||
remove_wait_queue(&nfs_client_active_wq, &myself);
|
||||
|
||||
if (signal_pending(current)) {
|
||||
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);
|
||||
}
|
||||
|
||||
dprintk("--> nfs_get_client() = %p [share]\n", clp);
|
||||
return clp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark a server as ready or failed
|
||||
*/
|
||||
void nfs_mark_client_ready(struct nfs_client *clp, int state)
|
||||
{
|
||||
clp->cl_cons_state = state;
|
||||
wake_up_all(&nfs_client_active_wq);
|
||||
}
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include "nfs4_fs.h"
|
||||
#include "delegation.h"
|
||||
#include "internal.h"
|
||||
|
||||
static struct nfs_delegation *nfs_alloc_delegation(void)
|
||||
{
|
||||
@ -145,7 +146,7 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
|
||||
sizeof(delegation->stateid)) != 0 ||
|
||||
delegation->type != nfsi->delegation->type) {
|
||||
printk("%s: server %u.%u.%u.%u, handed out a duplicate delegation!\n",
|
||||
__FUNCTION__, NIPQUAD(clp->cl_addr));
|
||||
__FUNCTION__, NIPQUAD(clp->cl_addr.sin_addr));
|
||||
status = -EIO;
|
||||
}
|
||||
}
|
||||
@ -254,7 +255,7 @@ restart:
|
||||
}
|
||||
out:
|
||||
spin_unlock(&clp->cl_lock);
|
||||
nfs4_put_client(clp);
|
||||
nfs_put_client(clp);
|
||||
module_put_and_exit(0);
|
||||
}
|
||||
|
||||
@ -266,10 +267,10 @@ void nfs_expire_all_delegations(struct nfs_client *clp)
|
||||
atomic_inc(&clp->cl_count);
|
||||
task = kthread_run(nfs_do_expire_all_delegations, clp,
|
||||
"%u.%u.%u.%u-delegreturn",
|
||||
NIPQUAD(clp->cl_addr));
|
||||
NIPQUAD(clp->cl_addr.sin_addr));
|
||||
if (!IS_ERR(task))
|
||||
return;
|
||||
nfs4_put_client(clp);
|
||||
nfs_put_client(clp);
|
||||
module_put(THIS_MODULE);
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,12 @@ struct nfs_clone_mount {
|
||||
rpc_authflavor_t authflavor;
|
||||
};
|
||||
|
||||
/* client.c */
|
||||
extern void nfs_put_client(struct nfs_client *);
|
||||
extern struct nfs_client *nfs_find_client(const struct sockaddr_in *, int);
|
||||
extern struct nfs_client *nfs_get_client(const char *, const struct sockaddr_in *, int);
|
||||
extern void nfs_mark_client_ready(struct nfs_client *, int);
|
||||
|
||||
/* nfs4namespace.c */
|
||||
#ifdef CONFIG_NFS_V4
|
||||
extern struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry);
|
||||
|
@ -42,55 +42,6 @@ enum nfs4_client_state {
|
||||
NFS4CLNT_LEASE_EXPIRED,
|
||||
};
|
||||
|
||||
/*
|
||||
* The nfs_client identifies our client state to the server.
|
||||
*/
|
||||
struct nfs_client {
|
||||
struct list_head cl_servers; /* Global list of servers */
|
||||
struct in_addr cl_addr; /* Server identifier */
|
||||
u64 cl_clientid; /* constant */
|
||||
nfs4_verifier cl_confirm;
|
||||
unsigned long cl_state;
|
||||
|
||||
u32 cl_lockowner_id;
|
||||
|
||||
/*
|
||||
* The following rwsem ensures exclusive access to the server
|
||||
* while we recover the state following a lease expiration.
|
||||
*/
|
||||
struct rw_semaphore cl_sem;
|
||||
|
||||
struct list_head cl_delegations;
|
||||
struct list_head cl_state_owners;
|
||||
struct list_head cl_unused;
|
||||
int cl_nunused;
|
||||
spinlock_t cl_lock;
|
||||
atomic_t cl_count;
|
||||
|
||||
struct rpc_clnt * cl_rpcclient;
|
||||
|
||||
struct list_head cl_superblocks; /* List of nfs_server structs */
|
||||
|
||||
unsigned long cl_lease_time;
|
||||
unsigned long cl_last_renewal;
|
||||
struct work_struct cl_renewd;
|
||||
struct work_struct cl_recoverd;
|
||||
|
||||
struct rpc_wait_queue cl_rpcwaitq;
|
||||
|
||||
/* used for the setclientid verifier */
|
||||
struct timespec cl_boot_time;
|
||||
|
||||
/* idmapper */
|
||||
struct idmap * cl_idmap;
|
||||
|
||||
/* Our own IP address, as a null-terminated string.
|
||||
* This is used to generate the clientid, and the callback address.
|
||||
*/
|
||||
char cl_ipaddr[16];
|
||||
unsigned char cl_id_uniquifier;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct rpc_sequence ensures that RPC calls are sent in the exact
|
||||
* order that they appear on the list.
|
||||
@ -239,9 +190,6 @@ extern void nfs4_renew_state(void *);
|
||||
/* nfs4state.c */
|
||||
extern void init_nfsv4_state(struct nfs_server *);
|
||||
extern void destroy_nfsv4_state(struct nfs_server *);
|
||||
extern struct nfs_client *nfs4_get_client(struct in_addr *);
|
||||
extern void nfs4_put_client(struct nfs_client *clp);
|
||||
extern struct nfs_client *nfs4_find_client(struct in_addr *);
|
||||
struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp);
|
||||
extern u32 nfs4_alloc_lockowner_id(struct nfs_client *);
|
||||
|
||||
|
@ -2968,7 +2968,7 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, unsigned short po
|
||||
for(;;) {
|
||||
setclientid.sc_name_len = scnprintf(setclientid.sc_name,
|
||||
sizeof(setclientid.sc_name), "%s/%u.%u.%u.%u %s %u",
|
||||
clp->cl_ipaddr, NIPQUAD(clp->cl_addr.s_addr),
|
||||
clp->cl_ipaddr, NIPQUAD(clp->cl_addr.sin_addr),
|
||||
cred->cr_ops->cr_name,
|
||||
clp->cl_id_uniquifier);
|
||||
setclientid.sc_netid_len = scnprintf(setclientid.sc_netid,
|
||||
|
@ -50,12 +50,12 @@
|
||||
#include "nfs4_fs.h"
|
||||
#include "callback.h"
|
||||
#include "delegation.h"
|
||||
#include "internal.h"
|
||||
|
||||
#define OPENOWNER_POOL_SIZE 8
|
||||
|
||||
const nfs4_stateid zero_stateid;
|
||||
|
||||
static DEFINE_SPINLOCK(state_spinlock);
|
||||
static LIST_HEAD(nfs4_clientid_list);
|
||||
|
||||
void
|
||||
@ -71,127 +71,11 @@ destroy_nfsv4_state(struct nfs_server *server)
|
||||
kfree(server->mnt_path);
|
||||
server->mnt_path = NULL;
|
||||
if (server->nfs_client) {
|
||||
nfs4_put_client(server->nfs_client);
|
||||
nfs_put_client(server->nfs_client);
|
||||
server->nfs_client = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* nfs4_get_client(): returns an empty client structure
|
||||
* nfs4_put_client(): drops reference to client structure
|
||||
*
|
||||
* Since these are allocated/deallocated very rarely, we don't
|
||||
* bother putting them in a slab cache...
|
||||
*/
|
||||
static struct nfs_client *
|
||||
nfs4_alloc_client(struct in_addr *addr)
|
||||
{
|
||||
struct nfs_client *clp;
|
||||
|
||||
if (nfs_callback_up() < 0)
|
||||
return NULL;
|
||||
if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) {
|
||||
nfs_callback_down();
|
||||
return NULL;
|
||||
}
|
||||
memcpy(&clp->cl_addr, addr, sizeof(clp->cl_addr));
|
||||
init_rwsem(&clp->cl_sem);
|
||||
INIT_LIST_HEAD(&clp->cl_delegations);
|
||||
INIT_LIST_HEAD(&clp->cl_state_owners);
|
||||
INIT_LIST_HEAD(&clp->cl_unused);
|
||||
spin_lock_init(&clp->cl_lock);
|
||||
atomic_set(&clp->cl_count, 1);
|
||||
INIT_WORK(&clp->cl_renewd, nfs4_renew_state, clp);
|
||||
INIT_LIST_HEAD(&clp->cl_superblocks);
|
||||
rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS4 client");
|
||||
clp->cl_rpcclient = ERR_PTR(-EINVAL);
|
||||
clp->cl_boot_time = CURRENT_TIME;
|
||||
clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;
|
||||
return clp;
|
||||
}
|
||||
|
||||
static void
|
||||
nfs4_free_client(struct nfs_client *clp)
|
||||
{
|
||||
struct nfs4_state_owner *sp;
|
||||
|
||||
while (!list_empty(&clp->cl_unused)) {
|
||||
sp = list_entry(clp->cl_unused.next,
|
||||
struct nfs4_state_owner,
|
||||
so_list);
|
||||
list_del(&sp->so_list);
|
||||
kfree(sp);
|
||||
}
|
||||
BUG_ON(!list_empty(&clp->cl_state_owners));
|
||||
nfs_idmap_delete(clp);
|
||||
if (!IS_ERR(clp->cl_rpcclient))
|
||||
rpc_shutdown_client(clp->cl_rpcclient);
|
||||
kfree(clp);
|
||||
nfs_callback_down();
|
||||
}
|
||||
|
||||
static struct nfs_client *__nfs4_find_client(struct in_addr *addr)
|
||||
{
|
||||
struct nfs_client *clp;
|
||||
list_for_each_entry(clp, &nfs4_clientid_list, cl_servers) {
|
||||
if (memcmp(&clp->cl_addr, addr, sizeof(clp->cl_addr)) == 0) {
|
||||
atomic_inc(&clp->cl_count);
|
||||
return clp;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct nfs_client *nfs4_find_client(struct in_addr *addr)
|
||||
{
|
||||
struct nfs_client *clp;
|
||||
spin_lock(&state_spinlock);
|
||||
clp = __nfs4_find_client(addr);
|
||||
spin_unlock(&state_spinlock);
|
||||
return clp;
|
||||
}
|
||||
|
||||
struct nfs_client *
|
||||
nfs4_get_client(struct in_addr *addr)
|
||||
{
|
||||
struct nfs_client *clp, *new = NULL;
|
||||
|
||||
spin_lock(&state_spinlock);
|
||||
for (;;) {
|
||||
clp = __nfs4_find_client(addr);
|
||||
if (clp != NULL)
|
||||
break;
|
||||
clp = new;
|
||||
if (clp != NULL) {
|
||||
list_add(&clp->cl_servers, &nfs4_clientid_list);
|
||||
new = NULL;
|
||||
break;
|
||||
}
|
||||
spin_unlock(&state_spinlock);
|
||||
new = nfs4_alloc_client(addr);
|
||||
spin_lock(&state_spinlock);
|
||||
if (new == NULL)
|
||||
break;
|
||||
}
|
||||
spin_unlock(&state_spinlock);
|
||||
if (new)
|
||||
nfs4_free_client(new);
|
||||
return clp;
|
||||
}
|
||||
|
||||
void
|
||||
nfs4_put_client(struct nfs_client *clp)
|
||||
{
|
||||
if (!atomic_dec_and_lock(&clp->cl_count, &state_spinlock))
|
||||
return;
|
||||
list_del(&clp->cl_servers);
|
||||
spin_unlock(&state_spinlock);
|
||||
BUG_ON(!list_empty(&clp->cl_superblocks));
|
||||
rpc_wake_up(&clp->cl_rpcwaitq);
|
||||
nfs4_kill_renewd(clp);
|
||||
nfs4_free_client(clp);
|
||||
}
|
||||
|
||||
static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred)
|
||||
{
|
||||
int status = nfs4_proc_setclientid(clp, NFS4_CALLBACK,
|
||||
@ -771,11 +655,11 @@ static void nfs4_recover_state(struct nfs_client *clp)
|
||||
__module_get(THIS_MODULE);
|
||||
atomic_inc(&clp->cl_count);
|
||||
task = kthread_run(reclaimer, clp, "%u.%u.%u.%u-reclaim",
|
||||
NIPQUAD(clp->cl_addr));
|
||||
NIPQUAD(clp->cl_addr.sin_addr));
|
||||
if (!IS_ERR(task))
|
||||
return;
|
||||
nfs4_clear_recover_bit(clp);
|
||||
nfs4_put_client(clp);
|
||||
nfs_put_client(clp);
|
||||
module_put(THIS_MODULE);
|
||||
}
|
||||
|
||||
@ -970,12 +854,12 @@ out:
|
||||
if (status == -NFS4ERR_CB_PATH_DOWN)
|
||||
nfs_handle_cb_pathdown(clp);
|
||||
nfs4_clear_recover_bit(clp);
|
||||
nfs4_put_client(clp);
|
||||
nfs_put_client(clp);
|
||||
module_put_and_exit(0);
|
||||
return 0;
|
||||
out_error:
|
||||
printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %u.%u.%u.%u with error %d\n",
|
||||
NIPQUAD(clp->cl_addr.s_addr), -status);
|
||||
NIPQUAD(clp->cl_addr.sin_addr), -status);
|
||||
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
|
||||
goto out;
|
||||
}
|
||||
|
@ -1104,47 +1104,46 @@ static struct rpc_clnt *nfs4_create_client(struct nfs_server *server,
|
||||
struct rpc_clnt *clnt = NULL;
|
||||
int err = -EIO;
|
||||
|
||||
clp = nfs4_get_client(&server->addr.sin_addr);
|
||||
clp = nfs_get_client(server->hostname, &server->addr, 4);
|
||||
if (!clp) {
|
||||
dprintk("%s: failed to create NFS4 client.\n", __FUNCTION__);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
/* Now create transport and client */
|
||||
down_write(&clp->cl_sem);
|
||||
if (IS_ERR(clp->cl_rpcclient)) {
|
||||
if (clp->cl_cons_state == NFS_CS_INITING) {
|
||||
xprt = xprt_create_proto(proto, &server->addr, timeparms);
|
||||
if (IS_ERR(xprt)) {
|
||||
up_write(&clp->cl_sem);
|
||||
err = PTR_ERR(xprt);
|
||||
dprintk("%s: cannot create RPC transport. Error = %d\n",
|
||||
__FUNCTION__, err);
|
||||
goto out_fail;
|
||||
goto client_init_error;
|
||||
}
|
||||
/* Bind to a reserved port! */
|
||||
xprt->resvport = 1;
|
||||
clnt = rpc_create_client(xprt, server->hostname, &nfs_program,
|
||||
server->rpc_ops->version, flavor);
|
||||
if (IS_ERR(clnt)) {
|
||||
up_write(&clp->cl_sem);
|
||||
err = PTR_ERR(clnt);
|
||||
dprintk("%s: cannot create RPC client. Error = %d\n",
|
||||
__FUNCTION__, err);
|
||||
goto out_fail;
|
||||
goto client_init_error;
|
||||
}
|
||||
clnt->cl_intr = 1;
|
||||
clnt->cl_softrtry = 1;
|
||||
clp->cl_rpcclient = clnt;
|
||||
memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr));
|
||||
if (nfs_idmap_new(clp) < 0)
|
||||
goto out_fail;
|
||||
err = nfs_idmap_new(clp);
|
||||
if (err < 0) {
|
||||
dprintk("%s: failed to create idmapper.\n",
|
||||
__FUNCTION__);
|
||||
goto client_init_error;
|
||||
}
|
||||
__set_bit(NFS_CS_IDMAP, &clp->cl_res_state);
|
||||
nfs_mark_client_ready(clp, 0);
|
||||
}
|
||||
list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks);
|
||||
|
||||
clnt = rpc_clone_client(clp->cl_rpcclient);
|
||||
if (!IS_ERR(clnt))
|
||||
server->nfs_client = clp;
|
||||
up_write(&clp->cl_sem);
|
||||
clp = NULL;
|
||||
|
||||
if (IS_ERR(clnt)) {
|
||||
dprintk("%s: cannot create RPC client. Error = %d\n",
|
||||
@ -1152,11 +1151,6 @@ static struct rpc_clnt *nfs4_create_client(struct nfs_server *server,
|
||||
return clnt;
|
||||
}
|
||||
|
||||
if (server->nfs_client->cl_idmap == NULL) {
|
||||
dprintk("%s: failed to create idmapper.\n", __FUNCTION__);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
if (clnt->cl_auth->au_flavor != flavor) {
|
||||
struct rpc_auth *auth;
|
||||
|
||||
@ -1166,11 +1160,16 @@ static struct rpc_clnt *nfs4_create_client(struct nfs_server *server,
|
||||
return (struct rpc_clnt *)auth;
|
||||
}
|
||||
}
|
||||
|
||||
server->nfs_client = clp;
|
||||
down_write(&clp->cl_sem);
|
||||
list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks);
|
||||
up_write(&clp->cl_sem);
|
||||
return clnt;
|
||||
|
||||
out_fail:
|
||||
if (clp)
|
||||
nfs4_put_client(clp);
|
||||
client_init_error:
|
||||
nfs_mark_client_ready(clp, err);
|
||||
nfs_put_client(clp);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
@ -1329,14 +1328,6 @@ static int nfs4_get_sb(struct file_system_type *fs_type,
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/* Fire up rpciod if not yet running */
|
||||
error = rpciod_up();
|
||||
if (error < 0) {
|
||||
dprintk("%s: couldn't start rpciod! Error = %d\n",
|
||||
__FUNCTION__, error);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
s = sget(fs_type, nfs4_compare_super, nfs_set_super, server);
|
||||
if (IS_ERR(s)) {
|
||||
error = PTR_ERR(s);
|
||||
@ -1383,8 +1374,6 @@ static void nfs4_kill_super(struct super_block *sb)
|
||||
|
||||
destroy_nfsv4_state(server);
|
||||
|
||||
rpciod_down();
|
||||
|
||||
nfs_free_iostats(server->io_stats);
|
||||
kfree(server->hostname);
|
||||
kfree(server);
|
||||
|
@ -586,6 +586,7 @@ extern void * nfs_root_data(void);
|
||||
#define NFSDBG_FILE 0x0040
|
||||
#define NFSDBG_ROOT 0x0080
|
||||
#define NFSDBG_CALLBACK 0x0100
|
||||
#define NFSDBG_CLIENT 0x0200
|
||||
#define NFSDBG_ALL 0xFFFF
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
@ -6,6 +6,66 @@
|
||||
|
||||
struct nfs_iostats;
|
||||
|
||||
/*
|
||||
* The nfs_client identifies our client state to the server.
|
||||
*/
|
||||
struct nfs_client {
|
||||
atomic_t cl_count;
|
||||
int cl_cons_state; /* current construction state (-ve: init error) */
|
||||
#define NFS_CS_READY 0 /* ready to be used */
|
||||
#define NFS_CS_INITING 1 /* busy initialising */
|
||||
int cl_nfsversion; /* NFS protocol version */
|
||||
unsigned long cl_res_state; /* NFS resources state */
|
||||
#define NFS_CS_RPCIOD 0 /* - rpciod started */
|
||||
#define NFS_CS_CALLBACK 1 /* - callback started */
|
||||
#define NFS_CS_IDMAP 2 /* - idmap started */
|
||||
struct sockaddr_in cl_addr; /* server identifier */
|
||||
char * cl_hostname; /* hostname of server */
|
||||
struct list_head cl_share_link; /* link in global client list */
|
||||
struct list_head cl_superblocks; /* List of nfs_server structs */
|
||||
|
||||
struct rpc_clnt * cl_rpcclient;
|
||||
|
||||
#ifdef CONFIG_NFS_V4
|
||||
u64 cl_clientid; /* constant */
|
||||
nfs4_verifier cl_confirm;
|
||||
unsigned long cl_state;
|
||||
|
||||
u32 cl_lockowner_id;
|
||||
|
||||
/*
|
||||
* The following rwsem ensures exclusive access to the server
|
||||
* while we recover the state following a lease expiration.
|
||||
*/
|
||||
struct rw_semaphore cl_sem;
|
||||
|
||||
struct list_head cl_delegations;
|
||||
struct list_head cl_state_owners;
|
||||
struct list_head cl_unused;
|
||||
int cl_nunused;
|
||||
spinlock_t cl_lock;
|
||||
|
||||
unsigned long cl_lease_time;
|
||||
unsigned long cl_last_renewal;
|
||||
struct work_struct cl_renewd;
|
||||
struct work_struct cl_recoverd;
|
||||
|
||||
struct rpc_wait_queue cl_rpcwaitq;
|
||||
|
||||
/* used for the setclientid verifier */
|
||||
struct timespec cl_boot_time;
|
||||
|
||||
/* idmapper */
|
||||
struct idmap * cl_idmap;
|
||||
|
||||
/* Our own IP address, as a null-terminated string.
|
||||
* This is used to generate the clientid, and the callback address.
|
||||
*/
|
||||
char cl_ipaddr[16];
|
||||
unsigned char cl_id_uniquifier;
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* NFS client parameters stored in the superblock.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user