diff options
-rw-r--r-- | include/linux/sysctl.h | 3 | ||||
-rw-r--r-- | kernel/sysctl.c | 63 |
2 files changed, 66 insertions, 0 deletions
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 956264d09ba0..3f6599aeb0db 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h | |||
@@ -1083,6 +1083,9 @@ struct ctl_table_header | |||
1083 | struct ctl_table *ctl_table_arg; | 1083 | struct ctl_table *ctl_table_arg; |
1084 | struct ctl_table_root *root; | 1084 | struct ctl_table_root *root; |
1085 | struct ctl_table_set *set; | 1085 | struct ctl_table_set *set; |
1086 | struct ctl_table *attached_by; | ||
1087 | struct ctl_table *attached_to; | ||
1088 | struct ctl_table_header *parent; | ||
1086 | }; | 1089 | }; |
1087 | 1090 | ||
1088 | /* struct ctl_path describes where in the hierarchy a table is added */ | 1091 | /* struct ctl_path describes where in the hierarchy a table is added */ |
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 60d9357e7172..c9a0af887033 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
@@ -1680,6 +1680,52 @@ static __init int sysctl_init(void) | |||
1680 | 1680 | ||
1681 | core_initcall(sysctl_init); | 1681 | core_initcall(sysctl_init); |
1682 | 1682 | ||
1683 | static int is_branch_in(struct ctl_table *branch, struct ctl_table *table) | ||
1684 | { | ||
1685 | struct ctl_table *p; | ||
1686 | const char *s = branch->procname; | ||
1687 | |||
1688 | /* branch should have named subdirectory as its first element */ | ||
1689 | if (!s || !branch->child) | ||
1690 | return 0; | ||
1691 | |||
1692 | /* ... and nothing else */ | ||
1693 | if (branch[1].procname || branch[1].ctl_name) | ||
1694 | return 0; | ||
1695 | |||
1696 | /* table should contain subdirectory with the same name */ | ||
1697 | for (p = table; p->procname || p->ctl_name; p++) { | ||
1698 | if (!p->child) | ||
1699 | continue; | ||
1700 | if (p->procname && strcmp(p->procname, s) == 0) | ||
1701 | return 1; | ||
1702 | } | ||
1703 | return 0; | ||
1704 | } | ||
1705 | |||
1706 | /* see if attaching q to p would be an improvement */ | ||
1707 | static void try_attach(struct ctl_table_header *p, struct ctl_table_header *q) | ||
1708 | { | ||
1709 | struct ctl_table *to = p->ctl_table, *by = q->ctl_table; | ||
1710 | int is_better = 0; | ||
1711 | int not_in_parent = !p->attached_by; | ||
1712 | |||
1713 | while (is_branch_in(by, to)) { | ||
1714 | if (by == q->attached_by) | ||
1715 | is_better = 1; | ||
1716 | if (to == p->attached_by) | ||
1717 | not_in_parent = 1; | ||
1718 | by = by->child; | ||
1719 | to = to->child; | ||
1720 | } | ||
1721 | |||
1722 | if (is_better && not_in_parent) { | ||
1723 | q->attached_by = by; | ||
1724 | q->attached_to = to; | ||
1725 | q->parent = p; | ||
1726 | } | ||
1727 | } | ||
1728 | |||
1683 | /** | 1729 | /** |
1684 | * __register_sysctl_paths - register a sysctl hierarchy | 1730 | * __register_sysctl_paths - register a sysctl hierarchy |
1685 | * @root: List of sysctl headers to register on | 1731 | * @root: List of sysctl headers to register on |
@@ -1759,6 +1805,7 @@ struct ctl_table_header *__register_sysctl_paths( | |||
1759 | struct ctl_table_header *header; | 1805 | struct ctl_table_header *header; |
1760 | struct ctl_table *new, **prevp; | 1806 | struct ctl_table *new, **prevp; |
1761 | unsigned int n, npath; | 1807 | unsigned int n, npath; |
1808 | struct ctl_table_set *set; | ||
1762 | 1809 | ||
1763 | /* Count the path components */ | 1810 | /* Count the path components */ |
1764 | for (npath = 0; path[npath].ctl_name || path[npath].procname; ++npath) | 1811 | for (npath = 0; path[npath].ctl_name || path[npath].procname; ++npath) |
@@ -1809,6 +1856,18 @@ struct ctl_table_header *__register_sysctl_paths( | |||
1809 | #endif | 1856 | #endif |
1810 | spin_lock(&sysctl_lock); | 1857 | spin_lock(&sysctl_lock); |
1811 | header->set = lookup_header_set(root, namespaces); | 1858 | header->set = lookup_header_set(root, namespaces); |
1859 | header->attached_by = header->ctl_table; | ||
1860 | header->attached_to = root_table; | ||
1861 | header->parent = &root_table_header; | ||
1862 | for (set = header->set; set; set = set->parent) { | ||
1863 | struct ctl_table_header *p; | ||
1864 | list_for_each_entry(p, &set->list, ctl_entry) { | ||
1865 | if (p->unregistering) | ||
1866 | continue; | ||
1867 | try_attach(p, header); | ||
1868 | } | ||
1869 | } | ||
1870 | header->parent->count++; | ||
1812 | list_add_tail(&header->ctl_entry, &header->set->list); | 1871 | list_add_tail(&header->ctl_entry, &header->set->list); |
1813 | spin_unlock(&sysctl_lock); | 1872 | spin_unlock(&sysctl_lock); |
1814 | 1873 | ||
@@ -1864,6 +1923,10 @@ void unregister_sysctl_table(struct ctl_table_header * header) | |||
1864 | 1923 | ||
1865 | spin_lock(&sysctl_lock); | 1924 | spin_lock(&sysctl_lock); |
1866 | start_unregistering(header); | 1925 | start_unregistering(header); |
1926 | if (!--header->parent->count) { | ||
1927 | WARN_ON(1); | ||
1928 | kfree(header->parent); | ||
1929 | } | ||
1867 | if (!--header->count) | 1930 | if (!--header->count) |
1868 | kfree(header); | 1931 | kfree(header); |
1869 | spin_unlock(&sysctl_lock); | 1932 | spin_unlock(&sysctl_lock); |