| // SPDX-License-Identifier: GPL-2.0-only | |
| /* | |
| * HugeTLB sysfs interfaces. | |
| * (C) Nadia Yvette Chambers, April 2004 | |
| */ | |
| #include <linux/sysctl.h> | |
| #include "hugetlb_internal.h" | |
| int movable_gigantic_pages; | |
| #ifdef CONFIG_SYSCTL | |
| static int proc_hugetlb_doulongvec_minmax(const struct ctl_table *table, int write, | |
| void *buffer, size_t *length, | |
| loff_t *ppos, unsigned long *out) | |
| { | |
| struct ctl_table dup_table; | |
| /* | |
| * In order to avoid races with __do_proc_doulongvec_minmax(), we | |
| * can duplicate the @table and alter the duplicate of it. | |
| */ | |
| dup_table = *table; | |
| dup_table.data = out; | |
| return proc_doulongvec_minmax(&dup_table, write, buffer, length, ppos); | |
| } | |
| static int hugetlb_sysctl_handler_common(bool obey_mempolicy, | |
| const struct ctl_table *table, int write, | |
| void *buffer, size_t *length, loff_t *ppos) | |
| { | |
| struct hstate *h = &default_hstate; | |
| unsigned long tmp = h->max_huge_pages; | |
| int ret; | |
| if (!hugepages_supported()) | |
| return -EOPNOTSUPP; | |
| ret = proc_hugetlb_doulongvec_minmax(table, write, buffer, length, ppos, | |
| &tmp); | |
| if (ret) | |
| goto out; | |
| if (write) | |
| ret = __nr_hugepages_store_common(obey_mempolicy, h, | |
| NUMA_NO_NODE, tmp, *length); | |
| out: | |
| return ret; | |
| } | |
| static int hugetlb_sysctl_handler(const struct ctl_table *table, int write, | |
| void *buffer, size_t *length, loff_t *ppos) | |
| { | |
| return hugetlb_sysctl_handler_common(false, table, write, | |
| buffer, length, ppos); | |
| } | |
| #ifdef CONFIG_NUMA | |
| static int hugetlb_mempolicy_sysctl_handler(const struct ctl_table *table, int write, | |
| void *buffer, size_t *length, loff_t *ppos) | |
| { | |
| return hugetlb_sysctl_handler_common(true, table, write, | |
| buffer, length, ppos); | |
| } | |
| #endif /* CONFIG_NUMA */ | |
| static int hugetlb_overcommit_handler(const struct ctl_table *table, int write, | |
| void *buffer, size_t *length, loff_t *ppos) | |
| { | |
| struct hstate *h = &default_hstate; | |
| unsigned long tmp; | |
| int ret; | |
| if (!hugepages_supported()) | |
| return -EOPNOTSUPP; | |
| tmp = h->nr_overcommit_huge_pages; | |
| if (write && hstate_is_gigantic_no_runtime(h)) | |
| return -EINVAL; | |
| ret = proc_hugetlb_doulongvec_minmax(table, write, buffer, length, ppos, | |
| &tmp); | |
| if (ret) | |
| goto out; | |
| if (write) { | |
| spin_lock_irq(&hugetlb_lock); | |
| h->nr_overcommit_huge_pages = tmp; | |
| spin_unlock_irq(&hugetlb_lock); | |
| } | |
| out: | |
| return ret; | |
| } | |
| static const struct ctl_table hugetlb_table[] = { | |
| { | |
| .procname = "nr_hugepages", | |
| .data = NULL, | |
| .maxlen = sizeof(unsigned long), | |
| .mode = 0644, | |
| .proc_handler = hugetlb_sysctl_handler, | |
| }, | |
| #ifdef CONFIG_NUMA | |
| { | |
| .procname = "nr_hugepages_mempolicy", | |
| .data = NULL, | |
| .maxlen = sizeof(unsigned long), | |
| .mode = 0644, | |
| .proc_handler = &hugetlb_mempolicy_sysctl_handler, | |
| }, | |
| #endif | |
| { | |
| .procname = "hugetlb_shm_group", | |
| .data = &sysctl_hugetlb_shm_group, | |
| .maxlen = sizeof(gid_t), | |
| .mode = 0644, | |
| .proc_handler = proc_dointvec, | |
| }, | |
| { | |
| .procname = "nr_overcommit_hugepages", | |
| .data = NULL, | |
| .maxlen = sizeof(unsigned long), | |
| .mode = 0644, | |
| .proc_handler = hugetlb_overcommit_handler, | |
| }, | |
| #ifdef CONFIG_ARCH_ENABLE_HUGEPAGE_MIGRATION | |
| { | |
| .procname = "movable_gigantic_pages", | |
| .data = &movable_gigantic_pages, | |
| .maxlen = sizeof(int), | |
| .mode = 0644, | |
| .proc_handler = proc_dointvec, | |
| }, | |
| #endif | |
| }; | |
| void __init hugetlb_sysctl_init(void) | |
| { | |
| register_sysctl_init("vm", hugetlb_table); | |
| } | |
| #endif /* CONFIG_SYSCTL */ |