NFSD: convert write_threads to netlink command
authorLorenzo Bianconi <lorenzo@kernel.org>
Tue, 23 Apr 2024 13:25:40 +0000 (15:25 +0200)
committerChuck Lever <chuck.lever@oracle.com>
Mon, 6 May 2024 13:07:21 +0000 (09:07 -0400)
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>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Documentation/netlink/specs/nfsd.yaml
fs/nfsd/netlink.c
fs/nfsd/netlink.h
fs/nfsd/nfsctl.c
include/uapi/linux/nfsd_netlink.h

index 05acc73..703c2d1 100644 (file)
@@ -62,6 +62,22 @@ attribute-sets:
         name: compound-ops
         type: u32
         multi-attr: true
+  -
+    name: server
+    attributes:
+      -
+        name: threads
+        type: u32
+        multi-attr: true
+      -
+        name: gracetime
+        type: u32
+      -
+        name: leasetime
+        type: u32
+      -
+        name: scope
+        type: string
 
 operations:
   list:
@@ -87,3 +103,26 @@ operations:
             - sport
             - dport
             - compound-ops
+    -
+      name: threads-set
+      doc: set the number of running threads
+      attribute-set: server
+      flags: [ admin-perm ]
+      do:
+        request:
+          attributes:
+            - threads
+            - gracetime
+            - leasetime
+            - scope
+    -
+      name: threads-get
+      doc: get the number of running threads
+      attribute-set: server
+      do:
+        reply:
+          attributes:
+            - threads
+            - gracetime
+            - leasetime
+            - scope
index 0e1d635..fe9eef5 100644 (file)
 
 #include <uapi/linux/nfsd_netlink.h>
 
+/* NFSD_CMD_THREADS_SET - do */
+static const struct nla_policy nfsd_threads_set_nl_policy[NFSD_A_SERVER_SCOPE + 1] = {
+       [NFSD_A_SERVER_THREADS] = { .type = NLA_U32, },
+       [NFSD_A_SERVER_GRACETIME] = { .type = NLA_U32, },
+       [NFSD_A_SERVER_LEASETIME] = { .type = NLA_U32, },
+       [NFSD_A_SERVER_SCOPE] = { .type = NLA_NUL_STRING, },
+};
+
 /* Ops table for nfsd */
 static const struct genl_split_ops nfsd_nl_ops[] = {
        {
@@ -19,6 +27,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_SCOPE,
+               .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 = {
index d83dd6b..4137fac 100644 (file)
@@ -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;
 
index 34148d7..5bfdebb 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/sunrpc/addr.h>
 #include <linux/sunrpc/gss_api.h>
 #include <linux/sunrpc/rpc_pipe_fs.h>
+#include <linux/sunrpc/svc.h>
 #include <linux/module.h>
 #include <linux/fsnotify.h>
 
@@ -1653,6 +1654,148 @@ 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)
+{
+       int nthreads = 0, count = 0, nrpools, ret = -EOPNOTSUPP, rem;
+       struct net *net = genl_info_net(info);
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+       const struct nlattr *attr;
+       const char *scope = NULL;
+
+       if (GENL_REQ_ATTR_CHECK(info, NFSD_A_SERVER_THREADS))
+               return -EINVAL;
+
+       /* count number of SERVER_THREADS values */
+       nlmsg_for_each_attr(attr, info->nlhdr, GENL_HDRLEN, rem) {
+               if (nla_type(attr) == NFSD_A_SERVER_THREADS)
+                       count++;
+       }
+
+       mutex_lock(&nfsd_mutex);
+
+       nrpools = nfsd_nrpools(net);
+       if (nrpools && count > nrpools)
+               count = nrpools;
+
+       /* XXX: make this handle non-global pool-modes */
+       if (count > 1)
+               goto out_unlock;
+
+       nthreads = nla_get_u32(info->attrs[NFSD_A_SERVER_THREADS]);
+       if (info->attrs[NFSD_A_SERVER_GRACETIME] ||
+           info->attrs[NFSD_A_SERVER_LEASETIME] ||
+           info->attrs[NFSD_A_SERVER_SCOPE]) {
+               ret = -EBUSY;
+               if (nn->nfsd_serv && nn->nfsd_serv->sv_nrthreads)
+                       goto out_unlock;
+
+               ret = -EINVAL;
+               attr = info->attrs[NFSD_A_SERVER_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_LEASETIME];
+               if (attr) {
+                       u32 leasetime = nla_get_u32(attr);
+
+                       if (leasetime < 10 || leasetime > 3600)
+                               goto out_unlock;
+
+                       nn->nfsd4_lease = leasetime;
+               }
+
+               attr = info->attrs[NFSD_A_SERVER_SCOPE];
+               if (attr)
+                       scope = nla_data(attr);
+       }
+
+       ret = nfsd_svc(nthreads, net, get_current_cred(), scope);
+
+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_GRACETIME,
+                         nn->nfsd4_grace) ||
+             nla_put_u32(skb, NFSD_A_SERVER_LEASETIME,
+                         nn->nfsd4_lease) ||
+             nla_put_string(skb, NFSD_A_SERVER_SCOPE,
+                         nn->nfsd_name);
+       if (err)
+               goto err_unlock;
+
+       if (nn->nfsd_serv) {
+               int i;
+
+               for (i = 0; i < nfsd_nrpools(net); ++i) {
+                       struct svc_pool *sp = &nn->nfsd_serv->sv_pools[i];
+
+                       err = nla_put_u32(skb, NFSD_A_SERVER_THREADS,
+                                         atomic_read(&sp->sp_nrthreads));
+                       if (err)
+                               goto err_unlock;
+               }
+       } else {
+               err = nla_put_u32(skb, NFSD_A_SERVER_THREADS, 0);
+               if (err)
+                       goto err_unlock;
+       }
+
+       mutex_unlock(&nfsd_mutex);
+
+       genlmsg_end(skb, hdr);
+
+       return genlmsg_reply(skb, info);
+
+err_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
index 3cd044e..4bbccd5 100644 (file)
@@ -29,8 +29,20 @@ enum {
        NFSD_A_RPC_STATUS_MAX = (__NFSD_A_RPC_STATUS_MAX - 1)
 };
 
+enum {
+       NFSD_A_SERVER_THREADS = 1,
+       NFSD_A_SERVER_GRACETIME,
+       NFSD_A_SERVER_LEASETIME,
+       NFSD_A_SERVER_SCOPE,
+
+       __NFSD_A_SERVER_MAX,
+       NFSD_A_SERVER_MAX = (__NFSD_A_SERVER_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)