diff options
-rw-r--r-- | include/linux/sysctl.h | 6 | ||||
-rw-r--r-- | kernel/sysctl.c | 37 |
2 files changed, 42 insertions, 1 deletions
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index c1e0cf408af9..956264d09ba0 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h | |||
@@ -957,6 +957,11 @@ extern void setup_sysctl_set(struct ctl_table_set *p, | |||
957 | struct ctl_table_set *parent, | 957 | struct ctl_table_set *parent, |
958 | int (*is_seen)(struct ctl_table_set *)); | 958 | int (*is_seen)(struct ctl_table_set *)); |
959 | 959 | ||
960 | struct ctl_table_header; | ||
961 | |||
962 | extern void sysctl_head_get(struct ctl_table_header *); | ||
963 | extern void sysctl_head_put(struct ctl_table_header *); | ||
964 | extern struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *); | ||
960 | extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev); | 965 | extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev); |
961 | extern struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, | 966 | extern struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, |
962 | struct ctl_table_header *prev); | 967 | struct ctl_table_header *prev); |
@@ -1073,6 +1078,7 @@ struct ctl_table_header | |||
1073 | struct ctl_table *ctl_table; | 1078 | struct ctl_table *ctl_table; |
1074 | struct list_head ctl_entry; | 1079 | struct list_head ctl_entry; |
1075 | int used; | 1080 | int used; |
1081 | int count; | ||
1076 | struct completion *unregistering; | 1082 | struct completion *unregistering; |
1077 | struct ctl_table *ctl_table_arg; | 1083 | struct ctl_table *ctl_table_arg; |
1078 | struct ctl_table_root *root; | 1084 | struct ctl_table_root *root; |
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 8ee4a0619fbb..60d9357e7172 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
@@ -1387,6 +1387,9 @@ static void start_unregistering(struct ctl_table_header *p) | |||
1387 | spin_unlock(&sysctl_lock); | 1387 | spin_unlock(&sysctl_lock); |
1388 | wait_for_completion(&wait); | 1388 | wait_for_completion(&wait); |
1389 | spin_lock(&sysctl_lock); | 1389 | spin_lock(&sysctl_lock); |
1390 | } else { | ||
1391 | /* anything non-NULL; we'll never dereference it */ | ||
1392 | p->unregistering = ERR_PTR(-EINVAL); | ||
1390 | } | 1393 | } |
1391 | /* | 1394 | /* |
1392 | * do not remove from the list until nobody holds it; walking the | 1395 | * do not remove from the list until nobody holds it; walking the |
@@ -1395,6 +1398,32 @@ static void start_unregistering(struct ctl_table_header *p) | |||
1395 | list_del_init(&p->ctl_entry); | 1398 | list_del_init(&p->ctl_entry); |
1396 | } | 1399 | } |
1397 | 1400 | ||
1401 | void sysctl_head_get(struct ctl_table_header *head) | ||
1402 | { | ||
1403 | spin_lock(&sysctl_lock); | ||
1404 | head->count++; | ||
1405 | spin_unlock(&sysctl_lock); | ||
1406 | } | ||
1407 | |||
1408 | void sysctl_head_put(struct ctl_table_header *head) | ||
1409 | { | ||
1410 | spin_lock(&sysctl_lock); | ||
1411 | if (!--head->count) | ||
1412 | kfree(head); | ||
1413 | spin_unlock(&sysctl_lock); | ||
1414 | } | ||
1415 | |||
1416 | struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) | ||
1417 | { | ||
1418 | if (!head) | ||
1419 | BUG(); | ||
1420 | spin_lock(&sysctl_lock); | ||
1421 | if (!use_table(head)) | ||
1422 | head = ERR_PTR(-ENOENT); | ||
1423 | spin_unlock(&sysctl_lock); | ||
1424 | return head; | ||
1425 | } | ||
1426 | |||
1398 | void sysctl_head_finish(struct ctl_table_header *head) | 1427 | void sysctl_head_finish(struct ctl_table_header *head) |
1399 | { | 1428 | { |
1400 | if (!head) | 1429 | if (!head) |
@@ -1771,6 +1800,7 @@ struct ctl_table_header *__register_sysctl_paths( | |||
1771 | header->unregistering = NULL; | 1800 | header->unregistering = NULL; |
1772 | header->root = root; | 1801 | header->root = root; |
1773 | sysctl_set_parent(NULL, header->ctl_table); | 1802 | sysctl_set_parent(NULL, header->ctl_table); |
1803 | header->count = 1; | ||
1774 | #ifdef CONFIG_SYSCTL_SYSCALL_CHECK | 1804 | #ifdef CONFIG_SYSCTL_SYSCALL_CHECK |
1775 | if (sysctl_check_table(namespaces, header->ctl_table)) { | 1805 | if (sysctl_check_table(namespaces, header->ctl_table)) { |
1776 | kfree(header); | 1806 | kfree(header); |
@@ -1834,8 +1864,9 @@ void unregister_sysctl_table(struct ctl_table_header * header) | |||
1834 | 1864 | ||
1835 | spin_lock(&sysctl_lock); | 1865 | spin_lock(&sysctl_lock); |
1836 | start_unregistering(header); | 1866 | start_unregistering(header); |
1867 | if (!--header->count) | ||
1868 | kfree(header); | ||
1837 | spin_unlock(&sysctl_lock); | 1869 | spin_unlock(&sysctl_lock); |
1838 | kfree(header); | ||
1839 | } | 1870 | } |
1840 | 1871 | ||
1841 | void setup_sysctl_set(struct ctl_table_set *p, | 1872 | void setup_sysctl_set(struct ctl_table_set *p, |
@@ -1869,6 +1900,10 @@ void setup_sysctl_set(struct ctl_table_set *p, | |||
1869 | { | 1900 | { |
1870 | } | 1901 | } |
1871 | 1902 | ||
1903 | void sysctl_head_put(struct ctl_table_header *head) | ||
1904 | { | ||
1905 | } | ||
1906 | |||
1872 | #endif /* CONFIG_SYSCTL */ | 1907 | #endif /* CONFIG_SYSCTL */ |
1873 | 1908 | ||
1874 | /* | 1909 | /* |