Commit f1d38e42 authored by Linus Torvalds's avatar Linus Torvalds

Merge git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/sysctl

Pull sysctl updates from Eric Biederman:

 - Rewrite of sysctl for speed and clarity.

   Insert/remove/Lookup in sysctl are all now O(NlogN) operations, and
   are no longer bottlenecks in the process of adding and removing
   network devices.

   sysctl is now focused on being a filesystem instead of system call
   and the code can all be found in fs/proc/proc_sysctl.c.  Hopefully
   this means the code is now approachable.

   Much thanks is owed to Lucian Grinjincu for keeping at this until
   something was found that was usable.

 - The recent proc_sys_poll oops found by the fuzzer during hibernation
   is fixed.

* git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/sysctl: (36 commits)
  sysctl: protect poll() in entries that may go away
  sysctl: Don't call sysctl_follow_link unless we are a link.
  sysctl: Comments to make the code clearer.
  sysctl: Correct error return from get_subdir
  sysctl: An easier to read version of find_subdir
  sysctl: fix memset parameters in setup_sysctl_set()
  sysctl: remove an unused variable
  sysctl: Add register_sysctl for normal sysctl users
  sysctl: Index sysctl directories with rbtrees.
  sysctl: Make the header lists per directory.
  sysctl: Move sysctl_check_dups into insert_header
  sysctl: Modify __register_sysctl_paths to take a set instead of a root and an nsproxy
  sysctl: Replace root_list with links between sysctl_table_sets.
  sysctl: Add sysctl_print_dir and use it in get_subdir
  sysctl: Stop requiring explicit management of sysctl directories
  sysctl: Add a root pointer to ctl_table_set
  sysctl: Rewrite proc_sys_readdir in terms of first_entry and next_entry
  sysctl: Rewrite proc_sys_lookup introducing find_entry and lookup_entry.
  sysctl: Normalize the root_table data structure.
  sysctl: Factor out insert_header and erase_header
  ...
