Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3bd64a5b authored by J. Bruce Fields's avatar J. Bruce Fields
Browse files

nfsd4: implement SEQ4_STATUS_RECALLABLE_STATE_REVOKED



A 4.1 server must notify a client that has had any state revoked using
the SEQ4_STATUS_RECALLABLE_STATE_REVOKED flag.  The client can figure
out exactly which state is the problem using CHECK_STATEID and then free
it using FREE_STATEID.  The status flag will be unset once all such
revoked stateids are freed.

Our server's only recallable state is delegations.  So we keep with each
4.1 client a list of delegations that have timed out and been recalled,
but haven't yet been freed by FREE_STATEID.

Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent 23340032
Loading
Loading
Loading
Loading
+47 −8
Original line number Original line Diff line number Diff line
@@ -445,7 +445,6 @@ static void unhash_stid(struct nfs4_stid *s)
static void
static void
unhash_delegation(struct nfs4_delegation *dp)
unhash_delegation(struct nfs4_delegation *dp)
{
{
	unhash_stid(&dp->dl_stid);
	list_del_init(&dp->dl_perclnt);
	list_del_init(&dp->dl_perclnt);
	spin_lock(&recall_lock);
	spin_lock(&recall_lock);
	list_del_init(&dp->dl_perfile);
	list_del_init(&dp->dl_perfile);
@@ -454,10 +453,37 @@ unhash_delegation(struct nfs4_delegation *dp)
	nfs4_put_deleg_lease(dp->dl_file);
	nfs4_put_deleg_lease(dp->dl_file);
	put_nfs4_file(dp->dl_file);
	put_nfs4_file(dp->dl_file);
	dp->dl_file = NULL;
	dp->dl_file = NULL;
}



static void destroy_revoked_delegation(struct nfs4_delegation *dp)
{
	list_del_init(&dp->dl_recall_lru);
	remove_stid(&dp->dl_stid);
	remove_stid(&dp->dl_stid);
	nfs4_put_delegation(dp);
	nfs4_put_delegation(dp);
}
}


static void destroy_delegation(struct nfs4_delegation *dp)
{
	unhash_delegation(dp);
	remove_stid(&dp->dl_stid);
	nfs4_put_delegation(dp);
}

static void revoke_delegation(struct nfs4_delegation *dp)
{
	struct nfs4_client *clp = dp->dl_stid.sc_client;

	if (clp->cl_minorversion == 0)
		destroy_delegation(dp);
	else {
		unhash_delegation(dp);
		dp->dl_stid.sc_type = NFS4_REVOKED_DELEG_STID;
		list_add(&dp->dl_recall_lru, &clp->cl_revoked);
	}
}

/* 
/* 
 * SETCLIENTID state 
 * SETCLIENTID state 
 */
 */
