root/mm/hugetlb_sysctl.c
// 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 */