]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
net: sysctl: allow dump_cpumask to handle higher numbers of CPUs
authorAntoine Tenart <atenart@kernel.org>
Thu, 17 Oct 2024 15:24:19 +0000 (17:24 +0200)
committerPaolo Abeni <pabeni@redhat.com>
Wed, 23 Oct 2024 08:28:26 +0000 (10:28 +0200)
This fixes the output of rps_default_mask and flow_limit_cpu_bitmap when
the CPU count is > 448, as it was truncated.

The underlying values are actually stored correctly when writing to
these sysctl but displaying them uses a fixed length temporary buffer in
dump_cpumask. This buffer can be too small if the CPU count is > 448.

Fix this by dynamically allocating the buffer in dump_cpumask, using a
guesstimate of what we need.

Signed-off-by: Antoine Tenart <atenart@kernel.org>
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
net/core/sysctl_net_core.c

index 8dc07f7b1772ec738aa3ff57d98a1973ca642f2e..cb8d32e5c14e67dd50321c43d4ff1fcc2694b31c 100644 (file)
@@ -51,22 +51,32 @@ int sysctl_devconf_inherit_init_net __read_mostly;
 EXPORT_SYMBOL(sysctl_devconf_inherit_init_net);
 
 #if IS_ENABLED(CONFIG_NET_FLOW_LIMIT) || IS_ENABLED(CONFIG_RPS)
-static void dump_cpumask(void *buffer, size_t *lenp, loff_t *ppos,
-                        struct cpumask *mask)
+static int dump_cpumask(void *buffer, size_t *lenp, loff_t *ppos,
+                       struct cpumask *mask)
 {
-       char kbuf[128];
+       char *kbuf;
        int len;
 
        if (*ppos || !*lenp) {
                *lenp = 0;
-               return;
+               return 0;
+       }
+
+       /* CPUs are displayed as a hex bitmap + a comma between each groups of 8
+        * nibbles (except the last one which has a newline instead).
+        * Guesstimate the buffer size at the group granularity level.
+        */
+       len = min(DIV_ROUND_UP(nr_cpumask_bits, 32) * (8 + 1), *lenp);
+       kbuf = kmalloc(len, GFP_KERNEL);
+       if (!kbuf) {
+               *lenp = 0;
+               return -ENOMEM;
        }
 
-       len = min(sizeof(kbuf), *lenp);
        len = scnprintf(kbuf, len, "%*pb", cpumask_pr_args(mask));
        if (!len) {
                *lenp = 0;
-               return;
+               goto free_buf;
        }
 
        /* scnprintf writes a trailing null char not counted in the returned
@@ -76,6 +86,10 @@ static void dump_cpumask(void *buffer, size_t *lenp, loff_t *ppos,
        memcpy(buffer, kbuf, len);
        *lenp = len;
        *ppos += len;
+
+free_buf:
+       kfree(kbuf);
+       return 0;
 }
 #endif
 
@@ -119,8 +133,8 @@ static int rps_default_mask_sysctl(const struct ctl_table *table, int write,
                if (err)
                        goto done;
        } else {
-               dump_cpumask(buffer, lenp, ppos,
-                            net->core.rps_default_mask ? : cpu_none_mask);
+               err = dump_cpumask(buffer, lenp, ppos,
+                                  net->core.rps_default_mask ? : cpu_none_mask);
        }
 
 done:
@@ -249,7 +263,7 @@ write_unlock:
                }
                rcu_read_unlock();
 
-               dump_cpumask(buffer, lenp, ppos, mask);
+               ret = dump_cpumask(buffer, lenp, ppos, mask);
        }
 
 done: