summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNeil Brown <neilb@cse.unsw.edu.au>2003-06-17 06:39:20 -0700
committerLinus Torvalds <torvalds@home.transmeta.com>2003-06-17 06:39:20 -0700
commit1ac4906cbff7aecb681b9bdf1bf23e1e5e8401b1 (patch)
treecb97f3699e554e71b887f8d0a8ace4e02dbc72a2
parent22239375e853c77598150bf64731744e57d2a477 (diff)
[PATCH] kNFSd: RENEW and lease management for NFSv4 server
From: "William A.(Andy) Adamson" <andros@citi.umich.edu> Put all clients in a LRU list and use a "work_queue" to expire old clients periodically.
-rw-r--r--fs/nfsd/nfs4proc.c6
-rw-r--r--fs/nfsd/nfs4state.c122
-rw-r--r--include/linux/nfsd/nfsd.h1
-rw-r--r--include/linux/nfsd/state.h5
4 files changed, 121 insertions, 13 deletions
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 0568af67b3f8..17728674ad08 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -147,12 +147,6 @@ nfsd4_open(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open
return 0;
}
-static inline int
-nfsd4_renew(clientid_t *clientid)
-{
- return nfs_ok;
-}
-
/*
* filehandle-manipulating ops.
*/
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index c5a87b73e8fa..7b4d3ba2c64c 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -43,6 +43,7 @@
#include <linux/nfsd/nfsd.h>
#include <linux/nfsd/cache.h>
#include <linux/mount.h>
+#include <linux/workqueue.h>
#include <linux/nfs4.h>
#include <linux/nfsd/state.h>
#include <linux/nfsd/xdr4.h>
@@ -105,11 +106,28 @@ static void release_stateid(struct nfs4_stateid *stp);
*
* unconf_str_hastbl[] and unconf_id_hashtbl[] hold unconfirmed
* setclientid info.
+ *
+ * client_lru holds client queue ordered by nfs4_client.cl_time
+ * for lease renewal.
*/
static struct list_head conf_id_hashtbl[CLIENT_HASH_SIZE];
static struct list_head conf_str_hashtbl[CLIENT_HASH_SIZE];
static struct list_head unconf_str_hashtbl[CLIENT_HASH_SIZE];
static struct list_head unconf_id_hashtbl[CLIENT_HASH_SIZE];
+static struct list_head client_lru;
+
+static inline void
+renew_client(struct nfs4_client *clp)
+{
+ /*
+ * Move client to the end to the LRU list.
+ */
+ dprintk("renewing client (clientid %08x/%08x)\n",
+ clp->cl_clientid.cl_boot,
+ clp->cl_clientid.cl_id);
+ list_move_tail(&clp->cl_lru, &client_lru);
+ clp->cl_time = get_seconds();
+}
/* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */
static int
@@ -160,6 +178,7 @@ expire_client(struct nfs4_client *clp)
dprintk("NFSD: expire_client\n");
list_del(&clp->cl_idhash);
list_del(&clp->cl_strhash);
+ list_del(&clp->cl_lru);
while (!list_empty(&clp->cl_perclient)) {
sop = list_entry(clp->cl_perclient.next, struct nfs4_stateowner, so_perclient);
release_stateowner(sop);
@@ -176,6 +195,7 @@ create_client(struct xdr_netobj name) {
INIT_LIST_HEAD(&clp->cl_idhash);
INIT_LIST_HEAD(&clp->cl_strhash);
INIT_LIST_HEAD(&clp->cl_perclient);
+ INIT_LIST_HEAD(&clp->cl_lru);
out:
return clp;
}
@@ -264,6 +284,8 @@ add_to_unconfirmed(struct nfs4_client *clp, unsigned int strhashval)
list_add(&clp->cl_strhash, &unconf_str_hashtbl[strhashval]);
idhashval = clientid_hashval(clp->cl_clientid.cl_id);
list_add(&clp->cl_idhash, &unconf_id_hashtbl[idhashval]);
+ list_add_tail(&clp->cl_lru, &client_lru);
+ clp->cl_time = get_seconds();
}
void
@@ -271,13 +293,13 @@ move_to_confirmed(struct nfs4_client *clp, unsigned int idhashval)
{
unsigned int strhashval;
- printk("ANDROS: move_to_confirm nfs4_client %p\n", clp);
list_del_init(&clp->cl_strhash);
list_del_init(&clp->cl_idhash);
list_add(&clp->cl_idhash, &conf_id_hashtbl[idhashval]);
strhashval = clientstr_hashval(clp->cl_name.data,
clp->cl_name.len);
list_add(&clp->cl_strhash, &conf_str_hashtbl[strhashval]);
+ renew_client(clp);
}
/*
@@ -940,9 +962,7 @@ instantiate_new_owner:
open->op_stateowner = sop;
status = nfs_ok;
renew:
- /* XXX implement LRU and state recovery thread
- * renew will place nfs4_client at end of LRU
- */
+ renew_client(sop->so_client);
out:
up(&client_sema); /*XXX need finer grained locking */
return status;
@@ -1048,11 +1068,94 @@ out_free:
kfree(stp);
goto out;
}
+static struct work_struct laundromat_work;
+static void laundromat_main(void *);
+static DECLARE_WORK(laundromat_work, laundromat_main, NULL);
+
+int
+nfsd4_renew(clientid_t *clid)
+{
+ struct nfs4_client *clp;
+ struct list_head *pos, *next;
+ unsigned int idhashval;
+ int status;
+
+ down(&client_sema);
+ printk("process_renew(%08x/%08x): starting\n",
+ clid->cl_boot, clid->cl_id);
+ status = nfserr_stale_clientid;
+ if (STALE_CLIENTID(clid))
+ goto out;
+ status = nfs_ok;
+ idhashval = clientid_hashval(clid->cl_id);
+ list_for_each_safe(pos, next, &conf_id_hashtbl[idhashval]) {
+ clp = list_entry(pos, struct nfs4_client, cl_idhash);
+ if (!cmp_clid(&clp->cl_clientid, clid))
+ continue;
+ renew_client(clp);
+ goto out;
+ }
+ list_for_each_safe(pos, next, &unconf_id_hashtbl[idhashval]) {
+ clp = list_entry(pos, struct nfs4_client, cl_idhash);
+ if (!cmp_clid(&clp->cl_clientid, clid))
+ continue;
+ renew_client(clp);
+ goto out;
+ }
+ /*
+ * Couldn't find an nfs4_client for this clientid.
+ * Presumably this is because the client took too long to
+ * RENEW, so return NFS4ERR_EXPIRED.
+ */
+ printk("nfsd4_renew: clientid not found!\n");
+ status = nfserr_expired;
+out:
+ up(&client_sema);
+ return status;
+}
+
+time_t
+nfs4_laundromat(void)
+{
+ struct nfs4_client *clp;
+ struct list_head *pos, *next;
+ time_t cutoff = get_seconds() - NFSD_LEASE_TIME;
+ time_t t, return_val = NFSD_LEASE_TIME;
+
+ down(&client_sema);
+
+ dprintk("NFSD: laundromat service - starting, examining clients\n");
+ list_for_each_safe(pos, next, &client_lru) {
+ clp = list_entry(pos, struct nfs4_client, cl_lru);
+ if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) {
+ t = clp->cl_time - cutoff;
+ if (return_val > t)
+ return_val = t;
+ break;
+ }
+ dprintk("NFSD: purging unused client (clientid %08x)\n",
+ clp->cl_clientid.cl_id);
+ expire_client(clp);
+ }
+ if (return_val < NFSD_LAUNDROMAT_MINTIMEOUT)
+ return_val = NFSD_LAUNDROMAT_MINTIMEOUT;
+ up(&client_sema);
+ return return_val;
+}
+
+void
+laundromat_main(void *not_used)
+{
+ time_t t;
+
+ t = nfs4_laundromat();
+ dprintk("NFSD: laundromat_main - sleeping for %ld seconds\n", t);
+ schedule_delayed_work(&laundromat_work, t*HZ);
+}
void
nfs4_state_init(void)
{
- struct timespec tv;
int i;
for (i = 0; i < CLIENT_HASH_SIZE; i++) {
@@ -1067,9 +1170,12 @@ nfs4_state_init(void)
for (i = 0; i < OWNER_HASH_SIZE; i++) {
INIT_LIST_HEAD(&ownerstr_hashtbl[i]);
}
+ INIT_LIST_HEAD(&client_lru);
init_MUTEX(&client_sema);
- tv = CURRENT_TIME;
- boot_time = tv.tv_sec;
+ boot_time = get_seconds();
+ INIT_WORK(&laundromat_work,laundromat_main, NULL);
+ schedule_delayed_work(&laundromat_work, NFSD_LEASE_TIME*HZ);
+
}
static void
@@ -1089,6 +1195,8 @@ __nfs4_state_shutdown(void)
}
}
release_all_files();
+ cancel_delayed_work(&laundromat_work);
+ flush_scheduled_work();
dprintk("NFSD: list_add_perfile %d list_del_perfile %d\n",
list_add_perfile, list_del_perfile);
dprintk("NFSD: add_perclient %d del_perclient %d\n",
diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h
index 745e759912d4..6c936cc67ec1 100644
--- a/include/linux/nfsd/nfsd.h
+++ b/include/linux/nfsd/nfsd.h
@@ -228,6 +228,7 @@ extern struct timeval nfssvc_boot;
#define COMPOUND_ERR_SLACK_SPACE 12 /* OP_SETATTR */
#define NFSD_LEASE_TIME 60 /* seconds */
+#define NFSD_LAUNDROMAT_MINTIMEOUT 10 /* seconds */
/*
* The following attributes are currently not supported by the NFSv4 server:
diff --git a/include/linux/nfsd/state.h b/include/linux/nfsd/state.h
index 11327bf38604..c9d6de8d3502 100644
--- a/include/linux/nfsd/state.h
+++ b/include/linux/nfsd/state.h
@@ -73,14 +73,19 @@ struct nfs4_client {
struct list_head cl_idhash; /* hash by cl_clientid.id */
struct list_head cl_strhash; /* hash by cl_name */
struct list_head cl_perclient; /* list: stateowners */
+ struct list_head cl_lru; /* tail queue */
struct xdr_netobj cl_name; /* id generated by client */
nfs4_verifier cl_verifier; /* generated by client */
+ time_t cl_time; /* time of last lease renewal */
u32 cl_addr; /* client ipaddress */
struct svc_cred cl_cred; /* setclientid principal */
clientid_t cl_clientid; /* generated by server */
nfs4_verifier cl_confirm; /* generated by server */
};
+extern time_t nfs4_laundromat(void);
+int nfsd4_renew(clientid_t *clid);
+
static inline void
update_stateid(stateid_t *stateid)
{