The branch stable/14 has been updated by rmacklem:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=98c53b0b55401eaed374b235a40f3a547a5ab4e9

commit 98c53b0b55401eaed374b235a40f3a547a5ab4e9
Author:     Rick Macklem <rmack...@freebsd.org>
AuthorDate: 2024-06-05 01:46:41 +0000
Commit:     Rick Macklem <rmack...@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++;
+       }
+}

Reply via email to