Skip to content

Commit d688d85

Browse files
neilbrownchucklever
authored andcommitted
nfsd: allow admin-revoked NFSv4.0 state to be freed.
For NFSv4.1 and later the client easily discovers if there is any admin-revoked state and will then find and explicitly free it. For NFSv4.0 there is no such mechanism. The client can only find that state is admin-revoked if it tries to use that state, and there is no way for it to explicitly free the state. So the server must hold on to the stateid (at least) for an indefinite amount of time. A RELEASE_LOCKOWNER request might justify forgetting some of these stateids, as would the whole clients lease lapsing, but these are not reliable. This patch takes two approaches. Whenever a client uses an revoked stateid, that stateid is then discarded and will not be recognised again. This might confuse a client which expect to get NFS4ERR_ADMIN_REVOKED consistently once it get it at all, but should mostly work. Hopefully one error will lead to other resources being closed (e.g. process exits), which will result in more stateid being freed when a CLOSE attempt gets NFS4ERR_ADMIN_REVOKED. Also, any admin-revoked stateids that have been that way for more than one lease time are periodically revoke. No actual freeing of state happens in this patch. That will come in future patches which handle the different sorts of revoked state. Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: NeilBrown <neilb@suse.de> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
1 parent 11b2cfb commit d688d85

2 files changed

Lines changed: 101 additions & 1 deletion

File tree

fs/nfsd/netns.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,10 @@ struct nfsd_net {
209209
atomic_t nfsd_courtesy_clients;
210210
struct shrinker *nfsd_client_shrinker;
211211
struct work_struct nfsd_shrinker_work;
212+
213+
/* last time an admin-revoke happened for NFSv4.0 */
214+
time64_t nfs40_last_revoke;
215+
212216
};
213217

214218
/* Simple check to find out if a given net was properly initialized */

fs/nfsd/nfs4state.c

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1733,6 +1733,14 @@ void nfsd4_revoke_states(struct net *net, struct super_block *sb)
17331733
}
17341734
nfs4_put_stid(stid);
17351735
spin_lock(&nn->client_lock);
1736+
if (clp->cl_minorversion == 0)
1737+
/* Allow cleanup after a lease period.
1738+
* store_release ensures cleanup will
1739+
* see any newly revoked states if it
1740+
* sees the time updated.
1741+
*/
1742+
nn->nfs40_last_revoke =
1743+
ktime_get_boottime_seconds();
17361744
goto retry;
17371745
}
17381746
}
@@ -4618,6 +4626,40 @@ nfsd4_find_existing_open(struct nfs4_file *fp, struct nfsd4_open *open)
46184626
return ret;
46194627
}
46204628

4629+
static void nfsd4_drop_revoked_stid(struct nfs4_stid *s)
4630+
__releases(&s->sc_client->cl_lock)
4631+
{
4632+
struct nfs4_client *cl = s->sc_client;
4633+
4634+
switch (s->sc_type) {
4635+
default:
4636+
spin_unlock(&cl->cl_lock);
4637+
}
4638+
}
4639+
4640+
static void nfsd40_drop_revoked_stid(struct nfs4_client *cl,
4641+
stateid_t *stid)
4642+
{
4643+
/* NFSv4.0 has no way for the client to tell the server
4644+
* that it can forget an admin-revoked stateid.
4645+
* So we keep it around until the first time that the
4646+
* client uses it, and drop it the first time
4647+
* nfserr_admin_revoked is returned.
4648+
* For v4.1 and later we wait until explicitly told
4649+
* to free the stateid.
4650+
*/
4651+
if (cl->cl_minorversion == 0) {
4652+
struct nfs4_stid *st;
4653+
4654+
spin_lock(&cl->cl_lock);
4655+
st = find_stateid_locked(cl, stid);
4656+
if (st)
4657+
nfsd4_drop_revoked_stid(st);
4658+
else
4659+
spin_unlock(&cl->cl_lock);
4660+
}
4661+
}
4662+
46214663
static __be32
46224664
nfsd4_verify_open_stid(struct nfs4_stid *s)
46234665
{
@@ -4640,6 +4682,10 @@ nfsd4_lock_ol_stateid(struct nfs4_ol_stateid *stp)
46404682

46414683
mutex_lock_nested(&stp->st_mutex, LOCK_STATEID_MUTEX);
46424684
ret = nfsd4_verify_open_stid(&stp->st_stid);
4685+
if (ret == nfserr_admin_revoked)
4686+
nfsd40_drop_revoked_stid(stp->st_stid.sc_client,
4687+
&stp->st_stid.sc_stateid);
4688+
46434689
if (ret != nfs_ok)
46444690
mutex_unlock(&stp->st_mutex);
46454691
return ret;
@@ -5221,6 +5267,7 @@ nfs4_check_deleg(struct nfs4_client *cl, struct nfsd4_open *open,
52215267
}
52225268
if (deleg->dl_stid.sc_status & SC_STATUS_REVOKED) {
52235269
nfs4_put_stid(&deleg->dl_stid);
5270+
nfsd40_drop_revoked_stid(cl, &open->op_delegate_stateid);
52245271
status = nfserr_deleg_revoked;
52255272
goto out;
52265273
}
@@ -6206,6 +6253,43 @@ nfs4_process_client_reaplist(struct list_head *reaplist)
62066253
}
62076254
}
62086255

