Skip to content

Commit

Permalink
Merge tag 'sysctl-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kern…
Browse files Browse the repository at this point in the history
…el/git/mcgrof/linux

Pull sysctl updates from Luis Chamberlain:
 "Long ago we set out to remove the kitchen sink on kernel/sysctl.c
  arrays and placings sysctls to their own sybsystem or file to help
  avoid merge conflicts. Matthew Wilcox pointed out though that if we're
  going to do that we might as well also *save* space while at it and
  try to remove the extra last sysctl entry added at the end of each
  array, a sentintel, instead of bloating the kernel by adding a new
  sentinel with each array moved.

  Doing that was not so trivial, and has required slowing down the moves
  of kernel/sysctl.c arrays and measuring the impact on size by each new
  move.

  The complex part of the effort to help reduce the size of each sysctl
  is being done by the patient work of el señor Don Joel Granados. A lot
  of this is truly painful code refactoring and testing and then trying
  to measure the savings of each move and removing the sentinels.
  Although Joel already has code which does most of this work,
  experience with sysctl moves in the past shows is we need to be
  careful due to the slew of odd build failures that are possible due to
  the amount of random Kconfig options sysctls use.

  To that end Joel's work is split by first addressing the major
  housekeeping needed to remove the sentinels, which is part of this
  merge request. The rest of the work to actually remove the sentinels
  will be done later in future kernel releases.

  The preliminary math is showing this will all help reduce the overall
  build time size of the kernel and run time memory consumed by the
  kernel by about ~64 bytes per array where we are able to remove each
  sentinel in the future. That also means there is no more bloating the
  kernel with the extra ~64 bytes per array moved as no new sentinels
  are created"

* tag 'sysctl-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux:
  sysctl: Use ctl_table_size as stopping criteria for list macro
  sysctl: SIZE_MAX->ARRAY_SIZE in register_net_sysctl
  vrf: Update to register_net_sysctl_sz
  networking: Update to register_net_sysctl_sz
  netfilter: Update to register_net_sysctl_sz
  ax.25: Update to register_net_sysctl_sz
  sysctl: Add size to register_net_sysctl function
  sysctl: Add size arg to __register_sysctl_init
  sysctl: Add size to register_sysctl
  sysctl: Add a size arg to __register_sysctl_table
  sysctl: Add size argument to init_header
  sysctl: Add ctl_table_size to ctl_table_header
  sysctl: Use ctl_table_header in list_for_each_table_entry
  sysctl: Prefer ctl_table_header in proc_sysctl
  • Loading branch information
torvalds committed Aug 30, 2023
2 parents daa22f5 + 53f3811 commit adfd671
Show file tree
Hide file tree
Showing 40 changed files with 222 additions and 113 deletions.
2 changes: 1 addition & 1 deletion arch/arm64/kernel/armv8_deprecated.c
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,7 @@ static void __init register_insn_emulation(struct insn_emulation *insn)
sysctl->extra2 = &insn->max;
sysctl->proc_handler = emulation_proc_handler;

register_sysctl("abi", sysctl);
register_sysctl_sz("abi", sysctl, 1);
}
}

Expand Down
2 changes: 1 addition & 1 deletion arch/s390/appldata/appldata_base.c
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ int appldata_register_ops(struct appldata_ops *ops)
ops->ctl_table[0].proc_handler = appldata_generic_handler;
ops->ctl_table[0].data = ops;

ops->sysctl_header = register_sysctl(appldata_proc_name, ops->ctl_table);
ops->sysctl_header = register_sysctl_sz(appldata_proc_name, ops->ctl_table, 1);
if (!ops->sysctl_header)
goto out;
return 0;
Expand Down
3 changes: 2 additions & 1 deletion drivers/net/vrf.c
Original file line number Diff line number Diff line change
Expand Up @@ -1977,7 +1977,8 @@ static int vrf_netns_init_sysctl(struct net *net, struct netns_vrf *nn_vrf)
/* init the extra1 parameter with the reference to current netns */
table[0].extra1 = net;

