git: 98c53b0b5540 - stable/14 - nfsd: Fix delegation handled for atomic upgrade
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 05 Jul 2024 01:14:43 UTC
The branch stable/14 has been updated by rmacklem:
URL: https://cgit.FreeBSD.org/src/commit/?id=98c53b0b55401eaed374b235a40f3a547a5ab4e9
commit 98c53b0b55401eaed374b235a40f3a547a5ab4e9
Author: Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2024-06-05 01:46:41 +0000
Commit: Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2024-07-05 01:12:26 +0000
nfsd: Fix delegation handled for atomic upgrade
For NFSv4.1/4.2, an atomic upgrade of a delegation from a
read delegation to a write delegation is allowed and can
result in signoficantly improved performance.
This patch adds support for this atomic upgrade, plus fixes
a couple of other delegation related bugs. Since there were
three cases where delegations were being issued, the patch
factors this out into a separate function called
nfsrv_issuedelegations().
This patch should only affect the NFSv4.1/4.2 behaviour
when delegations are enabled, which is not the default.
(cherry picked from commit e2c9fad2e0ae3f7049831bf7f2be1a3573363cdc)
---
sys/fs/nfsserver/nfs_nfsdserv.c | 7 +
sys/fs/nfsserver/nfs_nfsdstate.c | 297 ++++++++++++++++++---------------------
2 files changed, 141 insertions(+), 163 deletions(-)
diff --git a/sys/fs/nfsserver/nfs_nfsdserv.c b/sys/fs/nfsserver/nfs_nfsdserv.c
index 0c8bda6dc6a6..47e3a20390f4 100644
--- a/sys/fs/nfsserver/nfs_nfsdserv.c
+++ b/sys/fs/nfsserver/nfs_nfsdserv.c
@@ -3244,6 +3244,13 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSV4OPEN_RESOURCE);
*tl = newnfs_false;
+ } else if ((rflags &
+ NFSV4OPEN_WDNOTSUPPDOWNGRADE) != 0) {
+ NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
+ *tl = txdr_unsigned(NFSV4OPEN_NOTSUPPDOWNGRADE);
+ } else if ((rflags & NFSV4OPEN_WDNOTSUPPUPGRADE) != 0) {
+ NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
+ *tl = txdr_unsigned(NFSV4OPEN_NOTSUPPUPGRADE);
} else {
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OPEN_NOTWANTED);
diff --git a/sys/fs/nfsserver/nfs_nfsdstate.c b/sys/fs/nfsserver/nfs_nfsdstate.c
index c73840277022..ce3f3481f04a 100644
--- a/sys/fs/nfsserver/nfs_nfsdstate.c
+++ b/sys/fs/nfsserver/nfs_nfsdstate.c
@@ -240,6 +240,11 @@ static int nfsrv_createdsfile(vnode_t vp, fhandle_t *fhp, struct pnfsdsfile *pf,
static struct nfsdevice *nfsrv_findmirroredds(struct nfsmount *nmp);
static int nfsrv_checkmachcred(int op, struct nfsrv_descript *nd,
struct nfsclient *clp);
+static void nfsrv_issuedelegation(struct vnode *vp, struct nfsclient *clp,
+ struct nfsrv_descript *nd, int delegate, int writedeleg, int readonly,
+ u_quad_t filerev, uint64_t rdonly, struct nfsstate **new_delegp,
+ struct nfsstate *new_stp, struct nfslockfile *lfp, uint32_t *rflagsp,
+ nfsv4stateid_t *delegstateidp);
/*
* Scan the client list for a match and either return the current one,
@@ -442,7 +447,8 @@ nfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp,
/*
* If the verifier has changed, the client has rebooted
* and a new client id is issued. The old state info
- * can be thrown away once the SETCLIENTID_CONFIRM occurs.
+ * can be thrown away once the SetClientID_Confirm or
+ * Create_Session that confirms the clientid occurs.
*/
LIST_REMOVE(clp, lc_hash);
@@ -2648,6 +2654,8 @@ tryagain:
* considered a conflict since the client with a read delegation
* could have done an Open with ReadAccess and WriteDeny
* locally and then not have checked for the WriteDeny.)
+ * The exception is a NFSv4.1/4.2 client that has requested
+ * an atomic upgrade to a write delegation.
* Don't check for a Reclaim, since that will be dealt with
* by nfsrv_openctrl().
*/
@@ -2657,9 +2665,10 @@ tryagain:
while (stp != LIST_END(&lfp->lf_deleg)) {
nstp = LIST_NEXT(stp, ls_file);
if ((readonly && stp->ls_clp != clp &&
- (stp->ls_flags & NFSLCK_DELEGWRITE)) ||
+ (stp->ls_flags & NFSLCK_DELEGWRITE) != 0) ||
(!readonly && (stp->ls_clp != clp ||
- (stp->ls_flags & NFSLCK_DELEGREAD)))) {
+ ((stp->ls_flags & NFSLCK_DELEGREAD) != 0 &&
+ (new_stp->ls_flags & NFSLCK_WANTWDELEG) == 0)))) {
ret = nfsrv_delegconflict(stp, &haslock, p, vp);
if (ret) {
/*
@@ -2944,6 +2953,8 @@ tryagain:
* considered a conflict since the client with a read delegation
* could have done an Open with ReadAccess and WriteDeny
* locally and then not have checked for the WriteDeny.)
+ * The exception is a NFSv4.1/4.2 client that has requested
+ * an atomic upgrade to a write delegation.
*/
if (!(new_stp->ls_flags & (NFSLCK_DELEGPREV | NFSLCK_DELEGCUR))) {
stp = LIST_FIRST(&lfp->lf_deleg);
@@ -2951,12 +2962,15 @@ tryagain:
nstp = LIST_NEXT(stp, ls_file);
if (stp->ls_clp != clp && (stp->ls_flags & NFSLCK_DELEGREAD))
writedeleg = 0;
- else
+ else if (stp->ls_clp != clp ||
+ (stp->ls_flags & NFSLCK_DELEGWRITE) != 0 ||
+ (new_stp->ls_flags & NFSLCK_WANTWDELEG) == 0)
delegate = 0;
if ((readonly && stp->ls_clp != clp &&
- (stp->ls_flags & NFSLCK_DELEGWRITE)) ||
+ (stp->ls_flags & NFSLCK_DELEGWRITE) != 0) ||
(!readonly && (stp->ls_clp != clp ||
- (stp->ls_flags & NFSLCK_DELEGREAD)))) {
+ ((stp->ls_flags & NFSLCK_DELEGREAD) != 0 &&
+ (new_stp->ls_flags & NFSLCK_WANTWDELEG) == 0)))) {
if (new_stp->ls_flags & NFSLCK_RECLAIM) {
delegate = 2;
} else {
@@ -3204,47 +3218,9 @@ tryagain:
/*
* This is where we can choose to issue a delegation.
*/
- if ((new_stp->ls_flags & NFSLCK_WANTNODELEG) != 0)
- *rflagsp |= NFSV4OPEN_WDNOTWANTED;
- else if (nfsrv_issuedelegs == 0)
- *rflagsp |= NFSV4OPEN_WDSUPPFTYPE;
- else if (NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt))
- *rflagsp |= NFSV4OPEN_WDRESOURCE;
- else if (delegate == 0 || writedeleg == 0 ||
- NFSVNO_EXRDONLY(exp) || (readonly != 0 &&
- nfsrv_writedelegifpos == 0) ||
- !NFSVNO_DELEGOK(vp) ||
- (new_stp->ls_flags & NFSLCK_WANTRDELEG) != 0 ||
- (clp->lc_flags & (LCL_CALLBACKSON | LCL_CBDOWN)) !=
- LCL_CALLBACKSON)
- *rflagsp |= NFSV4OPEN_WDCONTENTION;
- else {
- new_deleg->ls_stateid.seqid = delegstateidp->seqid = 1;
- new_deleg->ls_stateid.other[0] = delegstateidp->other[0]
- = clp->lc_clientid.lval[0];
- new_deleg->ls_stateid.other[1] = delegstateidp->other[1]
- = clp->lc_clientid.lval[1];
- new_deleg->ls_stateid.other[2] = delegstateidp->other[2]
- = nfsrv_nextstateindex(clp);
- new_deleg->ls_flags = (NFSLCK_DELEGWRITE |
- NFSLCK_READACCESS | NFSLCK_WRITEACCESS);
- *rflagsp |= NFSV4OPEN_WRITEDELEGATE;
- new_deleg->ls_uid = new_stp->ls_uid;
- new_deleg->ls_lfp = lfp;
- 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,
- new_deleg->ls_stateid), new_deleg, ls_hash);
- LIST_INSERT_HEAD(&clp->lc_deleg, new_deleg, ls_list);
- new_deleg = NULL;
- NFSD_VNET(nfsstatsv1_p)->srvdelegates++;
- nfsrv_openpluslock++;
- nfsrv_delegatecnt++;
- }
+ nfsrv_issuedelegation(vp, clp, nd, delegate, writedeleg,
+ readonly, filerev, NFSVNO_EXRDONLY(exp), &new_deleg,
+ new_stp, lfp, rflagsp, delegstateidp);
} else {
new_open->ls_stateid.seqid = 1;
new_open->ls_stateid.other[0] = clp->lc_clientid.lval[0];
@@ -3269,52 +3245,9 @@ tryagain:
/*
* This is where we can choose to issue a delegation.
*/
- if ((new_stp->ls_flags & NFSLCK_WANTNODELEG) != 0)
- *rflagsp |= NFSV4OPEN_WDNOTWANTED;
- else if (nfsrv_issuedelegs == 0)
- *rflagsp |= NFSV4OPEN_WDSUPPFTYPE;
- else if (NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt))
- *rflagsp |= NFSV4OPEN_WDRESOURCE;
- else if (delegate == 0 || (writedeleg == 0 &&
- readonly == 0) || !NFSVNO_DELEGOK(vp) ||
- (clp->lc_flags & (LCL_CALLBACKSON | LCL_CBDOWN)) !=
- LCL_CALLBACKSON)
- *rflagsp |= NFSV4OPEN_WDCONTENTION;
- else {
- new_deleg->ls_stateid.seqid = delegstateidp->seqid = 1;
- new_deleg->ls_stateid.other[0] = delegstateidp->other[0]
- = clp->lc_clientid.lval[0];
- new_deleg->ls_stateid.other[1] = delegstateidp->other[1]
- = clp->lc_clientid.lval[1];
- new_deleg->ls_stateid.other[2] = delegstateidp->other[2]
- = nfsrv_nextstateindex(clp);
- if (writedeleg && !NFSVNO_EXRDONLY(exp) &&
- (nfsrv_writedelegifpos || !readonly) &&
- (new_stp->ls_flags & NFSLCK_WANTRDELEG) == 0) {
- new_deleg->ls_flags = (NFSLCK_DELEGWRITE |
- NFSLCK_READACCESS | NFSLCK_WRITEACCESS);
- *rflagsp |= NFSV4OPEN_WRITEDELEGATE;
- nfsrv_writedelegcnt++;
- } else {
- new_deleg->ls_flags = (NFSLCK_DELEGREAD |
- NFSLCK_READACCESS);
- *rflagsp |= NFSV4OPEN_READDELEGATE;
- }
- new_deleg->ls_uid = new_stp->ls_uid;
- new_deleg->ls_lfp = lfp;
- 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);
- LIST_INSERT_HEAD(&clp->lc_deleg, new_deleg, ls_list);
- new_deleg = NULL;
- NFSD_VNET(nfsstatsv1_p)->srvdelegates++;
- nfsrv_openpluslock++;
- nfsrv_delegatecnt++;
- }
+ nfsrv_issuedelegation(vp, clp, nd, delegate, writedeleg,
+ readonly, filerev, NFSVNO_EXRDONLY(exp), &new_deleg,
+ new_stp, lfp, rflagsp, delegstateidp);
}
} else {
/*
@@ -3337,78 +3270,28 @@ tryagain:
if (new_stp->ls_flags & NFSLCK_RECLAIM) {
new_stp->ls_flags = 0;
} else if ((nd->nd_flag & ND_NFSV41) != 0) {
- /* NFSv4.1 never needs confirmation. */
- new_stp->ls_flags = 0;
+ /*
+ * This is where we can choose to issue a delegation.
+ */
+ nfsrv_issuedelegation(vp, clp, nd, delegate, writedeleg,
+ readonly, filerev, NFSVNO_EXRDONLY(exp), &new_deleg,
+ new_stp, lfp, rflagsp, delegstateidp);
+ /* NFSv4.1 never needs confirmation. */
+ new_stp->ls_flags = 0;
- /*
- * This is where we can choose to issue a delegation.
- */
- if (delegate && nfsrv_issuedelegs &&
- (writedeleg || readonly) &&
- (clp->lc_flags & (LCL_CALLBACKSON | LCL_CBDOWN)) ==
- LCL_CALLBACKSON &&
- !NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt) &&
- NFSVNO_DELEGOK(vp) &&
- ((nd->nd_flag & ND_NFSV41) == 0 ||
- (new_stp->ls_flags & NFSLCK_WANTNODELEG) == 0)) {
- new_deleg->ls_stateid.seqid =
- delegstateidp->seqid = 1;
- new_deleg->ls_stateid.other[0] =
- delegstateidp->other[0]
- = clp->lc_clientid.lval[0];
- new_deleg->ls_stateid.other[1] =
- delegstateidp->other[1]
- = clp->lc_clientid.lval[1];
- new_deleg->ls_stateid.other[2] =
- delegstateidp->other[2]
- = nfsrv_nextstateindex(clp);
- if (writedeleg && !NFSVNO_EXRDONLY(exp) &&
- (nfsrv_writedelegifpos || !readonly) &&
- ((nd->nd_flag & ND_NFSV41) == 0 ||
- (new_stp->ls_flags & NFSLCK_WANTRDELEG) ==
- 0)) {
- new_deleg->ls_flags =
- (NFSLCK_DELEGWRITE |
- NFSLCK_READACCESS |
- NFSLCK_WRITEACCESS);
- *rflagsp |= NFSV4OPEN_WRITEDELEGATE;
- nfsrv_writedelegcnt++;
- } else {
- new_deleg->ls_flags =
- (NFSLCK_DELEGREAD |
- NFSLCK_READACCESS);
- *rflagsp |= NFSV4OPEN_READDELEGATE;
- }
- new_deleg->ls_uid = new_stp->ls_uid;
- new_deleg->ls_lfp = lfp;
- 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);
- LIST_INSERT_HEAD(&clp->lc_deleg, new_deleg,
- ls_list);
- new_deleg = NULL;
- NFSD_VNET(nfsstatsv1_p)->srvdelegates++;
- nfsrv_openpluslock++;
- nfsrv_delegatecnt++;
- }
- /*
- * Since NFSv4.1 never does an OpenConfirm, the first
- * open state will be acquired here.
- */
- if (!(clp->lc_flags & LCL_STAMPEDSTABLE)) {
- clp->lc_flags |= LCL_STAMPEDSTABLE;
- len = clp->lc_idlen;
- NFSBCOPY(clp->lc_id, clidp, len);
- gotstate = 1;
- }
+ /*
+ * Since NFSv4.1 never does an OpenConfirm, the first
+ * open state will be acquired here.
+ */
+ if (!(clp->lc_flags & LCL_STAMPEDSTABLE)) {
+ clp->lc_flags |= LCL_STAMPEDSTABLE;
+ len = clp->lc_idlen;
+ NFSBCOPY(clp->lc_id, clidp, len);
+ gotstate = 1;
+ }
} else {
- *rflagsp |= NFSV4OPEN_RESULTCONFIRM;
- new_stp->ls_flags = NFSLCK_NEEDSCONFIRM;
+ *rflagsp |= NFSV4OPEN_RESULTCONFIRM;
+ new_stp->ls_flags = NFSLCK_NEEDSCONFIRM;
}
nfsrvd_refcache(new_stp->ls_op);
new_stp->ls_noopens = 0;
@@ -5179,6 +5062,11 @@ nfsrv_markreclaim(struct nfsclient *clp)
* Now, just set the flag.
*/
sp->nst_flag |= NFSNST_RECLAIMED;
+
+ /*
+ * Free up any old delegations.
+ */
+ nfsrv_freedeleglist(&clp->lc_olddeleg);
}
/*
@@ -8943,3 +8831,86 @@ nfsrv_checkmachcred(int op, struct nfsrv_descript *nd, struct nfsclient *clp)
return (0);
return (NFSERR_AUTHERR | AUTH_TOOWEAK);
}
+
+/*
+ * Issue a delegation and, optionally set rflagsp for why not.
+ */
+static void
+nfsrv_issuedelegation(struct vnode *vp, struct nfsclient *clp,
+ struct nfsrv_descript *nd, int delegate, int writedeleg, int readonly,
+ u_quad_t filerev, uint64_t rdonly, struct nfsstate **new_delegp,
+ struct nfsstate *new_stp, struct nfslockfile *lfp, uint32_t *rflagsp,
+ nfsv4stateid_t *delegstateidp)
+{
+ struct nfsstate *up_deleg, *new_deleg;
+
+ new_deleg = *new_delegp;
+ up_deleg = LIST_FIRST(&lfp->lf_deleg);
+ if ((new_stp->ls_flags & NFSLCK_WANTNODELEG) != 0)
+ *rflagsp |= NFSV4OPEN_WDNOTWANTED;
+ else if (nfsrv_issuedelegs == 0)
+ *rflagsp |= NFSV4OPEN_WDSUPPFTYPE;
+ else if (NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt))
+ *rflagsp |= NFSV4OPEN_WDRESOURCE;
+ else if (delegate == 0 || !NFSVNO_DELEGOK(vp) ||
+ (writedeleg == 0 && (readonly == 0 ||
+ (new_stp->ls_flags & NFSLCK_WANTWDELEG) != 0)) ||
+ (clp->lc_flags & (LCL_CALLBACKSON | LCL_CBDOWN)) !=
+ LCL_CALLBACKSON) {
+ /* Is this a downgrade attempt? */
+ if (up_deleg != NULL && up_deleg->ls_clp == clp &&
+ (up_deleg->ls_flags & NFSLCK_DELEGWRITE) != 0 &&
+ (new_stp->ls_flags & NFSLCK_WANTRDELEG) != 0)
+ *rflagsp |= NFSV4OPEN_WDNOTSUPPDOWNGRADE;
+ else
+ *rflagsp |= NFSV4OPEN_WDCONTENTION;
+ } else if (up_deleg != NULL &&
+ (up_deleg->ls_flags & NFSLCK_DELEGREAD) != 0 &&
+ (new_stp->ls_flags & NFSLCK_WANTWDELEG) != 0) {
+ /* This is an atomic upgrade. */
+ up_deleg->ls_stateid.seqid++;
+ delegstateidp->seqid = up_deleg->ls_stateid.seqid;
+ delegstateidp->other[0] = up_deleg->ls_stateid.other[0];
+ delegstateidp->other[1] = up_deleg->ls_stateid.other[1];
+ delegstateidp->other[2] = up_deleg->ls_stateid.other[2];
+ up_deleg->ls_flags = (NFSLCK_DELEGWRITE |
+ NFSLCK_READACCESS | NFSLCK_WRITEACCESS);
+ *rflagsp |= NFSV4OPEN_WRITEDELEGATE;
+ nfsrv_writedelegcnt++;
+ } else {
+ new_deleg->ls_stateid.seqid = delegstateidp->seqid = 1;
+ new_deleg->ls_stateid.other[0] = delegstateidp->other[0]
+ = clp->lc_clientid.lval[0];
+ new_deleg->ls_stateid.other[1] = delegstateidp->other[1]
+ = clp->lc_clientid.lval[1];
+ new_deleg->ls_stateid.other[2] = delegstateidp->other[2]
+ = nfsrv_nextstateindex(clp);
+ if (writedeleg && !rdonly &&
+ (nfsrv_writedelegifpos || !readonly) &&
+ (new_stp->ls_flags & (NFSLCK_WANTRDELEG |
+ NFSLCK_WANTWDELEG)) != NFSLCK_WANTRDELEG) {
+ new_deleg->ls_flags = (NFSLCK_DELEGWRITE |
+ NFSLCK_READACCESS | NFSLCK_WRITEACCESS);
+ *rflagsp |= NFSV4OPEN_WRITEDELEGATE;
+ nfsrv_writedelegcnt++;
+ } else {
+ new_deleg->ls_flags = (NFSLCK_DELEGREAD |
+ NFSLCK_READACCESS);
+ *rflagsp |= NFSV4OPEN_READDELEGATE;
+ }
+ new_deleg->ls_uid = new_stp->ls_uid;
+ new_deleg->ls_lfp = lfp;
+ 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);
+ LIST_INSERT_HEAD(&clp->lc_deleg, new_deleg, ls_list);
+ *new_delegp = NULL;
+ NFSD_VNET(nfsstatsv1_p)->srvdelegates++;
+ nfsrv_openpluslock++;
+ nfsrv_delegatecnt++;
+ }
+}