Skip to content

Commit

Permalink
nfsd: trivial GET_DIR_DELEGATION support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
jtlayton authored and chucklever committed May 6, 2024
1 parent 38f080f commit 33a1e6e
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 2 deletions.
41 changes: 41 additions & 0 deletions fs/nfsd/nfs4proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down
76 changes: 74 additions & 2 deletions fs/nfsd/nfs4xdr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
19 changes: 19 additions & 0 deletions fs/nfsd/xdr4.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions include/linux/nfs4.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 33a1e6e

Please sign in to comment.