Commit ae7edecc authored by Al Viro's avatar Al Viro

[PATCH] sysctl: keep track of tree relationships

In a sense, that's the heart of the series.  It's based on the following
property of the trees we are actually asked to add: they can be split into
stem that is already covered by registered trees and crown that is entirely
new.  IOW, if a/b and a/c/d are introduced by our tree, then a/c is also
introduced by it.

That allows to associate tree and table entry with each node in the union;
while directory nodes might be covered by many trees, only one will cover
the node by its crown.  And that will allow much saner logics for /proc/sys
in the next patches.  This patch introduces the data structures needed to
keep track of that.

When adding a sysctl table, we find a "parent" one.  Which is to say,
find the deepest node on its stem that already is present in one of the
tables from our table set or its ancestor sets.  That table will be our
parent and that node in it - attachment point.  Add our table to list
anchored in parent, have it refer the parent and contents of attachment
point.  Also remember where its crown lives.
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent bd7b1533
...@@ -1083,6 +1083,9 @@ struct ctl_table_header ...@@ -1083,6 +1083,9 @@ 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_table *attached_to;
struct ctl_table_header *parent;
}; };
/* struct ctl_path describes where in the hierarchy a table is added */ /* struct ctl_path describes where in the hierarchy a table is added */
......
...@@ -1680,6 +1680,52 @@ static __init int sysctl_init(void) ...@@ -1680,6 +1680,52 @@ static __init int sysctl_init(void)
core_initcall(sysctl_init); core_initcall(sysctl_init);
static int is_branch_in(struct ctl_table *branch, struct ctl_table *table)
{
struct ctl_table *p;
const char *s = branch->procname;
/* branch should have named subdirectory as its first element */
if (!s || !branch->child)
return 0;
/* ... and nothing else */
if (branch[1].procname || branch[1].ctl_name)
return 0;
/* table should contain subdirectory with the same name */
for (p = table; p->procname || p->ctl_name; p++) {
if (!p->child)
continue;
if (p->procname && strcmp(p->procname, s) == 0)
return 1;
}
return 0;
}
/* see if attaching q to p would be an improvement */
static void try_attach(struct ctl_table_header *p, struct ctl_table_header *q)
{
struct ctl_table *to = p->ctl_table, *by = q->ctl_table;
int is_better = 0;
int not_in_parent = !p->attached_by;
while (is_branch_in(by, to)) {
if (by == q->attached_by)
is_better = 1;
if (to == p->attached_by)
not_in_parent = 1;
by = by->child;
to = to->child;
}
if (is_better && not_in_parent) {
q->attached_by = by;
q->attached_to = to;
q->parent = p;
}
}
/** /**
* __register_sysctl_paths - register a sysctl hierarchy * __register_sysctl_paths - register a sysctl hierarchy
* @root: List of sysctl headers to register on * @root: List of sysctl headers to register on
...@@ -1759,6 +1805,7 @@ struct ctl_table_header *__register_sysctl_paths( ...@@ -1759,6 +1805,7 @@ struct ctl_table_header *__register_sysctl_paths(
struct ctl_table_header *header; struct ctl_table_header *header;
struct ctl_table *new, **prevp; struct ctl_table *new, **prevp;
unsigned int n, npath; unsigned int n, npath;
struct ctl_table_set *set;
/* Count the path components */ /* Count the path components */
for (npath = 0; path[npath].ctl_name || path[npath].procname; ++npath) for (npath = 0; path[npath].ctl_name || path[npath].procname; ++npath)
...@@ -1809,6 +1856,18 @@ struct ctl_table_header *__register_sysctl_paths( ...@@ -1809,6 +1856,18 @@ struct ctl_table_header *__register_sysctl_paths(
#endif #endif
spin_lock(&sysctl_lock); spin_lock(&sysctl_lock);
header->set = lookup_header_set(root, namespaces); header->set = lookup_header_set(root, namespaces);
header->attached_by = header->ctl_table;
header->attached_to = root_table;
header->parent = &root_table_header;
for (set = header->set; set; set = set->parent) {
struct ctl_table_header *p;
list_for_each_entry(p, &set->list, ctl_entry) {
if (p->unregistering)
continue;
try_attach(p, header);
}
}
header->parent->count++;
list_add_tail(&header->ctl_entry, &header->set->list); list_add_tail(&header->ctl_entry, &header->set->list);
spin_unlock(&sysctl_lock); spin_unlock(&sysctl_lock);
...@@ -1864,6 +1923,10 @@ void unregister_sysctl_table(struct ctl_table_header * header) ...@@ -1864,6 +1923,10 @@ void unregister_sysctl_table(struct ctl_table_header * header)
spin_lock(&sysctl_lock); spin_lock(&sysctl_lock);
start_unregistering(header); start_unregistering(header);
if (!--header->parent->count) {
WARN_ON(1);
kfree(header->parent);
}
if (!--header->count) if (!--header->count)
kfree(header); kfree(header);
spin_unlock(&sysctl_lock); spin_unlock(&sysctl_lock);
......
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