Skip to content

Commit

Permalink
nfsd4: match close replays on stateid, not open owner id
Browse files Browse the repository at this point in the history
Keep around an unhashed copy of the final stateid after the last close
using an openowner, and when identifying a replay, match against that
stateid instead of just against the open owner id.  Free it the next
time the seqid is bumped or the stateowner is destroyed.

Signed-off-by: J. Bruce Fields <bfields@redhat.com>
  • Loading branch information
J. Bruce Fields committed Sep 17, 2011
1 parent dad1c06 commit 38c387b
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 7 deletions.
47 changes: 40 additions & 7 deletions fs/nfsd/nfs4state.c
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,6 @@ static void close_generic_stateid(struct nfs4_ol_stateid *stp)

static void free_generic_stateid(struct nfs4_ol_stateid *stp)
{
close_generic_stateid(stp);
kmem_cache_free(stateid_slab, stp);
}

Expand All @@ -450,6 +449,7 @@ static void release_lock_stateid(struct nfs4_ol_stateid *stp)
file = find_any_file(stp->st_file);
if (file)
locks_remove_posix(file, (fl_owner_t)lockowner(stp->st_stateowner));
close_generic_stateid(stp);
free_generic_stateid(stp);
}

Expand Down Expand Up @@ -485,10 +485,16 @@ release_stateid_lockowners(struct nfs4_ol_stateid *open_stp)
}
}

static void release_open_stateid(struct nfs4_ol_stateid *stp)
static void unhash_open_stateid(struct nfs4_ol_stateid *stp)
{
unhash_generic_stateid(stp);
release_stateid_lockowners(stp);
close_generic_stateid(stp);
}

static void release_open_stateid(struct nfs4_ol_stateid *stp)
{
unhash_open_stateid(stp);
free_generic_stateid(stp);
}

Expand All @@ -510,6 +516,8 @@ static void release_openowner(struct nfs4_openowner *oo)
{
unhash_openowner(oo);
list_del(&oo->oo_close_lru);
if (oo->oo_last_closed_stid)
free_generic_stateid(oo->oo_last_closed_stid);
nfs4_free_openowner(oo);
}

Expand Down Expand Up @@ -2324,6 +2332,7 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str
oo->oo_owner.so_seqid = open->op_seqid;
oo->oo_flags = 0;
oo->oo_time = 0;
oo->oo_last_closed_stid = NULL;
INIT_LIST_HEAD(&oo->oo_close_lru);
hash_openowner(oo, clp, strhashval);
return oo;
Expand Down Expand Up @@ -3120,12 +3129,14 @@ laundromat_main(struct work_struct *not_used)
queue_delayed_work(laundry_wq, &laundromat_work, t*HZ);
}

static struct nfs4_openowner * search_close_lru(u32 st_id)
static struct nfs4_openowner * search_close_lru(stateid_t *s)
{
struct nfs4_openowner *local;
struct nfs4_ol_stateid *os;

list_for_each_entry(local, &close_lru, oo_close_lru) {
if (local->oo_owner.so_id == st_id)
os = local->oo_last_closed_stid;
if (same_stateid(&os->st_stid.sc_stateid, s))
return local;
}
return NULL;
Expand Down Expand Up @@ -3589,6 +3600,27 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp,
return status;
}

void nfsd4_purge_closed_stateid(struct nfs4_stateowner *so)
{
struct nfs4_openowner *oo;
struct nfs4_ol_stateid *s;

if (!so->so_is_open_owner)
return;
oo = openowner(so);
s = oo->oo_last_closed_stid;
if (!s)
return;
if (!(oo->oo_flags & NFS4_OO_PURGE_CLOSE)) {
/* Release the last_closed_stid on the next seqid bump: */
oo->oo_flags |= NFS4_OO_PURGE_CLOSE;
return;
}
oo->oo_flags &= ~NFS4_OO_PURGE_CLOSE;
free_generic_stateid(oo->oo_last_closed_stid);
oo->oo_last_closed_stid = NULL;
}

/*
* nfs4_unlock_state() called after encode
*/
Expand All @@ -3613,7 +3645,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
* Also, we should make sure this isn't just the result of
* a replayed close:
*/
oo = search_close_lru(close->cl_stateid.si_stateownerid);
oo = search_close_lru(&close->cl_stateid);
/* It's not stale; let's assume it's expired: */
if (oo == NULL)
goto out;
Expand All @@ -3630,8 +3662,9 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
update_stateid(&stp->st_stid.sc_stateid);
memcpy(&close->cl_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));

/* release_stateid() calls nfsd_close() if needed */
release_open_stateid(stp);
/* unhash_open_stateid() calls nfsd_close() if needed */
oo->oo_last_closed_stid = stp;
unhash_open_stateid(stp);

/* place unused nfs4_stateowners on so_close_lru list to be
* released by the laundromat service after the lease period
Expand Down
1 change: 1 addition & 0 deletions fs/nfsd/nfs4xdr.c
Original file line number Diff line number Diff line change
Expand Up @@ -1636,6 +1636,7 @@ static void encode_seqid_op_tail(struct nfsd4_compoundres *resp, __be32 *save, _
(char *)resp->p - (char *)save;
memcpy(stateowner->so_replay.rp_buf, save,
stateowner->so_replay.rp_buflen);
nfsd4_purge_closed_stateid(stateowner);
}
}

Expand Down
3 changes: 3 additions & 0 deletions fs/nfsd/state.h
Original file line number Diff line number Diff line change
Expand Up @@ -370,8 +370,10 @@ struct nfs4_openowner {
struct nfs4_stateowner oo_owner; /* must be first field */
struct list_head oo_perclient;
struct list_head oo_close_lru; /* tail queue */
struct nfs4_ol_stateid *oo_last_closed_stid;
time_t oo_time; /* time of placement on so_close_lru */
#define NFS4_OO_CONFIRMED 1
#define NFS4_OO_PURGE_CLOSE 2
unsigned char oo_flags;
};

Expand Down Expand Up @@ -514,5 +516,6 @@ extern int nfsd4_create_clid_dir(struct nfs4_client *clp);
extern void nfsd4_remove_clid_dir(struct nfs4_client *clp);
extern void release_session_client(struct nfsd4_session *);
extern __be32 nfs4_validate_stateid(stateid_t *, bool);
extern void nfsd4_purge_closed_stateid(struct nfs4_stateowner *);

#endif /* NFSD4_STATE_H */

0 comments on commit 38c387b

Please sign in to comment.