6256+
static void nfs40_clean_admin_revoked(struct nfsd_net *nn,
6257+
struct laundry_time *lt)
6258+
{
6259+
struct nfs4_client *clp;
6260+
6261+
spin_lock(&nn->client_lock);
6262+
if (nn->nfs40_last_revoke == 0 ||
6263+
nn->nfs40_last_revoke > lt->cutoff) {
6264+
spin_unlock(&nn->client_lock);
6265+
return;
6266+
}
6267+
nn->nfs40_last_revoke = 0;
6268+
6269+
retry:
6270+
list_for_each_entry(clp, &nn->client_lru, cl_lru) {
6271+
unsigned long id, tmp;
6272+
struct nfs4_stid *stid;
6273+
6274+
if (atomic_read(&clp->cl_admin_revoked) == 0)
6275+
continue;
6276+
6277+
spin_lock(&clp->cl_lock);
6278+
idr_for_each_entry_ul(&clp->cl_stateids, stid, tmp, id)
6279+
if (stid->sc_status & SC_STATUS_ADMIN_REVOKED) {
6280+
refcount_inc(&stid->sc_count);
6281+
spin_unlock(&nn->client_lock);
6282+
/* this function drops ->cl_lock */
6283+
nfsd4_drop_revoked_stid(stid);
6284+
nfs4_put_stid(stid);
6285+
spin_lock(&nn->client_lock);
6286+
goto retry;
6287+
}
6288+
spin_unlock(&clp->cl_lock);
6289+
}
6290+
spin_unlock(&nn->client_lock);
6291+
}
6292+
62096293
static time64_t
62106294
nfs4_laundromat(struct nfsd_net *nn)
62116295
{
@@ -6239,6 +6323,8 @@ nfs4_laundromat(struct nfsd_net *nn)
62396323
nfs4_get_client_reaplist(nn, &reaplist, &lt);
62406324
nfs4_process_client_reaplist(&reaplist);
62416325

6326+
nfs40_clean_admin_revoked(nn, &lt);
6327+
62426328
spin_lock(&state_lock);
62436329
list_for_each_safe(pos, next, &nn->del_recall_lru) {
62446330
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
@@ -6457,6 +6543,9 @@ static __be32 nfsd4_stid_check_stateid_generation(stateid_t *in, struct nfs4_sti
64576543
if (ret == nfs_ok)
64586544
ret = check_stateid_generation(in, &s->sc_stateid, has_session);
64596545
spin_unlock(&s->sc_lock);
6546+
if (ret == nfserr_admin_revoked)
6547+
nfsd40_drop_revoked_stid(s->sc_client,
6548+
&s->sc_stateid);
64606549
return ret;
64616550
}
64626551

@@ -6501,6 +6590,8 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid)
65016590
}
65026591
out_unlock:
65036592
spin_unlock(&cl->cl_lock);
6593+
if (status == nfserr_admin_revoked)
6594+
nfsd40_drop_revoked_stid(cl, stateid);
65046595
return status;
65056596
}
65066597

@@ -6547,6 +6638,7 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
65476638
return nfserr_deleg_revoked;
65486639
}
65496640
if (stid->sc_status & SC_STATUS_ADMIN_REVOKED) {
6641+
nfsd40_drop_revoked_stid(cstate->clp, stateid);
65506642
nfs4_put_stid(stid);
65516643
return nfserr_admin_revoked;
65526644
}
@@ -6839,6 +6931,11 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
68396931
s = find_stateid_locked(cl, stateid);
68406932
if (!s || s->sc_status & SC_STATUS_CLOSED)
68416933
goto out_unlock;
6934+
if (s->sc_status & SC_STATUS_ADMIN_REVOKED) {
6935+
nfsd4_drop_revoked_stid(s);
6936+
ret = nfs_ok;
6937+
goto out;
6938+
}
68426939
spin_lock(&s->sc_lock);
68436940
switch (s->sc_type) {
68446941
case SC_TYPE_DELEG:
@@ -6865,7 +6962,6 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
68656962
spin_unlock(&cl->cl_lock);
68666963
ret = nfsd4_free_lock_stateid(stateid, s);
68676964
goto out;
6868-
/* Default falls through and returns nfserr_bad_stateid */
68696965
}
68706966
spin_unlock(&s->sc_lock);
68716967
out_unlock:

0 commit comments

Comments
 (0)