Commit 1ac4906c authored by Neil Brown's avatar Neil Brown Committed by Linus Torvalds

[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.
parent 22239375
...@@ -147,12 +147,6 @@ nfsd4_open(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open ...@@ -147,12 +147,6 @@ nfsd4_open(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open
return 0; return 0;
} }
static inline int
nfsd4_renew(clientid_t *clientid)
{
return nfs_ok;
}
/* /*
* filehandle-manipulating ops. * filehandle-manipulating ops.
*/ */
......
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
#include <linux/nfsd/nfsd.h> #include <linux/nfsd/nfsd.h>
#include <linux/nfsd/cache.h> #include <linux/nfsd/cache.h>
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/workqueue.h>
#include <linux/nfs4.h> #include <linux/nfs4.h>
#include <linux/nfsd/state.h> #include <linux/nfsd/state.h>
#include <linux/nfsd/xdr4.h> #include <linux/nfsd/xdr4.h>
...@@ -105,11 +106,28 @@ static void release_stateid(struct nfs4_stateid *stp); ...@@ -105,11 +106,28 @@ static void release_stateid(struct nfs4_stateid *stp);
* *
* unconf_str_hastbl[] and unconf_id_hashtbl[] hold unconfirmed * unconf_str_hastbl[] and unconf_id_hashtbl[] hold unconfirmed
* setclientid info. * 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_id_hashtbl[CLIENT_HASH_SIZE];
static struct list_head conf_str_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_str_hashtbl[CLIENT_HASH_SIZE];
static struct list_head unconf_id_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 */ /* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */
static int static int
...@@ -160,6 +178,7 @@ expire_client(struct nfs4_client *clp) ...@@ -160,6 +178,7 @@ expire_client(struct nfs4_client *clp)
dprintk("NFSD: expire_client\n"); dprintk("NFSD: expire_client\n");
list_del(&clp->cl_idhash); list_del(&clp->cl_idhash);
list_del(&clp->cl_strhash); list_del(&clp->cl_strhash);
list_del(&clp->cl_lru);
while (!list_empty(&clp->cl_perclient)) { while (!list_empty(&clp->cl_perclient)) {
sop = list_entry(clp->cl_perclient.next, struct nfs4_stateowner, so_perclient); sop = list_entry(clp->cl_perclient.next, struct nfs4_stateowner, so_perclient);
release_stateowner(sop); release_stateowner(sop);
...@@ -176,6 +195,7 @@ create_client(struct xdr_netobj name) { ...@@ -176,6 +195,7 @@ create_client(struct xdr_netobj name) {
INIT_LIST_HEAD(&clp->cl_idhash); INIT_LIST_HEAD(&clp->cl_idhash);
INIT_LIST_HEAD(&clp->cl_strhash); INIT_LIST_HEAD(&clp->cl_strhash);
INIT_LIST_HEAD(&clp->cl_perclient); INIT_LIST_HEAD(&clp->cl_perclient);
INIT_LIST_HEAD(&clp->cl_lru);
out: out:
return clp; return clp;
} }
...@@ -264,6 +284,8 @@ add_to_unconfirmed(struct nfs4_client *clp, unsigned int strhashval) ...@@ -264,6 +284,8 @@ add_to_unconfirmed(struct nfs4_client *clp, unsigned int strhashval)
list_add(&clp->cl_strhash, &unconf_str_hashtbl[strhashval]); list_add(&clp->cl_strhash, &unconf_str_hashtbl[strhashval]);
idhashval = clientid_hashval(clp->cl_clientid.cl_id); idhashval = clientid_hashval(clp->cl_clientid.cl_id);
list_add(&clp->cl_idhash, &unconf_id_hashtbl[idhashval]); list_add(&clp->cl_idhash, &unconf_id_hashtbl[idhashval]);
list_add_tail(&clp->cl_lru, &client_lru);
clp->cl_time = get_seconds();
} }
void void
...@@ -271,13 +293,13 @@ move_to_confirmed(struct nfs4_client *clp, unsigned int idhashval) ...@@ -271,13 +293,13 @@ move_to_confirmed(struct nfs4_client *clp, unsigned int idhashval)
{ {
unsigned int strhashval; unsigned int strhashval;
printk("ANDROS: move_to_confirm nfs4_client %p\n", clp);
list_del_init(&clp->cl_strhash); list_del_init(&clp->cl_strhash);
list_del_init(&clp->cl_idhash); list_del_init(&clp->cl_idhash);
list_add(&clp->cl_idhash, &conf_id_hashtbl[idhashval]); list_add(&clp->cl_idhash, &conf_id_hashtbl[idhashval]);
strhashval = clientstr_hashval(clp->cl_name.data, strhashval = clientstr_hashval(clp->cl_name.data,
clp->cl_name.len); clp->cl_name.len);
list_add(&clp->cl_strhash, &conf_str_hashtbl[strhashval]); list_add(&clp->cl_strhash, &conf_str_hashtbl[strhashval]);
renew_client(clp);
} }
/* /*
...@@ -940,9 +962,7 @@ nfsd4_process_open1(struct nfsd4_open *open) ...@@ -940,9 +962,7 @@ nfsd4_process_open1(struct nfsd4_open *open)
open->op_stateowner = sop; open->op_stateowner = sop;
status = nfs_ok; status = nfs_ok;
renew: renew:
/* XXX implement LRU and state recovery thread renew_client(sop->so_client);
* renew will place nfs4_client at end of LRU
*/
out: out:
up(&client_sema); /*XXX need finer grained locking */ up(&client_sema); /*XXX need finer grained locking */
return status; return status;
...@@ -1048,11 +1068,94 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf ...@@ -1048,11 +1068,94 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
kfree(stp); kfree(stp);
goto out; 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 void
nfs4_state_init(void) nfs4_state_init(void)
{ {
struct timespec tv;
int i; int i;
for (i = 0; i < CLIENT_HASH_SIZE; i++) { for (i = 0; i < CLIENT_HASH_SIZE; i++) {
...@@ -1067,9 +1170,12 @@ nfs4_state_init(void) ...@@ -1067,9 +1170,12 @@ nfs4_state_init(void)
for (i = 0; i < OWNER_HASH_SIZE; i++) { for (i = 0; i < OWNER_HASH_SIZE; i++) {
INIT_LIST_HEAD(&ownerstr_hashtbl[i]); INIT_LIST_HEAD(&ownerstr_hashtbl[i]);
} }
INIT_LIST_HEAD(&client_lru);
init_MUTEX(&client_sema); init_MUTEX(&client_sema);
tv = CURRENT_TIME; boot_time = get_seconds();
boot_time = tv.tv_sec; INIT_WORK(&laundromat_work,laundromat_main, NULL);
schedule_delayed_work(&laundromat_work, NFSD_LEASE_TIME*HZ);
} }
static void static void
...@@ -1089,6 +1195,8 @@ __nfs4_state_shutdown(void) ...@@ -1089,6 +1195,8 @@ __nfs4_state_shutdown(void)
} }
} }
release_all_files(); release_all_files();
cancel_delayed_work(&laundromat_work);
flush_scheduled_work();
dprintk("NFSD: list_add_perfile %d list_del_perfile %d\n", dprintk("NFSD: list_add_perfile %d list_del_perfile %d\n",
list_add_perfile, list_del_perfile); list_add_perfile, list_del_perfile);
dprintk("NFSD: add_perclient %d del_perclient %d\n", dprintk("NFSD: add_perclient %d del_perclient %d\n",
......
...@@ -228,6 +228,7 @@ extern struct timeval nfssvc_boot; ...@@ -228,6 +228,7 @@ extern struct timeval nfssvc_boot;
#define COMPOUND_ERR_SLACK_SPACE 12 /* OP_SETATTR */ #define COMPOUND_ERR_SLACK_SPACE 12 /* OP_SETATTR */
#define NFSD_LEASE_TIME 60 /* seconds */ #define NFSD_LEASE_TIME 60 /* seconds */
#define NFSD_LAUNDROMAT_MINTIMEOUT 10 /* seconds */
/* /*
* The following attributes are currently not supported by the NFSv4 server: * The following attributes are currently not supported by the NFSv4 server:
......
...@@ -73,14 +73,19 @@ struct nfs4_client { ...@@ -73,14 +73,19 @@ struct nfs4_client {
struct list_head cl_idhash; /* hash by cl_clientid.id */ struct list_head cl_idhash; /* hash by cl_clientid.id */
struct list_head cl_strhash; /* hash by cl_name */ struct list_head cl_strhash; /* hash by cl_name */
struct list_head cl_perclient; /* list: stateowners */ struct list_head cl_perclient; /* list: stateowners */
struct list_head cl_lru; /* tail queue */
struct xdr_netobj cl_name; /* id generated by client */ struct xdr_netobj cl_name; /* id generated by client */
nfs4_verifier cl_verifier; /* generated by client */ nfs4_verifier cl_verifier; /* generated by client */
time_t cl_time; /* time of last lease renewal */
u32 cl_addr; /* client ipaddress */ u32 cl_addr; /* client ipaddress */
struct svc_cred cl_cred; /* setclientid principal */ struct svc_cred cl_cred; /* setclientid principal */
clientid_t cl_clientid; /* generated by server */ clientid_t cl_clientid; /* generated by server */
nfs4_verifier cl_confirm; /* 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 static inline void
update_stateid(stateid_t *stateid) update_stateid(stateid_t *stateid)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment