-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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>
- Loading branch information
David Howells
authored and
Trond Myklebust
committed
Sep 23, 2006
1 parent
e9326dc
commit 24c8dbb
Showing
12 changed files
with
425 additions
and
222 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
Oops, something went wrong.