[RFC PATCH v1 20/30] sysctl: net: use sysctl_field in SMC sysctls

From: Alexey Gladkov

Date: Wed Aug 26 2026 - 15:49:38 EST


SMC sysctls are per-net, but non-init network namespaces currently get
a private ctl_table copy only so each data pointer can be adjusted from
init_net to the target net namespace. The copied table is then kept
around solely for unregister time.

Use sysctl_field so the data pointers are derived from the registration
context instead. This keeps the SMC sysctl description static and const
while preserving the existing per-net behaviour and the optional
handshake-control inheritance.

While converting the table, handle limit_smc_hs through a bool-aware
custom handler. The old ctl_table entry described the bool storage as
an int and relied on the untyped sysctl interface to hide that mismatch.

Signed-off-by: Alexey Gladkov <legion@xxxxxxxxxx>
---
net/smc/smc_sysctl.c | 208 ++++++++++++++++++-------------------------
1 file changed, 89 insertions(+), 119 deletions(-)

diff --git a/net/smc/smc_sysctl.c b/net/smc/smc_sysctl.c
index b1efed546243..801bbd8773d3 100644
--- a/net/smc/smc_sysctl.c
+++ b/net/smc/smc_sysctl.c
@@ -97,112 +97,98 @@ static int proc_smc_hs_ctrl(const struct ctl_table *ctl, int write,
}
#endif /* CONFIG_SMC_HS_CTRL_BPF */

-static struct ctl_table smc_table[] = {
- {
- .procname = "autocorking_size",
- .data = &init_net.smc.sysctl_autocorking_size,
- .maxlen = sizeof(unsigned int),
- .mode = 0644,
- .proc_handler = proc_douintvec,
- },
- {
- .procname = "smcr_buf_type",
- .data = &init_net.smc.sysctl_smcr_buf_type,
- .maxlen = sizeof(unsigned int),
- .mode = 0644,
- .proc_handler = proc_douintvec_minmax,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_TWO,
- },
- {
- .procname = "smcr_testlink_time",
- .data = &init_net.smc.sysctl_smcr_testlink_time,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_jiffies,
- },
- {
- .procname = "wmem",
- .data = &init_net.smc.sysctl_wmem,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = &min_sndbuf,
- .extra2 = &max_sndbuf,
- },
- {
- .procname = "rmem",
- .data = &init_net.smc.sysctl_rmem,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = &min_rcvbuf,
- .extra2 = &max_rcvbuf,
- },
- {
- .procname = "smcr_max_links_per_lgr",
- .data = &init_net.smc.sysctl_max_links_per_lgr,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = &links_per_lgr_min,
- .extra2 = &links_per_lgr_max,
- },
- {
- .procname = "smcr_max_conns_per_lgr",
- .data = &init_net.smc.sysctl_max_conns_per_lgr,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = &conns_per_lgr_min,
- .extra2 = &conns_per_lgr_max,
- },
- {
- .procname = "limit_smc_hs",
- .data = &init_net.smc.limit_smc_hs,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE,
- },
- {
- .procname = "smcr_max_send_wr",
- .data = &init_net.smc.sysctl_smcr_max_send_wr,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = &smcr_max_wr_min,
- .extra2 = &smcr_max_wr_max,
- },
- {
- .procname = "smcr_max_recv_wr",
- .data = &init_net.smc.sysctl_smcr_max_recv_wr,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = &smcr_max_wr_min,
- .extra2 = &smcr_max_wr_max,
- },
+#define SMC_SYSCTL_DATA(type, name) \
+static type *smc_##name##_data(const struct sysctl_context *ctx) \
+{ \
+ return &ctx->ns.net_ns->smc.sysctl_##name; \
+}
+
+#define SMC_SYSCTL_CUSTOM_DATA(name) \
+static void *smc_##name##_data(const struct sysctl_context *ctx) \
+{ \
+ return &ctx->ns.net_ns->smc.sysctl_##name; \
+}
+
+SMC_SYSCTL_DATA(unsigned int, autocorking_size)
+SMC_SYSCTL_DATA(unsigned int, smcr_buf_type)
+SMC_SYSCTL_CUSTOM_DATA(smcr_testlink_time)
+SMC_SYSCTL_DATA(int, wmem)
+SMC_SYSCTL_DATA(int, rmem)
+SMC_SYSCTL_DATA(int, max_links_per_lgr)
+SMC_SYSCTL_DATA(int, max_conns_per_lgr)
+SMC_SYSCTL_DATA(unsigned int, smcr_max_send_wr)
+SMC_SYSCTL_DATA(unsigned int, smcr_max_recv_wr)
+
+static void *smc_limit_smc_hs_data(const struct sysctl_context *ctx)
+{
+ return &ctx->ns.net_ns->smc.limit_smc_hs;
+}
+
+static int proc_smc_limit_smc_hs(const struct ctl_table *ctl, int write,
+ void *buffer, size_t *lenp, loff_t *ppos)
+{
+ bool *limit_smc_hs = ctl->data;
+ struct ctl_table tmp = *ctl;
+ int val = READ_ONCE(*limit_smc_hs);
+ int ret;
+
+ tmp.data = &val;
+ tmp.maxlen = sizeof(val);
+ tmp.extra1 = SYSCTL_ZERO;
+ tmp.extra2 = SYSCTL_ONE;
+
+ ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
+ if (write && !ret)
+ WRITE_ONCE(*limit_smc_hs, val);
+
+ return ret;
+}
+
#if IS_ENABLED(CONFIG_SMC_HS_CTRL_BPF)
- {
- .procname = "hs_ctrl",
- .data = &init_net.smc.hs_ctrl,
- .mode = 0644,
- .maxlen = SMC_HS_CTRL_NAME_MAX,
- .proc_handler = proc_smc_hs_ctrl,
- },
-#endif /* CONFIG_SMC_HS_CTRL_BPF */
+static void *smc_hs_ctrl_data(const struct sysctl_context *ctx)
+{
+ return &ctx->ns.net_ns->smc.hs_ctrl;
+}
+#endif
+
+static const struct sysctl_field smc_table[] = {
+ SYSCTL_FIELD_UINT("autocorking_size", 0644, smc_autocorking_size_data),
+ SYSCTL_FIELD_STATIC_UINT_MINMAX("smcr_buf_type", 0644,
+ smc_smcr_buf_type_data,
+ SYSCTL_UINT_ZERO, SYSCTL_UINT_TWO),
+ SYSCTL_FIELD_CUSTOM("smcr_testlink_time", 0644, sizeof(int),
+ smc_smcr_testlink_time_data, proc_dointvec_jiffies),
+ SYSCTL_FIELD_STATIC_INT_MINMAX("wmem", 0644, smc_wmem_data,
+ &min_sndbuf, &max_sndbuf),
+ SYSCTL_FIELD_STATIC_INT_MINMAX("rmem", 0644, smc_rmem_data,
+ &min_rcvbuf, &max_rcvbuf),
+ SYSCTL_FIELD_STATIC_INT_MINMAX("smcr_max_links_per_lgr", 0644,
+ smc_max_links_per_lgr_data,
+ &links_per_lgr_min, &links_per_lgr_max),
+ SYSCTL_FIELD_STATIC_INT_MINMAX("smcr_max_conns_per_lgr", 0644,
+ smc_max_conns_per_lgr_data,
+ &conns_per_lgr_min, &conns_per_lgr_max),
+ SYSCTL_FIELD_CUSTOM("limit_smc_hs", 0644, sizeof(bool),
+ smc_limit_smc_hs_data, proc_smc_limit_smc_hs),
+ SYSCTL_FIELD_STATIC_UINT_MINMAX("smcr_max_send_wr", 0644,
+ smc_smcr_max_send_wr_data,
+ &smcr_max_wr_min, &smcr_max_wr_max),
+ SYSCTL_FIELD_STATIC_UINT_MINMAX("smcr_max_recv_wr", 0644,
+ smc_smcr_max_recv_wr_data,
+ &smcr_max_wr_min, &smcr_max_wr_max),
+#if IS_ENABLED(CONFIG_SMC_HS_CTRL_BPF)
+ SYSCTL_FIELD_CUSTOM("hs_ctrl", 0644, SMC_HS_CTRL_NAME_MAX,
+ smc_hs_ctrl_data, proc_smc_hs_ctrl),
+#endif
};

int __net_init smc_sysctl_net_init(struct net *net)
{
- size_t table_size = ARRAY_SIZE(smc_table);
- struct ctl_table *table;
+ struct sysctl_context ctx = {
+ .ns.net_ns = net,
+ };

- table = smc_table;
if (!net_eq(net, &init_net)) {
- int i;
#if IS_ENABLED(CONFIG_SMC_HS_CTRL_BPF)
struct smc_hs_ctrl *ctrl;

@@ -213,19 +199,12 @@ int __net_init smc_sysctl_net_init(struct net *net)
rcu_assign_pointer(net->smc.hs_ctrl, ctrl);
rcu_read_unlock();
#endif /* CONFIG_SMC_HS_CTRL_BPF */
-
- table = kmemdup(table, sizeof(smc_table), GFP_KERNEL);
- if (!table)
- goto err_alloc;
-
- for (i = 0; i < table_size; i++)
- table[i].data += (void *)net - (void *)&init_net;
}

- net->smc.smc_hdr = register_net_sysctl_sz(net, "net/smc", table,
- table_size);
+ net->smc.smc_hdr = register_sysctl_fields(&net->sysctls, "net/smc",
+ smc_table, &ctx);
if (!net->smc.smc_hdr)
- goto err_reg;
+ goto err_alloc;

net->smc.sysctl_autocorking_size = SMC_AUTOCORKING_DEFAULT_SIZE;
net->smc.sysctl_smcr_buf_type = SMCR_PHYS_CONT_BUFS;
@@ -237,13 +216,10 @@ int __net_init smc_sysctl_net_init(struct net *net)
net->smc.sysctl_smcr_max_send_wr = SMCR_MAX_SEND_WR_DEF;
net->smc.sysctl_smcr_max_recv_wr = SMCR_MAX_RECV_WR_DEF;
/* disable handshake limitation by default */
- net->smc.limit_smc_hs = 0;
+ net->smc.limit_smc_hs = false;

return 0;

-err_reg:
- if (!net_eq(net, &init_net))
- kfree(table);
err_alloc:
#if IS_ENABLED(CONFIG_SMC_HS_CTRL_BPF)
smc_net_replace_smc_hs_ctrl(net, NULL);
@@ -253,14 +229,8 @@ int __net_init smc_sysctl_net_init(struct net *net)

void __net_exit smc_sysctl_net_exit(struct net *net)
{
- const struct ctl_table *table;
-
- table = net->smc.smc_hdr->ctl_table_arg;
unregister_net_sysctl_table(net->smc.smc_hdr);
#if IS_ENABLED(CONFIG_SMC_HS_CTRL_BPF)
smc_net_replace_smc_hs_ctrl(net, NULL);
#endif /* CONFIG_SMC_HS_CTRL_BPF */
-
- if (!net_eq(net, &init_net))
- kfree(table);
}
--
2.55.0