nfsd: trivial GET_DIR_DELEGATION support
authorJeff Layton <jlayton@kernel.org>
Sat, 23 Mar 2024 13:25:54 +0000 (09:25 -0400)
committerChuck Lever <chuck.lever@oracle.com>
Mon, 6 May 2024 13:07:17 +0000 (09:07 -0400)
This adds basic infrastructure for handing GET_DIR_DELEGATION calls from
clients, including the decoders and encoders. For now, it always just
returns NFS4_OK + GDD4_UNAVAIL.

Eventually clients may start sending this operation, and it's better if
we can return GDD4_UNAVAIL instead of having to abort the whole compound.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
fs/nfsd/nfs4proc.c
fs/nfsd/nfs4xdr.c
fs/nfsd/xdr4.h
include/linux/nfs4.h

index 2927b1263f086d8f0e64e8858d290c6a81822d36..3dc173b29803ff8f6e154d1c45fe8a9f005c10f7 100644 (file)
@@ -2154,6 +2154,29 @@ nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        return status == nfserr_same ? nfs_ok : status;
 }
 
+static __be32
+nfsd4_get_dir_delegation(struct svc_rqst *rqstp,
+                        struct nfsd4_compound_state *cstate,
+                        union nfsd4_op_u *u)
+{
+       struct nfsd4_get_dir_delegation *gdd = &u->get_dir_delegation;
+
+       /*
+        * RFC 8881, section 18.39.3 says:
+        *
+        * "The server may refuse to grant the delegation. In that case, the
+        *  server will return NFS4ERR_DIRDELEG_UNAVAIL."
+        *
+        * This is sub-optimal, since it means that the server would need to
+        * abort compound processing just because the delegation wasn't
+        * available. RFC8881bis should change this to allow the server to
+        * return NFS4_OK with a non-fatal status of GDD4_UNAVAIL in this
+        * situation.
+        */
+       gdd->gddrnf_status = GDD4_UNAVAIL;
+       return nfs_ok;
+}
+
 #ifdef CONFIG_NFSD_PNFS
 static const struct nfsd4_layout_ops *
 nfsd4_layout_verify(struct svc_export *exp, unsigned int layout_type)
@@ -3082,6 +3105,18 @@ static u32 nfsd4_copy_notify_rsize(const struct svc_rqst *rqstp,
                * sizeof(__be32);
 }
 
+static u32 nfsd4_get_dir_delegation_rsize(const struct svc_rqst *rqstp,
+                                         const struct nfsd4_op *op)
+{
+       return (op_encode_hdr_size +
+               1 /* gddr_status */ +
+               op_encode_verifier_maxsz +
+               op_encode_stateid_maxsz +
+               2 /* gddr_notification */ +
+               2 /* gddr_child_attributes */ +
+               2 /* gddr_dir_attributes */);
+}
+
 #ifdef CONFIG_NFSD_PNFS
 static u32 nfsd4_getdeviceinfo_rsize(const struct svc_rqst *rqstp,
                                     const struct nfsd4_op *op)
@@ -3470,6 +3505,12 @@ static const struct nfsd4_operation nfsd4_ops[] = {
                .op_get_currentstateid = nfsd4_get_freestateid,
                .op_rsize_bop = nfsd4_only_status_rsize,
        },
+       [OP_GET_DIR_DELEGATION] = {
+               .op_func = nfsd4_get_dir_delegation,
+               .op_flags = OP_MODIFIES_SOMETHING,
+               .op_name = "OP_GET_DIR_DELEGATION",
+               .op_rsize_bop = nfsd4_get_dir_delegation_rsize,
+       },
 #ifdef CONFIG_NFSD_PNFS
        [OP_GETDEVICEINFO] = {
                .op_func = nfsd4_getdeviceinfo,
index a644460f3a5e7a7e3f8574ab922fef1e94ccb753..ea76100e50caa120907ba8f9e007537522416a93 100644 (file)
@@ -1732,6 +1732,35 @@ nfsd4_decode_free_stateid(struct nfsd4_compoundargs *argp,
        return nfsd4_decode_stateid4(argp, &free_stateid->fr_stateid);
 }
 
+static __be32
+nfsd4_decode_get_dir_delegation(struct nfsd4_compoundargs *argp,
+               union nfsd4_op_u *u)
+{
+       struct nfsd4_get_dir_delegation *gdd = &u->get_dir_delegation;
+       __be32 status;
+
+       memset(gdd, 0, sizeof(*gdd));
+
+       if (xdr_stream_decode_bool(argp->xdr, &gdd->gdda_signal_deleg_avail) < 0)
+               return nfserr_bad_xdr;
+       status = nfsd4_decode_bitmap4(argp, gdd->gdda_notification_types,
+                                     ARRAY_SIZE(gdd->gdda_notification_types));
+       if (status)
+               return status;
+       status = nfsd4_decode_nfstime4(argp, &gdd->gdda_child_attr_delay);
+       if (status)
+               return status;
+       status = nfsd4_decode_nfstime4(argp, &gdd->gdda_dir_attr_delay);
+       if (status)
+               return status;
+       status = nfsd4_decode_bitmap4(argp, gdd->gdda_child_attributes,
+                                       ARRAY_SIZE(gdd->gdda_child_attributes));
+       if (status)
+               return status;
+       return nfsd4_decode_bitmap4(argp, gdd->gdda_dir_attributes,
+                                       ARRAY_SIZE(gdd->gdda_dir_attributes));
+}
+
 #ifdef CONFIG_NFSD_PNFS
 static __be32
 nfsd4_decode_getdeviceinfo(struct nfsd4_compoundargs *argp,
@@ -2370,7 +2399,7 @@ static const nfsd4_dec nfsd4_dec_ops[] = {
        [OP_CREATE_SESSION]     = nfsd4_decode_create_session,
        [OP_DESTROY_SESSION]    = nfsd4_decode_destroy_session,
        [OP_FREE_STATEID]       = nfsd4_decode_free_stateid,
-       [OP_GET_DIR_DELEGATION] = nfsd4_decode_notsupp,
+       [OP_GET_DIR_DELEGATION] = nfsd4_decode_get_dir_delegation,
 #ifdef CONFIG_NFSD_PNFS
        [OP_GETDEVICEINFO]      = nfsd4_decode_getdeviceinfo,
        [OP_GETDEVICELIST]      = nfsd4_decode_notsupp,
@@ -4963,6 +4992,49 @@ nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, __be32 nfserr,
        return nfs_ok;
 }
 
+static __be32
+nfsd4_encode_get_dir_delegation(struct nfsd4_compoundres *resp, __be32 nfserr,
+                               union nfsd4_op_u *u)
+{
+       struct nfsd4_get_dir_delegation *gdd = &u->get_dir_delegation;
+       struct xdr_stream *xdr = resp->xdr;
+       __be32 status = nfserr_resource;
+
+       switch(gdd->gddrnf_status) {
+       case GDD4_OK:
+               if (xdr_stream_encode_u32(xdr, GDD4_OK) != XDR_UNIT)
+                       break;
+               status = nfsd4_encode_verifier4(xdr, &gdd->gddr_cookieverf);
+               if (status)
+                       break;
+               status = nfsd4_encode_stateid4(xdr, &gdd->gddr_stateid);
+               if (status)
+                       break;
+               status = nfsd4_encode_bitmap4(xdr, gdd->gddr_notification[0], 0, 0);
+               if (status)
+                       break;
+               status = nfsd4_encode_bitmap4(xdr, gdd->gddr_child_attributes[0],
+                                                  gdd->gddr_child_attributes[1],
+                                                  gdd->gddr_child_attributes[2]);
+               if (status)
+                       break;
+               status = nfsd4_encode_bitmap4(xdr, gdd->gddr_dir_attributes[0],
+                                                  gdd->gddr_dir_attributes[1],
+                                                  gdd->gddr_dir_attributes[2]);
+               break;
+       default:
+               pr_warn("nfsd: bad gddrnf_status (%u)\n", gdd->gddrnf_status);
+               gdd->gddrnf_will_signal_deleg_avail = 0;
+               fallthrough;
+       case GDD4_UNAVAIL:
+               if (xdr_stream_encode_u32(xdr, GDD4_UNAVAIL) != XDR_UNIT)
+                       break;
+               status = nfsd4_encode_bool(xdr, gdd->gddrnf_will_signal_deleg_avail);
+               break;
+       }
+       return status;
+}
+
 #ifdef CONFIG_NFSD_PNFS
 static __be32
 nfsd4_encode_device_addr4(struct xdr_stream *xdr,
@@ -5579,7 +5651,7 @@ static const nfsd4_enc nfsd4_enc_ops[] = {
        [OP_CREATE_SESSION]     = nfsd4_encode_create_session,
        [OP_DESTROY_SESSION]    = nfsd4_encode_noop,
        [OP_FREE_STATEID]       = nfsd4_encode_noop,
-       [OP_GET_DIR_DELEGATION] = nfsd4_encode_noop,
+       [OP_GET_DIR_DELEGATION] = nfsd4_encode_get_dir_delegation,
 #ifdef CONFIG_NFSD_PNFS
        [OP_GETDEVICEINFO]      = nfsd4_encode_getdeviceinfo,
        [OP_GETDEVICELIST]      = nfsd4_encode_noop,
index 415516c1b27e5d896f7f2f22bc680481b9d468ee..446e72b0385e74e06900e3667e370f440a951bdc 100644 (file)
@@ -518,6 +518,24 @@ struct nfsd4_free_stateid {
        stateid_t       fr_stateid;         /* request */
 };
 
+struct nfsd4_get_dir_delegation {
+       /* request */
+       u32                     gdda_signal_deleg_avail;
+       u32                     gdda_notification_types[1];
+       struct timespec64       gdda_child_attr_delay;
+       struct timespec64       gdda_dir_attr_delay;
+       u32                     gdda_child_attributes[3];
+       u32                     gdda_dir_attributes[3];
+       /* response */
+       u32                     gddrnf_status;
+       nfs4_verifier           gddr_cookieverf;
+       stateid_t               gddr_stateid;
+       u32                     gddr_notification[1];
+       u32                     gddr_child_attributes[3];
+       u32                     gddr_dir_attributes[3];
+       bool                    gddrnf_will_signal_deleg_avail;
+};
+
 /* also used for NVERIFY */
 struct nfsd4_verify {
        u32             ve_bmval[3];        /* request */
@@ -797,6 +815,7 @@ struct nfsd4_op {
                struct nfsd4_reclaim_complete   reclaim_complete;
                struct nfsd4_test_stateid       test_stateid;
                struct nfsd4_free_stateid       free_stateid;
+               struct nfsd4_get_dir_delegation get_dir_delegation;
                struct nfsd4_getdeviceinfo      getdeviceinfo;
                struct nfsd4_layoutget          layoutget;
                struct nfsd4_layoutcommit       layoutcommit;
index ef8d2d618d5b343b5c831ca0fccd639fa3ce7e2f..0d896ce296cec53908035e271e14c50c6a474f55 100644 (file)
@@ -701,6 +701,12 @@ enum state_protect_how4 {
        SP4_SSV         = 2
 };
 
+/* GET_DIR_DELEGATION non-fatal status codes */
+enum gddrnf4_status {
+       GDD4_OK         = 0,
+       GDD4_UNAVAIL    = 1
+};
+
 enum pnfs_layouttype {
        LAYOUT_NFSV4_1_FILES  = 1,
        LAYOUT_OSD2_OBJECTS = 2,