nn_vrf->ctl_hdr = register_net_sysctl(net, "net/vrf", table);
nn_vrf->ctl_hdr = register_net_sysctl_sz(net, "net/vrf", table,
ARRAY_SIZE(vrf_table));
if (!nn_vrf->ctl_hdr) {
kfree(table);
return -ENOMEM;
Expand Down
90 changes: 46 additions & 44 deletions fs/proc/proc_sysctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
#include <linux/kmemleak.h>
#include "internal.h"

#define list_for_each_table_entry(entry, table) \
for ((entry) = (table); (entry)->procname; (entry)++)
#define list_for_each_table_entry(entry, header) \
entry = header->ctl_table; \
for (size_t i = 0 ; i < header->ctl_table_size && entry->procname; ++i, entry++)

static const struct dentry_operations proc_sys_dentry_operations;
static const struct file_operations proc_sys_file_operations;
Expand All @@ -43,7 +44,7 @@ static struct ctl_table sysctl_mount_point[] = {
*/
struct ctl_table_header *register_sysctl_mount_point(const char *path)
{
return register_sysctl(path, sysctl_mount_point);
return register_sysctl_sz(path, sysctl_mount_point, 0);
}
EXPORT_SYMBOL(register_sysctl_mount_point);

Expand Down Expand Up @@ -188,9 +189,10 @@ static void erase_entry(struct ctl_table_header *head, struct ctl_table *entry)

static void init_header(struct ctl_table_header *head,
struct ctl_table_root *root, struct ctl_table_set *set,
struct ctl_node *node, struct ctl_table *table)
struct ctl_node *node, struct ctl_table *table, size_t table_size)
{
head->ctl_table = table;
head->ctl_table_size = table_size;
head->ctl_table_arg = table;
head->used = 0;
head->count = 1;
Expand All @@ -204,7 +206,7 @@ static void init_header(struct ctl_table_header *head,
if (node) {
struct ctl_table *entry;

list_for_each_table_entry(entry, table) {
list_for_each_table_entry(entry, head) {
node->header = head;
node++;
}
Expand All @@ -215,7 +217,7 @@ static void erase_header(struct ctl_table_header *head)
{
struct ctl_table *entry;

list_for_each_table_entry(entry, head->ctl_table)
list_for_each_table_entry(entry, head)
erase_entry(head, entry);
}

Expand All @@ -242,7 +244,7 @@ static int insert_header(struct ctl_dir *dir, struct ctl_table_header *header)
err = insert_links(header);
if (err)
goto fail_links;
list_for_each_table_entry(entry, header->ctl_table) {
list_for_each_table_entry(entry, header) {
err = insert_entry(header, entry);
if (err)
goto fail;
Expand Down Expand Up @@ -973,7 +975,7 @@ 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);
init_header(&new->header, set->dir.header.root, set, node, table, 1);

return new;
}
Expand Down Expand Up @@ -1125,11 +1127,11 @@ static int sysctl_check_table_array(const char *path, struct ctl_table *table)
return err;
}

static int sysctl_check_table(const char *path, struct ctl_table *table)
static int sysctl_check_table(const char *path, struct ctl_table_header *header)
{
struct ctl_table *entry;
int err = 0;
list_for_each_table_entry(entry, table) {
list_for_each_table_entry(entry, header) {
if ((entry->proc_handler == proc_dostring) ||
(entry->proc_handler == proc_dobool) ||
(entry->proc_handler == proc_dointvec) ||
Expand Down Expand Up @@ -1159,8 +1161,7 @@ static int sysctl_check_table(const char *path, struct ctl_table *table)
return err;
}

static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table *table,
struct ctl_table_root *link_root)
static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table_header *head)
{
struct ctl_table *link_table, *entry, *link;
struct ctl_table_header *links;
Expand All @@ -1170,7 +1171,7 @@ static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table

name_bytes = 0;
nr_entries = 0;
list_for_each_table_entry(entry, table) {
list_for_each_table_entry(entry, head) {
nr_entries++;
name_bytes += strlen(entry->procname) + 1;
}
Expand All @@ -1189,31 +1190,33 @@ static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table
link_name = (char *)&link_table[nr_entries + 1];
link = link_table;

list_for_each_table_entry(entry, table) {
list_for_each_table_entry(entry, head) {
int len = strlen(entry->procname) + 1;
memcpy(link_name, entry->procname, len);
link->procname = link_name;
link->mode = S_IFLNK|S_IRWXUGO;
link->data = link_root;
link->data = head->root;
link_name += len;
link++;
}
init_header(links, dir->header.root, dir->header.set, node, link_table);
init_header(links, dir->header.root, dir->header.set, node, link_table,
head->ctl_table_size);
links->nreg = nr_entries;

return links;
}

static bool get_links(struct ctl_dir *dir,
struct ctl_table *table, struct ctl_table_root *link_root)
struct ctl_table_header *header,
struct ctl_table_root *link_root)
{
struct ctl_table_header *head;
struct ctl_table_header *tmp_head;
struct ctl_table *entry, *link;

/* Are there links available for every entry in table? */
list_for_each_table_entry(entry, table) {
list_for_each_table_entry(entry, header) {
const char *procname = entry->procname;
link = find_entry(&head, dir, procname, strlen(procname));
link = find_entry(&tmp_head, dir, procname, strlen(procname));
if (!link)
return false;
if (S_ISDIR(link->mode) && S_ISDIR(entry->mode))
Expand All @@ -1224,10 +1227,10 @@ static bool get_links(struct ctl_dir *dir,
}

/* The checks passed. Increase the registration count on the links */
list_for_each_table_entry(entry, table) {
list_for_each_table_entry(entry, header) {
const char *procname = entry->procname;
link = find_entry(&head, dir, procname, strlen(procname));
head->nreg++;
link = find_entry(&tmp_head, dir, procname, strlen(procname));
tmp_head->nreg++;
}
return true;
}
Expand All @@ -1246,21 +1249,21 @@ static int insert_links(struct ctl_table_header *head)
if (IS_ERR(core_parent))
return 0;

if (get_links(core_parent, head->ctl_table, head->root))
if (get_links(core_parent, head, head->root))
return 0;

core_parent->header.nreg++;
spin_unlock(&sysctl_lock);

links = new_links(core_parent, head->ctl_table, head->root);
links = new_links(core_parent, head);

spin_lock(&sysctl_lock);
err = -ENOMEM;
if (!links)
goto out;

err = 0;
if (get_links(core_parent, head->ctl_table, head->root)) {
if (get_links(core_parent, head, head->root)) {
kfree(links);
goto out;
}
Expand Down Expand Up @@ -1310,6 +1313,7 @@ static struct ctl_dir *sysctl_mkdir_p(struct ctl_dir *dir, const char *path)
* 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. @table should be a filled in ctl_table
* array. A completely 0 filled entry terminates the table.
Expand Down Expand Up @@ -1352,26 +1356,21 @@ static struct ctl_dir *sysctl_mkdir_p(struct ctl_dir *dir, const char *path)
*/
struct ctl_table_header *__register_sysctl_table(
struct ctl_table_set *set,
const char *path, struct ctl_table *table)
const char *path, struct ctl_table *table, size_t table_size)
{
struct ctl_table_root *root = set->dir.header.root;
struct ctl_table_header *header;
struct ctl_dir *dir;
struct ctl_table *entry;
struct ctl_node *node;
int nr_entries = 0;

list_for_each_table_entry(entry, table)
nr_entries++;

header = kzalloc(sizeof(struct ctl_table_header) +
sizeof(struct ctl_node)*nr_entries, GFP_KERNEL_ACCOUNT);
sizeof(struct ctl_node)*table_size, GFP_KERNEL_ACCOUNT);
if (!header)
return NULL;

node = (struct ctl_node *)(header + 1);
init_header(header, root, set, node, table);
if (sysctl_check_table(path, table))
init_header(header, root, set, node, table, table_size);
if (sysctl_check_table(path, header))
goto fail;

spin_lock(&sysctl_lock);
Expand Down Expand Up @@ -1401,7 +1400,7 @@ struct ctl_table_header *__register_sysctl_table(
}

/**
* register_sysctl - register a sysctl table
* register_sysctl_sz - register a sysctl table
* @path: The path to the directory the sysctl table is in. If the path
* doesn't exist we will create it for you.
* @table: the table structure. The calller must ensure the life of the @table
Expand All @@ -1411,18 +1410,20 @@ struct ctl_table_header *__register_sysctl_table(
* to call unregister_sysctl_table() and can instead use something like
* register_sysctl_init() which does not care for the result of the syctl
* registration.
* @table_size: The number of elements in table.
*
* Register a sysctl table. @table should be a filled in ctl_table
* array. A completely 0 filled entry terminates the table.
*
* See __register_sysctl_table for more details.
*/
struct ctl_table_header *register_sysctl(const char *path, struct ctl_table *table)
struct ctl_table_header *register_sysctl_sz(const char *path, struct ctl_table *table,
size_t table_size)
{
return __register_sysctl_table(&sysctl_table_root.default_set,
path, table);
path, table, table_size);
}
EXPORT_SYMBOL(register_sysctl);
EXPORT_SYMBOL(register_sysctl_sz);

/**
* __register_sysctl_init() - register sysctl table to path
Expand All @@ -1433,6 +1434,7 @@ EXPORT_SYMBOL(register_sysctl);
* lifetime use of the sysctl.
* @table_name: The name of sysctl table, only used for log printing when
* registration fails
* @table_size: The number of elements in table
*
* The sysctl interface is used by userspace to query or modify at runtime
* a predefined value set on a variable. These variables however have default
Expand All @@ -1445,12 +1447,12 @@ EXPORT_SYMBOL(register_sysctl);
* Context: if your base directory does not exist it will be created for you.
*/
void __init __register_sysctl_init(const char *path, struct ctl_table *table,
const char *table_name)
const char *table_name, size_t table_size)
{
struct ctl_table_header *hdr = register_sysctl(path, table);
struct ctl_table_header *hdr = register_sysctl_sz(path, table, table_size);

if (unlikely(!hdr)) {
pr_err("failed when register_sysctl %s to %s\n", table_name, path);
pr_err("failed when register_sysctl_sz %s to %s\n", table_name, path);
return;
}
kmemleak_not_leak(hdr);
Expand All @@ -1471,7 +1473,7 @@ static void put_links(struct ctl_table_header *header)
if (IS_ERR(core_parent))
return;

list_for_each_table_entry(entry, header->ctl_table) {
list_for_each_table_entry(entry, header) {
struct ctl_table_header *link_head;
struct ctl_table *link;
const char *name = entry->procname;
Expand Down Expand Up @@ -1535,7 +1537,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);
init_header(&set->dir.header, root, set, NULL, root_table, 1);
}

void retire_sysctl_set(struct ctl_table_set *set)
Expand Down
31 changes: 24 additions & 7 deletions include/linux/sysctl.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,12 +159,22 @@ struct ctl_node {
struct ctl_table_header *header;
};

/* struct ctl_table_header is used to maintain dynamic lists of
struct ctl_table trees. */
/**
* struct ctl_table_header - maintains dynamic lists of struct ctl_table trees
* @ctl_table: pointer to the first element in ctl_table array
* @ctl_table_size: number of elements pointed by @ctl_table
* @used: The entry will never be touched when equal to 0.
* @count: Upped every time something is added to @inodes and downed every time
* something is removed from inodes
* @nreg: When nreg drops to 0 the ctl_table_header will be unregistered.
* @rcu: Delays the freeing of the inode. Introduced with "unfuck proc_sysctl ->d_compare()"
*
*/
struct ctl_table_header {
union {
struct {
struct ctl_table *ctl_table;
int ctl_table_size;
int used;
int count;
int nreg;
Expand Down Expand Up @@ -205,6 +215,9 @@ struct ctl_path {
const char *procname;
};

#define register_sysctl(path, table) \
register_sysctl_sz(path, table, ARRAY_SIZE(table))

#ifdef CONFIG_SYSCTL

void proc_sys_poll_notify(struct ctl_table_poll *poll);
Expand All @@ -216,14 +229,16 @@ extern void retire_sysctl_set(struct ctl_table_set *set);

struct ctl_table_header *__register_sysctl_table(
struct ctl_table_set *set,
const char *path, struct ctl_table *table);
struct ctl_table_header *register_sysctl(const char *path, struct ctl_table *table);
const char *path, struct ctl_table *table, size_t table_size);
struct ctl_table_header *register_sysctl_sz(const char *path, struct ctl_table *table,
size_t table_size);
void unregister_sysctl_table(struct ctl_table_header * table);

extern int sysctl_init_bases(void);
extern void __register_sysctl_init(const char *path, struct ctl_table *table,
const char *table_name);
#define register_sysctl_init(path, table) __register_sysctl_init(path, table, #table)
const char *table_name, size_t table_size);
#define register_sysctl_init(path, table) \
__register_sysctl_init(path, table, #table, ARRAY_SIZE(table))
extern struct ctl_table_header *register_sysctl_mount_point(const char *path);

void do_sysctl_args(void);
Expand Down Expand Up @@ -252,7 +267,9 @@ static inline struct ctl_table_header *register_sysctl_mount_point(const char *p
return NULL;
}

static inline struct ctl_table_header *register_sysctl(const char *path, struct ctl_table *table)
static inline struct ctl_table_header *register_sysctl_sz(const char *path,
struct ctl_table *table,
size_t table_size)
{
return NULL;
}
Expand Down
Loading

0 comments on commit adfd671

Please sign in to comment.