Skip to content

Commit

Permalink
NFS: Generalise the nfs_client structure
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
David Howells authored and Trond Myklebust committed Sep 23, 2006
1 parent e9326dc commit 24c8dbb
Show file tree
Hide file tree
Showing 12 changed files with 425 additions and 222 deletions.
6 changes: 3 additions & 3 deletions fs/nfs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 5 additions & 4 deletions fs/nfs/callback.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include "nfs4_fs.h"
#include "callback.h"
#include "internal.h"

#define NFSDBG_FACILITY NFSDBG_CALLBACK

Expand Down Expand Up @@ -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)
Expand Down
9 changes: 5 additions & 4 deletions fs/nfs/callback_proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "nfs4_fs.h"
#include "callback.h"
#include "delegation.h"
#include "internal.h"

#define NFSDBG_FACILITY NFSDBG_CALLBACK

Expand All @@ -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);
Expand All @@ -48,7 +49,7 @@ unsigned nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres
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;
Expand All @@ -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);
Expand All @@ -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;
Expand Down
312 changes: 312 additions & 0 deletions fs/nfs/client.c
Original file line number Diff line number Diff line change
@@ -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);
}
Loading

0 comments on commit 24c8dbb

Please sign in to comment.