| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * HugeTLB sysfs interfaces. |
| * (C) Nadia Yvette Chambers, April 2004 |
| */ |
| |
| #include <linux/sysctl.h> |
| |
| #include "hugetlb_internal.h" |
| |
| #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, |
| }, |
| }; |
| |
| void __init hugetlb_sysctl_init(void) |
| { |
| register_sysctl_init("vm", hugetlb_table); |
| } |
| #endif /* CONFIG_SYSCTL */ |