diff options
| author | Ingo Molnar <mingo@elte.hu> | 2008-09-22 07:08:57 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2008-09-22 07:08:57 -0400 |
| commit | 0b88641f1bafdbd087d5e63987a30cc0eadd63b9 (patch) | |
| tree | 81dcf756db373444140bb2623584710c628e3048 /kernel/sysctl.c | |
| parent | fbdbf709938d155c719c76b9894d28342632c797 (diff) | |
| parent | 72d31053f62c4bc464c2783974926969614a8649 (diff) | |
Merge commit 'v2.6.27-rc7' into x86/debug
Diffstat (limited to 'kernel/sysctl.c')
| -rw-r--r-- | kernel/sysctl.c | 183 |
1 files changed, 161 insertions, 22 deletions
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 2a7b9d88706b..50ec0886fa3d 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
| @@ -43,6 +43,7 @@ | |||
| 43 | #include <linux/limits.h> | 43 | #include <linux/limits.h> |
| 44 | #include <linux/dcache.h> | 44 | #include <linux/dcache.h> |
| 45 | #include <linux/syscalls.h> | 45 | #include <linux/syscalls.h> |
| 46 | #include <linux/vmstat.h> | ||
| 46 | #include <linux/nfs_fs.h> | 47 | #include <linux/nfs_fs.h> |
| 47 | #include <linux/acpi.h> | 48 | #include <linux/acpi.h> |
| 48 | #include <linux/reboot.h> | 49 | #include <linux/reboot.h> |
| @@ -80,7 +81,6 @@ extern int sysctl_drop_caches; | |||
| 80 | extern int percpu_pagelist_fraction; | 81 | extern int percpu_pagelist_fraction; |
| 81 | extern int compat_log; | 82 | extern int compat_log; |
| 82 | extern int maps_protect; | 83 | extern int maps_protect; |
| 83 | extern int sysctl_stat_interval; | ||
| 84 | extern int latencytop_enabled; | 84 | extern int latencytop_enabled; |
| 85 | extern int sysctl_nr_open_min, sysctl_nr_open_max; | 85 | extern int sysctl_nr_open_min, sysctl_nr_open_max; |
| 86 | #ifdef CONFIG_RCU_TORTURE_TEST | 86 | #ifdef CONFIG_RCU_TORTURE_TEST |
| @@ -159,13 +159,15 @@ static int proc_dointvec_taint(struct ctl_table *table, int write, struct file * | |||
| 159 | static struct ctl_table root_table[]; | 159 | static struct ctl_table root_table[]; |
| 160 | static struct ctl_table_root sysctl_table_root; | 160 | static struct ctl_table_root sysctl_table_root; |
| 161 | static struct ctl_table_header root_table_header = { | 161 | static struct ctl_table_header root_table_header = { |
| 162 | .count = 1, | ||
| 162 | .ctl_table = root_table, | 163 | .ctl_table = root_table, |
| 163 | .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.header_list), | 164 | .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.list), |
| 164 | .root = &sysctl_table_root, | 165 | .root = &sysctl_table_root, |
| 166 | .set = &sysctl_table_root.default_set, | ||
| 165 | }; | 167 | }; |
| 166 | static struct ctl_table_root sysctl_table_root = { | 168 | static struct ctl_table_root sysctl_table_root = { |
| 167 | .root_list = LIST_HEAD_INIT(sysctl_table_root.root_list), | 169 | .root_list = LIST_HEAD_INIT(sysctl_table_root.root_list), |
| 168 | .header_list = LIST_HEAD_INIT(root_table_header.ctl_entry), | 170 | .default_set.list = LIST_HEAD_INIT(root_table_header.ctl_entry), |
| 169 | }; | 171 | }; |
| 170 | 172 | ||
| 171 | static struct ctl_table kern_table[]; | 173 | static struct ctl_table kern_table[]; |
| @@ -624,7 +626,7 @@ static struct ctl_table kern_table[] = { | |||
| 624 | { | 626 | { |
| 625 | .ctl_name = KERN_PRINTK_RATELIMIT, | 627 | .ctl_name = KERN_PRINTK_RATELIMIT, |
| 626 | .procname = "printk_ratelimit", | 628 | .procname = "printk_ratelimit", |
| 627 | .data = &printk_ratelimit_jiffies, | 629 | .data = &printk_ratelimit_state.interval, |
| 628 | .maxlen = sizeof(int), | 630 | .maxlen = sizeof(int), |
| 629 | .mode = 0644, | 631 | .mode = 0644, |
| 630 | .proc_handler = &proc_dointvec_jiffies, | 632 | .proc_handler = &proc_dointvec_jiffies, |
| @@ -633,7 +635,7 @@ static struct ctl_table kern_table[] = { | |||
| 633 | { | 635 | { |
| 634 | .ctl_name = KERN_PRINTK_RATELIMIT_BURST, | 636 | .ctl_name = KERN_PRINTK_RATELIMIT_BURST, |
| 635 | .procname = "printk_ratelimit_burst", | 637 | .procname = "printk_ratelimit_burst", |
| 636 | .data = &printk_ratelimit_burst, | 638 | .data = &printk_ratelimit_state.burst, |
| 637 | .maxlen = sizeof(int), | 639 | .maxlen = sizeof(int), |
| 638 | .mode = 0644, | 640 | .mode = 0644, |
| 639 | .proc_handler = &proc_dointvec, | 641 | .proc_handler = &proc_dointvec, |
| @@ -959,7 +961,7 @@ static struct ctl_table vm_table[] = { | |||
| 959 | #ifdef CONFIG_HUGETLB_PAGE | 961 | #ifdef CONFIG_HUGETLB_PAGE |
| 960 | { | 962 | { |
| 961 | .procname = "nr_hugepages", | 963 | .procname = "nr_hugepages", |
| 962 | .data = &max_huge_pages, | 964 | .data = NULL, |
| 963 | .maxlen = sizeof(unsigned long), | 965 | .maxlen = sizeof(unsigned long), |
| 964 | .mode = 0644, | 966 | .mode = 0644, |
| 965 | .proc_handler = &hugetlb_sysctl_handler, | 967 | .proc_handler = &hugetlb_sysctl_handler, |
| @@ -985,10 +987,12 @@ static struct ctl_table vm_table[] = { | |||
| 985 | { | 987 | { |
| 986 | .ctl_name = CTL_UNNUMBERED, | 988 | .ctl_name = CTL_UNNUMBERED, |
| 987 | .procname = "nr_overcommit_hugepages", | 989 | .procname = "nr_overcommit_hugepages", |
| 988 | .data = &sysctl_overcommit_huge_pages, | 990 | .data = NULL, |
| 989 | .maxlen = sizeof(sysctl_overcommit_huge_pages), | 991 | .maxlen = sizeof(unsigned long), |
| 990 | .mode = 0644, | 992 | .mode = 0644, |
| 991 | .proc_handler = &hugetlb_overcommit_handler, | 993 | .proc_handler = &hugetlb_overcommit_handler, |
| 994 | .extra1 = (void *)&hugetlb_zero, | ||
| 995 | .extra2 = (void *)&hugetlb_infinity, | ||
| 992 | }, | 996 | }, |
| 993 | #endif | 997 | #endif |
| 994 | { | 998 | { |
| @@ -1384,6 +1388,9 @@ static void start_unregistering(struct ctl_table_header *p) | |||
| 1384 | spin_unlock(&sysctl_lock); | 1388 | spin_unlock(&sysctl_lock); |
| 1385 | wait_for_completion(&wait); | 1389 | wait_for_completion(&wait); |
| 1386 | spin_lock(&sysctl_lock); | 1390 | spin_lock(&sysctl_lock); |
| 1391 | } else { | ||
| 1392 | /* anything non-NULL; we'll never dereference it */ | ||
| 1393 | p->unregistering = ERR_PTR(-EINVAL); | ||
| 1387 | } | 1394 | } |
| 1388 | /* | 1395 | /* |
| 1389 | * do not remove from the list until nobody holds it; walking the | 1396 | * do not remove from the list until nobody holds it; walking the |
| @@ -1392,6 +1399,32 @@ static void start_unregistering(struct ctl_table_header *p) | |||
| 1392 | list_del_init(&p->ctl_entry); | 1399 | list_del_init(&p->ctl_entry); |
| 1393 | } | 1400 | } |
| 1394 | 1401 | ||
| 1402 | void sysctl_head_get(struct ctl_table_header *head) | ||
| 1403 | { | ||
| 1404 | spin_lock(&sysctl_lock); | ||
| 1405 | head->count++; | ||
| 1406 | spin_unlock(&sysctl_lock); | ||
| 1407 | } | ||
| 1408 | |||
| 1409 | void sysctl_head_put(struct ctl_table_header *head) | ||
| 1410 | { | ||
| 1411 | spin_lock(&sysctl_lock); | ||
| 1412 | if (!--head->count) | ||
| 1413 | kfree(head); | ||
| 1414 | spin_unlock(&sysctl_lock); | ||
| 1415 | } | ||
| 1416 | |||
| 1417 | struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) | ||
| 1418 | { | ||
| 1419 | if (!head) | ||
| 1420 | BUG(); | ||
| 1421 | spin_lock(&sysctl_lock); | ||
| 1422 | if (!use_table(head)) | ||
| 1423 | head = ERR_PTR(-ENOENT); | ||
| 1424 | spin_unlock(&sysctl_lock); | ||
| 1425 | return head; | ||
| 1426 | } | ||
| 1427 | |||
| 1395 | void sysctl_head_finish(struct ctl_table_header *head) | 1428 | void sysctl_head_finish(struct ctl_table_header *head) |
| 1396 | { | 1429 | { |
| 1397 | if (!head) | 1430 | if (!head) |
| @@ -1401,14 +1434,20 @@ void sysctl_head_finish(struct ctl_table_header *head) | |||
| 1401 | spin_unlock(&sysctl_lock); | 1434 | spin_unlock(&sysctl_lock); |
| 1402 | } | 1435 | } |
| 1403 | 1436 | ||
| 1437 | static struct ctl_table_set * | ||
| 1438 | lookup_header_set(struct ctl_table_root *root, struct nsproxy *namespaces) | ||
| 1439 | { | ||
| 1440 | struct ctl_table_set *set = &root->default_set; | ||
| 1441 | if (root->lookup) | ||
| 1442 | set = root->lookup(root, namespaces); | ||
| 1443 | return set; | ||
| 1444 | } | ||
| 1445 | |||
| 1404 | static struct list_head * | 1446 | static struct list_head * |
| 1405 | lookup_header_list(struct ctl_table_root *root, struct nsproxy *namespaces) | 1447 | lookup_header_list(struct ctl_table_root *root, struct nsproxy *namespaces) |
| 1406 | { | 1448 | { |
| 1407 | struct list_head *header_list; | 1449 | struct ctl_table_set *set = lookup_header_set(root, namespaces); |
| 1408 | header_list = &root->header_list; | 1450 | return &set->list; |
| 1409 | if (root->lookup) | ||
| 1410 | header_list = root->lookup(root, namespaces); | ||
| 1411 | return header_list; | ||
| 1412 | } | 1451 | } |
| 1413 | 1452 | ||
| 1414 | struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, | 1453 | struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, |
| @@ -1478,9 +1517,9 @@ static int do_sysctl_strategy(struct ctl_table_root *root, | |||
| 1478 | int op = 0, rc; | 1517 | int op = 0, rc; |
| 1479 | 1518 | ||
| 1480 | if (oldval) | 1519 | if (oldval) |
| 1481 | op |= 004; | 1520 | op |= MAY_READ; |
| 1482 | if (newval) | 1521 | if (newval) |
| 1483 | op |= 002; | 1522 | op |= MAY_WRITE; |
| 1484 | if (sysctl_perm(root, table, op)) | 1523 | if (sysctl_perm(root, table, op)) |
| 1485 | return -EPERM; | 1524 | return -EPERM; |
| 1486 | 1525 | ||
| @@ -1522,7 +1561,7 @@ repeat: | |||
| 1522 | if (n == table->ctl_name) { | 1561 | if (n == table->ctl_name) { |
| 1523 | int error; | 1562 | int error; |
| 1524 | if (table->child) { | 1563 | if (table->child) { |
| 1525 | if (sysctl_perm(root, table, 001)) | 1564 | if (sysctl_perm(root, table, MAY_EXEC)) |
| 1526 | return -EPERM; | 1565 | return -EPERM; |
| 1527 | name++; | 1566 | name++; |
| 1528 | nlen--; | 1567 | nlen--; |
| @@ -1597,7 +1636,7 @@ static int test_perm(int mode, int op) | |||
| 1597 | mode >>= 6; | 1636 | mode >>= 6; |
| 1598 | else if (in_egroup_p(0)) | 1637 | else if (in_egroup_p(0)) |
| 1599 | mode >>= 3; | 1638 | mode >>= 3; |
| 1600 | if ((mode & op & 0007) == op) | 1639 | if ((op & ~mode & (MAY_READ|MAY_WRITE|MAY_EXEC)) == 0) |
| 1601 | return 0; | 1640 | return 0; |
| 1602 | return -EACCES; | 1641 | return -EACCES; |
| 1603 | } | 1642 | } |
| @@ -1607,7 +1646,7 @@ int sysctl_perm(struct ctl_table_root *root, struct ctl_table *table, int op) | |||
| 1607 | int error; | 1646 | int error; |
| 1608 | int mode; | 1647 | int mode; |
| 1609 | 1648 | ||
| 1610 | error = security_sysctl(table, op); | 1649 | error = security_sysctl(table, op & (MAY_READ | MAY_WRITE | MAY_EXEC)); |
| 1611 | if (error) | 1650 | if (error) |
| 1612 | return error; | 1651 | return error; |
| 1613 | 1652 | ||
| @@ -1642,6 +1681,54 @@ static __init int sysctl_init(void) | |||
| 1642 | 1681 | ||
| 1643 | core_initcall(sysctl_init); | 1682 | core_initcall(sysctl_init); |
| 1644 | 1683 | ||
| 1684 | static struct ctl_table *is_branch_in(struct ctl_table *branch, | ||
| 1685 | struct ctl_table *table) | ||
| 1686 | { | ||
| 1687 | struct ctl_table *p; | ||
| 1688 | const char *s = branch->procname; | ||
| 1689 | |||
| 1690 | /* branch should have named subdirectory as its first element */ | ||
| 1691 | if (!s || !branch->child) | ||
| 1692 | return NULL; | ||
| 1693 | |||
| 1694 | /* ... and nothing else */ | ||
| 1695 | if (branch[1].procname || branch[1].ctl_name) | ||
| 1696 | return NULL; | ||
| 1697 | |||
| 1698 | /* table should contain subdirectory with the same name */ | ||
| 1699 | for (p = table; p->procname || p->ctl_name; p++) { | ||
| 1700 | if (!p->child) | ||
| 1701 | continue; | ||
| 1702 | if (p->procname && strcmp(p->procname, s) == 0) | ||
| 1703 | return p; | ||
| 1704 | } | ||
| 1705 | return NULL; | ||
| 1706 | } | ||
| 1707 | |||
| 1708 | /* see if attaching q to p would be an improvement */ | ||
| 1709 | static void try_attach(struct ctl_table_header *p, struct ctl_table_header *q) | ||
| 1710 | { | ||
| 1711 | struct ctl_table *to = p->ctl_table, *by = q->ctl_table; | ||
| 1712 | struct ctl_table *next; | ||
| 1713 | int is_better = 0; | ||
| 1714 | int not_in_parent = !p->attached_by; | ||
| 1715 | |||
| 1716 | while ((next = is_branch_in(by, to)) != NULL) { | ||
| 1717 | if (by == q->attached_by) | ||
| 1718 | is_better = 1; | ||
| 1719 | if (to == p->attached_by) | ||
| 1720 | not_in_parent = 1; | ||
| 1721 | by = by->child; | ||
| 1722 | to = next->child; | ||
| 1723 | } | ||
| 1724 | |||
| 1725 | if (is_better && not_in_parent) { | ||
| 1726 | q->attached_by = by; | ||
| 1727 | q->attached_to = to; | ||
| 1728 | q->parent = p; | ||
| 1729 | } | ||
| 1730 | } | ||
| 1731 | |||
| 1645 | /** | 1732 | /** |
| 1646 | * __register_sysctl_paths - register a sysctl hierarchy | 1733 | * __register_sysctl_paths - register a sysctl hierarchy |
| 1647 | * @root: List of sysctl headers to register on | 1734 | * @root: List of sysctl headers to register on |
| @@ -1718,10 +1805,10 @@ struct ctl_table_header *__register_sysctl_paths( | |||
| 1718 | struct nsproxy *namespaces, | 1805 | struct nsproxy *namespaces, |
| 1719 | const struct ctl_path *path, struct ctl_table *table) | 1806 | const struct ctl_path *path, struct ctl_table *table) |
| 1720 | { | 1807 | { |
| 1721 | struct list_head *header_list; | ||
| 1722 | struct ctl_table_header *header; | 1808 | struct ctl_table_header *header; |
| 1723 | struct ctl_table *new, **prevp; | 1809 | struct ctl_table *new, **prevp; |
| 1724 | unsigned int n, npath; | 1810 | unsigned int n, npath; |
| 1811 | struct ctl_table_set *set; | ||
| 1725 | 1812 | ||
| 1726 | /* Count the path components */ | 1813 | /* Count the path components */ |
| 1727 | for (npath = 0; path[npath].ctl_name || path[npath].procname; ++npath) | 1814 | for (npath = 0; path[npath].ctl_name || path[npath].procname; ++npath) |
| @@ -1763,6 +1850,7 @@ struct ctl_table_header *__register_sysctl_paths( | |||
| 1763 | header->unregistering = NULL; | 1850 | header->unregistering = NULL; |
| 1764 | header->root = root; | 1851 | header->root = root; |
| 1765 | sysctl_set_parent(NULL, header->ctl_table); | 1852 | sysctl_set_parent(NULL, header->ctl_table); |
| 1853 | header->count = 1; | ||
| 1766 | #ifdef CONFIG_SYSCTL_SYSCALL_CHECK | 1854 | #ifdef CONFIG_SYSCTL_SYSCALL_CHECK |
| 1767 | if (sysctl_check_table(namespaces, header->ctl_table)) { | 1855 | if (sysctl_check_table(namespaces, header->ctl_table)) { |
| 1768 | kfree(header); | 1856 | kfree(header); |
| @@ -1770,8 +1858,20 @@ struct ctl_table_header *__register_sysctl_paths( | |||
| 1770 | } | 1858 | } |
| 1771 | #endif | 1859 | #endif |
| 1772 | spin_lock(&sysctl_lock); | 1860 | spin_lock(&sysctl_lock); |
| 1773 | header_list = lookup_header_list(root, namespaces); | 1861 | header->set = lookup_header_set(root, namespaces); |
| 1774 | list_add_tail(&header->ctl_entry, header_list); | 1862 | header->attached_by = header->ctl_table; |
| 1863 | header->attached_to = root_table; | ||
| 1864 | header->parent = &root_table_header; | ||
| 1865 | for (set = header->set; set; set = set->parent) { | ||
| 1866 | struct ctl_table_header *p; | ||
| 1867 | list_for_each_entry(p, &set->list, ctl_entry) { | ||
| 1868 | if (p->unregistering) | ||
| 1869 | continue; | ||
| 1870 | try_attach(p, header); | ||
| 1871 | } | ||
| 1872 | } | ||
| 1873 | header->parent->count++; | ||
| 1874 | list_add_tail(&header->ctl_entry, &header->set->list); | ||
| 1775 | spin_unlock(&sysctl_lock); | 1875 | spin_unlock(&sysctl_lock); |
| 1776 | 1876 | ||
| 1777 | return header; | 1877 | return header; |
| @@ -1826,8 +1926,37 @@ void unregister_sysctl_table(struct ctl_table_header * header) | |||
| 1826 | 1926 | ||
| 1827 | spin_lock(&sysctl_lock); | 1927 | spin_lock(&sysctl_lock); |
| 1828 | start_unregistering(header); | 1928 | start_unregistering(header); |
| 1929 | if (!--header->parent->count) { | ||
| 1930 | WARN_ON(1); | ||
| 1931 | kfree(header->parent); | ||
| 1932 | } | ||
| 1933 | if (!--header->count) | ||
| 1934 | kfree(header); | ||
| 1935 | spin_unlock(&sysctl_lock); | ||
| 1936 | } | ||
| 1937 | |||
| 1938 | int sysctl_is_seen(struct ctl_table_header *p) | ||
| 1939 | { | ||
| 1940 | struct ctl_table_set *set = p->set; | ||
| 1941 | int res; | ||
| 1942 | spin_lock(&sysctl_lock); | ||
| 1943 | if (p->unregistering) | ||
| 1944 | res = 0; | ||
| 1945 | else if (!set->is_seen) | ||
| 1946 | res = 1; | ||
| 1947 | else | ||
| 1948 | res = set->is_seen(set); | ||
| 1829 | spin_unlock(&sysctl_lock); | 1949 | spin_unlock(&sysctl_lock); |
| 1830 | kfree(header); | 1950 | return res; |
| 1951 | } | ||
| 1952 | |||
| 1953 | void setup_sysctl_set(struct ctl_table_set *p, | ||
| 1954 | struct ctl_table_set *parent, | ||
| 1955 | int (*is_seen)(struct ctl_table_set *)) | ||
| 1956 | { | ||
| 1957 | INIT_LIST_HEAD(&p->list); | ||
| 1958 | p->parent = parent ? parent : &sysctl_table_root.default_set; | ||
| 1959 | p->is_seen = is_seen; | ||
| 1831 | } | 1960 | } |
| 1832 | 1961 | ||
| 1833 | #else /* !CONFIG_SYSCTL */ | 1962 | #else /* !CONFIG_SYSCTL */ |
| @@ -1846,6 +1975,16 @@ void unregister_sysctl_table(struct ctl_table_header * table) | |||
| 1846 | { | 1975 | { |
| 1847 | } | 1976 | } |
| 1848 | 1977 | ||
| 1978 | void setup_sysctl_set(struct ctl_table_set *p, | ||
| 1979 | struct ctl_table_set *parent, | ||
| 1980 | int (*is_seen)(struct ctl_table_set *)) | ||
| 1981 | { | ||
| 1982 | } | ||
| 1983 | |||
| 1984 | void sysctl_head_put(struct ctl_table_header *head) | ||
| 1985 | { | ||
| 1986 | } | ||
| 1987 | |||
| 1849 | #endif /* CONFIG_SYSCTL */ | 1988 | #endif /* CONFIG_SYSCTL */ |
| 1850 | 1989 | ||
| 1851 | /* | 1990 | /* |
