[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <0b14aee6b687501e5d4eb4f0e62928fbe016af40.camel@kernel.org>
Date: Thu, 11 Apr 2024 18:48:34 -0400
From: Jeff Layton <jlayton@...nel.org>
To: Lorenzo Bianconi <lorenzo@...nel.org>, linux-nfs@...r.kernel.org
Cc: lorenzo.bianconi@...hat.com, chuck.lever@...cle.com, neilb@...e.de,
netdev@...r.kernel.org, kuba@...nel.org
Subject: Re: [PATCH v7 2/5] NFSD: add write_version to netlink command
On Thu, 2024-04-11 at 18:47 +0200, Lorenzo Bianconi wrote:
> Introduce write_version netlink command through a "declarative" interface.
> This patch introduces a change in behavior since for version-set userspace
> is expected to provide a NFS major/minor version list it wants to enable
> while all the other ones will be disabled. (procfs write_version
> command implements imperative interface where the admin writes +3/-3 to
> enable/disable a single version.
>
> Tested-by: Jeff Layton <jlayton@...nel.org>
> Signed-off-by: Lorenzo Bianconi <lorenzo@...nel.org>
> ---
> Documentation/netlink/specs/nfsd.yaml | 37 +++++++
> fs/nfsd/netlink.c | 24 ++++
> fs/nfsd/netlink.h | 5 +
> fs/nfsd/netns.h | 1 +
> fs/nfsd/nfsctl.c | 153 ++++++++++++++++++++++++++
> fs/nfsd/nfssvc.c | 3 +-
> include/uapi/linux/nfsd_netlink.h | 18 +++
> 7 files changed, 239 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/netlink/specs/nfsd.yaml b/Documentation/netlink/specs/nfsd.yaml
> index c92e1425d316..cb93e3e37119 100644
> --- a/Documentation/netlink/specs/nfsd.yaml
> +++ b/Documentation/netlink/specs/nfsd.yaml
> @@ -68,6 +68,26 @@ attribute-sets:
> -
> name: threads
> type: u32
> + -
> + name: version
> + attributes:
> + -
> + name: major
> + type: u32
> + -
> + name: minor
> + type: u32
> + -
> + name: enabled
> + type: flag
> + -
> + name: server-proto
> + attributes:
> + -
> + name: version
> + type: nest
> + nested-attributes: version
> + multi-attr: true
>
> operations:
> list:
> @@ -110,3 +130,20 @@ operations:
> reply:
> attributes:
> - threads
> + -
> + name: version-set
> + doc: set nfs enabled versions
> + attribute-set: server-proto
> + flags: [ admin-perm ]
> + do:
> + request:
> + attributes:
> + - version
> + -
> + name: version-get
> + doc: get nfs enabled versions
> + attribute-set: server-proto
> + do:
> + reply:
> + attributes:
> + - version
> diff --git a/fs/nfsd/netlink.c b/fs/nfsd/netlink.c
> index 1a59a8e6c7e2..75f609b57ceb 100644
> --- a/fs/nfsd/netlink.c
> +++ b/fs/nfsd/netlink.c
> @@ -10,11 +10,23 @@
>
> #include <uapi/linux/nfsd_netlink.h>
>
> +/* Common nested types */
> +const struct nla_policy nfsd_version_nl_policy[NFSD_A_VERSION_ENABLED + 1] = {
> + [NFSD_A_VERSION_MAJOR] = { .type = NLA_U32, },
> + [NFSD_A_VERSION_MINOR] = { .type = NLA_U32, },
> + [NFSD_A_VERSION_ENABLED] = { .type = NLA_FLAG, },
> +};
> +
> /* NFSD_CMD_THREADS_SET - do */
> static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_WORKER_THREADS + 1] = {
> [NFSD_A_SERVER_WORKER_THREADS] = { .type = NLA_U32, },
> };
>
> +/* NFSD_CMD_VERSION_SET - do */
> +static const struct nla_policy nfsd_version_set_nl_policy[NFSD_A_SERVER_PROTO_VERSION + 1] = {
> + [NFSD_A_SERVER_PROTO_VERSION] = NLA_POLICY_NESTED(nfsd_version_nl_policy),
> +};
> +
> /* Ops table for nfsd */
> static const struct genl_split_ops nfsd_nl_ops[] = {
> {
> @@ -36,6 +48,18 @@ static const struct genl_split_ops nfsd_nl_ops[] = {
> .doit = nfsd_nl_threads_get_doit,
> .flags = GENL_CMD_CAP_DO,
> },
> + {
> + .cmd = NFSD_CMD_VERSION_SET,
> + .doit = nfsd_nl_version_set_doit,
> + .policy = nfsd_version_set_nl_policy,
> + .maxattr = NFSD_A_SERVER_PROTO_VERSION,
> + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
> + },
> + {
> + .cmd = NFSD_CMD_VERSION_GET,
> + .doit = nfsd_nl_version_get_doit,
> + .flags = GENL_CMD_CAP_DO,
> + },
> };
>
> struct genl_family nfsd_nl_family __ro_after_init = {
> diff --git a/fs/nfsd/netlink.h b/fs/nfsd/netlink.h
> index 4137fac477e4..c7c0da275481 100644
> --- a/fs/nfsd/netlink.h
> +++ b/fs/nfsd/netlink.h
> @@ -11,6 +11,9 @@
>
> #include <uapi/linux/nfsd_netlink.h>
>
> +/* Common nested types */
> +extern const struct nla_policy nfsd_version_nl_policy[NFSD_A_VERSION_ENABLED + 1];
> +
> int nfsd_nl_rpc_status_get_start(struct netlink_callback *cb);
> int nfsd_nl_rpc_status_get_done(struct netlink_callback *cb);
>
> @@ -18,6 +21,8 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
> struct netlink_callback *cb);
> int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info);
> int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info);
> +int nfsd_nl_version_set_doit(struct sk_buff *skb, struct genl_info *info);
> +int nfsd_nl_version_get_doit(struct sk_buff *skb, struct genl_info *info);
>
> extern struct genl_family nfsd_nl_family;
>
> diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h
> index d4be519b5734..14ec15656320 100644
> --- a/fs/nfsd/netns.h
> +++ b/fs/nfsd/netns.h
> @@ -218,6 +218,7 @@ struct nfsd_net {
> /* Simple check to find out if a given net was properly initialized */
> #define nfsd_netns_ready(nn) ((nn)->sessionid_hashtbl)
>
> +extern bool nfsd_support_version(int vers);
> extern void nfsd_netns_free_versions(struct nfsd_net *nn);
>
> extern unsigned int nfsd_net_id;
> diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
> index 71608744e67f..341efab4eaa7 100644
> --- a/fs/nfsd/nfsctl.c
> +++ b/fs/nfsd/nfsctl.c
> @@ -1711,6 +1711,159 @@ int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info)
> return err;
> }
>
> +/**
> + * nfsd_nl_version_set_doit - set the nfs enabled versions
> + * @skb: reply buffer
> + * @info: netlink metadata and command arguments
> + *
> + * Return 0 on success or a negative errno.
> + */
> +int nfsd_nl_version_set_doit(struct sk_buff *skb, struct genl_info *info)
> +{
> + const struct nlattr *attr;
> + struct nfsd_net *nn;
> + int i, rem;
> +
> + if (GENL_REQ_ATTR_CHECK(info, NFSD_A_SERVER_PROTO_VERSION))
> + return -EINVAL;
> +
> + mutex_lock(&nfsd_mutex);
> +
> + nn = net_generic(genl_info_net(info), nfsd_net_id);
> + if (nn->nfsd_serv) {
> + mutex_unlock(&nfsd_mutex);
> + return -EBUSY;
> + }
> +
> + /* clear current supported versions. */
> + nfsd_vers(nn, 2, NFSD_CLEAR);
> + nfsd_vers(nn, 3, NFSD_CLEAR);
> + for (i = 0; i <= NFSD_SUPPORTED_MINOR_VERSION; i++)
> + nfsd_minorversion(nn, i, NFSD_CLEAR);
> +
> + nlmsg_for_each_attr(attr, info->nlhdr, GENL_HDRLEN, rem) {
> + struct nlattr *tb[NFSD_A_VERSION_MAX + 1];
> + u32 major, minor = 0;
> + bool enabled;
> +
> + if (nla_type(attr) != NFSD_A_SERVER_PROTO_VERSION)
> + continue;
> +
> + if (nla_parse_nested(tb, NFSD_A_VERSION_MAX, attr,
> + nfsd_version_nl_policy, info->extack) < 0)
> + continue;
> +
> + if (!tb[NFSD_A_VERSION_MAJOR])
> + continue;
> +
> + major = nla_get_u32(tb[NFSD_A_VERSION_MAJOR]);
> + if (tb[NFSD_A_VERSION_MINOR])
> + minor = nla_get_u32(tb[NFSD_A_VERSION_MINOR]);
> +
> + enabled = nla_get_flag(tb[NFSD_A_VERSION_ENABLED]);
> +
> + switch (major) {
> + case 4:
> + nfsd_minorversion(nn, minor, enabled ? NFSD_SET : NFSD_CLEAR);
> + break;
> + case 3:
> + case 2:
> + if (!minor)
> + nfsd_vers(nn, major, enabled ? NFSD_SET : NFSD_CLEAR);
> + break;
> + default:
> + break;
> + }
> + }
> +
> + mutex_unlock(&nfsd_mutex);
> +
> + return 0;
> +}
> +
> +/**
> + * nfsd_nl_version_get_doit - get the nfs enabled versions
> + * @skb: reply buffer
> + * @info: netlink metadata and command arguments
> + *
> + * Return 0 on success or a negative errno.
> + */
> +int nfsd_nl_version_get_doit(struct sk_buff *skb, struct genl_info *info)
> +{
> + struct nfsd_net *nn;
> + int i, err;
> + void *hdr;
> +
> + skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
> + if (!skb)
> + return -ENOMEM;
> +
> + hdr = genlmsg_iput(skb, info);
> + if (!hdr) {
> + err = -EMSGSIZE;
> + goto err_free_msg;
> + }
> +
> + mutex_lock(&nfsd_mutex);
> + nn = net_generic(genl_info_net(info), nfsd_net_id);
> +
> + for (i = 2; i <= 4; i++) {
> + int j;
> +
> + for (j = 0; j <= NFSD_SUPPORTED_MINOR_VERSION; j++) {
> + struct nlattr *attr;
> +
> + /* Don't record any versions the kernel doesn't have
> + * compiled in
> + */
> + if (!nfsd_support_version(i))
> + continue;
> +
> + /* NFSv{2,3} does not support minor numbers */
> + if (i < 4 && j)
> + continue;
> +
> + if (i == 4 && !nfsd_minorversion(nn, j, NFSD_TEST))
> + continue;
> +
I think this is not quite right. We don't want to skip returning a
minorversion just because it's not enabled. We want to return the entry
with the enabled flag cleared. I'd suggest just dropping the above if
statement entirely.
> + attr = nla_nest_start_noflag(skb,
> + NFSD_A_SERVER_PROTO_VERSION);
> + if (!attr) {
> + err = -EINVAL;
> + goto err_nfsd_unlock;
> + }
> +
> + if (nla_put_u32(skb, NFSD_A_VERSION_MAJOR, i) ||
> + nla_put_u32(skb, NFSD_A_VERSION_MINOR, j)) {
> + err = -EINVAL;
> + goto err_nfsd_unlock;
> + }
> +
> + /* Set the enabled flag if the version is enabled */
> + if (nfsd_vers(nn, i, NFSD_TEST) &&
> + (i < 4 || nfsd_minorversion(nn, j, NFSD_TEST)) &&
> + nla_put_flag(skb, NFSD_A_VERSION_ENABLED)) {
> + err = -EINVAL;
> + goto err_nfsd_unlock;
> + }
> +
> + nla_nest_end(skb, attr);
> + }
> + }
> +
> + mutex_unlock(&nfsd_mutex);
> + genlmsg_end(skb, hdr);
> +
> + return genlmsg_reply(skb, info);
> +
> +err_nfsd_unlock:
> + mutex_unlock(&nfsd_mutex);
> +err_free_msg:
> + nlmsg_free(skb);
> +
> + return err;
> +}
> +
> /**
> * nfsd_net_init - Prepare the nfsd_net portion of a new net namespace
> * @net: a freshly-created network namespace
> diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
> index c0d17b92b249..4452a9bb9bbb 100644
> --- a/fs/nfsd/nfssvc.c
> +++ b/fs/nfsd/nfssvc.c
> @@ -133,8 +133,7 @@ struct svc_program nfsd_program = {
> .pg_rpcbind_set = nfsd_rpcbind_set,
> };
>
> -static bool
> -nfsd_support_version(int vers)
> +bool nfsd_support_version(int vers)
> {
> if (vers >= NFSD_MINVERS && vers < NFSD_NRVERS)
> return nfsd_version[vers] != NULL;
> diff --git a/include/uapi/linux/nfsd_netlink.h b/include/uapi/linux/nfsd_netlink.h
> index 1b6fe1f9ed0e..ccf3e15fe160 100644
> --- a/include/uapi/linux/nfsd_netlink.h
> +++ b/include/uapi/linux/nfsd_netlink.h
> @@ -36,10 +36,28 @@ enum {
> NFSD_A_SERVER_WORKER_MAX = (__NFSD_A_SERVER_WORKER_MAX - 1)
> };
>
> +enum {
> + NFSD_A_VERSION_MAJOR = 1,
> + NFSD_A_VERSION_MINOR,
> + NFSD_A_VERSION_ENABLED,
> +
> + __NFSD_A_VERSION_MAX,
> + NFSD_A_VERSION_MAX = (__NFSD_A_VERSION_MAX - 1)
> +};
> +
> +enum {
> + NFSD_A_SERVER_PROTO_VERSION = 1,
> +
> + __NFSD_A_SERVER_PROTO_MAX,
> + NFSD_A_SERVER_PROTO_MAX = (__NFSD_A_SERVER_PROTO_MAX - 1)
> +};
> +
> enum {
> NFSD_CMD_RPC_STATUS_GET = 1,
> NFSD_CMD_THREADS_SET,
> NFSD_CMD_THREADS_GET,
> + NFSD_CMD_VERSION_SET,
> + NFSD_CMD_VERSION_GET,
>
> __NFSD_CMD_MAX,
> NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)
--
Jeff Layton <jlayton@...nel.org>
Powered by blists - more mailing lists