Commit 599692fc authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] kNFSdv4: Keep state to allow replays for 'close' to work.

From: NeilBrown <neilb@cse.unsw.edu.au>

From: "J. Bruce Fields" <bfields@fieldses.org>

From: Andros: Idea is to keep around a list of openowners recently released
by closes, and make sure they stay around long enough so that replays still
work.
parent 020e39c5
...@@ -136,12 +136,16 @@ static void release_file(struct nfs4_file *fp); ...@@ -136,12 +136,16 @@ static void release_file(struct nfs4_file *fp);
* *
* client_lru holds client queue ordered by nfs4_client.cl_time * client_lru holds client queue ordered by nfs4_client.cl_time
* for lease renewal. * for lease renewal.
*
* close_lru holds (open) stateowner queue ordered by nfs4_stateowner.so_time
* for last close replay.
*/ */
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 struct list_head client_lru;
static struct list_head close_lru;
static inline void static inline void
renew_client(struct nfs4_client *clp) renew_client(struct nfs4_client *clp)
...@@ -376,7 +380,6 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid) ...@@ -376,7 +380,6 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid)
unsigned int strhashval; unsigned int strhashval;
struct nfs4_client * conf, * unconf, * new, * clp; struct nfs4_client * conf, * unconf, * new, * clp;
int status; int status;
struct list_head *pos, *next;
status = nfserr_inval; status = nfserr_inval;
if (!check_name(clname)) if (!check_name(clname))
...@@ -391,8 +394,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid) ...@@ -391,8 +394,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid)
conf = NULL; conf = NULL;
nfs4_lock_state(); nfs4_lock_state();
list_for_each_safe(pos, next, &conf_str_hashtbl[strhashval]) { list_for_each_entry(clp, &conf_str_hashtbl[strhashval], cl_strhash) {
clp = list_entry(pos, struct nfs4_client, cl_strhash);
if (!cmp_name(&clp->cl_name, &clname)) if (!cmp_name(&clp->cl_name, &clname))
continue; continue;
/* /*
...@@ -422,8 +424,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid) ...@@ -422,8 +424,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_setclientid *setclid)
break; break;
} }
unconf = NULL; unconf = NULL;
list_for_each_safe(pos, next, &unconf_str_hashtbl[strhashval]) { list_for_each_entry(clp, &unconf_str_hashtbl[strhashval], cl_strhash) {
clp = list_entry(pos, struct nfs4_client, cl_strhash);
if (!cmp_name(&clp->cl_name, &clname)) if (!cmp_name(&clp->cl_name, &clname))
continue; continue;
/* cl_name match from a previous SETCLIENTID operation */ /* cl_name match from a previous SETCLIENTID operation */
...@@ -549,7 +550,6 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, struct nfsd4_setclientid_confi ...@@ -549,7 +550,6 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, struct nfsd4_setclientid_confi
struct nfs4_client *clp, *conf = NULL, *unconf = NULL; struct nfs4_client *clp, *conf = NULL, *unconf = NULL;
nfs4_verifier confirm = setclientid_confirm->sc_confirm; nfs4_verifier confirm = setclientid_confirm->sc_confirm;
clientid_t * clid = &setclientid_confirm->sc_clientid; clientid_t * clid = &setclientid_confirm->sc_clientid;
struct list_head *pos, *next;
int status; int status;
status = nfserr_stale_clientid; status = nfserr_stale_clientid;
...@@ -562,8 +562,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, struct nfsd4_setclientid_confi ...@@ -562,8 +562,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, struct nfsd4_setclientid_confi
idhashval = clientid_hashval(clid->cl_id); idhashval = clientid_hashval(clid->cl_id);
nfs4_lock_state(); nfs4_lock_state();
list_for_each_safe(pos, next, &conf_id_hashtbl[idhashval]) { list_for_each_entry(clp, &conf_id_hashtbl[idhashval], cl_idhash) {
clp = list_entry(pos, struct nfs4_client, cl_idhash);
if (!cmp_clid(&clp->cl_clientid, clid)) if (!cmp_clid(&clp->cl_clientid, clid))
continue; continue;
...@@ -582,8 +581,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, struct nfsd4_setclientid_confi ...@@ -582,8 +581,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, struct nfsd4_setclientid_confi
conf = clp; conf = clp;
break; break;
} }
list_for_each_safe(pos, next, &unconf_id_hashtbl[idhashval]) { list_for_each_entry(clp, &unconf_id_hashtbl[idhashval], cl_idhash) {
clp = list_entry(pos, struct nfs4_client, cl_idhash);
if (!cmp_clid(&clp->cl_clientid, clid)) if (!cmp_clid(&clp->cl_clientid, clid))
continue; continue;
status = nfserr_inval; status = nfserr_inval;
...@@ -774,6 +772,8 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str ...@@ -774,6 +772,8 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str
INIT_LIST_HEAD(&sop->so_perclient); INIT_LIST_HEAD(&sop->so_perclient);
INIT_LIST_HEAD(&sop->so_perfilestate); INIT_LIST_HEAD(&sop->so_perfilestate);
INIT_LIST_HEAD(&sop->so_perlockowner); /* not used */ INIT_LIST_HEAD(&sop->so_perlockowner); /* not used */
INIT_LIST_HEAD(&sop->so_close_lru);
sop->so_time = 0;
list_add(&sop->so_idhash, &ownerid_hashtbl[idhashval]); list_add(&sop->so_idhash, &ownerid_hashtbl[idhashval]);
list_add(&sop->so_strhash, &ownerstr_hashtbl[strhashval]); list_add(&sop->so_strhash, &ownerstr_hashtbl[strhashval]);
list_add(&sop->so_perclient, &clp->cl_perclient); list_add(&sop->so_perclient, &clp->cl_perclient);
...@@ -814,6 +814,7 @@ release_stateowner(struct nfs4_stateowner *sop) ...@@ -814,6 +814,7 @@ release_stateowner(struct nfs4_stateowner *sop)
list_del(&sop->so_strhash); list_del(&sop->so_strhash);
list_del(&sop->so_perclient); list_del(&sop->so_perclient);
list_del(&sop->so_perlockowner); list_del(&sop->so_perlockowner);
list_del(&sop->so_close_lru);
del_perclient++; del_perclient++;
while (!list_empty(&sop->so_perfilestate)) { while (!list_empty(&sop->so_perfilestate)) {
stp = list_entry(sop->so_perfilestate.next, stp = list_entry(sop->so_perfilestate.next,
...@@ -881,6 +882,19 @@ release_file(struct nfs4_file *fp) ...@@ -881,6 +882,19 @@ release_file(struct nfs4_file *fp)
kfree(fp); kfree(fp);
} }
void
move_to_close_lru(struct nfs4_stateowner *sop)
{
dprintk("NFSD: move_to_close_lru nfs4_stateowner %p\n", sop);
/* remove stateowner from all other hash lists except perclient */
list_del_init(&sop->so_idhash);
list_del_init(&sop->so_strhash);
list_del_init(&sop->so_perlockowner);
list_add_tail(&sop->so_close_lru, &close_lru);
sop->so_time = get_seconds();
}
void void
release_state_owner(struct nfs4_stateid *stp, struct nfs4_stateowner **sopp, release_state_owner(struct nfs4_stateid *stp, struct nfs4_stateowner **sopp,
int flag) int flag)
...@@ -890,16 +904,13 @@ release_state_owner(struct nfs4_stateid *stp, struct nfs4_stateowner **sopp, ...@@ -890,16 +904,13 @@ release_state_owner(struct nfs4_stateid *stp, struct nfs4_stateowner **sopp,
dprintk("NFSD: release_state_owner\n"); dprintk("NFSD: release_state_owner\n");
release_stateid(stp, flag); release_stateid(stp, flag);
/*
* release unused nfs4_stateowners. /* place unused nfs4_stateowners on so_close_lru list to be
* XXX will need to be placed on an open_stateid_lru list to be
* released by the laundromat service after the lease period * released by the laundromat service after the lease period
* to enable us to handle CLOSE replay * to enable us to handle CLOSE replay
*/ */
if (sop->so_confirmed && list_empty(&sop->so_perfilestate)) { if (sop->so_confirmed && list_empty(&sop->so_perfilestate))
release_stateowner(sop); move_to_close_lru(sop);
*sopp = NULL;
}
/* unused nfs4_file's are releseed. XXX slab cache? */ /* unused nfs4_file's are releseed. XXX slab cache? */
if (list_empty(&fp->fi_perfile)) { if (list_empty(&fp->fi_perfile)) {
release_file(fp); release_file(fp);
...@@ -916,11 +927,9 @@ cmp_owner_str(struct nfs4_stateowner *sop, struct xdr_netobj *owner, clientid_t ...@@ -916,11 +927,9 @@ cmp_owner_str(struct nfs4_stateowner *sop, struct xdr_netobj *owner, clientid_t
/* search ownerstr_hashtbl[] for owner */ /* search ownerstr_hashtbl[] for owner */
static int static int
find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open, struct nfs4_stateowner **op) { find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open, struct nfs4_stateowner **op) {
struct list_head *pos, *next;
struct nfs4_stateowner *local = NULL; struct nfs4_stateowner *local = NULL;
list_for_each_safe(pos, next, &ownerstr_hashtbl[hashval]) { list_for_each_entry(local, &ownerstr_hashtbl[hashval], so_strhash) {
local = list_entry(pos, struct nfs4_stateowner, so_strhash);
if(!cmp_owner_str(local, &open->op_owner, &open->op_clientid)) if(!cmp_owner_str(local, &open->op_owner, &open->op_clientid))
continue; continue;
*op = local; *op = local;
...@@ -933,12 +942,10 @@ find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open, struct nf ...@@ -933,12 +942,10 @@ find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open, struct nf
static int static int
verify_clientid(struct nfs4_client **client, clientid_t *clid) { verify_clientid(struct nfs4_client **client, clientid_t *clid) {
struct list_head *pos, *next;
struct nfs4_client *clp; struct nfs4_client *clp;
unsigned int idhashval = clientid_hashval(clid->cl_id); unsigned int idhashval = clientid_hashval(clid->cl_id);
list_for_each_safe(pos, next, &conf_id_hashtbl[idhashval]) { list_for_each_entry(clp, &conf_id_hashtbl[idhashval], cl_idhash) {
clp = list_entry(pos, struct nfs4_client, cl_idhash);
if (!cmp_clid(&clp->cl_clientid, clid)) if (!cmp_clid(&clp->cl_clientid, clid))
continue; continue;
*client = clp; *client = clp;
...@@ -951,11 +958,9 @@ verify_clientid(struct nfs4_client **client, clientid_t *clid) { ...@@ -951,11 +958,9 @@ verify_clientid(struct nfs4_client **client, clientid_t *clid) {
/* search file_hashtbl[] for file */ /* search file_hashtbl[] for file */
static int static int
find_file(unsigned int hashval, struct inode *ino, struct nfs4_file **fp) { find_file(unsigned int hashval, struct inode *ino, struct nfs4_file **fp) {
struct list_head *pos, *next;
struct nfs4_file *local = NULL; struct nfs4_file *local = NULL;
list_for_each_safe(pos, next, &file_hashtbl[hashval]) { list_for_each_entry(local, &file_hashtbl[hashval], fi_hash) {
local = list_entry(pos, struct nfs4_file, fi_hash);
if (local->fi_inode == ino) { if (local->fi_inode == ino) {
*fp = local; *fp = local;
return(1); return(1);
...@@ -1011,15 +1016,13 @@ nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type) ...@@ -1011,15 +1016,13 @@ nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type)
unsigned int fi_hashval; unsigned int fi_hashval;
struct nfs4_file *fp; struct nfs4_file *fp;
struct nfs4_stateid *stp; struct nfs4_stateid *stp;
struct list_head *pos, *next;
dprintk("NFSD: nfs4_share_conflict\n"); dprintk("NFSD: nfs4_share_conflict\n");
fi_hashval = file_hashval(ino); fi_hashval = file_hashval(ino);
if (find_file(fi_hashval, ino, &fp)) { if (find_file(fi_hashval, ino, &fp)) {
/* Search for conflicting share reservations */ /* Search for conflicting share reservations */
list_for_each_safe(pos, next, &fp->fi_perfile) { list_for_each_entry(stp, &fp->fi_perfile, st_perfile) {
stp = list_entry(pos, struct nfs4_stateid, st_perfile);
if (test_bit(deny_type, &stp->st_deny_bmap) || if (test_bit(deny_type, &stp->st_deny_bmap) ||
test_bit(NFS4_SHARE_DENY_BOTH, &stp->st_deny_bmap)) test_bit(NFS4_SHARE_DENY_BOTH, &stp->st_deny_bmap))
return nfserr_share_denied; return nfserr_share_denied;
...@@ -1154,7 +1157,6 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf ...@@ -1154,7 +1157,6 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
struct nfs4_file *fp; struct nfs4_file *fp;
struct inode *ino; struct inode *ino;
unsigned int fi_hashval; unsigned int fi_hashval;
struct list_head *pos, *next;
struct nfs4_stateid *stq, *stp = NULL; struct nfs4_stateid *stq, *stp = NULL;
int status; int status;
...@@ -1173,8 +1175,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf ...@@ -1173,8 +1175,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
if (find_file(fi_hashval, ino, &fp)) { if (find_file(fi_hashval, ino, &fp)) {
/* Search for conflicting share reservations */ /* Search for conflicting share reservations */
status = nfserr_share_denied; status = nfserr_share_denied;
list_for_each_safe(pos, next, &fp->fi_perfile) { list_for_each_entry(stq, &fp->fi_perfile, st_perfile) {
stq = list_entry(pos, struct nfs4_stateid, st_perfile);
if(stq->st_stateowner == sop) { if(stq->st_stateowner == sop) {
stp = stq; stp = stq;
continue; continue;
...@@ -1274,7 +1275,6 @@ int ...@@ -1274,7 +1275,6 @@ int
nfsd4_renew(clientid_t *clid) nfsd4_renew(clientid_t *clid)
{ {
struct nfs4_client *clp; struct nfs4_client *clp;
struct list_head *pos, *next;
unsigned int idhashval; unsigned int idhashval;
int status; int status;
...@@ -1286,15 +1286,13 @@ nfsd4_renew(clientid_t *clid) ...@@ -1286,15 +1286,13 @@ nfsd4_renew(clientid_t *clid)
goto out; goto out;
status = nfs_ok; status = nfs_ok;
idhashval = clientid_hashval(clid->cl_id); idhashval = clientid_hashval(clid->cl_id);
list_for_each_safe(pos, next, &conf_id_hashtbl[idhashval]) { list_for_each_entry(clp, &conf_id_hashtbl[idhashval], cl_idhash) {
clp = list_entry(pos, struct nfs4_client, cl_idhash);
if (!cmp_clid(&clp->cl_clientid, clid)) if (!cmp_clid(&clp->cl_clientid, clid))
continue; continue;
renew_client(clp); renew_client(clp);
goto out; goto out;
} }
list_for_each_safe(pos, next, &unconf_id_hashtbl[idhashval]) { list_for_each_entry(clp, &unconf_id_hashtbl[idhashval], cl_idhash) {
clp = list_entry(pos, struct nfs4_client, cl_idhash);
if (!cmp_clid(&clp->cl_clientid, clid)) if (!cmp_clid(&clp->cl_clientid, clid))
continue; continue;
renew_client(clp); renew_client(clp);
...@@ -1316,9 +1314,11 @@ time_t ...@@ -1316,9 +1314,11 @@ time_t
nfs4_laundromat(void) nfs4_laundromat(void)
{ {
struct nfs4_client *clp; struct nfs4_client *clp;
struct nfs4_stateowner *sop;
struct list_head *pos, *next; struct list_head *pos, *next;
time_t cutoff = get_seconds() - NFSD_LEASE_TIME; time_t cutoff = get_seconds() - NFSD_LEASE_TIME;
time_t t, return_val = NFSD_LEASE_TIME; time_t t, clientid_val = NFSD_LEASE_TIME;
time_t u, close_val = NFSD_LEASE_TIME;
nfs4_lock_state(); nfs4_lock_state();
...@@ -1327,18 +1327,30 @@ nfs4_laundromat(void) ...@@ -1327,18 +1327,30 @@ nfs4_laundromat(void)
clp = list_entry(pos, struct nfs4_client, cl_lru); clp = list_entry(pos, struct nfs4_client, cl_lru);
if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) { if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) {
t = clp->cl_time - cutoff; t = clp->cl_time - cutoff;
if (return_val > t) if (clientid_val > t)
return_val = t; clientid_val = t;
break; break;
} }
dprintk("NFSD: purging unused client (clientid %08x)\n", dprintk("NFSD: purging unused client (clientid %08x)\n",
clp->cl_clientid.cl_id); clp->cl_clientid.cl_id);
expire_client(clp); expire_client(clp);
} }
if (return_val < NFSD_LAUNDROMAT_MINTIMEOUT) list_for_each_safe(pos, next, &close_lru) {
return_val = NFSD_LAUNDROMAT_MINTIMEOUT; sop = list_entry(pos, struct nfs4_stateowner, so_close_lru);
if (time_after((unsigned long)sop->so_time, (unsigned long)cutoff)) {
u = sop->so_time - cutoff;
if (close_val > u)
close_val = u;
break;
}
dprintk("NFSD: purging unused open stateowner (so_id %d)\n",
sop->so_id);
release_stateowner(sop);
}
if (clientid_val < NFSD_LAUNDROMAT_MINTIMEOUT)
clientid_val = NFSD_LAUNDROMAT_MINTIMEOUT;
nfs4_unlock_state(); nfs4_unlock_state();
return return_val; return clientid_val;
} }
void void
...@@ -1351,18 +1363,20 @@ laundromat_main(void *not_used) ...@@ -1351,18 +1363,20 @@ laundromat_main(void *not_used)
schedule_delayed_work(&laundromat_work, t*HZ); schedule_delayed_work(&laundromat_work, t*HZ);
} }
/* search ownerid_hashtbl[] for stateid owner (stateid->si_stateownerid) */ /* search ownerid_hashtbl[] and close_lru for stateid owner
* (stateid->si_stateownerid)
*/
struct nfs4_stateowner * struct nfs4_stateowner *
find_openstateowner_id(u32 st_id) { find_openstateowner_id(u32 st_id, int flags) {
struct list_head *pos, *next;
struct nfs4_stateowner *local = NULL; struct nfs4_stateowner *local = NULL;
unsigned int hashval = ownerid_hashval(st_id);
list_for_each_safe(pos, next, &ownerid_hashtbl[hashval]) { dprintk("NFSD: find_openstateowner_id %d\n", st_id);
local = list_entry(pos, struct nfs4_stateowner, so_idhash); if (flags & CLOSE_STATE) {
list_for_each_entry(local, &close_lru, so_close_lru) {
if(local->so_id == st_id) if(local->so_id == st_id)
return local; return local;
} }
}
return NULL; return NULL;
} }
...@@ -1547,11 +1561,12 @@ nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *statei ...@@ -1547,11 +1561,12 @@ nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *statei
* starting by trying to look up the stateowner. * starting by trying to look up the stateowner.
* If stateowner is not found - stateid is bad. * If stateowner is not found - stateid is bad.
*/ */
if (!(sop = find_openstateowner_id(stateid->si_stateownerid))) { if (!(sop = find_openstateowner_id(stateid->si_stateownerid, flags))) {
printk("NFSD: preprocess_seqid_op: no stateowner or nfs4_stateid!\n"); printk("NFSD: preprocess_seqid_op: no stateowner or nfs4_stateid!\n");
status = nfserr_bad_stateid; status = nfserr_bad_stateid;
goto out; goto out;
} }
*sopp = sop;
check_replay: check_replay:
if (seqid == sop->so_seqid) { if (seqid == sop->so_seqid) {
...@@ -1690,9 +1705,10 @@ nfsd4_close(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_clos ...@@ -1690,9 +1705,10 @@ nfsd4_close(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_clos
current_fh->fh_dentry->d_name.name); current_fh->fh_dentry->d_name.name);
nfs4_lock_state(); nfs4_lock_state();
/* check close_lru for replay */
if ((status = nfs4_preprocess_seqid_op(current_fh, close->cl_seqid, if ((status = nfs4_preprocess_seqid_op(current_fh, close->cl_seqid,
&close->cl_stateid, &close->cl_stateid,
CHECK_FH | OPEN_STATE, CHECK_FH | OPEN_STATE | CLOSE_STATE,
&close->cl_stateowner, &stp, NULL))) &close->cl_stateowner, &stp, NULL)))
goto out; goto out;
/* /*
...@@ -1729,7 +1745,6 @@ static struct list_head lockstateid_hashtbl[STATEID_HASH_SIZE]; ...@@ -1729,7 +1745,6 @@ static struct list_head lockstateid_hashtbl[STATEID_HASH_SIZE];
struct nfs4_stateid * struct nfs4_stateid *
find_stateid(stateid_t *stid, int flags) find_stateid(stateid_t *stid, int flags)
{ {
struct list_head *pos, *next;
struct nfs4_stateid *local = NULL; struct nfs4_stateid *local = NULL;
u32 st_id = stid->si_stateownerid; u32 st_id = stid->si_stateownerid;
u32 f_id = stid->si_fileid; u32 f_id = stid->si_fileid;
...@@ -1738,8 +1753,7 @@ find_stateid(stateid_t *stid, int flags) ...@@ -1738,8 +1753,7 @@ find_stateid(stateid_t *stid, int flags)
dprintk("NFSD: find_stateid flags 0x%x\n",flags); dprintk("NFSD: find_stateid flags 0x%x\n",flags);
if ((flags & LOCK_STATE) || (flags & RDWR_STATE)) { if ((flags & LOCK_STATE) || (flags & RDWR_STATE)) {
hashval = stateid_hashval(st_id, f_id); hashval = stateid_hashval(st_id, f_id);
list_for_each_safe(pos, next, &lockstateid_hashtbl[hashval]) { list_for_each_entry(local, &lockstateid_hashtbl[hashval], st_hash) {
local = list_entry(pos, struct nfs4_stateid, st_hash);
if((local->st_stateid.si_stateownerid == st_id) && if((local->st_stateid.si_stateownerid == st_id) &&
(local->st_stateid.si_fileid == f_id)) (local->st_stateid.si_fileid == f_id))
return local; return local;
...@@ -1747,8 +1761,7 @@ find_stateid(stateid_t *stid, int flags) ...@@ -1747,8 +1761,7 @@ find_stateid(stateid_t *stid, int flags)
} }
if ((flags & OPEN_STATE) || (flags & RDWR_STATE)) { if ((flags & OPEN_STATE) || (flags & RDWR_STATE)) {
hashval = stateid_hashval(st_id, f_id); hashval = stateid_hashval(st_id, f_id);
list_for_each_safe(pos, next, &stateid_hashtbl[hashval]) { list_for_each_entry(local, &stateid_hashtbl[hashval], st_hash) {
local = list_entry(pos, struct nfs4_stateid, st_hash);
if((local->st_stateid.si_stateownerid == st_id) && if((local->st_stateid.si_stateownerid == st_id) &&
(local->st_stateid.si_fileid == f_id)) (local->st_stateid.si_fileid == f_id))
return local; return local;
...@@ -1779,14 +1792,12 @@ nfs4_transform_lock_offset(struct file_lock *lock) ...@@ -1779,14 +1792,12 @@ nfs4_transform_lock_offset(struct file_lock *lock)
int int
nfs4_verify_lock_stateowner(struct nfs4_stateowner *sop, unsigned int hashval) nfs4_verify_lock_stateowner(struct nfs4_stateowner *sop, unsigned int hashval)
{ {
struct list_head *pos, *next;
struct nfs4_stateowner *local = NULL; struct nfs4_stateowner *local = NULL;
int status = 0; int status = 0;
if (hashval >= LOCK_HASH_SIZE) if (hashval >= LOCK_HASH_SIZE)
goto out; goto out;
list_for_each_safe(pos, next, &lock_ownerid_hashtbl[hashval]) { list_for_each_entry(local, &lock_ownerid_hashtbl[hashval], so_idhash) {
local = list_entry(pos, struct nfs4_stateowner, so_idhash);
if (local == sop) { if (local == sop) {
status = 1; status = 1;
goto out; goto out;
...@@ -1817,11 +1828,9 @@ nfs4_set_lock_denied(struct file_lock *fl, struct nfsd4_lock_denied *deny) ...@@ -1817,11 +1828,9 @@ nfs4_set_lock_denied(struct file_lock *fl, struct nfsd4_lock_denied *deny)
static int static int
find_lockstateowner_str(unsigned int hashval, struct xdr_netobj *owner, clientid_t *clid, struct nfs4_stateowner **op) { find_lockstateowner_str(unsigned int hashval, struct xdr_netobj *owner, clientid_t *clid, struct nfs4_stateowner **op) {
struct list_head *pos, *next;
struct nfs4_stateowner *local = NULL; struct nfs4_stateowner *local = NULL;
list_for_each_safe(pos, next, &lock_ownerstr_hashtbl[hashval]) { list_for_each_entry(local, &lock_ownerstr_hashtbl[hashval], so_strhash) {
local = list_entry(pos, struct nfs4_stateowner, so_strhash);
if(!cmp_owner_str(local, owner, clid)) if(!cmp_owner_str(local, owner, clid))
continue; continue;
*op = local; *op = local;
...@@ -1854,6 +1863,8 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str ...@@ -1854,6 +1863,8 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str
INIT_LIST_HEAD(&sop->so_perclient); INIT_LIST_HEAD(&sop->so_perclient);
INIT_LIST_HEAD(&sop->so_perfilestate); INIT_LIST_HEAD(&sop->so_perfilestate);
INIT_LIST_HEAD(&sop->so_perlockowner); INIT_LIST_HEAD(&sop->so_perlockowner);
INIT_LIST_HEAD(&sop->so_close_lru); /* not used */
sop->so_time = 0;
list_add(&sop->so_idhash, &lock_ownerid_hashtbl[idhashval]); list_add(&sop->so_idhash, &lock_ownerid_hashtbl[idhashval]);
list_add(&sop->so_strhash, &lock_ownerstr_hashtbl[strhashval]); list_add(&sop->so_strhash, &lock_ownerstr_hashtbl[strhashval]);
list_add(&sop->so_perclient, &clp->cl_perclient); list_add(&sop->so_perclient, &clp->cl_perclient);
...@@ -2265,7 +2276,6 @@ int ...@@ -2265,7 +2276,6 @@ int
nfsd4_release_lockowner(struct svc_rqst *rqstp, struct nfsd4_release_lockowner *rlockowner) nfsd4_release_lockowner(struct svc_rqst *rqstp, struct nfsd4_release_lockowner *rlockowner)
{ {
clientid_t *clid = &rlockowner->rl_clientid; clientid_t *clid = &rlockowner->rl_clientid;
struct list_head *pos, *next;
struct nfs4_stateowner *local = NULL; struct nfs4_stateowner *local = NULL;
struct xdr_netobj *owner = &rlockowner->rl_owner; struct xdr_netobj *owner = &rlockowner->rl_owner;
int status, i; int status, i;
...@@ -2286,9 +2296,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, struct nfsd4_release_lockowner * ...@@ -2286,9 +2296,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, struct nfsd4_release_lockowner *
/* find the lockowner */ /* find the lockowner */
status = nfs_ok; status = nfs_ok;
for (i=0; i < LOCK_HASH_SIZE; i++) { for (i=0; i < LOCK_HASH_SIZE; i++) {
list_for_each_safe(pos, next, &lock_ownerstr_hashtbl[i]) { list_for_each_entry(local, &lock_ownerstr_hashtbl[i], so_strhash) {
local = list_entry(pos, struct nfs4_stateowner,
so_strhash);
if(cmp_owner_str(local, owner, clid)) if(cmp_owner_str(local, owner, clid))
break; break;
} }
...@@ -2299,9 +2307,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, struct nfsd4_release_lockowner * ...@@ -2299,9 +2307,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, struct nfsd4_release_lockowner *
/* check for any locks held by any stateid associated with the /* check for any locks held by any stateid associated with the
* (lock) stateowner */ * (lock) stateowner */
status = nfserr_locks_held; status = nfserr_locks_held;
list_for_each_safe(pos, next, &local->so_perfilestate) { list_for_each_entry(stp, &local->so_perfilestate, st_perfilestate) {
stp = list_entry(pos, struct nfs4_stateid,
st_perfilestate);
if(stp->st_vfs_set) { if(stp->st_vfs_set) {
if (check_for_locks(&stp->st_vfs_file, local)) if (check_for_locks(&stp->st_vfs_file, local))
goto out; goto out;
...@@ -2351,6 +2357,7 @@ nfs4_state_init(void) ...@@ -2351,6 +2357,7 @@ nfs4_state_init(void)
memset(&zerostateid, 0, sizeof(stateid_t)); memset(&zerostateid, 0, sizeof(stateid_t));
memset(&onestateid, ~0, sizeof(stateid_t)); memset(&onestateid, ~0, sizeof(stateid_t));
INIT_LIST_HEAD(&close_lru);
INIT_LIST_HEAD(&client_lru); INIT_LIST_HEAD(&client_lru);
init_MUTEX(&client_sema); init_MUTEX(&client_sema);
boot_time = get_seconds(); boot_time = get_seconds();
......
...@@ -132,6 +132,9 @@ struct nfs4_replay { ...@@ -132,6 +132,9 @@ struct nfs4_replay {
* release a stateowner. * release a stateowner.
* so_perlockowner: (open) nfs4_stateid->st_perlockowner entry - used when * so_perlockowner: (open) nfs4_stateid->st_perlockowner entry - used when
* close is called to reap associated byte-range locks * close is called to reap associated byte-range locks
* so_close_lru: (open) stateowner is placed on this list instead of being
* reaped (when so_perfilestate is empty) to hold the last close replay.
* reaped by laundramat thread after lease period.
*/ */
struct nfs4_stateowner { struct nfs4_stateowner {
struct list_head so_idhash; /* hash by so_id */ struct list_head so_idhash; /* hash by so_id */
...@@ -139,6 +142,8 @@ struct nfs4_stateowner { ...@@ -139,6 +142,8 @@ struct nfs4_stateowner {
struct list_head so_perclient; /* nfs4_client->cl_perclient */ struct list_head so_perclient; /* nfs4_client->cl_perclient */
struct list_head so_perfilestate; /* list: nfs4_stateid */ struct list_head so_perfilestate; /* list: nfs4_stateid */
struct list_head so_perlockowner; /* nfs4_stateid->st_perlockowner */ struct list_head so_perlockowner; /* nfs4_stateid->st_perlockowner */
struct list_head so_close_lru; /* tail queue */
time_t so_time; /* time of placement on so_close_lru */
int so_is_open_owner; /* 1=openowner,0=lockowner */ int so_is_open_owner; /* 1=openowner,0=lockowner */
u32 so_id; u32 so_id;
struct nfs4_client * so_client; struct nfs4_client * so_client;
...@@ -194,6 +199,7 @@ struct nfs4_stateid { ...@@ -194,6 +199,7 @@ struct nfs4_stateid {
#define OPEN_STATE 0x00000004 #define OPEN_STATE 0x00000004
#define LOCK_STATE 0x00000008 #define LOCK_STATE 0x00000008
#define RDWR_STATE 0x00000010 #define RDWR_STATE 0x00000010
#define CLOSE_STATE 0x00000020
#define seqid_mutating_err(err) \ #define seqid_mutating_err(err) \
(((err) != nfserr_stale_clientid) && \ (((err) != nfserr_stale_clientid) && \
......
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