[Devel] [PATCH 1/3] The sysctl shadows
Pavel Emelyanov
xemul at openvz.org
Tue Nov 20 03:43:42 PST 2007
The sysctl shadow is nothing but a ctl_table_head, that
hides behind some other one and which is used in
proc_sys_readdir, proc_sys_lookup, etc when scanning
through the list of these heads.
Each "shadow" carries its own set of ctl_tables that
are relevant to this shadow. The appropriate shadow
is get via the ->shadow() callback on the head.
When putting the shadow back (unuse it) one should
work with the shadow->origin head.
The API:
* register_sysctl_table_shadow: registers the ctl
table and tells that is will be shadowed with
the specified callback;
* create_sysctl_shadow: clones the original head's
table and creates a shadow for it. The caller then
may move the ctl_table->data pointer on the new
(shadow) tables to point to another variables.
It may also change names for tables, etc;
* free_sysctl_shadow: destroys the shadow.
Signed-off-by: Pavel Emelyanov <xemul at openvz.org>
---
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 4f5047d..064f132 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -1057,9 +1057,16 @@ struct ctl_table_header
struct list_head ctl_entry;
int used;
struct completion *unregistering;
+ struct ctl_table_header *(*shadow)(struct ctl_table_header *);
+ struct ctl_table_header *origin;
};
struct ctl_table_header *register_sysctl_table(struct ctl_table * table);
+struct ctl_table_header *register_sysctl_table_shadow(struct ctl_table * table,
+ struct ctl_table_header *(*shadow)(struct ctl_table_header *));
+
+struct ctl_table_header *create_sysctl_shadow(struct ctl_table_header *h);
+void free_sysctl_shadow(struct ctl_table_header *h);
void unregister_sysctl_table(struct ctl_table_header * table);
int sysctl_check_table(struct ctl_table *table);
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 489b0d1..bfea4fa 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1320,6 +1320,9 @@ void sysctl_head_finish(struct ctl_table_header *head)
{
if (!head)
return;
+ if (head->origin)
+ head = head->origin;
+
spin_lock(&sysctl_lock);
unuse_table(head);
spin_unlock(&sysctl_lock);
@@ -1331,6 +1334,9 @@ struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev)
struct list_head *tmp;
spin_lock(&sysctl_lock);
if (prev) {
+ if (prev->origin != NULL)
+ prev = prev->origin;
+
tmp = &prev->ctl_entry;
unuse_table(prev);
goto next;
@@ -1342,6 +1348,10 @@ struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev)
if (!use_table(head))
goto next;
spin_unlock(&sysctl_lock);
+
+ if (head->shadow)
+ head = head->shadow(head);
+
return head;
next:
tmp = tmp->next;
@@ -1582,7 +1592,8 @@ core_initcall(sysctl_init);
* 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 * table)
+struct ctl_table_header *register_sysctl_table_shadow(struct ctl_table * table,
+ struct ctl_table_header *(*shadow)(struct ctl_table_header *))
{
struct ctl_table_header *tmp;
tmp = kmalloc(sizeof(struct ctl_table_header), GFP_KERNEL);
@@ -1592,6 +1603,8 @@ struct ctl_table_header *register_sysctl_table(struct ctl_table * table)
INIT_LIST_HEAD(&tmp->ctl_entry);
tmp->used = 0;
tmp->unregistering = NULL;
+ tmp->shadow = shadow;
+ tmp->origin = NULL;
sysctl_set_parent(NULL, table);
if (sysctl_check_table(tmp->ctl_table)) {
kfree(tmp);
@@ -1603,6 +1616,11 @@ struct ctl_table_header *register_sysctl_table(struct ctl_table * table)
return tmp;
}
+struct ctl_table_header *register_sysctl_table(struct ctl_table * table)
+{
+ return register_sysctl_table_shadow(table, NULL);
+}
+
/**
* unregister_sysctl_table - unregister a sysctl table hierarchy
* @header: the header returned from register_sysctl_table
@@ -1619,6 +1637,84 @@ void unregister_sysctl_table(struct ctl_table_header * header)
kfree(header);
}
+/*
+ * ctl tables shadow management
+ */
+static void ctl_free_table(struct ctl_table *t)
+{
+ struct ctl_table *tmp;
+
+ for (tmp = t; tmp->ctl_name || tmp->procname; tmp++)
+ if (tmp->child)
+ ctl_free_table(tmp->child);
+
+ kfree(t);
+}
+
+static struct ctl_table *ctl_dup_table(struct ctl_table *parent,
+ struct ctl_table *t)
+{
+ int i;
+ struct ctl_table *copy;
+
+ for (copy = t, i = 1; copy->ctl_name || copy->procname; copy++, i++);
+
+ copy = kmemdup(t, i * sizeof(struct ctl_table), GFP_KERNEL);
+ if (copy == NULL)
+ goto out;
+
+ for (i = 0; copy[i].ctl_name || copy[i].procname; i++) {
+ copy[i].parent = parent;
+ if (copy[i].child == NULL)
+ continue;
+
+ copy[i].child = ctl_dup_table(©[i], t[i].child);
+ if (copy[i].child == NULL)
+ goto unroll;
+ }
+
+ return copy;
+
+unroll:
+ for (i--; i >= 0; i--)
+ if (copy[i].child)
+ ctl_free_table(copy[i].child);
+ kfree(copy);
+out:
+ return NULL;
+}
+
+struct ctl_table_header *create_sysctl_shadow(struct ctl_table_header *h)
+{
+ struct ctl_table_header *ret;
+ struct ctl_table *tbl;
+
+ ret = kmemdup(h, sizeof(struct ctl_table_header *), GFP_KERNEL);
+ if (ret == NULL)
+ goto err_header;
+
+ ret->shadow = NULL;
+ ret->origin = h;
+
+ tbl = ctl_dup_table(NULL, h->ctl_table);
+ if (tbl == NULL)
+ goto err_table;
+
+ ret->ctl_table = tbl;
+ return ret;
+
+err_table:
+ kfree(ret);
+err_header:
+ return NULL;
+}
+
+void free_sysctl_shadow(struct ctl_table_header *shadow)
+{
+ ctl_free_table(shadow->ctl_table);
+ kfree(shadow);
+}
+
#else /* !CONFIG_SYSCTL */
struct ctl_table_header *register_sysctl_table(struct ctl_table * table)
{
_______________________________________________
Containers mailing list
Containers at lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/containers
More information about the Devel
mailing list