Skip to contentSkip to conversation

proc: sysctl: address table entries by index

RFC PATCH In review Alexey Gladkov 3 participants 39 messages Aug 26 – Sep 17 1 message not in the archive

Subsystems (19)
lore
Share & tools
lore

A message that is not in the archive (sent off-list or not yet received): [email protected]

#1 lore raw

RFC PATCH 1/30 proc: sysctl: address table entries by index

proc_sysctl keeps pointers to ctl_table entries in several lookup and inode paths. That makes it hard to support alternative descriptor formats because the tree logic assumes that every registered entry already is a ctl_table object.

Store the entry index in proc inodes and pass header/index pairs through the lookup, iteration and permission paths instead. The existing ctl_table entries are still used for all handler, permission and poll operations, but the core no longer needs to carry a direct ctl_table pointer through every internal path.

---
 fs/proc/inode.c       |   2 +-
 fs/proc/internal.h    |   2 +-
 fs/proc/proc_sysctl.c | 330 ++++++++++++++++++++++++------------------
 3 files changed, 194 insertions(+), 140 deletions(-)
fs/proc/inode.c +1 -1
index b7634f975d98..c735338ecc8d 100644
@@ -62,7 +62,7 @@ static struct inode *proc_alloc_inode(struct super_block *sb)
 	ei->op.proc_get_link = NULL;
 	ei->pde = NULL;
 	ei->sysctl = NULL;
-	ei->sysctl_entry = NULL;
+	ei->sysctl_entry_idx = 0;
 	INIT_HLIST_NODE(&ei->sibling_inodes);
 	ei->ns_ops = NULL;
 	return &ei->vfs_inode;
fs/proc/internal.h +1 -1
index 64dc44832808..dd3a102b144f 100644
@@ -120,7 +120,7 @@ struct proc_inode {
 	union proc_op op;
 	struct proc_dir_entry *pde;
 	struct ctl_table_header *sysctl;
-	const struct ctl_table *sysctl_entry;
+	size_t sysctl_entry_idx;
 	struct hlist_node sibling_inodes;
 	const struct proc_ns_operations *ns_ops;
 	struct inode vfs_inode;
fs/proc/proc_sysctl.c +192 -138

763 lines not on this page · raw message

Signature · 1 line
2.55.0
#2 lore raw

RFC PATCH 2/30 sysctl: add unsigned int limit constants

In reply to a message not in the archive

Some sysctl handlers use unsigned int storage for their limit arguments. In particular, proc_dou8vec_minmax() expects extra1 and extra2 to point to unsigned int values even though the controlled data is an u8.

Provide shared unsigned int constants so typed sysctl descriptors can pass correctly typed min and max pointers without casting the existing int constants.

---
 include/linux/sysctl.h | 7 +++++++
 kernel/sysctl.c        | 3 +++
 2 files changed, 10 insertions(+)
include/linux/sysctl.h +7 -0
index 2886fbceb5d6..8d993ba1488b 100644
@@ -59,6 +59,12 @@ extern const int sysctl_vals[];
 #define SYSCTL_LONG_ONE		((void *)&sysctl_long_vals[1])
 #define SYSCTL_LONG_MAX		((void *)&sysctl_long_vals[2])
 
+#define SYSCTL_UINT_ZERO	((unsigned int *)&sysctl_uint_vals[0])
+#define SYSCTL_UINT_ONE		((unsigned int *)&sysctl_uint_vals[1])
+#define SYSCTL_UINT_TWO		((unsigned int *)&sysctl_uint_vals[2])
+#define SYSCTL_UINT_THREE	((unsigned int *)&sysctl_uint_vals[3])
+#define SYSCTL_UINT_FOUR	((unsigned int *)&sysctl_uint_vals[4])
+
 /**
  *
  * "dir" originates from read_iter (dir = 0) or write_iter (dir = 1)
@@ -73,6 +79,7 @@ extern const int sysctl_vals[];
 #define SYSCTL_KERN_TO_USER(dir) (!dir)
 
 extern const unsigned long sysctl_long_vals[];
+extern const unsigned int sysctl_uint_vals[];
 
 typedef int proc_handler(const struct ctl_table *ctl, int write, void *buffer,
 		size_t *lenp, loff_t *ppos);
kernel/sysctl.c +3 -0
index c9efb17cc255..d1b5284141c4 100644
@@ -29,6 +29,9 @@ EXPORT_SYMBOL(sysctl_vals);
 const unsigned long sysctl_long_vals[] = { 0, 1, LONG_MAX };
 EXPORT_SYMBOL_GPL(sysctl_long_vals);
 
+const unsigned int sysctl_uint_vals[] = { 0, 1, 2, 3, 4 };
+EXPORT_SYMBOL_GPL(sysctl_uint_vals);
+
 #if defined(CONFIG_SYSCTL)
 
 /* Constants used for minimum and maximum */
Signature · 1 line
2.55.0
#3 lore raw

RFC PATCH 3/30 sysctl: add typed field descriptors

In reply to a message not in the archive

A number of sysctl users need to bind a static table description to data owned by a namespace or another object at registration time. ctl_table cannot express this directly because data, extra1 and extra2 contain the resolved pointers consumed by proc handlers.

Such users currently duplicate ctl_table arrays and replace individual members, often by table index. Besides allocating one descriptor for every sysctl instance, this couples the fixup code to the exact ordering of the source table. The void pointers used for data and limits also prevent the compiler from detecting mismatches between a proc handler and its arguments.

Introduce ctl_field as an alternative descriptor format. Each field selects a supported value type and provides typed accessors which resolve the backing data and optional limits from a registration context. Both context-dependent and static limits are supported so common constants do not require trivial accessor functions. A custom field remains available for handlers whose arguments cannot be described by the typed variants.

Store either ctl_table or ctl_field descriptors in ctl_table_header and copy the registration context into the header allocation. This gives the resolved objects the same lifetime as the registered table without allocating or modifying a ctl_table array. The regular registration macro derives the field count and context size at compile time, while the double-underscore interface remains available for contexts embedded in larger objects.

Keep the existing proc handler, permission and BPF interfaces unchanged by materializing a temporary ctl_table only when crossing one of those legacy boundaries. Existing ctl_table users therefore retain their current behaviour, while converted users can share a single read-only descriptor array and gain compile-time checking of the common data and limit types.

---
 fs/proc/proc_sysctl.c  | 280 +++++++++++++++++++++++++++++++++++++----
 include/linux/sysctl.h | 254 ++++++++++++++++++++++++++++++++++++-
 2 files changed, 503 insertions(+), 31 deletions(-)
fs/proc/proc_sysctl.c +254 -26
index 131496490991..88d3cc79fc33 100644
@@ -18,6 +18,7 @@
 #include <linux/mount.h>
 #include <linux/kmemleak.h>
 #include <linux/lockdep.h>
+#include <linux/overflow.h>
 #include "internal.h"
 
 #define list_for_each_table_entry(index, header)			\
@@ -91,20 +92,62 @@ static int sysctl_follow_link(struct ctl_table_header **phead, size_t *pindex);
 static int insert_links(struct ctl_table_header *head);
 static void put_links(struct ctl_table_header *header);
 
+static inline bool is_field_table(const struct ctl_table_header *head)
+{
+	return head->table_kind == SYSCTL_TABLE_KIND_FIELD;
+}
+
 static const char *sysctl_entry_procname(struct ctl_table_header *head,
 					 size_t index)
 {
+	if (is_field_table(head))
+		return head->ctl_fields[index].procname;
+
 	return head->ctl_table[index].procname;
 }
 
 static umode_t sysctl_entry_mode(struct ctl_table_header *head, size_t index)
 {
-	return head->ctl_table[index].mode;
+	const struct sysctl_field *field;
+
+	if (!is_field_table(head))
+		return head->ctl_table[index].mode;
+
+	field = &head->ctl_fields[index];
+
+	if (field->mode_fn) {
+		lockdep_assert_not_held(&sysctl_lock);
+		return field->mode_fn(head->ctx);
+	}
+
+	return field->mode;
+}
+
+static bool sysctl_entry_is_dir(const struct ctl_table_header *head,
+				size_t index)
+{
+	/* Do not call field mode_fn callback while holding sysctl_lock. */
+	if (is_field_table(head))
+		return false;
+
+	return S_ISDIR(head->ctl_table[index].mode);
+}
+
+static bool sysctl_entry_is_link(const struct ctl_table_header *head,
+				 size_t index)
+{
+	if (is_field_table(head))
+		return false;
+
+	return S_ISLNK(head->ctl_table[index].mode);
 }
 
 static struct ctl_table_poll *sysctl_entry_poll(struct ctl_table_header *head,
 						size_t index)
 {
+	if (is_field_table(head))
+		return NULL;
+
 	return head->ctl_table[index].poll;
 }
 
@@ -112,7 +155,122 @@ static const struct ctl_table *
 sysctl_entry_table(struct ctl_table_header *head, size_t index,
 		   struct ctl_table *table)
 {
-	return &head->ctl_table[index];
+	const struct sysctl_field *field;
+
+	if (!is_field_table(head))
+		return &head->ctl_table[index];
+
+	field = &head->ctl_fields[index];
+
+	memset(table, 0, sizeof(*table));
+	table->procname = field->procname;
+	table->mode = field->mode;
+
+	if (field->mode_fn)
+		table->mode = field->mode_fn(head->ctx);
+
+	switch (field->type) {
+	case SYSCTL_FIELD_CUSTOM:
+		table->proc_handler = field->ctl_custom.proc_handler;
+		table->maxlen = field->ctl_custom.maxlen;
+		if (field->ctl_custom.data)
+			table->data = field->ctl_custom.data(head->ctx);
+		if (field->ctl_custom.extra1)
+			table->extra1 = field->ctl_custom.extra1(head->ctx);
+		if (field->ctl_custom.extra2)
+			table->extra2 = field->ctl_custom.extra2(head->ctx);
+		break;
+	case SYSCTL_FIELD_STRING:
+		table->proc_handler = proc_dostring;
+		table->maxlen = field->ctl_string.maxlen;
+		table->data   = field->ctl_string.data(head->ctx);
+		break;
+	case SYSCTL_FIELD_BOOL:
+		table->proc_handler = proc_dobool;
+		table->maxlen = sizeof(bool);
+		table->data   = field->ctl_bool.data(head->ctx);
+		break;
+	case SYSCTL_FIELD_STATIC_U8_MINMAX:
+		table->proc_handler = proc_dou8vec_minmax;
+		table->maxlen = sizeof(u8);
+		table->data   = field->ctl_static_u8.data(head->ctx);
+		table->extra1 = field->ctl_static_u8.min_value;
+		table->extra2 = field->ctl_static_u8.max_value;
+		break;
+	case SYSCTL_FIELD_STATIC_INT_MINMAX:
+		table->proc_handler = proc_dointvec_minmax;
+		table->maxlen = sizeof(int);
+		table->data   = field->ctl_static_int.data(head->ctx);
+		table->extra1 = field->ctl_static_int.min_value;
+		table->extra2 = field->ctl_static_int.max_value;
+		break;
+	case SYSCTL_FIELD_STATIC_UINT_MINMAX:
+		table->proc_handler = proc_douintvec_minmax;
+		table->maxlen = sizeof(unsigned int);
+		table->data   = field->ctl_static_uint.data(head->ctx);
+		table->extra1 = field->ctl_static_uint.min_value;
+		table->extra2 = field->ctl_static_uint.max_value;
+		break;
+	case SYSCTL_FIELD_STATIC_ULONG_MINMAX:
+		table->proc_handler = proc_doulongvec_minmax;
+		table->maxlen = sizeof(unsigned long);
+		table->data   = field->ctl_static_ulong.data(head->ctx);
+		table->extra1 = field->ctl_static_ulong.min_value;
+		table->extra2 = field->ctl_static_ulong.max_value;
+		break;
+	case SYSCTL_FIELD_U8:
+	case SYSCTL_FIELD_U8_MINMAX:
+		table->proc_handler = proc_dou8vec_minmax;
+		table->maxlen = sizeof(u8);
+		table->data   = field->ctl_u8.data(head->ctx);
+
+		if (field->ctl_u8.min_value)
+			table->extra1 = field->ctl_u8.min_value(head->ctx);
+
+		if (field->ctl_u8.max_value)
+			table->extra2 = field->ctl_u8.max_value(head->ctx);
+		break;
+	case SYSCTL_FIELD_INT:
+	case SYSCTL_FIELD_INT_MINMAX:
+		table->proc_handler = field->type == SYSCTL_FIELD_INT ?
+				      proc_dointvec : proc_dointvec_minmax;
+		table->maxlen = sizeof(int);
+		table->data   = field->ctl_int.data(head->ctx);
+
+		if (field->ctl_int.min_value)
+			table->extra1 = field->ctl_int.min_value(head->ctx);
+
+		if (field->ctl_int.max_value)
+			table->extra2 = field->ctl_int.max_value(head->ctx);
+		break;
+	case SYSCTL_FIELD_UINT:
+	case SYSCTL_FIELD_UINT_MINMAX:
+		table->proc_handler = field->type == SYSCTL_FIELD_UINT ?
+				      proc_douintvec : proc_douintvec_minmax;
+		table->maxlen = sizeof(unsigned int);
+		table->data   = field->ctl_uint.data(head->ctx);
+
+		if (field->ctl_uint.min_value)
+			table->extra1 = field->ctl_uint.min_value(head->ctx);
+
+		if (field->ctl_uint.max_value)
+			table->extra2 = field->ctl_uint.max_value(head->ctx);
+		break;
+	case SYSCTL_FIELD_ULONG:
+	case SYSCTL_FIELD_ULONG_MINMAX:
+		table->proc_handler = proc_doulongvec_minmax;
+		table->maxlen = sizeof(unsigned long);
+		table->data   = field->ctl_ulong.data(head->ctx);
+
+		if (field->ctl_ulong.min_value)
+			table->extra1 = field->ctl_ulong.min_value(head->ctx);
+
+		if (field->ctl_ulong.max_value)
+			table->extra2 = field->ctl_ulong.max_value(head->ctx);
+		break;
+	}
+
+	return table;
 }
 
 static void sysctl_print_dir(struct ctl_dir *dir)
@@ -212,9 +370,17 @@ static void erase_entry(struct ctl_table_header *head, size_t index)
 
 static void init_header(struct ctl_table_header *head,
 	struct ctl_table_root *root, struct ctl_table_set *set,
-	struct ctl_node *node, const struct ctl_table *table, size_t table_size)
+	struct ctl_node *node, const struct ctl_table *table,
+	const struct sysctl_field *fields, size_t table_size,
+	const struct sysctl_context *ctx)
 {
-	head->ctl_table = table;
+	if (fields) {
+		head->ctl_fields = fields;
+		head->table_kind = SYSCTL_TABLE_KIND_FIELD;
+	} else {
+		head->ctl_table = table;
+		head->table_kind = SYSCTL_TABLE_KIND_TABLE;
+	}
 	head->ctl_table_size = table_size;
 	head->ctl_table_arg = table;
 	head->used = 0;
@@ -223,6 +389,7 @@ static void init_header(struct ctl_table_header *head,
 	head->unregistering = NULL;
 	head->root = root;
 	head->set = set;
+	head->ctx = ctx;
 	head->parent = NULL;
 	head->node = node;
 	INIT_HLIST_HEAD(&head->inodes);
@@ -982,7 +1149,7 @@ static struct ctl_dir *find_subdir(struct ctl_dir *dir,
 
 	if (!find_entry(&head, &index, dir, name, namelen))
 		return ERR_PTR(-ENOENT);
-	if (!S_ISDIR(sysctl_entry_mode(head, index)))
+	if (!sysctl_entry_is_dir(head, index))
 		return ERR_PTR(-ENOTDIR);
 	return container_of(head, struct ctl_dir, header);
 }
@@ -1007,7 +1174,8 @@ static struct ctl_dir *new_dir(struct ctl_table_set *set,
 	memcpy(new_name, name, namelen);
 	table[0].procname = new_name;
 	table[0].mode = S_IFDIR|S_IRUGO|S_IXUGO;
-	init_header(&new->header, set->dir.header.root, set, node, table, 1);
+	init_header(&new->header, set->dir.header.root, set, node, table, NULL,
+		    1, NULL);
 
 	return new;
 }
@@ -1256,7 +1424,7 @@ static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table_
 		link++;
 	}
 	init_header(links, dir->header.root, dir->header.set, node, link_table,
-		    head->ctl_table_size);
+		    NULL, head->ctl_table_size, NULL);
 	links->nreg = head->ctl_table_size;
 
 	return links;
@@ -1280,10 +1448,10 @@ static bool get_links(struct ctl_dir *dir,
 		if (!find_entry(&tmp_head, &link_index, dir, procname,
 				strlen(procname)))
 			return false;
-		if (S_ISDIR(sysctl_entry_mode(tmp_head, link_index)) &&
-		    S_ISDIR(sysctl_entry_mode(header, index)))
+		if (sysctl_entry_is_dir(tmp_head, link_index) &&
+		    sysctl_entry_is_dir(header, index))
 			continue;
-		if (S_ISLNK(sysctl_entry_mode(tmp_head, link_index)) &&
+		if (sysctl_entry_is_link(tmp_head, link_index) &&
 		    tmp_head->ctl_table[link_index].data == link_root)
 			continue;
 		return false;
@@ -1370,18 +1538,22 @@ static struct ctl_dir *sysctl_mkdir_p(struct ctl_dir *dir, const char *path)
 }
 
 /**
- * __register_sysctl_table - register a leaf sysctl table
+ * __register_sysctl_table_internal - register a leaf sysctl table
  * @set: Sysctl tree to register on
  * @path: The path to the directory the sysctl table is in.
+ * @table: The top-level ctl_table array, or %NULL when registering @fields.
+ * @fields: The top-level ctl_field array, or %NULL when registering @table.
+ * @table_size: The number of elements in @table or @fields.
+ * @ctx: Optional context used to resolve @fields entries.
+ * @ctx_size: Size of @ctx, including any wrapper object that embeds it.
  *
- * @table: the top-level table structure. This table should not be free'd
- *         after registration. So it should not be used on stack. It can either
- *         be a global or dynamically allocated by the caller and free'd later
- *         after sysctl unregistration.
- * @table_size : The number of elements in table
+ * Register a sysctl table hierarchy. One of @table or @fields must be
+ * provided. The descriptor array should not be freed after registration, so it
+ * should not be used on stack. It can either be global or dynamically
+ * allocated by the caller and freed later after sysctl unregistration.
  *
- * Register a sysctl table hierarchy. @table should be a filled in ctl_table
- * array.
+ * If @ctx points to a wrapper object, &struct sysctl_context must be the first
+ * member so @ctx can be copied together with the rest of that object.
  *
  * The members of the &struct ctl_table structure are used as follows:
  * procname - the name of the sysctl file under /proc/sys. Set to %NULL to not
@@ -1412,25 +1584,62 @@ static struct ctl_dir *sysctl_mkdir_p(struct ctl_dir *dir, const char *path)
  * This routine returns %NULL on a failure to register, and a pointer
  * to the table header on success.
  */
-struct ctl_table_header *__register_sysctl_table(
-	struct ctl_table_set *set,
-	const char *path, const struct ctl_table *table, size_t table_size)
+static struct ctl_table_header *
+__register_sysctl_table_internal(struct ctl_table_set *set, const char *path,
+				 const struct ctl_table *table,
+				 const struct sysctl_field *fields,
+				 size_t table_size,
+				 const struct sysctl_context *ctx, size_t ctx_size)
 {
 	struct ctl_table_root *root = set->dir.header.root;
 	struct ctl_table_header *header;
 	struct ctl_dir *dir;
 	struct ctl_node *node;
+	const struct sysctl_context *header_ctx = NULL;
+	size_t nodes_size;
 	size_t alloc_size;
+	size_t context_offset;
+
+	if (ctx && ctx_size < sizeof(*ctx))
+		return NULL;
+
+	if (!ctx && ctx_size)
+		return NULL;
 
-	alloc_size = sizeof(struct ctl_table_header) +
-		     sizeof(struct ctl_node) * table_size;
+	if (check_mul_overflow(sizeof(struct ctl_node), table_size, &nodes_size))
+		return NULL;
+
+	if (check_add_overflow(sizeof(*header), nodes_size, &context_offset))
+		return NULL;
+
+	/*
+	 * Store the copied context after the ctl_node array. struct sysctl_context
+	 * is the first member of any caller-defined wrapper, whose alignment
+	 * must not exceed that of struct sysctl_context.
+	 */
+	if (ctx) {
+		if (check_add_overflow(context_offset,
+				       __alignof__(*ctx) - 1, &context_offset))
+			return NULL;
+		context_offset = ALIGN_DOWN(context_offset, __alignof__(*ctx));
+	}
+
+	if (check_add_overflow(context_offset, ctx_size, &alloc_size))
+		return NULL;
 
 	header = kzalloc(alloc_size, GFP_KERNEL_ACCOUNT);
 	if (!header)
 		return NULL;
 
 	node = (struct ctl_node *)(header + 1);
-	init_header(header, root, set, node, table, table_size);
+	if (ctx) {
+		header_ctx = (const struct sysctl_context *)((void *)header +
+							   context_offset);
+		memcpy((void *)header_ctx, ctx, ctx_size);
+	}
+
+	init_header(header, root, set, node, table, fields, table_size,
+		    header_ctx);
 	if (sysctl_check_table(path, header))
 		goto fail;
 
@@ -1460,6 +1669,25 @@ struct ctl_table_header *__register_sysctl_table(
 	return NULL;
 }
 
+struct ctl_table_header *
+__register_sysctl_fields(struct ctl_table_set *set, const char *path,
+			 const struct sysctl_field *fields, size_t field_count,
+			 const struct sysctl_context *ctx, size_t ctx_size)
+{
+	return __register_sysctl_table_internal(set, path, NULL, fields,
+						field_count, ctx, ctx_size);
+}
+EXPORT_SYMBOL(__register_sysctl_fields);
+
+struct ctl_table_header *
+__register_sysctl_table(struct ctl_table_set *set, const char *path,
+			const struct ctl_table *table, size_t table_size)
+{
+	return __register_sysctl_table_internal(set, path, table, NULL,
+						table_size, NULL, 0);
+}
+EXPORT_SYMBOL(__register_sysctl_table);
+
 /**
  * register_sysctl_sz - register a sysctl table
  * @path: The path to the directory the sysctl table is in. If the path
@@ -1548,7 +1776,7 @@ static void put_links(struct ctl_table_header *header)
 
 		if (link &&
 		    ((S_ISDIR(link->mode) &&
-		      S_ISDIR(sysctl_entry_mode(header, index))) ||
+		      sysctl_entry_is_dir(header, index)) ||
 		     (S_ISLNK(link->mode) && (link->data == root)))) {
 			drop_sysctl_table(link_head);
 		} else {
@@ -1604,7 +1832,7 @@ void setup_sysctl_set(struct ctl_table_set *set,
 {
 	memset(set, 0, sizeof(*set));
 	set->is_seen = is_seen;
-	init_header(&set->dir.header, root, set, NULL, root_table, 1);
+	init_header(&set->dir.header, root, set, NULL, root_table, NULL, 1, NULL);
 }
 
 void retire_sysctl_set(struct ctl_table_set *set)
include/linux/sysctl.h +249 -5

329 lines not on this page · raw message

Signature · 1 line
2.55.0
#4 lore raw

RFC PATCH 4/30 sysctl: use sysctl_field in ucounts

In reply to a message not in the archive

Convert ucount sysctls. The table can now share one static array across namespaces instead of allocating and rewriting a ctl_table copy for each registration.

---
 kernel/ucount.c | 83 +++++++++++++++++++++++++------------------------
 1 file changed, 43 insertions(+), 40 deletions(-)
kernel/ucount.c +43 -40
index d6dc3e859f12..e16d858b663b 100644
@@ -60,34 +60,48 @@ static struct ctl_table_root set_root = {
 	.permissions = set_permissions,
 };
 
-static long ue_zero = 0;
-static long ue_int_max = INT_MAX;
-
-#define UCOUNT_ENTRY(name)					\
-	{							\
-		.procname	= name,				\
-		.maxlen		= sizeof(long),			\
-		.mode		= 0644,				\
-		.proc_handler	= proc_doulongvec_minmax,	\
-		.extra1		= &ue_zero,			\
-		.extra2		= &ue_int_max,			\
-	}
-static const struct ctl_table user_table[] = {
-	UCOUNT_ENTRY("max_user_namespaces"),
-	UCOUNT_ENTRY("max_pid_namespaces"),
-	UCOUNT_ENTRY("max_uts_namespaces"),
-	UCOUNT_ENTRY("max_ipc_namespaces"),
-	UCOUNT_ENTRY("max_net_namespaces"),
-	UCOUNT_ENTRY("max_mnt_namespaces"),
-	UCOUNT_ENTRY("max_cgroup_namespaces"),
-	UCOUNT_ENTRY("max_time_namespaces"),
+static unsigned long ue_zero = 0;
+static unsigned long ue_int_max = INT_MAX;
+
+#define UCOUNT_DATA(name, type)						\
+static unsigned long *name ## _data(const struct sysctl_context *ctx)	\
+{									\
+	return &ctx->ns.user_ns->ucount_max[type];			\
+}
+
+UCOUNT_DATA(user_ns, UCOUNT_USER_NAMESPACES);
+UCOUNT_DATA(pid_ns, UCOUNT_PID_NAMESPACES);
+UCOUNT_DATA(uts_ns, UCOUNT_UTS_NAMESPACES);
+UCOUNT_DATA(ipc_ns, UCOUNT_IPC_NAMESPACES);
+UCOUNT_DATA(net_ns, UCOUNT_NET_NAMESPACES);
+UCOUNT_DATA(mnt_ns, UCOUNT_MNT_NAMESPACES);
+UCOUNT_DATA(cgroup_ns, UCOUNT_CGROUP_NAMESPACES);
+UCOUNT_DATA(time_ns, UCOUNT_TIME_NAMESPACES);
+#ifdef CONFIG_INOTIFY_USER
+UCOUNT_DATA(inotify_instances, UCOUNT_INOTIFY_INSTANCES);
+UCOUNT_DATA(inotify_watches, UCOUNT_INOTIFY_WATCHES);
+#endif
+#ifdef CONFIG_FANOTIFY
+UCOUNT_DATA(fanotify_groups, UCOUNT_FANOTIFY_GROUPS);
+UCOUNT_DATA(fanotify_marks, UCOUNT_FANOTIFY_MARKS);
+#endif
+
+static const struct sysctl_field user_table[] = {
+	SYSCTL_FIELD_STATIC_ULONG_MINMAX("max_user_namespaces", 0644, user_ns_data, &ue_zero, &ue_int_max),
+	SYSCTL_FIELD_STATIC_ULONG_MINMAX("max_pid_namespaces", 0644, pid_ns_data, &ue_zero, &ue_int_max),
+	SYSCTL_FIELD_STATIC_ULONG_MINMAX("max_uts_namespaces", 0644, uts_ns_data, &ue_zero, &ue_int_max),
+	SYSCTL_FIELD_STATIC_ULONG_MINMAX("max_ipc_namespaces", 0644, ipc_ns_data, &ue_zero, &ue_int_max),
+	SYSCTL_FIELD_STATIC_ULONG_MINMAX("max_net_namespaces", 0644, net_ns_data, &ue_zero, &ue_int_max),
+	SYSCTL_FIELD_STATIC_ULONG_MINMAX("max_mnt_namespaces", 0644, mnt_ns_data, &ue_zero, &ue_int_max),
+	SYSCTL_FIELD_STATIC_ULONG_MINMAX("max_cgroup_namespaces", 0644, cgroup_ns_data, &ue_zero, &ue_int_max),
+	SYSCTL_FIELD_STATIC_ULONG_MINMAX("max_time_namespaces", 0644, time_ns_data, &ue_zero, &ue_int_max),
 #ifdef CONFIG_INOTIFY_USER
-	UCOUNT_ENTRY("max_inotify_instances"),
-	UCOUNT_ENTRY("max_inotify_watches"),
+	SYSCTL_FIELD_STATIC_ULONG_MINMAX("max_inotify_instances", 0644, inotify_instances_data, &ue_zero, &ue_int_max),
+	SYSCTL_FIELD_STATIC_ULONG_MINMAX("max_inotify_watches", 0644, inotify_watches_data, &ue_zero, &ue_int_max),
 #endif
 #ifdef CONFIG_FANOTIFY
-	UCOUNT_ENTRY("max_fanotify_groups"),
-	UCOUNT_ENTRY("max_fanotify_marks"),
+	SYSCTL_FIELD_STATIC_ULONG_MINMAX("max_fanotify_groups", 0644, fanotify_groups_data, &ue_zero, &ue_int_max),
+	SYSCTL_FIELD_STATIC_ULONG_MINMAX("max_fanotify_marks", 0644, fanotify_marks_data, &ue_zero, &ue_int_max),
 #endif
 };
 #endif /* CONFIG_SYSCTL */
@@ -95,21 +109,14 @@ static const struct ctl_table user_table[] = {
 bool setup_userns_sysctls(struct user_namespace *ns)
 {
 #ifdef CONFIG_SYSCTL
-	struct ctl_table *tbl;
+	struct sysctl_context ctx = {
+		.ns.user_ns = ns,
+	};
 
 	BUILD_BUG_ON(ARRAY_SIZE(user_table) != UCOUNT_COUNTS);
 	setup_sysctl_set(&ns->set, &set_root, set_is_seen);
-	tbl = kmemdup(user_table, sizeof(user_table), GFP_KERNEL);
-	if (tbl) {
-		int i;
-		for (i = 0; i < UCOUNT_COUNTS; i++) {
-			tbl[i].data = &ns->ucount_max[i];
-		}
-		ns->sysctls = __register_sysctl_table(&ns->set, "user", tbl,
-						      ARRAY_SIZE(user_table));
-	}
+	ns->sysctls = register_sysctl_fields(&ns->set, "user", user_table, &ctx);
 	if (!ns->sysctls) {
-		kfree(tbl);
 		retire_sysctl_set(&ns->set);
 		return false;
 	}
@@ -120,12 +127,8 @@ bool setup_userns_sysctls(struct user_namespace *ns)
 void retire_userns_sysctls(struct user_namespace *ns)
 {
 #ifdef CONFIG_SYSCTL
-	const struct ctl_table *tbl;
-
-	tbl = ns->sysctls->ctl_table_arg;
 	unregister_sysctl_table(ns->sysctls);
 	retire_sysctl_set(&ns->set);
-	kfree(tbl);
 #endif
 }
 
Signature · 1 line
2.55.0
#5 lore raw

RFC PATCH 5/30 sysctl: ipc: use sysctl_field in mq_sysctl

In reply to a message not in the archive

Convert mq_sysctls. The table can now share one static array across ipc namespaces instead of allocating and rewriting a ctl_table copy for each registration.

---
 include/linux/sysctl.h |   2 +
 ipc/mq_sysctl.c        | 100 +++++++++--------------------------------
 2 files changed, 24 insertions(+), 78 deletions(-)
include/linux/sysctl.h +2 -0
index 3c7f2ffab334..e2831190e484 100644
@@ -38,6 +38,7 @@ struct nsproxy;
 struct ctl_table_root;
 struct ctl_table_header;
 struct ctl_dir;
+struct ipc_namespace;
 struct user_namespace;
 
 /* Keep the same order as in fs/proc/proc_sysctl.c */
@@ -90,6 +91,7 @@ typedef int proc_handler(const struct ctl_table *ctl, int write, void *buffer,
 struct sysctl_context {
 	union {
 		struct user_namespace *user_ns;
+		struct ipc_namespace *ipc_ns;
 	} ns;
 };
 
ipc/mq_sysctl.c +22 -78

128 lines not on this page · raw message

Signature · 1 line
2.55.0
#6 lore raw

RFC PATCH 6/30 sysctl: ipc: use sysctl_field in ipc_sysctl

In reply to a message not in the archive

IPC clones its sysctl table for each namespace so registration can rewrite data pointers from init_ipc_ns to the target namespace. The cloned table then has to stay alive until namespace teardown only so it can be freed after unregistering the sysctls.

Use sysctl_field to derive IPC namespace data from the registration context instead. This keeps the sysctl descriptors static and const, and removes the per-namespace table copy and unregister-time free.

Keep the IPC-specific handlers responsible for their own min/max bounds so the conversion does not need to extend the generic custom field interface.

---
 ipc/ipc_sysctl.c | 256 ++++++++++++++++++-----------------------------
 1 file changed, 98 insertions(+), 158 deletions(-)
ipc/ipc_sysctl.c +98 -158
index 9b087ebeb643..7b85b0f5ef82 100644
@@ -13,7 +13,6 @@
 #include <linux/capability.h>
 #include <linux/ipc_namespace.h>
 #include <linux/msg.h>
-#include <linux/slab.h>
 #include <linux/cred.h>
 #include "util.h"
 
@@ -48,6 +47,10 @@ static int proc_ipc_auto_msgmni(const struct ctl_table *table, int write,
 	return proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos);
 }
 
+int ipc_mni = IPCMNI;
+int ipc_mni_shift = IPCMNI_SHIFT;
+int ipc_min_cycle = RADIX_TREE_MAP_SIZE;
+
 static int proc_ipc_sem_dointvec(const struct ctl_table *table, int write,
 	void *buffer, size_t *lenp, loff_t *ppos)
 {
@@ -69,117 +72,100 @@ static int proc_ipc_sem_dointvec(const struct ctl_table *table, int write,
 	return ret;
 }
 
-int ipc_mni = IPCMNI;
-int ipc_mni_shift = IPCMNI_SHIFT;
-int ipc_min_cycle = RADIX_TREE_MAP_SIZE;
+#define IPC_DATA(type, field)						\
+static type *ipc_ ## field ## _data(const struct sysctl_context *ctx)	\
+{									\
+	return &ctx->ns.ipc_ns->field;					\
+}
+
+IPC_DATA(void, shm_ctlmax)
+IPC_DATA(void, shm_ctlall)
+IPC_DATA(int, shm_ctlmni)
+IPC_DATA(void, shm_rmid_forced)
+IPC_DATA(void, msg_ctlmax)
+IPC_DATA(void, msg_ctlmni)
+IPC_DATA(void, msg_ctlmnb)
+IPC_DATA(void, sem_ctls)
+
+#define IPC_LIMIT(name, value)						\
+static void *ipc_ ## name ## _limit(const struct sysctl_context *ctx)	\
+{									\
+	return value;							\
+}
+
+IPC_LIMIT(zero, SYSCTL_ZERO)
+IPC_LIMIT(one, SYSCTL_ONE)
+IPC_LIMIT(int_max, SYSCTL_INT_MAX)
+IPC_LIMIT(mni, &ipc_mni)
+
+#define IPC_FIELD_CUSTOM_MINMAX(_procname, _len, _data, _handler, _min, _max) \
+	{								\
+		.procname = (_procname),				\
+		.mode = 0644,						\
+		.type = SYSCTL_FIELD_CUSTOM,				\
+		.ctl_custom = {						\
+			.proc_handler = (_handler),			\
+			.maxlen = (_len),				\
+			.data = (_data),				\
+			.extra1 = (_min),				\
+			.extra2 = (_max),				\
+		},							\
+	}
 
-static const struct ctl_table ipc_sysctls[] = {
-	{
-		.procname	= "shmmax",
-		.data		= &init_ipc_ns.shm_ctlmax,
-		.maxlen		= sizeof(init_ipc_ns.shm_ctlmax),
-		.mode		= 0644,
-		.proc_handler	= proc_doulongvec_minmax,
-	},
-	{
-		.procname	= "shmall",
-		.data		= &init_ipc_ns.shm_ctlall,
-		.maxlen		= sizeof(init_ipc_ns.shm_ctlall),
-		.mode		= 0644,
-		.proc_handler	= proc_doulongvec_minmax,
-	},
-	{
-		.procname	= "shmmni",
-		.data		= &init_ipc_ns.shm_ctlmni,
-		.maxlen		= sizeof(init_ipc_ns.shm_ctlmni),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &ipc_mni,
-	},
-	{
-		.procname	= "shm_rmid_forced",
-		.data		= &init_ipc_ns.shm_rmid_forced,
-		.maxlen		= sizeof(init_ipc_ns.shm_rmid_forced),
-		.mode		= 0644,
-		.proc_handler	= proc_ipc_dointvec_minmax_orphans,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
-	},
-	{
-		.procname	= "msgmax",
-		.data		= &init_ipc_ns.msg_ctlmax,
-		.maxlen		= sizeof(init_ipc_ns.msg_ctlmax),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
-	},
-	{
-		.procname	= "msgmni",
-		.data		= &init_ipc_ns.msg_ctlmni,
-		.maxlen		= sizeof(init_ipc_ns.msg_ctlmni),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &ipc_mni,
-	},
-	{
-		.procname	= "auto_msgmni",
-		.data		= NULL,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_ipc_auto_msgmni,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
-	},
-	{
-		.procname	=  "msgmnb",
-		.data		= &init_ipc_ns.msg_ctlmnb,
-		.maxlen		= sizeof(init_ipc_ns.msg_ctlmnb),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
-	},
-	{
-		.procname	= "sem",
-		.data		= &init_ipc_ns.sem_ctls,
-		.maxlen		= 4*sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_ipc_sem_dointvec,
-	},
 #ifdef CONFIG_CHECKPOINT_RESTORE
-	{
-		.procname	= "sem_next_id",
-		.data		= &init_ipc_ns.ids[IPC_SEM_IDS].next_id,
-		.maxlen		= sizeof(init_ipc_ns.ids[IPC_SEM_IDS].next_id),
-		.mode		= 0444,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
-	},
-	{
-		.procname	= "msg_next_id",
-		.data		= &init_ipc_ns.ids[IPC_MSG_IDS].next_id,
-		.maxlen		= sizeof(init_ipc_ns.ids[IPC_MSG_IDS].next_id),
-		.mode		= 0444,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
-	},
-	{
-		.procname	= "shm_next_id",
-		.data		= &init_ipc_ns.ids[IPC_SHM_IDS].next_id,
-		.maxlen		= sizeof(init_ipc_ns.ids[IPC_SHM_IDS].next_id),
-		.mode		= 0444,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
-	},
+#define IPC_ID_NEXT_DATA(name, id)					\
+static int *ipc_ ## name ## _data(const struct sysctl_context *ctx)	\
+{									\
+	return &ctx->ns.ipc_ns->ids[id].next_id;			\
+}
+
+IPC_ID_NEXT_DATA(sem_next_id, IPC_SEM_IDS)
+IPC_ID_NEXT_DATA(msg_next_id, IPC_MSG_IDS)
+IPC_ID_NEXT_DATA(shm_next_id, IPC_SHM_IDS)
+#endif
+
+static const struct sysctl_field ipc_sysctls[] = {
+	SYSCTL_FIELD_CUSTOM("shmmax", 0644, sizeof(init_ipc_ns.shm_ctlmax),
+			 ipc_shm_ctlmax_data, proc_doulongvec_minmax),
+	SYSCTL_FIELD_CUSTOM("shmall", 0644, sizeof(init_ipc_ns.shm_ctlall),
+			 ipc_shm_ctlall_data, proc_doulongvec_minmax),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("shmmni", 0644, ipc_shm_ctlmni_data,
+				    SYSCTL_ZERO, &ipc_mni),
+	IPC_FIELD_CUSTOM_MINMAX("shm_rmid_forced",
+				sizeof(init_ipc_ns.shm_rmid_forced),
+				ipc_shm_rmid_forced_data,
+				proc_ipc_dointvec_minmax_orphans,
+				ipc_zero_limit, ipc_one_limit),
+	IPC_FIELD_CUSTOM_MINMAX("msgmax", sizeof(init_ipc_ns.msg_ctlmax),
+				ipc_msg_ctlmax_data, proc_dointvec_minmax,
+				ipc_zero_limit, ipc_int_max_limit),
+	IPC_FIELD_CUSTOM_MINMAX("msgmni", sizeof(init_ipc_ns.msg_ctlmni),
+				ipc_msg_ctlmni_data, proc_dointvec_minmax,
+				ipc_zero_limit, ipc_mni_limit),
+	IPC_FIELD_CUSTOM_MINMAX("auto_msgmni", sizeof(int), NULL,
+				proc_ipc_auto_msgmni,
+				ipc_zero_limit, ipc_one_limit),
+	IPC_FIELD_CUSTOM_MINMAX("msgmnb", sizeof(init_ipc_ns.msg_ctlmnb),
+				ipc_msg_ctlmnb_data, proc_dointvec_minmax,
+				ipc_zero_limit, ipc_int_max_limit),
+	SYSCTL_FIELD_CUSTOM("sem", 0644, 4 * sizeof(int), ipc_sem_ctls_data,
+			 proc_ipc_sem_dointvec),
+#ifdef CONFIG_CHECKPOINT_RESTORE
+	SYSCTL_FIELD_STATIC_INT_MINMAX("sem_next_id", 0444,
+				    ipc_sem_next_id_data, SYSCTL_ZERO,
+				    SYSCTL_INT_MAX),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("msg_next_id", 0444,
+				    ipc_msg_next_id_data, SYSCTL_ZERO,
+				    SYSCTL_INT_MAX),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("shm_next_id", 0444,
+				    ipc_shm_next_id_data, SYSCTL_ZERO,
+				    SYSCTL_INT_MAX),
 #endif
 };
 
+#undef IPC_FIELD_CUSTOM_MINMAX
+#undef IPC_LIMIT
+
 static struct ctl_table_set *set_lookup(struct ctl_table_root *root)
 {
 	return &current->nsproxy->ipc_ns->ipc_set;
@@ -244,57 +230,15 @@ static struct ctl_table_root set_root = {
 
 bool setup_ipc_sysctls(struct ipc_namespace *ns)
 {
-	struct ctl_table *tbl;
+	struct sysctl_context ctx = {
+		.ns.ipc_ns = ns,
+	};
 
 	setup_sysctl_set(&ns->ipc_set, &set_root, set_is_seen);
 
-	tbl = kmemdup(ipc_sysctls, sizeof(ipc_sysctls), GFP_KERNEL);
-	if (tbl) {
-		int i;
-
-		for (i = 0; i < ARRAY_SIZE(ipc_sysctls); i++) {
-			if (tbl[i].data == &init_ipc_ns.shm_ctlmax)
-				tbl[i].data = &ns->shm_ctlmax;
-
-			else if (tbl[i].data == &init_ipc_ns.shm_ctlall)
-				tbl[i].data = &ns->shm_ctlall;
-
-			else if (tbl[i].data == &init_ipc_ns.shm_ctlmni)
-				tbl[i].data = &ns->shm_ctlmni;
-
-			else if (tbl[i].data == &init_ipc_ns.shm_rmid_forced)
-				tbl[i].data = &ns->shm_rmid_forced;
-
-			else if (tbl[i].data == &init_ipc_ns.msg_ctlmax)
-				tbl[i].data = &ns->msg_ctlmax;
-
-			else if (tbl[i].data == &init_ipc_ns.msg_ctlmni)
-				tbl[i].data = &ns->msg_ctlmni;
-
-			else if (tbl[i].data == &init_ipc_ns.msg_ctlmnb)
-				tbl[i].data = &ns->msg_ctlmnb;
-
-			else if (tbl[i].data == &init_ipc_ns.sem_ctls)
-				tbl[i].data = &ns->sem_ctls;
-#ifdef CONFIG_CHECKPOINT_RESTORE
-			else if (tbl[i].data == &init_ipc_ns.ids[IPC_SEM_IDS].next_id)
-				tbl[i].data = &ns->ids[IPC_SEM_IDS].next_id;
-
-			else if (tbl[i].data == &init_ipc_ns.ids[IPC_MSG_IDS].next_id)
-				tbl[i].data = &ns->ids[IPC_MSG_IDS].next_id;
-
-			else if (tbl[i].data == &init_ipc_ns.ids[IPC_SHM_IDS].next_id)
-				tbl[i].data = &ns->ids[IPC_SHM_IDS].next_id;
-#endif
-			else
-				tbl[i].data = NULL;
-		}
-
-		ns->ipc_sysctls = __register_sysctl_table(&ns->ipc_set, "kernel", tbl,
-							  ARRAY_SIZE(ipc_sysctls));
-	}
+	ns->ipc_sysctls = register_sysctl_fields(&ns->ipc_set, "kernel",
+						 ipc_sysctls, &ctx);
 	if (!ns->ipc_sysctls) {
-		kfree(tbl);
 		retire_sysctl_set(&ns->ipc_set);
 		return false;
 	}
@@ -304,12 +248,8 @@ bool setup_ipc_sysctls(struct ipc_namespace *ns)
 
 void retire_ipc_sysctls(struct ipc_namespace *ns)
 {
-	const struct ctl_table *tbl;
-
-	tbl = ns->ipc_sysctls->ctl_table_arg;
 	unregister_sysctl_table(ns->ipc_sysctls);
 	retire_sysctl_set(&ns->ipc_set);
-	kfree(tbl);
 }
 
 static int __init ipc_sysctl_init(void)
Signature · 1 line
2.55.0
#7 lore raw

RFC PATCH 7/30 sysctl: use sysctl_field in pid sysctls

In reply to a message not in the archive

The pid namespace sysctl registration allocates a private ctl_table copy only to replace the static pid_max data pointer with the value from the registered namespace. This keeps otherwise shared sysctl metadata writable at registration time and requires per-namespace table lifetime management.

Use a sysctl_field accessor to derive pid_max from the registration context instead. The pid sysctl table can remain static and const, while registration no longer needs to clone, patch and free a ctl_table array.

---
 include/linux/sysctl.h |  2 ++
 kernel/pid.c           | 41 +++++++++++++----------------------------
 2 files changed, 15 insertions(+), 28 deletions(-)
include/linux/sysctl.h +2 -0
index e2831190e484..056d10f4ab3d 100644
@@ -39,6 +39,7 @@ struct ctl_table_root;
 struct ctl_table_header;
 struct ctl_dir;
 struct ipc_namespace;
+struct pid_namespace;
 struct user_namespace;
 
 /* Keep the same order as in fs/proc/proc_sysctl.c */
@@ -92,6 +93,7 @@ struct sysctl_context {
 	union {
 		struct user_namespace *user_ns;
 		struct ipc_namespace *ipc_ns;
+		struct pid_namespace *pid_ns;
 	} ns;
 };
 
kernel/pid.c +13 -28

69 lines not on this page · raw message

Signature · 1 line
2.55.0
#8 lore raw

RFC PATCH 8/30 sysctl: net: use sysctl_field in unix sysctl

In reply to a message not in the archive

The unix sysctl table is cloned for non-init network namespaces only to replace the data pointer with per-net storage. That keeps a mutable ctl_table copy alive for each namespace even though the descriptor itself is otherwise static.

Add a netns-aware sysctl_field registration helper and use it for the unix sysctl table. The data pointer is derived from the registration context, so the table can stay const and the per-net clone and free path are no longer needed.

---
 include/linux/sysctl.h     |  2 ++
 net/unix/sysctl_net_unix.c | 48 +++++++++++---------------------------
 2 files changed, 15 insertions(+), 35 deletions(-)
include/linux/sysctl.h +2 -0
index 056d10f4ab3d..bbdb0fcfbcd4 100644
@@ -38,6 +38,7 @@ struct nsproxy;
 struct ctl_table_root;
 struct ctl_table_header;
 struct ctl_dir;
+struct net;
 struct ipc_namespace;
 struct pid_namespace;
 struct user_namespace;
@@ -94,6 +95,7 @@ struct sysctl_context {
 		struct user_namespace *user_ns;
 		struct ipc_namespace *ipc_ns;
 		struct pid_namespace *pid_ns;
+		struct net *net_ns;
 	} ns;
 };
 
net/unix/sysctl_net_unix.c +13 -35

71 lines not on this page · raw message

Signature · 1 line
2.55.0
#9 lore raw

RFC PATCH 9/30 sysctl: net: use sysctl_field in xfrm sysctls

In reply to a message not in the archive

The xfrm sysctl table is cloned for each network namespace only to replace data pointers with per-net storage. The descriptors are otherwise static, while unprivileged network namespaces still register an empty table to preserve the existing visibility rule.

Use sysctl_field accessors to derive the per-net storage from the registration context instead. This keeps the xfrm descriptors const and removes the per-net ctl_table allocation and free path without changing which sysctls are exposed.

---
 net/xfrm/xfrm_sysctl.c | 77 ++++++++++++++++--------------------------
 1 file changed, 30 insertions(+), 47 deletions(-)
net/xfrm/xfrm_sysctl.c +30 -47
index ca003e8a0376..1097dd4dc0dc 100644
@@ -1,6 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <linux/sysctl.h>
-#include <linux/slab.h>
 #include <net/net_namespace.h>
 #include <net/xfrm.h>
 
@@ -13,71 +12,55 @@ static void __net_init __xfrm_sysctl_init(struct net *net)
 }
 
 #ifdef CONFIG_SYSCTL
-static struct ctl_table xfrm_table[] = {
-	{
-		.procname	= "xfrm_aevent_etime",
-		.maxlen		= sizeof(u32),
-		.mode		= 0644,
-		.proc_handler	= proc_douintvec
-	},
-	{
-		.procname	= "xfrm_aevent_rseqth",
-		.maxlen		= sizeof(u32),
-		.mode		= 0644,
-		.proc_handler	= proc_douintvec
-	},
-	{
-		.procname	= "xfrm_larval_drop",
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec
-	},
-	{
-		.procname	= "xfrm_acq_expires",
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec
-	},
+#define XFRM_UINT_DATA(name)						\
+static unsigned int *xfrm_ ## name ## _data(const struct sysctl_context *ctx) \
+{									\
+	return &ctx->ns.net_ns->xfrm.name;				\
+}
+
+#define XFRM_INT_DATA(name)						\
+static int *xfrm_ ## name ## _data(const struct sysctl_context *ctx)	\
+{									\
+	return &ctx->ns.net_ns->xfrm.name;				\
+}
+
+XFRM_UINT_DATA(sysctl_aevent_etime)
+XFRM_UINT_DATA(sysctl_aevent_rseqth)
+XFRM_INT_DATA(sysctl_larval_drop)
+XFRM_INT_DATA(sysctl_acq_expires)
+
+static const struct sysctl_field xfrm_table[] = {
+	SYSCTL_FIELD_UINT("xfrm_aevent_etime", 0644, xfrm_sysctl_aevent_etime_data),
+	SYSCTL_FIELD_UINT("xfrm_aevent_rseqth", 0644, xfrm_sysctl_aevent_rseqth_data),
+	SYSCTL_FIELD_INT("xfrm_larval_drop", 0644, xfrm_sysctl_larval_drop_data),
+	SYSCTL_FIELD_INT("xfrm_acq_expires", 0644, xfrm_sysctl_acq_expires_data),
 };
 
 int __net_init xfrm_sysctl_init(struct net *net)
 {
-	struct ctl_table *table;
+	struct sysctl_context ctx = {
+		.ns.net_ns = net,
+	};
 	size_t table_size = ARRAY_SIZE(xfrm_table);
 
 	__xfrm_sysctl_init(net);
 
-	table = kmemdup(xfrm_table, sizeof(xfrm_table), GFP_KERNEL);
-	if (!table)
-		goto out_kmemdup;
-	table[0].data = &net->xfrm.sysctl_aevent_etime;
-	table[1].data = &net->xfrm.sysctl_aevent_rseqth;
-	table[2].data = &net->xfrm.sysctl_larval_drop;
-	table[3].data = &net->xfrm.sysctl_acq_expires;
-
 	/* Don't export sysctls to unprivileged users */
 	if (net->user_ns != &init_user_ns)
 		table_size = 0;
 
-	net->xfrm.sysctl_hdr = register_net_sysctl_sz(net, "net/core", table,
-						      table_size);
+	net->xfrm.sysctl_hdr = __register_sysctl_fields(&net->sysctls, "net/core",
+							xfrm_table, table_size,
+							&ctx, sizeof(ctx));
 	if (!net->xfrm.sysctl_hdr)
-		goto out_register;
-	return 0;
+		return -ENOMEM;
 
-out_register:
-	kfree(table);
-out_kmemdup:
-	return -ENOMEM;
+	return 0;
 }
 
 void __net_exit xfrm_sysctl_fini(struct net *net)
 {
-	const struct ctl_table *table;
-
-	table = net->xfrm.sysctl_hdr->ctl_table_arg;
 	unregister_net_sysctl_table(net->xfrm.sysctl_hdr);
-	kfree(table);
 }
 #else
 int __net_init xfrm_sysctl_init(struct net *net)
Signature · 1 line
2.55.0
#10 lore raw

RFC PATCH 10/30 sysctl: net: use sysctl_field for simple IPv4 per-net sysctls

In reply to a message not in the archive

Several IPv4 sysctl tables are cloned per network namespace only to bind entries to namespace-local storage. The tables themselves are otherwise static, and some entries also use extra fields only to recover the owning network namespace.

Use sysctl_field for the simple per-net IPv4 sysctls in xfrm4, IP fragment handling, and route configuration. The data pointers are derived from the registration context, so the descriptors can stay const and the per-net ctl_table allocation and free paths are no longer needed.

---
 net/ipv4/ip_fragment.c  |  96 +++++++++++++++----------------------
 net/ipv4/route.c        | 102 ++++++++++++++++------------------------
 net/ipv4/xfrm4_policy.c |  52 ++++++--------------
 3 files changed, 92 insertions(+), 158 deletions(-)
net/ipv4/ip_fragment.c +38 -58
index 56b0f738d2f2..9d808ecf7b71 100644
@@ -548,32 +548,37 @@ EXPORT_SYMBOL(ip_check_defrag);
 #ifdef CONFIG_SYSCTL
 static int dist_min;
 
-static struct ctl_table ip4_frags_ns_ctl_table[] = {
-	{
-		.procname	= "ipfrag_high_thresh",
-		.maxlen		= sizeof(unsigned long),
-		.mode		= 0644,
-		.proc_handler	= proc_doulongvec_minmax,
-	},
-	{
-		.procname	= "ipfrag_low_thresh",
-		.maxlen		= sizeof(unsigned long),
-		.mode		= 0644,
-		.proc_handler	= proc_doulongvec_minmax,
-	},
-	{
-		.procname	= "ipfrag_time",
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	{
-		.procname	= "ipfrag_max_dist",
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &dist_min,
-	},
+static unsigned long *ip4_frags_high_thresh_data(const struct sysctl_context *ctx)
+{
+	return &ctx->ns.net_ns->ipv4.fqdir->high_thresh;
+}
+
+static unsigned long *ip4_frags_low_thresh_data(const struct sysctl_context *ctx)
+{
+	return &ctx->ns.net_ns->ipv4.fqdir->low_thresh;
+}
+
+static int *ip4_frags_max_dist_data(const struct sysctl_context *ctx)
+{
+	return &ctx->ns.net_ns->ipv4.fqdir->max_dist;
+}
+
+static void *ip4_frags_timeout_data(const struct sysctl_context *ctx)
+{
+	return &ctx->ns.net_ns->ipv4.fqdir->timeout;
+}
+
+static const struct sysctl_field ip4_frags_ns_ctl_table[] = {
+	SYSCTL_FIELD_ULONG_MINMAX("ipfrag_high_thresh", 0644,
+			       ip4_frags_high_thresh_data,
+			       ip4_frags_low_thresh_data, NULL),
+	SYSCTL_FIELD_ULONG_MINMAX("ipfrag_low_thresh", 0644,
+			       ip4_frags_low_thresh_data,
+			       NULL, ip4_frags_high_thresh_data),
+	SYSCTL_FIELD_CUSTOM("ipfrag_time", 0644, sizeof(int),
+			 ip4_frags_timeout_data, proc_dointvec_jiffies),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("ipfrag_max_dist", 0644,
+				    ip4_frags_max_dist_data, &dist_min, NULL),
 };
 
 /* secret interval has been deprecated */
@@ -590,45 +595,20 @@ static struct ctl_table ip4_frags_ctl_table[] = {
 
 static int __net_init ip4_frags_ns_ctl_register(struct net *net)
 {
-	struct ctl_table *table;
-	struct ctl_table_header *hdr;
-
-	table = ip4_frags_ns_ctl_table;
-	if (!net_eq(net, &init_net)) {
-		table = kmemdup(table, sizeof(ip4_frags_ns_ctl_table), GFP_KERNEL);
-		if (!table)
-			goto err_alloc;
+	struct sysctl_context ctx = {
+		.ns.net_ns = net,
+	};
 
-	}
-	table[0].data	= &net->ipv4.fqdir->high_thresh;
-	table[0].extra1	= &net->ipv4.fqdir->low_thresh;
-	table[1].data	= &net->ipv4.fqdir->low_thresh;
-	table[1].extra2	= &net->ipv4.fqdir->high_thresh;
-	table[2].data	= &net->ipv4.fqdir->timeout;
-	table[3].data	= &net->ipv4.fqdir->max_dist;
-
-	hdr = register_net_sysctl_sz(net, "net/ipv4", table,
-				     ARRAY_SIZE(ip4_frags_ns_ctl_table));
-	if (!hdr)
-		goto err_reg;
-
-	net->ipv4.frags_hdr = hdr;
+	net->ipv4.frags_hdr = register_sysctl_fields(&net->sysctls, "net/ipv4",
+						     ip4_frags_ns_ctl_table, &ctx);
+	if (!net->ipv4.frags_hdr)
+		return -ENOMEM;
 	return 0;
-
-err_reg:
-	if (!net_eq(net, &init_net))
-		kfree(table);
-err_alloc:
-	return -ENOMEM;
 }
 
 static void __net_exit ip4_frags_ns_ctl_unregister(struct net *net)
 {
-	const struct ctl_table *table;
-
-	table = net->ipv4.frags_hdr->ctl_table_arg;
 	unregister_net_sysctl_table(net->ipv4.frags_hdr);
-	kfree(table);
 }
 
 static void __init ip4_frags_ctl_register(void)
net/ipv4/route.c +40 -62

132 lines not on this page · raw message

net/ipv4/xfrm4_policy.c +14 -38

70 lines not on this page · raw message

Signature · 1 line
2.55.0
#11 lore raw

RFC PATCH 11/30 sysctl: net: use sysctl_field in IPv4 sysctls

In reply to a message not in the archive

The per-net IPv4 sysctl table is cloned for every network namespace so that data pointers can be rebound from init_net to the namespace being registered. The table is otherwise static, and the clone also relies on mutating selected ctl_table entries before registration.

Use sysctl_field descriptors for the per-net IPv4 sysctls instead. The data pointers are derived from the registration context, so the table can stay const and the per-net ctl_table allocation, offset patching, and free path are no longer needed.

Keep the global IPv4 sysctl table on ctl_table, since it is registered only for init_net and is not part of the per-net clone path.

---
 net/ipv4/sysctl_net_ipv4.c | 1622 +++++++++++++-----------------------
 1 file changed, 559 insertions(+), 1063 deletions(-)
net/ipv4/sysctl_net_ipv4.c +559 -1063
index d8bdb1bdbff1..75860e7f272e 100644
@@ -20,22 +20,22 @@
 #include <net/protocol.h>
 #include <net/netevent.h>
 
-static int tcp_retr1_max = 255;
+static unsigned int tcp_retr1_max = 255;
 static int ip_local_port_range_min[] = { 1, 1 };
 static int ip_local_port_range_max[] = { 65535, 65535 };
 static int tcp_adv_win_scale_min = -31;
 static int tcp_adv_win_scale_max = 31;
-static int tcp_app_win_max = 31;
+static unsigned int tcp_app_win_max = 31;
 static int tcp_min_snd_mss_min = TCP_MIN_SND_MSS;
 static int tcp_min_snd_mss_max = 65535;
 static int tcp_rto_max_max = TCP_RTO_MAX_SEC * MSEC_PER_SEC;
 static int ip_privileged_port_min;
 static int ip_privileged_port_max = 65535;
-static int ip_ttl_min = 1;
-static int ip_ttl_max = 255;
-static int tcp_syn_retries_min = 1;
-static int tcp_syn_retries_max = MAX_TCP_SYNCNT;
-static int tcp_syn_linear_timeouts_max = MAX_TCP_SYNCNT;
+static unsigned int ip_ttl_min = 1;
+static unsigned int ip_ttl_max = 255;
+static unsigned int tcp_syn_retries_min = 1;
+static unsigned int tcp_syn_retries_max = MAX_TCP_SYNCNT;
+static unsigned int tcp_syn_linear_timeouts_max = MAX_TCP_SYNCNT;
 static unsigned long ip_ping_group_range_min[] = { 0, 0 };
 static unsigned long ip_ping_group_range_max[] = { GID_T_MAX, GID_T_MAX };
 static u32 u32_max_div_HZ = UINT_MAX / HZ;
@@ -44,11 +44,11 @@ static u32 fib_multipath_hash_fields_all_mask __maybe_unused =
 	FIB_MULTIPATH_HASH_FIELD_ALL_MASK;
 static unsigned int tcp_child_ehash_entries_max = 16 * 1024 * 1024;
 static unsigned int udp_child_hash_entries_max = UDP_HTABLE_SIZE_MAX;
-static int tcp_plb_max_rounds = 31;
+static unsigned int tcp_plb_max_rounds = 31;
 static int tcp_plb_max_cong_thresh = 256;
 static unsigned int tcp_tw_reuse_delay_max = TCP_PAWS_MSL * MSEC_PER_SEC;
-static int tcp_ecn_mode_max = 5;
-static u32 icmp_errors_extension_mask_all =
+static unsigned int tcp_ecn_mode_max = 5;
+static unsigned int icmp_errors_extension_mask_all =
 	GENMASK_U8(ICMP_ERR_EXT_COUNT - 1, 0);
 
 /* obsolete */
@@ -203,11 +203,16 @@ static int ipv4_fwd_update_priority(const struct ctl_table *table, int write,
 				    void *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct net *net;
+	struct ctl_table tmp;
 	int ret;
 
 	net = container_of(table->data, struct net,
 			   ipv4.sysctl_ip_fwd_update_priority);
-	ret = proc_dou8vec_minmax(table, write, buffer, lenp, ppos);
+	tmp = *table;
+	tmp.extra1 = SYSCTL_UINT_ZERO;
+	tmp.extra2 = SYSCTL_UINT_ONE;
+
+	ret = proc_dou8vec_minmax(&tmp, write, buffer, lenp, ppos);
 	if (write && ret == 0)
 		call_netevent_notifiers(NETEVENT_IPV4_FWD_UPDATE_PRIORITY_UPDATE,
 					net);
@@ -365,15 +370,28 @@ static int proc_tfo_blackhole_detect_timeout(const struct ctl_table *table,
 {
 	struct net *net = container_of(table->data, struct net,
 	    ipv4.sysctl_tcp_fastopen_blackhole_timeout);
+	struct ctl_table tmp = *table;
 	int ret;
 
-	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+	tmp.extra1 = SYSCTL_ZERO;
+
+	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
 	if (write && ret == 0)
 		atomic_set(&net->ipv4.tfo_active_disable_times, 0);
 
 	return ret;
 }
 
+static int proc_dointvec_minmax_one(const struct ctl_table *table, int write,
+				    void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct ctl_table tmp = *table;
+
+	tmp.extra1 = SYSCTL_ONE;
+
+	return proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
+}
+
 static int proc_tcp_available_ulp(const struct ctl_table *ctl,
 				  int write, void *buffer, size_t *lenp,
 				  loff_t *ppos)
@@ -445,9 +463,13 @@ static int proc_fib_multipath_hash_policy(const struct ctl_table *table, int wri
 {
 	struct net *net = container_of(table->data, struct net,
 	    ipv4.sysctl_fib_multipath_hash_policy);
+	struct ctl_table tmp = *table;
 	int ret;
 
-	ret = proc_dou8vec_minmax(table, write, buffer, lenp, ppos);
+	tmp.extra1 = SYSCTL_UINT_ZERO;
+	tmp.extra2 = SYSCTL_UINT_THREE;
+
+	ret = proc_dou8vec_minmax(&tmp, write, buffer, lenp, ppos);
 	if (write && ret == 0)
 		call_netevent_notifiers(NETEVENT_IPV4_MPATH_HASH_UPDATE, net);
 
@@ -459,11 +481,15 @@ static int proc_fib_multipath_hash_fields(const struct ctl_table *table, int wri
 					  loff_t *ppos)
 {
 	struct net *net;
+	struct ctl_table tmp = *table;
 	int ret;
 
 	net = container_of(table->data, struct net,
 			   ipv4.sysctl_fib_multipath_hash_fields);
-	ret = proc_douintvec_minmax(table, write, buffer, lenp, ppos);
+	tmp.extra1 = SYSCTL_ONE;
+	tmp.extra2 = &fib_multipath_hash_fields_all_mask;
+
+	ret = proc_douintvec_minmax(&tmp, write, buffer, lenp, ppos);
 	if (write && ret == 0)
 		call_netevent_notifiers(NETEVENT_IPV4_MPATH_HASH_UPDATE, net);
 
@@ -624,1065 +650,543 @@ static struct ctl_table ipv4_table[] = {
 	},
 };
 
-static struct ctl_table ipv4_net_table[] = {
-	{
-		.procname	= "tcp_max_tw_buckets",
-		.data		= &init_net.ipv4.tcp_death_row.sysctl_max_tw_buckets,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec
-	},
-	{
-		.procname	= "icmp_echo_ignore_all",
-		.data		= &init_net.ipv4.sysctl_icmp_echo_ignore_all,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE
-	},
-	{
-		.procname	= "icmp_echo_enable_probe",
-		.data		= &init_net.ipv4.sysctl_icmp_echo_enable_probe,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE
-	},
-	{
-		.procname	= "icmp_echo_ignore_broadcasts",
-		.data		= &init_net.ipv4.sysctl_icmp_echo_ignore_broadcasts,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE
-	},
-	{
-		.procname	= "icmp_ignore_bogus_error_responses",
-		.data		= &init_net.ipv4.sysctl_icmp_ignore_bogus_error_responses,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE
-	},
-	{
-		.procname	= "icmp_errors_use_inbound_ifaddr",
-		.data		= &init_net.ipv4.sysctl_icmp_errors_use_inbound_ifaddr,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE
-	},
-	{
-		.procname	= "icmp_errors_extension_mask",
-		.data		= &init_net.ipv4.sysctl_icmp_errors_extension_mask,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &icmp_errors_extension_mask_all,
-	},
-	{
-		.procname	= "icmp_ratelimit",
-		.data		= &init_net.ipv4.sysctl_icmp_ratelimit,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_ms_jiffies,
-	},
-	{
-		.procname	= "icmp_ratemask",
-		.data		= &init_net.ipv4.sysctl_icmp_ratemask,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec
-	},
-	{
-		.procname	= "icmp_msgs_per_sec",
-		.data		= &init_net.ipv4.sysctl_icmp_msgs_per_sec,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-	},
-	{
-		.procname	= "icmp_msgs_burst",
-		.data		= &init_net.ipv4.sysctl_icmp_msgs_burst,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-	},
-	{
-		.procname	= "ping_group_range",
-		.data		= &init_net.ipv4.ping_group_range.range,
-		.maxlen		= sizeof(gid_t)*2,
-		.mode		= 0644,
-		.proc_handler	= ipv4_ping_group_range,
-	},
+static umode_t ipv4_init_net_writable_mode(const struct sysctl_context *ctx)
+{
+	return net_eq(ctx->ns.net_ns, &init_net) ? 0644 : 0444;
+}
+
+static void *ipv4_netns_data(const struct sysctl_context *ctx)
+{
+	return ctx->ns.net_ns;
+}
+
+#define IPV4_DATA(type, name, field)					\
+static type *ipv4_ ## name ## _data(const struct sysctl_context *ctx)	\
+{									\
+	return &ctx->ns.net_ns->ipv4.field;				\
+}
+
+#define IPV4_SYSCTL_DATA(type, name)	IPV4_DATA(type, name, sysctl_ ## name)
+
+IPV4_DATA(int, tcp_max_tw_buckets, tcp_death_row.sysctl_max_tw_buckets)
+IPV4_DATA(void, ping_group_range, ping_group_range.range)
+IPV4_DATA(void, ip_local_reserved_ports, sysctl_local_reserved_ports)
+IPV4_DATA(u8, ip_forward_use_pmtu, sysctl_ip_fwd_use_pmtu)
+IPV4_DATA(void, ip_forward_update_priority, sysctl_ip_fwd_update_priority)
+IPV4_DATA(u8, igmp_link_local_mcast_reports, sysctl_igmp_llm_reports)
+IPV4_DATA(void, tcp_congestion_control, tcp_congestion_control)
+IPV4_DATA(int, tcp_max_syn_backlog, sysctl_max_syn_backlog)
+IPV4_DATA(void, tcp_fastopen_key, sysctl_tcp_fastopen)
+IPV4_DATA(void, tcp_fastopen_blackhole_timeout_sec, sysctl_tcp_fastopen_blackhole_timeout)
+IPV4_DATA(void, ip_unprivileged_port_start, sysctl_ip_prot_sock)
+IPV4_DATA(u8, tcp_no_metrics_save, sysctl_tcp_nometrics_save)
+IPV4_DATA(void, tcp_ehash_entries, sysctl_tcp_child_ehash_entries)
+IPV4_DATA(void, udp_hash_entries, sysctl_udp_child_hash_entries)
+
+IPV4_SYSCTL_DATA(u8, icmp_echo_ignore_all)
+IPV4_SYSCTL_DATA(u8, icmp_echo_enable_probe)
+IPV4_SYSCTL_DATA(u8, icmp_echo_ignore_broadcasts)
+IPV4_SYSCTL_DATA(u8, icmp_ignore_bogus_error_responses)
+IPV4_SYSCTL_DATA(u8, icmp_errors_use_inbound_ifaddr)
+IPV4_SYSCTL_DATA(u8, icmp_errors_extension_mask)
+IPV4_SYSCTL_DATA(void, icmp_ratelimit)
+IPV4_SYSCTL_DATA(int, icmp_ratemask)
+IPV4_SYSCTL_DATA(int, icmp_msgs_per_sec)
+IPV4_SYSCTL_DATA(int, icmp_msgs_burst)
 #ifdef CONFIG_NET_L3_MASTER_DEV
-	{
-		.procname	= "raw_l3mdev_accept",
-		.data		= &init_net.ipv4.sysctl_raw_l3mdev_accept,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
-	},
+IPV4_SYSCTL_DATA(u8, raw_l3mdev_accept)
 #endif
-	{
-		.procname	= "tcp_ecn",
-		.data		= &init_net.ipv4.sysctl_tcp_ecn,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &tcp_ecn_mode_max,
-	},
-	{
-		.procname	= "tcp_ecn_option",
-		.data		= &init_net.ipv4.sysctl_tcp_ecn_option,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_THREE,
-	},
-	{
-		.procname	= "tcp_ecn_option_beacon",
-		.data		= &init_net.ipv4.sysctl_tcp_ecn_option_beacon,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_THREE,
-	},
-	{
-		.procname	= "tcp_ecn_fallback",
-		.data		= &init_net.ipv4.sysctl_tcp_ecn_fallback,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
-	},
-	{
-		.procname	= "ip_dynaddr",
-		.data		= &init_net.ipv4.sysctl_ip_dynaddr,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "ip_early_demux",
-		.data		= &init_net.ipv4.sysctl_ip_early_demux,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname       = "udp_early_demux",
-		.data           = &init_net.ipv4.sysctl_udp_early_demux,
-		.maxlen         = sizeof(u8),
-		.mode           = 0644,
-		.proc_handler   = proc_dou8vec_minmax,
-	},
-	{
-		.procname       = "tcp_early_demux",
-		.data           = &init_net.ipv4.sysctl_tcp_early_demux,
-		.maxlen         = sizeof(u8),
-		.mode           = 0644,
-		.proc_handler   = proc_dou8vec_minmax,
-	},
-	{
-		.procname       = "nexthop_compat_mode",
-		.data           = &init_net.ipv4.sysctl_nexthop_compat_mode,
-		.maxlen         = sizeof(u8),
-		.mode           = 0644,
-		.proc_handler   = proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
-	},
-	{
-		.procname	= "ip_default_ttl",
-		.data		= &init_net.ipv4.sysctl_ip_default_ttl,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= &ip_ttl_min,
-		.extra2		= &ip_ttl_max,
-	},
-	{
-		.procname	= "ip_local_port_range",
-		.maxlen		= 0,
-		.data		= &init_net,
-		.mode		= 0644,
-		.proc_handler	= ipv4_local_port_range,
-	},
-	{
-		.procname	= "ip_local_port_step_width",
-		.maxlen		= sizeof(u32),
-		.data		= &init_net.ipv4.sysctl_ip_local_port_step_width,
-		.mode		= 0644,
-		.proc_handler	= proc_douintvec,
-	},
-	{
-		.procname	= "ip_local_reserved_ports",
-		.data		= &init_net.ipv4.sysctl_local_reserved_ports,
-		.maxlen		= 65536,
-		.mode		= 0644,
-		.proc_handler	= proc_do_large_bitmap,
-	},
-	{
-		.procname	= "ip_no_pmtu_disc",
-		.data		= &init_net.ipv4.sysctl_ip_no_pmtu_disc,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "ip_forward_use_pmtu",
-		.data		= &init_net.ipv4.sysctl_ip_fwd_use_pmtu,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "ip_forward_update_priority",
-		.data		= &init_net.ipv4.sysctl_ip_fwd_update_priority,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler   = ipv4_fwd_update_priority,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
-	},
-	{
-		.procname	= "ip_nonlocal_bind",
-		.data		= &init_net.ipv4.sysctl_ip_nonlocal_bind,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "ip_autobind_reuse",
-		.data		= &init_net.ipv4.sysctl_ip_autobind_reuse,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE,
-	},
-	{
-		.procname	= "fwmark_reflect",
-		.data		= &init_net.ipv4.sysctl_fwmark_reflect,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "tcp_fwmark_accept",
-		.data		= &init_net.ipv4.sysctl_tcp_fwmark_accept,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
+IPV4_SYSCTL_DATA(u8, tcp_ecn)
+IPV4_SYSCTL_DATA(u8, tcp_ecn_option)
+IPV4_SYSCTL_DATA(u8, tcp_ecn_option_beacon)
+IPV4_SYSCTL_DATA(u8, tcp_ecn_fallback)
+IPV4_SYSCTL_DATA(u8, ip_dynaddr)
+IPV4_SYSCTL_DATA(u8, ip_early_demux)
+IPV4_SYSCTL_DATA(u8, udp_early_demux)
+IPV4_SYSCTL_DATA(u8, tcp_early_demux)
+IPV4_SYSCTL_DATA(u8, nexthop_compat_mode)
+IPV4_SYSCTL_DATA(u8, ip_default_ttl)
+IPV4_SYSCTL_DATA(unsigned int, ip_local_port_step_width)
+IPV4_SYSCTL_DATA(u8, ip_no_pmtu_disc)
+IPV4_SYSCTL_DATA(u8, ip_nonlocal_bind)
+IPV4_SYSCTL_DATA(u8, ip_autobind_reuse)
+IPV4_SYSCTL_DATA(u8, fwmark_reflect)
+IPV4_SYSCTL_DATA(u8, tcp_fwmark_accept)
+IPV4_SYSCTL_DATA(u8, tcp_mtu_probing)
 #ifdef CONFIG_NET_L3_MASTER_DEV
-	{
-		.procname	= "tcp_l3mdev_accept",
-		.data		= &init_net.ipv4.sysctl_tcp_l3mdev_accept,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
-	},
+IPV4_SYSCTL_DATA(u8, tcp_l3mdev_accept)
 #endif
-	{
-		.procname	= "tcp_mtu_probing",
-		.data		= &init_net.ipv4.sysctl_tcp_mtu_probing,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "tcp_base_mss",
-		.data		= &init_net.ipv4.sysctl_tcp_base_mss,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "tcp_min_snd_mss",
-		.data		= &init_net.ipv4.sysctl_tcp_min_snd_mss,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &tcp_min_snd_mss_min,
-		.extra2		= &tcp_min_snd_mss_max,
-	},
-	{
-		.procname	= "tcp_mtu_probe_floor",
-		.data		= &init_net.ipv4.sysctl_tcp_mtu_probe_floor,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &tcp_min_snd_mss_min,
-		.extra2		= &tcp_min_snd_mss_max,
-	},
-	{
-		.procname	= "tcp_probe_threshold",
-		.data		= &init_net.ipv4.sysctl_tcp_probe_threshold,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "tcp_probe_interval",
-		.data		= &init_net.ipv4.sysctl_tcp_probe_interval,
-		.maxlen		= sizeof(u32),
-		.mode		= 0644,
-		.proc_handler	= proc_douintvec_minmax,
-		.extra2		= &u32_max_div_HZ,
-	},
-	{
-		.procname	= "igmp_link_local_mcast_reports",
-		.data		= &init_net.ipv4.sysctl_igmp_llm_reports,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "igmp_max_memberships",
-		.data		= &init_net.ipv4.sysctl_igmp_max_memberships,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec
-	},
-	{
-		.procname	= "igmp_max_msf",
-		.data		= &init_net.ipv4.sysctl_igmp_max_msf,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec
-	},
+IPV4_SYSCTL_DATA(int, tcp_base_mss)
+IPV4_SYSCTL_DATA(int, tcp_min_snd_mss)
+IPV4_SYSCTL_DATA(int, tcp_mtu_probe_floor)
+IPV4_SYSCTL_DATA(int, tcp_probe_threshold)
+IPV4_SYSCTL_DATA(unsigned int, tcp_probe_interval)
+IPV4_SYSCTL_DATA(int, igmp_max_memberships)
+IPV4_SYSCTL_DATA(int, igmp_max_msf)
 #ifdef CONFIG_IP_MULTICAST
-	{
-		.procname	= "igmp_qrv",
-		.data		= &init_net.ipv4.sysctl_igmp_qrv,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE
-	},
+IPV4_SYSCTL_DATA(int, igmp_qrv)
 #endif
-	{
-		.procname	= "tcp_congestion_control",
-		.data		= &init_net.ipv4.tcp_congestion_control,
-		.mode		= 0644,
-		.maxlen		= TCP_CA_NAME_MAX,
-		.proc_handler	= proc_tcp_congestion_control,
-	},
-	{
-		.procname	= "tcp_available_congestion_control",
-		.maxlen		= TCP_CA_BUF_MAX,
-		.mode		= 0444,
-		.proc_handler   = proc_tcp_available_congestion_control,
-	},
-	{
-		.procname	= "tcp_allowed_congestion_control",
-		.maxlen		= TCP_CA_BUF_MAX,
-		.mode		= 0644,
-		.proc_handler   = proc_allowed_congestion_control,
-	},
-	{
-		.procname	= "tcp_keepalive_time",
-		.data		= &init_net.ipv4.sysctl_tcp_keepalive_time,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	{
-		.procname	= "tcp_keepalive_probes",
-		.data		= &init_net.ipv4.sysctl_tcp_keepalive_probes,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "tcp_keepalive_intvl",
-		.data		= &init_net.ipv4.sysctl_tcp_keepalive_intvl,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	{
-		.procname	= "tcp_syn_retries",
-		.data		= &init_net.ipv4.sysctl_tcp_syn_retries,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= &tcp_syn_retries_min,
-		.extra2		= &tcp_syn_retries_max
-	},
-	{
-		.procname	= "tcp_synack_retries",
-		.data		= &init_net.ipv4.sysctl_tcp_synack_retries,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
+IPV4_SYSCTL_DATA(void, tcp_keepalive_time)
+IPV4_SYSCTL_DATA(u8, tcp_keepalive_probes)
+IPV4_SYSCTL_DATA(void, tcp_keepalive_intvl)
+IPV4_SYSCTL_DATA(u8, tcp_syn_retries)
+IPV4_SYSCTL_DATA(u8, tcp_synack_retries)
 #ifdef CONFIG_SYN_COOKIES
-	{
-		.procname	= "tcp_syncookies",
-		.data		= &init_net.ipv4.sysctl_tcp_syncookies,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
+IPV4_SYSCTL_DATA(u8, tcp_syncookies)
 #endif
-	{
-		.procname	= "tcp_migrate_req",
-		.data		= &init_net.ipv4.sysctl_tcp_migrate_req,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE
-	},
-	{
-		.procname	= "tcp_reordering",
-		.data		= &init_net.ipv4.sysctl_tcp_reordering,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec
-	},
-	{
-		.procname	= "tcp_retries1",
-		.data		= &init_net.ipv4.sysctl_tcp_retries1,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra2		= &tcp_retr1_max
-	},
-	{
-		.procname	= "tcp_retries2",
-		.data		= &init_net.ipv4.sysctl_tcp_retries2,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "tcp_orphan_retries",
-		.data		= &init_net.ipv4.sysctl_tcp_orphan_retries,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "tcp_fin_timeout",
-		.data		= &init_net.ipv4.sysctl_tcp_fin_timeout,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	{
-		.procname	= "tcp_notsent_lowat",
-		.data		= &init_net.ipv4.sysctl_tcp_notsent_lowat,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_douintvec,
-	},
-	{
-		.procname	= "tcp_tw_reuse",
-		.data		= &init_net.ipv4.sysctl_tcp_tw_reuse,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO,
-	},
-	{
-		.procname	= "tcp_tw_reuse_delay",
-		.data		= &init_net.ipv4.sysctl_tcp_tw_reuse_delay,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_douintvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= &tcp_tw_reuse_delay_max,
-	},
-	{
-		.procname	= "tcp_max_syn_backlog",
-		.data		= &init_net.ipv4.sysctl_max_syn_backlog,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec
-	},
-	{
-		.procname	= "tcp_fastopen",
-		.data		= &init_net.ipv4.sysctl_tcp_fastopen,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "tcp_fastopen_key",
-		.mode		= 0600,
-		.data		= &init_net.ipv4.sysctl_tcp_fastopen,
-		/* maxlen to print the list of keys in hex (*2), with dashes
-		 * separating doublewords and a comma in between keys.
-		 */
-		.maxlen		= ((TCP_FASTOPEN_KEY_LENGTH *
-				   2 * TCP_FASTOPEN_KEY_MAX) +
-				   (TCP_FASTOPEN_KEY_MAX * 5)),
-		.proc_handler	= proc_tcp_fastopen_key,
-	},
-	{
-		.procname	= "tcp_fastopen_blackhole_timeout_sec",
-		.data		= &init_net.ipv4.sysctl_tcp_fastopen_blackhole_timeout,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_tfo_blackhole_detect_timeout,
-		.extra1		= SYSCTL_ZERO,
-	},
+IPV4_SYSCTL_DATA(u8, tcp_migrate_req)
+IPV4_SYSCTL_DATA(int, tcp_reordering)
+IPV4_SYSCTL_DATA(u8, tcp_retries1)
+IPV4_SYSCTL_DATA(u8, tcp_retries2)
+IPV4_SYSCTL_DATA(u8, tcp_orphan_retries)
+IPV4_SYSCTL_DATA(void, tcp_fin_timeout)
+IPV4_SYSCTL_DATA(unsigned int, tcp_notsent_lowat)
+IPV4_SYSCTL_DATA(u8, tcp_tw_reuse)
+IPV4_SYSCTL_DATA(unsigned int, tcp_tw_reuse_delay)
+IPV4_SYSCTL_DATA(int, tcp_fastopen)
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
-	{
-		.procname	= "fib_multipath_use_neigh",
-		.data		= &init_net.ipv4.sysctl_fib_multipath_use_neigh,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
-	},
-	{
-		.procname	= "fib_multipath_hash_policy",
-		.data		= &init_net.ipv4.sysctl_fib_multipath_hash_policy,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_fib_multipath_hash_policy,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_THREE,
-	},
-	{
-		.procname	= "fib_multipath_hash_fields",
-		.data		= &init_net.ipv4.sysctl_fib_multipath_hash_fields,
-		.maxlen		= sizeof(u32),
-		.mode		= 0644,
-		.proc_handler	= proc_fib_multipath_hash_fields,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= &fib_multipath_hash_fields_all_mask,
-	},
-	{
-		.procname	= "fib_multipath_hash_seed",
-		.data		= &init_net,
-		.maxlen		= sizeof(u32),
-		.mode		= 0644,
-		.proc_handler	= proc_fib_multipath_hash_seed,
-	},
+IPV4_SYSCTL_DATA(u8, fib_multipath_use_neigh)
+IPV4_SYSCTL_DATA(void, fib_multipath_hash_policy)
+IPV4_SYSCTL_DATA(void, fib_multipath_hash_fields)
 #endif
-	{
-		.procname	= "ip_unprivileged_port_start",
-		.maxlen		= sizeof(int),
-		.data		= &init_net.ipv4.sysctl_ip_prot_sock,
-		.mode		= 0644,
-		.proc_handler	= ipv4_privileged_ports,
-	},
 #ifdef CONFIG_NET_L3_MASTER_DEV
-	{
-		.procname	= "udp_l3mdev_accept",
-		.data		= &init_net.ipv4.sysctl_udp_l3mdev_accept,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
-	},
+IPV4_SYSCTL_DATA(u8, udp_l3mdev_accept)
 #endif
-	{
-		.procname	= "tcp_sack",
-		.data		= &init_net.ipv4.sysctl_tcp_sack,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "tcp_window_scaling",
-		.data		= &init_net.ipv4.sysctl_tcp_window_scaling,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "tcp_timestamps",
-		.data		= &init_net.ipv4.sysctl_tcp_timestamps,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "tcp_early_retrans",
-		.data		= &init_net.ipv4.sysctl_tcp_early_retrans,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_FOUR,
-	},
-	{
-		.procname	= "tcp_recovery",
-		.data		= &init_net.ipv4.sysctl_tcp_recovery,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname       = "tcp_thin_linear_timeouts",
-		.data           = &init_net.ipv4.sysctl_tcp_thin_linear_timeouts,
-		.maxlen         = sizeof(u8),
-		.mode           = 0644,
-		.proc_handler   = proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "tcp_slow_start_after_idle",
-		.data		= &init_net.ipv4.sysctl_tcp_slow_start_after_idle,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "tcp_retrans_collapse",
-		.data		= &init_net.ipv4.sysctl_tcp_retrans_collapse,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "tcp_stdurg",
-		.data		= &init_net.ipv4.sysctl_tcp_stdurg,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "tcp_rfc1337",
-		.data		= &init_net.ipv4.sysctl_tcp_rfc1337,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "tcp_abort_on_overflow",
-		.data		= &init_net.ipv4.sysctl_tcp_abort_on_overflow,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "tcp_fack",
-		.data		= &init_net.ipv4.sysctl_tcp_fack,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "tcp_max_reordering",
-		.data		= &init_net.ipv4.sysctl_tcp_max_reordering,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec
-	},
-	{
-		.procname	= "tcp_dsack",
-		.data		= &init_net.ipv4.sysctl_tcp_dsack,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "tcp_app_win",
-		.data		= &init_net.ipv4.sysctl_tcp_app_win,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &tcp_app_win_max,
-	},
-	{
-		.procname	= "tcp_adv_win_scale",
-		.data		= &init_net.ipv4.sysctl_tcp_adv_win_scale,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &tcp_adv_win_scale_min,
-		.extra2		= &tcp_adv_win_scale_max,
-	},
-	{
-		.procname	= "tcp_frto",
-		.data		= &init_net.ipv4.sysctl_tcp_frto,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "tcp_no_metrics_save",
-		.data		= &init_net.ipv4.sysctl_tcp_nometrics_save,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "tcp_no_ssthresh_metrics_save",
-		.data		= &init_net.ipv4.sysctl_tcp_no_ssthresh_metrics_save,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
-	},
-	{
-		.procname	= "tcp_moderate_rcvbuf",
-		.data		= &init_net.ipv4.sysctl_tcp_moderate_rcvbuf,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "tcp_rcvbuf_low_rtt",
-		.data		= &init_net.ipv4.sysctl_tcp_rcvbuf_low_rtt,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
-	},
-	{
-		.procname	= "tcp_tso_win_divisor",
-		.data		= &init_net.ipv4.sysctl_tcp_tso_win_divisor,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "tcp_workaround_signed_windows",
-		.data		= &init_net.ipv4.sysctl_tcp_workaround_signed_windows,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "tcp_limit_output_bytes",
-		.data		= &init_net.ipv4.sysctl_tcp_limit_output_bytes,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec
-	},
-	{
-		.procname	= "tcp_challenge_ack_limit",
-		.data		= &init_net.ipv4.sysctl_tcp_challenge_ack_limit,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec
-	},
-	{
-		.procname	= "tcp_min_tso_segs",
-		.data		= &init_net.ipv4.sysctl_tcp_min_tso_segs,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ONE,
-	},
-	{
-		.procname	= "tcp_tso_rtt_log",
-		.data		= &init_net.ipv4.sysctl_tcp_tso_rtt_log,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "tcp_min_rtt_wlen",
-		.data		= &init_net.ipv4.sysctl_tcp_min_rtt_wlen,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &one_day_secs
-	},
-	{
-		.procname	= "tcp_autocorking",
-		.data		= &init_net.ipv4.sysctl_tcp_autocorking,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
-	},
-	{
-		.procname	= "tcp_invalid_ratelimit",
-		.data		= &init_net.ipv4.sysctl_tcp_invalid_ratelimit,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_ms_jiffies,
-	},
-	{
-		.procname	= "tcp_pacing_ss_ratio",
-		.data		= &init_net.ipv4.sysctl_tcp_pacing_ss_ratio,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE_THOUSAND,
-	},
-	{
-		.procname	= "tcp_pacing_ca_ratio",
-		.data		= &init_net.ipv4.sysctl_tcp_pacing_ca_ratio,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE_THOUSAND,
-	},
-	{
-		.procname	= "tcp_wmem",
-		.data		= &init_net.ipv4.sysctl_tcp_wmem,
-		.maxlen		= sizeof(init_net.ipv4.sysctl_tcp_wmem),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-	},
-	{
-		.procname	= "tcp_rmem",
-		.data		= &init_net.ipv4.sysctl_tcp_rmem,
-		.maxlen		= sizeof(init_net.ipv4.sysctl_tcp_rmem),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-	},
-	{
-		.procname	= "tcp_comp_sack_delay_ns",
-		.data		= &init_net.ipv4.sysctl_tcp_comp_sack_delay_ns,
-		.maxlen		= sizeof(unsigned long),
-		.mode		= 0644,
-		.proc_handler	= proc_doulongvec_minmax,
-	},
-	{
-		.procname	= "tcp_comp_sack_rtt_percent",
-		.data		= &init_net.ipv4.sysctl_tcp_comp_sack_rtt_percent,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= SYSCTL_ONE_THOUSAND,
-	},
-	{
-		.procname	= "tcp_comp_sack_slack_ns",
-		.data		= &init_net.ipv4.sysctl_tcp_comp_sack_slack_ns,
-		.maxlen		= sizeof(unsigned long),
-		.mode		= 0644,
-		.proc_handler	= proc_doulongvec_minmax,
-	},
-	{
-		.procname	= "tcp_comp_sack_nr",
-		.data		= &init_net.ipv4.sysctl_tcp_comp_sack_nr,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-	},
-	{
-		.procname	= "tcp_backlog_ack_defer",
-		.data		= &init_net.ipv4.sysctl_tcp_backlog_ack_defer,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
-	},
-	{
-		.procname       = "tcp_reflect_tos",
-		.data           = &init_net.ipv4.sysctl_tcp_reflect_tos,
-		.maxlen         = sizeof(u8),
-		.mode           = 0644,
-		.proc_handler   = proc_dou8vec_minmax,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE,
-	},
-	{
-		.procname	= "tcp_ehash_entries",
-		.data		= &init_net.ipv4.sysctl_tcp_child_ehash_entries,
-		.mode		= 0444,
-		.proc_handler	= proc_tcp_ehash_entries,
-	},
-	{
-		.procname	= "tcp_child_ehash_entries",
-		.data		= &init_net.ipv4.sysctl_tcp_child_ehash_entries,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_douintvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &tcp_child_ehash_entries_max,
-	},
-	{
-		.procname	= "udp_hash_entries",
-		.data		= &init_net.ipv4.sysctl_udp_child_hash_entries,
-		.mode		= 0444,
-		.proc_handler	= proc_udp_hash_entries,
-	},
-	{
-		.procname	= "udp_child_hash_entries",
-		.data		= &init_net.ipv4.sysctl_udp_child_hash_entries,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_douintvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &udp_child_hash_entries_max,
-	},
-	{
-		.procname	= "udp_rmem_min",
-		.data		= &init_net.ipv4.sysctl_udp_rmem_min,
-		.maxlen		= sizeof(init_net.ipv4.sysctl_udp_rmem_min),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE
-	},
-	{
-		.procname	= "udp_wmem_min",
-		.data		= &init_net.ipv4.sysctl_udp_wmem_min,
-		.maxlen		= sizeof(init_net.ipv4.sysctl_udp_wmem_min),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE
-	},
-	{
-		.procname	= "fib_notify_on_flag_change",
-		.data		= &init_net.ipv4.sysctl_fib_notify_on_flag_change,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO,
-	},
-	{
-		.procname       = "tcp_plb_enabled",
-		.data           = &init_net.ipv4.sysctl_tcp_plb_enabled,
-		.maxlen         = sizeof(u8),
-		.mode           = 0644,
-		.proc_handler   = proc_dou8vec_minmax,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE,
-	},
-	{
-		.procname       = "tcp_plb_idle_rehash_rounds",
-		.data           = &init_net.ipv4.sysctl_tcp_plb_idle_rehash_rounds,
-		.maxlen         = sizeof(u8),
-		.mode           = 0644,
-		.proc_handler   = proc_dou8vec_minmax,
-		.extra2		= &tcp_plb_max_rounds,
-	},
-	{
-		.procname       = "tcp_plb_rehash_rounds",
-		.data           = &init_net.ipv4.sysctl_tcp_plb_rehash_rounds,
-		.maxlen         = sizeof(u8),
-		.mode           = 0644,
-		.proc_handler   = proc_dou8vec_minmax,
-		.extra2         = &tcp_plb_max_rounds,
-	},
-	{
-		.procname       = "tcp_plb_suspend_rto_sec",
-		.data           = &init_net.ipv4.sysctl_tcp_plb_suspend_rto_sec,
-		.maxlen         = sizeof(u8),
-		.mode           = 0644,
-		.proc_handler   = proc_dou8vec_minmax,
-	},
-	{
-		.procname       = "tcp_plb_cong_thresh",
-		.data           = &init_net.ipv4.sysctl_tcp_plb_cong_thresh,
-		.maxlen         = sizeof(int),
-		.mode           = 0644,
-		.proc_handler   = proc_dointvec_minmax,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = &tcp_plb_max_cong_thresh,
-	},
-	{
-		.procname	= "tcp_syn_linear_timeouts",
-		.data		= &init_net.ipv4.sysctl_tcp_syn_linear_timeouts,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &tcp_syn_linear_timeouts_max,
-	},
-	{
-		.procname	= "tcp_shrink_window",
-		.data		= &init_net.ipv4.sysctl_tcp_shrink_window,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
-	},
-	{
-		.procname	= "tcp_pingpong_thresh",
-		.data		= &init_net.ipv4.sysctl_tcp_pingpong_thresh,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ONE,
-	},
-	{
-		.procname	= "tcp_rto_min_us",
-		.data		= &init_net.ipv4.sysctl_tcp_rto_min_us,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-	},
-	{
-		.procname	= "tcp_rto_max_ms",
-		.data		= &init_net.ipv4.sysctl_tcp_rto_max_ms,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE_THOUSAND,
-		.extra2		= &tcp_rto_max_max,
-	},
+IPV4_SYSCTL_DATA(u8, tcp_sack)
+IPV4_SYSCTL_DATA(u8, tcp_window_scaling)
+IPV4_SYSCTL_DATA(u8, tcp_timestamps)
+IPV4_SYSCTL_DATA(u8, tcp_early_retrans)
+IPV4_SYSCTL_DATA(u8, tcp_recovery)
+IPV4_SYSCTL_DATA(u8, tcp_thin_linear_timeouts)
+IPV4_SYSCTL_DATA(u8, tcp_slow_start_after_idle)
+IPV4_SYSCTL_DATA(u8, tcp_retrans_collapse)
+IPV4_SYSCTL_DATA(u8, tcp_stdurg)
+IPV4_SYSCTL_DATA(u8, tcp_rfc1337)
+IPV4_SYSCTL_DATA(u8, tcp_abort_on_overflow)
+IPV4_SYSCTL_DATA(u8, tcp_fack)
+IPV4_SYSCTL_DATA(int, tcp_max_reordering)
+IPV4_SYSCTL_DATA(u8, tcp_dsack)
+IPV4_SYSCTL_DATA(u8, tcp_app_win)
+IPV4_SYSCTL_DATA(int, tcp_adv_win_scale)
+IPV4_SYSCTL_DATA(u8, tcp_frto)
+IPV4_SYSCTL_DATA(u8, tcp_no_ssthresh_metrics_save)
+IPV4_SYSCTL_DATA(u8, tcp_moderate_rcvbuf)
+IPV4_SYSCTL_DATA(int, tcp_rcvbuf_low_rtt)
+IPV4_SYSCTL_DATA(u8, tcp_tso_win_divisor)
+IPV4_SYSCTL_DATA(u8, tcp_workaround_signed_windows)
+IPV4_SYSCTL_DATA(int, tcp_limit_output_bytes)
+IPV4_SYSCTL_DATA(int, tcp_challenge_ack_limit)
+IPV4_SYSCTL_DATA(u8, tcp_min_tso_segs)
+IPV4_SYSCTL_DATA(u8, tcp_tso_rtt_log)
+IPV4_SYSCTL_DATA(int, tcp_min_rtt_wlen)
+IPV4_SYSCTL_DATA(u8, tcp_autocorking)
+IPV4_SYSCTL_DATA(void, tcp_invalid_ratelimit)
+IPV4_SYSCTL_DATA(int, tcp_pacing_ss_ratio)
+IPV4_SYSCTL_DATA(int, tcp_pacing_ca_ratio)
+IPV4_SYSCTL_DATA(void, tcp_wmem)
+IPV4_SYSCTL_DATA(void, tcp_rmem)
+IPV4_SYSCTL_DATA(unsigned long, tcp_comp_sack_delay_ns)
+IPV4_SYSCTL_DATA(int, tcp_comp_sack_rtt_percent)
+IPV4_SYSCTL_DATA(unsigned long, tcp_comp_sack_slack_ns)
+IPV4_SYSCTL_DATA(u8, tcp_comp_sack_nr)
+IPV4_SYSCTL_DATA(u8, tcp_backlog_ack_defer)
+IPV4_SYSCTL_DATA(u8, tcp_reflect_tos)
+IPV4_SYSCTL_DATA(unsigned int, tcp_child_ehash_entries)
+IPV4_SYSCTL_DATA(unsigned int, udp_child_hash_entries)
+IPV4_SYSCTL_DATA(int, udp_rmem_min)
+IPV4_SYSCTL_DATA(int, udp_wmem_min)
+IPV4_SYSCTL_DATA(u8, fib_notify_on_flag_change)
+IPV4_SYSCTL_DATA(u8, tcp_plb_enabled)
+IPV4_SYSCTL_DATA(u8, tcp_plb_idle_rehash_rounds)
+IPV4_SYSCTL_DATA(u8, tcp_plb_rehash_rounds)
+IPV4_SYSCTL_DATA(u8, tcp_plb_suspend_rto_sec)
+IPV4_SYSCTL_DATA(int, tcp_plb_cong_thresh)
+IPV4_SYSCTL_DATA(u8, tcp_syn_linear_timeouts)
+IPV4_SYSCTL_DATA(u8, tcp_shrink_window)
+IPV4_SYSCTL_DATA(u8, tcp_pingpong_thresh)
+IPV4_SYSCTL_DATA(int, tcp_rto_min_us)
+IPV4_SYSCTL_DATA(int, tcp_rto_max_ms)
+
+static const struct sysctl_field ipv4_net_table[] = {
+	SYSCTL_FIELD_INT("tcp_max_tw_buckets", 0644,
+			ipv4_tcp_max_tw_buckets_data),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("icmp_echo_ignore_all", 0644,
+			ipv4_icmp_echo_ignore_all_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("icmp_echo_enable_probe", 0644,
+			ipv4_icmp_echo_enable_probe_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("icmp_echo_ignore_broadcasts", 0644,
+			ipv4_icmp_echo_ignore_broadcasts_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("icmp_ignore_bogus_error_responses", 0644,
+			ipv4_icmp_ignore_bogus_error_responses_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("icmp_errors_use_inbound_ifaddr", 0644,
+			ipv4_icmp_errors_use_inbound_ifaddr_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("icmp_errors_extension_mask", 0644,
+			ipv4_icmp_errors_extension_mask_data,
+			SYSCTL_UINT_ZERO, &icmp_errors_extension_mask_all),
+	SYSCTL_FIELD_CUSTOM("icmp_ratelimit", 0644,
+			sizeof(int),
+			ipv4_icmp_ratelimit_data,
+			proc_dointvec_ms_jiffies),
+	SYSCTL_FIELD_INT("icmp_ratemask", 0644,
+			ipv4_icmp_ratemask_data),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("icmp_msgs_per_sec", 0644,
+			ipv4_icmp_msgs_per_sec_data,
+			SYSCTL_ZERO, NULL),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("icmp_msgs_burst", 0644,
+			ipv4_icmp_msgs_burst_data,
+			SYSCTL_ZERO, NULL),
+	SYSCTL_FIELD_CUSTOM("ping_group_range", 0644,
+			sizeof(gid_t) * 2,
+			ipv4_ping_group_range_data,
+			ipv4_ping_group_range),
+#ifdef CONFIG_NET_L3_MASTER_DEV
+	SYSCTL_FIELD_STATIC_U8_MINMAX("raw_l3mdev_accept", 0644,
+			ipv4_raw_l3mdev_accept_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+#endif
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tcp_ecn", 0644,
+			ipv4_tcp_ecn_data,
+			SYSCTL_UINT_ZERO, &tcp_ecn_mode_max),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tcp_ecn_option", 0644,
+			ipv4_tcp_ecn_option_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_THREE),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tcp_ecn_option_beacon", 0644,
+			ipv4_tcp_ecn_option_beacon_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_THREE),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tcp_ecn_fallback", 0644,
+			ipv4_tcp_ecn_fallback_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_U8("ip_dynaddr", 0644,
+			ipv4_ip_dynaddr_data),
+	SYSCTL_FIELD_U8("ip_early_demux", 0644,
+			ipv4_ip_early_demux_data),
+	SYSCTL_FIELD_U8("udp_early_demux", 0644,
+			ipv4_udp_early_demux_data),
+	SYSCTL_FIELD_U8("tcp_early_demux", 0644,
+			ipv4_tcp_early_demux_data),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("nexthop_compat_mode", 0644,
+			ipv4_nexthop_compat_mode_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("ip_default_ttl", 0644,
+			ipv4_ip_default_ttl_data,
+			&ip_ttl_min, &ip_ttl_max),
+	SYSCTL_FIELD_CUSTOM("ip_local_port_range", 0644,
+			0,
+			ipv4_netns_data,
+			ipv4_local_port_range),
+	SYSCTL_FIELD_UINT("ip_local_port_step_width", 0644,
+			ipv4_ip_local_port_step_width_data),
+	SYSCTL_FIELD_CUSTOM("ip_local_reserved_ports", 0644,
+			65536,
+			ipv4_ip_local_reserved_ports_data,
+			proc_do_large_bitmap),
+	SYSCTL_FIELD_U8("ip_no_pmtu_disc", 0644,
+			ipv4_ip_no_pmtu_disc_data),
+	SYSCTL_FIELD_U8("ip_forward_use_pmtu", 0644,
+			ipv4_ip_forward_use_pmtu_data),
+	SYSCTL_FIELD_CUSTOM("ip_forward_update_priority", 0644,
+			sizeof(u8),
+			ipv4_ip_forward_update_priority_data,
+			ipv4_fwd_update_priority),
+	SYSCTL_FIELD_U8("ip_nonlocal_bind", 0644,
+			ipv4_ip_nonlocal_bind_data),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("ip_autobind_reuse", 0644,
+			ipv4_ip_autobind_reuse_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_U8("fwmark_reflect", 0644,
+			ipv4_fwmark_reflect_data),
+	SYSCTL_FIELD_U8("tcp_fwmark_accept", 0644,
+			ipv4_tcp_fwmark_accept_data),
+#ifdef CONFIG_NET_L3_MASTER_DEV
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tcp_l3mdev_accept", 0644,
+			ipv4_tcp_l3mdev_accept_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+#endif
+	SYSCTL_FIELD_U8("tcp_mtu_probing", 0644,
+			ipv4_tcp_mtu_probing_data),
+	SYSCTL_FIELD_INT("tcp_base_mss", 0644,
+			ipv4_tcp_base_mss_data),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("tcp_min_snd_mss", 0644,
+			ipv4_tcp_min_snd_mss_data,
+			&tcp_min_snd_mss_min, &tcp_min_snd_mss_max),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("tcp_mtu_probe_floor", 0644,
+			ipv4_tcp_mtu_probe_floor_data,
+			&tcp_min_snd_mss_min, &tcp_min_snd_mss_max),
+	SYSCTL_FIELD_INT("tcp_probe_threshold", 0644,
+			ipv4_tcp_probe_threshold_data),
+	SYSCTL_FIELD_STATIC_UINT_MINMAX("tcp_probe_interval", 0644,
+			ipv4_tcp_probe_interval_data,
+			NULL, &u32_max_div_HZ),
+	SYSCTL_FIELD_U8("igmp_link_local_mcast_reports", 0644,
+			ipv4_igmp_link_local_mcast_reports_data),
+	SYSCTL_FIELD_INT("igmp_max_memberships", 0644,
+			ipv4_igmp_max_memberships_data),
+	SYSCTL_FIELD_INT("igmp_max_msf", 0644,
+			ipv4_igmp_max_msf_data),
+#ifdef CONFIG_IP_MULTICAST
+	SYSCTL_FIELD_STATIC_INT_MINMAX("igmp_qrv", 0644,
+			ipv4_igmp_qrv_data,
+			SYSCTL_ONE, NULL),
+#endif
+	SYSCTL_FIELD_CUSTOM("tcp_congestion_control", 0644,
+			TCP_CA_NAME_MAX,
+			ipv4_tcp_congestion_control_data,
+			proc_tcp_congestion_control),
+	SYSCTL_FIELD_CUSTOM("tcp_available_congestion_control", 0444,
+			TCP_CA_BUF_MAX,
+			NULL,
+			proc_tcp_available_congestion_control),
+	SYSCTL_FIELD_CUSTOM_MODE("tcp_allowed_congestion_control", 0644,
+			ipv4_init_net_writable_mode, TCP_CA_BUF_MAX, NULL,
+			proc_allowed_congestion_control),
+	SYSCTL_FIELD_CUSTOM("tcp_keepalive_time", 0644,
+			sizeof(int),
+			ipv4_tcp_keepalive_time_data,
+			proc_dointvec_jiffies),
+	SYSCTL_FIELD_U8("tcp_keepalive_probes", 0644,
+			ipv4_tcp_keepalive_probes_data),
+	SYSCTL_FIELD_CUSTOM("tcp_keepalive_intvl", 0644,
+			sizeof(int),
+			ipv4_tcp_keepalive_intvl_data,
+			proc_dointvec_jiffies),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tcp_syn_retries", 0644,
+			ipv4_tcp_syn_retries_data,
+			&tcp_syn_retries_min, &tcp_syn_retries_max),
+	SYSCTL_FIELD_U8("tcp_synack_retries", 0644,
+			ipv4_tcp_synack_retries_data),
+#ifdef CONFIG_SYN_COOKIES
+	SYSCTL_FIELD_U8("tcp_syncookies", 0644,
+			ipv4_tcp_syncookies_data),
+#endif
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tcp_migrate_req", 0644,
+			ipv4_tcp_migrate_req_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_INT("tcp_reordering", 0644,
+			ipv4_tcp_reordering_data),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tcp_retries1", 0644,
+			ipv4_tcp_retries1_data,
+			NULL, &tcp_retr1_max),
+	SYSCTL_FIELD_U8("tcp_retries2", 0644,
+			ipv4_tcp_retries2_data),
+	SYSCTL_FIELD_U8("tcp_orphan_retries", 0644,
+			ipv4_tcp_orphan_retries_data),
+	SYSCTL_FIELD_CUSTOM("tcp_fin_timeout", 0644,
+			sizeof(int),
+			ipv4_tcp_fin_timeout_data,
+			proc_dointvec_jiffies),
+	SYSCTL_FIELD_UINT("tcp_notsent_lowat", 0644,
+			ipv4_tcp_notsent_lowat_data),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tcp_tw_reuse", 0644,
+			ipv4_tcp_tw_reuse_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_TWO),
+	SYSCTL_FIELD_STATIC_UINT_MINMAX("tcp_tw_reuse_delay", 0644,
+			ipv4_tcp_tw_reuse_delay_data,
+			SYSCTL_UINT_ONE, &tcp_tw_reuse_delay_max),
+	SYSCTL_FIELD_INT("tcp_max_syn_backlog", 0644,
+			ipv4_tcp_max_syn_backlog_data),
+	SYSCTL_FIELD_INT("tcp_fastopen", 0644,
+			ipv4_tcp_fastopen_data),
+	SYSCTL_FIELD_CUSTOM("tcp_fastopen_key", 0600,
+			((TCP_FASTOPEN_KEY_LENGTH * 2 * TCP_FASTOPEN_KEY_MAX) + (TCP_FASTOPEN_KEY_MAX * 5)),
+			ipv4_tcp_fastopen_key_data,
+			proc_tcp_fastopen_key),
+	SYSCTL_FIELD_CUSTOM("tcp_fastopen_blackhole_timeout_sec", 0644,
+			sizeof(int),
+			ipv4_tcp_fastopen_blackhole_timeout_sec_data,
+			proc_tfo_blackhole_detect_timeout),
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+	SYSCTL_FIELD_STATIC_U8_MINMAX("fib_multipath_use_neigh", 0644,
+			ipv4_fib_multipath_use_neigh_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_CUSTOM("fib_multipath_hash_policy", 0644,
+			sizeof(u8),
+			ipv4_fib_multipath_hash_policy_data,
+			proc_fib_multipath_hash_policy),
+	SYSCTL_FIELD_CUSTOM("fib_multipath_hash_fields", 0644,
+			sizeof(u32),
+			ipv4_fib_multipath_hash_fields_data,
+			proc_fib_multipath_hash_fields),
+	SYSCTL_FIELD_CUSTOM("fib_multipath_hash_seed", 0644,
+			sizeof(u32),
+			ipv4_netns_data,
+			proc_fib_multipath_hash_seed),
+#endif
+	SYSCTL_FIELD_CUSTOM("ip_unprivileged_port_start", 0644,
+			sizeof(int),
+			ipv4_ip_unprivileged_port_start_data,
+			ipv4_privileged_ports),
+#ifdef CONFIG_NET_L3_MASTER_DEV
+	SYSCTL_FIELD_STATIC_U8_MINMAX("udp_l3mdev_accept", 0644,
+			ipv4_udp_l3mdev_accept_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+#endif
+	SYSCTL_FIELD_U8("tcp_sack", 0644,
+			ipv4_tcp_sack_data),
+	SYSCTL_FIELD_U8("tcp_window_scaling", 0644,
+			ipv4_tcp_window_scaling_data),
+	SYSCTL_FIELD_U8("tcp_timestamps", 0644,
+			ipv4_tcp_timestamps_data),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tcp_early_retrans", 0644,
+			ipv4_tcp_early_retrans_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_FOUR),
+	SYSCTL_FIELD_U8("tcp_recovery", 0644,
+			ipv4_tcp_recovery_data),
+	SYSCTL_FIELD_U8("tcp_thin_linear_timeouts", 0644,
+			ipv4_tcp_thin_linear_timeouts_data),
+	SYSCTL_FIELD_U8("tcp_slow_start_after_idle", 0644,
+			ipv4_tcp_slow_start_after_idle_data),
+	SYSCTL_FIELD_U8("tcp_retrans_collapse", 0644,
+			ipv4_tcp_retrans_collapse_data),
+	SYSCTL_FIELD_U8("tcp_stdurg", 0644,
+			ipv4_tcp_stdurg_data),
+	SYSCTL_FIELD_U8("tcp_rfc1337", 0644,
+			ipv4_tcp_rfc1337_data),
+	SYSCTL_FIELD_U8("tcp_abort_on_overflow", 0644,
+			ipv4_tcp_abort_on_overflow_data),
+	SYSCTL_FIELD_U8("tcp_fack", 0644,
+			ipv4_tcp_fack_data),
+	SYSCTL_FIELD_INT("tcp_max_reordering", 0644,
+			ipv4_tcp_max_reordering_data),
+	SYSCTL_FIELD_U8("tcp_dsack", 0644,
+			ipv4_tcp_dsack_data),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tcp_app_win", 0644,
+			ipv4_tcp_app_win_data,
+			SYSCTL_UINT_ZERO, &tcp_app_win_max),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("tcp_adv_win_scale", 0644,
+			ipv4_tcp_adv_win_scale_data,
+			&tcp_adv_win_scale_min, &tcp_adv_win_scale_max),
+	SYSCTL_FIELD_U8("tcp_frto", 0644,
+			ipv4_tcp_frto_data),
+	SYSCTL_FIELD_U8("tcp_no_metrics_save", 0644,
+			ipv4_tcp_no_metrics_save_data),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tcp_no_ssthresh_metrics_save", 0644,
+			ipv4_tcp_no_ssthresh_metrics_save_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_U8("tcp_moderate_rcvbuf", 0644,
+			ipv4_tcp_moderate_rcvbuf_data),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("tcp_rcvbuf_low_rtt", 0644,
+			ipv4_tcp_rcvbuf_low_rtt_data,
+			SYSCTL_ZERO, SYSCTL_INT_MAX),
+	SYSCTL_FIELD_U8("tcp_tso_win_divisor", 0644,
+			ipv4_tcp_tso_win_divisor_data),
+	SYSCTL_FIELD_U8("tcp_workaround_signed_windows", 0644,
+			ipv4_tcp_workaround_signed_windows_data),
+	SYSCTL_FIELD_INT("tcp_limit_output_bytes", 0644,
+			ipv4_tcp_limit_output_bytes_data),
+	SYSCTL_FIELD_INT("tcp_challenge_ack_limit", 0644,
+			ipv4_tcp_challenge_ack_limit_data),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tcp_min_tso_segs", 0644,
+			ipv4_tcp_min_tso_segs_data,
+			SYSCTL_UINT_ONE, NULL),
+	SYSCTL_FIELD_U8("tcp_tso_rtt_log", 0644,
+			ipv4_tcp_tso_rtt_log_data),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("tcp_min_rtt_wlen", 0644,
+			ipv4_tcp_min_rtt_wlen_data,
+			SYSCTL_ZERO, &one_day_secs),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tcp_autocorking", 0644,
+			ipv4_tcp_autocorking_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_CUSTOM("tcp_invalid_ratelimit", 0644,
+			sizeof(int),
+			ipv4_tcp_invalid_ratelimit_data,
+			proc_dointvec_ms_jiffies),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("tcp_pacing_ss_ratio", 0644,
+			ipv4_tcp_pacing_ss_ratio_data,
+			SYSCTL_ZERO, SYSCTL_ONE_THOUSAND),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("tcp_pacing_ca_ratio", 0644,
+			ipv4_tcp_pacing_ca_ratio_data,
+			SYSCTL_ZERO, SYSCTL_ONE_THOUSAND),
+	SYSCTL_FIELD_CUSTOM("tcp_wmem", 0644,
+			sizeof(init_net.ipv4.sysctl_tcp_wmem),
+			ipv4_tcp_wmem_data,
+			proc_dointvec_minmax_one),
+	SYSCTL_FIELD_CUSTOM("tcp_rmem", 0644,
+			sizeof(init_net.ipv4.sysctl_tcp_rmem),
+			ipv4_tcp_rmem_data,
+			proc_dointvec_minmax_one),
+	SYSCTL_FIELD_ULONG("tcp_comp_sack_delay_ns", 0644,
+			ipv4_tcp_comp_sack_delay_ns_data),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("tcp_comp_sack_rtt_percent", 0644,
+			ipv4_tcp_comp_sack_rtt_percent_data,
+			SYSCTL_ONE, SYSCTL_ONE_THOUSAND),
+	SYSCTL_FIELD_ULONG("tcp_comp_sack_slack_ns", 0644,
+			ipv4_tcp_comp_sack_slack_ns_data),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tcp_comp_sack_nr", 0644,
+			ipv4_tcp_comp_sack_nr_data,
+			SYSCTL_UINT_ZERO, NULL),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tcp_backlog_ack_defer", 0644,
+			ipv4_tcp_backlog_ack_defer_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tcp_reflect_tos", 0644,
+			ipv4_tcp_reflect_tos_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_CUSTOM("tcp_ehash_entries", 0444,
+			0,
+			ipv4_tcp_ehash_entries_data,
+			proc_tcp_ehash_entries),
+	SYSCTL_FIELD_STATIC_UINT_MINMAX("tcp_child_ehash_entries", 0644,
+			ipv4_tcp_child_ehash_entries_data,
+			SYSCTL_UINT_ZERO, &tcp_child_ehash_entries_max),
+	SYSCTL_FIELD_CUSTOM("udp_hash_entries", 0444,
+			0,
+			ipv4_udp_hash_entries_data,
+			proc_udp_hash_entries),
+	SYSCTL_FIELD_STATIC_UINT_MINMAX("udp_child_hash_entries", 0644,
+			ipv4_udp_child_hash_entries_data,
+			SYSCTL_UINT_ZERO, &udp_child_hash_entries_max),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("udp_rmem_min", 0644,
+			ipv4_udp_rmem_min_data, SYSCTL_ONE, NULL),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("udp_wmem_min", 0644,
+			ipv4_udp_wmem_min_data, SYSCTL_ONE, NULL),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("fib_notify_on_flag_change", 0644,
+			ipv4_fib_notify_on_flag_change_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_TWO),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tcp_plb_enabled", 0644,
+			ipv4_tcp_plb_enabled_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tcp_plb_idle_rehash_rounds", 0644,
+			ipv4_tcp_plb_idle_rehash_rounds_data,
+			NULL, &tcp_plb_max_rounds),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tcp_plb_rehash_rounds", 0644,
+			ipv4_tcp_plb_rehash_rounds_data,
+			NULL, &tcp_plb_max_rounds),
+	SYSCTL_FIELD_U8("tcp_plb_suspend_rto_sec", 0644,
+			ipv4_tcp_plb_suspend_rto_sec_data),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("tcp_plb_cong_thresh", 0644,
+			ipv4_tcp_plb_cong_thresh_data,
+			SYSCTL_ZERO, &tcp_plb_max_cong_thresh),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tcp_syn_linear_timeouts", 0644,
+			ipv4_tcp_syn_linear_timeouts_data,
+			SYSCTL_UINT_ZERO, &tcp_syn_linear_timeouts_max),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tcp_shrink_window", 0644,
+			ipv4_tcp_shrink_window_data,
+			SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tcp_pingpong_thresh", 0644,
+			ipv4_tcp_pingpong_thresh_data,
+			SYSCTL_UINT_ONE, NULL),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("tcp_rto_min_us", 0644,
+			ipv4_tcp_rto_min_us_data,
+			SYSCTL_ONE, NULL),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("tcp_rto_max_ms", 0644,
+			ipv4_tcp_rto_max_ms_data,
+			SYSCTL_ONE_THOUSAND, &tcp_rto_max_max),
 };
 
 static __net_init int ipv4_sysctl_init_net(struct net *net)
 {
-	size_t table_size = ARRAY_SIZE(ipv4_net_table);
-	struct ctl_table *table;
-
-	table = ipv4_net_table;
-	if (!net_eq(net, &init_net)) {
-		int i;
-
-		table = kmemdup(table, sizeof(ipv4_net_table), GFP_KERNEL);
-		if (!table)
-			goto err_alloc;
-
-		for (i = 0; i < table_size; i++) {
-			if (table[i].data) {
-				/* Update the variables to point into
-				 * the current struct net
-				 */
-				table[i].data += (void *)net - (void *)&init_net;
-			} else {
-				/* Entries without data pointer are global;
-				 * Make them read-only in non-init_net ns
-				 */
-				table[i].mode &= ~0222;
-			}
-		}
-	}
+	struct sysctl_context ctx = {
+		.ns.net_ns = net,
+	};
 
-	net->ipv4.ipv4_hdr = register_net_sysctl_sz(net, "net/ipv4", table,
-						    table_size);
+	net->ipv4.ipv4_hdr = register_sysctl_fields(&net->sysctls, "net/ipv4",
+						    ipv4_net_table, &ctx);
 	if (!net->ipv4.ipv4_hdr)
-		goto err_reg;
+		return -ENOMEM;
 
 	net->ipv4.sysctl_local_reserved_ports = kzalloc(65536 / 8, GFP_KERNEL);
 	if (!net->ipv4.sysctl_local_reserved_ports)
@@ -1694,21 +1198,13 @@ static __net_init int ipv4_sysctl_init_net(struct net *net)
 
 err_ports:
 	unregister_net_sysctl_table(net->ipv4.ipv4_hdr);
-err_reg:
-	if (!net_eq(net, &init_net))
-		kfree(table);
-err_alloc:
 	return -ENOMEM;
 }
 
 static __net_exit void ipv4_sysctl_exit_net(struct net *net)
 {
-	const struct ctl_table *table;
-
 	kfree(net->ipv4.sysctl_local_reserved_ports);
-	table = net->ipv4.ipv4_hdr->ctl_table_arg;
 	unregister_net_sysctl_table(net->ipv4.ipv4_hdr);
-	kfree(table);
 }
 
 static __net_initdata struct pernet_operations ipv4_sysctl_ops = {
Signature · 1 line
2.55.0
#12 lore raw

RFC PATCH 12/30 sysctl: net: use sysctl_field in IPv6 xfrm sysctls

In reply to a message not in the archive

The IPv6 xfrm sysctl table is cloned for every non-init network namespace only to rebind the xfrm6_gc_thresh data pointer. The descriptor itself is static and does not need per-net storage.

Use a sysctl_field descriptor so the data pointer is derived from the network namespace registration context. This keeps the table const and removes the per-net allocation and free path.

---
 net/ipv6/xfrm6_policy.c | 48 +++++++++++------------------------------
 1 file changed, 13 insertions(+), 35 deletions(-)
net/ipv6/xfrm6_policy.c +13 -35
index 125ea9a5b8a0..3df9872100c8 100644
@@ -186,56 +186,34 @@ static void xfrm6_policy_fini(void)
 }
 
 #ifdef CONFIG_SYSCTL
-static struct ctl_table xfrm6_policy_table[] = {
-	{
-		.procname       = "xfrm6_gc_thresh",
-		.data		= &init_net.xfrm.xfrm6_dst_ops.gc_thresh,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler   = proc_dointvec,
-	},
+static int *xfrm6_gc_thresh_data(const struct sysctl_context *ctx)
+{
+	return &ctx->ns.net_ns->xfrm.xfrm6_dst_ops.gc_thresh;
+}
+
+static const struct sysctl_field xfrm6_policy_table[] = {
+	SYSCTL_FIELD_INT("xfrm6_gc_thresh", 0644, xfrm6_gc_thresh_data),
 };
 
 static int __net_init xfrm6_net_sysctl_init(struct net *net)
 {
-	struct ctl_table *table;
+	struct sysctl_context ctx = {
+		.ns.net_ns = net,
+	};
 	struct ctl_table_header *hdr;
 
-	table = xfrm6_policy_table;
-	if (!net_eq(net, &init_net)) {
-		table = kmemdup(table, sizeof(xfrm6_policy_table), GFP_KERNEL);
-		if (!table)
-			goto err_alloc;
-
-		table[0].data = &net->xfrm.xfrm6_dst_ops.gc_thresh;
-	}
-
-	hdr = register_net_sysctl_sz(net, "net/ipv6", table,
-				     ARRAY_SIZE(xfrm6_policy_table));
+	hdr = register_sysctl_fields(&net->sysctls, "net/ipv6",
+				     xfrm6_policy_table, &ctx);
 	if (!hdr)
-		goto err_reg;
+		return -ENOMEM;
 
 	net->ipv6.sysctl.xfrm6_hdr = hdr;
 	return 0;
-
-err_reg:
-	if (!net_eq(net, &init_net))
-		kfree(table);
-err_alloc:
-	return -ENOMEM;
 }
 
 static void __net_exit xfrm6_net_sysctl_exit(struct net *net)
 {
-	const struct ctl_table *table;
-
-	if (!net->ipv6.sysctl.xfrm6_hdr)
-		return;
-
-	table = net->ipv6.sysctl.xfrm6_hdr->ctl_table_arg;
 	unregister_net_sysctl_table(net->ipv6.sysctl.xfrm6_hdr);
-	if (!net_eq(net, &init_net))
-		kfree(table);
 }
 #else /* CONFIG_SYSCTL */
 static inline int xfrm6_net_sysctl_init(struct net *net)
Signature · 1 line
2.55.0
#13 lore raw

RFC PATCH 13/30 sysctl: net: use sysctl_field in IPv6 fragment sysctls

In reply to a message not in the archive

The IPv6 fragment sysctl tables are cloned for every non-init network namespace only to bind the entries to that namespace's fqdir. The clone also has to patch the low/high threshold limits through extra1 and extra2 before registration.

Use sysctl_field descriptors for the IPv6 fragment and conntrack fragment sysctls. The data and limit pointers are derived from the registration context, so the tables can stay const and the per-net allocation, pointer patching, and free paths are no longer needed.

---
 net/ipv6/netfilter/nf_conntrack_reasm.c | 77 ++++++++++---------------
 net/ipv6/reassembly.c                   | 77 ++++++++++---------------
 2 files changed, 60 insertions(+), 94 deletions(-)
net/ipv6/netfilter/nf_conntrack_reasm.c +30 -47
index 64ab23ff559b..f9785d9bd8f3 100644
@@ -43,73 +43,56 @@ static struct nft_ct_frag6_pernet *nf_frag_pernet(struct net *net)
 
 #ifdef CONFIG_SYSCTL
 
-static struct ctl_table nf_ct_frag6_sysctl_table[] = {
-	{
-		.procname	= "nf_conntrack_frag6_timeout",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	{
-		.procname	= "nf_conntrack_frag6_low_thresh",
-		.maxlen		= sizeof(unsigned long),
-		.mode		= 0644,
-		.proc_handler	= proc_doulongvec_minmax,
-	},
-	{
-		.procname	= "nf_conntrack_frag6_high_thresh",
-		.maxlen		= sizeof(unsigned long),
-		.mode		= 0644,
-		.proc_handler	= proc_doulongvec_minmax,
-	},
+static void *nf_ct_frag6_timeout_data(const struct sysctl_context *ctx)
+{
+	return &nf_frag_pernet(ctx->ns.net_ns)->fqdir->timeout;
+}
+
+static unsigned long *nf_ct_frag6_low_thresh_data(const struct sysctl_context *ctx)
+{
+	return &nf_frag_pernet(ctx->ns.net_ns)->fqdir->low_thresh;
+}
+
+static unsigned long *nf_ct_frag6_high_thresh_data(const struct sysctl_context *ctx)
+{
+	return &nf_frag_pernet(ctx->ns.net_ns)->fqdir->high_thresh;
+}
+
+static const struct sysctl_field nf_ct_frag6_sysctl_table[] = {
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_frag6_timeout", 0644, sizeof(unsigned int),
+			 nf_ct_frag6_timeout_data, proc_dointvec_jiffies),
+	SYSCTL_FIELD_ULONG_MINMAX("nf_conntrack_frag6_low_thresh", 0644,
+			       nf_ct_frag6_low_thresh_data, NULL,
+			       nf_ct_frag6_high_thresh_data),
+	SYSCTL_FIELD_ULONG_MINMAX("nf_conntrack_frag6_high_thresh", 0644,
+			       nf_ct_frag6_high_thresh_data,
+			       nf_ct_frag6_low_thresh_data, NULL),
 };
 
 static int nf_ct_frag6_sysctl_register(struct net *net)
 {
+	struct sysctl_context ctx = {
+		.ns.net_ns = net,
+	};
 	struct nft_ct_frag6_pernet *nf_frag;
-	struct ctl_table *table;
 	struct ctl_table_header *hdr;
 
-	table = nf_ct_frag6_sysctl_table;
-	if (!net_eq(net, &init_net)) {
-		table = kmemdup(table, sizeof(nf_ct_frag6_sysctl_table),
-				GFP_KERNEL);
-		if (table == NULL)
-			goto err_alloc;
-	}
-
 	nf_frag = nf_frag_pernet(net);
 
-	table[0].data	= &nf_frag->fqdir->timeout;
-	table[1].data	= &nf_frag->fqdir->low_thresh;
-	table[1].extra2	= &nf_frag->fqdir->high_thresh;
-	table[2].data	= &nf_frag->fqdir->high_thresh;
-	table[2].extra1	= &nf_frag->fqdir->low_thresh;
-
-	hdr = register_net_sysctl_sz(net, "net/netfilter", table,
-				     ARRAY_SIZE(nf_ct_frag6_sysctl_table));
+	hdr = register_sysctl_fields(&net->sysctls, "net/netfilter",
+				     nf_ct_frag6_sysctl_table, &ctx);
 	if (hdr == NULL)
-		goto err_reg;
+		return -ENOMEM;
 
 	nf_frag->nf_frag_frags_hdr = hdr;
 	return 0;
-
-err_reg:
-	if (!net_eq(net, &init_net))
-		kfree(table);
-err_alloc:
-	return -ENOMEM;
 }
 
 static void __net_exit nf_ct_frags6_sysctl_unregister(struct net *net)
 {
 	struct nft_ct_frag6_pernet *nf_frag = nf_frag_pernet(net);
-	const struct ctl_table *table;
 
-	table = nf_frag->nf_frag_frags_hdr->ctl_table_arg;
 	unregister_net_sysctl_table(nf_frag->nf_frag_frags_hdr);
-	if (!net_eq(net, &init_net))
-		kfree(table);
 }
 
 #else
net/ipv6/reassembly.c +30 -47

100 lines not on this page · raw message

Signature · 1 line
2.55.0
#14 lore raw

RFC PATCH 14/30 sysctl: net: use sysctl_field in 6lowpan fragment sysctls

In reply to a message not in the archive

The 6lowpan fragment sysctl table is cloned for every non-init network namespace only to bind the entries to that namespace's fqdir. The clone also has to patch the low/high threshold limits through extra1 and extra2 before registration.

Use sysctl_field descriptors so the data and limit pointers are derived from the registration context. This keeps the table const and removes the per-net allocation, pointer patching, and free path while preserving the existing unprivileged net namespace visibility rule.

---
 net/ieee802154/6lowpan/reassembly.c | 84 ++++++++++++-----------------
 1 file changed, 33 insertions(+), 51 deletions(-)
net/ieee802154/6lowpan/reassembly.c +33 -51
index ddb6a5817d09..c54e50a213f4 100644
@@ -326,25 +326,30 @@ int lowpan_frag_rcv(struct sk_buff *skb, u8 frag_type)
 
 #ifdef CONFIG_SYSCTL
 
-static struct ctl_table lowpan_frags_ns_ctl_table[] = {
-	{
-		.procname	= "6lowpanfrag_high_thresh",
-		.maxlen		= sizeof(unsigned long),
-		.mode		= 0644,
-		.proc_handler	= proc_doulongvec_minmax,
-	},
-	{
-		.procname	= "6lowpanfrag_low_thresh",
-		.maxlen		= sizeof(unsigned long),
-		.mode		= 0644,
-		.proc_handler	= proc_doulongvec_minmax,
-	},
-	{
-		.procname	= "6lowpanfrag_time",
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
+static unsigned long *lowpan_frags_high_thresh_data(const struct sysctl_context *ctx)
+{
+	return &net_ieee802154_lowpan(ctx->ns.net_ns)->fqdir->high_thresh;
+}
+
+static unsigned long *lowpan_frags_low_thresh_data(const struct sysctl_context *ctx)
+{
+	return &net_ieee802154_lowpan(ctx->ns.net_ns)->fqdir->low_thresh;
+}
+
+static void *lowpan_frags_timeout_data(const struct sysctl_context *ctx)
+{
+	return &net_ieee802154_lowpan(ctx->ns.net_ns)->fqdir->timeout;
+}
+
+static const struct sysctl_field lowpan_frags_ns_ctl_table[] = {
+	SYSCTL_FIELD_ULONG_MINMAX("6lowpanfrag_high_thresh", 0644,
+			       lowpan_frags_high_thresh_data,
+			       lowpan_frags_low_thresh_data, NULL),
+	SYSCTL_FIELD_ULONG_MINMAX("6lowpanfrag_low_thresh", 0644,
+			       lowpan_frags_low_thresh_data,
+			       NULL, lowpan_frags_high_thresh_data),
+	SYSCTL_FIELD_CUSTOM("6lowpanfrag_time", 0644, sizeof(int),
+			 lowpan_frags_timeout_data, proc_dointvec_jiffies),
 };
 
 /* secret interval has been deprecated */
@@ -361,55 +366,32 @@ static struct ctl_table lowpan_frags_ctl_table[] = {
 
 static int __net_init lowpan_frags_ns_sysctl_register(struct net *net)
 {
-	struct ctl_table *table;
+	struct sysctl_context ctx = {
+		.ns.net_ns = net,
+	};
 	struct ctl_table_header *hdr;
 	struct netns_ieee802154_lowpan *ieee802154_lowpan =
 		net_ieee802154_lowpan(net);
-	size_t table_size = ARRAY_SIZE(lowpan_frags_ns_ctl_table);
-
-	table = lowpan_frags_ns_ctl_table;
-	if (!net_eq(net, &init_net)) {
-		table = kmemdup(table, sizeof(lowpan_frags_ns_ctl_table),
-				GFP_KERNEL);
-		if (table == NULL)
-			goto err_alloc;
-
-		/* Don't export sysctls to unprivileged users */
-		if (net->user_ns != &init_user_ns)
-			table_size = 0;
-	}
 
-	table[0].data	= &ieee802154_lowpan->fqdir->high_thresh;
-	table[0].extra1	= &ieee802154_lowpan->fqdir->low_thresh;
-	table[1].data	= &ieee802154_lowpan->fqdir->low_thresh;
-	table[1].extra2	= &ieee802154_lowpan->fqdir->high_thresh;
-	table[2].data	= &ieee802154_lowpan->fqdir->timeout;
+	/* Don't export sysctls to unprivileged users */
+	if (!net_eq(net, &init_net) && net->user_ns != &init_user_ns)
+		return 0;
 
-	hdr = register_net_sysctl_sz(net, "net/ieee802154/6lowpan", table,
-				     table_size);
+	hdr = register_sysctl_fields(&net->sysctls, "net/ieee802154/6lowpan",
+				     lowpan_frags_ns_ctl_table, &ctx);
 	if (hdr == NULL)
-		goto err_reg;
+		return -ENOMEM;
 
 	ieee802154_lowpan->sysctl.frags_hdr = hdr;
 	return 0;
-
-err_reg:
-	if (!net_eq(net, &init_net))
-		kfree(table);
-err_alloc:
-	return -ENOMEM;
 }
 
 static void __net_exit lowpan_frags_ns_sysctl_unregister(struct net *net)
 {
-	const struct ctl_table *table;
 	struct netns_ieee802154_lowpan *ieee802154_lowpan =
 		net_ieee802154_lowpan(net);
 
-	table = ieee802154_lowpan->sysctl.frags_hdr->ctl_table_arg;
 	unregister_net_sysctl_table(ieee802154_lowpan->sysctl.frags_hdr);
-	if (!net_eq(net, &init_net))
-		kfree(table);
 }
 
 static struct ctl_table_header *lowpan_ctl_header;
Signature · 1 line
2.55.0
#15 lore raw

RFC PATCH 15/30 sysctl: net: use sysctl_field in vsock sysctls

In reply to a message not in the archive

The vsock sysctl table is cloned for every non-init network namespace only to bind the entries to that namespace's vsock state. The custom mode handlers already derive the namespace from the data pointer, so the table clone is just per-net storage for otherwise static descriptors.

Use sysctl_field descriptors so the data pointers are derived from the registration context. This keeps the table const and removes the per-net allocation, pointer patching, and free path.

---
 net/vmw_vsock/af_vsock.c | 91 ++++++++++++++++------------------------
 1 file changed, 36 insertions(+), 55 deletions(-)
net/vmw_vsock/af_vsock.c +36 -55
index 44037b066a5f..74af67e01430 100644
@@ -2875,71 +2875,52 @@ static int vsock_net_child_mode_string(const struct ctl_table *table, int write,
 	return 0;
 }
 
-static struct ctl_table vsock_table[] = {
-	{
-		.procname	= "ns_mode",
-		.data		= &init_net.vsock.mode,
-		.maxlen		= VSOCK_NET_MODE_STR_MAX,
-		.mode		= 0444,
-		.proc_handler	= vsock_net_mode_string
-	},
-	{
-		.procname	= "child_ns_mode",
-		.data		= &init_net.vsock.child_ns_mode,
-		.maxlen		= VSOCK_NET_MODE_STR_MAX,
-		.mode		= 0644,
-		.proc_handler	= vsock_net_child_mode_string
-	},
-	{
-		.procname	= "g2h_fallback",
-		.data		= &init_net.vsock.g2h_fallback,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
-	},
-};
+#ifdef CONFIG_SYSCTL
+static void *vsock_net_mode_data(const struct sysctl_context *ctx)
+{
+	return &ctx->ns.net_ns->vsock.mode;
+}
 
-static int __net_init vsock_sysctl_register(struct net *net)
+static void *vsock_net_child_mode_data(const struct sysctl_context *ctx)
 {
-	struct ctl_table *table;
+	return &ctx->ns.net_ns->vsock.child_ns_mode;
+}
 
-	if (net_eq(net, &init_net)) {
-		table = vsock_table;
-	} else {
-		table = kmemdup(vsock_table, sizeof(vsock_table), GFP_KERNEL);
-		if (!table)
-			goto err_alloc;
+static int *vsock_g2h_fallback_data(const struct sysctl_context *ctx)
+{
+	return &ctx->ns.net_ns->vsock.g2h_fallback;
+}
 
-		table[0].data = &net->vsock.mode;
-		table[1].data = &net->vsock.child_ns_mode;
-		table[2].data = &net->vsock.g2h_fallback;
-	}
+static const struct sysctl_field vsock_table[] = {
+	SYSCTL_FIELD_CUSTOM("ns_mode", 0444, VSOCK_NET_MODE_STR_MAX,
+			    vsock_net_mode_data, vsock_net_mode_string),
+	SYSCTL_FIELD_CUSTOM("child_ns_mode", 0644, VSOCK_NET_MODE_STR_MAX,
+			    vsock_net_child_mode_data,
+			    vsock_net_child_mode_string),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("g2h_fallback", 0644,
+				       vsock_g2h_fallback_data,
+				       SYSCTL_ZERO, SYSCTL_ONE),
+};
+
+static int __net_init vsock_sysctl_register(struct net *net)
+{
+	struct sysctl_context ctx = {
+		.ns.net_ns = net,
+	};
 
-	net->vsock.sysctl_hdr = register_net_sysctl_sz(net, "net/vsock", table,
-						       ARRAY_SIZE(vsock_table));
+	net->vsock.sysctl_hdr = register_sysctl_fields(&net->sysctls, "net/vsock",
+						       vsock_table, &ctx);
 	if (!net->vsock.sysctl_hdr)
-		goto err_reg;
+		return -ENOMEM;
 
 	return 0;
-
-err_reg:
-	if (!net_eq(net, &init_net))
-		kfree(table);
-err_alloc:
-	return -ENOMEM;
 }
-
-static void vsock_sysctl_unregister(struct net *net)
+#else
+static int __net_init vsock_sysctl_register(struct net *net)
 {
-	const struct ctl_table *table;
-
-	table = net->vsock.sysctl_hdr->ctl_table_arg;
-	unregister_net_sysctl_table(net->vsock.sysctl_hdr);
-	if (!net_eq(net, &init_net))
-		kfree(table);
+	return 0;
 }
+#endif
 
 static void vsock_net_init(struct net *net)
 {
@@ -2965,7 +2946,7 @@ static __net_init int vsock_sysctl_init_net(struct net *net)
 
 static __net_exit void vsock_sysctl_exit_net(struct net *net)
 {
-	vsock_sysctl_unregister(net);
+	unregister_net_sysctl_table(net->vsock.sysctl_hdr);
 }
 
 static struct pernet_operations vsock_sysctl_ops = {
Signature · 1 line
2.55.0
#16 lore raw

RFC PATCH 16/30 sysctl: net: use sysctl_field in MPTCP sysctls

In reply to a message not in the archive

The MPTCP sysctl table is cloned for every non-init network namespace only to bind the entries to that namespace's pernet state. The cloned table is then patched by index, which makes the registration path depend on the table layout staying in sync with the data pointer assignments.

Use sysctl_field descriptors so the data pointers are derived from the registration context. Keep the special min/max handling inside the custom handlers that need it, and leave read-only informational entries without data pointers.

This keeps the table const and removes the per-net allocation, pointer patching, and free path.

---
 net/mptcp/ctrl.c | 205 ++++++++++++++++++-----------------------------
 1 file changed, 76 insertions(+), 129 deletions(-)
net/mptcp/ctrl.c +76 -129
index d96130e49942..31bde3e2eb28 100644
@@ -19,7 +19,7 @@
 static int mptcp_pernet_id;
 
 #ifdef CONFIG_SYSCTL
-static int mptcp_pm_type_max = __MPTCP_PM_TYPE_MAX;
+static unsigned int mptcp_pm_type_max = __MPTCP_PM_TYPE_MAX;
 #endif
 
 struct mptcp_pernet {
@@ -172,9 +172,12 @@ static int proc_blackhole_detect_timeout(const struct ctl_table *table,
 	struct mptcp_pernet *pernet = container_of(table->data,
 						   struct mptcp_pernet,
 						   blackhole_timeout);
+	struct ctl_table tmp = *table;
 	int ret;
 
-	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+	tmp.extra1 = SYSCTL_ZERO;
+
+	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
 	if (write && ret == 0)
 		atomic_set(&pernet->active_disable_times, 0);
 
@@ -236,9 +239,13 @@ static int proc_pm_type(const struct ctl_table *ctl, int write,
 	struct mptcp_pernet *pernet = container_of(ctl->data,
 						   struct mptcp_pernet,
 						   pm_type);
+	struct ctl_table tmp = *ctl;
 	int ret;
 
-	ret = proc_dou8vec_minmax(ctl, write, buffer, lenp, ppos);
+	tmp.extra1 = SYSCTL_UINT_ZERO;
+	tmp.extra2 = &mptcp_pm_type_max;
+
+	ret = proc_dou8vec_minmax(&tmp, write, buffer, lenp, ppos);
 	if (write && ret == 0) {
 		u8 pm_type = READ_ONCE(*(u8 *)ctl->data);
 		char *pm_name = "";
@@ -271,148 +278,88 @@ static int proc_available_path_managers(const struct ctl_table *ctl,
 	return ret;
 }
 
-static struct ctl_table mptcp_sysctl_table[] = {
-	{
-		.procname = "enabled",
-		.maxlen = sizeof(u8),
-		.mode = 0644,
-		/* users with CAP_NET_ADMIN or root (not and) can change this
-		 * value, same as other sysctl or the 'net' tree.
-		 */
-		.proc_handler = proc_dou8vec_minmax,
-		.extra1       = SYSCTL_ZERO,
-		.extra2       = SYSCTL_ONE
-	},
-	{
-		.procname = "add_addr_timeout",
-		.maxlen = sizeof(unsigned int),
-		.mode = 0644,
-		.proc_handler = proc_dointvec_jiffies,
-	},
-	{
-		.procname = "checksum_enabled",
-		.maxlen = sizeof(u8),
-		.mode = 0644,
-		.proc_handler = proc_dou8vec_minmax,
-		.extra1       = SYSCTL_ZERO,
-		.extra2       = SYSCTL_ONE
-	},
-	{
-		.procname = "allow_join_initial_addr_port",
-		.maxlen = sizeof(u8),
-		.mode = 0644,
-		.proc_handler = proc_dou8vec_minmax,
-		.extra1       = SYSCTL_ZERO,
-		.extra2       = SYSCTL_ONE
-	},
-	{
-		.procname = "stale_loss_cnt",
-		.maxlen = sizeof(unsigned int),
-		.mode = 0644,
-		.proc_handler = proc_douintvec_minmax,
-	},
-	{
-		.procname = "pm_type",
-		.maxlen = sizeof(u8),
-		.mode = 0644,
-		.proc_handler = proc_pm_type,
-		.extra1       = SYSCTL_ZERO,
-		.extra2       = &mptcp_pm_type_max
-	},
-	{
-		.procname = "scheduler",
-		.maxlen	= MPTCP_SCHED_NAME_MAX,
-		.mode = 0644,
-		.proc_handler = proc_scheduler,
-	},
-	{
-		.procname = "available_schedulers",
-		.maxlen	= MPTCP_SCHED_BUF_MAX,
-		.mode = 0444,
-		.proc_handler = proc_available_schedulers,
-	},
-	{
-		.procname = "close_timeout",
-		.maxlen = sizeof(unsigned int),
-		.mode = 0644,
-		.proc_handler = proc_dointvec_jiffies,
-	},
-	{
-		.procname = "blackhole_timeout",
-		.maxlen = sizeof(unsigned int),
-		.mode = 0644,
-		.proc_handler = proc_blackhole_detect_timeout,
-		.extra1 = SYSCTL_ZERO,
-	},
-	{
-		.procname = "syn_retrans_before_tcp_fallback",
-		.maxlen = sizeof(u8),
-		.mode = 0644,
-		.proc_handler = proc_dou8vec_minmax,
-	},
-	{
-		.procname = "path_manager",
-		.maxlen	= MPTCP_PM_NAME_MAX,
-		.mode = 0644,
-		.proc_handler = proc_path_manager,
-	},
-	{
-		.procname = "available_path_managers",
-		.maxlen	= MPTCP_PM_BUF_MAX,
-		.mode = 0444,
-		.proc_handler = proc_available_path_managers,
-	},
+#define MPTCP_DATA(type, field)						\
+static type *mptcp_ ## field ## _data(const struct sysctl_context *ctx)	\
+{									\
+	return &mptcp_get_pernet(ctx->ns.net_ns)->field;		\
+}
+
+#define MPTCP_CUSTOM_DATA(field)					\
+static void *mptcp_ ## field ## _data(const struct sysctl_context *ctx)	\
+{									\
+	return &mptcp_get_pernet(ctx->ns.net_ns)->field;		\
+}
+
+static u8 *mptcp_enabled_data(const struct sysctl_context *ctx)
+{
+	return &mptcp_get_pernet(ctx->ns.net_ns)->mptcp_enabled;
+}
+
+MPTCP_CUSTOM_DATA(add_addr_timeout)
+MPTCP_DATA(u8, checksum_enabled)
+MPTCP_DATA(u8, allow_join_initial_addr_port)
+MPTCP_DATA(unsigned int, stale_loss_cnt)
+MPTCP_CUSTOM_DATA(pm_type)
+MPTCP_CUSTOM_DATA(scheduler)
+MPTCP_CUSTOM_DATA(close_timeout)
+MPTCP_CUSTOM_DATA(blackhole_timeout)
+MPTCP_DATA(u8, syn_retrans_before_tcp_fallback)
+MPTCP_CUSTOM_DATA(path_manager)
+
+static const struct sysctl_field mptcp_sysctl_table[] = {
+	/* users with CAP_NET_ADMIN or root (not and) can change this
+	 * value, same as other sysctl or the 'net' tree.
+	 */
+	SYSCTL_FIELD_STATIC_U8_MINMAX("enabled", 0644, mptcp_enabled_data,
+				   SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_CUSTOM("add_addr_timeout", 0644, sizeof(unsigned int),
+			 mptcp_add_addr_timeout_data, proc_dointvec_jiffies),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("checksum_enabled", 0644,
+				   mptcp_checksum_enabled_data,
+				   SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("allow_join_initial_addr_port", 0644,
+				   mptcp_allow_join_initial_addr_port_data,
+				   SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_UINT("stale_loss_cnt", 0644, mptcp_stale_loss_cnt_data),
+	SYSCTL_FIELD_CUSTOM("pm_type", 0644, sizeof(u8),
+			 mptcp_pm_type_data, proc_pm_type),
+	SYSCTL_FIELD_CUSTOM("scheduler", 0644, MPTCP_SCHED_NAME_MAX,
+			 mptcp_scheduler_data, proc_scheduler),
+	SYSCTL_FIELD_CUSTOM("available_schedulers", 0444, MPTCP_SCHED_BUF_MAX,
+			 NULL, proc_available_schedulers),
+	SYSCTL_FIELD_CUSTOM("close_timeout", 0644, sizeof(unsigned int),
+			 mptcp_close_timeout_data, proc_dointvec_jiffies),
+	SYSCTL_FIELD_CUSTOM("blackhole_timeout", 0644, sizeof(unsigned int),
+			 mptcp_blackhole_timeout_data,
+			 proc_blackhole_detect_timeout),
+	SYSCTL_FIELD_U8("syn_retrans_before_tcp_fallback", 0644,
+		     mptcp_syn_retrans_before_tcp_fallback_data),
+	SYSCTL_FIELD_CUSTOM("path_manager", 0644, MPTCP_PM_NAME_MAX,
+			 mptcp_path_manager_data, proc_path_manager),
+	SYSCTL_FIELD_CUSTOM("available_path_managers", 0444, MPTCP_PM_BUF_MAX,
+			 NULL, proc_available_path_managers),
 };
 
 static int mptcp_pernet_new_table(struct net *net, struct mptcp_pernet *pernet)
 {
+	struct sysctl_context ctx = {
+		.ns.net_ns = net,
+	};
 	struct ctl_table_header *hdr;
-	struct ctl_table *table;
-
-	table = mptcp_sysctl_table;
-	if (!net_eq(net, &init_net)) {
-		table = kmemdup(table, sizeof(mptcp_sysctl_table), GFP_KERNEL);
-		if (!table)
-			goto err_alloc;
-	}
 
-	table[0].data = &pernet->mptcp_enabled;
-	table[1].data = &pernet->add_addr_timeout;
-	table[2].data = &pernet->checksum_enabled;
-	table[3].data = &pernet->allow_join_initial_addr_port;
-	table[4].data = &pernet->stale_loss_cnt;
-	table[5].data = &pernet->pm_type;
-	table[6].data = &pernet->scheduler;
-	/* table[7] is for available_schedulers which is read-only info */
-	table[8].data = &pernet->close_timeout;
-	table[9].data = &pernet->blackhole_timeout;
-	table[10].data = &pernet->syn_retrans_before_tcp_fallback;
-	table[11].data = &pernet->path_manager;
-	/* table[12] is for available_path_managers which is read-only info */
-
-	hdr = register_net_sysctl_sz(net, MPTCP_SYSCTL_PATH, table,
-				     ARRAY_SIZE(mptcp_sysctl_table));
+	hdr = register_sysctl_fields(&net->sysctls, MPTCP_SYSCTL_PATH,
+				     mptcp_sysctl_table, &ctx);
 	if (!hdr)
-		goto err_reg;
+		return -ENOMEM;
 
 	pernet->ctl_table_hdr = hdr;
 
 	return 0;
-
-err_reg:
-	if (!net_eq(net, &init_net))
-		kfree(table);
-err_alloc:
-	return -ENOMEM;
 }
 
 static void mptcp_pernet_del_table(struct mptcp_pernet *pernet)
 {
-	const struct ctl_table *table = pernet->ctl_table_hdr->ctl_table_arg;
-
 	unregister_net_sysctl_table(pernet->ctl_table_hdr);
-
-	kfree(table);
 }
 
 #else
Signature · 1 line
2.55.0
#17 lore raw

RFC PATCH 17/30 sysctl: net: use sysctl_field in SCTP sysctls

In reply to a message not in the archive

The SCTP per-net sysctl table is cloned for every network namespace so that all data pointers can be shifted from init_net to the namespace being registered. A few entries then rely on table indexes to patch cross-field limits such as rto_min/rto_max and pf_retrans/ps_retrans.

Use sysctl_field descriptors for the per-net SCTP sysctls instead. The data and limit pointers are derived from the registration context, so the table can stay const and the per-net allocation, offset arithmetic, index-based patching, and free path are no longer needed.

Keep the special validation inside the custom handlers that need it.

---
 net/sctp/sysctl.c | 505 ++++++++++++++++------------------------------
 1 file changed, 169 insertions(+), 336 deletions(-)
net/sctp/sysctl.c +169 -336
index 15e7db9a3ab2..a3577683b9b9 100644
@@ -25,9 +25,9 @@
 #include <net/sctp/sctp.h>
 #include <linux/sysctl.h>
 
-static int timer_max = 86400000; /* ms in one day */
-static int sack_timer_min = 1;
-static int sack_timer_max = 500;
+static unsigned int timer_max = 86400000; /* ms in one day */
+static unsigned int sack_timer_min = 1;
+static unsigned int sack_timer_max = 500;
 static int addr_scope_max = SCTP_SCOPE_POLICY_MAX;
 static int rwnd_scale_max = 16;
 static int rto_alpha_min = 0;
@@ -51,8 +51,10 @@ static int proc_sctp_do_rto_max(const struct ctl_table *ctl, int write, void *bu
 				size_t *lenp, loff_t *ppos);
 static int proc_sctp_do_udp_port(const struct ctl_table *ctl, int write, void *buffer,
 				 size_t *lenp, loff_t *ppos);
-static int proc_sctp_do_alpha_beta(const struct ctl_table *ctl, int write,
-				   void *buffer, size_t *lenp, loff_t *ppos);
+static int proc_sctp_do_alpha(const struct ctl_table *ctl, int write,
+			      void *buffer, size_t *lenp, loff_t *ppos);
+static int proc_sctp_do_beta(const struct ctl_table *ctl, int write,
+			     void *buffer, size_t *lenp, loff_t *ppos);
 static int proc_sctp_do_auth(const struct ctl_table *ctl, int write,
 			     void *buffer, size_t *lenp, loff_t *ppos);
 static int proc_sctp_do_probe_interval(const struct ctl_table *ctl, int write,
@@ -82,306 +84,138 @@ static struct ctl_table sctp_table[] = {
 	},
 };
 
-/* The following index defines are used in sctp_sysctl_net_register().
- * If you add new items to the sctp_net_table, please ensure that
- * the index values of these defines hold the same meaning indicated by
- * their macro names when they appear in sctp_net_table.
- */
-#define SCTP_RTO_MIN_IDX       0
-#define SCTP_RTO_MAX_IDX       1
-#define SCTP_PF_RETRANS_IDX    2
-#define SCTP_PS_RETRANS_IDX    3
-
-static struct ctl_table sctp_net_table[] = {
-	[SCTP_RTO_MIN_IDX] = {
-		.procname	= "rto_min",
-		.data		= &init_net.sctp.rto_min,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_sctp_do_rto_min,
-		.extra1         = SYSCTL_ONE,
-		.extra2         = &init_net.sctp.rto_max
-	},
-	[SCTP_RTO_MAX_IDX] =  {
-		.procname	= "rto_max",
-		.data		= &init_net.sctp.rto_max,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_sctp_do_rto_max,
-		.extra1         = &init_net.sctp.rto_min,
-		.extra2         = &timer_max
-	},
-	[SCTP_PF_RETRANS_IDX] = {
-		.procname	= "pf_retrans",
-		.data		= &init_net.sctp.pf_retrans,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &init_net.sctp.ps_retrans,
-	},
-	[SCTP_PS_RETRANS_IDX] = {
-		.procname	= "ps_retrans",
-		.data		= &init_net.sctp.ps_retrans,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &init_net.sctp.pf_retrans,
-		.extra2		= &ps_retrans_max,
-	},
-	{
-		.procname	= "rto_initial",
-		.data		= &init_net.sctp.rto_initial,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1         = SYSCTL_ONE,
-		.extra2         = &timer_max
-	},
-	{
-		.procname	= "rto_alpha_exp_divisor",
-		.data		= &init_net.sctp.rto_alpha,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_sctp_do_alpha_beta,
-		.extra1		= &rto_alpha_min,
-		.extra2		= &rto_alpha_max,
-	},
-	{
-		.procname	= "rto_beta_exp_divisor",
-		.data		= &init_net.sctp.rto_beta,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_sctp_do_alpha_beta,
-		.extra1		= &rto_beta_min,
-		.extra2		= &rto_beta_max,
-	},
-	{
-		.procname	= "max_burst",
-		.data		= &init_net.sctp.max_burst,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
-	},
-	{
-		.procname	= "cookie_preserve_enable",
-		.data		= &init_net.sctp.cookie_preserve_enable,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "cookie_hmac_alg",
-		.data		= &init_net.sctp.cookie_auth_enable,
-		.maxlen		= 8,
-		.mode		= 0644,
-		.proc_handler	= proc_sctp_do_hmac_alg,
-	},
-	{
-		.procname	= "valid_cookie_life",
-		.data		= &init_net.sctp.valid_cookie_life,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1         = SYSCTL_ONE,
-		.extra2         = &timer_max
-	},
-	{
-		.procname	= "sack_timeout",
-		.data		= &init_net.sctp.sack_timeout,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1         = &sack_timer_min,
-		.extra2         = &sack_timer_max,
-	},
-	{
-		.procname	= "hb_interval",
-		.data		= &init_net.sctp.hb_interval,
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1         = SYSCTL_ONE,
-		.extra2         = &timer_max
-	},
-	{
-		.procname	= "association_max_retrans",
-		.data		= &init_net.sctp.max_retrans_association,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= SYSCTL_INT_MAX,
-	},
-	{
-		.procname	= "path_max_retrans",
-		.data		= &init_net.sctp.max_retrans_path,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= SYSCTL_INT_MAX,
-	},
-	{
-		.procname	= "max_init_retransmits",
-		.data		= &init_net.sctp.max_retrans_init,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= SYSCTL_INT_MAX,
-	},
-	{
-		.procname	= "sndbuf_policy",
-		.data		= &init_net.sctp.sndbuf_policy,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "rcvbuf_policy",
-		.data		= &init_net.sctp.rcvbuf_policy,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "default_auto_asconf",
-		.data		= &init_net.sctp.default_auto_asconf,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "addip_enable",
-		.data		= &init_net.sctp.addip_enable,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "addip_noauth_enable",
-		.data		= &init_net.sctp.addip_noauth,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "prsctp_enable",
-		.data		= &init_net.sctp.prsctp_enable,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "reconf_enable",
-		.data		= &init_net.sctp.reconf_enable,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "auth_enable",
-		.data		= &init_net.sctp.auth_enable,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_sctp_do_auth,
-	},
-	{
-		.procname	= "intl_enable",
-		.data		= &init_net.sctp.intl_enable,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "ecn_enable",
-		.data		= &init_net.sctp.ecn_enable,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "plpmtud_probe_interval",
-		.data		= &init_net.sctp.probe_interval,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_sctp_do_probe_interval,
-	},
-	{
-		.procname	= "udp_port",
-		.data		= &init_net.sctp.udp_port,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_sctp_do_udp_port,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &udp_port_max,
-	},
-	{
-		.procname	= "encap_port",
-		.data		= &init_net.sctp.encap_port,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &udp_port_max,
-	},
-	{
-		.procname	= "addr_scope_policy",
-		.data		= &init_net.sctp.scope_policy,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &addr_scope_max,
-	},
-	{
-		.procname	= "rwnd_update_shift",
-		.data		= &init_net.sctp.rwnd_upd_shift,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= &rwnd_scale_max,
-	},
-	{
-		.procname	= "max_autoclose",
-		.data		= &init_net.sctp.max_autoclose,
-		.maxlen		= sizeof(unsigned long),
-		.mode		= 0644,
-		.proc_handler	= &proc_doulongvec_minmax,
-		.extra1		= &max_autoclose_min,
-		.extra2		= &max_autoclose_max,
-	},
+static int *sctp_ps_retrans_max_data(const struct sysctl_context *ctx)
+{
+	return &ps_retrans_max;
+}
+
+#define SCTP_DATA(type, field)						\
+static type *sctp_ ## field ## _data(const struct sysctl_context *ctx)	\
+{									\
+	return &ctx->ns.net_ns->sctp.field;				\
+}
+
+#define SCTP_CUSTOM_DATA(field)						\
+static void *sctp_ ## field ## _data(const struct sysctl_context *ctx)	\
+{									\
+	return &ctx->ns.net_ns->sctp.field;				\
+}
+
+SCTP_CUSTOM_DATA(rto_min)
+SCTP_CUSTOM_DATA(rto_max)
+SCTP_DATA(int, pf_retrans)
+SCTP_DATA(int, ps_retrans)
+SCTP_DATA(unsigned int, rto_initial)
+SCTP_CUSTOM_DATA(rto_alpha)
+SCTP_CUSTOM_DATA(rto_beta)
+SCTP_DATA(int, max_burst)
+SCTP_DATA(int, cookie_preserve_enable)
+SCTP_CUSTOM_DATA(cookie_auth_enable)
+SCTP_DATA(unsigned int, valid_cookie_life)
+SCTP_DATA(unsigned int, sack_timeout)
+SCTP_DATA(unsigned int, hb_interval)
+SCTP_DATA(int, max_retrans_association)
+SCTP_DATA(int, max_retrans_path)
+SCTP_DATA(int, max_retrans_init)
+SCTP_DATA(int, sndbuf_policy)
+SCTP_DATA(int, rcvbuf_policy)
+SCTP_DATA(int, default_auto_asconf)
+SCTP_DATA(int, addip_enable)
+SCTP_DATA(int, addip_noauth)
+SCTP_DATA(int, prsctp_enable)
+SCTP_DATA(int, reconf_enable)
+SCTP_CUSTOM_DATA(auth_enable)
+SCTP_DATA(int, intl_enable)
+SCTP_DATA(int, ecn_enable)
+SCTP_CUSTOM_DATA(probe_interval)
+SCTP_CUSTOM_DATA(udp_port)
+SCTP_DATA(int, encap_port)
+SCTP_DATA(int, scope_policy)
+SCTP_DATA(int, rwnd_upd_shift)
+SCTP_DATA(unsigned long, max_autoclose)
+SCTP_DATA(int, pf_enable)
+SCTP_DATA(int, pf_expose)
 #ifdef CONFIG_NET_L3_MASTER_DEV
-	{
-		.procname	= "l3mdev_accept",
-		.data		= &init_net.sctp.l3mdev_accept,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
-	},
+SCTP_DATA(int, l3mdev_accept)
 #endif
-	{
-		.procname	= "pf_enable",
-		.data		= &init_net.sctp.pf_enable,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "pf_expose",
-		.data		= &init_net.sctp.pf_expose,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &pf_expose_max,
-	},
+
+static const struct sysctl_field sctp_net_table[] = {
+	SYSCTL_FIELD_CUSTOM("rto_min", 0644, sizeof(unsigned int),
+			 sctp_rto_min_data, proc_sctp_do_rto_min),
+	SYSCTL_FIELD_CUSTOM("rto_max", 0644, sizeof(unsigned int),
+			 sctp_rto_max_data, proc_sctp_do_rto_max),
+	SYSCTL_FIELD_INT_MINMAX("pf_retrans", 0644, sctp_pf_retrans_data,
+			     SYSCTL_ZERO, sctp_ps_retrans_data),
+	SYSCTL_FIELD_INT_MINMAX("ps_retrans", 0644, sctp_ps_retrans_data,
+			     sctp_pf_retrans_data, sctp_ps_retrans_max_data),
+	SYSCTL_FIELD_STATIC_UINT_MINMAX("rto_initial", 0644,
+				     sctp_rto_initial_data,
+				     SYSCTL_UINT_ONE, &timer_max),
+	SYSCTL_FIELD_CUSTOM("rto_alpha_exp_divisor", 0644, sizeof(int),
+			 sctp_rto_alpha_data, proc_sctp_do_alpha),
+	SYSCTL_FIELD_CUSTOM("rto_beta_exp_divisor", 0644, sizeof(int),
+			 sctp_rto_beta_data, proc_sctp_do_beta),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("max_burst", 0644, sctp_max_burst_data,
+				    SYSCTL_ZERO, SYSCTL_INT_MAX),
+	SYSCTL_FIELD_INT("cookie_preserve_enable", 0644,
+		      sctp_cookie_preserve_enable_data),
+	SYSCTL_FIELD_CUSTOM("cookie_hmac_alg", 0644, 8,
+			 sctp_cookie_auth_enable_data, proc_sctp_do_hmac_alg),
+	SYSCTL_FIELD_STATIC_UINT_MINMAX("valid_cookie_life", 0644,
+				     sctp_valid_cookie_life_data,
+				     SYSCTL_UINT_ONE, &timer_max),
+	SYSCTL_FIELD_STATIC_UINT_MINMAX("sack_timeout", 0644,
+				     sctp_sack_timeout_data,
+				     &sack_timer_min, &sack_timer_max),
+	SYSCTL_FIELD_STATIC_UINT_MINMAX("hb_interval", 0644,
+				     sctp_hb_interval_data,
+				     SYSCTL_UINT_ONE, &timer_max),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("association_max_retrans", 0644,
+				    sctp_max_retrans_association_data,
+				    SYSCTL_ONE, SYSCTL_INT_MAX),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("path_max_retrans", 0644,
+				    sctp_max_retrans_path_data,
+				    SYSCTL_ONE, SYSCTL_INT_MAX),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("max_init_retransmits", 0644,
+				    sctp_max_retrans_init_data,
+				    SYSCTL_ONE, SYSCTL_INT_MAX),
+	SYSCTL_FIELD_INT("sndbuf_policy", 0644, sctp_sndbuf_policy_data),
+	SYSCTL_FIELD_INT("rcvbuf_policy", 0644, sctp_rcvbuf_policy_data),
+	SYSCTL_FIELD_INT("default_auto_asconf", 0644,
+		      sctp_default_auto_asconf_data),
+	SYSCTL_FIELD_INT("addip_enable", 0644, sctp_addip_enable_data),
+	SYSCTL_FIELD_INT("addip_noauth_enable", 0644, sctp_addip_noauth_data),
+	SYSCTL_FIELD_INT("prsctp_enable", 0644, sctp_prsctp_enable_data),
+	SYSCTL_FIELD_INT("reconf_enable", 0644, sctp_reconf_enable_data),
+	SYSCTL_FIELD_CUSTOM("auth_enable", 0644, sizeof(int),
+			 sctp_auth_enable_data, proc_sctp_do_auth),
+	SYSCTL_FIELD_INT("intl_enable", 0644, sctp_intl_enable_data),
+	SYSCTL_FIELD_INT("ecn_enable", 0644, sctp_ecn_enable_data),
+	SYSCTL_FIELD_CUSTOM("plpmtud_probe_interval", 0644, sizeof(int),
+			 sctp_probe_interval_data,
+			 proc_sctp_do_probe_interval),
+	SYSCTL_FIELD_CUSTOM("udp_port", 0644, sizeof(int),
+			 sctp_udp_port_data, proc_sctp_do_udp_port),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("encap_port", 0644,
+				    sctp_encap_port_data,
+				    SYSCTL_ZERO, &udp_port_max),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("addr_scope_policy", 0644,
+				    sctp_scope_policy_data,
+				    SYSCTL_ZERO, &addr_scope_max),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("rwnd_update_shift", 0644,
+				    sctp_rwnd_upd_shift_data,
+				    SYSCTL_ONE, &rwnd_scale_max),
+	SYSCTL_FIELD_STATIC_ULONG_MINMAX("max_autoclose", 0644,
+				      sctp_max_autoclose_data,
+				      &max_autoclose_min, &max_autoclose_max),
+#ifdef CONFIG_NET_L3_MASTER_DEV
+	SYSCTL_FIELD_STATIC_INT_MINMAX("l3mdev_accept", 0644,
+				    sctp_l3mdev_accept_data,
+				    SYSCTL_ZERO, SYSCTL_ONE),
+#endif
+	SYSCTL_FIELD_INT("pf_enable", 0644, sctp_pf_enable_data),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("pf_expose", 0644, sctp_pf_expose_data,
+				    SYSCTL_ZERO, &pf_expose_max),
 };
 
 static int proc_sctp_do_hmac_alg(const struct ctl_table *ctl, int write,
@@ -423,8 +257,6 @@ static int proc_sctp_do_rto_min(const struct ctl_table *ctl, int write,
 				void *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct net *net = container_of(ctl->data, struct net, sctp.rto_min);
-	unsigned int min = *(unsigned int *) ctl->extra1;
-	unsigned int max = *(unsigned int *) ctl->extra2;
 	struct ctl_table tbl;
 	int ret, new_value;
 
@@ -438,7 +270,7 @@ static int proc_sctp_do_rto_min(const struct ctl_table *ctl, int write,
 
 	ret = proc_dointvec(&tbl, write, buffer, lenp, ppos);
 	if (write && ret == 0) {
-		if (new_value > max || new_value < min)
+		if (new_value > (int) net->sctp.rto_max || new_value < 1)
 			return -EINVAL;
 
 		net->sctp.rto_min = new_value;
@@ -451,8 +283,6 @@ static int proc_sctp_do_rto_max(const struct ctl_table *ctl, int write,
 				void *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct net *net = container_of(ctl->data, struct net, sctp.rto_max);
-	unsigned int min = *(unsigned int *) ctl->extra1;
-	unsigned int max = *(unsigned int *) ctl->extra2;
 	struct ctl_table tbl;
 	int ret, new_value;
 
@@ -466,7 +296,8 @@ static int proc_sctp_do_rto_max(const struct ctl_table *ctl, int write,
 
 	ret = proc_dointvec(&tbl, write, buffer, lenp, ppos);
 	if (write && ret == 0) {
-		if (new_value > max || new_value < min)
+		if (new_value > (int) timer_max ||
+		    new_value < (int) net->sctp.rto_min)
 			return -EINVAL;
 
 		net->sctp.rto_max = new_value;
@@ -485,6 +316,28 @@ static int proc_sctp_do_alpha_beta(const struct ctl_table *ctl, int write,
 	return proc_dointvec_minmax(ctl, write, buffer, lenp, ppos);
 }
 
+static int proc_sctp_do_alpha(const struct ctl_table *ctl, int write,
+			      void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct ctl_table tmp = *ctl;
+
+	tmp.extra1 = &rto_alpha_min;
+	tmp.extra2 = &rto_alpha_max;
+
+	return proc_sctp_do_alpha_beta(&tmp, write, buffer, lenp, ppos);
+}
+
+static int proc_sctp_do_beta(const struct ctl_table *ctl, int write,
+			     void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct ctl_table tmp = *ctl;
+
+	tmp.extra1 = &rto_beta_min;
+	tmp.extra2 = &rto_beta_max;
+
+	return proc_sctp_do_alpha_beta(&tmp, write, buffer, lenp, ppos);
+}
+
 static int proc_sctp_do_auth(const struct ctl_table *ctl, int write,
 			     void *buffer, size_t *lenp, loff_t *ppos)
 {
@@ -520,8 +373,6 @@ static int proc_sctp_do_udp_port(const struct ctl_table *ctl, int write,
 				 void *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct net *net = container_of(ctl->data, struct net, sctp.udp_port);
-	unsigned int min = *(unsigned int *)ctl->extra1;
-	unsigned int max = *(unsigned int *)ctl->extra2;
 	struct ctl_table tbl;
 	int ret, new_value;
 
@@ -537,7 +388,7 @@ static int proc_sctp_do_udp_port(const struct ctl_table *ctl, int write,
 	if (write && ret == 0) {
 		struct sock *sk = net->sctp.ctl_sock;
 
-		if (new_value > max || new_value < min)
+		if (new_value > udp_port_max || new_value < 0)
 			return -EINVAL;
 
 		mutex_lock(&sctp_sysctl_mutex);
@@ -588,38 +439,20 @@ static int proc_sctp_do_probe_interval(const struct ctl_table *ctl, int write,
 
 int sctp_sysctl_net_register(struct net *net)
 {
-	size_t table_size = ARRAY_SIZE(sctp_net_table);
-	struct ctl_table *table;
-	int i;
-
-	table = kmemdup(sctp_net_table, sizeof(sctp_net_table), GFP_KERNEL);
-	if (!table)
+	struct sysctl_context ctx = {
+		.ns.net_ns = net,
+	};
+	net->sctp.sysctl_header = register_sysctl_fields(&net->sysctls, "net/sctp",
+							 sctp_net_table, &ctx);
+	if (!net->sctp.sysctl_header)
 		return -ENOMEM;
 
-	for (i = 0; i < table_size; i++)
-		table[i].data += (char *)(&net->sctp) - (char *)&init_net.sctp;
-
-	table[SCTP_RTO_MIN_IDX].extra2 = &net->sctp.rto_max;
-	table[SCTP_RTO_MAX_IDX].extra1 = &net->sctp.rto_min;
-	table[SCTP_PF_RETRANS_IDX].extra2 = &net->sctp.ps_retrans;
-	table[SCTP_PS_RETRANS_IDX].extra1 = &net->sctp.pf_retrans;
-
-	net->sctp.sysctl_header = register_net_sysctl_sz(net, "net/sctp",
-							 table, table_size);
-	if (net->sctp.sysctl_header == NULL) {
-		kfree(table);
-		return -ENOMEM;
-	}
 	return 0;
 }
 
 void sctp_sysctl_net_unregister(struct net *net)
 {
-	const struct ctl_table *table;
-
-	table = net->sctp.sysctl_header->ctl_table_arg;
 	unregister_net_sysctl_table(net->sctp.sysctl_header);
-	kfree(table);
 }
 
 static struct ctl_table_header *sctp_sysctl_header;
Signature · 1 line
2.55.0
#18 lore raw

Re: [RFC PATCH v1 17/30] sysctl: net: use sysctl_field in SCTP sysctls

I detest this series.

Maybe it makes technical sense, but as long as it makes no human visual sense, I'm NAK'ing it as being garbage.

Quoted 1 line · Alexey Gladkov

[..]

The whole series is full of complete illegible noise like this:

net/sctp/sysctl.c static struct ctl_table sctp_table[] = { · line 153

Quoted 12 lines
net/sctp/sysctl.c +12 -0
+static const struct sysctl_field sctp_net_table[] = {
+       SYSCTL_FIELD_CUSTOM("rto_min", 0644, sizeof(unsigned int),
+                        sctp_rto_min_data, proc_sctp_do_rto_min),
+       SYSCTL_FIELD_CUSTOM("rto_max", 0644, sizeof(unsigned int),
+                        sctp_rto_max_data, proc_sctp_do_rto_max),
+       SYSCTL_FIELD_INT_MINMAX("pf_retrans", 0644, sctp_pf_retrans_data,
+                            SYSCTL_ZERO, sctp_ps_retrans_data),
+       SYSCTL_FIELD_INT_MINMAX("ps_retrans", 0644, sctp_ps_retrans_data,
+                            sctp_pf_retrans_data, sctp_ps_retrans_max_data),
+       SYSCTL_FIELD_STATIC_UINT_MINMAX("rto_initial", 0644,
+                                    sctp_rto_initial_data,
+                                    SYSCTL_UINT_ONE, &timer_max),

[...]

where apparently the indentation has been decided by a rodent on crack who was given an Ouija board and instructed to ask his dead ancestors what indentation to use.

So no.

That kind of complete random code is simply not acceptable.

I don't know what the correct answer is, but it is *not* this series. It needs to be consistent and visually parseable by humans *without* asking your dead ancestors for help.

And it's not just the indentation. That SYSCTL_FIELD_CUSTOM() thing needs to be usable and able to do some minimal type checking - not just passed a random sizeof() in a random argumentt. I'd suggest passing the actual type, and then checking that the type *matches* the data pointer it is passed too.

The old code may be ugly too, and have various other warts, but at least it had somewhat legible and understandable initializers:

{
.procname = "prsctp_enable",
.data = &init_net.sctp.prsctp_enable,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec,
},

is at least something that can be read by a human and those things had consistent whitespace rather than some quantum randomness.

And this is also just complete line noise that only makes the code worse:

    +#define SCTP_DATA(type, field)                                         \
    +static type *sctp_ ## field ## _data(const struct sysctl_context *ctx) \
    +{                                                                      \
    +       return &ctx->ns.net_ns->sctp.field;                             \
    +}
    +
    +#define SCTP_CUSTOM_DATA(field)
             \
    +static void *sctp_ ## field ## _data(const struct sysctl_context *ctx) \
    +{                                                                      \
    +       return &ctx->ns.net_ns->sctp.field;                             \
    +}
    +
    +SCTP_CUSTOM_DATA(rto_min)
    +SCTP_CUSTOM_DATA(rto_max)
    +SCTP_DATA(int, pf_retrans)
    +SCTP_DATA(int, ps_retrans)
    +SCTP_DATA(unsigned int, rto_initial)
    [...]

If we're doing these kinds of changes, the end result has to look *BETTER* than the thing it replaces, not worse.

Yes, a few of the patches did look better. But the majority of them only looked worse. Some of it should be easy to fix: use consistent whitespace, and use sane argument ordering.

But honestly, the old setup didn't *rely* on argument ordering, and used named initializers to make things more legible and robust.

So I suspect that should be what you should aim for in the new setup too, and that probably means "completely different approach".

Linus

#19 lore raw

Re: [RFC PATCH v1 17/30] sysctl: net: use sysctl_field in SCTP sysctls

Quoted 4 lines · Linus Torvalds

I detest this series.

Maybe it makes technical sense, but as long as it makes no human visual sense, I'm NAK'ing it as being garbage.

You are right that my approach added too many macros and made the tables harder to read. Sorry.

Quoted 81 lines · Alexey Gladkov
Quoted 1 line

[..]

The whole series is full of complete illegible noise like this:

net/sctp/sysctl.c static struct ctl_table sctp_table[] = { · line 153

Quoted 12 lines
net/sctp/sysctl.c +12 -0
+static const struct sysctl_field sctp_net_table[] = {
+       SYSCTL_FIELD_CUSTOM("rto_min", 0644, sizeof(unsigned int),
+                        sctp_rto_min_data, proc_sctp_do_rto_min),
+       SYSCTL_FIELD_CUSTOM("rto_max", 0644, sizeof(unsigned int),
+                        sctp_rto_max_data, proc_sctp_do_rto_max),
+       SYSCTL_FIELD_INT_MINMAX("pf_retrans", 0644, sctp_pf_retrans_data,
+                            SYSCTL_ZERO, sctp_ps_retrans_data),
+       SYSCTL_FIELD_INT_MINMAX("ps_retrans", 0644, sctp_ps_retrans_data,
+                            sctp_pf_retrans_data, sctp_ps_retrans_max_data),
+       SYSCTL_FIELD_STATIC_UINT_MINMAX("rto_initial", 0644,
+                                    sctp_rto_initial_data,
+                                    SYSCTL_UINT_ONE, &timer_max),

[...]

where apparently the indentation has been decided by a rodent on crack who was given an Ouija board and instructed to ask his dead ancestors what indentation to use.

So no.

That kind of complete random code is simply not acceptable.

I don't know what the correct answer is, but it is *not* this series. It needs to be consistent and visually parseable by humans *without* asking your dead ancestors for help.

And it's not just the indentation. That SYSCTL_FIELD_CUSTOM() thing needs to be usable and able to do some minimal type checking - not just passed a random sizeof() in a random argumentt. I'd suggest passing the actual type, and then checking that the type *matches* the data pointer it is passed too.

The old code may be ugly too, and have various other warts, but at least it had somewhat legible and understandable initializers:

{
.procname = "prsctp_enable",
.data = &init_net.sctp.prsctp_enable,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec,
},

is at least something that can be read by a human and those things had consistent whitespace rather than some quantum randomness.

And this is also just complete line noise that only makes the code worse:

    +#define SCTP_DATA(type, field)                                         \
    +static type *sctp_ ## field ## _data(const struct sysctl_context *ctx) \
    +{                                                                      \
    +       return &ctx->ns.net_ns->sctp.field;                             \
    +}
    +
    +#define SCTP_CUSTOM_DATA(field)
             \
    +static void *sctp_ ## field ## _data(const struct sysctl_context *ctx) \
    +{                                                                      \
    +       return &ctx->ns.net_ns->sctp.field;                             \
    +}
    +
    +SCTP_CUSTOM_DATA(rto_min)
    +SCTP_CUSTOM_DATA(rto_max)
    +SCTP_DATA(int, pf_retrans)
    +SCTP_DATA(int, ps_retrans)
    +SCTP_DATA(unsigned int, rto_initial)
    [...]

If we're doing these kinds of changes, the end result has to look *BETTER* than the thing it replaces, not worse.

Yes, a few of the patches did look better. But the majority of them only looked worse. Some of it should be easy to fix: use consistent whitespace, and use sane argument ordering.

Quoted 8 lines

But honestly, the old setup didn't *rely* on argument ordering, and used named initializers to make things more legible and robust.

So I suspect that should be what you should aim for in the new setup too, and that probably means "completely different approach".

Linus

I tried an alternative based on offsets. The following four patches add the infrastructure and convert IPC, SCTP, and MPLS as examples.

The remaining macros are short wrappers around offsetof() and are only used for compile-time type checking. If they still hurt readability, I can drop that checking and use plain offsetof() instead.

Does this look better for you, or should I just stop?

Alexey Gladkov (4):
sysctl: add typed field descriptors
sysctl: ipc: use typed fields for IPC namespace sysctls
sctp: use typed fields for per-net sysctls
mpls: use typed fields for per-device sysctls

 fs/proc/proc_sysctl.c  | 282 ++++++++++++++++++++++++++---
 include/linux/sysctl.h | 134 +++++++++++++-
 ipc/ipc_sysctl.c       | 188 +++++++++----------
 net/mpls/af_mpls.c     |  66 ++++---
 net/sctp/sysctl.c      | 398 +++++++++++++++++++++--------------------
 5 files changed, 703 insertions(+), 365 deletions(-)
Signature · 1 line
Rgrds, legion
#20 lore raw

RFC PATCH 1/4 sysctl: add typed field descriptors

Several sysctl users duplicate ctl_table arrays at registration time so data and limit pointers can be redirected to namespace or device state. The copies consume memory for every instance and their index-based fixups silently depend on the source table order.

Add ctl_field as an alternative static descriptor. A field records the value kind and a checked offset into an object selected by a registration context. Type-specific offset helpers verify the backing member type at build time, while the core derives the legacy proc handler, size and limit pointers from the field kind.

Keep ctl_table as the interface used by proc handlers, permissions and BPF by materializing one entry on the stack when those paths need it. Existing ctl_table registrations are unchanged, and converted users can share one read-only descriptor array without allocating a table copy.

---
 fs/proc/proc_sysctl.c  | 282 +++++++++++++++++++++++++++++++++++++----
 include/linux/sysctl.h | 134 +++++++++++++++++++-
 2 files changed, 384 insertions(+), 32 deletions(-)
fs/proc/proc_sysctl.c +255 -27
index 131496490991..b52ce725c212 100644
@@ -18,6 +18,7 @@
 #include <linux/mount.h>
 #include <linux/kmemleak.h>
 #include <linux/lockdep.h>
+#include <linux/overflow.h>
 #include "internal.h"
 
 #define list_for_each_table_entry(index, header)			\
@@ -91,28 +92,171 @@ static int sysctl_follow_link(struct ctl_table_header **phead, size_t *pindex);
 static int insert_links(struct ctl_table_header *head);
 static void put_links(struct ctl_table_header *header);
 
+static inline bool is_field_table(const struct ctl_table_header *head)
+{
+	return head->table_kind == SYSCTL_TABLE_KIND_FIELD;
+}
+
 static const char *sysctl_entry_procname(struct ctl_table_header *head,
 					 size_t index)
 {
+	if (is_field_table(head))
+		return head->ctl_fields[index].procname;
+
 	return head->ctl_table[index].procname;
 }
 
 static umode_t sysctl_entry_mode(struct ctl_table_header *head, size_t index)
 {
-	return head->ctl_table[index].mode;
+	const struct sysctl_field *field;
+
+	if (!is_field_table(head))
+		return head->ctl_table[index].mode;
+
+	field = &head->ctl_fields[index];
+
+	if (field->mode_fn) {
+		lockdep_assert_not_held(&sysctl_lock);
+		return field->mode_fn(head->ctx);
+	}
+
+	return field->mode;
+}
+
+static bool sysctl_entry_is_dir(const struct ctl_table_header *head,
+				size_t index)
+{
+	/* Do not call field mode_fn callback while holding sysctl_lock. */
+	if (is_field_table(head))
+		return false;
+
+	return S_ISDIR(head->ctl_table[index].mode);
+}
+
+static bool sysctl_entry_is_link(const struct ctl_table_header *head,
+				 size_t index)
+{
+	if (is_field_table(head))
+		return false;
+
+	return S_ISLNK(head->ctl_table[index].mode);
 }
 
 static struct ctl_table_poll *sysctl_entry_poll(struct ctl_table_header *head,
 						size_t index)
 {
+	if (is_field_table(head))
+		return NULL;
+
 	return head->ctl_table[index].poll;
 }
 
+static void *sysctl_context_object(const struct sysctl_context *ctx)
+{
+	if (!ctx)
+		return NULL;
+	if (ctx->object)
+		return ctx->object(ctx);
+
+	switch (ctx->type) {
+	case SYSCTL_CONTEXT_USER_NS:
+		return ctx->ns.user_ns;
+	case SYSCTL_CONTEXT_IPC_NS:
+		return ctx->ns.ipc_ns;
+	case SYSCTL_CONTEXT_PID_NS:
+		return ctx->ns.pid_ns;
+	case SYSCTL_CONTEXT_NET_NS:
+		return ctx->ns.net_ns;
+	}
+
+	return NULL;
+}
+
+static void *sysctl_context_data(const struct sysctl_context *ctx,
+				 size_t offset, size_t size)
+{
+	void *object = sysctl_context_object(ctx);
+
+	if (!object || offset > ctx->object_size ||
+	    size > ctx->object_size - offset)
+		return NULL;
+
+	return (char *)object + offset;
+}
+
 static const struct ctl_table *
 sysctl_entry_table(struct ctl_table_header *head, size_t index,
 		   struct ctl_table *table)
 {
-	return &head->ctl_table[index];
+	const struct sysctl_field *field;
+
+	if (!is_field_table(head))
+		return &head->ctl_table[index];
+
+	field = &head->ctl_fields[index];
+
+	memset(table, 0, sizeof(*table));
+	table->procname = field->procname;
+	table->mode = field->mode;
+
+	if (field->mode_fn)
+		table->mode = field->mode_fn(head->ctx);
+
+	switch (field->type) {
+	case SYSCTL_FIELD_NO_DATA:
+		break;
+	case SYSCTL_FIELD_STRING:
+		table->proc_handler = proc_dostring;
+		table->maxlen = field->maxlen;
+		break;
+	case SYSCTL_FIELD_BOOL:
+		table->proc_handler = proc_dobool;
+		table->maxlen = sizeof(bool);
+		break;
+	case SYSCTL_FIELD_U8:
+	case SYSCTL_FIELD_U8_MINMAX:
+		table->proc_handler = proc_dou8vec_minmax;
+		table->maxlen = sizeof(u8);
+		table->extra1 = field->u8_limits.min_value;
+		table->extra2 = field->u8_limits.max_value;
+		break;
+	case SYSCTL_FIELD_INT:
+	case SYSCTL_FIELD_INT_MINMAX:
+		table->proc_handler = field->type == SYSCTL_FIELD_INT ?
+				      proc_dointvec : proc_dointvec_minmax;
+		table->maxlen = sizeof(int);
+		table->extra1 = field->int_limits.min_value;
+		table->extra2 = field->int_limits.max_value;
+		break;
+	case SYSCTL_FIELD_UINT:
+	case SYSCTL_FIELD_UINT_MINMAX:
+		table->proc_handler = field->type == SYSCTL_FIELD_UINT ?
+				      proc_douintvec : proc_douintvec_minmax;
+		table->maxlen = sizeof(unsigned int);
+		table->extra1 = field->uint_limits.min_value;
+		table->extra2 = field->uint_limits.max_value;
+		break;
+	case SYSCTL_FIELD_ULONG:
+	case SYSCTL_FIELD_ULONG_MINMAX:
+		table->proc_handler = proc_doulongvec_minmax;
+		table->maxlen = sizeof(unsigned long);
+		table->extra1 = field->ulong_limits.min_value;
+		table->extra2 = field->ulong_limits.max_value;
+		break;
+	case SYSCTL_FIELD_SIZE_T:
+		table->proc_handler = proc_doulongvec_minmax;
+		table->maxlen = sizeof(size_t);
+		break;
+	}
+
+	if (field->type != SYSCTL_FIELD_NO_DATA)
+		table->data = sysctl_context_data(head->ctx, field->data_offset, table->maxlen);
+	if (field->proc_handler)
+		table->proc_handler = field->proc_handler;
+	if (field->maxlen)
+		table->maxlen = field->maxlen;
+
+	return table;
 }
 
 static void sysctl_print_dir(struct ctl_dir *dir)
@@ -212,9 +356,17 @@ static void erase_entry(struct ctl_table_header *head, size_t index)
 
 static void init_header(struct ctl_table_header *head,
 	struct ctl_table_root *root, struct ctl_table_set *set,
-	struct ctl_node *node, const struct ctl_table *table, size_t table_size)
+	struct ctl_node *node, const struct ctl_table *table,
+	const struct sysctl_field *fields, size_t table_size,
+	const struct sysctl_context *ctx)
 {
-	head->ctl_table = table;
+	if (fields) {
+		head->ctl_fields = fields;
+		head->table_kind = SYSCTL_TABLE_KIND_FIELD;
+	} else {
+		head->ctl_table = table;
+		head->table_kind = SYSCTL_TABLE_KIND_TABLE;
+	}
 	head->ctl_table_size = table_size;
 	head->ctl_table_arg = table;
 	head->used = 0;
@@ -223,6 +375,7 @@ static void init_header(struct ctl_table_header *head,
 	head->unregistering = NULL;
 	head->root = root;
 	head->set = set;
+	head->ctx = ctx;
 	head->parent = NULL;
 	head->node = node;
 	INIT_HLIST_HEAD(&head->inodes);
@@ -982,7 +1135,7 @@ static struct ctl_dir *find_subdir(struct ctl_dir *dir,
 
 	if (!find_entry(&head, &index, dir, name, namelen))
 		return ERR_PTR(-ENOENT);
-	if (!S_ISDIR(sysctl_entry_mode(head, index)))
+	if (!sysctl_entry_is_dir(head, index))
 		return ERR_PTR(-ENOTDIR);
 	return container_of(head, struct ctl_dir, header);
 }
@@ -1007,7 +1160,8 @@ static struct ctl_dir *new_dir(struct ctl_table_set *set,
 	memcpy(new_name, name, namelen);
 	table[0].procname = new_name;
 	table[0].mode = S_IFDIR|S_IRUGO|S_IXUGO;
-	init_header(&new->header, set->dir.header.root, set, node, table, 1);
+	init_header(&new->header, set->dir.header.root, set, node, table, NULL,
+		    1, NULL);
 
 	return new;
 }
@@ -1188,6 +1342,10 @@ static int sysctl_check_table(const char *path, struct ctl_table_header *header)
 		entry = sysctl_entry_table(header, index, &table);
 		if (!entry->procname)
 			err |= sysctl_err(path, entry, "procname is null");
+		if (is_field_table(header) &&
+		    header->ctl_fields[index].type != SYSCTL_FIELD_NO_DATA &&
+		    !entry->data)
+			err |= sysctl_err(path, entry, "No data");
 		if ((entry->proc_handler == proc_dostring) ||
 		    (entry->proc_handler == proc_dobool) ||
 		    (entry->proc_handler == proc_dointvec) ||
@@ -1200,7 +1358,7 @@ static int sysctl_check_table(const char *path, struct ctl_table_header *header)
 		    (entry->proc_handler == proc_dointvec_ms_jiffies) ||
 		    (entry->proc_handler == proc_doulongvec_minmax) ||
 		    (entry->proc_handler == proc_doulongvec_ms_jiffies_minmax)) {
-			if (!entry->data)
+			if (!is_field_table(header) && !entry->data)
 				err |= sysctl_err(path, entry, "No data");
 			if (!entry->maxlen)
 				err |= sysctl_err(path, entry, "No maxlen");
@@ -1256,7 +1414,7 @@ static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table_
 		link++;
 	}
 	init_header(links, dir->header.root, dir->header.set, node, link_table,
-		    head->ctl_table_size);
+		    NULL, head->ctl_table_size, NULL);
 	links->nreg = head->ctl_table_size;
 
 	return links;
@@ -1280,10 +1438,10 @@ static bool get_links(struct ctl_dir *dir,
 		if (!find_entry(&tmp_head, &link_index, dir, procname,
 				strlen(procname)))
 			return false;
-		if (S_ISDIR(sysctl_entry_mode(tmp_head, link_index)) &&
-		    S_ISDIR(sysctl_entry_mode(header, index)))
+		if (sysctl_entry_is_dir(tmp_head, link_index) &&
+		    sysctl_entry_is_dir(header, index))
 			continue;
-		if (S_ISLNK(sysctl_entry_mode(tmp_head, link_index)) &&
+		if (sysctl_entry_is_link(tmp_head, link_index) &&
 		    tmp_head->ctl_table[link_index].data == link_root)
 			continue;
 		return false;
@@ -1370,18 +1528,25 @@ static struct ctl_dir *sysctl_mkdir_p(struct ctl_dir *dir, const char *path)
 }
 
 /**
- * __register_sysctl_table - register a leaf sysctl table
+ * __register_sysctl_table_internal - register a leaf sysctl table
  * @set: Sysctl tree to register on
  * @path: The path to the directory the sysctl table is in.
+ * @table: The top-level ctl_table array, or %NULL when registering @fields.
+ * @fields: The top-level sysctl_field array, or %NULL when registering @table.
+ * @table_size: The number of elements in @table or @fields.
+ * @ctx: Optional context used to resolve @fields entries.
+ * @ctx_size: Size of @ctx, including any wrapper object that embeds it.
  *
- * @table: the top-level table structure. This table should not be free'd
- *         after registration. So it should not be used on stack. It can either
- *         be a global or dynamically allocated by the caller and free'd later
- *         after sysctl unregistration.
- * @table_size : The number of elements in table
+ * Register a sysctl table hierarchy. One of @table or @fields must be
+ * provided. The descriptor array should not be freed after registration, so it
+ * should not be used on stack. It can either be global or dynamically
+ * allocated by the caller and freed later after sysctl unregistration.
  *
- * Register a sysctl table hierarchy. @table should be a filled in ctl_table
- * array.
+ * If @ctx points to a wrapper object, &struct sysctl_context must be the first
+ * member so @ctx can be copied together with the rest of that object.
+ * Data offsets stored in @fields are checked against @ctx->object_size and
+ * applied to the namespace selected by @ctx->ns. A context wrapper may provide
+ * @ctx->object for tables whose data belongs to another object.
  *
  * The members of the &struct ctl_table structure are used as follows:
  * procname - the name of the sysctl file under /proc/sys. Set to %NULL to not
@@ -1412,25 +1577,69 @@ static struct ctl_dir *sysctl_mkdir_p(struct ctl_dir *dir, const char *path)
  * This routine returns %NULL on a failure to register, and a pointer
  * to the table header on success.
  */
-struct ctl_table_header *__register_sysctl_table(
-	struct ctl_table_set *set,
-	const char *path, const struct ctl_table *table, size_t table_size)
+static struct ctl_table_header *
+__register_sysctl_table_internal(struct ctl_table_set *set, const char *path,
+				 const struct ctl_table *table,
+				 const struct sysctl_field *fields,
+				 size_t table_size,
+				 const struct sysctl_context *ctx, size_t ctx_size)
 {
 	struct ctl_table_root *root = set->dir.header.root;
 	struct ctl_table_header *header;
 	struct ctl_dir *dir;
 	struct ctl_node *node;
+	const struct sysctl_context *header_ctx = NULL;
+	size_t nodes_size;
 	size_t alloc_size;
+	size_t context_offset;
+
+	if (!!table == !!fields)
+		return NULL;
+
+	if (ctx && ctx_size < sizeof(*ctx))
+		return NULL;
+
+	if (!ctx && ctx_size)
+		return NULL;
+
+	if (fields && (!ctx || !ctx->object_size ||
+		       !sysctl_context_object(ctx)))
+		return NULL;
+
+	if (check_mul_overflow(sizeof(struct ctl_node), table_size, &nodes_size))
+		return NULL;
+
+	if (check_add_overflow(sizeof(*header), nodes_size, &context_offset))
+		return NULL;
+
+	/*
+	 * Store the copied context after the ctl_node array. struct sysctl_context
+	 * is the first member of any caller-defined wrapper, whose alignment
+	 * must not exceed that of struct sysctl_context.
+	 */
+	if (ctx) {
+		if (check_add_overflow(context_offset,
+				       __alignof__(*ctx) - 1, &context_offset))
+			return NULL;
+		context_offset = ALIGN_DOWN(context_offset, __alignof__(*ctx));
+	}
 
-	alloc_size = sizeof(struct ctl_table_header) +
-		     sizeof(struct ctl_node) * table_size;
+	if (check_add_overflow(context_offset, ctx_size, &alloc_size))
+		return NULL;
 
 	header = kzalloc(alloc_size, GFP_KERNEL_ACCOUNT);
 	if (!header)
 		return NULL;
 
 	node = (struct ctl_node *)(header + 1);
-	init_header(header, root, set, node, table, table_size);
+	if (ctx) {
+		header_ctx = (const struct sysctl_context *)((void *)header +
+							   context_offset);
+		memcpy((void *)header_ctx, ctx, ctx_size);
+	}
+
+	init_header(header, root, set, node, table, fields, table_size,
+		    header_ctx);
 	if (sysctl_check_table(path, header))
 		goto fail;
 
@@ -1460,6 +1669,25 @@ struct ctl_table_header *__register_sysctl_table(
 	return NULL;
 }
 
+struct ctl_table_header *
+__register_sysctl_fields(struct ctl_table_set *set, const char *path,
+			 const struct sysctl_field *fields, size_t field_count,
+			 const struct sysctl_context *ctx, size_t ctx_size)
+{
+	return __register_sysctl_table_internal(set, path, NULL, fields,
+						field_count, ctx, ctx_size);
+}
+EXPORT_SYMBOL(__register_sysctl_fields);
+
+struct ctl_table_header *
+__register_sysctl_table(struct ctl_table_set *set, const char *path,
+			const struct ctl_table *table, size_t table_size)
+{
+	return __register_sysctl_table_internal(set, path, table, NULL,
+						table_size, NULL, 0);
+}
+EXPORT_SYMBOL(__register_sysctl_table);
+
 /**
  * register_sysctl_sz - register a sysctl table
  * @path: The path to the directory the sysctl table is in. If the path
@@ -1548,7 +1776,7 @@ static void put_links(struct ctl_table_header *header)
 
 		if (link &&
 		    ((S_ISDIR(link->mode) &&
-		      S_ISDIR(sysctl_entry_mode(header, index))) ||
+		      sysctl_entry_is_dir(header, index)) ||
 		     (S_ISLNK(link->mode) && (link->data == root)))) {
 			drop_sysctl_table(link_head);
 		} else {
@@ -1604,7 +1832,7 @@ void setup_sysctl_set(struct ctl_table_set *set,
 {
 	memset(set, 0, sizeof(*set));
 	set->is_seen = is_seen;
-	init_header(&set->dir.header, root, set, NULL, root_table, 1);
+	init_header(&set->dir.header, root, set, NULL, root_table, NULL, 1, NULL);
 }
 
 void retire_sysctl_set(struct ctl_table_set *set)
include/linux/sysctl.h +129 -5

213 lines not on this page · raw message

Signature · 1 line
2.55.0
#21 lore raw

RFC PATCH 2/4 sysctl: ipc: use typed fields for IPC namespace sysctls

In reply to Alexey Gladkov

IPC sysctl registration clones the table and rewrites data and limit pointers for each namespace. This allocates a full ctl_table array per namespace and ties the fixup loop to the table layout.

Describe the table with ctl_field offsets into ipc_namespace instead. Keep the auto_msgmni entry data-less because its handler selects the effective value itself. This removes the per-namespace table allocation while preserving the existing handlers and limits.

---
 ipc/ipc_sysctl.c | 188 +++++++++++++++++++++--------------------------
 1 file changed, 82 insertions(+), 106 deletions(-)
ipc/ipc_sysctl.c +82 -106
index 9b087ebeb643..f017b4109cc8 100644
@@ -13,7 +13,6 @@
 #include <linux/capability.h>
 #include <linux/ipc_namespace.h>
 #include <linux/msg.h>
-#include <linux/slab.h>
 #include <linux/cred.h>
 #include "util.h"
 
@@ -41,6 +40,8 @@ static int proc_ipc_auto_msgmni(const struct ctl_table *table, int write,
 
 	memcpy(&ipc_table, table, sizeof(ipc_table));
 	ipc_table.data = &dummy;
+	ipc_table.extra1 = SYSCTL_ZERO;
+	ipc_table.extra2 = SYSCTL_ONE;
 
 	if (write)
 		pr_info_once("writing to auto_msgmni has no effect");
@@ -72,110 +73,129 @@ static int proc_ipc_sem_dointvec(const struct ctl_table *table, int write,
 int ipc_mni = IPCMNI;
 int ipc_mni_shift = IPCMNI_SHIFT;
 int ipc_min_cycle = RADIX_TREE_MAP_SIZE;
+static unsigned int ipc_mni_max = IPCMNI;
+static unsigned int ipc_uint_max = INT_MAX;
 
-static const struct ctl_table ipc_sysctls[] = {
+static const struct sysctl_field ipc_sysctls[] = {
 	{
 		.procname	= "shmmax",
-		.data		= &init_ipc_ns.shm_ctlmax,
-		.maxlen		= sizeof(init_ipc_ns.shm_ctlmax),
 		.mode		= 0644,
-		.proc_handler	= proc_doulongvec_minmax,
+		.type		= SYSCTL_FIELD_SIZE_T,
+		.data_offset	= SYSCTL_FIELD_SIZE_T_OFFSET(struct ipc_namespace,
+							     shm_ctlmax),
 	},
 	{
 		.procname	= "shmall",
-		.data		= &init_ipc_ns.shm_ctlall,
-		.maxlen		= sizeof(init_ipc_ns.shm_ctlall),
 		.mode		= 0644,
-		.proc_handler	= proc_doulongvec_minmax,
+		.type		= SYSCTL_FIELD_SIZE_T,
+		.data_offset	= SYSCTL_FIELD_SIZE_T_OFFSET(struct ipc_namespace,
+							     shm_ctlall),
 	},
 	{
 		.procname	= "shmmni",
-		.data		= &init_ipc_ns.shm_ctlmni,
-		.maxlen		= sizeof(init_ipc_ns.shm_ctlmni),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &ipc_mni,
+		.type		= SYSCTL_FIELD_INT_MINMAX,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct ipc_namespace,
+							  shm_ctlmni),
+		.int_limits = {
+			.min_value	= SYSCTL_ZERO,
+			.max_value	= &ipc_mni,
+		},
 	},
 	{
 		.procname	= "shm_rmid_forced",
-		.data		= &init_ipc_ns.shm_rmid_forced,
-		.maxlen		= sizeof(init_ipc_ns.shm_rmid_forced),
 		.mode		= 0644,
+		.type		= SYSCTL_FIELD_INT_MINMAX,
 		.proc_handler	= proc_ipc_dointvec_minmax_orphans,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct ipc_namespace,
+							  shm_rmid_forced),
+		.int_limits = {
+			.min_value	= SYSCTL_ZERO,
+			.max_value	= SYSCTL_ONE,
+		},
 	},
 	{
 		.procname	= "msgmax",
-		.data		= &init_ipc_ns.msg_ctlmax,
-		.maxlen		= sizeof(init_ipc_ns.msg_ctlmax),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
+		.type		= SYSCTL_FIELD_UINT_MINMAX,
+		.data_offset	= SYSCTL_FIELD_UINT_OFFSET(struct ipc_namespace,
+							   msg_ctlmax),
+		.uint_limits = {
+			.min_value	= SYSCTL_UINT_ZERO,
+			.max_value	= &ipc_uint_max,
+		},
 	},
 	{
 		.procname	= "msgmni",
-		.data		= &init_ipc_ns.msg_ctlmni,
-		.maxlen		= sizeof(init_ipc_ns.msg_ctlmni),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &ipc_mni,
+		.type		= SYSCTL_FIELD_UINT_MINMAX,
+		.data_offset	= SYSCTL_FIELD_UINT_OFFSET(struct ipc_namespace,
+							   msg_ctlmni),
+		.uint_limits = {
+			.min_value	= SYSCTL_UINT_ZERO,
+			.max_value	= &ipc_mni_max,
+		},
 	},
 	{
 		.procname	= "auto_msgmni",
-		.data		= NULL,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
+		.type		= SYSCTL_FIELD_NO_DATA,
 		.proc_handler	= proc_ipc_auto_msgmni,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.maxlen		= sizeof(int),
 	},
 	{
-		.procname	=  "msgmnb",
-		.data		= &init_ipc_ns.msg_ctlmnb,
-		.maxlen		= sizeof(init_ipc_ns.msg_ctlmnb),
+		.procname	= "msgmnb",
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
+		.type		= SYSCTL_FIELD_UINT_MINMAX,
+		.data_offset	= SYSCTL_FIELD_UINT_OFFSET(struct ipc_namespace,
+							   msg_ctlmnb),
+		.uint_limits = {
+			.min_value	= SYSCTL_UINT_ZERO,
+			.max_value	= &ipc_uint_max,
+		},
 	},
 	{
 		.procname	= "sem",
-		.data		= &init_ipc_ns.sem_ctls,
-		.maxlen		= 4*sizeof(int),
 		.mode		= 0644,
+		.type		= SYSCTL_FIELD_INT,
 		.proc_handler	= proc_ipc_sem_dointvec,
+		.maxlen		= 4 * sizeof(int),
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct ipc_namespace,
+							  sem_ctls[0]),
 	},
 #ifdef CONFIG_CHECKPOINT_RESTORE
 	{
 		.procname	= "sem_next_id",
-		.data		= &init_ipc_ns.ids[IPC_SEM_IDS].next_id,
-		.maxlen		= sizeof(init_ipc_ns.ids[IPC_SEM_IDS].next_id),
 		.mode		= 0444,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
+		.type		= SYSCTL_FIELD_INT_MINMAX,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct ipc_namespace,
+							  ids[IPC_SEM_IDS].next_id),
+		.int_limits = {
+			.min_value	= SYSCTL_ZERO,
+			.max_value	= SYSCTL_INT_MAX,
+		},
 	},
 	{
 		.procname	= "msg_next_id",
-		.data		= &init_ipc_ns.ids[IPC_MSG_IDS].next_id,
-		.maxlen		= sizeof(init_ipc_ns.ids[IPC_MSG_IDS].next_id),
 		.mode		= 0444,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
+		.type		= SYSCTL_FIELD_INT_MINMAX,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct ipc_namespace,
+							  ids[IPC_MSG_IDS].next_id),
+		.int_limits = {
+			.min_value	= SYSCTL_ZERO,
+			.max_value	= SYSCTL_INT_MAX,
+		},
 	},
 	{
 		.procname	= "shm_next_id",
-		.data		= &init_ipc_ns.ids[IPC_SHM_IDS].next_id,
-		.maxlen		= sizeof(init_ipc_ns.ids[IPC_SHM_IDS].next_id),
 		.mode		= 0444,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
+		.type		= SYSCTL_FIELD_INT_MINMAX,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct ipc_namespace,
+							  ids[IPC_SHM_IDS].next_id),
+		.int_limits = {
+			.min_value	= SYSCTL_ZERO,
+			.max_value	= SYSCTL_INT_MAX,
+		},
 	},
 #endif
 };
@@ -244,57 +264,16 @@ static struct ctl_table_root set_root = {
 
 bool setup_ipc_sysctls(struct ipc_namespace *ns)
 {
-	struct ctl_table *tbl;
+	struct sysctl_context ctx = {
+		.type = SYSCTL_CONTEXT_IPC_NS,
+		.object_size = sizeof(*ns),
+		.ns.ipc_ns = ns,
+	};
 
 	setup_sysctl_set(&ns->ipc_set, &set_root, set_is_seen);
-
-	tbl = kmemdup(ipc_sysctls, sizeof(ipc_sysctls), GFP_KERNEL);
-	if (tbl) {
-		int i;
-
-		for (i = 0; i < ARRAY_SIZE(ipc_sysctls); i++) {
-			if (tbl[i].data == &init_ipc_ns.shm_ctlmax)
-				tbl[i].data = &ns->shm_ctlmax;
-
-			else if (tbl[i].data == &init_ipc_ns.shm_ctlall)
-				tbl[i].data = &ns->shm_ctlall;
-
-			else if (tbl[i].data == &init_ipc_ns.shm_ctlmni)
-				tbl[i].data = &ns->shm_ctlmni;
-
-			else if (tbl[i].data == &init_ipc_ns.shm_rmid_forced)
-				tbl[i].data = &ns->shm_rmid_forced;
-
-			else if (tbl[i].data == &init_ipc_ns.msg_ctlmax)
-				tbl[i].data = &ns->msg_ctlmax;
-
-			else if (tbl[i].data == &init_ipc_ns.msg_ctlmni)
-				tbl[i].data = &ns->msg_ctlmni;
-
-			else if (tbl[i].data == &init_ipc_ns.msg_ctlmnb)
-				tbl[i].data = &ns->msg_ctlmnb;
-
-			else if (tbl[i].data == &init_ipc_ns.sem_ctls)
-				tbl[i].data = &ns->sem_ctls;
-#ifdef CONFIG_CHECKPOINT_RESTORE
-			else if (tbl[i].data == &init_ipc_ns.ids[IPC_SEM_IDS].next_id)
-				tbl[i].data = &ns->ids[IPC_SEM_IDS].next_id;
-
-			else if (tbl[i].data == &init_ipc_ns.ids[IPC_MSG_IDS].next_id)
-				tbl[i].data = &ns->ids[IPC_MSG_IDS].next_id;
-
-			else if (tbl[i].data == &init_ipc_ns.ids[IPC_SHM_IDS].next_id)
-				tbl[i].data = &ns->ids[IPC_SHM_IDS].next_id;
-#endif
-			else
-				tbl[i].data = NULL;
-		}
-
-		ns->ipc_sysctls = __register_sysctl_table(&ns->ipc_set, "kernel", tbl,
-							  ARRAY_SIZE(ipc_sysctls));
-	}
+	ns->ipc_sysctls =
+		register_sysctl_fields(&ns->ipc_set, "kernel", ipc_sysctls, &ctx);
 	if (!ns->ipc_sysctls) {
-		kfree(tbl);
 		retire_sysctl_set(&ns->ipc_set);
 		return false;
 	}
@@ -304,12 +283,8 @@ bool setup_ipc_sysctls(struct ipc_namespace *ns)
 
 void retire_ipc_sysctls(struct ipc_namespace *ns)
 {
-	const struct ctl_table *tbl;
-
-	tbl = ns->ipc_sysctls->ctl_table_arg;
 	unregister_sysctl_table(ns->ipc_sysctls);
 	retire_sysctl_set(&ns->ipc_set);
-	kfree(tbl);
 }
 
 static int __init ipc_sysctl_init(void)
@@ -326,6 +301,7 @@ device_initcall(ipc_sysctl_init);
 static int __init ipc_mni_extend(char *str)
 {
 	ipc_mni = IPCMNI_EXTEND;
+	ipc_mni_max = IPCMNI_EXTEND;
 	ipc_mni_shift = IPCMNI_EXTEND_SHIFT;
 	ipc_min_cycle = IPCMNI_EXTEND_MIN_CYCLE;
 	pr_info("IPCMNI extended to %d.\n", ipc_mni);
Signature · 1 line
2.55.0
#22 lore raw

RFC PATCH 3/4 sctp: use typed fields for per-net sysctls

In reply to Alexey Gladkov

SCTP clones its sysctl table for every network namespace and rewrites entries by index to install namespace data and dynamic limits. The index fixups depend on the table order and retain a full ctl_table allocation per namespace.

Replace the cloned table with static ctl_field descriptors using checked offsets into struct net. Keep the specialized handlers for values whose bounds depend on other SCTP settings, and let them construct the temporary ctl_table arguments required by the existing proc helpers.

---
 net/sctp/sysctl.c | 398 ++++++++++++++++++++++++----------------------
 1 file changed, 206 insertions(+), 192 deletions(-)
net/sctp/sysctl.c +206 -192
index 15e7db9a3ab2..030c7924be05 100644
@@ -25,9 +25,9 @@
 #include <net/sctp/sctp.h>
 #include <linux/sysctl.h>
 
-static int timer_max = 86400000; /* ms in one day */
-static int sack_timer_min = 1;
-static int sack_timer_max = 500;
+static unsigned int timer_max = 86400000; /* ms in one day */
+static unsigned int sack_timer_min = 1;
+static unsigned int sack_timer_max = 500;
 static int addr_scope_max = SCTP_SCOPE_POLICY_MAX;
 static int rwnd_scale_max = 16;
 static int rto_alpha_min = 0;
@@ -49,10 +49,18 @@ static int proc_sctp_do_rto_min(const struct ctl_table *ctl, int write,
 				void *buffer, size_t *lenp, loff_t *ppos);
 static int proc_sctp_do_rto_max(const struct ctl_table *ctl, int write, void *buffer,
 				size_t *lenp, loff_t *ppos);
+static int proc_sctp_do_pf_retrans(const struct ctl_table *ctl, int write,
+				   void *buffer, size_t *lenp, loff_t *ppos);
+static int proc_sctp_do_ps_retrans(const struct ctl_table *ctl, int write,
+				   void *buffer, size_t *lenp, loff_t *ppos);
 static int proc_sctp_do_udp_port(const struct ctl_table *ctl, int write, void *buffer,
 				 size_t *lenp, loff_t *ppos);
 static int proc_sctp_do_alpha_beta(const struct ctl_table *ctl, int write,
 				   void *buffer, size_t *lenp, loff_t *ppos);
+static int proc_sctp_do_alpha(const struct ctl_table *ctl, int write,
+			      void *buffer, size_t *lenp, loff_t *ppos);
+static int proc_sctp_do_beta(const struct ctl_table *ctl, int write,
+			     void *buffer, size_t *lenp, loff_t *ppos);
 static int proc_sctp_do_auth(const struct ctl_table *ctl, int write,
 			     void *buffer, size_t *lenp, loff_t *ppos);
 static int proc_sctp_do_probe_interval(const struct ctl_table *ctl, int write,
@@ -82,305 +90,285 @@ static struct ctl_table sctp_table[] = {
 	},
 };
 
-/* The following index defines are used in sctp_sysctl_net_register().
- * If you add new items to the sctp_net_table, please ensure that
- * the index values of these defines hold the same meaning indicated by
- * their macro names when they appear in sctp_net_table.
- */
-#define SCTP_RTO_MIN_IDX       0
-#define SCTP_RTO_MAX_IDX       1
-#define SCTP_PF_RETRANS_IDX    2
-#define SCTP_PS_RETRANS_IDX    3
-
-static struct ctl_table sctp_net_table[] = {
-	[SCTP_RTO_MIN_IDX] = {
+static const struct sysctl_field sctp_net_table[] = {
+	{
 		.procname	= "rto_min",
-		.data		= &init_net.sctp.rto_min,
-		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
+		.type		= SYSCTL_FIELD_UINT,
 		.proc_handler	= proc_sctp_do_rto_min,
-		.extra1         = SYSCTL_ONE,
-		.extra2         = &init_net.sctp.rto_max
+		.data_offset	= SYSCTL_FIELD_UINT_OFFSET(struct net, sctp.rto_min),
 	},
-	[SCTP_RTO_MAX_IDX] =  {
+	{
 		.procname	= "rto_max",
-		.data		= &init_net.sctp.rto_max,
-		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
+		.type		= SYSCTL_FIELD_UINT,
 		.proc_handler	= proc_sctp_do_rto_max,
-		.extra1         = &init_net.sctp.rto_min,
-		.extra2         = &timer_max
+		.data_offset	= SYSCTL_FIELD_UINT_OFFSET(struct net, sctp.rto_max),
 	},
-	[SCTP_PF_RETRANS_IDX] = {
+	{
 		.procname	= "pf_retrans",
-		.data		= &init_net.sctp.pf_retrans,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &init_net.sctp.ps_retrans,
+		.type		= SYSCTL_FIELD_INT,
+		.proc_handler	= proc_sctp_do_pf_retrans,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.pf_retrans),
 	},
-	[SCTP_PS_RETRANS_IDX] = {
+	{
 		.procname	= "ps_retrans",
-		.data		= &init_net.sctp.ps_retrans,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &init_net.sctp.pf_retrans,
-		.extra2		= &ps_retrans_max,
+		.type		= SYSCTL_FIELD_INT,
+		.proc_handler	= proc_sctp_do_ps_retrans,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.ps_retrans),
 	},
 	{
 		.procname	= "rto_initial",
-		.data		= &init_net.sctp.rto_initial,
-		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1         = SYSCTL_ONE,
-		.extra2         = &timer_max
+		.type		= SYSCTL_FIELD_UINT_MINMAX,
+		.data_offset	= SYSCTL_FIELD_UINT_OFFSET(struct net, sctp.rto_initial),
+		.uint_limits = {
+			.min_value	= SYSCTL_UINT_ONE,
+			.max_value	= &timer_max,
+		},
 	},
 	{
 		.procname	= "rto_alpha_exp_divisor",
-		.data		= &init_net.sctp.rto_alpha,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_sctp_do_alpha_beta,
-		.extra1		= &rto_alpha_min,
-		.extra2		= &rto_alpha_max,
+		.type		= SYSCTL_FIELD_INT,
+		.proc_handler	= proc_sctp_do_alpha,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.rto_alpha),
 	},
 	{
 		.procname	= "rto_beta_exp_divisor",
-		.data		= &init_net.sctp.rto_beta,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_sctp_do_alpha_beta,
-		.extra1		= &rto_beta_min,
-		.extra2		= &rto_beta_max,
+		.type		= SYSCTL_FIELD_INT,
+		.proc_handler	= proc_sctp_do_beta,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.rto_beta),
 	},
 	{
 		.procname	= "max_burst",
-		.data		= &init_net.sctp.max_burst,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
+		.type		= SYSCTL_FIELD_INT_MINMAX,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.max_burst),
+		.int_limits = {
+			.min_value	= SYSCTL_ZERO,
+			.max_value	= SYSCTL_INT_MAX,
+		},
 	},
 	{
 		.procname	= "cookie_preserve_enable",
-		.data		= &init_net.sctp.cookie_preserve_enable,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
+		.type		= SYSCTL_FIELD_INT,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.cookie_preserve_enable),
 	},
 	{
 		.procname	= "cookie_hmac_alg",
-		.data		= &init_net.sctp.cookie_auth_enable,
-		.maxlen		= 8,
 		.mode		= 0644,
+		.type		= SYSCTL_FIELD_INT,
 		.proc_handler	= proc_sctp_do_hmac_alg,
+		.maxlen		= 8,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.cookie_auth_enable),
 	},
 	{
 		.procname	= "valid_cookie_life",
-		.data		= &init_net.sctp.valid_cookie_life,
-		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1         = SYSCTL_ONE,
-		.extra2         = &timer_max
+		.type		= SYSCTL_FIELD_UINT_MINMAX,
+		.data_offset	= SYSCTL_FIELD_UINT_OFFSET(struct net, sctp.valid_cookie_life),
+		.uint_limits = {
+			.min_value	= SYSCTL_UINT_ONE,
+			.max_value	= &timer_max,
+		},
 	},
 	{
 		.procname	= "sack_timeout",
-		.data		= &init_net.sctp.sack_timeout,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1         = &sack_timer_min,
-		.extra2         = &sack_timer_max,
+		.type		= SYSCTL_FIELD_UINT_MINMAX,
+		.data_offset	= SYSCTL_FIELD_UINT_OFFSET(struct net, sctp.sack_timeout),
+		.uint_limits = {
+			.min_value	= &sack_timer_min,
+			.max_value	= &sack_timer_max,
+		},
 	},
 	{
 		.procname	= "hb_interval",
-		.data		= &init_net.sctp.hb_interval,
-		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1         = SYSCTL_ONE,
-		.extra2         = &timer_max
+		.type		= SYSCTL_FIELD_UINT_MINMAX,
+		.data_offset	= SYSCTL_FIELD_UINT_OFFSET(struct net, sctp.hb_interval),
+		.uint_limits = {
+			.min_value	= SYSCTL_UINT_ONE,
+			.max_value	= &timer_max,
+		},
 	},
 	{
 		.procname	= "association_max_retrans",
-		.data		= &init_net.sctp.max_retrans_association,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= SYSCTL_INT_MAX,
+		.type		= SYSCTL_FIELD_INT_MINMAX,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.max_retrans_association),
+		.int_limits = {
+			.min_value	= SYSCTL_ONE,
+			.max_value	= SYSCTL_INT_MAX,
+		},
 	},
 	{
 		.procname	= "path_max_retrans",
-		.data		= &init_net.sctp.max_retrans_path,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= SYSCTL_INT_MAX,
+		.type		= SYSCTL_FIELD_INT_MINMAX,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.max_retrans_path),
+		.int_limits = {
+			.min_value	= SYSCTL_ONE,
+			.max_value	= SYSCTL_INT_MAX,
+		},
 	},
 	{
 		.procname	= "max_init_retransmits",
-		.data		= &init_net.sctp.max_retrans_init,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= SYSCTL_INT_MAX,
+		.type		= SYSCTL_FIELD_INT_MINMAX,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.max_retrans_init),
+		.int_limits = {
+			.min_value	= SYSCTL_ONE,
+			.max_value	= SYSCTL_INT_MAX,
+		},
 	},
 	{
 		.procname	= "sndbuf_policy",
-		.data		= &init_net.sctp.sndbuf_policy,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
+		.type		= SYSCTL_FIELD_INT,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.sndbuf_policy),
 	},
 	{
 		.procname	= "rcvbuf_policy",
-		.data		= &init_net.sctp.rcvbuf_policy,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
+		.type		= SYSCTL_FIELD_INT,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.rcvbuf_policy),
 	},
 	{
 		.procname	= "default_auto_asconf",
-		.data		= &init_net.sctp.default_auto_asconf,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
+		.type		= SYSCTL_FIELD_INT,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.default_auto_asconf),
 	},
 	{
 		.procname	= "addip_enable",
-		.data		= &init_net.sctp.addip_enable,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
+		.type		= SYSCTL_FIELD_INT,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.addip_enable),
 	},
 	{
 		.procname	= "addip_noauth_enable",
-		.data		= &init_net.sctp.addip_noauth,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
+		.type		= SYSCTL_FIELD_INT,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.addip_noauth),
 	},
 	{
 		.procname	= "prsctp_enable",
-		.data		= &init_net.sctp.prsctp_enable,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
+		.type		= SYSCTL_FIELD_INT,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.prsctp_enable),
 	},
 	{
 		.procname	= "reconf_enable",
-		.data		= &init_net.sctp.reconf_enable,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
+		.type		= SYSCTL_FIELD_INT,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.reconf_enable),
 	},
 	{
 		.procname	= "auth_enable",
-		.data		= &init_net.sctp.auth_enable,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
+		.type		= SYSCTL_FIELD_INT,
 		.proc_handler	= proc_sctp_do_auth,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.auth_enable),
 	},
 	{
 		.procname	= "intl_enable",
-		.data		= &init_net.sctp.intl_enable,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
+		.type		= SYSCTL_FIELD_INT,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.intl_enable),
 	},
 	{
 		.procname	= "ecn_enable",
-		.data		= &init_net.sctp.ecn_enable,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
+		.type		= SYSCTL_FIELD_INT,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.ecn_enable),
 	},
 	{
 		.procname	= "plpmtud_probe_interval",
-		.data		= &init_net.sctp.probe_interval,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
+		.type		= SYSCTL_FIELD_UINT,
 		.proc_handler	= proc_sctp_do_probe_interval,
+		.data_offset	= SYSCTL_FIELD_UINT_OFFSET(struct net, sctp.probe_interval),
 	},
 	{
 		.procname	= "udp_port",
-		.data		= &init_net.sctp.udp_port,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
+		.type		= SYSCTL_FIELD_INT,
 		.proc_handler	= proc_sctp_do_udp_port,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &udp_port_max,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.udp_port),
 	},
 	{
 		.procname	= "encap_port",
-		.data		= &init_net.sctp.encap_port,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &udp_port_max,
+		.type		= SYSCTL_FIELD_INT_MINMAX,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.encap_port),
+		.int_limits = {
+			.min_value	= SYSCTL_ZERO,
+			.max_value	= &udp_port_max,
+		},
 	},
 	{
 		.procname	= "addr_scope_policy",
-		.data		= &init_net.sctp.scope_policy,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &addr_scope_max,
+		.type		= SYSCTL_FIELD_INT_MINMAX,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.scope_policy),
+		.int_limits = {
+			.min_value	= SYSCTL_ZERO,
+			.max_value	= &addr_scope_max,
+		},
 	},
 	{
 		.procname	= "rwnd_update_shift",
-		.data		= &init_net.sctp.rwnd_upd_shift,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= &rwnd_scale_max,
+		.type		= SYSCTL_FIELD_INT_MINMAX,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.rwnd_upd_shift),
+		.int_limits = {
+			.min_value	= SYSCTL_ONE,
+			.max_value	= &rwnd_scale_max,
+		},
 	},
 	{
 		.procname	= "max_autoclose",
-		.data		= &init_net.sctp.max_autoclose,
-		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
-		.proc_handler	= &proc_doulongvec_minmax,
-		.extra1		= &max_autoclose_min,
-		.extra2		= &max_autoclose_max,
+		.type		= SYSCTL_FIELD_ULONG_MINMAX,
+		.data_offset	= SYSCTL_FIELD_ULONG_OFFSET(struct net, sctp.max_autoclose),
+		.ulong_limits = {
+			.min_value	= &max_autoclose_min,
+			.max_value	= &max_autoclose_max,
+		},
 	},
 #ifdef CONFIG_NET_L3_MASTER_DEV
 	{
 		.procname	= "l3mdev_accept",
-		.data		= &init_net.sctp.l3mdev_accept,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.type		= SYSCTL_FIELD_INT_MINMAX,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.l3mdev_accept),
+		.int_limits = {
+			.min_value	= SYSCTL_ZERO,
+			.max_value	= SYSCTL_ONE,
+		},
 	},
 #endif
 	{
 		.procname	= "pf_enable",
-		.data		= &init_net.sctp.pf_enable,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
+		.type		= SYSCTL_FIELD_INT,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.pf_enable),
 	},
 	{
 		.procname	= "pf_expose",
-		.data		= &init_net.sctp.pf_expose,
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &pf_expose_max,
+		.type		= SYSCTL_FIELD_INT_MINMAX,
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct net, sctp.pf_expose),
+		.int_limits = {
+			.min_value	= SYSCTL_ZERO,
+			.max_value	= &pf_expose_max,
+		},
 	},
 };
 
@@ -423,8 +411,6 @@ static int proc_sctp_do_rto_min(const struct ctl_table *ctl, int write,
 				void *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct net *net = container_of(ctl->data, struct net, sctp.rto_min);
-	unsigned int min = *(unsigned int *) ctl->extra1;
-	unsigned int max = *(unsigned int *) ctl->extra2;
 	struct ctl_table tbl;
 	int ret, new_value;
 
@@ -438,7 +424,7 @@ static int proc_sctp_do_rto_min(const struct ctl_table *ctl, int write,
 
 	ret = proc_dointvec(&tbl, write, buffer, lenp, ppos);
 	if (write && ret == 0) {
-		if (new_value > max || new_value < min)
+		if (new_value > net->sctp.rto_max || new_value < 1)
 			return -EINVAL;
 
 		net->sctp.rto_min = new_value;
@@ -451,8 +437,6 @@ static int proc_sctp_do_rto_max(const struct ctl_table *ctl, int write,
 				void *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct net *net = container_of(ctl->data, struct net, sctp.rto_max);
-	unsigned int min = *(unsigned int *) ctl->extra1;
-	unsigned int max = *(unsigned int *) ctl->extra2;
 	struct ctl_table tbl;
 	int ret, new_value;
 
@@ -466,7 +450,7 @@ static int proc_sctp_do_rto_max(const struct ctl_table *ctl, int write,
 
 	ret = proc_dointvec(&tbl, write, buffer, lenp, ppos);
 	if (write && ret == 0) {
-		if (new_value > max || new_value < min)
+		if (new_value > timer_max || new_value < net->sctp.rto_min)
 			return -EINVAL;
 
 		net->sctp.rto_max = new_value;
@@ -475,6 +459,30 @@ static int proc_sctp_do_rto_max(const struct ctl_table *ctl, int write,
 	return ret;
 }
 
+static int proc_sctp_do_pf_retrans(const struct ctl_table *ctl, int write,
+				   void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct net *net = container_of(ctl->data, struct net, sctp.pf_retrans);
+	struct ctl_table table = *ctl;
+
+	table.extra1 = SYSCTL_ZERO;
+	table.extra2 = &net->sctp.ps_retrans;
+
+	return proc_dointvec_minmax(&table, write, buffer, lenp, ppos);
+}
+
+static int proc_sctp_do_ps_retrans(const struct ctl_table *ctl, int write,
+				   void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct net *net = container_of(ctl->data, struct net, sctp.ps_retrans);
+	struct ctl_table table = *ctl;
+
+	table.extra1 = &net->sctp.pf_retrans;
+	table.extra2 = &ps_retrans_max;
+
+	return proc_dointvec_minmax(&table, write, buffer, lenp, ppos);
+}
+
 static int proc_sctp_do_alpha_beta(const struct ctl_table *ctl, int write,
 				   void *buffer, size_t *lenp, loff_t *ppos)
 {
@@ -485,6 +493,28 @@ static int proc_sctp_do_alpha_beta(const struct ctl_table *ctl, int write,
 	return proc_dointvec_minmax(ctl, write, buffer, lenp, ppos);
 }
 
+static int proc_sctp_do_alpha(const struct ctl_table *ctl, int write,
+			      void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct ctl_table table = *ctl;
+
+	table.extra1 = &rto_alpha_min;
+	table.extra2 = &rto_alpha_max;
+
+	return proc_sctp_do_alpha_beta(&table, write, buffer, lenp, ppos);
+}
+
+static int proc_sctp_do_beta(const struct ctl_table *ctl, int write,
+			     void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct ctl_table table = *ctl;
+
+	table.extra1 = &rto_beta_min;
+	table.extra2 = &rto_beta_max;
+
+	return proc_sctp_do_alpha_beta(&table, write, buffer, lenp, ppos);
+}
+
 static int proc_sctp_do_auth(const struct ctl_table *ctl, int write,
 			     void *buffer, size_t *lenp, loff_t *ppos)
 {
@@ -520,8 +550,6 @@ static int proc_sctp_do_udp_port(const struct ctl_table *ctl, int write,
 				 void *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct net *net = container_of(ctl->data, struct net, sctp.udp_port);
-	unsigned int min = *(unsigned int *)ctl->extra1;
-	unsigned int max = *(unsigned int *)ctl->extra2;
 	struct ctl_table tbl;
 	int ret, new_value;
 
@@ -537,7 +565,7 @@ static int proc_sctp_do_udp_port(const struct ctl_table *ctl, int write,
 	if (write && ret == 0) {
 		struct sock *sk = net->sctp.ctl_sock;
 
-		if (new_value > max || new_value < min)
+		if (new_value > udp_port_max || new_value < 0)
 			return -EINVAL;
 
 		mutex_lock(&sctp_sysctl_mutex);
@@ -588,38 +616,24 @@ static int proc_sctp_do_probe_interval(const struct ctl_table *ctl, int write,
 
 int sctp_sysctl_net_register(struct net *net)
 {
-	size_t table_size = ARRAY_SIZE(sctp_net_table);
-	struct ctl_table *table;
-	int i;
-
-	table = kmemdup(sctp_net_table, sizeof(sctp_net_table), GFP_KERNEL);
-	if (!table)
+	struct sysctl_context ctx = {
+		.type = SYSCTL_CONTEXT_NET_NS,
+		.object_size = sizeof(init_net),
+		.ns.net_ns = net,
+	};
+
+	net->sctp.sysctl_header =
+		register_sysctl_fields(&net->sysctls, "net/sctp", sctp_net_table,
+				       &ctx);
+	if (!net->sctp.sysctl_header)
 		return -ENOMEM;
 
-	for (i = 0; i < table_size; i++)
-		table[i].data += (char *)(&net->sctp) - (char *)&init_net.sctp;
-
-	table[SCTP_RTO_MIN_IDX].extra2 = &net->sctp.rto_max;
-	table[SCTP_RTO_MAX_IDX].extra1 = &net->sctp.rto_min;
-	table[SCTP_PF_RETRANS_IDX].extra2 = &net->sctp.ps_retrans;
-	table[SCTP_PS_RETRANS_IDX].extra1 = &net->sctp.pf_retrans;
-
-	net->sctp.sysctl_header = register_net_sysctl_sz(net, "net/sctp",
-							 table, table_size);
-	if (net->sctp.sysctl_header == NULL) {
-		kfree(table);
-		return -ENOMEM;
-	}
 	return 0;
 }
 
 void sctp_sysctl_net_unregister(struct net *net)
 {
-	const struct ctl_table *table;
-
-	table = net->sctp.sysctl_header->ctl_table_arg;
 	unregister_net_sysctl_table(net->sctp.sysctl_header);
-	kfree(table);
 }
 
 static struct ctl_table_header *sctp_sysctl_header;
Signature · 1 line
2.55.0
#23 lore raw

RFC PATCH 4/4 mpls: use typed fields for per-device sysctls

In reply to Alexey Gladkov

MPLS clones its device sysctl table and converts relative data pointers into mpls_dev addresses at registration time. It also stores the device and network namespace in extra1 and extra2 for the handler.

Use a context wrapper to retain both objects and select mpls_dev as the backing object for typed field offsets. The static field table can then be shared by all devices without allocating and rewriting a ctl_table array.

---
 net/mpls/af_mpls.c | 66 ++++++++++++++++++++++------------------------
 1 file changed, 31 insertions(+), 35 deletions(-)
net/mpls/af_mpls.c +31 -35
index 26340a7306b5..725df199cf1d 100644
@@ -1387,8 +1387,18 @@ static int mpls_netconf_dump_devconf(struct sk_buff *skb,
 	return err;
 }
 
-#define MPLS_PERDEV_SYSCTL_OFFSET(field)	\
-	(&((struct mpls_dev *)0)->field)
+struct mpls_dev_sysctl_context {
+	struct sysctl_context context;
+	struct mpls_dev *mdev;
+};
+
+static void *mpls_dev_sysctl_object(const struct sysctl_context *ctx)
+{
+	const struct mpls_dev_sysctl_context *mpls_ctx;
+
+	mpls_ctx = container_of(ctx, struct mpls_dev_sysctl_context, context);
+	return mpls_ctx->mdev;
+}
 
 static int mpls_conf_proc(const struct ctl_table *ctl, int write,
 			  void *buffer, size_t *lenp, loff_t *ppos)
@@ -1397,14 +1407,12 @@ static int mpls_conf_proc(const struct ctl_table *ctl, int write,
 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
 
 	if (write) {
-		struct mpls_dev *mdev = ctl->extra1;
-		int i = (int *)ctl->data - (int *)mdev;
-		struct net *net = ctl->extra2;
+		struct mpls_dev *mdev;
 		int val = *(int *)ctl->data;
 
-		if (i == offsetof(struct mpls_dev, input_enabled) &&
-		    val != oval) {
-			mpls_netconf_notify_devconf(net, RTM_NEWNETCONF,
+		mdev = container_of(ctl->data, struct mpls_dev, input_enabled);
+		if (val != oval) {
+			mpls_netconf_notify_devconf(dev_net(mdev->dev), RTM_NEWNETCONF,
 						    NETCONFA_INPUT, mdev);
 		}
 	}
@@ -1412,13 +1420,13 @@ static int mpls_conf_proc(const struct ctl_table *ctl, int write,
 	return ret;
 }
 
-static const struct ctl_table mpls_dev_table[] = {
+static const struct sysctl_field mpls_dev_table[] = {
 	{
 		.procname	= "input",
-		.maxlen		= sizeof(int),
 		.mode		= 0644,
+		.type		= SYSCTL_FIELD_INT,
 		.proc_handler	= mpls_conf_proc,
-		.data		= MPLS_PERDEV_SYSCTL_OFFSET(input_enabled),
+		.data_offset	= SYSCTL_FIELD_INT_OFFSET(struct mpls_dev, input_enabled),
 	},
 };
 
@@ -1426,35 +1434,27 @@ static int mpls_dev_sysctl_register(struct net_device *dev,
 				    struct mpls_dev *mdev)
 {
 	char path[sizeof("net/mpls/conf/") + IFNAMSIZ];
-	size_t table_size = ARRAY_SIZE(mpls_dev_table);
 	struct net *net = dev_net(dev);
-	struct ctl_table *table;
-	int i;
-
-	table = kmemdup(&mpls_dev_table, sizeof(mpls_dev_table), GFP_KERNEL);
-	if (!table)
-		goto out;
-
-	/* Table data contains only offsets relative to the base of
-	 * the mdev at this point, so make them absolute.
-	 */
-	for (i = 0; i < table_size; i++) {
-		table[i].data = (char *)mdev + (uintptr_t)table[i].data;
-		table[i].extra1 = mdev;
-		table[i].extra2 = net;
-	}
+	struct mpls_dev_sysctl_context ctx = {
+		.context = {
+			.object_size = sizeof(*mdev),
+			.object = mpls_dev_sysctl_object,
+		},
+		.mdev = mdev,
+	};
 
 	snprintf(path, sizeof(path), "net/mpls/conf/%s", dev->name);
 
-	mdev->sysctl = register_net_sysctl_sz(net, path, table, table_size);
+	mdev->sysctl =
+		__register_sysctl_fields(&net->sysctls, path, mpls_dev_table,
+					 ARRAY_SIZE(mpls_dev_table), &ctx.context,
+					 sizeof(ctx));
 	if (!mdev->sysctl)
-		goto free;
+		goto out;
 
 	mpls_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL, mdev);
 	return 0;
 
-free:
-	kfree(table);
 out:
 	mdev->sysctl = NULL;
 	return -ENOBUFS;
@@ -1464,14 +1464,10 @@ static void mpls_dev_sysctl_unregister(struct net_device *dev,
 				       struct mpls_dev *mdev)
 {
 	struct net *net = dev_net(dev);
-	const struct ctl_table *table;
-
 	if (!mdev->sysctl)
 		return;
 
-	table = mdev->sysctl->ctl_table_arg;
 	unregister_net_sysctl_table(mdev->sysctl);
-	kfree(table);
 
 	mpls_netconf_notify_devconf(net, RTM_DELNETCONF, 0, mdev);
 }
Signature · 1 line
2.55.0
#24 lore raw

Re: [RFC PATCH v1 17/30] sysctl: net: use sysctl_field in SCTP sysctls

In reply to Alexey Gladkov
Quoted 2 lines · Alexey Gladkov

Does this look better for you, or should I just stop?

This certainly looks a lot less disruptive, and seems to allow subsystems to migrate to the new model incrementally.

The conversion patches for ipc and mpls looked sane to me. I imagine the other various cases would be similar. So I don't see any huge issues here - obviously the affected subsystem maintainers may have some (I suspect mainly networking), but the whole "avoid having to copying the tables" is certainly a worthy goal.

Linus

maintainer
#25 lore raw

Re: [RFC PATCH v1 17/30] sysctl: net: use sysctl_field in SCTP sysctls

In reply to Alexey Gladkov
Quoted 93 lines · Alexey Gladkov
Quoted 4 lines · Linus Torvalds

I detest this series.

Maybe it makes technical sense, but as long as it makes no human visual sense, I'm NAK'ing it as being garbage.

You are right that my approach added too many macros and made the tables harder to read. Sorry.

Quoted 84 lines · Alexey Gladkov
Quoted 1 line

[..]

The whole series is full of complete illegible noise like this:

net/sctp/sysctl.c static struct ctl_table sctp_table[] = { · line 153

Quoted 12 lines
net/sctp/sysctl.c +12 -0
+static const struct sysctl_field sctp_net_table[] = {
+       SYSCTL_FIELD_CUSTOM("rto_min", 0644, sizeof(unsigned int),
+                        sctp_rto_min_data, proc_sctp_do_rto_min),
+       SYSCTL_FIELD_CUSTOM("rto_max", 0644, sizeof(unsigned int),
+                        sctp_rto_max_data, proc_sctp_do_rto_max),
+       SYSCTL_FIELD_INT_MINMAX("pf_retrans", 0644, sctp_pf_retrans_data,
+                            SYSCTL_ZERO, sctp_ps_retrans_data),
+       SYSCTL_FIELD_INT_MINMAX("ps_retrans", 0644, sctp_ps_retrans_data,
+                            sctp_pf_retrans_data, sctp_ps_retrans_max_data),
+       SYSCTL_FIELD_STATIC_UINT_MINMAX("rto_initial", 0644,
+                                    sctp_rto_initial_data,
+                                    SYSCTL_UINT_ONE, &timer_max),

[...]

where apparently the indentation has been decided by a rodent on crack who was given an Ouija board and instructed to ask his dead ancestors what indentation to use.

So no.

That kind of complete random code is simply not acceptable.

I don't know what the correct answer is, but it is *not* this series. It needs to be consistent and visually parseable by humans *without* asking your dead ancestors for help.

And it's not just the indentation. That SYSCTL_FIELD_CUSTOM() thing needs to be usable and able to do some minimal type checking - not just passed a random sizeof() in a random argumentt. I'd suggest passing the actual type, and then checking that the type *matches* the data pointer it is passed too.

The old code may be ugly too, and have various other warts, but at least it had somewhat legible and understandable initializers:

{
.procname = "prsctp_enable",
.data = &init_net.sctp.prsctp_enable,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec,
},

is at least something that can be read by a human and those things had consistent whitespace rather than some quantum randomness.

And this is also just complete line noise that only makes the code worse:

    +#define SCTP_DATA(type, field)                                         \
    +static type *sctp_ ## field ## _data(const struct sysctl_context *ctx) \
    +{                                                                      \
    +       return &ctx->ns.net_ns->sctp.field;                             \
    +}
    +
    +#define SCTP_CUSTOM_DATA(field)
             \
    +static void *sctp_ ## field ## _data(const struct sysctl_context *ctx) \
    +{                                                                      \
    +       return &ctx->ns.net_ns->sctp.field;                             \
    +}
    +
    +SCTP_CUSTOM_DATA(rto_min)
    +SCTP_CUSTOM_DATA(rto_max)
    +SCTP_DATA(int, pf_retrans)
    +SCTP_DATA(int, ps_retrans)
    +SCTP_DATA(unsigned int, rto_initial)
    [...]

If we're doing these kinds of changes, the end result has to look *BETTER* than the thing it replaces, not worse.

Yes, a few of the patches did look better. But the majority of them only looked worse. Some of it should be easy to fix: use consistent whitespace, and use sane argument ordering.

But honestly, the old setup didn't *rely* on argument ordering, and used named initializers to make things more legible and robust.

Quoted 8 lines
Quoted 5 lines

So I suspect that should be what you should aim for in the new setup too, and that probably means "completely different approach".

Linus

I tried an alternative based on offsets. The following four patches add the infrastructure and convert IPC, SCTP, and MPLS as examples.

The last 4 patches landed intertwined with the initial 30+ that you sent. Will you send a V2 with just the latest 4?

Best

#26 lore raw

Re: [RFC PATCH v1 17/30] sysctl: net: use sysctl_field in SCTP sysctls

Quoted 97 lines · Joel Granados
Quoted 96 lines · Alexey Gladkov
Quoted 4 lines · Linus Torvalds

I detest this series.

Maybe it makes technical sense, but as long as it makes no human visual sense, I'm NAK'ing it as being garbage.

You are right that my approach added too many macros and made the tables harder to read. Sorry.

Quoted 87 lines · Alexey Gladkov
Quoted 1 line

[..]

The whole series is full of complete illegible noise like this:

net/sctp/sysctl.c static struct ctl_table sctp_table[] = { · line 153

Quoted 12 lines
net/sctp/sysctl.c +12 -0
+static const struct sysctl_field sctp_net_table[] = {
+       SYSCTL_FIELD_CUSTOM("rto_min", 0644, sizeof(unsigned int),
+                        sctp_rto_min_data, proc_sctp_do_rto_min),
+       SYSCTL_FIELD_CUSTOM("rto_max", 0644, sizeof(unsigned int),
+                        sctp_rto_max_data, proc_sctp_do_rto_max),
+       SYSCTL_FIELD_INT_MINMAX("pf_retrans", 0644, sctp_pf_retrans_data,
+                            SYSCTL_ZERO, sctp_ps_retrans_data),
+       SYSCTL_FIELD_INT_MINMAX("ps_retrans", 0644, sctp_ps_retrans_data,
+                            sctp_pf_retrans_data, sctp_ps_retrans_max_data),
+       SYSCTL_FIELD_STATIC_UINT_MINMAX("rto_initial", 0644,
+                                    sctp_rto_initial_data,
+                                    SYSCTL_UINT_ONE, &timer_max),

[...]

where apparently the indentation has been decided by a rodent on crack who was given an Ouija board and instructed to ask his dead ancestors what indentation to use.

So no.

That kind of complete random code is simply not acceptable.

I don't know what the correct answer is, but it is *not* this series. It needs to be consistent and visually parseable by humans *without* asking your dead ancestors for help.

And it's not just the indentation. That SYSCTL_FIELD_CUSTOM() thing needs to be usable and able to do some minimal type checking - not just passed a random sizeof() in a random argumentt. I'd suggest passing the actual type, and then checking that the type *matches* the data pointer it is passed too.

The old code may be ugly too, and have various other warts, but at least it had somewhat legible and understandable initializers:

{
.procname = "prsctp_enable",
.data = &init_net.sctp.prsctp_enable,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec,
},

is at least something that can be read by a human and those things had consistent whitespace rather than some quantum randomness.

And this is also just complete line noise that only makes the code worse:

    +#define SCTP_DATA(type, field)                                         \
    +static type *sctp_ ## field ## _data(const struct sysctl_context *ctx) \
    +{                                                                      \
    +       return &ctx->ns.net_ns->sctp.field;                             \
    +}
    +
    +#define SCTP_CUSTOM_DATA(field)
             \
    +static void *sctp_ ## field ## _data(const struct sysctl_context *ctx) \
    +{                                                                      \
    +       return &ctx->ns.net_ns->sctp.field;                             \
    +}
    +
    +SCTP_CUSTOM_DATA(rto_min)
    +SCTP_CUSTOM_DATA(rto_max)
    +SCTP_DATA(int, pf_retrans)
    +SCTP_DATA(int, ps_retrans)
    +SCTP_DATA(unsigned int, rto_initial)
    [...]

If we're doing these kinds of changes, the end result has to look *BETTER* than the thing it replaces, not worse.

Yes, a few of the patches did look better. But the majority of them only looked worse. Some of it should be easy to fix: use consistent whitespace, and use sane argument ordering.

But honestly, the old setup didn't *rely* on argument ordering, and used named initializers to make things more legible and robust.

So I suspect that should be what you should aim for in the new setup too, and that probably means "completely different approach".

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

Linus

I tried an alternative based on offsets. The following four patches add the infrastructure and convert IPC, SCTP, and MPLS as examples.

The last 4 patches landed intertwined with the initial 30+ that you sent. Will you send a V2 with just the latest 4?

Yes, I want to prepare a patchset with a new implementation this weekend.

I want to split the patchset into several parts to make the review easier. First, I want to create patches for proc_sysctls that add a new mechanism and a few simple users, such as ipc_sysctls and mq_sysctls. Then I’ll create a patchset for the network subsystem, ucounts and other large users of dynamic sysctls.

I need to rebase to the current master branch because sashiko can't verify my patches.

Signature · 1 line
Rgrds, legion
#27 lore raw

RFC PATCH 18/30 sysctl: net: use sysctl_field in core IPv6 sysctls

In reply to a message not in the archive

The core IPv6, route and ICMP sysctl tables are per-netns, but they currently get there by cloning ctl_table arrays and patching each data pointer after allocation. That keeps writable table copies around for every net namespace and makes the code depend on table indexes staying in sync with the patch-up code.

Describe these tables with sysctl_field instead. The data pointers are now derived from the registration context, so the tables can stay static and const while still resolving to the right net namespace.

Keep the few non-standard handlers as custom fields. The route flush handler now derives the net namespace from its data pointer instead of using extra1, and the multipath and wide IOAM handlers keep their limits inside the handler-local ctl_table copy.

---
 include/net/ipv6.h         |   6 +-
 net/ipv6/icmp.c            | 127 ++++++---------
 net/ipv6/route.c           | 173 ++++++++------------
 net/ipv6/sysctl_net_ipv6.c | 322 ++++++++++++-------------------------
 4 files changed, 224 insertions(+), 404 deletions(-)
include/net/ipv6.h +2 -4
index 1dec81faff28..08ede58aabfb 100644
@@ -1207,10 +1207,8 @@ static inline int snmp6_unregister_dev(struct inet6_dev *idev) { return 0; }
 #endif
 
 #ifdef CONFIG_SYSCTL
-struct ctl_table *ipv6_icmp_sysctl_init(struct net *net);
-size_t ipv6_icmp_sysctl_table_size(void);
-struct ctl_table *ipv6_route_sysctl_init(struct net *net);
-size_t ipv6_route_sysctl_table_size(struct net *net);
+int ipv6_icmp_sysctl_register(struct sysctl_context *ctx);
+int ipv6_route_sysctl_register(struct sysctl_context *ctx);
 int ipv6_sysctl_register(void);
 void ipv6_sysctl_unregister(void);
 #endif
net/ipv6/icmp.c +51 -76

138 lines not on this page · raw message

net/ipv6/route.c +64 -109

191 lines not on this page · raw message

net/ipv6/sysctl_net_ipv6.c +107 -215

386 lines not on this page · raw message

Signature · 1 line
2.55.0
#28 lore raw

RFC PATCH 19/30 sysctl: net: use sysctl_field in net core per-net sysctls

In reply to a message not in the archive

The net core per-net sysctl table is cloned for every non-init network namespace so that the per-net entries can be patched to point at the current struct net and the global buffer limit entries can be made read-only.

Describe the table with sysctl_field instead. The per-net entries now derive their storage from the registration context, and the global buffer limit entries keep their init-net-only write permission through a mode callback.

This keeps the table static and const while preserving the existing permission model.

---
 net/core/sysctl_net_core.c | 241 ++++++++++++++++++-------------------
 1 file changed, 116 insertions(+), 125 deletions(-)
net/core/sysctl_net_core.c +116 -125
index b508618bfc12..39b112a302c2 100644
@@ -678,100 +678,118 @@ static struct ctl_table net_core_table[] = {
 	},
 };
 
-static struct ctl_table netns_core_table[] = {
+static umode_t netns_core_init_net_writable_mode(const struct sysctl_context *ctx)
+{
+	return net_eq(ctx->ns.net_ns, &init_net) ? 0644 : 0444;
+}
+
+#define NETNS_CORE_SYSCTL_DATA(type, name)				\
+static type *netns_core_##name##_data(const struct sysctl_context *ctx)	\
+{									\
+	return &ctx->ns.net_ns->core.sysctl_##name;			\
+}
+
+#define NETNS_CORE_SYSCTL_CUSTOM_DATA(name)				\
+static void *netns_core_##name##_data(const struct sysctl_context *ctx)	\
+{									\
+	return &ctx->ns.net_ns->core.sysctl_##name;			\
+}
+
+NETNS_CORE_SYSCTL_DATA(int, somaxconn)
+NETNS_CORE_SYSCTL_DATA(int, optmem_max)
+NETNS_CORE_SYSCTL_DATA(u8, txrehash)
+NETNS_CORE_SYSCTL_CUSTOM_DATA(txq_reselection)
+NETNS_CORE_SYSCTL_DATA(u8, bypass_prot_mem)
+
+static u8 *netns_core_tstamp_allow_data_value(const struct sysctl_context *ctx)
+{
+	return &ctx->ns.net_ns->core.sysctl_tstamp_allow_data;
+}
+
 #if IS_ENABLED(CONFIG_RPS)
-	{
-		.procname	= "rps_default_mask",
-		.data		= &init_net,
-		.mode		= 0644,
-		.proc_handler	= rps_default_mask_sysctl
-	},
+static void *netns_core_rps_default_mask_data(const struct sysctl_context *ctx)
+{
+	return ctx->ns.net_ns;
+}
 #endif
-	{
-		.procname	= "somaxconn",
-		.data		= &init_net.core.sysctl_somaxconn,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.extra1		= SYSCTL_ZERO,
-		.proc_handler	= proc_dointvec_minmax
-	},
-	{
-		.procname	= "optmem_max",
-		.data		= &init_net.core.sysctl_optmem_max,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.extra1		= SYSCTL_ZERO,
-		.proc_handler	= proc_dointvec_minmax
-	},
-	{
-		.procname	= "txrehash",
-		.data		= &init_net.core.sysctl_txrehash,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	{
-		.procname	= "txq_reselection_ms",
-		.data		= &init_net.core.sysctl_txq_reselection,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_ms_jiffies,
-	},
-	{
-		.procname	= "tstamp_allow_data",
-		.data		= &init_net.core.sysctl_tstamp_allow_data,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE
-	},
-	{
-		.procname	= "bypass_prot_mem",
-		.data		= &init_net.core.sysctl_bypass_prot_mem,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE
-	},
-	/* sysctl_core_net_init() will set the values after this
-	 * to readonly in network namespaces
-	 */
-	{
-		.procname	= "wmem_max",
-		.data		= &sysctl_wmem_max,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &min_sndbuf,
-	},
-	{
-		.procname	= "rmem_max",
-		.data		= &sysctl_rmem_max,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &min_rcvbuf,
-	},
-	{
-		.procname	= "wmem_default",
-		.data		= &sysctl_wmem_default,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &min_sndbuf,
-	},
-	{
-		.procname	= "rmem_default",
-		.data		= &sysctl_rmem_default,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &min_rcvbuf,
-	},
+
+static void *netns_core_sysctl_wmem_max_data(const struct sysctl_context *ctx)
+{
+	return &sysctl_wmem_max;
+}
+
+static void *netns_core_sysctl_rmem_max_data(const struct sysctl_context *ctx)
+{
+	return &sysctl_rmem_max;
+}
+
+static void *netns_core_sysctl_wmem_default_data(const struct sysctl_context *ctx)
+{
+	return &sysctl_wmem_default;
+}
+
+static void *netns_core_sysctl_rmem_default_data(const struct sysctl_context *ctx)
+{
+	return &sysctl_rmem_default;
+}
+
+static int proc_dointvec_minmax_sndbuf(const struct ctl_table *table, int write,
+				       void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct ctl_table tmp = *table;
+
+	tmp.extra1 = &min_sndbuf;
+	return proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
+}
+
+static int proc_dointvec_minmax_rcvbuf(const struct ctl_table *table, int write,
+				       void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct ctl_table tmp = *table;
+
+	tmp.extra1 = &min_rcvbuf;
+	return proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
+}
+
+static const struct sysctl_field netns_core_table[] = {
+#if IS_ENABLED(CONFIG_RPS)
+	SYSCTL_FIELD_CUSTOM("rps_default_mask", 0644, 0,
+			 netns_core_rps_default_mask_data,
+			 rps_default_mask_sysctl),
+#endif
+	SYSCTL_FIELD_STATIC_INT_MINMAX("somaxconn", 0644,
+				    netns_core_somaxconn_data,
+				    SYSCTL_ZERO, NULL),
+	SYSCTL_FIELD_STATIC_INT_MINMAX("optmem_max", 0644,
+				    netns_core_optmem_max_data,
+				    SYSCTL_ZERO, NULL),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("txrehash", 0644,
+				   netns_core_txrehash_data,
+				   SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_CUSTOM("txq_reselection_ms", 0644, sizeof(int),
+			 netns_core_txq_reselection_data, proc_dointvec_ms_jiffies),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("tstamp_allow_data", 0644,
+				   netns_core_tstamp_allow_data_value,
+				   SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("bypass_prot_mem", 0644,
+				   netns_core_bypass_prot_mem_data,
+				   SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_CUSTOM_MODE("wmem_max", 0644,
+			      netns_core_init_net_writable_mode, sizeof(int),
+			      netns_core_sysctl_wmem_max_data,
+			      proc_dointvec_minmax_sndbuf),
+	SYSCTL_FIELD_CUSTOM_MODE("rmem_max", 0644,
+			      netns_core_init_net_writable_mode, sizeof(int),
+			      netns_core_sysctl_rmem_max_data,
+			      proc_dointvec_minmax_rcvbuf),
+	SYSCTL_FIELD_CUSTOM_MODE("wmem_default", 0644,
+			      netns_core_init_net_writable_mode, sizeof(int),
+			      netns_core_sysctl_wmem_default_data,
+			      proc_dointvec_minmax_sndbuf),
+	SYSCTL_FIELD_CUSTOM_MODE("rmem_default", 0644,
+			      netns_core_init_net_writable_mode, sizeof(int),
+			      netns_core_sysctl_rmem_default_data,
+			      proc_dointvec_minmax_rcvbuf),
 };
 
 static int __init fb_tunnels_only_for_init_net_sysctl_setup(char *str)
@@ -789,50 +807,23 @@ __setup("fb_tunnels=", fb_tunnels_only_for_init_net_sysctl_setup);
 
 static __net_init int sysctl_core_net_init(struct net *net)
 {
-	size_t table_size = ARRAY_SIZE(netns_core_table);
-	struct ctl_table *tbl;
-
-	tbl = netns_core_table;
-	if (!net_eq(net, &init_net)) {
-		int i;
-		tbl = kmemdup(tbl, sizeof(netns_core_table), GFP_KERNEL);
-		if (tbl == NULL)
-			goto err_dup;
-
-		for (i = 0; i < table_size; ++i) {
-			if (tbl[i].data == &sysctl_wmem_max)
-				break;
-
-			tbl[i].data += (char *)net - (char *)&init_net;
-		}
-		for (; i < table_size; ++i)
-			tbl[i].mode &= ~0222;
-	}
-
-	net->core.sysctl_hdr = register_net_sysctl_sz(net, "net/core", tbl, table_size);
+	struct sysctl_context ctx = {
+		.ns.net_ns = net,
+	};
+	net->core.sysctl_hdr = register_sysctl_fields(&net->sysctls, "net/core",
+						      netns_core_table, &ctx);
 	if (net->core.sysctl_hdr == NULL)
-		goto err_reg;
+		return -ENOMEM;
 
 	return 0;
-
-err_reg:
-	if (tbl != netns_core_table)
-		kfree(tbl);
-err_dup:
-	return -ENOMEM;
 }
 
 static __net_exit void sysctl_core_net_exit(struct net *net)
 {
-	const struct ctl_table *tbl;
-
-	tbl = net->core.sysctl_hdr->ctl_table_arg;
 	unregister_net_sysctl_table(net->core.sysctl_hdr);
-	BUG_ON(tbl == netns_core_table);
 #if IS_ENABLED(CONFIG_RPS)
 	kfree(net->core.rps_default_mask);
 #endif
-	kfree(tbl);
 }
 
 static __net_initdata struct pernet_operations sysctl_core_ops = {
Signature · 1 line
2.55.0
#29 lore raw

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

In reply to a message not in the archive

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.

---
 net/smc/smc_sysctl.c | 208 ++++++++++++++++++-------------------------
 1 file changed, 89 insertions(+), 119 deletions(-)
net/smc/smc_sysctl.c +89 -119
index b1efed546243..801bbd8773d3 100644
@@ -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);
 }
Signature · 1 line
2.55.0
#30 lore raw

RFC PATCH 21/30 sysctl: net: use sysctl_field in VRF sysctls

In reply to a message not in the archive

VRF has a single per-net sysctl, but still clones its ctl_table so the handler can receive the current net namespace through extra1. The copied table then has to be kept until namespace teardown only to free it after unregistering the sysctl.

---
 drivers/net/vrf.c | 43 +++++++++++++++----------------------------
 1 file changed, 15 insertions(+), 28 deletions(-)
drivers/net/vrf.c +15 -28
index 46209917ae4d..2155297f2f73 100644
@@ -1828,7 +1828,7 @@ static int vrf_strict_mode_change(struct vrf_map *vmap, bool new_mode)
 static int vrf_shared_table_handler(const struct ctl_table *table, int write,
 				    void *buffer, size_t *lenp, loff_t *ppos)
 {
-	struct net *net = (struct net *)table->extra1;
+	struct net *net = table->data;
 	struct vrf_map *vmap = netns_vrf_map(net);
 	int proc_strict_mode = 0;
 	struct ctl_table tmp = {
@@ -1852,35 +1852,25 @@ static int vrf_shared_table_handler(const struct ctl_table *table, int write,
 	return ret;
 }
 
-static const struct ctl_table vrf_table[] = {
-	{
-		.procname	= "strict_mode",
-		.data		= NULL,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= vrf_shared_table_handler,
-		/* set by the vrf_netns_init */
-		.extra1		= NULL,
-	},
+static void *vrf_strict_mode_data(const struct sysctl_context *ctx)
+{
+	return ctx->ns.net_ns;
+}
+
+static const struct sysctl_field vrf_table[] = {
+	SYSCTL_FIELD_CUSTOM("strict_mode", 0644, 0, vrf_strict_mode_data,
+			 vrf_shared_table_handler),
 };
 
 static int vrf_netns_init_sysctl(struct net *net, struct netns_vrf *nn_vrf)
 {
-	struct ctl_table *table;
-
-	table = kmemdup(vrf_table, sizeof(vrf_table), GFP_KERNEL);
-	if (!table)
-		return -ENOMEM;
-
-	/* init the extra1 parameter with the reference to current netns */
-	table[0].extra1 = net;
-
-	nn_vrf->ctl_hdr = register_net_sysctl_sz(net, "net/vrf", table,
-						 ARRAY_SIZE(vrf_table));
-	if (!nn_vrf->ctl_hdr) {
-		kfree(table);
+	struct sysctl_context ctx = {
+		.ns.net_ns = net,
+	};
+	nn_vrf->ctl_hdr = register_sysctl_fields(&net->sysctls, "net/vrf",
+						 vrf_table, &ctx);
+	if (!nn_vrf->ctl_hdr)
 		return -ENOMEM;
-	}
 
 	return 0;
 }
@@ -1888,11 +1878,8 @@ static int vrf_netns_init_sysctl(struct net *net, struct netns_vrf *nn_vrf)
 static void vrf_netns_exit_sysctl(struct net *net)
 {
 	struct netns_vrf *nn_vrf = net_generic(net, vrf_net_id);
-	const struct ctl_table *table;
 
-	table = nn_vrf->ctl_hdr->ctl_table_arg;
 	unregister_net_sysctl_table(nn_vrf->ctl_hdr);
-	kfree(table);
 }
 #else
 static int vrf_netns_init_sysctl(struct net *net, struct netns_vrf *nn_vrf)
Signature · 1 line
2.55.0
#31 lore raw

RFC PATCH 22/30 sysctl: net: use sysctl_field in RDS sysctls

In reply to a message not in the archive

RDS TCP clones its sysctl table for every non-init network namespace so that the per-net buffer limits can be patched into the ctl_table entries. The handlers also rely on the ctl_table data pointer to recover the owning RDS per-net state.

Use sysctl_field descriptors instead and resolve the per-net data from the sysctl context at registration time. This keeps the RDS TCP sysctl layout static, removes the per-net table allocation, and avoids binding the code to fixed ctl_table indexes.

Pass the per-entry minimum and the RDS per-net state through the custom field callbacks so both buffer sysctls can share one handler.

---
 net/rds/tcp.c | 129 ++++++++++++++++++++++++--------------------------
 net/rds/tcp.h |   1 -
 2 files changed, 63 insertions(+), 67 deletions(-)
net/rds/tcp.c +63 -66
index 5830b31a1f37..2504221ec592 100644
@@ -34,6 +34,7 @@
 #include <linux/slab.h>
 #include <linux/in.h>
 #include <linux/module.h>
+#include <linux/sysctl.h>
 #include <net/tcp.h>
 #include <net/net_namespace.h>
 #include <net/netns/generic.h>
@@ -61,33 +62,60 @@ static atomic_t rds_tcp_unloading = ATOMIC_INIT(0);
 
 static struct kmem_cache *rds_tcp_conn_slab;
 
-static int rds_tcp_sndbuf_handler(const struct ctl_table *ctl, int write,
-				  void *buffer, size_t *lenp, loff_t *fpos);
-static int rds_tcp_rcvbuf_handler(const struct ctl_table *ctl, int write,
-				  void *buffer, size_t *lenp, loff_t *fpos);
+static int rds_tcp_skbuf_handler(const struct ctl_table *ctl, int write,
+				 void *buffer, size_t *lenp, loff_t *fpos);
 
 static int rds_tcp_min_sndbuf = SOCK_MIN_SNDBUF;
 static int rds_tcp_min_rcvbuf = SOCK_MIN_RCVBUF;
 
-static struct ctl_table rds_tcp_sysctl_table[] = {
-#define	RDS_TCP_SNDBUF	0
-	{
-		.procname       = "rds_tcp_sndbuf",
-		/* data is per-net pointer */
-		.maxlen         = sizeof(int),
-		.mode           = 0644,
-		.proc_handler   = rds_tcp_sndbuf_handler,
-		.extra1		= &rds_tcp_min_sndbuf,
-	},
-#define	RDS_TCP_RCVBUF	1
-	{
-		.procname       = "rds_tcp_rcvbuf",
-		/* data is per-net pointer */
-		.maxlen         = sizeof(int),
-		.mode           = 0644,
-		.proc_handler   = rds_tcp_rcvbuf_handler,
-		.extra1		= &rds_tcp_min_rcvbuf,
-	},
+static void *rds_tcp_sndbuf_data(const struct sysctl_context *ctx)
+{
+	struct rds_tcp_net *rtn = net_generic(ctx->ns.net_ns, rds_tcp_netid);
+
+	return &rtn->sndbuf_size;
+}
+
+static void *rds_tcp_rcvbuf_data(const struct sysctl_context *ctx)
+{
+	struct rds_tcp_net *rtn = net_generic(ctx->ns.net_ns, rds_tcp_netid);
+
+	return &rtn->rcvbuf_size;
+}
+
+static void *rds_tcp_net_data(const struct sysctl_context *ctx)
+{
+	return net_generic(ctx->ns.net_ns, rds_tcp_netid);
+}
+
+static void *rds_tcp_min_sndbuf_data(const struct sysctl_context *ctx)
+{
+	return &rds_tcp_min_sndbuf;
+}
+
+static void *rds_tcp_min_rcvbuf_data(const struct sysctl_context *ctx)
+{
+	return &rds_tcp_min_rcvbuf;
+}
+
+#define RDS_TCP_SKBUF_ENTRY(_name, _data, _min)				\
+	{								\
+		.procname	= (_name),				\
+		.mode		= 0644,					\
+		.type		= SYSCTL_FIELD_CUSTOM,			\
+		.ctl_custom	= {					\
+			.proc_handler	= rds_tcp_skbuf_handler,	\
+			.data		= (_data),			\
+			.extra1		= (_min),			\
+			.extra2		= rds_tcp_net_data,		\
+			.maxlen		= sizeof(int),			\
+		},							\
+	}
+
+static const struct sysctl_field rds_tcp_sysctl_table[] = {
+	RDS_TCP_SKBUF_ENTRY("rds_tcp_sndbuf", rds_tcp_sndbuf_data,
+			    rds_tcp_min_sndbuf_data),
+	RDS_TCP_SKBUF_ENTRY("rds_tcp_rcvbuf", rds_tcp_rcvbuf_data,
+			    rds_tcp_min_rcvbuf_data),
 };
 
 u32 rds_tcp_write_seq(struct rds_tcp_connection *tc)
@@ -542,36 +570,28 @@ void rds_tcp_accept_work(struct rds_tcp_net *rtn)
 static __net_init int rds_tcp_init_net(struct net *net)
 {
 	struct rds_tcp_net *rtn = net_generic(net, rds_tcp_netid);
-	struct ctl_table *tbl;
 	int err = 0;
 
 	memset(rtn, 0, sizeof(*rtn));
 
 	mutex_init(&rtn->rds_tcp_accept_lock);
 
+#if IS_ENABLED(CONFIG_SYSCTL)
+	struct sysctl_context ctx = {
+		.ns.net_ns = net,
+	};
+
 	/* {snd, rcv}buf_size default to 0, which implies we let the
 	 * stack pick the value, and permit auto-tuning of buffer size.
 	 */
-	if (net == &init_net) {
-		tbl = rds_tcp_sysctl_table;
-	} else {
-		tbl = kmemdup(rds_tcp_sysctl_table,
-			      sizeof(rds_tcp_sysctl_table), GFP_KERNEL);
-		if (!tbl) {
-			pr_warn("could not set allocate sysctl table\n");
-			return -ENOMEM;
-		}
-		rtn->ctl_table = tbl;
-	}
-	tbl[RDS_TCP_SNDBUF].data = &rtn->sndbuf_size;
-	tbl[RDS_TCP_RCVBUF].data = &rtn->rcvbuf_size;
-	rtn->rds_tcp_sysctl = register_net_sysctl_sz(net, "net/rds/tcp", tbl,
-						     ARRAY_SIZE(rds_tcp_sysctl_table));
+	rtn->rds_tcp_sysctl = register_sysctl_fields(&net->sysctls, "net/rds/tcp",
+						     rds_tcp_sysctl_table, &ctx);
 	if (!rtn->rds_tcp_sysctl) {
 		pr_warn("could not register sysctl\n");
 		err = -ENOMEM;
 		goto fail;
 	}
+#endif
 
 #if IS_ENABLED(CONFIG_IPV6)
 	rtn->rds_tcp_listen_sock = rds_tcp_listen_init(net, true);
@@ -598,8 +618,6 @@ static __net_init int rds_tcp_init_net(struct net *net)
 	return 0;
 
 fail:
-	if (net != &init_net)
-		kfree(tbl);
 	return err;
 }
 
@@ -640,9 +658,6 @@ static void __net_exit rds_tcp_exit_net(struct net *net)
 
 	if (rtn->rds_tcp_sysctl)
 		unregister_net_sysctl_table(rtn->rds_tcp_sysctl);
-
-	if (net != &init_net)
-		kfree(rtn->ctl_table);
 }
 
 static struct pernet_operations rds_tcp_net_ops = {
@@ -685,16 +700,16 @@ static void rds_tcp_sysctl_reset(struct net *net)
 	spin_unlock_irq(&rds_tcp_conn_lock);
 }
 
-static int rds_tcp_skbuf_handler(struct rds_tcp_net *rtn,
-				 const struct ctl_table *ctl, int write,
+static int rds_tcp_skbuf_handler(const struct ctl_table *ctl, int write,
 				 void *buffer, size_t *lenp, loff_t *fpos)
 {
+	struct rds_tcp_net *rtn = ctl->extra2;
+	int *min = ctl->extra1;
 	int err;
 
 	err = proc_dointvec_minmax(ctl, write, buffer, lenp, fpos);
 	if (err < 0) {
-		pr_warn("Invalid input. Must be >= %d\n",
-			*(int *)(ctl->extra1));
+		pr_warn("Invalid input. Must be >= %d\n", *min);
 		return err;
 	}
 
@@ -707,24 +722,6 @@ static int rds_tcp_skbuf_handler(struct rds_tcp_net *rtn,
 	return 0;
 }
 
-static int rds_tcp_sndbuf_handler(const struct ctl_table *ctl, int write,
-				  void *buffer, size_t *lenp, loff_t *fpos)
-{
-	struct rds_tcp_net *rtn = container_of(ctl->data, struct rds_tcp_net,
-					       sndbuf_size);
-
-	return rds_tcp_skbuf_handler(rtn, ctl, write, buffer, lenp, fpos);
-}
-
-static int rds_tcp_rcvbuf_handler(const struct ctl_table *ctl, int write,
-				  void *buffer, size_t *lenp, loff_t *fpos)
-{
-	struct rds_tcp_net *rtn = container_of(ctl->data, struct rds_tcp_net,
-					       rcvbuf_size);
-
-	return rds_tcp_skbuf_handler(rtn, ctl, write, buffer, lenp, fpos);
-}
-
 static void rds_tcp_exit(void)
 {
 	rds_tcp_set_unloading();
net/rds/tcp.h +0 -1
index 39c86347188c..266e1d765fd5 100644
@@ -14,7 +14,6 @@ struct rds_tcp_net {
 	struct socket		*rds_tcp_accepted_sock;
 	struct work_struct	rds_tcp_accept_w;
 	struct ctl_table_header	*rds_tcp_sysctl;
-	const struct ctl_table	*ctl_table;
 	int			sndbuf_size;
 	int			rcvbuf_size;
 };
Signature · 1 line
2.55.0
#32 lore raw

RFC PATCH 23/30 sysctl: netfilter: use sysctl_field for per-net sysctls

In reply to a message not in the archive

Netfilter still clones several sysctl tables for each network namespace so the registration code can patch data pointers, modes, and namespace context into the copied ctl_table entries. This keeps the tables writable and ties the setup code to the exact table layout.

Use sysctl_field for the per-net conntrack, nf_log, and lwtunnel sysctls instead. The table descriptions can stay static and const while the effective ctl_table entries are derived from the registration context.

This removes the per-net table copies and the unregister-time frees, and avoids the conntrack index patch-up helpers that had to stay in sync with the ctl_table layout.

---
 net/netfilter/nf_conntrack_standalone.c | 743 ++++++++++--------------
 net/netfilter/nf_hooks_lwtunnel.c       |  40 +-
 net/netfilter/nf_log.c                  |  90 +--
 3 files changed, 369 insertions(+), 504 deletions(-)
net/netfilter/nf_conntrack_standalone.c +315 -428
index be2953c7d702..94a5353fcb93 100644
@@ -581,345 +581,344 @@ nf_conntrack_log_invalid_sysctl(const struct ctl_table *table, int write,
 }
 
 static struct ctl_table_header *nf_ct_netfilter_header;
+static unsigned int nf_ct_max_limit = INT_MAX;
+
+static umode_t nf_ct_global_sysctl_mode(const struct sysctl_context *ctx)
+{
+	return net_eq(ctx->ns.net_ns, &init_net) ? 0644 : 0444;
+}
+
+static unsigned int *nf_ct_max_data(const struct sysctl_context *ctx)
+{
+	return &nf_conntrack_max;
+}
+
+static void *nf_ct_count_data(const struct sysctl_context *ctx)
+{
+	struct nf_conntrack_net *cnet = nf_ct_pernet(ctx->ns.net_ns);
+
+	return &cnet->count;
+}
+
+static void *nf_ct_buckets_data(const struct sysctl_context *ctx)
+{
+	return &nf_conntrack_htable_size_user;
+}
+
+static u8 *nf_ct_checksum_data(const struct sysctl_context *ctx)
+{
+	return &ctx->ns.net_ns->ct.sysctl_checksum;
+}
+
+static void *nf_ct_log_invalid_data(const struct sysctl_context *ctx)
+{
+	return &ctx->ns.net_ns->ct.sysctl_log_invalid;
+}
+
+static unsigned int *nf_ct_expect_max_data(const struct sysctl_context *ctx)
+{
+	return &nf_ct_expect_max;
+}
+
+static u8 *nf_ct_acct_data(const struct sysctl_context *ctx)
+{
+	return &ctx->ns.net_ns->ct.sysctl_acct;
+}
 
-enum nf_ct_sysctl_index {
-	NF_SYSCTL_CT_MAX,
-	NF_SYSCTL_CT_COUNT,
-	NF_SYSCTL_CT_BUCKETS,
-	NF_SYSCTL_CT_CHECKSUM,
-	NF_SYSCTL_CT_LOG_INVALID,
-	NF_SYSCTL_CT_EXPECT_MAX,
-	NF_SYSCTL_CT_ACCT,
 #ifdef CONFIG_NF_CONNTRACK_EVENTS
-	NF_SYSCTL_CT_EVENTS,
+static u8 *nf_ct_events_data(const struct sysctl_context *ctx)
+{
+	return &ctx->ns.net_ns->ct.sysctl_events;
+}
 #endif
+
 #ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
-	NF_SYSCTL_CT_TIMESTAMP,
+static u8 *nf_ct_timestamp_data(const struct sysctl_context *ctx)
+{
+	return &ctx->ns.net_ns->ct.sysctl_tstamp;
+}
 #endif
-	NF_SYSCTL_CT_PROTO_TIMEOUT_GENERIC,
-	NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_SYN_SENT,
-	NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_SYN_RECV,
-	NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_ESTABLISHED,
-	NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_FIN_WAIT,
-	NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_CLOSE_WAIT,
-	NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_LAST_ACK,
-	NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_TIME_WAIT,
-	NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_CLOSE,
-	NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_RETRANS,
-	NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_UNACK,
+
+static void *nf_ct_generic_timeout_data(const struct sysctl_context *ctx)
+{
+	return &nf_generic_pernet(ctx->ns.net_ns)->timeout;
+}
+
+#define NF_CT_TCP_TIMEOUT_DATA(name, state)				\
+static void *nf_ct_tcp_timeout_ ## name ## _data(const struct sysctl_context *ctx)	\
+{									\
+	struct nf_tcp_net *tn = nf_tcp_pernet(ctx->ns.net_ns);		\
+	return &tn->timeouts[TCP_CONNTRACK_ ## state];			\
+}
+
+NF_CT_TCP_TIMEOUT_DATA(syn_sent, SYN_SENT)
+NF_CT_TCP_TIMEOUT_DATA(syn_recv, SYN_RECV)
+NF_CT_TCP_TIMEOUT_DATA(established, ESTABLISHED)
+NF_CT_TCP_TIMEOUT_DATA(fin_wait, FIN_WAIT)
+NF_CT_TCP_TIMEOUT_DATA(close_wait, CLOSE_WAIT)
+NF_CT_TCP_TIMEOUT_DATA(last_ack, LAST_ACK)
+NF_CT_TCP_TIMEOUT_DATA(time_wait, TIME_WAIT)
+NF_CT_TCP_TIMEOUT_DATA(close, CLOSE)
+NF_CT_TCP_TIMEOUT_DATA(retrans, RETRANS)
+NF_CT_TCP_TIMEOUT_DATA(unack, UNACK)
+#undef NF_CT_TCP_TIMEOUT_DATA
+
 #if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
-	NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_OFFLOAD,
+static void *nf_ct_tcp_offload_timeout_data(const struct sysctl_context *ctx)
+{
+	return &nf_tcp_pernet(ctx->ns.net_ns)->offload_timeout;
+}
 #endif
-	NF_SYSCTL_CT_PROTO_TCP_LOOSE,
-	NF_SYSCTL_CT_PROTO_TCP_LIBERAL,
-	NF_SYSCTL_CT_PROTO_TCP_IGNORE_INVALID_RST,
-	NF_SYSCTL_CT_PROTO_TCP_MAX_RETRANS,
-	NF_SYSCTL_CT_PROTO_TIMEOUT_UDP,
-	NF_SYSCTL_CT_PROTO_TIMEOUT_UDP_STREAM,
+
+#define NF_CT_TCP_U8_DATA(name, field)					\
+static u8 *nf_ct_tcp_ ## name ## _data(const struct sysctl_context *ctx)	\
+{									\
+	return &nf_tcp_pernet(ctx->ns.net_ns)->field;			\
+}
+
+NF_CT_TCP_U8_DATA(loose, tcp_loose)
+NF_CT_TCP_U8_DATA(liberal, tcp_be_liberal)
+NF_CT_TCP_U8_DATA(ignore_invalid_rst, tcp_ignore_invalid_rst)
+NF_CT_TCP_U8_DATA(max_retrans, tcp_max_retrans)
+#undef NF_CT_TCP_U8_DATA
+
+static void *nf_ct_udp_timeout_data(const struct sysctl_context *ctx)
+{
+	return &nf_udp_pernet(ctx->ns.net_ns)->timeouts[UDP_CT_UNREPLIED];
+}
+
+static void *nf_ct_udp_stream_timeout_data(const struct sysctl_context *ctx)
+{
+	return &nf_udp_pernet(ctx->ns.net_ns)->timeouts[UDP_CT_REPLIED];
+}
+
 #if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
-	NF_SYSCTL_CT_PROTO_TIMEOUT_UDP_OFFLOAD,
+static void *nf_ct_udp_offload_timeout_data(const struct sysctl_context *ctx)
+{
+	return &nf_udp_pernet(ctx->ns.net_ns)->offload_timeout;
+}
 #endif
-	NF_SYSCTL_CT_PROTO_TIMEOUT_ICMP,
-	NF_SYSCTL_CT_PROTO_TIMEOUT_ICMPV6,
+
+static void *nf_ct_icmp_timeout_data(const struct sysctl_context *ctx)
+{
+	return &nf_icmp_pernet(ctx->ns.net_ns)->timeout;
+}
+
+static void *nf_ct_icmpv6_timeout_data(const struct sysctl_context *ctx)
+{
+	return &nf_icmpv6_pernet(ctx->ns.net_ns)->timeout;
+}
+
 #ifdef CONFIG_NF_CT_PROTO_SCTP
-	NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_CLOSED,
-	NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_COOKIE_WAIT,
-	NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_COOKIE_ECHOED,
-	NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_ESTABLISHED,
-	NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_SHUTDOWN_SENT,
-	NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_SHUTDOWN_RECD,
-	NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_SHUTDOWN_ACK_SENT,
-	NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_HEARTBEAT_SENT,
+#define NF_CT_SCTP_TIMEOUT_DATA(name, state)				\
+static void *nf_ct_sctp_timeout_ ## name ## _data(const struct sysctl_context *ctx) \
+{									\
+	struct nf_sctp_net *sn = nf_sctp_pernet(ctx->ns.net_ns);	\
+	return &sn->timeouts[SCTP_CONNTRACK_ ## state];			\
+}
+
+NF_CT_SCTP_TIMEOUT_DATA(closed, CLOSED)
+NF_CT_SCTP_TIMEOUT_DATA(cookie_wait, COOKIE_WAIT)
+NF_CT_SCTP_TIMEOUT_DATA(cookie_echoed, COOKIE_ECHOED)
+NF_CT_SCTP_TIMEOUT_DATA(established, ESTABLISHED)
+NF_CT_SCTP_TIMEOUT_DATA(shutdown_sent, SHUTDOWN_SENT)
+NF_CT_SCTP_TIMEOUT_DATA(shutdown_recd, SHUTDOWN_RECD)
+NF_CT_SCTP_TIMEOUT_DATA(shutdown_ack_sent, SHUTDOWN_ACK_SENT)
+NF_CT_SCTP_TIMEOUT_DATA(heartbeat_sent, HEARTBEAT_SENT)
+#undef NF_CT_SCTP_TIMEOUT_DATA
 #endif
+
 #ifdef CONFIG_NF_CT_PROTO_GRE
-	NF_SYSCTL_CT_PROTO_TIMEOUT_GRE,
-	NF_SYSCTL_CT_PROTO_TIMEOUT_GRE_STREAM,
+static void *nf_ct_gre_timeout_data(const struct sysctl_context *ctx)
+{
+	return &nf_gre_pernet(ctx->ns.net_ns)->timeouts[GRE_CT_UNREPLIED];
+}
+
+static void *nf_ct_gre_stream_timeout_data(const struct sysctl_context *ctx)
+{
+	return &nf_gre_pernet(ctx->ns.net_ns)->timeouts[GRE_CT_REPLIED];
+}
 #endif
 
-	NF_SYSCTL_CT_LAST_SYSCTL,
-};
+#define NF_CT_GLOBAL_UINT_MINMAX(_procname, _data)			\
+	{								\
+		.procname	= (_procname),				\
+		.mode		= 0644,					\
+		.mode_fn	= nf_ct_global_sysctl_mode,		\
+		.type		= SYSCTL_FIELD_STATIC_UINT_MINMAX,		\
+		.ctl_static_uint = {					\
+			.data		= (_data),			\
+			.min_value	= SYSCTL_UINT_ONE,		\
+			.max_value	= &nf_ct_max_limit,		\
+		},							\
+	}
 
-static struct ctl_table nf_ct_sysctl_table[] = {
-	[NF_SYSCTL_CT_MAX] = {
-		.procname	= "nf_conntrack_max",
-		.data		= &nf_conntrack_max,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= SYSCTL_INT_MAX,
-	},
-	[NF_SYSCTL_CT_COUNT] = {
-		.procname	= "nf_conntrack_count",
-		.maxlen		= sizeof(int),
-		.mode		= 0444,
-		.proc_handler	= proc_dointvec,
-	},
-	[NF_SYSCTL_CT_BUCKETS] = {
-		.procname       = "nf_conntrack_buckets",
-		.data           = &nf_conntrack_htable_size_user,
-		.maxlen         = sizeof(unsigned int),
-		.mode           = 0644,
-		.proc_handler   = nf_conntrack_hash_sysctl,
-	},
-	[NF_SYSCTL_CT_CHECKSUM] = {
-		.procname	= "nf_conntrack_checksum",
-		.data		= &init_net.ct.sysctl_checksum,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1 	= SYSCTL_ZERO,
-		.extra2 	= SYSCTL_ONE,
-	},
-	[NF_SYSCTL_CT_LOG_INVALID] = {
-		.procname	= "nf_conntrack_log_invalid",
-		.data		= &init_net.ct.sysctl_log_invalid,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= nf_conntrack_log_invalid_sysctl,
-	},
-	[NF_SYSCTL_CT_EXPECT_MAX] = {
-		.procname	= "nf_conntrack_expect_max",
-		.data		= &nf_ct_expect_max,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= SYSCTL_INT_MAX,
-	},
-	[NF_SYSCTL_CT_ACCT] = {
-		.procname	= "nf_conntrack_acct",
-		.data		= &init_net.ct.sysctl_acct,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1 	= SYSCTL_ZERO,
-		.extra2 	= SYSCTL_ONE,
-	},
+static const struct sysctl_field nf_ct_sysctl_table[] = {
+	NF_CT_GLOBAL_UINT_MINMAX("nf_conntrack_max", nf_ct_max_data),
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_count", 0444, sizeof(int),
+			 nf_ct_count_data, proc_dointvec),
+	SYSCTL_FIELD_CUSTOM_MODE("nf_conntrack_buckets", 0644,
+			      nf_ct_global_sysctl_mode,
+			      sizeof(unsigned int),
+			      nf_ct_buckets_data,
+			      nf_conntrack_hash_sysctl),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("nf_conntrack_checksum", 0644,
+				   nf_ct_checksum_data, SYSCTL_UINT_ZERO,
+				   SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_log_invalid", 0644, sizeof(u8),
+			 nf_ct_log_invalid_data,
+			 nf_conntrack_log_invalid_sysctl),
+	NF_CT_GLOBAL_UINT_MINMAX("nf_conntrack_expect_max",
+				 nf_ct_expect_max_data),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("nf_conntrack_acct", 0644,
+				   nf_ct_acct_data, SYSCTL_UINT_ZERO,
+				   SYSCTL_UINT_ONE),
 #ifdef CONFIG_NF_CONNTRACK_EVENTS
-	[NF_SYSCTL_CT_EVENTS] = {
-		.procname	= "nf_conntrack_events",
-		.data		= &init_net.ct.sysctl_events,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1 	= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO,
-	},
+	SYSCTL_FIELD_STATIC_U8_MINMAX("nf_conntrack_events", 0644,
+				   nf_ct_events_data, SYSCTL_UINT_ZERO,
+				   SYSCTL_UINT_TWO),
 #endif
 #ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
-	[NF_SYSCTL_CT_TIMESTAMP] = {
-		.procname	= "nf_conntrack_timestamp",
-		.data		= &init_net.ct.sysctl_tstamp,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1 	= SYSCTL_ZERO,
-		.extra2 	= SYSCTL_ONE,
-	},
+	SYSCTL_FIELD_STATIC_U8_MINMAX("nf_conntrack_timestamp", 0644,
+				   nf_ct_timestamp_data,
+				   SYSCTL_UINT_ZERO,
+				   SYSCTL_UINT_ONE),
 #endif
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_GENERIC] = {
-		.procname	= "nf_conntrack_generic_timeout",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_SYN_SENT] = {
-		.procname	= "nf_conntrack_tcp_timeout_syn_sent",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_SYN_RECV] = {
-		.procname	= "nf_conntrack_tcp_timeout_syn_recv",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_ESTABLISHED] = {
-		.procname	= "nf_conntrack_tcp_timeout_established",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_FIN_WAIT] = {
-		.procname	= "nf_conntrack_tcp_timeout_fin_wait",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_CLOSE_WAIT] = {
-		.procname	= "nf_conntrack_tcp_timeout_close_wait",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_LAST_ACK] = {
-		.procname	= "nf_conntrack_tcp_timeout_last_ack",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_TIME_WAIT] = {
-		.procname	= "nf_conntrack_tcp_timeout_time_wait",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_CLOSE] = {
-		.procname	= "nf_conntrack_tcp_timeout_close",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_RETRANS] = {
-		.procname	= "nf_conntrack_tcp_timeout_max_retrans",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_UNACK] = {
-		.procname	= "nf_conntrack_tcp_timeout_unacknowledged",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_generic_timeout", 0644,
+			 sizeof(unsigned int),
+			 nf_ct_generic_timeout_data,
+			 proc_dointvec_jiffies),
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_tcp_timeout_syn_sent", 0644,
+			 sizeof(unsigned int),
+			 nf_ct_tcp_timeout_syn_sent_data,
+			 proc_dointvec_jiffies),
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_tcp_timeout_syn_recv", 0644,
+			 sizeof(unsigned int),
+			 nf_ct_tcp_timeout_syn_recv_data,
+			 proc_dointvec_jiffies),
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_tcp_timeout_established", 0644,
+			 sizeof(unsigned int),
+			 nf_ct_tcp_timeout_established_data,
+			 proc_dointvec_jiffies),
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_tcp_timeout_fin_wait", 0644,
+			 sizeof(unsigned int),
+			 nf_ct_tcp_timeout_fin_wait_data,
+			 proc_dointvec_jiffies),
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_tcp_timeout_close_wait", 0644,
+			 sizeof(unsigned int),
+			 nf_ct_tcp_timeout_close_wait_data,
+			 proc_dointvec_jiffies),
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_tcp_timeout_last_ack", 0644,
+			 sizeof(unsigned int),
+			 nf_ct_tcp_timeout_last_ack_data,
+			 proc_dointvec_jiffies),
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_tcp_timeout_time_wait", 0644,
+			 sizeof(unsigned int),
+			 nf_ct_tcp_timeout_time_wait_data,
+			 proc_dointvec_jiffies),
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_tcp_timeout_close", 0644,
+			 sizeof(unsigned int),
+			 nf_ct_tcp_timeout_close_data,
+			 proc_dointvec_jiffies),
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_tcp_timeout_max_retrans", 0644,
+			 sizeof(unsigned int),
+			 nf_ct_tcp_timeout_retrans_data,
+			 proc_dointvec_jiffies),
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_tcp_timeout_unacknowledged", 0644,
+			 sizeof(unsigned int),
+			 nf_ct_tcp_timeout_unack_data,
+			 proc_dointvec_jiffies),
 #if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_OFFLOAD] = {
-		.procname	= "nf_flowtable_tcp_timeout",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
+	SYSCTL_FIELD_CUSTOM("nf_flowtable_tcp_timeout", 0644,
+			 sizeof(unsigned int),
+			 nf_ct_tcp_offload_timeout_data,
+			 proc_dointvec_jiffies),
 #endif
-	[NF_SYSCTL_CT_PROTO_TCP_LOOSE] = {
-		.procname	= "nf_conntrack_tcp_loose",
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1 	= SYSCTL_ZERO,
-		.extra2 	= SYSCTL_ONE,
-	},
-	[NF_SYSCTL_CT_PROTO_TCP_LIBERAL] = {
-		.procname       = "nf_conntrack_tcp_be_liberal",
-		.maxlen		= sizeof(u8),
-		.mode           = 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1 	= SYSCTL_ZERO,
-		.extra2 	= SYSCTL_ONE,
-	},
-	[NF_SYSCTL_CT_PROTO_TCP_IGNORE_INVALID_RST] = {
-		.procname	= "nf_conntrack_tcp_ignore_invalid_rst",
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
-	},
-	[NF_SYSCTL_CT_PROTO_TCP_MAX_RETRANS] = {
-		.procname	= "nf_conntrack_tcp_max_retrans",
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-	},
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_UDP] = {
-		.procname	= "nf_conntrack_udp_timeout",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_UDP_STREAM] = {
-		.procname	= "nf_conntrack_udp_timeout_stream",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
+	SYSCTL_FIELD_STATIC_U8_MINMAX("nf_conntrack_tcp_loose", 0644,
+				   nf_ct_tcp_loose_data,
+				   SYSCTL_UINT_ZERO,
+				   SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("nf_conntrack_tcp_be_liberal",
+				   0644, nf_ct_tcp_liberal_data,
+				   SYSCTL_UINT_ZERO,
+				   SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_STATIC_U8_MINMAX("nf_conntrack_tcp_ignore_invalid_rst",
+				   0644,
+				   nf_ct_tcp_ignore_invalid_rst_data,
+				   SYSCTL_UINT_ZERO,
+				   SYSCTL_UINT_ONE),
+	SYSCTL_FIELD_U8("nf_conntrack_tcp_max_retrans", 0644,
+			     nf_ct_tcp_max_retrans_data),
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_udp_timeout", 0644,
+			 sizeof(unsigned int),
+			 nf_ct_udp_timeout_data,
+			 proc_dointvec_jiffies),
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_udp_timeout_stream", 0644,
+			 sizeof(unsigned int),
+			 nf_ct_udp_stream_timeout_data,
+			 proc_dointvec_jiffies),
 #if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_UDP_OFFLOAD] = {
-		.procname	= "nf_flowtable_udp_timeout",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
+	SYSCTL_FIELD_CUSTOM("nf_flowtable_udp_timeout", 0644,
+			 sizeof(unsigned int),
+			 nf_ct_udp_offload_timeout_data,
+			 proc_dointvec_jiffies),
 #endif
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_ICMP] = {
-		.procname	= "nf_conntrack_icmp_timeout",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_ICMPV6] = {
-		.procname	= "nf_conntrack_icmpv6_timeout",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_icmp_timeout", 0644,
+			 sizeof(unsigned int),
+			 nf_ct_icmp_timeout_data,
+			 proc_dointvec_jiffies),
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_icmpv6_timeout", 0644,
+			 sizeof(unsigned int),
+			 nf_ct_icmpv6_timeout_data,
+			 proc_dointvec_jiffies),
 #ifdef CONFIG_NF_CT_PROTO_SCTP
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_CLOSED] = {
-		.procname	= "nf_conntrack_sctp_timeout_closed",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_COOKIE_WAIT] = {
-		.procname	= "nf_conntrack_sctp_timeout_cookie_wait",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_COOKIE_ECHOED] = {
-		.procname	= "nf_conntrack_sctp_timeout_cookie_echoed",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_ESTABLISHED] = {
-		.procname	= "nf_conntrack_sctp_timeout_established",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_SHUTDOWN_SENT] = {
-		.procname	= "nf_conntrack_sctp_timeout_shutdown_sent",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_SHUTDOWN_RECD] = {
-		.procname	= "nf_conntrack_sctp_timeout_shutdown_recd",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_SHUTDOWN_ACK_SENT] = {
-		.procname	= "nf_conntrack_sctp_timeout_shutdown_ack_sent",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_HEARTBEAT_SENT] = {
-		.procname	= "nf_conntrack_sctp_timeout_heartbeat_sent",
-		.maxlen		= sizeof(unsigned int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_sctp_timeout_closed", 0644,
+			 sizeof(unsigned int),
+			 nf_ct_sctp_timeout_closed_data,
+			 proc_dointvec_jiffies),
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_sctp_timeout_cookie_wait", 0644,
+			 sizeof(unsigned int),
+			 nf_ct_sctp_timeout_cookie_wait_data,
+			 proc_dointvec_jiffies),
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_sctp_timeout_cookie_echoed",
+			 0644, sizeof(unsigned int),
+			 nf_ct_sctp_timeout_cookie_echoed_data,
+			 proc_dointvec_jiffies),
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_sctp_timeout_established",
+			 0644, sizeof(unsigned int),
+			 nf_ct_sctp_timeout_established_data,
+			 proc_dointvec_jiffies),
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_sctp_timeout_shutdown_sent",
+			 0644, sizeof(unsigned int),
+			 nf_ct_sctp_timeout_shutdown_sent_data,
+			 proc_dointvec_jiffies),
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_sctp_timeout_shutdown_recd",
+			 0644, sizeof(unsigned int),
+			 nf_ct_sctp_timeout_shutdown_recd_data,
+			 proc_dointvec_jiffies),
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_sctp_timeout_shutdown_ack_sent",
+			 0644, sizeof(unsigned int),
+			 nf_ct_sctp_timeout_shutdown_ack_sent_data,
+			 proc_dointvec_jiffies),
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_sctp_timeout_heartbeat_sent",
+			 0644, sizeof(unsigned int),
+			 nf_ct_sctp_timeout_heartbeat_sent_data,
+			 proc_dointvec_jiffies),
 #endif
 #ifdef CONFIG_NF_CT_PROTO_GRE
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_GRE] = {
-		.procname       = "nf_conntrack_gre_timeout",
-		.maxlen         = sizeof(unsigned int),
-		.mode           = 0644,
-		.proc_handler   = proc_dointvec_jiffies,
-	},
-	[NF_SYSCTL_CT_PROTO_TIMEOUT_GRE_STREAM] = {
-		.procname       = "nf_conntrack_gre_timeout_stream",
-		.maxlen         = sizeof(unsigned int),
-		.mode           = 0644,
-		.proc_handler   = proc_dointvec_jiffies,
-	},
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_gre_timeout", 0644,
+			 sizeof(unsigned int),
+			 nf_ct_gre_timeout_data,
+			 proc_dointvec_jiffies),
+	SYSCTL_FIELD_CUSTOM("nf_conntrack_gre_timeout_stream", 0644,
+			 sizeof(unsigned int),
+			 nf_ct_gre_stream_timeout_data,
+			 proc_dointvec_jiffies),
 #endif
 };
 
+#undef NF_CT_GLOBAL_UINT_MINMAX
+
 static struct ctl_table nf_ct_netfilter_table[] = {
 	{
 		.procname	= "nf_conntrack_max",
@@ -932,138 +931,26 @@ static struct ctl_table nf_ct_netfilter_table[] = {
 	},
 };
 
-static void nf_conntrack_standalone_init_tcp_sysctl(struct net *net,
-						    struct ctl_table *table)
-{
-	struct nf_tcp_net *tn = nf_tcp_pernet(net);
-
-#define XASSIGN(XNAME, tn) \
-	table[NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_ ## XNAME].data = \
-			&(tn)->timeouts[TCP_CONNTRACK_ ## XNAME]
-
-	XASSIGN(SYN_SENT, tn);
-	XASSIGN(SYN_RECV, tn);
-	XASSIGN(ESTABLISHED, tn);
-	XASSIGN(FIN_WAIT, tn);
-	XASSIGN(CLOSE_WAIT, tn);
-	XASSIGN(LAST_ACK, tn);
-	XASSIGN(TIME_WAIT, tn);
-	XASSIGN(CLOSE, tn);
-	XASSIGN(RETRANS, tn);
-	XASSIGN(UNACK, tn);
-#undef XASSIGN
-#define XASSIGN(XNAME, rval) \
-	table[NF_SYSCTL_CT_PROTO_TCP_ ## XNAME].data = (rval)
-
-	XASSIGN(LOOSE, &tn->tcp_loose);
-	XASSIGN(LIBERAL, &tn->tcp_be_liberal);
-	XASSIGN(MAX_RETRANS, &tn->tcp_max_retrans);
-	XASSIGN(IGNORE_INVALID_RST, &tn->tcp_ignore_invalid_rst);
-#undef XASSIGN
-
-#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
-	table[NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_OFFLOAD].data = &tn->offload_timeout;
-#endif
-
-}
-
-static void nf_conntrack_standalone_init_sctp_sysctl(struct net *net,
-						     struct ctl_table *table)
-{
-#ifdef CONFIG_NF_CT_PROTO_SCTP
-	struct nf_sctp_net *sn = nf_sctp_pernet(net);
-
-#define XASSIGN(XNAME, sn) \
-	table[NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_ ## XNAME].data = \
-			&(sn)->timeouts[SCTP_CONNTRACK_ ## XNAME]
-
-	XASSIGN(CLOSED, sn);
-	XASSIGN(COOKIE_WAIT, sn);
-	XASSIGN(COOKIE_ECHOED, sn);
-	XASSIGN(ESTABLISHED, sn);
-	XASSIGN(SHUTDOWN_SENT, sn);
-	XASSIGN(SHUTDOWN_RECD, sn);
-	XASSIGN(SHUTDOWN_ACK_SENT, sn);
-	XASSIGN(HEARTBEAT_SENT, sn);
-#undef XASSIGN
-#endif
-}
-
-static void nf_conntrack_standalone_init_gre_sysctl(struct net *net,
-						    struct ctl_table *table)
-{
-#ifdef CONFIG_NF_CT_PROTO_GRE
-	struct nf_gre_net *gn = nf_gre_pernet(net);
-
-	table[NF_SYSCTL_CT_PROTO_TIMEOUT_GRE].data = &gn->timeouts[GRE_CT_UNREPLIED];
-	table[NF_SYSCTL_CT_PROTO_TIMEOUT_GRE_STREAM].data = &gn->timeouts[GRE_CT_REPLIED];
-#endif
-}
-
 static int nf_conntrack_standalone_init_sysctl(struct net *net)
 {
+	struct sysctl_context ctx = {
+		.ns.net_ns = net,
+	};
 	struct nf_conntrack_net *cnet = nf_ct_pernet(net);
-	struct nf_udp_net *un = nf_udp_pernet(net);
-	struct ctl_table *table;
-
-	BUILD_BUG_ON(ARRAY_SIZE(nf_ct_sysctl_table) != NF_SYSCTL_CT_LAST_SYSCTL);
-
-	table = kmemdup(nf_ct_sysctl_table, sizeof(nf_ct_sysctl_table),
-			GFP_KERNEL);
-	if (!table)
-		return -ENOMEM;
 
-	table[NF_SYSCTL_CT_COUNT].data = &cnet->count;
-	table[NF_SYSCTL_CT_CHECKSUM].data = &net->ct.sysctl_checksum;
-	table[NF_SYSCTL_CT_LOG_INVALID].data = &net->ct.sysctl_log_invalid;
-	table[NF_SYSCTL_CT_ACCT].data = &net->ct.sysctl_acct;
-#ifdef CONFIG_NF_CONNTRACK_EVENTS
-	table[NF_SYSCTL_CT_EVENTS].data = &net->ct.sysctl_events;
-#endif
-#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
-	table[NF_SYSCTL_CT_TIMESTAMP].data = &net->ct.sysctl_tstamp;
-#endif
-	table[NF_SYSCTL_CT_PROTO_TIMEOUT_GENERIC].data = &nf_generic_pernet(net)->timeout;
-	table[NF_SYSCTL_CT_PROTO_TIMEOUT_ICMP].data = &nf_icmp_pernet(net)->timeout;
-	table[NF_SYSCTL_CT_PROTO_TIMEOUT_ICMPV6].data = &nf_icmpv6_pernet(net)->timeout;
-	table[NF_SYSCTL_CT_PROTO_TIMEOUT_UDP].data = &un->timeouts[UDP_CT_UNREPLIED];
-	table[NF_SYSCTL_CT_PROTO_TIMEOUT_UDP_STREAM].data = &un->timeouts[UDP_CT_REPLIED];
-#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
-	table[NF_SYSCTL_CT_PROTO_TIMEOUT_UDP_OFFLOAD].data = &un->offload_timeout;
-#endif
-
-	nf_conntrack_standalone_init_tcp_sysctl(net, table);
-	nf_conntrack_standalone_init_sctp_sysctl(net, table);
-	nf_conntrack_standalone_init_gre_sysctl(net, table);
-
-	/* Don't allow non-init_net ns to alter global sysctls */
-	if (!net_eq(&init_net, net)) {
-		table[NF_SYSCTL_CT_MAX].mode = 0444;
-		table[NF_SYSCTL_CT_EXPECT_MAX].mode = 0444;
-		table[NF_SYSCTL_CT_BUCKETS].mode = 0444;
-	}
-
-	cnet->sysctl_header = register_net_sysctl_sz(net, "net/netfilter",
-						     table,
-						     ARRAY_SIZE(nf_ct_sysctl_table));
+	cnet->sysctl_header = register_sysctl_fields(&net->sysctls, "net/netfilter",
+						     nf_ct_sysctl_table, &ctx);
 	if (!cnet->sysctl_header)
-		goto out_unregister_netfilter;
+		return -ENOMEM;
 
 	return 0;
-
-out_unregister_netfilter:
-	kfree(table);
-	return -ENOMEM;
 }
 
 static void nf_conntrack_standalone_fini_sysctl(struct net *net)
 {
 	struct nf_conntrack_net *cnet = nf_ct_pernet(net);
-	const struct ctl_table *table;
 
-	table = cnet->sysctl_header->ctl_table_arg;
 	unregister_net_sysctl_table(cnet->sysctl_header);
-	kfree(table);
 }
 #else
 static int nf_conntrack_standalone_init_sysctl(struct net *net)
net/netfilter/nf_hooks_lwtunnel.c +9 -31
index 2d890dd04ff8..7c7730dd107c 100644
@@ -54,53 +54,31 @@ int nf_hooks_lwtunnel_sysctl_handler(const struct ctl_table *table, int write,
 }
 EXPORT_SYMBOL_GPL(nf_hooks_lwtunnel_sysctl_handler);
 
-static struct ctl_table nf_lwtunnel_sysctl_table[] = {
-	{
-		.procname	= "nf_hooks_lwtunnel",
-		.data		= NULL,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= nf_hooks_lwtunnel_sysctl_handler,
-	},
+static const struct sysctl_field nf_lwtunnel_sysctl_table[] = {
+	SYSCTL_FIELD_CUSTOM("nf_hooks_lwtunnel", 0644, sizeof(int), NULL,
+			 nf_hooks_lwtunnel_sysctl_handler),
 };
 
 static int __net_init nf_lwtunnel_net_init(struct net *net)
 {
+	struct sysctl_context ctx = {
+		.ns.net_ns = net,
+	};
 	struct ctl_table_header *hdr;
-	struct ctl_table *table;
-
-	table = nf_lwtunnel_sysctl_table;
-	if (!net_eq(net, &init_net)) {
-		table = kmemdup(nf_lwtunnel_sysctl_table,
-				sizeof(nf_lwtunnel_sysctl_table),
-				GFP_KERNEL);
-		if (!table)
-			goto err_alloc;
-	}
 
-	hdr = register_net_sysctl_sz(net, "net/netfilter", table,
-				     ARRAY_SIZE(nf_lwtunnel_sysctl_table));
+	hdr = register_sysctl_fields(&net->sysctls, "net/netfilter",
+				     nf_lwtunnel_sysctl_table, &ctx);
 	if (!hdr)
-		goto err_reg;
+		return -ENOMEM;
 
 	net->nf.nf_lwtnl_dir_header = hdr;
 
 	return 0;
-err_reg:
-	if (!net_eq(net, &init_net))
-		kfree(table);
-err_alloc:
-	return -ENOMEM;
 }
 
 static void __net_exit nf_lwtunnel_net_exit(struct net *net)
 {
-	const struct ctl_table *table;
-
-	table = net->nf.nf_lwtnl_dir_header->ctl_table_arg;
 	unregister_net_sysctl_table(net->nf.nf_lwtnl_dir_header);
-	if (!net_eq(net, &init_net))
-		kfree(table);
 }
 
 static struct pernet_operations nf_lwtunnel_net_ops = {
net/netfilter/nf_log.c +45 -45

131 lines not on this page · raw message

Signature · 1 line
2.55.0
#33 lore raw

RFC PATCH 24/30 sysctl: ipvs: use sysctl_field for per-net sysctls

In reply to a message not in the archive

IPVS still builds per-net sysctl tables by cloning ctl_table arrays and then patching the copied entries with namespace-local data pointers, modes, and handler context. The main table also depends on the entry order matching the initialization code.

Use sysctl_field for the IPVS per-net sysctls instead. The sysctl descriptors can stay static and const, while data pointers and per-namespace modes are derived from the registration context.

This removes the per-net table copies from the IPVS control, LBLC, and LBLCR sysctls, and drops the table pointers that only existed so the cloned tables could be freed at namespace teardown.

---
 include/net/ip_vs.h              |   3 -
 net/netfilter/ipvs/ip_vs_ctl.c   | 533 +++++++++++++------------------
 net/netfilter/ipvs/ip_vs_lblc.c  |  49 ++-
 net/netfilter/ipvs/ip_vs_lblcr.c |  50 ++-
 4 files changed, 263 insertions(+), 372 deletions(-)
include/net/ip_vs.h +0 -3
index a02e569813d2..e0833ab99f30 100644
@@ -1214,7 +1214,6 @@ struct netns_ipvs {
 
 	/* sys-ctl struct */
 	struct ctl_table_header	*sysctl_hdr;
-	struct ctl_table	*sysctl_tbl;
 #endif
 
 	/* sysctl variables */
@@ -1259,11 +1258,9 @@ struct netns_ipvs {
 	/* ip_vs_lblc */
 	int			sysctl_lblc_expiration;
 	struct ctl_table_header	*lblc_ctl_header;
-	struct ctl_table	*lblc_ctl_table;
 	/* ip_vs_lblcr */
 	int			sysctl_lblcr_expiration;
 	struct ctl_table_header	*lblcr_ctl_header;
-	struct ctl_table	*lblcr_ctl_table;
 	unsigned long		work_flags;	/* IP_VS_WORK_* flags */
 	/* ip_vs_est */
 	struct delayed_work	est_reload_work;/* Reload kthread tasks */
net/netfilter/ipvs/ip_vs_ctl.c +226 -307

676 lines not on this page · raw message

net/netfilter/ipvs/ip_vs_lblc.c +18 -31

77 lines not on this page · raw message

net/netfilter/ipvs/ip_vs_lblcr.c +19 -31

78 lines not on this page · raw message

Signature · 1 line
2.55.0
#34 lore raw

RFC PATCH 25/30 sysctl: bridge: use sysctl_field for br_netfilter sysctls

In reply to a message not in the archive

br_netfilter clones its sysctl table for each non-init network namespace so the registration path can patch in namespace-local data pointers. The copied table then has to be kept alive until namespace teardown only so it can be freed after unregistering the sysctls.

Use sysctl_field to derive the br_netfilter per-net data from the registration context instead. This keeps the sysctl descriptors static and const, and removes the per-net table copy and unregister-time free.

---
 net/bridge/br_netfilter_hooks.c | 99 ++++++++++++---------------------
 1 file changed, 37 insertions(+), 62 deletions(-)
net/bridge/br_netfilter_hooks.c +37 -62
index 0a394e5f4391..18eccde39885 100644
@@ -1181,43 +1181,37 @@ int brnf_sysctl_call_tables(const struct ctl_table *ctl, int write,
 	return ret;
 }
 
-static struct ctl_table brnf_table[] = {
-	{
-		.procname	= "bridge-nf-call-arptables",
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= brnf_sysctl_call_tables,
-	},
-	{
-		.procname	= "bridge-nf-call-iptables",
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= brnf_sysctl_call_tables,
-	},
-	{
-		.procname	= "bridge-nf-call-ip6tables",
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= brnf_sysctl_call_tables,
-	},
-	{
-		.procname	= "bridge-nf-filter-vlan-tagged",
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= brnf_sysctl_call_tables,
-	},
-	{
-		.procname	= "bridge-nf-filter-pppoe-tagged",
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= brnf_sysctl_call_tables,
-	},
-	{
-		.procname	= "bridge-nf-pass-vlan-input-dev",
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= brnf_sysctl_call_tables,
-	},
+#define BRNF_DATA(field)						\
+static void *brnf_ ## field ## _data(const struct sysctl_context *ctx)	\
+{									\
+	struct brnf_net *brnet = net_generic(ctx->ns.net_ns, brnf_net_id); \
+									\
+	return &brnet->field;						\
+}
+
+BRNF_DATA(call_arptables)
+BRNF_DATA(call_iptables)
+BRNF_DATA(call_ip6tables)
+BRNF_DATA(filter_vlan_tagged)
+BRNF_DATA(filter_pppoe_tagged)
+BRNF_DATA(pass_vlan_indev)
+#undef BRNF_DATA
+
+static const struct sysctl_field brnf_table[] = {
+	SYSCTL_FIELD_CUSTOM("bridge-nf-call-arptables", 0644, sizeof(int),
+			 brnf_call_arptables_data, brnf_sysctl_call_tables),
+	SYSCTL_FIELD_CUSTOM("bridge-nf-call-iptables", 0644, sizeof(int),
+			 brnf_call_iptables_data, brnf_sysctl_call_tables),
+	SYSCTL_FIELD_CUSTOM("bridge-nf-call-ip6tables", 0644, sizeof(int),
+			 brnf_call_ip6tables_data, brnf_sysctl_call_tables),
+	SYSCTL_FIELD_CUSTOM("bridge-nf-filter-vlan-tagged", 0644, sizeof(int),
+			 brnf_filter_vlan_tagged_data,
+			 brnf_sysctl_call_tables),
+	SYSCTL_FIELD_CUSTOM("bridge-nf-filter-pppoe-tagged", 0644, sizeof(int),
+			 brnf_filter_pppoe_tagged_data,
+			 brnf_sysctl_call_tables),
+	SYSCTL_FIELD_CUSTOM("bridge-nf-pass-vlan-input-dev", 0644, sizeof(int),
+			 brnf_pass_vlan_indev_data, brnf_sysctl_call_tables),
 };
 
 static inline void br_netfilter_sysctl_default(struct brnf_net *brnf)
@@ -1232,33 +1226,18 @@ static inline void br_netfilter_sysctl_default(struct brnf_net *brnf)
 
 static int br_netfilter_sysctl_init_net(struct net *net)
 {
-	struct ctl_table *table = brnf_table;
+	struct sysctl_context ctx = {
+		.ns.net_ns = net,
+	};
 	struct brnf_net *brnet;
 
-	if (!net_eq(net, &init_net)) {
-		table = kmemdup(table, sizeof(brnf_table), GFP_KERNEL);
-		if (!table)
-			return -ENOMEM;
-	}
-
 	brnet = net_generic(net, brnf_net_id);
-	table[0].data = &brnet->call_arptables;
-	table[1].data = &brnet->call_iptables;
-	table[2].data = &brnet->call_ip6tables;
-	table[3].data = &brnet->filter_vlan_tagged;
-	table[4].data = &brnet->filter_pppoe_tagged;
-	table[5].data = &brnet->pass_vlan_indev;
-
 	br_netfilter_sysctl_default(brnet);
 
-	brnet->ctl_hdr = register_net_sysctl_sz(net, "net/bridge", table,
-						ARRAY_SIZE(brnf_table));
-	if (!brnet->ctl_hdr) {
-		if (!net_eq(net, &init_net))
-			kfree(table);
-
+	brnet->ctl_hdr = register_sysctl_fields(&net->sysctls, "net/bridge",
+						brnf_table, &ctx);
+	if (!brnet->ctl_hdr)
 		return -ENOMEM;
-	}
 
 	return 0;
 }
@@ -1266,11 +1245,7 @@ static int br_netfilter_sysctl_init_net(struct net *net)
 static void br_netfilter_sysctl_exit_net(struct net *net,
 					 struct brnf_net *brnet)
 {
-	const struct ctl_table *table = brnet->ctl_hdr->ctl_table_arg;
-
 	unregister_net_sysctl_table(brnet->ctl_hdr);
-	if (!net_eq(net, &init_net))
-		kfree(table);
 }
 
 static int __net_init brnf_init_net(struct net *net)
Signature · 1 line
2.55.0
#35 lore raw

RFC PATCH 26/30 sysctl: net: use sysctl_field for MPLS sysctls

In reply to a message not in the archive

MPLS clones both its per-net and per-device sysctl tables so registration can turn offset-based entries into pointers to the corresponding network namespace or MPLS device. These mutable copies then have to remain allocated until the sysctls are unregistered.

Use sysctl_field accessors to resolve per-net data from struct sysctl_context, and embed that context in an MPLS device-specific wrapper for the per-device table. This allows both descriptor arrays to remain static and const while preserving access to the network namespace and mpls_dev state.

Remove the per-instance table clones, offset arithmetic, and matching unregister-time frees.

---
 include/linux/sysctl.h |   4 ++
 net/mpls/af_mpls.c     | 154 ++++++++++++++++-------------------------
 2 files changed, 62 insertions(+), 96 deletions(-)
include/linux/sysctl.h +4 -0
index bbdb0fcfbcd4..6cf2ef4f13e8 100644
@@ -486,6 +486,10 @@ struct ctl_table_root {
 	__register_sysctl_fields(set, path, fields, ARRAY_SIZE(fields),	\
 				 (ctx), sizeof(*(ctx)))
 
+#define register_sysctl_fields_ctx(set, path, fields, ctx)		\
+	__register_sysctl_fields(set, path, fields, ARRAY_SIZE(fields),	\
+				 (&(ctx)->context), sizeof(*(ctx)))
+
 #ifdef CONFIG_SYSCTL
 
 void proc_sys_poll_notify(struct ctl_table_poll *poll);
net/mpls/af_mpls.c +58 -96

224 lines not on this page · raw message

Signature · 1 line
2.55.0
#36 lore raw

RFC PATCH 27/30 sysctl: net: use sysctl_field in IPv4 devconf sysctls

In reply to a message not in the archive

IPv4 devconf clones its sysctl tables for each registered devconf so the registration path can rewrite data pointers and attach the matching network namespace through extra2. The copied tables then have to stay alive until unregistration only so they can be freed.

Allow sysctl_field custom entries to derive extra1 and extra2 from the registration context, and use that in devinet. This keeps the devconf sysctl descriptors static and const while preserving the existing handlers' view of data, extra1 and extra2.

The per-net ip_forward entry uses the same context path, so it no longer needs a one-entry table copy either.

---
 net/ipv4/devinet.c | 258 ++++++++++++++++++++++++---------------------
 1 file changed, 140 insertions(+), 118 deletions(-)
net/ipv4/devinet.c +140 -118
index 58fe7cb69545..69b4b720a0e2 100644
@@ -2613,15 +2613,44 @@ static int ipv4_doint_and_flush(const struct ctl_table *ctl, int write,
 	return ret;
 }
 
-#define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
-	{ \
-		.procname	= name, \
-		.data		= ipv4_devconf.data + \
-				  IPV4_DEVCONF_ ## attr - 1, \
-		.maxlen		= sizeof(int), \
-		.mode		= mval, \
-		.proc_handler	= proc, \
-		.extra1		= &ipv4_devconf, \
+struct devinet_ctl_context {
+	struct sysctl_context context;
+	struct ipv4_devconf *devconf;
+};
+
+#define DEVINET_DATA(attr)						\
+static void *devinet_ ## attr ## _data(const struct sysctl_context *ctx)	\
+{									\
+	const struct devinet_ctl_context *devinet_ctx =                 \
+		container_of(ctx, struct devinet_ctl_context, context); \
+	return &devinet_ctx->devconf->data[IPV4_DEVCONF_ ## attr - 1];  \
+}
+
+static void *devinet_conf_data(const struct sysctl_context *ctx)
+{
+	const struct devinet_ctl_context *devinet_ctx =
+		container_of(ctx, struct devinet_ctl_context, context);
+
+	return devinet_ctx->devconf;
+}
+
+static void *devinet_net_data(const struct sysctl_context *ctx)
+{
+	return ctx->ns.net_ns;
+}
+
+#define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc)			\
+	{								\
+		.procname	= name,					\
+		.mode		= mval,					\
+		.type		= SYSCTL_FIELD_CUSTOM,			\
+		.ctl_custom	= {					\
+			.proc_handler	= proc,				\
+			.data		= devinet_ ## attr ## _data,	\
+			.extra1		= devinet_conf_data,		\
+			.extra2		= devinet_net_data,		\
+			.maxlen		= sizeof(int),			\
+		},							\
 	}
 
 #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
@@ -2636,104 +2665,118 @@ static int ipv4_doint_and_flush(const struct ctl_table *ctl, int write,
 #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
 
-static struct devinet_sysctl_table {
-	struct ctl_table_header *sysctl_header;
-	struct ctl_table devinet_vars[IPV4_DEVCONF_MAX];
-} devinet_sysctl = {
-	.devinet_vars = {
-		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
-					     devinet_sysctl_forward),
-		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
-		DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"),
-
-		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
-		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
-		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
-		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
-		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
-		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
-					"accept_source_route"),
-		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
-		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
-		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
-		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
-		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
-		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
-		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
-		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
-		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
-		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
-		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
-		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
-		DEVINET_SYSCTL_RW_ENTRY(ARP_EVICT_NOCARRIER,
-					"arp_evict_nocarrier"),
-		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
-		DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION,
-					"force_igmp_version"),
-		DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL,
-					"igmpv2_unsolicited_report_interval"),
-		DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL,
-					"igmpv3_unsolicited_report_interval"),
-		DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN,
-					"ignore_routes_with_linkdown"),
-		DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP,
-					"drop_gratuitous_arp"),
-
-		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
-		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
-		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
-					      "promote_secondaries"),
-		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
-					      "route_localnet"),
-		DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST,
-					      "drop_unicast_in_l2_multicast"),
-	},
+DEVINET_DATA(FORWARDING)
+DEVINET_DATA(MC_FORWARDING)
+DEVINET_DATA(BC_FORWARDING)
+DEVINET_DATA(ACCEPT_REDIRECTS)
+DEVINET_DATA(SECURE_REDIRECTS)
+DEVINET_DATA(SHARED_MEDIA)
+DEVINET_DATA(RP_FILTER)
+DEVINET_DATA(SEND_REDIRECTS)
+DEVINET_DATA(ACCEPT_SOURCE_ROUTE)
+DEVINET_DATA(ACCEPT_LOCAL)
+DEVINET_DATA(SRC_VMARK)
+DEVINET_DATA(PROXY_ARP)
+DEVINET_DATA(MEDIUM_ID)
+DEVINET_DATA(BOOTP_RELAY)
+DEVINET_DATA(LOG_MARTIANS)
+DEVINET_DATA(TAG)
+DEVINET_DATA(ARPFILTER)
+DEVINET_DATA(ARP_ANNOUNCE)
+DEVINET_DATA(ARP_IGNORE)
+DEVINET_DATA(ARP_ACCEPT)
+DEVINET_DATA(ARP_NOTIFY)
+DEVINET_DATA(ARP_EVICT_NOCARRIER)
+DEVINET_DATA(PROXY_ARP_PVLAN)
+DEVINET_DATA(FORCE_IGMP_VERSION)
+DEVINET_DATA(IGMPV2_UNSOLICITED_REPORT_INTERVAL)
+DEVINET_DATA(IGMPV3_UNSOLICITED_REPORT_INTERVAL)
+DEVINET_DATA(IGNORE_ROUTES_WITH_LINKDOWN)
+DEVINET_DATA(DROP_GRATUITOUS_ARP)
+DEVINET_DATA(NOXFRM)
+DEVINET_DATA(NOPOLICY)
+DEVINET_DATA(PROMOTE_SECONDARIES)
+DEVINET_DATA(ROUTE_LOCALNET)
+DEVINET_DATA(DROP_UNICAST_IN_L2_MULTICAST)
+
+static const struct sysctl_field devinet_sysctls[] = {
+	DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
+				     devinet_sysctl_forward),
+	DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
+	DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"),
+	DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
+	DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
+	DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
+	DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
+	DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
+	DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
+				"accept_source_route"),
+	DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
+	DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
+	DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
+	DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
+	DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
+	DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
+	DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
+	DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
+	DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
+	DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
+	DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
+	DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
+	DEVINET_SYSCTL_RW_ENTRY(ARP_EVICT_NOCARRIER,
+				"arp_evict_nocarrier"),
+	DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
+	DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION,
+				"force_igmp_version"),
+	DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL,
+				"igmpv2_unsolicited_report_interval"),
+	DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL,
+				"igmpv3_unsolicited_report_interval"),
+	DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN,
+				"ignore_routes_with_linkdown"),
+	DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP,
+				"drop_gratuitous_arp"),
+	DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
+	DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
+	DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
+				      "promote_secondaries"),
+	DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
+				      "route_localnet"),
+	DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST,
+				      "drop_unicast_in_l2_multicast"),
 };
 
 static int __devinet_sysctl_register(struct net *net, char *dev_name,
 				     int ifindex, struct ipv4_devconf *p)
 {
-	int i;
-	struct devinet_sysctl_table *t;
+	struct ctl_table_header *hdr;
+	struct devinet_ctl_context ctx = {
+		.context.ns.net_ns = net,
+		.devconf = p,
+	};
 	char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
 
-	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL_ACCOUNT);
-	if (!t)
-		goto out;
-
-	for (i = 0; i < ARRAY_SIZE(t->devinet_vars); i++) {
-		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
-		t->devinet_vars[i].extra1 = p;
-		t->devinet_vars[i].extra2 = net;
-	}
-
 	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
 
-	t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
-	if (!t->sysctl_header)
-		goto free;
+	hdr = register_sysctl_fields_ctx(&net->sysctls, path, devinet_sysctls, &ctx);
+	if (!hdr)
+		return -ENOMEM;
 
-	p->sysctl = t;
+	p->sysctl = hdr;
 
 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL,
 				    ifindex, p);
 	return 0;
-
-free:
-	kfree(t);
-out:
-	return -ENOMEM;
 }
 
 static void __devinet_sysctl_unregister(struct net *net,
 					struct ipv4_devconf *cnf, int ifindex)
 {
-	struct devinet_sysctl_table *t = cnf->sysctl;
+	struct ctl_table_header *hdr = cnf->sysctl;
 
-	if (t) {
+	if (hdr) {
 		cnf->sysctl = NULL;
-		unregister_net_sysctl_table(t->sysctl_header);
-		kfree(t);
+		unregister_net_sysctl_table(hdr);
 	}
 
 	inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL);
@@ -2764,17 +2807,9 @@ static void devinet_sysctl_unregister(struct in_device *idev)
 	neigh_sysctl_unregister(idev->arp_parms);
 }
 
-static struct ctl_table ctl_forward_entry[] = {
-	{
-		.procname	= "ip_forward",
-		.data		= &ipv4_devconf.data[
-					IPV4_DEVCONF_FORWARDING - 1],
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= devinet_sysctl_forward,
-		.extra1		= &ipv4_devconf,
-		.extra2		= &init_net,
-	},
+static const struct sysctl_field ctl_forward_entry[] = {
+	DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "ip_forward",
+				     devinet_sysctl_forward),
 };
 #endif
 
@@ -2782,7 +2817,7 @@ static __net_init int devinet_init_net(struct net *net)
 {
 #ifdef CONFIG_SYSCTL
 	struct ctl_table_header *forw_hdr;
-	struct ctl_table *tbl;
+	struct devinet_ctl_context forw_ctx;
 #endif
 	struct ipv4_devconf *all, *dflt;
 	int err;
@@ -2802,16 +2837,6 @@ static __net_init int devinet_init_net(struct net *net)
 	if (!dflt)
 		goto err_alloc_dflt;
 
-#ifdef CONFIG_SYSCTL
-	tbl = kmemdup(ctl_forward_entry, sizeof(ctl_forward_entry), GFP_KERNEL);
-	if (!tbl)
-		goto err_alloc_ctl;
-
-	tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
-	tbl[0].extra1 = all;
-	tbl[0].extra2 = net;
-#endif
-
 	if (!net_eq(net, &init_net)) {
 		switch (net_inherit_devconf()) {
 		case 3:
@@ -2847,10 +2872,15 @@ static __net_init int devinet_init_net(struct net *net)
 		goto err_reg_dflt;
 
 	err = -ENOMEM;
-	forw_hdr = register_net_sysctl_sz(net, "net/ipv4", tbl,
-					  ARRAY_SIZE(ctl_forward_entry));
+	forw_ctx = (struct devinet_ctl_context) {
+		.context.ns.net_ns = net,
+		.devconf = all,
+	};
+	forw_hdr = register_sysctl_fields_ctx(&net->sysctls, "net/ipv4",
+					      ctl_forward_entry, &forw_ctx);
 	if (!forw_hdr)
 		goto err_reg_ctl;
+
 	net->ipv4.forw_hdr = forw_hdr;
 #endif
 
@@ -2869,8 +2899,6 @@ static __net_init int devinet_init_net(struct net *net)
 err_reg_dflt:
 	__devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL);
 err_reg_all:
-	kfree(tbl);
-err_alloc_ctl:
 #endif
 	kfree(dflt);
 err_alloc_dflt:
@@ -2883,20 +2911,14 @@ static __net_init int devinet_init_net(struct net *net)
 
 static __net_exit void devinet_exit_net(struct net *net)
 {
-#ifdef CONFIG_SYSCTL
-	const struct ctl_table *tbl;
-#endif
-
 	cancel_delayed_work_sync(&net->ipv4.addr_chk_work);
 
 #ifdef CONFIG_SYSCTL
-	tbl = net->ipv4.forw_hdr->ctl_table_arg;
 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
 	__devinet_sysctl_unregister(net, net->ipv4.devconf_dflt,
 				    NETCONFA_IFINDEX_DEFAULT);
 	__devinet_sysctl_unregister(net, net->ipv4.devconf_all,
 				    NETCONFA_IFINDEX_ALL);
-	kfree(tbl);
 #endif
 	kfree(net->ipv4.devconf_dflt);
 	kfree(net->ipv4.devconf_all);
Signature · 1 line
2.55.0
#37 lore raw

RFC PATCH 28/30 sysctl: net: use sysctl_field in IPv6 devconf sysctls

In reply to a message not in the archive

IPv6 devconf allocates a private sysctl table for each registered devconf only so registration can rewrite data pointers and attach the matching inet6_dev and net namespace through extra1 and extra2.

Use sysctl_field with an IPv6 devconf registration context instead. The table descriptors can stay static and const, while custom handlers still see the same data, extra1 and extra2 values they used before.

This removes the per-devconf sysctl table clone and the matching unregister side free.

---
 net/ipv6/addrconf.c | 746 +++++++++++++++-----------------------------
 1 file changed, 252 insertions(+), 494 deletions(-)
net/ipv6/addrconf.c +252 -494
index 5476b6536eb7..a3bac460e7bd 100644
@@ -6826,516 +6826,280 @@ static int addrconf_sysctl_force_forwarding(const struct ctl_table *ctl, int wri
 }
 
 static int minus_one = -1;
-static const int two_five_five = 255;
+static int two_five_five = 255;
 static u32 ioam6_if_id_max = U16_MAX;
 
-static const struct ctl_table addrconf_sysctl[] = {
-	{
-		.procname	= "forwarding",
-		.data		= &ipv6_devconf.forwarding,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= addrconf_sysctl_forward,
-	},
-	{
-		.procname	= "hop_limit",
-		.data		= &ipv6_devconf.hop_limit,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= (void *)SYSCTL_ONE,
-		.extra2		= (void *)&two_five_five,
-	},
-	{
-		.procname	= "mtu",
-		.data		= &ipv6_devconf.mtu6,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= addrconf_sysctl_mtu,
-	},
-	{
-		.procname	= "accept_ra",
-		.data		= &ipv6_devconf.accept_ra,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "accept_redirects",
-		.data		= &ipv6_devconf.accept_redirects,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "autoconf",
-		.data		= &ipv6_devconf.autoconf,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "dad_transmits",
-		.data		= &ipv6_devconf.dad_transmits,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "router_solicitations",
-		.data		= &ipv6_devconf.rtr_solicits,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &minus_one,
-	},
-	{
-		.procname	= "router_solicitation_interval",
-		.data		= &ipv6_devconf.rtr_solicit_interval,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	{
-		.procname	= "router_solicitation_max_interval",
-		.data		= &ipv6_devconf.rtr_solicit_max_interval,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	{
-		.procname	= "router_solicitation_delay",
-		.data		= &ipv6_devconf.rtr_solicit_delay,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
-	{
-		.procname	= "force_mld_version",
-		.data		= &ipv6_devconf.force_mld_version,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "mldv1_unsolicited_report_interval",
-		.data		=
-			&ipv6_devconf.mldv1_unsolicited_report_interval,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_ms_jiffies,
-	},
-	{
-		.procname	= "mldv2_unsolicited_report_interval",
-		.data		=
-			&ipv6_devconf.mldv2_unsolicited_report_interval,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_ms_jiffies,
-	},
-	{
-		.procname	= "use_tempaddr",
-		.data		= &ipv6_devconf.use_tempaddr,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "temp_valid_lft",
-		.data		= &ipv6_devconf.temp_valid_lft,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "temp_prefered_lft",
-		.data		= &ipv6_devconf.temp_prefered_lft,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname       = "regen_min_advance",
-		.data           = &ipv6_devconf.regen_min_advance,
-		.maxlen         = sizeof(int),
-		.mode           = 0644,
-		.proc_handler   = proc_dointvec,
-	},
-	{
-		.procname	= "regen_max_retry",
-		.data		= &ipv6_devconf.regen_max_retry,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "max_desync_factor",
-		.data		= &ipv6_devconf.max_desync_factor,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "max_addresses",
-		.data		= &ipv6_devconf.max_addresses,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "accept_ra_defrtr",
-		.data		= &ipv6_devconf.accept_ra_defrtr,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "ra_defrtr_metric",
-		.data		= &ipv6_devconf.ra_defrtr_metric,
-		.maxlen		= sizeof(u32),
-		.mode		= 0644,
-		.proc_handler	= proc_douintvec_minmax,
-		.extra1		= (void *)SYSCTL_ONE,
-	},
-	{
-		.procname	= "accept_ra_min_hop_limit",
-		.data		= &ipv6_devconf.accept_ra_min_hop_limit,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "accept_ra_min_lft",
-		.data		= &ipv6_devconf.accept_ra_min_lft,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "accept_ra_pinfo",
-		.data		= &ipv6_devconf.accept_ra_pinfo,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "ra_honor_pio_life",
-		.data		= &ipv6_devconf.ra_honor_pio_life,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
-	},
-	{
-		.procname	= "ra_honor_pio_pflag",
-		.data		= &ipv6_devconf.ra_honor_pio_pflag,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
-	},
+struct addrconf_ctl_context {
+	struct sysctl_context context;
+	struct ipv6_devconf *devconf;
+};
+
+#define ADDRCONF_DATA(type, field)						\
+static type *addrconf_ ## field ## _data(const struct sysctl_context *ctx)	\
+{										\
+	const struct addrconf_ctl_context *addrconf_ctx =			\
+		container_of(ctx, struct addrconf_ctl_context, context);	\
+	return &addrconf_ctx->devconf->field;					\
+}
+
+static void *addrconf_idev_data(const struct sysctl_context *ctx)
+{
+	const struct addrconf_ctl_context *addrconf_ctx =
+		container_of(ctx, struct addrconf_ctl_context, context);
+	struct ipv6_devconf *cnf = addrconf_ctx->devconf;
+	struct net *net = ctx->ns.net_ns;
+
+	if (cnf == net->ipv6.devconf_all || cnf == net->ipv6.devconf_dflt)
+		return NULL;
+
+	return container_of(cnf, struct inet6_dev, cnf);
+}
+
+static void *addrconf_net_data(const struct sysctl_context *ctx)
+{
+	return ctx->ns.net_ns;
+}
+
+#define ADDRCONF_CUSTOM_ENTRY(field, name, mval, len, proc)		\
+	{								\
+		.procname	= name,					\
+		.mode		= mval,					\
+		.type		= SYSCTL_FIELD_CUSTOM,			\
+		.ctl_custom	= {					\
+			.proc_handler	= proc,				\
+			.data		= addrconf_ ## field ## _data,	\
+			.extra1		= addrconf_idev_data,		\
+			.extra2		= addrconf_net_data,		\
+			.maxlen		= len,				\
+		},							\
+	}
+
+#define ADDRCONF_INT_ENTRY(field, name) \
+	SYSCTL_FIELD_INT(name, 0644, addrconf_ ## field ## _data)
+
+#define ADDRCONF_INT_RO_ENTRY(field, name) \
+	SYSCTL_FIELD_INT(name, 0444, addrconf_ ## field ## _data)
+
+#define ADDRCONF_INT_MINMAX_ENTRY(field, name, min, max) \
+	SYSCTL_FIELD_STATIC_INT_MINMAX(name, 0644, \
+				       addrconf_ ## field ## _data, min, max)
+
+#define ADDRCONF_UINT_ENTRY(field, name) \
+	SYSCTL_FIELD_UINT(name, 0644, addrconf_ ## field ## _data)
+
+#define ADDRCONF_UINT_MINMAX_ENTRY(field, name, min, max) \
+	SYSCTL_FIELD_STATIC_UINT_MINMAX(name, 0644, \
+				        addrconf_ ## field ## _data, min, max)
+
+#define ADDRCONF_U8_MINMAX_ENTRY(field, name, min, max) \
+	SYSCTL_FIELD_STATIC_U8_MINMAX(name, 0644, \
+				      addrconf_ ## field ## _data, min, max)
+
+ADDRCONF_DATA(void, forwarding)
+ADDRCONF_DATA(int, hop_limit)
+ADDRCONF_DATA(void, mtu6)
+ADDRCONF_DATA(int, accept_ra)
+ADDRCONF_DATA(int, accept_redirects)
+ADDRCONF_DATA(int, autoconf)
+ADDRCONF_DATA(int, dad_transmits)
+ADDRCONF_DATA(int, rtr_solicits)
+ADDRCONF_DATA(void, rtr_solicit_interval)
+ADDRCONF_DATA(void, rtr_solicit_max_interval)
+ADDRCONF_DATA(void, rtr_solicit_delay)
+ADDRCONF_DATA(int, force_mld_version)
+ADDRCONF_DATA(void, mldv1_unsolicited_report_interval)
+ADDRCONF_DATA(void, mldv2_unsolicited_report_interval)
+ADDRCONF_DATA(int, use_tempaddr)
+ADDRCONF_DATA(int, temp_valid_lft)
+ADDRCONF_DATA(int, temp_prefered_lft)
+ADDRCONF_DATA(int, regen_min_advance)
+ADDRCONF_DATA(int, regen_max_retry)
+ADDRCONF_DATA(int, max_desync_factor)
+ADDRCONF_DATA(int, max_addresses)
+ADDRCONF_DATA(int, accept_ra_defrtr)
+ADDRCONF_DATA(unsigned int, ra_defrtr_metric)
+ADDRCONF_DATA(int, accept_ra_min_hop_limit)
+ADDRCONF_DATA(int, accept_ra_min_lft)
+ADDRCONF_DATA(int, accept_ra_pinfo)
+ADDRCONF_DATA(u8, ra_honor_pio_life)
+ADDRCONF_DATA(u8, ra_honor_pio_pflag)
 #ifdef CONFIG_IPV6_ROUTER_PREF
-	{
-		.procname	= "accept_ra_rtr_pref",
-		.data		= &ipv6_devconf.accept_ra_rtr_pref,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "router_probe_interval",
-		.data		= &ipv6_devconf.rtr_probe_interval,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_jiffies,
-	},
+ADDRCONF_DATA(int, accept_ra_rtr_pref)
+ADDRCONF_DATA(void, rtr_probe_interval)
 #ifdef CONFIG_IPV6_ROUTE_INFO
-	{
-		.procname	= "accept_ra_rt_info_min_plen",
-		.data		= &ipv6_devconf.accept_ra_rt_info_min_plen,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "accept_ra_rt_info_max_plen",
-		.data		= &ipv6_devconf.accept_ra_rt_info_max_plen,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
+ADDRCONF_DATA(int, accept_ra_rt_info_min_plen)
+ADDRCONF_DATA(int, accept_ra_rt_info_max_plen)
 #endif
 #endif
-	{
-		.procname	= "proxy_ndp",
-		.data		= &ipv6_devconf.proxy_ndp,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= addrconf_sysctl_proxy_ndp,
-	},
-	{
-		.procname	= "accept_source_route",
-		.data		= &ipv6_devconf.accept_source_route,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
+ADDRCONF_DATA(void, proxy_ndp)
+ADDRCONF_DATA(int, accept_source_route)
 #ifdef CONFIG_IPV6_OPTIMISTIC_DAD
-	{
-		.procname	= "optimistic_dad",
-		.data		= &ipv6_devconf.optimistic_dad,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler   = proc_dointvec,
-	},
-	{
-		.procname	= "use_optimistic",
-		.data		= &ipv6_devconf.use_optimistic,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
+ADDRCONF_DATA(int, optimistic_dad)
+ADDRCONF_DATA(int, use_optimistic)
 #endif
 #ifdef CONFIG_IPV6_MROUTE
-	{
-		.procname	= "mc_forwarding",
-		.data		= &ipv6_devconf.mc_forwarding,
-		.maxlen		= sizeof(int),
-		.mode		= 0444,
-		.proc_handler	= proc_dointvec,
-	},
+ADDRCONF_DATA(void, mc_forwarding)
 #endif
-	{
-		.procname	= "disable_ipv6",
-		.data		= &ipv6_devconf.disable_ipv6,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= addrconf_sysctl_disable,
-	},
-	{
-		.procname	= "accept_dad",
-		.data		= &ipv6_devconf.accept_dad,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "force_tllao",
-		.data		= &ipv6_devconf.force_tllao,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec
-	},
-	{
-		.procname	= "ndisc_notify",
-		.data		= &ipv6_devconf.ndisc_notify,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec
-	},
-	{
-		.procname	= "suppress_frag_ndisc",
-		.data		= &ipv6_devconf.suppress_frag_ndisc,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec
-	},
-	{
-		.procname	= "accept_ra_from_local",
-		.data		= &ipv6_devconf.accept_ra_from_local,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "accept_ra_mtu",
-		.data		= &ipv6_devconf.accept_ra_mtu,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "stable_secret",
-		.data		= &ipv6_devconf.stable_secret,
-		.maxlen		= IPV6_MAX_STRLEN,
-		.mode		= 0600,
-		.proc_handler	= addrconf_sysctl_stable_secret,
-	},
-	{
-		.procname	= "use_oif_addrs_only",
-		.data		= &ipv6_devconf.use_oif_addrs_only,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "ignore_routes_with_linkdown",
-		.data		= &ipv6_devconf.ignore_routes_with_linkdown,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= addrconf_sysctl_ignore_routes_with_linkdown,
-	},
-	{
-		.procname	= "drop_unicast_in_l2_multicast",
-		.data		= &ipv6_devconf.drop_unicast_in_l2_multicast,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "drop_unsolicited_na",
-		.data		= &ipv6_devconf.drop_unsolicited_na,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
-	{
-		.procname	= "keep_addr_on_down",
-		.data		= &ipv6_devconf.keep_addr_on_down,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-
-	},
-	{
-		.procname	= "seg6_enabled",
-		.data		= &ipv6_devconf.seg6_enabled,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
+ADDRCONF_DATA(void, disable_ipv6)
+ADDRCONF_DATA(int, accept_dad)
+ADDRCONF_DATA(int, force_tllao)
+ADDRCONF_DATA(int, ndisc_notify)
+ADDRCONF_DATA(int, suppress_frag_ndisc)
+ADDRCONF_DATA(int, accept_ra_from_local)
+ADDRCONF_DATA(int, accept_ra_mtu)
+ADDRCONF_DATA(void, stable_secret)
+ADDRCONF_DATA(int, use_oif_addrs_only)
+ADDRCONF_DATA(void, ignore_routes_with_linkdown)
+ADDRCONF_DATA(int, drop_unicast_in_l2_multicast)
+ADDRCONF_DATA(int, drop_unsolicited_na)
+ADDRCONF_DATA(int, keep_addr_on_down)
+ADDRCONF_DATA(int, seg6_enabled)
 #ifdef CONFIG_IPV6_SEG6_HMAC
-	{
-		.procname	= "seg6_require_hmac",
-		.data		= &ipv6_devconf.seg6_require_hmac,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-	},
+ADDRCONF_DATA(int, seg6_require_hmac)
 #endif
-	{
-		.procname       = "enhanced_dad",
-		.data           = &ipv6_devconf.enhanced_dad,
-		.maxlen         = sizeof(int),
-		.mode           = 0644,
-		.proc_handler   = proc_dointvec,
-	},
-	{
-		.procname	= "addr_gen_mode",
-		.data		= &ipv6_devconf.addr_gen_mode,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= addrconf_sysctl_addr_gen_mode,
-	},
-	{
-		.procname       = "disable_policy",
-		.data           = &ipv6_devconf.disable_policy,
-		.maxlen         = sizeof(int),
-		.mode           = 0644,
-		.proc_handler   = addrconf_sysctl_disable_policy,
-	},
-	{
-		.procname	= "ndisc_tclass",
-		.data		= &ipv6_devconf.ndisc_tclass,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= (void *)SYSCTL_ZERO,
-		.extra2		= (void *)&two_five_five,
-	},
-	{
-		.procname	= "rpl_seg_enabled",
-		.data		= &ipv6_devconf.rpl_seg_enabled,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler   = proc_dointvec_minmax,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE,
-	},
-	{
-		.procname	= "ioam6_enabled",
-		.data		= &ipv6_devconf.ioam6_enabled,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= (void *)SYSCTL_ZERO,
-		.extra2		= (void *)SYSCTL_ONE,
-	},
-	{
-		.procname	= "ioam6_id",
-		.data		= &ipv6_devconf.ioam6_id,
-		.maxlen		= sizeof(u32),
-		.mode		= 0644,
-		.proc_handler	= proc_douintvec_minmax,
-		.extra1		= (void *)SYSCTL_ZERO,
-		.extra2		= (void *)&ioam6_if_id_max,
-	},
-	{
-		.procname	= "ioam6_id_wide",
-		.data		= &ipv6_devconf.ioam6_id_wide,
-		.maxlen		= sizeof(u32),
-		.mode		= 0644,
-		.proc_handler	= proc_douintvec,
-	},
-	{
-		.procname	= "ndisc_evict_nocarrier",
-		.data		= &ipv6_devconf.ndisc_evict_nocarrier,
-		.maxlen		= sizeof(u8),
-		.mode		= 0644,
-		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= (void *)SYSCTL_ZERO,
-		.extra2		= (void *)SYSCTL_ONE,
-	},
-	{
-		.procname	= "accept_untracked_na",
-		.data		= &ipv6_devconf.accept_untracked_na,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO,
-	},
-	{
-		.procname	= "force_forwarding",
-		.data		= &ipv6_devconf.force_forwarding,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= addrconf_sysctl_force_forwarding,
-	},
+ADDRCONF_DATA(void, enhanced_dad)
+ADDRCONF_DATA(void, addr_gen_mode)
+ADDRCONF_DATA(void, disable_policy)
+ADDRCONF_DATA(int, ndisc_tclass)
+ADDRCONF_DATA(int, rpl_seg_enabled)
+ADDRCONF_DATA(u8, ioam6_enabled)
+ADDRCONF_DATA(unsigned int, ioam6_id)
+ADDRCONF_DATA(unsigned int, ioam6_id_wide)
+ADDRCONF_DATA(u8, ndisc_evict_nocarrier)
+ADDRCONF_DATA(int, accept_untracked_na)
+ADDRCONF_DATA(void, force_forwarding)
+
+static const struct sysctl_field addrconf_sysctl[] = {
+	ADDRCONF_CUSTOM_ENTRY(forwarding, "forwarding", 0644, sizeof(int),
+			      addrconf_sysctl_forward),
+	ADDRCONF_INT_MINMAX_ENTRY(hop_limit, "hop_limit", SYSCTL_ONE,
+				  &two_five_five),
+	ADDRCONF_CUSTOM_ENTRY(mtu6, "mtu", 0644, sizeof(int),
+			      addrconf_sysctl_mtu),
+	ADDRCONF_INT_ENTRY(accept_ra, "accept_ra"),
+	ADDRCONF_INT_ENTRY(accept_redirects, "accept_redirects"),
+	ADDRCONF_INT_ENTRY(autoconf, "autoconf"),
+	ADDRCONF_INT_ENTRY(dad_transmits, "dad_transmits"),
+	ADDRCONF_INT_MINMAX_ENTRY(rtr_solicits, "router_solicitations",
+				  &minus_one, NULL),
+	ADDRCONF_CUSTOM_ENTRY(rtr_solicit_interval,
+			      "router_solicitation_interval", 0644,
+			      sizeof(int), proc_dointvec_jiffies),
+	ADDRCONF_CUSTOM_ENTRY(rtr_solicit_max_interval,
+			      "router_solicitation_max_interval", 0644,
+			      sizeof(int), proc_dointvec_jiffies),
+	ADDRCONF_CUSTOM_ENTRY(rtr_solicit_delay,
+			      "router_solicitation_delay", 0644,
+			      sizeof(int), proc_dointvec_jiffies),
+	ADDRCONF_INT_ENTRY(force_mld_version, "force_mld_version"),
+	ADDRCONF_CUSTOM_ENTRY(mldv1_unsolicited_report_interval,
+			      "mldv1_unsolicited_report_interval", 0644,
+			      sizeof(int), proc_dointvec_ms_jiffies),
+	ADDRCONF_CUSTOM_ENTRY(mldv2_unsolicited_report_interval,
+			      "mldv2_unsolicited_report_interval", 0644,
+			      sizeof(int), proc_dointvec_ms_jiffies),
+	ADDRCONF_INT_ENTRY(use_tempaddr, "use_tempaddr"),
+	ADDRCONF_INT_ENTRY(temp_valid_lft, "temp_valid_lft"),
+	ADDRCONF_INT_ENTRY(temp_prefered_lft, "temp_prefered_lft"),
+	ADDRCONF_INT_ENTRY(regen_min_advance, "regen_min_advance"),
+	ADDRCONF_INT_ENTRY(regen_max_retry, "regen_max_retry"),
+	ADDRCONF_INT_ENTRY(max_desync_factor, "max_desync_factor"),
+	ADDRCONF_INT_ENTRY(max_addresses, "max_addresses"),
+	ADDRCONF_INT_ENTRY(accept_ra_defrtr, "accept_ra_defrtr"),
+	ADDRCONF_UINT_MINMAX_ENTRY(ra_defrtr_metric, "ra_defrtr_metric",
+				   SYSCTL_UINT_ONE, NULL),
+	ADDRCONF_INT_ENTRY(accept_ra_min_hop_limit,
+			 "accept_ra_min_hop_limit"),
+	ADDRCONF_INT_ENTRY(accept_ra_min_lft, "accept_ra_min_lft"),
+	ADDRCONF_INT_ENTRY(accept_ra_pinfo, "accept_ra_pinfo"),
+	ADDRCONF_U8_MINMAX_ENTRY(ra_honor_pio_life, "ra_honor_pio_life",
+				 SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	ADDRCONF_U8_MINMAX_ENTRY(ra_honor_pio_pflag, "ra_honor_pio_pflag",
+				 SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+#ifdef CONFIG_IPV6_ROUTER_PREF
+	ADDRCONF_INT_ENTRY(accept_ra_rtr_pref, "accept_ra_rtr_pref"),
+	ADDRCONF_CUSTOM_ENTRY(rtr_probe_interval, "router_probe_interval",
+			      0644, sizeof(int), proc_dointvec_jiffies),
+#ifdef CONFIG_IPV6_ROUTE_INFO
+	ADDRCONF_INT_ENTRY(accept_ra_rt_info_min_plen,
+			 "accept_ra_rt_info_min_plen"),
+	ADDRCONF_INT_ENTRY(accept_ra_rt_info_max_plen,
+			 "accept_ra_rt_info_max_plen"),
+#endif
+#endif
+	ADDRCONF_CUSTOM_ENTRY(proxy_ndp, "proxy_ndp", 0644, sizeof(int),
+			      addrconf_sysctl_proxy_ndp),
+	ADDRCONF_INT_ENTRY(accept_source_route, "accept_source_route"),
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	ADDRCONF_INT_ENTRY(optimistic_dad, "optimistic_dad"),
+	ADDRCONF_INT_ENTRY(use_optimistic, "use_optimistic"),
+#endif
+#ifdef CONFIG_IPV6_MROUTE
+	ADDRCONF_CUSTOM_ENTRY(mc_forwarding, "mc_forwarding", 0444,
+			      sizeof(int), proc_dointvec),
+#endif
+	ADDRCONF_CUSTOM_ENTRY(disable_ipv6, "disable_ipv6", 0644,
+			      sizeof(int), addrconf_sysctl_disable),
+	ADDRCONF_INT_ENTRY(accept_dad, "accept_dad"),
+	ADDRCONF_INT_ENTRY(force_tllao, "force_tllao"),
+	ADDRCONF_INT_ENTRY(ndisc_notify, "ndisc_notify"),
+	ADDRCONF_INT_ENTRY(suppress_frag_ndisc, "suppress_frag_ndisc"),
+	ADDRCONF_INT_ENTRY(accept_ra_from_local, "accept_ra_from_local"),
+	ADDRCONF_INT_ENTRY(accept_ra_mtu, "accept_ra_mtu"),
+	ADDRCONF_CUSTOM_ENTRY(stable_secret, "stable_secret", 0600,
+			      IPV6_MAX_STRLEN, addrconf_sysctl_stable_secret),
+	ADDRCONF_INT_ENTRY(use_oif_addrs_only, "use_oif_addrs_only"),
+	ADDRCONF_CUSTOM_ENTRY(ignore_routes_with_linkdown,
+			      "ignore_routes_with_linkdown", 0644, sizeof(int),
+			      addrconf_sysctl_ignore_routes_with_linkdown),
+	ADDRCONF_INT_ENTRY(drop_unicast_in_l2_multicast,
+			 "drop_unicast_in_l2_multicast"),
+	ADDRCONF_INT_ENTRY(drop_unsolicited_na, "drop_unsolicited_na"),
+	ADDRCONF_INT_ENTRY(keep_addr_on_down, "keep_addr_on_down"),
+	ADDRCONF_INT_ENTRY(seg6_enabled, "seg6_enabled"),
+#ifdef CONFIG_IPV6_SEG6_HMAC
+	ADDRCONF_INT_ENTRY(seg6_require_hmac, "seg6_require_hmac"),
+#endif
+	ADDRCONF_CUSTOM_ENTRY(enhanced_dad, "enhanced_dad", 0644,
+			      sizeof(int), proc_dointvec),
+	ADDRCONF_CUSTOM_ENTRY(addr_gen_mode, "addr_gen_mode", 0644,
+			      sizeof(int), addrconf_sysctl_addr_gen_mode),
+	ADDRCONF_CUSTOM_ENTRY(disable_policy, "disable_policy", 0644,
+			      sizeof(int), addrconf_sysctl_disable_policy),
+	ADDRCONF_INT_MINMAX_ENTRY(ndisc_tclass, "ndisc_tclass",
+				  SYSCTL_ZERO, &two_five_five),
+	ADDRCONF_INT_MINMAX_ENTRY(rpl_seg_enabled, "rpl_seg_enabled",
+				  SYSCTL_ZERO, SYSCTL_ONE),
+	ADDRCONF_U8_MINMAX_ENTRY(ioam6_enabled, "ioam6_enabled",
+				 SYSCTL_UINT_ZERO, SYSCTL_UINT_ONE),
+	ADDRCONF_UINT_MINMAX_ENTRY(ioam6_id, "ioam6_id",
+				   SYSCTL_UINT_ZERO, &ioam6_if_id_max),
+	ADDRCONF_UINT_ENTRY(ioam6_id_wide, "ioam6_id_wide"),
+	ADDRCONF_U8_MINMAX_ENTRY(ndisc_evict_nocarrier,
+				 "ndisc_evict_nocarrier", SYSCTL_UINT_ZERO,
+				 SYSCTL_UINT_ONE),
+	ADDRCONF_INT_MINMAX_ENTRY(accept_untracked_na, "accept_untracked_na",
+				  SYSCTL_ZERO, SYSCTL_TWO),
+	ADDRCONF_CUSTOM_ENTRY(force_forwarding, "force_forwarding", 0644,
+			      sizeof(int), addrconf_sysctl_force_forwarding),
 };
 
 static int __addrconf_sysctl_register(struct net *net, char *dev_name,
 		struct inet6_dev *idev, struct ipv6_devconf *p)
 {
-	size_t table_size = ARRAY_SIZE(addrconf_sysctl);
-	int i, ifindex;
-	struct ctl_table *table;
+	struct addrconf_ctl_context ctx = {
+		.context.ns.net_ns = net,
+		.devconf = p,
+	};
+	int ifindex;
 	char path[sizeof("net/ipv6/conf/") + IFNAMSIZ];
 
-	table = kmemdup(addrconf_sysctl, sizeof(addrconf_sysctl), GFP_KERNEL_ACCOUNT);
-	if (!table)
-		goto out;
-
-	for (i = 0; i < table_size; i++) {
-		table[i].data += (char *)p - (char *)&ipv6_devconf;
-		/* If one of these is already set, then it is not safe to
-		 * overwrite either of them: this makes proc_dointvec_minmax
-		 * usable.
-		 */
-		if (!table[i].extra1 && !table[i].extra2) {
-			table[i].extra1 = idev; /* embedded; no ref */
-			table[i].extra2 = net;
-		}
-	}
-
 	snprintf(path, sizeof(path), "net/ipv6/conf/%s", dev_name);
 
-	p->sysctl_header = register_net_sysctl_sz(net, path, table,
-						  table_size);
+	p->sysctl_header = register_sysctl_fields_ctx(&net->sysctls, path,
+						      addrconf_sysctl, &ctx);
 	if (!p->sysctl_header)
-		goto free;
+		goto out;
 
 	if (!strcmp(dev_name, "all"))
 		ifindex = NETCONFA_IFINDEX_ALL;
@@ -7347,8 +7111,6 @@ static int __addrconf_sysctl_register(struct net *net, char *dev_name,
 				     ifindex, p);
 	return 0;
 
-free:
-	kfree(table);
 out:
 	return -ENOBUFS;
 }
@@ -7356,15 +7118,11 @@ static int __addrconf_sysctl_register(struct net *net, char *dev_name,
 static void __addrconf_sysctl_unregister(struct net *net,
 					 struct ipv6_devconf *p, int ifindex)
 {
-	const struct ctl_table *table;
-
 	if (!p->sysctl_header)
 		return;
 
-	table = p->sysctl_header->ctl_table_arg;
 	unregister_net_sysctl_table(p->sysctl_header);
 	p->sysctl_header = NULL;
-	kfree(table);
 
 	inet6_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL);
 }
Signature · 1 line
2.55.0
#38 lore raw

RFC PATCH 29/30 sysctl: net: use sysctl_field in neighbour sysctls

In reply to a message not in the archive

Neighbour sysctl registration clones the template table so it can rewrite data pointers, attach the net device and neigh_parms through extra1 and extra2, and override a few IPv6 NDISC handlers.

Use sysctl_field with a neigh_parms registration context instead. The sysctl descriptors can stay static and const, while accessors provide the same data, extra1 and extra2 values to the existing handlers.

Keep the NDISC handler selection local to neighbour sysctls by storing the optional handler in neigh_parms and using fixed wrapper handlers for the affected entries. This avoids cloning the sysctl table while preserving the old IPv6 notification behavior.

---
 include/net/neighbour.h |   1 +
 net/core/neighbour.c    | 329 ++++++++++++++++++++++++----------------
 2 files changed, 202 insertions(+), 128 deletions(-)
include/net/neighbour.h +1 -0
index 8860cc2175fc..73b6c7017264 100644
@@ -78,6 +78,7 @@ struct neigh_parms {
 	struct neigh_table *tbl;
 
 	void	*sysctl_table;
+	proc_handler *sysctl_handler;
 
 	int dead;
 	refcount_t refcnt;
net/core/neighbour.c +201 -128

383 lines not on this page · raw message

Signature · 1 line
2.55.0
#39 lore raw

RFC PATCH 30/30 sysctl: parport: use sysctl_field for dynamic sysctls

In reply to a message not in the archive

parport builds per-port and per-device sysctl tables by cloning template ctl_table arrays and then patching the data and extra pointers at fixed indexes. That keeps the static table definitions writable in practice and makes the registration path depend on the exact order of the table entries.

Use sysctl_field for those tables instead. The per-object state is supplied through the registration context, so the table layout can stay static and the data pointers are resolved when proc_sysctl builds the effective ctl_table. This removes the need to duplicate the tables for every parport object and keeps the per-entry state binding next to the entry definition.

---
 drivers/parport/procfs.c | 317 ++++++++++++++++++---------------------
 fs/proc/proc_sysctl.c    |  11 ++
 include/linux/parport.h  |   6 +-
 include/linux/sysctl.h   |  12 ++
 4 files changed, 171 insertions(+), 175 deletions(-)
drivers/parport/procfs.c +144 -173
index 3880460e67f2..6348d1342069 100644
@@ -236,147 +236,134 @@ do {									\
 	return 0;
 }
 
-static const unsigned long parport_min_timeslice_value =
+static unsigned long parport_min_timeslice_value =
 PARPORT_MIN_TIMESLICE_VALUE;
 
-static const unsigned long parport_max_timeslice_value =
+static unsigned long parport_max_timeslice_value =
 PARPORT_MAX_TIMESLICE_VALUE;
 
-static const  int parport_min_spintime_value =
+static int parport_min_spintime_value =
 PARPORT_MIN_SPINTIME_VALUE;
 
-static const int parport_max_spintime_value =
+static int parport_max_spintime_value =
 PARPORT_MAX_SPINTIME_VALUE;
 
+struct parport_ctl_context {
+	struct sysctl_context context;
+	struct parport *port;
+};
 
-struct parport_sysctl_table {
-	struct ctl_table_header *port_header;
-	struct ctl_table_header *devices_header;
-#ifdef CONFIG_PARPORT_1284
-	struct ctl_table vars[10];
-#else
-	struct ctl_table vars[5];
-#endif /* IEEE 1284 support */
-	struct ctl_table device_dir[1];
+struct pardevice_ctl_context {
+	struct sysctl_context context;
+	struct pardevice *device;
 };
 
-static const struct parport_sysctl_table parport_sysctl_template = {
-	.port_header = NULL,
-	.devices_header = NULL,
-	{
-		{
-			.procname	= "spintime",
-			.data		= NULL,
-			.maxlen		= sizeof(int),
-			.mode		= 0644,
-			.proc_handler	= proc_dointvec_minmax,
-			.extra1		= (void*) &parport_min_spintime_value,
-			.extra2		= (void*) &parport_max_spintime_value
-		},
-		{
-			.procname	= "base-addr",
-			.data		= NULL,
-			.maxlen		= 0,
-			.mode		= 0444,
-			.proc_handler	= do_hardware_base_addr
-		},
-		{
-			.procname	= "irq",
-			.data		= NULL,
-			.maxlen		= 0,
-			.mode		= 0444,
-			.proc_handler	= do_hardware_irq
-		},
-		{
-			.procname	= "dma",
-			.data		= NULL,
-			.maxlen		= 0,
-			.mode		= 0444,
-			.proc_handler	= do_hardware_dma
-		},
-		{
-			.procname	= "modes",
-			.data		= NULL,
-			.maxlen		= 0,
-			.mode		= 0444,
-			.proc_handler	= do_hardware_modes
-		},
+static int *parport_spintime_data(const struct sysctl_context *ctx)
+{
+	const struct parport_ctl_context *parport_ctx =
+		container_of(ctx, struct parport_ctl_context, context);
+
+	return &parport_ctx->port->spintime;
+}
+
+static void *pardevice_timeslice_data(const struct sysctl_context *ctx)
+{
+	const struct pardevice_ctl_context *pardevice_ctx =
+		container_of(ctx, struct pardevice_ctl_context, context);
+
+	return &pardevice_ctx->device->timeslice;
+}
+
+static void *parport_data(const struct sysctl_context *ctx)
+{
+	const struct parport_ctl_context *parport_ctx =
+		container_of(ctx, struct parport_ctl_context, context);
+
+	return parport_ctx->port;
+}
+
+static void *parport_min_timeslice_data(const struct sysctl_context *ctx)
+{
+	return &parport_min_timeslice_value;
+}
+
+static void *parport_max_timeslice_data(const struct sysctl_context *ctx)
+{
+	return &parport_max_timeslice_value;
+}
+
 #ifdef CONFIG_PARPORT_1284
-		{
-			.procname	= "autoprobe",
-			.data		= NULL,
-			.maxlen		= 0,
-			.mode		= 0444,
-			.proc_handler	= do_autoprobe
-		},
-		{
-			.procname	= "autoprobe0",
-			.data		= NULL,
-			.maxlen		= 0,
-			.mode		= 0444,
-			.proc_handler	= do_autoprobe
-		},
-		{
-			.procname	= "autoprobe1",
-			.data		= NULL,
-			.maxlen		= 0,
-			.mode		= 0444,
-			.proc_handler	= do_autoprobe
-		},
-		{
-			.procname	= "autoprobe2",
-			.data		= NULL,
-			.maxlen		= 0,
-			.mode		= 0444,
-			.proc_handler	= do_autoprobe
-		},
-		{
-			.procname	= "autoprobe3",
-			.data		= NULL,
-			.maxlen		= 0,
-			.mode		= 0444,
-			.proc_handler	= do_autoprobe
-		},
-#endif /* IEEE 1284 support */
-	},
-	{
-		{
-			.procname	= "active",
-			.data		= NULL,
-			.maxlen		= 0,
-			.mode		= 0444,
-			.proc_handler	= do_active_device
-		},
-	},
+#define PARPORT_PROBE_DATA(index)							\
+static void *parport_probe_info_ ## index ## _data(const struct sysctl_context *ctx)	\
+{											\
+	const struct parport_ctl_context *parport_ctx =					\
+		container_of(ctx, struct parport_ctl_context, context);			\
+	return &parport_ctx->port->probe_info[index];					\
+}
+
+PARPORT_PROBE_DATA(0)
+PARPORT_PROBE_DATA(1)
+PARPORT_PROBE_DATA(2)
+PARPORT_PROBE_DATA(3)
+PARPORT_PROBE_DATA(4)
+#endif
+
+#define PARPORT_PORT_ENTRY(name, proc)					\
+	{								\
+		.procname	= name,					\
+		.mode		= 0444,					\
+		.type		= SYSCTL_FIELD_CUSTOM,			\
+		.ctl_custom	= {					\
+			.proc_handler	= proc,				\
+			.extra1		= parport_data,			\
+			.maxlen		= 0,				\
+		},							\
+	}
+
+#define PARPORT_PROBE_ENTRY(name, index)				\
+	{								\
+		.procname	= name,					\
+		.mode		= 0444,					\
+		.type		= SYSCTL_FIELD_CUSTOM,			\
+		.ctl_custom	= {					\
+			.proc_handler	= do_autoprobe,			\
+			.extra2		= parport_probe_info_ ## index ## _data, \
+			.maxlen		= 0,				\
+		},							\
+	}
+
+static const struct sysctl_field parport_sysctl_table[] = {
+	SYSCTL_FIELD_STATIC_INT_MINMAX("spintime", 0644, parport_spintime_data,
+				       &parport_min_spintime_value,
+				       &parport_max_spintime_value),
+	PARPORT_PORT_ENTRY("base-addr", do_hardware_base_addr),
+	PARPORT_PORT_ENTRY("irq", do_hardware_irq),
+	PARPORT_PORT_ENTRY("dma", do_hardware_dma),
+	PARPORT_PORT_ENTRY("modes", do_hardware_modes),
+#ifdef CONFIG_PARPORT_1284
+	PARPORT_PROBE_ENTRY("autoprobe", 0),
+	PARPORT_PROBE_ENTRY("autoprobe0", 1),
+	PARPORT_PROBE_ENTRY("autoprobe1", 2),
+	PARPORT_PROBE_ENTRY("autoprobe2", 3),
+	PARPORT_PROBE_ENTRY("autoprobe3", 4),
+#endif
 };
 
-struct parport_device_sysctl_table
-{
-	struct ctl_table_header *sysctl_header;
-	struct ctl_table vars[1];
-	struct ctl_table device_dir[1];
+static const struct sysctl_field parport_device_dir_table[] = {
+	PARPORT_PORT_ENTRY("active", do_active_device),
 };
 
-static const struct parport_device_sysctl_table
-parport_device_sysctl_template = {
-	.sysctl_header = NULL,
+static const struct sysctl_field parport_device_sysctl_table[] = {
 	{
-		{
-			.procname 	= "timeslice",
-			.data		= NULL,
-			.maxlen		= sizeof(unsigned long),
-			.mode		= 0644,
+		.procname	= "timeslice",
+		.mode		= 0644,
+		.type		= SYSCTL_FIELD_CUSTOM,
+		.ctl_custom	= {
 			.proc_handler	= proc_doulongvec_ms_jiffies_minmax,
-			.extra1		= (void*) &parport_min_timeslice_value,
-			.extra2		= (void*) &parport_max_timeslice_value
-		},
-	},
-	{
-		{
-			.procname	= NULL,
-			.data		= NULL,
-			.maxlen		= 0,
-			.mode		= 0555,
+			.data		= pardevice_timeslice_data,
+			.extra1		= parport_min_timeslice_data,
+			.extra2		= parport_max_timeslice_data,
+			.maxlen		= sizeof(unsigned long),
 		},
 	}
 };
@@ -414,35 +401,27 @@ parport_default_sysctl_table = {
 
 int parport_proc_register(struct parport *port)
 {
-	struct parport_sysctl_table *t;
+	struct parport_ctl_context ctx = {
+		.port = port,
+	};
+	struct ctl_table_header *hdr;
 	char *tmp_dir_path;
-	int i, err = 0;
-
-	t = kmemdup(&parport_sysctl_template, sizeof(*t), GFP_KERNEL);
-	if (t == NULL)
-		return -ENOMEM;
-
-	t->device_dir[0].extra1 = port;
-
-	t->vars[0].data = &port->spintime;
-	for (i = 0; i < 5; i++) {
-		t->vars[i].extra1 = port;
-#ifdef CONFIG_PARPORT_1284
-		t->vars[5 + i].extra2 = &port->probe_info[i];
-#endif /* IEEE 1284 support */
-	}
+	int err = 0;
 
 	tmp_dir_path = kasprintf(GFP_KERNEL, "dev/parport/%s/devices", port->name);
 	if (!tmp_dir_path) {
 		err = -ENOMEM;
-		goto exit_free_t;
+		goto out;
 	}
 
-	t->devices_header = register_sysctl(tmp_dir_path, t->device_dir);
-	if (t->devices_header == NULL) {
+	hdr = register_sysctl_fields_sz(tmp_dir_path, parport_device_dir_table,
+					ARRAY_SIZE(parport_device_dir_table),
+					&ctx.context, sizeof(ctx));
+	if (hdr == NULL) {
 		err = -ENOENT;
 		goto  exit_free_tmp_dir_path;
 	}
+	port->sysctl_devices_header = hdr;
 
 	kfree(tmp_dir_path);
 
@@ -452,83 +431,75 @@ int parport_proc_register(struct parport *port)
 		goto unregister_devices_h;
 	}
 
-	t->port_header = register_sysctl(tmp_dir_path, t->vars);
-	if (t->port_header == NULL) {
+	hdr = register_sysctl_fields_sz(tmp_dir_path, parport_sysctl_table,
+					ARRAY_SIZE(parport_sysctl_table),
+					&ctx.context, sizeof(ctx));
+	if (hdr == NULL) {
 		err = -ENOENT;
 		goto unregister_devices_h;
 	}
-
-	port->sysctl_table = t;
+	port->sysctl_table = hdr;
 
 	kfree(tmp_dir_path);
 	return 0;
 
 unregister_devices_h:
-	unregister_sysctl_table(t->devices_header);
+	unregister_sysctl_table(port->sysctl_devices_header);
+	port->sysctl_devices_header = NULL;
 
 exit_free_tmp_dir_path:
 	kfree(tmp_dir_path);
 
-exit_free_t:
-	kfree(t);
+out:
 	return err;
 }
 
 int parport_proc_unregister(struct parport *port)
 {
+	if (port->sysctl_devices_header) {
+		unregister_sysctl_table(port->sysctl_devices_header);
+		port->sysctl_devices_header = NULL;
+	}
 	if (port->sysctl_table) {
-		struct parport_sysctl_table *t = port->sysctl_table;
+		unregister_sysctl_table(port->sysctl_table);
 		port->sysctl_table = NULL;
-		unregister_sysctl_table(t->devices_header);
-		unregister_sysctl_table(t->port_header);
-		kfree(t);
 	}
 	return 0;
 }
 
 int parport_device_proc_register(struct pardevice *device)
 {
-	struct parport_device_sysctl_table *t;
+	struct pardevice_ctl_context ctx = {
+		.device = device,
+	};
 	struct parport * port = device->port;
 	char *tmp_dir_path;
 	int err = 0;
-	
-	t = kmemdup(&parport_device_sysctl_template, sizeof(*t), GFP_KERNEL);
-	if (t == NULL)
-		return -ENOMEM;
 
 	/* Allocate a buffer for two paths: dev/parport/PORT/devices/DEVICE. */
 	tmp_dir_path = kasprintf(GFP_KERNEL, "dev/parport/%s/devices/%s", port->name, device->name);
 	if (!tmp_dir_path) {
 		err = -ENOMEM;
-		goto exit_free_t;
+		goto out;
 	}
 
-	t->vars[0].data = &device->timeslice;
-
-	t->sysctl_header = register_sysctl(tmp_dir_path, t->vars);
-	if (t->sysctl_header == NULL) {
-		kfree(t);
-		t = NULL;
-	}
-	device->sysctl_table = t;
+	device->sysctl_table =
+		register_sysctl_fields_sz(tmp_dir_path, parport_device_sysctl_table,
+				       ARRAY_SIZE(parport_device_sysctl_table),
+				       &ctx.context, sizeof(ctx));
 
 	kfree(tmp_dir_path);
 	return 0;
 
-exit_free_t:
-	kfree(t);
-
+out:
 	return err;
 }
 
 int parport_device_proc_unregister(struct pardevice *device)
 {
 	if (device->sysctl_table) {
-		struct parport_device_sysctl_table *t = device->sysctl_table;
+		unregister_sysctl_table(device->sysctl_table);
 		device->sysctl_table = NULL;
-		unregister_sysctl_table(t->sysctl_header);
-		kfree(t);
 	}
 	return 0;
 }
fs/proc/proc_sysctl.c +11 -0
index 88d3cc79fc33..c715f625a410 100644
@@ -1714,6 +1714,17 @@ struct ctl_table_header *register_sysctl_sz(const char *path, const struct ctl_t
 }
 EXPORT_SYMBOL(register_sysctl_sz);
 
+struct ctl_table_header *register_sysctl_fields_sz(const char *path,
+						   const struct sysctl_field *fields,
+						   size_t field_count,
+						   const struct sysctl_context *ctx,
+						   size_t ctx_size)
+{
+	return __register_sysctl_fields(&sysctl_table_root.default_set, path,
+					fields, field_count, ctx, ctx_size);
+}
+EXPORT_SYMBOL(register_sysctl_fields_sz);
+
 /**
  * __register_sysctl_init() - register sysctl table to path
  * @path: path name for sysctl base. If that path doesn't exist we will create
include/linux/parport.h +4 -2
index 464c2ad28039..ea3dd405ea3c 100644
@@ -20,6 +20,7 @@
 /* Define this later. */
 struct parport;
 struct pardevice;
+struct ctl_table_header;
 
 struct pc_parport_state {
 	unsigned int ctr;
@@ -151,7 +152,7 @@ struct pardevice {
 	unsigned long waiting;		 /* long req'd for set_bit --RR */
 	struct pardevice *waitprev;
 	struct pardevice *waitnext;
-	void * sysctl_table;
+	struct ctl_table_header *sysctl_table;
 };
 
 #define to_pardevice(n) container_of(n, struct pardevice, dev)
@@ -223,7 +224,8 @@ struct parport {
 	struct timer_list timer;
 	unsigned int flags;
 
-	void *sysctl_table;
+	struct ctl_table_header *sysctl_table;
+	struct ctl_table_header *sysctl_devices_header;
 	struct parport_device_info probe_info[5]; /* 0-3 + non-IEEE1284.3 */
 	struct ieee1284_info ieee1284;
 
include/linux/sysctl.h +12 -0
index 6cf2ef4f13e8..b98f1ee94df2 100644
@@ -506,6 +506,10 @@ struct ctl_table_header *
 __register_sysctl_fields(struct ctl_table_set *set, const char *path,
 			 const struct sysctl_field *fields, size_t field_count,
 			 const struct sysctl_context *ctx, size_t ctx_size);
+struct ctl_table_header *
+register_sysctl_fields_sz(const char *path,
+			  const struct sysctl_field *fields, size_t field_count,
+			  const struct sysctl_context *ctx, size_t ctx_size);
 struct ctl_table_header *register_sysctl_sz(const char *path, const struct ctl_table *table,
 					    size_t table_size);
 void unregister_sysctl_table(struct ctl_table_header * table);
@@ -541,6 +545,14 @@ static inline struct ctl_table_header *register_sysctl_sz(const char *path,
 	return NULL;
 }
 
+static inline struct ctl_table_header *
+register_sysctl_fields_sz(const char *path,
+			  const struct ctl_field *fields, size_t field_count,
+			  const struct ctl_context *ctx, size_t ctx_size)
+{
+	return NULL;
+}
+
 static inline struct ctl_table_header *
 __register_sysctl_fields(struct ctl_table_set *set, const char *path,
 			 const struct sysctl_field *fields, size_t field_count,
Signature · 1 line
2.55.0