nfsd: make the server repeat CB_RECALL every couple of seconds

Commit 01ae8969a9 stopped the NFSv4.1/4.2 server from implicitly
binding the back channel to a new TCP connection so that it
conforms to RFC5661, for NFSv4.1/4.2. An effect of this
for the Linux NFS client is that it will do a
BindConnectionToSession when it sees NFSV4SEQ_CBPATHDOWN
set in a sequence reply. This will fix the back channel, but the
first attempt at a callback like CB_RECALL will already have
failed. Without this patch, a CB_RECALL will not be retried
and that can result in a 5 minute delay until the delegation
times out.

This patch modifies the code so that it will retry the
CB_RECALL every couple of seconds, often avoiding the
5 minute delay.

This is not critical for correct behaviour, but avoids
the 5 minute delay for the case where the Linux client
re-binds the back channel via BindConnectionToSession.

MFC after:	2 weeks
This commit is contained in:
Rick Macklem 2021-04-04 18:15:54 -07:00
parent ea444392bb
commit 7a606f280a
2 changed files with 16 additions and 6 deletions

View file

@ -220,6 +220,7 @@ struct nfsstate {
time_t expiry;
time_t limit;
u_int64_t compref;
time_t last;
} deleg;
} ls_un;
struct nfslockfile *ls_lfp; /* Back pointer */
@ -238,6 +239,7 @@ struct nfsstate {
#define ls_delegtime ls_un.deleg.expiry
#define ls_delegtimelimit ls_un.deleg.limit
#define ls_compref ls_un.deleg.compref
#define ls_lastrecall ls_un.deleg.last
/*
* Nfs lock structure.

View file

@ -3070,6 +3070,7 @@ nfsrv_openctrl(struct nfsrv_descript *nd, vnode_t vp,
new_deleg->ls_clp = clp;
new_deleg->ls_filerev = filerev;
new_deleg->ls_compref = nd->nd_compref;
new_deleg->ls_lastrecall = 0;
LIST_INSERT_HEAD(&lfp->lf_deleg, new_deleg, ls_file);
LIST_INSERT_HEAD(NFSSTATEHASH(clp,
new_deleg->ls_stateid), new_deleg, ls_hash);
@ -3191,6 +3192,7 @@ nfsrv_openctrl(struct nfsrv_descript *nd, vnode_t vp,
new_deleg->ls_clp = clp;
new_deleg->ls_filerev = filerev;
new_deleg->ls_compref = nd->nd_compref;
new_deleg->ls_lastrecall = 0;
nfsrv_writedelegcnt++;
LIST_INSERT_HEAD(&lfp->lf_deleg, new_deleg, ls_file);
LIST_INSERT_HEAD(NFSSTATEHASH(clp,
@ -3261,6 +3263,7 @@ nfsrv_openctrl(struct nfsrv_descript *nd, vnode_t vp,
new_deleg->ls_clp = clp;
new_deleg->ls_filerev = filerev;
new_deleg->ls_compref = nd->nd_compref;
new_deleg->ls_lastrecall = 0;
LIST_INSERT_HEAD(&lfp->lf_deleg, new_deleg, ls_file);
LIST_INSERT_HEAD(NFSSTATEHASH(clp,
new_deleg->ls_stateid), new_deleg, ls_hash);
@ -3339,6 +3342,7 @@ nfsrv_openctrl(struct nfsrv_descript *nd, vnode_t vp,
new_deleg->ls_clp = clp;
new_deleg->ls_filerev = filerev;
new_deleg->ls_compref = nd->nd_compref;
new_deleg->ls_lastrecall = 0;
LIST_INSERT_HEAD(&lfp->lf_deleg, new_deleg,
ls_file);
LIST_INSERT_HEAD(NFSSTATEHASH(clp,
@ -5265,7 +5269,8 @@ nfsrv_delegconflict(struct nfsstate *stp, int *haslockp, NFSPROC_T *p,
* - check to see if the delegation has expired
* - if so, get the v4root lock and then expire it
*/
if (!(stp->ls_flags & NFSLCK_DELEGRECALL)) {
if ((stp->ls_flags & NFSLCK_DELEGRECALL) == 0 || stp->ls_lastrecall <
time_uptime) {
/*
* - do a recall callback, since not yet done
* For now, never allow truncate to be set. To use
@ -5280,11 +5285,14 @@ nfsrv_delegconflict(struct nfsstate *stp, int *haslockp, NFSPROC_T *p,
* will be extended when ops are done on the delegation
* stateid, up to the timelimit.)
*/
stp->ls_delegtime = NFSD_MONOSEC + (2 * nfsrv_lease) +
NFSRV_LEASEDELTA;
stp->ls_delegtimelimit = NFSD_MONOSEC + (6 * nfsrv_lease) +
NFSRV_LEASEDELTA;
stp->ls_flags |= NFSLCK_DELEGRECALL;
if ((stp->ls_flags & NFSLCK_DELEGRECALL) == 0) {
stp->ls_delegtime = NFSD_MONOSEC + (2 * nfsrv_lease) +
NFSRV_LEASEDELTA;
stp->ls_delegtimelimit = NFSD_MONOSEC + (6 *
nfsrv_lease) + NFSRV_LEASEDELTA;
stp->ls_flags |= NFSLCK_DELEGRECALL;
}
stp->ls_lastrecall = time_uptime + 1;
/*
* Loop NFSRV_CBRETRYCNT times while the CBRecall replies