@@ -1114,7 +1140,7 @@ destroy_client(struct nfs4_client *clp)
	spin_unlock(&recall_lock);
	spin_unlock(&recall_lock);
	while (!list_empty(&reaplist)) {
	while (!list_empty(&reaplist)) {
		dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru);
		dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru);
		unhash_delegation(dp);
		destroy_delegation(dp);
	}
	}
	while (!list_empty(&clp->cl_openowners)) {
	while (!list_empty(&clp->cl_openowners)) {
		oo = list_entry(clp->cl_openowners.next, struct nfs4_openowner, oo_perclient);
		oo = list_entry(clp->cl_openowners.next, struct nfs4_openowner, oo_perclient);
@@ -1310,6 +1336,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name,
	INIT_LIST_HEAD(&clp->cl_delegations);
	INIT_LIST_HEAD(&clp->cl_delegations);
	INIT_LIST_HEAD(&clp->cl_lru);
	INIT_LIST_HEAD(&clp->cl_lru);
	INIT_LIST_HEAD(&clp->cl_callbacks);
	INIT_LIST_HEAD(&clp->cl_callbacks);
	INIT_LIST_HEAD(&clp->cl_revoked);
	spin_lock_init(&clp->cl_lock);
	spin_lock_init(&clp->cl_lock);
	nfsd4_init_callback(&clp->cl_cb_null);
	nfsd4_init_callback(&clp->cl_cb_null);
	clp->cl_time = get_seconds();
	clp->cl_time = get_seconds();
@@ -2171,6 +2198,8 @@ nfsd4_sequence(struct svc_rqst *rqstp,
	default:
	default:
		seq->status_flags = 0;
		seq->status_flags = 0;
	}
	}
	if (!list_empty(&clp->cl_revoked))
		seq->status_flags |= SEQ4_STATUS_RECALLABLE_STATE_REVOKED;
out_no_session:
out_no_session:
	kfree(conn);
	kfree(conn);
	spin_unlock(&nn->client_lock);
	spin_unlock(&nn->client_lock);
@@ -3297,7 +3326,7 @@ nfs4_laundromat(struct nfsd_net *nn)
	spin_unlock(&recall_lock);
	spin_unlock(&recall_lock);
	list_for_each_safe(pos, next, &reaplist) {
	list_for_each_safe(pos, next, &reaplist) {
		dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
		dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
		unhash_delegation(dp);
		revoke_delegation(dp);
	}
	}
	test_val = nn->nfsd4_lease;
	test_val = nn->nfsd4_lease;
	list_for_each_safe(pos, next, &nn->close_lru) {
	list_for_each_safe(pos, next, &nn->close_lru) {
@@ -3459,6 +3488,8 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid)
	switch (s->sc_type) {
	switch (s->sc_type) {
	case NFS4_DELEG_STID:
	case NFS4_DELEG_STID:
		return nfs_ok;
		return nfs_ok;
	case NFS4_REVOKED_DELEG_STID:
		return nfserr_deleg_revoked;
	case NFS4_OPEN_STID:
	case NFS4_OPEN_STID:
	case NFS4_LOCK_STID:
	case NFS4_LOCK_STID:
		ols = openlockstateid(s);
		ols = openlockstateid(s);
@@ -3602,6 +3633,7 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
{
{
	stateid_t *stateid = &free_stateid->fr_stateid;
	stateid_t *stateid = &free_stateid->fr_stateid;
	struct nfs4_stid *s;
	struct nfs4_stid *s;
	struct nfs4_delegation *dp;
	struct nfs4_client *cl = cstate->session->se_client;
	struct nfs4_client *cl = cstate->session->se_client;
	__be32 ret = nfserr_bad_stateid;
	__be32 ret = nfserr_bad_stateid;


@@ -3623,6 +3655,11 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
		else
		else
			ret = nfserr_locks_held;
			ret = nfserr_locks_held;
		break;
		break;
	case NFS4_REVOKED_DELEG_STID:
		dp = delegstateid(s);
		destroy_revoked_delegation(dp);
		ret = nfs_ok;
		break;
	default:
	default:
		ret = nfserr_bad_stateid;
		ret = nfserr_bad_stateid;
	}
	}
@@ -3647,10 +3684,12 @@ static __be32 nfs4_seqid_op_checks(struct nfsd4_compound_state *cstate, stateid_
	status = nfsd4_check_seqid(cstate, sop, seqid);
	status = nfsd4_check_seqid(cstate, sop, seqid);
	if (status)
	if (status)
		return status;
		return status;
	if (stp->st_stid.sc_type == NFS4_CLOSED_STID)
	if (stp->st_stid.sc_type == NFS4_CLOSED_STID
		|| stp->st_stid.sc_type == NFS4_REVOKED_DELEG_STID)
		/*
		/*
		 * "Closed" stateid's exist *only* to return
		 * "Closed" stateid's exist *only* to return
		 * nfserr_replay_me from the previous step.
		 * nfserr_replay_me from the previous step, and
		 * revoked delegations are kept only for free_stateid.
		 */
		 */
		return nfserr_bad_stateid;
		return nfserr_bad_stateid;
	status = check_stateid_generation(stateid, &stp->st_stid.sc_stateid, nfsd4_has_session(cstate));
	status = check_stateid_generation(stateid, &stp->st_stid.sc_stateid, nfsd4_has_session(cstate));
@@ -3913,7 +3952,7 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	if (status)
	if (status)
		goto out;
		goto out;


	unhash_delegation(dp);
	destroy_delegation(dp);
out:
out:
	nfs4_unlock_state();
	nfs4_unlock_state();


@@ -4763,7 +4802,7 @@ u64 nfsd_forget_client_delegations(struct nfs4_client *clp, u64 max)
	spin_unlock(&recall_lock);
	spin_unlock(&recall_lock);


	list_for_each_entry_safe(dp, next, &victims, dl_recall_lru)
	list_for_each_entry_safe(dp, next, &victims, dl_recall_lru)
		unhash_delegation(dp);
		revoke_delegation(dp);


	return count;
	return count;
}
}
@@ -5018,7 +5057,7 @@ nfs4_state_shutdown_net(struct net *net)
	spin_unlock(&recall_lock);
	spin_unlock(&recall_lock);
	list_for_each_safe(pos, next, &reaplist) {
	list_for_each_safe(pos, next, &reaplist) {
		dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
		dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
		unhash_delegation(dp);
		destroy_delegation(dp);
	}
	}


	nfsd4_client_tracking_exit(net);
	nfsd4_client_tracking_exit(net);
+3 −0
Original line number Original line Diff line number Diff line
@@ -79,6 +79,8 @@ struct nfs4_stid {
#define NFS4_DELEG_STID 4
#define NFS4_DELEG_STID 4
/* For an open stateid kept around *only* to process close replays: */
/* For an open stateid kept around *only* to process close replays: */
#define NFS4_CLOSED_STID 8
#define NFS4_CLOSED_STID 8
/* For a deleg stateid kept around only to process free_stateid's: */
#define NFS4_REVOKED_DELEG_STID 16
	unsigned char sc_type;
	unsigned char sc_type;
	stateid_t sc_stateid;
	stateid_t sc_stateid;
	struct nfs4_client *sc_client;
	struct nfs4_client *sc_client;
@@ -238,6 +240,7 @@ struct nfs4_client {
	struct list_head	cl_openowners;
	struct list_head	cl_openowners;
	struct idr		cl_stateids;	/* stateid lookup */
	struct idr		cl_stateids;	/* stateid lookup */
	struct list_head	cl_delegations;
	struct list_head	cl_delegations;
	struct list_head	cl_revoked;	/* unacknowledged, revoked 4.1 state */
	struct list_head        cl_lru;         /* tail queue */
	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 */