parents dae430c6 4e474a00
...@@ -10,12 +10,15 @@ ...@@ -10,12 +10,15 @@
*/ */
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
struct ctl_table_header;
extern struct proc_dir_entry proc_root; extern struct proc_dir_entry proc_root;
#ifdef CONFIG_PROC_SYSCTL #ifdef CONFIG_PROC_SYSCTL
extern int proc_sys_init(void); extern int proc_sys_init(void);
extern void sysctl_head_put(struct ctl_table_header *head);
#else #else
static inline void proc_sys_init(void) { } static inline void proc_sys_init(void) { }
static inline void sysctl_head_put(struct ctl_table_header *head) { }
#endif #endif
#ifdef CONFIG_NET #ifdef CONFIG_NET
extern int proc_net_init(void); extern int proc_net_init(void);
......
This diff is collapsed.
...@@ -932,34 +932,14 @@ enum ...@@ -932,34 +932,14 @@ enum
#include <linux/list.h> #include <linux/list.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/rbtree.h>
/* For the /proc/sys support */ /* For the /proc/sys support */
struct ctl_table; struct ctl_table;
struct nsproxy; struct nsproxy;
struct ctl_table_root; struct ctl_table_root;
struct ctl_table_set {
struct list_head list;
struct ctl_table_set *parent;
int (*is_seen)(struct ctl_table_set *);
};
extern void setup_sysctl_set(struct ctl_table_set *p,
struct ctl_table_set *parent,
int (*is_seen)(struct ctl_table_set *));
struct ctl_table_header; struct ctl_table_header;
struct ctl_dir;
extern void sysctl_head_get(struct ctl_table_header *);
extern void sysctl_head_put(struct ctl_table_header *);
extern int sysctl_is_seen(struct ctl_table_header *);
extern struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *);
extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev);
extern struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces,
struct ctl_table_header *prev);
extern void sysctl_head_finish(struct ctl_table_header *prev);
extern int sysctl_perm(struct ctl_table_root *root,
struct ctl_table *table, int op);
typedef struct ctl_table ctl_table; typedef struct ctl_table ctl_table;
...@@ -1023,8 +1003,6 @@ static inline void *proc_sys_poll_event(struct ctl_table_poll *poll) ...@@ -1023,8 +1003,6 @@ static inline void *proc_sys_poll_event(struct ctl_table_poll *poll)
return (void *)(unsigned long)atomic_read(&poll->event); return (void *)(unsigned long)atomic_read(&poll->event);
} }
void proc_sys_poll_notify(struct ctl_table_poll *poll);
#define __CTL_TABLE_POLL_INITIALIZER(name) { \ #define __CTL_TABLE_POLL_INITIALIZER(name) { \
.event = ATOMIC_INIT(0), \ .event = ATOMIC_INIT(0), \
.wait = __WAIT_QUEUE_HEAD_INITIALIZER(name.wait) } .wait = __WAIT_QUEUE_HEAD_INITIALIZER(name.wait) }
...@@ -1039,21 +1017,16 @@ struct ctl_table ...@@ -1039,21 +1017,16 @@ struct ctl_table
void *data; void *data;
int maxlen; int maxlen;
umode_t mode; umode_t mode;
struct ctl_table *child; struct ctl_table *child; /* Deprecated */
struct ctl_table *parent; /* Automatically set */
proc_handler *proc_handler; /* Callback for text formatting */ proc_handler *proc_handler; /* Callback for text formatting */
struct ctl_table_poll *poll; struct ctl_table_poll *poll;
void *extra1; void *extra1;
void *extra2; void *extra2;
}; };
struct ctl_table_root { struct ctl_node {
struct list_head root_list; struct rb_node node;
struct ctl_table_set default_set; struct ctl_table_header *header;
struct ctl_table_set *(*lookup)(struct ctl_table_root *root,
struct nsproxy *namespaces);
int (*permissions)(struct ctl_table_root *root,
struct nsproxy *namespaces, struct ctl_table *table);
}; };
/* struct ctl_table_header is used to maintain dynamic lists of /* struct ctl_table_header is used to maintain dynamic lists of
...@@ -1063,9 +1036,9 @@ struct ctl_table_header ...@@ -1063,9 +1036,9 @@ struct ctl_table_header
union { union {
struct { struct {
struct ctl_table *ctl_table; struct ctl_table *ctl_table;
struct list_head ctl_entry;
int used; int used;
int count; int count;
int nreg;
}; };
struct rcu_head rcu; struct rcu_head rcu;
}; };
...@@ -1073,9 +1046,27 @@ struct ctl_table_header ...@@ -1073,9 +1046,27 @@ struct ctl_table_header
struct ctl_table *ctl_table_arg; struct ctl_table *ctl_table_arg;
struct ctl_table_root *root; struct ctl_table_root *root;
struct ctl_table_set *set; struct ctl_table_set *set;
struct ctl_table *attached_by; struct ctl_dir *parent;
struct ctl_table *attached_to; struct ctl_node *node;
struct ctl_table_header *parent; };
struct ctl_dir {
/* Header must be at the start of ctl_dir */
struct ctl_table_header header;
struct rb_root root;
};
struct ctl_table_set {
int (*is_seen)(struct ctl_table_set *);
struct ctl_dir dir;
};
struct ctl_table_root {
struct ctl_table_set default_set;
struct ctl_table_set *(*lookup)(struct ctl_table_root *root,
struct nsproxy *namespaces);
int (*permissions)(struct ctl_table_root *root,
struct nsproxy *namespaces, struct ctl_table *table);
}; };
/* struct ctl_path describes where in the hierarchy a table is added */ /* struct ctl_path describes where in the hierarchy a table is added */
...@@ -1083,16 +1074,53 @@ struct ctl_path { ...@@ -1083,16 +1074,53 @@ struct ctl_path {
const char *procname; const char *procname;
}; };
#ifdef CONFIG_SYSCTL
void proc_sys_poll_notify(struct ctl_table_poll *poll);
extern void setup_sysctl_set(struct ctl_table_set *p,
struct ctl_table_root *root,
int (*is_seen)(struct ctl_table_set *));
extern void retire_sysctl_set(struct ctl_table_set *set);
void register_sysctl_root(struct ctl_table_root *root); void register_sysctl_root(struct ctl_table_root *root);
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_paths( struct ctl_table_header *__register_sysctl_paths(
struct ctl_table_root *root, struct nsproxy *namespaces, struct ctl_table_set *set,
const struct ctl_path *path, struct ctl_table *table); const struct ctl_path *path, struct ctl_table *table);
struct ctl_table_header *register_sysctl(const char *path, struct ctl_table *table);
struct ctl_table_header *register_sysctl_table(struct ctl_table * table); struct ctl_table_header *register_sysctl_table(struct ctl_table * table);
struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path, struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path,
struct ctl_table *table); struct ctl_table *table);
void unregister_sysctl_table(struct ctl_table_header * table); void unregister_sysctl_table(struct ctl_table_header * table);
int sysctl_check_table(struct nsproxy *namespaces, struct ctl_table *table);
extern int sysctl_init(void);
#else /* CONFIG_SYSCTL */
static inline struct ctl_table_header *register_sysctl_table(struct ctl_table * table)
{
return NULL;
}
static inline struct ctl_table_header *register_sysctl_paths(
const struct ctl_path *path, struct ctl_table *table)
{
return NULL;
}
static inline void unregister_sysctl_table(struct ctl_table_header * table)
{
}
static inline void setup_sysctl_set(struct ctl_table_set *p,
struct ctl_table_root *root,
int (*is_seen)(struct ctl_table_set *))
{
}
#endif /* CONFIG_SYSCTL */
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
...@@ -27,7 +27,6 @@ obj-y += power/ ...@@ -27,7 +27,6 @@ obj-y += power/
obj-$(CONFIG_FREEZER) += freezer.o obj-$(CONFIG_FREEZER) += freezer.o
obj-$(CONFIG_PROFILING) += profile.o obj-$(CONFIG_PROFILING) += profile.o
obj-$(CONFIG_SYSCTL_SYSCALL_CHECK) += sysctl_check.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-y += time/ obj-y += time/
obj-$(CONFIG_DEBUG_MUTEXES) += mutex-debug.o obj-$(CONFIG_DEBUG_MUTEXES) += mutex-debug.o
......
This diff is collapsed.
#include <linux/stat.h>
#include <linux/sysctl.h>
#include "../fs/xfs/xfs_sysctl.h"
#include <linux/sunrpc/debug.h>
#include <linux/string.h>
#include <net/ip_vs.h>
static int sysctl_depth(struct ctl_table *table)
{
struct ctl_table *tmp;
int depth;
depth = 0;
for (tmp = table; tmp->parent; tmp = tmp->parent)
depth++;
return depth;
}
static struct ctl_table *sysctl_parent(struct ctl_table *table, int n)
{
int i;
for (i = 0; table && i < n; i++)
table = table->parent;
return table;
}
static void sysctl_print_path(struct ctl_table *table)
{
struct ctl_table *tmp;
int depth, i;
depth = sysctl_depth(table);
if (table->procname) {
for (i = depth; i >= 0; i--) {
tmp = sysctl_parent(table, i);
printk("/%s", tmp->procname?tmp->procname:"");
}
}
printk(" ");
}
static struct ctl_table *sysctl_check_lookup(struct nsproxy *namespaces,
struct ctl_table *table)
{
struct ctl_table_header *head;
struct ctl_table *ref, *test;
int depth, cur_depth;
depth = sysctl_depth(table);
for (head = __sysctl_head_next(namespaces, NULL); head;
head = __sysctl_head_next(namespaces, head)) {
cur_depth = depth;
ref = head->ctl_table;
repeat:
test = sysctl_parent(table, cur_depth);
for (; ref->procname; ref++) {
int match = 0;
if (cur_depth && !ref->child)
continue;
if (test->procname && ref->procname &&
(strcmp(test->procname, ref->procname) == 0))
match++;
if (match) {
if (cur_depth != 0) {
cur_depth--;
ref = ref->child;
goto repeat;
}
goto out;
}
}
}
ref = NULL;
out:
sysctl_head_finish(head);
return ref;
}
static void set_fail(const char **fail, struct ctl_table *table, const char *str)
{
if (*fail) {
printk(KERN_ERR "sysctl table check failed: ");
sysctl_print_path(table);
printk(" %s\n", *fail);
dump_stack();
}
*fail = str;
}
static void sysctl_check_leaf(struct nsproxy *namespaces,
struct ctl_table *table, const char **fail)
{
struct ctl_table *ref;
ref = sysctl_check_lookup(namespaces, table);
if (ref && (ref != table))
set_fail(fail, table, "Sysctl already exists");
}
int sysctl_check_table(struct nsproxy *namespaces, struct ctl_table *table)
{
int error = 0;
for (; table->procname; table++) {
const char *fail = NULL;
if (table->parent) {
if (!table->parent->procname)
set_fail(&fail, table, "Parent without procname");
}
if (table->child) {
if (table->data)
set_fail(&fail, table, "Directory with data?");
if (table->maxlen)
set_fail(&fail, table, "Directory with maxlen?");
if ((table->mode & (S_IRUGO|S_IXUGO)) != table->mode)
set_fail(&fail, table, "Writable sysctl directory");
if (table->proc_handler)
set_fail(&fail, table, "Directory with proc_handler");
if (table->extra1)
set_fail(&fail, table, "Directory with extra1");
if (table->extra2)
set_fail(&fail, table, "Directory with extra2");
} else {
if ((table->proc_handler == proc_dostring) ||
(table->proc_handler == proc_dointvec) ||
(table->proc_handler == proc_dointvec_minmax) ||
(table->proc_handler == proc_dointvec_jiffies) ||
(table->proc_handler == proc_dointvec_userhz_jiffies) ||
(table->proc_handler == proc_dointvec_ms_jiffies) ||
(table->proc_handler == proc_doulongvec_minmax) ||
(table->proc_handler == proc_doulongvec_ms_jiffies_minmax)) {
if (!table->data)
set_fail(&fail, table, "No data");
if (!table->maxlen)
set_fail(&fail, table, "No maxlen");
}
#ifdef CONFIG_PROC_SYSCTL
if (!table->proc_handler)
set_fail(&fail, table, "No proc_handler");
#endif
sysctl_check_leaf(namespaces, table, &fail);
}
if (table->mode > 0777)
set_fail(&fail, table, "bogus .mode");
if (fail) {
set_fail(&fail, table, NULL);
error = -EINVAL;
}
if (table->child)
error |= sysctl_check_table(namespaces, table->child);
}
return error;
}
...@@ -1141,14 +1141,6 @@ config LATENCYTOP ...@@ -1141,14 +1141,6 @@ config LATENCYTOP
Enable this option if you want to use the LatencyTOP tool Enable this option if you want to use the LatencyTOP tool
to find out which userspace is blocking on what kernel operations. to find out which userspace is blocking on what kernel operations.
config SYSCTL_SYSCALL_CHECK
bool "Sysctl checks"
depends on SYSCTL
---help---
sys_sysctl uses binary paths that have been found challenging
to properly maintain and use. This enables checks that help
you to keep things correct.
source mm/Kconfig.debug source mm/Kconfig.debug
source kernel/trace/Kconfig source kernel/trace/Kconfig
......
...@@ -74,15 +74,13 @@ static struct ctl_table_root net_sysctl_ro_root = { ...@@ -74,15 +74,13 @@ static struct ctl_table_root net_sysctl_ro_root = {
static int __net_init sysctl_net_init(struct net *net) static int __net_init sysctl_net_init(struct net *net)
{ {
setup_sysctl_set(&net->sysctls, setup_sysctl_set(&net->sysctls, &net_sysctl_root, is_seen);
&net_sysctl_ro_root.default_set,
is_seen);
return 0; return 0;
} }
static void __net_exit sysctl_net_exit(struct net *net) static void __net_exit sysctl_net_exit(struct net *net)
{ {
WARN_ON(!list_empty(&net->sysctls.list)); retire_sysctl_set(&net->sysctls);
} }
static struct pernet_operations sysctl_pernet_ops = { static struct pernet_operations sysctl_pernet_ops = {
...@@ -90,36 +88,32 @@ static struct pernet_operations sysctl_pernet_ops = { ...@@ -90,36 +88,32 @@ static struct pernet_operations sysctl_pernet_ops = {
.exit = sysctl_net_exit, .exit = sysctl_net_exit,
}; };
static __init int sysctl_init(void) static __init int net_sysctl_init(void)
{ {
int ret; int ret;
ret = register_pernet_subsys(&sysctl_pernet_ops); ret = register_pernet_subsys(&sysctl_pernet_ops);
if (ret) if (ret)
goto out; goto out;
register_sysctl_root(&net_sysctl_root); setup_sysctl_set(&net_sysctl_ro_root.default_set, &net_sysctl_ro_root, NULL);
setup_sysctl_set(&net_sysctl_ro_root.default_set, NULL, NULL);
register_sysctl_root(&net_sysctl_ro_root); register_sysctl_root(&net_sysctl_ro_root);
register_sysctl_root(&net_sysctl_root);
out: out:
return ret; return ret;
} }
subsys_initcall(sysctl_init); subsys_initcall(net_sysctl_init);
struct ctl_table_header *register_net_sysctl_table(struct net *net, struct ctl_table_header *register_net_sysctl_table(struct net *net,
const struct ctl_path *path, struct ctl_table *table) const struct ctl_path *path, struct ctl_table *table)
{ {
struct nsproxy namespaces; return __register_sysctl_paths(&net->sysctls, path, table);
namespaces = *current->nsproxy;
namespaces.net_ns = net;
return __register_sysctl_paths(&net_sysctl_root,
&namespaces, path, table);
} }
EXPORT_SYMBOL_GPL(register_net_sysctl_table); EXPORT_SYMBOL_GPL(register_net_sysctl_table);
struct ctl_table_header *register_net_sysctl_rotable(const struct ctl_table_header *register_net_sysctl_rotable(const
struct ctl_path *path, struct ctl_table *table) struct ctl_path *path, struct ctl_table *table)
{ {
return __register_sysctl_paths(&net_sysctl_ro_root, return __register_sysctl_paths(&net_sysctl_ro_root.default_set,
&init_nsproxy, path, table); path, table);
} }
EXPORT_SYMBOL_GPL(register_net_sysctl_rotable); EXPORT_SYMBOL_GPL(register_net_sysctl_rotable);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment