diff mbox series

[v8,2/6] NFSD: convert write_threads to netlink command

Message ID 4ff777ebb8652e31709bd91c3af50693edf86a26.1713209938.git.lorenzo@kernel.org (mailing list archive)
State New
Headers show
Series convert write_threads, write_version and write_ports to netlink commands | expand

Commit Message

Lorenzo Bianconi April 15, 2024, 7:44 p.m. UTC
Introduce write_threads netlink command similar to the one available
through the procfs.

Tested-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Co-developed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
 Documentation/netlink/specs/nfsd.yaml |  33 ++++++++
 fs/nfsd/netlink.c                     |  19 +++++
 fs/nfsd/netlink.h                     |   2 +
 fs/nfsd/nfsctl.c                      | 104 ++++++++++++++++++++++++++
 include/uapi/linux/nfsd_netlink.h     |  11 +++
 5 files changed, 169 insertions(+)

Comments

NeilBrown April 16, 2024, 9:55 p.m. UTC | #1
On Tue, 16 Apr 2024, Lorenzo Bianconi wrote:
> Introduce write_threads netlink command similar to the one available
> through the procfs.

I think this should support write_pool_threads too.
i.e.  the number of threads should be an array.  If it is a singleton,
the it does write_threads.   If larger it does write_pool_threads.
I don't think we want to add a separate command later for pool_threads.

NeilBrown


> 
> Tested-by: Jeff Layton <jlayton@kernel.org>
> Reviewed-by: Jeff Layton <jlayton@kernel.org>
> Co-developed-by: Jeff Layton <jlayton@kernel.org>
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
>  Documentation/netlink/specs/nfsd.yaml |  33 ++++++++
>  fs/nfsd/netlink.c                     |  19 +++++
>  fs/nfsd/netlink.h                     |   2 +
>  fs/nfsd/nfsctl.c                      | 104 ++++++++++++++++++++++++++
>  include/uapi/linux/nfsd_netlink.h     |  11 +++
>  5 files changed, 169 insertions(+)
> 
> diff --git a/Documentation/netlink/specs/nfsd.yaml b/Documentation/netlink/specs/nfsd.yaml
> index 05acc73e2e33..cbe6c5fd6c4d 100644
> --- a/Documentation/netlink/specs/nfsd.yaml
> +++ b/Documentation/netlink/specs/nfsd.yaml
> @@ -62,6 +62,18 @@ attribute-sets:
>          name: compound-ops
>          type: u32
>          multi-attr: true
> +  -
> +    name: server-worker
> +    attributes:
> +      -
> +        name: threads
> +        type: u32
> +      -
> +        name: gracetime
> +        type: u32
> +      -
> +        name: leasetime
> +        type: u32
>  
>  operations:
>    list:
> @@ -87,3 +99,24 @@ operations:
>              - sport
>              - dport
>              - compound-ops
> +    -
> +      name: threads-set
> +      doc: set the number of running threads
> +      attribute-set: server-worker
> +      flags: [ admin-perm ]
> +      do:
> +        request:
> +          attributes:
> +            - threads
> +            - gracetime
> +            - leasetime
> +    -
> +      name: threads-get
> +      doc: get the number of running threads
> +      attribute-set: server-worker
> +      do:
> +        reply:
> +          attributes:
> +            - threads
> +            - gracetime
> +            - leasetime
> diff --git a/fs/nfsd/netlink.c b/fs/nfsd/netlink.c
> index 0e1d635ec5f9..20a646af0324 100644
> --- a/fs/nfsd/netlink.c
> +++ b/fs/nfsd/netlink.c
> @@ -10,6 +10,13 @@
>  
>  #include <uapi/linux/nfsd_netlink.h>
>  
> +/* NFSD_CMD_THREADS_SET - do */
> +static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_WORKER_LEASETIME + 1] = {
> +	[NFSD_A_SERVER_WORKER_THREADS] = { .type = NLA_U32, },
> +	[NFSD_A_SERVER_WORKER_GRACETIME] = { .type = NLA_U32, },
> +	[NFSD_A_SERVER_WORKER_LEASETIME] = { .type = NLA_U32, },
> +};
> +
>  /* Ops table for nfsd */
>  static const struct genl_split_ops nfsd_nl_ops[] = {
>  	{
> @@ -19,6 +26,18 @@ static const struct genl_split_ops nfsd_nl_ops[] = {
>  		.done	= nfsd_nl_rpc_status_get_done,
>  		.flags	= GENL_CMD_CAP_DUMP,
>  	},
> +	{
> +		.cmd		= NFSD_CMD_THREADS_SET,
> +		.doit		= nfsd_nl_threads_set_doit,
> +		.policy		= nfsd_threads_set_nl_policy,
> +		.maxattr	= NFSD_A_SERVER_WORKER_LEASETIME,
> +		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
> +	},
> +	{
> +		.cmd	= NFSD_CMD_THREADS_GET,
> +		.doit	= nfsd_nl_threads_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 d83dd6bdee92..4137fac477e4 100644
> --- a/fs/nfsd/netlink.h
> +++ b/fs/nfsd/netlink.h
> @@ -16,6 +16,8 @@ int nfsd_nl_rpc_status_get_done(struct netlink_callback *cb);
>  
>  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);
>  
>  extern struct genl_family nfsd_nl_family;
>  
> diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
> index f2e442d7fe16..38a5df03981b 100644
> --- a/fs/nfsd/nfsctl.c
> +++ b/fs/nfsd/nfsctl.c
> @@ -1653,6 +1653,110 @@ int nfsd_nl_rpc_status_get_done(struct netlink_callback *cb)
>  	return 0;
>  }
>  
> +/**
> + * nfsd_nl_threads_set_doit - set the number of running threads
> + * @skb: reply buffer
> + * @info: netlink metadata and command arguments
> + *
> + * Return 0 on success or a negative errno.
> + */
> +int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct net *net = genl_info_net(info);
> +	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
> +	int ret = -EBUSY;
> +	u32 nthreads;
> +
> +	if (GENL_REQ_ATTR_CHECK(info, NFSD_A_SERVER_WORKER_THREADS))
> +		return -EINVAL;
> +
> +	nthreads = nla_get_u32(info->attrs[NFSD_A_SERVER_WORKER_THREADS]);
> +
> +	mutex_lock(&nfsd_mutex);
> +	if (info->attrs[NFSD_A_SERVER_WORKER_GRACETIME] ||
> +	    info->attrs[NFSD_A_SERVER_WORKER_LEASETIME]) {
> +		const struct nlattr *attr;
> +
> +		if (nn->nfsd_serv && nn->nfsd_serv->sv_nrthreads)
> +			goto out_unlock;
> +
> +		ret = -EINVAL;
> +		attr = info->attrs[NFSD_A_SERVER_WORKER_GRACETIME];
> +		if (attr) {
> +			u32 gracetime = nla_get_u32(attr);
> +
> +			if (gracetime < 10 || gracetime > 3600)
> +				goto out_unlock;
> +
> +			nn->nfsd4_grace = gracetime;
> +		}
> +
> +		attr = info->attrs[NFSD_A_SERVER_WORKER_LEASETIME];
> +		if (attr) {
> +			u32 leasetime = nla_get_u32(attr);
> +
> +			if (leasetime < 10 || leasetime > 3600)
> +				goto out_unlock;
> +
> +			nn->nfsd4_lease = leasetime;
> +		}
> +	}
> +
> +	ret = nfsd_svc(nthreads, net, get_current_cred());
> +out_unlock:
> +	mutex_unlock(&nfsd_mutex);
> +
> +	return ret == nthreads ? 0 : ret;
> +}
> +
> +/**
> + * nfsd_nl_threads_get_doit - get the number of running threads
> + * @skb: reply buffer
> + * @info: netlink metadata and command arguments
> + *
> + * Return 0 on success or a negative errno.
> + */
> +int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct net *net = genl_info_net(info);
> +	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
> +	void *hdr;
> +	int err;
> +
> +	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);
> +	err = nla_put_u32(skb, NFSD_A_SERVER_WORKER_GRACETIME,
> +			  nn->nfsd4_grace) ||
> +	      nla_put_u32(skb, NFSD_A_SERVER_WORKER_LEASETIME,
> +			  nn->nfsd4_lease) ||
> +	      nla_put_u32(skb, NFSD_A_SERVER_WORKER_THREADS,
> +			  nn->nfsd_serv ? nn->nfsd_serv->sv_nrthreads : 0);
> +	mutex_unlock(&nfsd_mutex);
> +
> +	if (err) {
> +		err = -EINVAL;
> +		goto err_free_msg;
> +	}
> +
> +	genlmsg_end(skb, hdr);
> +
> +	return genlmsg_reply(skb, info);
> +
> +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/include/uapi/linux/nfsd_netlink.h b/include/uapi/linux/nfsd_netlink.h
> index 3cd044edee5d..ccc78a5ee650 100644
> --- a/include/uapi/linux/nfsd_netlink.h
> +++ b/include/uapi/linux/nfsd_netlink.h
> @@ -29,8 +29,19 @@ enum {
>  	NFSD_A_RPC_STATUS_MAX = (__NFSD_A_RPC_STATUS_MAX - 1)
>  };
>  
> +enum {
> +	NFSD_A_SERVER_WORKER_THREADS = 1,
> +	NFSD_A_SERVER_WORKER_GRACETIME,
> +	NFSD_A_SERVER_WORKER_LEASETIME,
> +
> +	__NFSD_A_SERVER_WORKER_MAX,
> +	NFSD_A_SERVER_WORKER_MAX = (__NFSD_A_SERVER_WORKER_MAX - 1)
> +};
> +
>  enum {
>  	NFSD_CMD_RPC_STATUS_GET = 1,
> +	NFSD_CMD_THREADS_SET,
> +	NFSD_CMD_THREADS_GET,
>  
>  	__NFSD_CMD_MAX,
>  	NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)
> -- 
> 2.44.0
> 
>
NeilBrown April 16, 2024, 10:28 p.m. UTC | #2
On Tue, 16 Apr 2024, Lorenzo Bianconi wrote:
> Introduce write_threads netlink command similar to the one available
> through the procfs.
> 
> Tested-by: Jeff Layton <jlayton@kernel.org>
> Reviewed-by: Jeff Layton <jlayton@kernel.org>
> Co-developed-by: Jeff Layton <jlayton@kernel.org>
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
>  Documentation/netlink/specs/nfsd.yaml |  33 ++++++++
>  fs/nfsd/netlink.c                     |  19 +++++
>  fs/nfsd/netlink.h                     |   2 +
>  fs/nfsd/nfsctl.c                      | 104 ++++++++++++++++++++++++++
>  include/uapi/linux/nfsd_netlink.h     |  11 +++
>  5 files changed, 169 insertions(+)
> 
> diff --git a/Documentation/netlink/specs/nfsd.yaml b/Documentation/netlink/specs/nfsd.yaml
> index 05acc73e2e33..cbe6c5fd6c4d 100644
> --- a/Documentation/netlink/specs/nfsd.yaml
> +++ b/Documentation/netlink/specs/nfsd.yaml
> @@ -62,6 +62,18 @@ attribute-sets:
>          name: compound-ops
>          type: u32
>          multi-attr: true
> +  -
> +    name: server-worker
> +    attributes:
> +      -
> +        name: threads
> +        type: u32
> +      -
> +        name: gracetime
> +        type: u32
> +      -
> +        name: leasetime
> +        type: u32

Another thought: I would be really happy if the "scope" were another
optional argument here.  The mechanism of setting the scope by user the
hostname works but is ugly.  I'm inclined to ignore the hostname
completely when netlink is used, but I'm not completely sure about that.
(aside - I think using the hostname for the default scope was a really
bad idea.  It should have been a fixed string like "Linux").

NeilBrown



>  
>  operations:
>    list:
> @@ -87,3 +99,24 @@ operations:
>              - sport
>              - dport
>              - compound-ops
> +    -
> +      name: threads-set
> +      doc: set the number of running threads
> +      attribute-set: server-worker
> +      flags: [ admin-perm ]
> +      do:
> +        request:
> +          attributes:
> +            - threads
> +            - gracetime
> +            - leasetime
> +    -
> +      name: threads-get
> +      doc: get the number of running threads
> +      attribute-set: server-worker
> +      do:
> +        reply:
> +          attributes:
> +            - threads
> +            - gracetime
> +            - leasetime
> diff --git a/fs/nfsd/netlink.c b/fs/nfsd/netlink.c
> index 0e1d635ec5f9..20a646af0324 100644
> --- a/fs/nfsd/netlink.c
> +++ b/fs/nfsd/netlink.c
> @@ -10,6 +10,13 @@
>  
>  #include <uapi/linux/nfsd_netlink.h>
>  
> +/* NFSD_CMD_THREADS_SET - do */
> +static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_WORKER_LEASETIME + 1] = {
> +	[NFSD_A_SERVER_WORKER_THREADS] = { .type = NLA_U32, },
> +	[NFSD_A_SERVER_WORKER_GRACETIME] = { .type = NLA_U32, },
> +	[NFSD_A_SERVER_WORKER_LEASETIME] = { .type = NLA_U32, },
> +};
> +
>  /* Ops table for nfsd */
>  static const struct genl_split_ops nfsd_nl_ops[] = {
>  	{
> @@ -19,6 +26,18 @@ static const struct genl_split_ops nfsd_nl_ops[] = {
>  		.done	= nfsd_nl_rpc_status_get_done,
>  		.flags	= GENL_CMD_CAP_DUMP,
>  	},
> +	{
> +		.cmd		= NFSD_CMD_THREADS_SET,
> +		.doit		= nfsd_nl_threads_set_doit,
> +		.policy		= nfsd_threads_set_nl_policy,
> +		.maxattr	= NFSD_A_SERVER_WORKER_LEASETIME,
> +		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
> +	},
> +	{
> +		.cmd	= NFSD_CMD_THREADS_GET,
> +		.doit	= nfsd_nl_threads_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 d83dd6bdee92..4137fac477e4 100644
> --- a/fs/nfsd/netlink.h
> +++ b/fs/nfsd/netlink.h
> @@ -16,6 +16,8 @@ int nfsd_nl_rpc_status_get_done(struct netlink_callback *cb);
>  
>  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);
>  
>  extern struct genl_family nfsd_nl_family;
>  
> diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
> index f2e442d7fe16..38a5df03981b 100644
> --- a/fs/nfsd/nfsctl.c
> +++ b/fs/nfsd/nfsctl.c
> @@ -1653,6 +1653,110 @@ int nfsd_nl_rpc_status_get_done(struct netlink_callback *cb)
>  	return 0;
>  }
>  
> +/**
> + * nfsd_nl_threads_set_doit - set the number of running threads
> + * @skb: reply buffer
> + * @info: netlink metadata and command arguments
> + *
> + * Return 0 on success or a negative errno.
> + */
> +int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct net *net = genl_info_net(info);
> +	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
> +	int ret = -EBUSY;
> +	u32 nthreads;
> +
> +	if (GENL_REQ_ATTR_CHECK(info, NFSD_A_SERVER_WORKER_THREADS))
> +		return -EINVAL;
> +
> +	nthreads = nla_get_u32(info->attrs[NFSD_A_SERVER_WORKER_THREADS]);
> +
> +	mutex_lock(&nfsd_mutex);
> +	if (info->attrs[NFSD_A_SERVER_WORKER_GRACETIME] ||
> +	    info->attrs[NFSD_A_SERVER_WORKER_LEASETIME]) {
> +		const struct nlattr *attr;
> +
> +		if (nn->nfsd_serv && nn->nfsd_serv->sv_nrthreads)
> +			goto out_unlock;
> +
> +		ret = -EINVAL;
> +		attr = info->attrs[NFSD_A_SERVER_WORKER_GRACETIME];
> +		if (attr) {
> +			u32 gracetime = nla_get_u32(attr);
> +
> +			if (gracetime < 10 || gracetime > 3600)
> +				goto out_unlock;
> +
> +			nn->nfsd4_grace = gracetime;
> +		}
> +
> +		attr = info->attrs[NFSD_A_SERVER_WORKER_LEASETIME];
> +		if (attr) {
> +			u32 leasetime = nla_get_u32(attr);
> +
> +			if (leasetime < 10 || leasetime > 3600)
> +				goto out_unlock;
> +
> +			nn->nfsd4_lease = leasetime;
> +		}
> +	}
> +
> +	ret = nfsd_svc(nthreads, net, get_current_cred());
> +out_unlock:
> +	mutex_unlock(&nfsd_mutex);
> +
> +	return ret == nthreads ? 0 : ret;
> +}
> +
> +/**
> + * nfsd_nl_threads_get_doit - get the number of running threads
> + * @skb: reply buffer
> + * @info: netlink metadata and command arguments
> + *
> + * Return 0 on success or a negative errno.
> + */
> +int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct net *net = genl_info_net(info);
> +	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
> +	void *hdr;
> +	int err;
> +
> +	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);
> +	err = nla_put_u32(skb, NFSD_A_SERVER_WORKER_GRACETIME,
> +			  nn->nfsd4_grace) ||
> +	      nla_put_u32(skb, NFSD_A_SERVER_WORKER_LEASETIME,
> +			  nn->nfsd4_lease) ||
> +	      nla_put_u32(skb, NFSD_A_SERVER_WORKER_THREADS,
> +			  nn->nfsd_serv ? nn->nfsd_serv->sv_nrthreads : 0);
> +	mutex_unlock(&nfsd_mutex);
> +
> +	if (err) {
> +		err = -EINVAL;
> +		goto err_free_msg;
> +	}
> +
> +	genlmsg_end(skb, hdr);
> +
> +	return genlmsg_reply(skb, info);
> +
> +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/include/uapi/linux/nfsd_netlink.h b/include/uapi/linux/nfsd_netlink.h
> index 3cd044edee5d..ccc78a5ee650 100644
> --- a/include/uapi/linux/nfsd_netlink.h
> +++ b/include/uapi/linux/nfsd_netlink.h
> @@ -29,8 +29,19 @@ enum {
>  	NFSD_A_RPC_STATUS_MAX = (__NFSD_A_RPC_STATUS_MAX - 1)
>  };
>  
> +enum {
> +	NFSD_A_SERVER_WORKER_THREADS = 1,
> +	NFSD_A_SERVER_WORKER_GRACETIME,
> +	NFSD_A_SERVER_WORKER_LEASETIME,
> +
> +	__NFSD_A_SERVER_WORKER_MAX,
> +	NFSD_A_SERVER_WORKER_MAX = (__NFSD_A_SERVER_WORKER_MAX - 1)
> +};
> +
>  enum {
>  	NFSD_CMD_RPC_STATUS_GET = 1,
> +	NFSD_CMD_THREADS_SET,
> +	NFSD_CMD_THREADS_GET,
>  
>  	__NFSD_CMD_MAX,
>  	NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)
> -- 
> 2.44.0
> 
>
Jeffrey Layton April 16, 2024, 10:39 p.m. UTC | #3
On Wed, 2024-04-17 at 07:55 +1000, NeilBrown wrote:
> On Tue, 16 Apr 2024, Lorenzo Bianconi wrote:
> > Introduce write_threads netlink command similar to the one available
> > through the procfs.
> 
> I think this should support write_pool_threads too.
> i.e.  the number of threads should be an array.  If it is a singleton,
> the it does write_threads.   If larger it does write_pool_threads.
> I don't think we want to add a separate command later for pool_threads.
> 
> NeilBrown
> 

That is a very good point. We'll take a closer look at the current
pool_threads interface and see how we can extend this one to cover that
use case. Making it an array sounds reasonable at first blush.

> 
> > 
> > Tested-by: Jeff Layton <jlayton@kernel.org>
> > Reviewed-by: Jeff Layton <jlayton@kernel.org>
> > Co-developed-by: Jeff Layton <jlayton@kernel.org>
> > Signed-off-by: Jeff Layton <jlayton@kernel.org>
> > Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> > ---
> >  Documentation/netlink/specs/nfsd.yaml |  33 ++++++++
> >  fs/nfsd/netlink.c                     |  19 +++++
> >  fs/nfsd/netlink.h                     |   2 +
> >  fs/nfsd/nfsctl.c                      | 104 ++++++++++++++++++++++++++
> >  include/uapi/linux/nfsd_netlink.h     |  11 +++
> >  5 files changed, 169 insertions(+)
> > 
> > diff --git a/Documentation/netlink/specs/nfsd.yaml b/Documentation/netlink/specs/nfsd.yaml
> > index 05acc73e2e33..cbe6c5fd6c4d 100644
> > --- a/Documentation/netlink/specs/nfsd.yaml
> > +++ b/Documentation/netlink/specs/nfsd.yaml
> > @@ -62,6 +62,18 @@ attribute-sets:
> >          name: compound-ops
> >          type: u32
> >          multi-attr: true
> > +  -
> > +    name: server-worker
> > +    attributes:
> > +      -
> > +        name: threads
> > +        type: u32
> > +      -
> > +        name: gracetime
> > +        type: u32
> > +      -
> > +        name: leasetime
> > +        type: u32
> >  
> >  operations:
> >    list:
> > @@ -87,3 +99,24 @@ operations:
> >              - sport
> >              - dport
> >              - compound-ops
> > +    -
> > +      name: threads-set
> > +      doc: set the number of running threads
> > +      attribute-set: server-worker
> > +      flags: [ admin-perm ]
> > +      do:
> > +        request:
> > +          attributes:
> > +            - threads
> > +            - gracetime
> > +            - leasetime
> > +    -
> > +      name: threads-get
> > +      doc: get the number of running threads
> > +      attribute-set: server-worker
> > +      do:
> > +        reply:
> > +          attributes:
> > +            - threads
> > +            - gracetime
> > +            - leasetime
> > diff --git a/fs/nfsd/netlink.c b/fs/nfsd/netlink.c
> > index 0e1d635ec5f9..20a646af0324 100644
> > --- a/fs/nfsd/netlink.c
> > +++ b/fs/nfsd/netlink.c
> > @@ -10,6 +10,13 @@
> >  
> >  #include <uapi/linux/nfsd_netlink.h>
> >  
> > +/* NFSD_CMD_THREADS_SET - do */
> > +static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_WORKER_LEASETIME + 1] = {
> > +	[NFSD_A_SERVER_WORKER_THREADS] = { .type = NLA_U32, },
> > +	[NFSD_A_SERVER_WORKER_GRACETIME] = { .type = NLA_U32, },
> > +	[NFSD_A_SERVER_WORKER_LEASETIME] = { .type = NLA_U32, },
> > +};
> > +
> >  /* Ops table for nfsd */
> >  static const struct genl_split_ops nfsd_nl_ops[] = {
> >  	{
> > @@ -19,6 +26,18 @@ static const struct genl_split_ops nfsd_nl_ops[] = {
> >  		.done	= nfsd_nl_rpc_status_get_done,
> >  		.flags	= GENL_CMD_CAP_DUMP,
> >  	},
> > +	{
> > +		.cmd		= NFSD_CMD_THREADS_SET,
> > +		.doit		= nfsd_nl_threads_set_doit,
> > +		.policy		= nfsd_threads_set_nl_policy,
> > +		.maxattr	= NFSD_A_SERVER_WORKER_LEASETIME,
> > +		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
> > +	},
> > +	{
> > +		.cmd	= NFSD_CMD_THREADS_GET,
> > +		.doit	= nfsd_nl_threads_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 d83dd6bdee92..4137fac477e4 100644
> > --- a/fs/nfsd/netlink.h
> > +++ b/fs/nfsd/netlink.h
> > @@ -16,6 +16,8 @@ int nfsd_nl_rpc_status_get_done(struct netlink_callback *cb);
> >  
> >  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);
> >  
> >  extern struct genl_family nfsd_nl_family;
> >  
> > diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
> > index f2e442d7fe16..38a5df03981b 100644
> > --- a/fs/nfsd/nfsctl.c
> > +++ b/fs/nfsd/nfsctl.c
> > @@ -1653,6 +1653,110 @@ int nfsd_nl_rpc_status_get_done(struct netlink_callback *cb)
> >  	return 0;
> >  }
> >  
> > +/**
> > + * nfsd_nl_threads_set_doit - set the number of running threads
> > + * @skb: reply buffer
> > + * @info: netlink metadata and command arguments
> > + *
> > + * Return 0 on success or a negative errno.
> > + */
> > +int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
> > +{
> > +	struct net *net = genl_info_net(info);
> > +	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
> > +	int ret = -EBUSY;
> > +	u32 nthreads;
> > +
> > +	if (GENL_REQ_ATTR_CHECK(info, NFSD_A_SERVER_WORKER_THREADS))
> > +		return -EINVAL;
> > +
> > +	nthreads = nla_get_u32(info->attrs[NFSD_A_SERVER_WORKER_THREADS]);
> > +
> > +	mutex_lock(&nfsd_mutex);
> > +	if (info->attrs[NFSD_A_SERVER_WORKER_GRACETIME] ||
> > +	    info->attrs[NFSD_A_SERVER_WORKER_LEASETIME]) {
> > +		const struct nlattr *attr;
> > +
> > +		if (nn->nfsd_serv && nn->nfsd_serv->sv_nrthreads)
> > +			goto out_unlock;
> > +
> > +		ret = -EINVAL;
> > +		attr = info->attrs[NFSD_A_SERVER_WORKER_GRACETIME];
> > +		if (attr) {
> > +			u32 gracetime = nla_get_u32(attr);
> > +
> > +			if (gracetime < 10 || gracetime > 3600)
> > +				goto out_unlock;
> > +
> > +			nn->nfsd4_grace = gracetime;
> > +		}
> > +
> > +		attr = info->attrs[NFSD_A_SERVER_WORKER_LEASETIME];
> > +		if (attr) {
> > +			u32 leasetime = nla_get_u32(attr);
> > +
> > +			if (leasetime < 10 || leasetime > 3600)
> > +				goto out_unlock;
> > +
> > +			nn->nfsd4_lease = leasetime;
> > +		}
> > +	}
> > +
> > +	ret = nfsd_svc(nthreads, net, get_current_cred());
> > +out_unlock:
> > +	mutex_unlock(&nfsd_mutex);
> > +
> > +	return ret == nthreads ? 0 : ret;
> > +}
> > +
> > +/**
> > + * nfsd_nl_threads_get_doit - get the number of running threads
> > + * @skb: reply buffer
> > + * @info: netlink metadata and command arguments
> > + *
> > + * Return 0 on success or a negative errno.
> > + */
> > +int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info)
> > +{
> > +	struct net *net = genl_info_net(info);
> > +	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
> > +	void *hdr;
> > +	int err;
> > +
> > +	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);
> > +	err = nla_put_u32(skb, NFSD_A_SERVER_WORKER_GRACETIME,
> > +			  nn->nfsd4_grace) ||
> > +	      nla_put_u32(skb, NFSD_A_SERVER_WORKER_LEASETIME,
> > +			  nn->nfsd4_lease) ||
> > +	      nla_put_u32(skb, NFSD_A_SERVER_WORKER_THREADS,
> > +			  nn->nfsd_serv ? nn->nfsd_serv->sv_nrthreads : 0);
> > +	mutex_unlock(&nfsd_mutex);
> > +
> > +	if (err) {
> > +		err = -EINVAL;
> > +		goto err_free_msg;
> > +	}
> > +
> > +	genlmsg_end(skb, hdr);
> > +
> > +	return genlmsg_reply(skb, info);
> > +
> > +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/include/uapi/linux/nfsd_netlink.h b/include/uapi/linux/nfsd_netlink.h
> > index 3cd044edee5d..ccc78a5ee650 100644
> > --- a/include/uapi/linux/nfsd_netlink.h
> > +++ b/include/uapi/linux/nfsd_netlink.h
> > @@ -29,8 +29,19 @@ enum {
> >  	NFSD_A_RPC_STATUS_MAX = (__NFSD_A_RPC_STATUS_MAX - 1)
> >  };
> >  
> > +enum {
> > +	NFSD_A_SERVER_WORKER_THREADS = 1,
> > +	NFSD_A_SERVER_WORKER_GRACETIME,
> > +	NFSD_A_SERVER_WORKER_LEASETIME,
> > +
> > +	__NFSD_A_SERVER_WORKER_MAX,
> > +	NFSD_A_SERVER_WORKER_MAX = (__NFSD_A_SERVER_WORKER_MAX - 1)
> > +};
> > +
> >  enum {
> >  	NFSD_CMD_RPC_STATUS_GET = 1,
> > +	NFSD_CMD_THREADS_SET,
> > +	NFSD_CMD_THREADS_GET,
> >  
> >  	__NFSD_CMD_MAX,
> >  	NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)
> > -- 
> > 2.44.0
> > 
> > 
>
Jeffrey Layton April 16, 2024, 10:47 p.m. UTC | #4
On Wed, 2024-04-17 at 08:28 +1000, NeilBrown wrote:
> On Tue, 16 Apr 2024, Lorenzo Bianconi wrote:
> > Introduce write_threads netlink command similar to the one available
> > through the procfs.
> > 
> > Tested-by: Jeff Layton <jlayton@kernel.org>
> > Reviewed-by: Jeff Layton <jlayton@kernel.org>
> > Co-developed-by: Jeff Layton <jlayton@kernel.org>
> > Signed-off-by: Jeff Layton <jlayton@kernel.org>
> > Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> > ---
> >  Documentation/netlink/specs/nfsd.yaml |  33 ++++++++
> >  fs/nfsd/netlink.c                     |  19 +++++
> >  fs/nfsd/netlink.h                     |   2 +
> >  fs/nfsd/nfsctl.c                      | 104 ++++++++++++++++++++++++++
> >  include/uapi/linux/nfsd_netlink.h     |  11 +++
> >  5 files changed, 169 insertions(+)
> > 
> > diff --git a/Documentation/netlink/specs/nfsd.yaml b/Documentation/netlink/specs/nfsd.yaml
> > index 05acc73e2e33..cbe6c5fd6c4d 100644
> > --- a/Documentation/netlink/specs/nfsd.yaml
> > +++ b/Documentation/netlink/specs/nfsd.yaml
> > @@ -62,6 +62,18 @@ attribute-sets:
> >          name: compound-ops
> >          type: u32
> >          multi-attr: true
> > +  -
> > +    name: server-worker
> > +    attributes:
> > +      -
> > +        name: threads
> > +        type: u32
> > +      -
> > +        name: gracetime
> > +        type: u32
> > +      -
> > +        name: leasetime
> > +        type: u32
> 
> Another thought: I would be really happy if the "scope" were another
> optional argument here.  The mechanism of setting the scope by user the
> hostname works but is ugly.  I'm inclined to ignore the hostname
> completely when netlink is used, but I'm not completely sure about that.
> (aside - I think using the hostname for the default scope was a really
> bad idea.  It should have been a fixed string like "Linux").
> 

I'd be ok with that.

I replicated how rpc.nfsd does this in the userland tool, but I also
thought it was pretty ugly. I think all that it does currently is make
this work in nfsd_svc() since it overrides the nodename in the new
namespace:

        strscpy(nn->nfsd_name, utsname()->nodename,
                sizeof(nn->nfsd_name));

If we make that a new optional parameter, we could pass the scope in as
a new const char * argument to nfsd_svc. Then we'd just copy that into
nfsd_name instead of the nodename when it's provided.

I wonder too if we ought to rename this particular operations too. It's
becoming less of a "set threads" interface and more of a generic server
setup.

> 
> 
> >  
> >  operations:
> >    list:
> > @@ -87,3 +99,24 @@ operations:
> >              - sport
> >              - dport
> >              - compound-ops
> > +    -
> > +      name: threads-set
> > +      doc: set the number of running threads
> > +      attribute-set: server-worker
> > +      flags: [ admin-perm ]
> > +      do:
> > +        request:
> > +          attributes:
> > +            - threads
> > +            - gracetime
> > +            - leasetime
> > +    -
> > +      name: threads-get
> > +      doc: get the number of running threads
> > +      attribute-set: server-worker
> > +      do:
> > +        reply:
> > +          attributes:
> > +            - threads
> > +            - gracetime
> > +            - leasetime
> > diff --git a/fs/nfsd/netlink.c b/fs/nfsd/netlink.c
> > index 0e1d635ec5f9..20a646af0324 100644
> > --- a/fs/nfsd/netlink.c
> > +++ b/fs/nfsd/netlink.c
> > @@ -10,6 +10,13 @@
> >  
> >  #include <uapi/linux/nfsd_netlink.h>
> >  
> > +/* NFSD_CMD_THREADS_SET - do */
> > +static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_WORKER_LEASETIME + 1] = {
> > +	[NFSD_A_SERVER_WORKER_THREADS] = { .type = NLA_U32, },
> > +	[NFSD_A_SERVER_WORKER_GRACETIME] = { .type = NLA_U32, },
> > +	[NFSD_A_SERVER_WORKER_LEASETIME] = { .type = NLA_U32, },
> > +};
> > +
> >  /* Ops table for nfsd */
> >  static const struct genl_split_ops nfsd_nl_ops[] = {
> >  	{
> > @@ -19,6 +26,18 @@ static const struct genl_split_ops nfsd_nl_ops[] = {
> >  		.done	= nfsd_nl_rpc_status_get_done,
> >  		.flags	= GENL_CMD_CAP_DUMP,
> >  	},
> > +	{
> > +		.cmd		= NFSD_CMD_THREADS_SET,
> > +		.doit		= nfsd_nl_threads_set_doit,
> > +		.policy		= nfsd_threads_set_nl_policy,
> > +		.maxattr	= NFSD_A_SERVER_WORKER_LEASETIME,
> > +		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
> > +	},
> > +	{
> > +		.cmd	= NFSD_CMD_THREADS_GET,
> > +		.doit	= nfsd_nl_threads_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 d83dd6bdee92..4137fac477e4 100644
> > --- a/fs/nfsd/netlink.h
> > +++ b/fs/nfsd/netlink.h
> > @@ -16,6 +16,8 @@ int nfsd_nl_rpc_status_get_done(struct netlink_callback *cb);
> >  
> >  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);
> >  
> >  extern struct genl_family nfsd_nl_family;
> >  
> > diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
> > index f2e442d7fe16..38a5df03981b 100644
> > --- a/fs/nfsd/nfsctl.c
> > +++ b/fs/nfsd/nfsctl.c
> > @@ -1653,6 +1653,110 @@ int nfsd_nl_rpc_status_get_done(struct netlink_callback *cb)
> >  	return 0;
> >  }
> >  
> > +/**
> > + * nfsd_nl_threads_set_doit - set the number of running threads
> > + * @skb: reply buffer
> > + * @info: netlink metadata and command arguments
> > + *
> > + * Return 0 on success or a negative errno.
> > + */
> > +int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
> > +{
> > +	struct net *net = genl_info_net(info);
> > +	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
> > +	int ret = -EBUSY;
> > +	u32 nthreads;
> > +
> > +	if (GENL_REQ_ATTR_CHECK(info, NFSD_A_SERVER_WORKER_THREADS))
> > +		return -EINVAL;
> > +
> > +	nthreads = nla_get_u32(info->attrs[NFSD_A_SERVER_WORKER_THREADS]);
> > +
> > +	mutex_lock(&nfsd_mutex);
> > +	if (info->attrs[NFSD_A_SERVER_WORKER_GRACETIME] ||
> > +	    info->attrs[NFSD_A_SERVER_WORKER_LEASETIME]) {
> > +		const struct nlattr *attr;
> > +
> > +		if (nn->nfsd_serv && nn->nfsd_serv->sv_nrthreads)
> > +			goto out_unlock;
> > +
> > +		ret = -EINVAL;
> > +		attr = info->attrs[NFSD_A_SERVER_WORKER_GRACETIME];
> > +		if (attr) {
> > +			u32 gracetime = nla_get_u32(attr);
> > +
> > +			if (gracetime < 10 || gracetime > 3600)
> > +				goto out_unlock;
> > +
> > +			nn->nfsd4_grace = gracetime;
> > +		}
> > +
> > +		attr = info->attrs[NFSD_A_SERVER_WORKER_LEASETIME];
> > +		if (attr) {
> > +			u32 leasetime = nla_get_u32(attr);
> > +
> > +			if (leasetime < 10 || leasetime > 3600)
> > +				goto out_unlock;
> > +
> > +			nn->nfsd4_lease = leasetime;
> > +		}
> > +	}
> > +
> > +	ret = nfsd_svc(nthreads, net, get_current_cred());
> > +out_unlock:
> > +	mutex_unlock(&nfsd_mutex);
> > +
> > +	return ret == nthreads ? 0 : ret;
> > +}
> > +
> > +/**
> > + * nfsd_nl_threads_get_doit - get the number of running threads
> > + * @skb: reply buffer
> > + * @info: netlink metadata and command arguments
> > + *
> > + * Return 0 on success or a negative errno.
> > + */
> > +int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info)
> > +{
> > +	struct net *net = genl_info_net(info);
> > +	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
> > +	void *hdr;
> > +	int err;
> > +
> > +	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);
> > +	err = nla_put_u32(skb, NFSD_A_SERVER_WORKER_GRACETIME,
> > +			  nn->nfsd4_grace) ||
> > +	      nla_put_u32(skb, NFSD_A_SERVER_WORKER_LEASETIME,
> > +			  nn->nfsd4_lease) ||
> > +	      nla_put_u32(skb, NFSD_A_SERVER_WORKER_THREADS,
> > +			  nn->nfsd_serv ? nn->nfsd_serv->sv_nrthreads : 0);
> > +	mutex_unlock(&nfsd_mutex);
> > +
> > +	if (err) {
> > +		err = -EINVAL;
> > +		goto err_free_msg;
> > +	}
> > +
> > +	genlmsg_end(skb, hdr);
> > +
> > +	return genlmsg_reply(skb, info);
> > +
> > +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/include/uapi/linux/nfsd_netlink.h b/include/uapi/linux/nfsd_netlink.h
> > index 3cd044edee5d..ccc78a5ee650 100644
> > --- a/include/uapi/linux/nfsd_netlink.h
> > +++ b/include/uapi/linux/nfsd_netlink.h
> > @@ -29,8 +29,19 @@ enum {
> >  	NFSD_A_RPC_STATUS_MAX = (__NFSD_A_RPC_STATUS_MAX - 1)
> >  };
> >  
> > +enum {
> > +	NFSD_A_SERVER_WORKER_THREADS = 1,
> > +	NFSD_A_SERVER_WORKER_GRACETIME,
> > +	NFSD_A_SERVER_WORKER_LEASETIME,
> > +
> > +	__NFSD_A_SERVER_WORKER_MAX,
> > +	NFSD_A_SERVER_WORKER_MAX = (__NFSD_A_SERVER_WORKER_MAX - 1)
> > +};
> > +
> >  enum {
> >  	NFSD_CMD_RPC_STATUS_GET = 1,
> > +	NFSD_CMD_THREADS_SET,
> > +	NFSD_CMD_THREADS_GET,
> >  
> >  	__NFSD_CMD_MAX,
> >  	NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)
> > -- 
> > 2.44.0
> > 
> > 
>
Jeffrey Layton April 17, 2024, 1:04 p.m. UTC | #5
On Tue, 2024-04-16 at 18:39 -0400, Jeff Layton wrote:
> On Wed, 2024-04-17 at 07:55 +1000, NeilBrown wrote:
> > On Tue, 16 Apr 2024, Lorenzo Bianconi wrote:
> > > Introduce write_threads netlink command similar to the one available
> > > through the procfs.
> > 
> > I think this should support write_pool_threads too.
> > i.e.  the number of threads should be an array.  If it is a singleton,
> > the it does write_threads.   If larger it does write_pool_threads.
> > I don't think we want to add a separate command later for pool_threads.
> > 
> > NeilBrown
> > 
> 
> That is a very good point. We'll take a closer look at the current
> pool_threads interface and see how we can extend this one to cover that
> use case. Making it an array sounds reasonable at first blush.
> 

Lorenzo and I had a look this morning:

Doing this properly is going to take some refactoring, I think. The
existing pool code has a weird restriction where you can't bring the
server up and down exclusively using the pool_threads interface. There
are no userland tools for it either, so it's out of scope since it's not
really part of the how rpc.nfsd works today.

I think the way forward here is to go ahead and make this interface use
an array of integers (like Neil suggests), but for now if someone tries
to send down a THREAD_SET operation and the pool_mode isn't global,
we'll just return an error of some sort (EINVAL, EOPNOTSUPP, ?).

One the new interfaces are in I'll take a look at adding support for
managing other pool_modes properly.

Does that sound ok?


> > 
> > > 
> > > Tested-by: Jeff Layton <jlayton@kernel.org>
> > > Reviewed-by: Jeff Layton <jlayton@kernel.org>
> > > Co-developed-by: Jeff Layton <jlayton@kernel.org>
> > > Signed-off-by: Jeff Layton <jlayton@kernel.org>
> > > Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> > > ---
> > >  Documentation/netlink/specs/nfsd.yaml |  33 ++++++++
> > >  fs/nfsd/netlink.c                     |  19 +++++
> > >  fs/nfsd/netlink.h                     |   2 +
> > >  fs/nfsd/nfsctl.c                      | 104 ++++++++++++++++++++++++++
> > >  include/uapi/linux/nfsd_netlink.h     |  11 +++
> > >  5 files changed, 169 insertions(+)
> > > 
> > > diff --git a/Documentation/netlink/specs/nfsd.yaml b/Documentation/netlink/specs/nfsd.yaml
> > > index 05acc73e2e33..cbe6c5fd6c4d 100644
> > > --- a/Documentation/netlink/specs/nfsd.yaml
> > > +++ b/Documentation/netlink/specs/nfsd.yaml
> > > @@ -62,6 +62,18 @@ attribute-sets:
> > >          name: compound-ops
> > >          type: u32
> > >          multi-attr: true
> > > +  -
> > > +    name: server-worker
> > > +    attributes:
> > > +      -
> > > +        name: threads
> > > +        type: u32
> > > +      -
> > > +        name: gracetime
> > > +        type: u32
> > > +      -
> > > +        name: leasetime
> > > +        type: u32
> > >  
> > >  operations:
> > >    list:
> > > @@ -87,3 +99,24 @@ operations:
> > >              - sport
> > >              - dport
> > >              - compound-ops
> > > +    -
> > > +      name: threads-set
> > > +      doc: set the number of running threads
> > > +      attribute-set: server-worker
> > > +      flags: [ admin-perm ]
> > > +      do:
> > > +        request:
> > > +          attributes:
> > > +            - threads
> > > +            - gracetime
> > > +            - leasetime
> > > +    -
> > > +      name: threads-get
> > > +      doc: get the number of running threads
> > > +      attribute-set: server-worker
> > > +      do:
> > > +        reply:
> > > +          attributes:
> > > +            - threads
> > > +            - gracetime
> > > +            - leasetime
> > > diff --git a/fs/nfsd/netlink.c b/fs/nfsd/netlink.c
> > > index 0e1d635ec5f9..20a646af0324 100644
> > > --- a/fs/nfsd/netlink.c
> > > +++ b/fs/nfsd/netlink.c
> > > @@ -10,6 +10,13 @@
> > >  
> > >  #include <uapi/linux/nfsd_netlink.h>
> > >  
> > > +/* NFSD_CMD_THREADS_SET - do */
> > > +static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_WORKER_LEASETIME + 1] = {
> > > +	[NFSD_A_SERVER_WORKER_THREADS] = { .type = NLA_U32, },
> > > +	[NFSD_A_SERVER_WORKER_GRACETIME] = { .type = NLA_U32, },
> > > +	[NFSD_A_SERVER_WORKER_LEASETIME] = { .type = NLA_U32, },
> > > +};
> > > +
> > >  /* Ops table for nfsd */
> > >  static const struct genl_split_ops nfsd_nl_ops[] = {
> > >  	{
> > > @@ -19,6 +26,18 @@ static const struct genl_split_ops nfsd_nl_ops[] = {
> > >  		.done	= nfsd_nl_rpc_status_get_done,
> > >  		.flags	= GENL_CMD_CAP_DUMP,
> > >  	},
> > > +	{
> > > +		.cmd		= NFSD_CMD_THREADS_SET,
> > > +		.doit		= nfsd_nl_threads_set_doit,
> > > +		.policy		= nfsd_threads_set_nl_policy,
> > > +		.maxattr	= NFSD_A_SERVER_WORKER_LEASETIME,
> > > +		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
> > > +	},
> > > +	{
> > > +		.cmd	= NFSD_CMD_THREADS_GET,
> > > +		.doit	= nfsd_nl_threads_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 d83dd6bdee92..4137fac477e4 100644
> > > --- a/fs/nfsd/netlink.h
> > > +++ b/fs/nfsd/netlink.h
> > > @@ -16,6 +16,8 @@ int nfsd_nl_rpc_status_get_done(struct netlink_callback *cb);
> > >  
> > >  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);
> > >  
> > >  extern struct genl_family nfsd_nl_family;
> > >  
> > > diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
> > > index f2e442d7fe16..38a5df03981b 100644
> > > --- a/fs/nfsd/nfsctl.c
> > > +++ b/fs/nfsd/nfsctl.c
> > > @@ -1653,6 +1653,110 @@ int nfsd_nl_rpc_status_get_done(struct netlink_callback *cb)
> > >  	return 0;
> > >  }
> > >  
> > > +/**
> > > + * nfsd_nl_threads_set_doit - set the number of running threads
> > > + * @skb: reply buffer
> > > + * @info: netlink metadata and command arguments
> > > + *
> > > + * Return 0 on success or a negative errno.
> > > + */
> > > +int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
> > > +{
> > > +	struct net *net = genl_info_net(info);
> > > +	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
> > > +	int ret = -EBUSY;
> > > +	u32 nthreads;
> > > +
> > > +	if (GENL_REQ_ATTR_CHECK(info, NFSD_A_SERVER_WORKER_THREADS))
> > > +		return -EINVAL;
> > > +
> > > +	nthreads = nla_get_u32(info->attrs[NFSD_A_SERVER_WORKER_THREADS]);
> > > +
> > > +	mutex_lock(&nfsd_mutex);
> > > +	if (info->attrs[NFSD_A_SERVER_WORKER_GRACETIME] ||
> > > +	    info->attrs[NFSD_A_SERVER_WORKER_LEASETIME]) {
> > > +		const struct nlattr *attr;
> > > +
> > > +		if (nn->nfsd_serv && nn->nfsd_serv->sv_nrthreads)
> > > +			goto out_unlock;
> > > +
> > > +		ret = -EINVAL;
> > > +		attr = info->attrs[NFSD_A_SERVER_WORKER_GRACETIME];
> > > +		if (attr) {
> > > +			u32 gracetime = nla_get_u32(attr);
> > > +
> > > +			if (gracetime < 10 || gracetime > 3600)
> > > +				goto out_unlock;
> > > +
> > > +			nn->nfsd4_grace = gracetime;
> > > +		}
> > > +
> > > +		attr = info->attrs[NFSD_A_SERVER_WORKER_LEASETIME];
> > > +		if (attr) {
> > > +			u32 leasetime = nla_get_u32(attr);
> > > +
> > > +			if (leasetime < 10 || leasetime > 3600)
> > > +				goto out_unlock;
> > > +
> > > +			nn->nfsd4_lease = leasetime;
> > > +		}
> > > +	}
> > > +
> > > +	ret = nfsd_svc(nthreads, net, get_current_cred());
> > > +out_unlock:
> > > +	mutex_unlock(&nfsd_mutex);
> > > +
> > > +	return ret == nthreads ? 0 : ret;
> > > +}
> > > +
> > > +/**
> > > + * nfsd_nl_threads_get_doit - get the number of running threads
> > > + * @skb: reply buffer
> > > + * @info: netlink metadata and command arguments
> > > + *
> > > + * Return 0 on success or a negative errno.
> > > + */
> > > +int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info)
> > > +{
> > > +	struct net *net = genl_info_net(info);
> > > +	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
> > > +	void *hdr;
> > > +	int err;
> > > +
> > > +	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);
> > > +	err = nla_put_u32(skb, NFSD_A_SERVER_WORKER_GRACETIME,
> > > +			  nn->nfsd4_grace) ||
> > > +	      nla_put_u32(skb, NFSD_A_SERVER_WORKER_LEASETIME,
> > > +			  nn->nfsd4_lease) ||
> > > +	      nla_put_u32(skb, NFSD_A_SERVER_WORKER_THREADS,
> > > +			  nn->nfsd_serv ? nn->nfsd_serv->sv_nrthreads : 0);
> > > +	mutex_unlock(&nfsd_mutex);
> > > +
> > > +	if (err) {
> > > +		err = -EINVAL;
> > > +		goto err_free_msg;
> > > +	}
> > > +
> > > +	genlmsg_end(skb, hdr);
> > > +
> > > +	return genlmsg_reply(skb, info);
> > > +
> > > +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/include/uapi/linux/nfsd_netlink.h b/include/uapi/linux/nfsd_netlink.h
> > > index 3cd044edee5d..ccc78a5ee650 100644
> > > --- a/include/uapi/linux/nfsd_netlink.h
> > > +++ b/include/uapi/linux/nfsd_netlink.h
> > > @@ -29,8 +29,19 @@ enum {
> > >  	NFSD_A_RPC_STATUS_MAX = (__NFSD_A_RPC_STATUS_MAX - 1)
> > >  };
> > >  
> > > +enum {
> > > +	NFSD_A_SERVER_WORKER_THREADS = 1,
> > > +	NFSD_A_SERVER_WORKER_GRACETIME,
> > > +	NFSD_A_SERVER_WORKER_LEASETIME,
> > > +
> > > +	__NFSD_A_SERVER_WORKER_MAX,
> > > +	NFSD_A_SERVER_WORKER_MAX = (__NFSD_A_SERVER_WORKER_MAX - 1)
> > > +};
> > > +
> > >  enum {
> > >  	NFSD_CMD_RPC_STATUS_GET = 1,
> > > +	NFSD_CMD_THREADS_SET,
> > > +	NFSD_CMD_THREADS_GET,
> > >  
> > >  	__NFSD_CMD_MAX,
> > >  	NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)
> > > -- 
> > > 2.44.0
> > > 
> > > 
> > 
>
diff mbox series

