summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/nfsd/nfs4callback.c7
-rw-r--r--fs/nfsd/nfs4state.c54
2 files changed, 47 insertions, 14 deletions
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 509195d488c9..9afe2d78d39d 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -1530,12 +1530,14 @@ out:
/**
* nfsd41_cb_destroy_referring_call_list - release referring call info
- * @cb: context of a callback that has completed
+ * @cb: context of callback to release referring calls from
*
* Callers who allocate referring calls using nfsd41_cb_referring_call() must
* release those resources by calling nfsd41_cb_destroy_referring_call_list.
*
- * Caller serializes access to @cb.
+ * Caller serializes access to @cb. No CB_COMPOUND for @cb may be in
+ * flight, because encode_cb_sequence4args() walks this list as it
+ * encodes.
*/
void nfsd41_cb_destroy_referring_call_list(struct nfsd4_callback *cb)
{
@@ -1557,6 +1559,7 @@ void nfsd41_cb_destroy_referring_call_list(struct nfsd4_callback *cb)
list_del(&rcl->__list);
kfree(rcl);
}
+ cb->cb_nr_referring_call_list = 0;
}
static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 5774c7a1b3de..510380b6aa7a 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1167,6 +1167,8 @@ static void nfs4_free_deleg(struct nfs4_stid *stid)
WARN_ON_ONCE(!list_empty(&dp->dl_perfile));
WARN_ON_ONCE(!list_empty(&dp->dl_perclnt));
WARN_ON_ONCE(!list_empty(&dp->dl_recall_lru));
+ /* The list outlives one recall, so ->release() cannot free it. */
+ nfsd41_cb_destroy_referring_call_list(&dp->dl_recall);
kmem_cache_free(deleg_slab, stid);
atomic_long_dec(&num_delegations);
}
@@ -6091,6 +6093,18 @@ bool nfsd_wait_for_delegreturn(struct svc_rqst *rqstp, struct inode *inode)
return timeo > 0;
}
+/*
+ * gen_sessionid() composes a sessionid from the client's clientid and a
+ * sequence counter, so the sequence alone identifies the granting session.
+ */
+static void nfsd4_recall_grant_sessionid(const struct nfs4_delegation *dp,
+ struct nfsd4_sessionid *sid)
+{
+ sid->clientid = dp->dl_stid.sc_client->cl_clientid;
+ sid->sequence = dp->dl_recall_grant.sessionid_seq;
+ sid->reserved = 0;
+}
+
static bool nfsd4_recall_grant_slot_retired(struct nfs4_delegation *dp)
{
struct nfs4_client *clp = dp->dl_stid.sc_client;
@@ -6103,14 +6117,7 @@ static bool nfsd4_recall_grant_slot_retired(struct nfs4_delegation *dp)
if (!dp->dl_recall_grant.valid)
return false;
- /*
- * gen_sessionid() composes a sessionid from the client's clientid
- * and a sequence counter, so the sequence alone identifies the
- * granting session.
- */
- sid.clientid = clp->cl_clientid;
- sid.sequence = dp->dl_recall_grant.sessionid_seq;
- sid.reserved = 0;
+ nfsd4_recall_grant_sessionid(dp, &sid);
/*
* A missing session does not prove the client saw the grant: a
@@ -6145,6 +6152,21 @@ static bool nfsd4_recall_grant_slot_retired(struct nfs4_delegation *dp)
return retired;
}
+/*
+ * ->prepare does not run on every send: nfsd4_run_cb_work() skips it
+ * on a requeue, and a retry via rpc_restart_call_prepare() re-enters
+ * the RPC layer beneath it. The granting request does not change, so
+ * a send inherits a correct list. Retirement is the one transition
+ * the list has to follow.
+ */
+static void nfsd4_refresh_recall_grant(struct nfs4_delegation *dp)
+{
+ dp->dl_recall_grant.retired_at_send =
+ nfsd4_recall_grant_slot_retired(dp);
+ if (dp->dl_recall_grant.retired_at_send)
+ nfsd41_cb_destroy_referring_call_list(&dp->dl_recall);
+}
+
static bool nfsd4_cb_recall_prepare(struct nfsd4_callback *cb)
{
struct nfs4_delegation *dp = cb_to_delegation(cb);
@@ -6167,8 +6189,17 @@ static bool nfsd4_cb_recall_prepare(struct nfsd4_callback *cb)
}
spin_unlock(&nn->deleg_lock);
- dp->dl_recall_grant.retired_at_send =
- nfsd4_recall_grant_slot_retired(dp);
+ nfsd4_refresh_recall_grant(dp);
+
+ if (dp->dl_recall_grant.valid && !dp->dl_recall_grant.retired_at_send) {
+ struct nfsd4_sessionid sid;
+
+ nfsd4_recall_grant_sessionid(dp, &sid);
+ nfsd41_cb_referring_call(&dp->dl_recall,
+ (struct nfs4_sessionid *)&sid,
+ dp->dl_recall_grant.slotid,
+ dp->dl_recall_grant.seqid);
+ }
return true;
}
@@ -6225,8 +6256,7 @@ static int nfsd4_cb_recall_done(struct nfsd4_callback *cb,
return 1;
}
if (!dp->dl_stid.sc_status && dp->dl_retries--) {
- dp->dl_recall_grant.retired_at_send =
- nfsd4_recall_grant_slot_retired(dp);
+ nfsd4_refresh_recall_grant(dp);
rpc_delay(task, 2 * HZ);
return 0;
}