Patch

diff --git a/Documentation/netlink/specs/nfsd.yaml b/Documentation/netlink/specs/nfsd.yaml
index 05acc73e2e33..cbe6c5fd6c4d 100644
--- a/Documentation/netlink/specs/nfsd.yaml
+++ b/Documentation/netlink/specs/nfsd.yaml
@@ -62,6 +62,18 @@  attribute-sets:
         name: compound-ops
         type: u32
         multi-attr: true
+  -
+    name: server-worker
+    attributes:
+      -
+        name: threads
+        type: u32
+      -
+        name: gracetime
+        type: u32
+      -
+        name: leasetime
+        type: u32
 
 operations:
   list:
@@ -87,3 +99,24 @@  operations:
             - sport
             - dport
             - compound-ops
+    -
+      name: threads-set
+      doc: set the number of running threads
+      attribute-set: server-worker
+      flags: [ admin-perm ]
+      do:
+        request:
+          attributes:
+            - threads
+            - gracetime
+            - leasetime
+    -
+      name: threads-get
+      doc: get the number of running threads
+      attribute-set: server-worker
+      do:
+        reply:
+          attributes:
+            - threads
+            - gracetime
+            - leasetime
diff --git a/fs/nfsd/netlink.c b/fs/nfsd/netlink.c
index 0e1d635ec5f9..20a646af0324 100644
--- a/fs/nfsd/netlink.c
+++ b/fs/nfsd/netlink.c
@@ -10,6 +10,13 @@ 
 
 #include <uapi/linux/nfsd_netlink.h>
 
+/* NFSD_CMD_THREADS_SET - do */
+static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_WORKER_LEASETIME + 1] = {
+	[NFSD_A_SERVER_WORKER_THREADS] = { .type = NLA_U32, },
+	[NFSD_A_SERVER_WORKER_GRACETIME] = { .type = NLA_U32, },
+	[NFSD_A_SERVER_WORKER_LEASETIME] = { .type = NLA_U32, },
+};
+
 /* Ops table for nfsd */
 static const struct genl_split_ops nfsd_nl_ops[] = {
 	{
@@ -19,6 +26,18 @@  static const struct genl_split_ops nfsd_nl_ops[] = {
 		.done	= nfsd_nl_rpc_status_get_done,
 		.flags	= GENL_CMD_CAP_DUMP,
 	},
+	{
+		.cmd		= NFSD_CMD_THREADS_SET,
+		.doit		= nfsd_nl_threads_set_doit,
+		.policy		= nfsd_threads_set_nl_policy,
+		.maxattr	= NFSD_A_SERVER_WORKER_LEASETIME,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd	= NFSD_CMD_THREADS_GET,
+		.doit	= nfsd_nl_threads_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 d83dd6bdee92..4137fac477e4 100644
--- a/fs/nfsd/netlink.h
+++ b/fs/nfsd/netlink.h
@@ -16,6 +16,8 @@  int nfsd_nl_rpc_status_get_done(struct netlink_callback *cb);
 
 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);
 
 extern struct genl_family nfsd_nl_family;
 
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index f2e442d7fe16..38a5df03981b 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -1653,6 +1653,110 @@  int nfsd_nl_rpc_status_get_done(struct netlink_callback *cb)
 	return 0;
 }
 
+/**
+ * nfsd_nl_threads_set_doit - set the number of running threads
+ * @skb: reply buffer
+ * @info: netlink metadata and command arguments
+ *
+ * Return 0 on success or a negative errno.
+ */
+int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	struct net *net = genl_info_net(info);
+	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+	int ret = -EBUSY;
+	u32 nthreads;
+
+	if (GENL_REQ_ATTR_CHECK(info, NFSD_A_SERVER_WORKER_THREADS))
+		return -EINVAL;
+
+	nthreads = nla_get_u32(info->attrs[NFSD_A_SERVER_WORKER_THREADS]);
+
+	mutex_lock(&nfsd_mutex);
+	if (info->attrs[NFSD_A_SERVER_WORKER_GRACETIME] ||
+	    info->attrs[NFSD_A_SERVER_WORKER_LEASETIME]) {
+		const struct nlattr *attr;
+
+		if (nn->nfsd_serv && nn->nfsd_serv->sv_nrthreads)
+			goto out_unlock;
+
+		ret = -EINVAL;
+		attr = info->attrs[NFSD_A_SERVER_WORKER_GRACETIME];
+		if (attr) {
+			u32 gracetime = nla_get_u32(attr);
+
+			if (gracetime < 10 || gracetime > 3600)
+				goto out_unlock;
+
+			nn->nfsd4_grace = gracetime;
+		}
+
+		attr = info->attrs[NFSD_A_SERVER_WORKER_LEASETIME];
+		if (attr) {
+			u32 leasetime = nla_get_u32(attr);
+
+			if (leasetime < 10 || leasetime > 3600)
+				goto out_unlock;
+
+			nn->nfsd4_lease = leasetime;
+		}
+	}
+
+	ret = nfsd_svc(nthreads, net, get_current_cred());
+out_unlock:
+	mutex_unlock(&nfsd_mutex);
+
+	return ret == nthreads ? 0 : ret;
+}
+
+/**
+ * nfsd_nl_threads_get_doit - get the number of running threads
+ * @skb: reply buffer
+ * @info: netlink metadata and command arguments
+ *
+ * Return 0 on success or a negative errno.
+ */
+int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	struct net *net = genl_info_net(info);
+	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+	void *hdr;
+	int err;
+
+	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);
+	err = nla_put_u32(skb, NFSD_A_SERVER_WORKER_GRACETIME,
+			  nn->nfsd4_grace) ||
+	      nla_put_u32(skb, NFSD_A_SERVER_WORKER_LEASETIME,
+			  nn->nfsd4_lease) ||
+	      nla_put_u32(skb, NFSD_A_SERVER_WORKER_THREADS,
+			  nn->nfsd_serv ? nn->nfsd_serv->sv_nrthreads : 0);
+	mutex_unlock(&nfsd_mutex);
+
+	if (err) {
+		err = -EINVAL;
+		goto err_free_msg;
+	}
+
+	genlmsg_end(skb, hdr);
+
+	return genlmsg_reply(skb, info);
+
+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/include/uapi/linux/nfsd_netlink.h b/include/uapi/linux/nfsd_netlink.h
index 3cd044edee5d..ccc78a5ee650 100644
--- a/include/uapi/linux/nfsd_netlink.h
+++ b/include/uapi/linux/nfsd_netlink.h
@@ -29,8 +29,19 @@  enum {
 	NFSD_A_RPC_STATUS_MAX = (__NFSD_A_RPC_STATUS_MAX - 1)
 };
 
+enum {
+	NFSD_A_SERVER_WORKER_THREADS = 1,
+	NFSD_A_SERVER_WORKER_GRACETIME,
+	NFSD_A_SERVER_WORKER_LEASETIME,
+
+	__NFSD_A_SERVER_WORKER_MAX,
+	NFSD_A_SERVER_WORKER_MAX = (__NFSD_A_SERVER_WORKER_MAX - 1)
+};
+
 enum {
 	NFSD_CMD_RPC_STATUS_GET = 1,
+	NFSD_CMD_THREADS_SET,
+	NFSD_CMD_THREADS_GET,
 
 	__NFSD_CMD_MAX,
 	NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)