| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * HugeTLB sysfs interfaces.
- * (C) Nadia Yvette Chambers, April 2004
- */
- #include <linux/swap.h>
- #include <linux/page_owner.h>
- #include <linux/page-isolation.h>
- #include "hugetlb_vmemmap.h"
- #include "hugetlb_internal.h"
- #define HSTATE_ATTR_RO(_name) \
- static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
- #define HSTATE_ATTR_WO(_name) \
- static struct kobj_attribute _name##_attr = __ATTR_WO(_name)
- #define HSTATE_ATTR(_name) \
- static struct kobj_attribute _name##_attr = __ATTR_RW(_name)
- static struct kobject *hugepages_kobj;
- static struct kobject *hstate_kobjs[HUGE_MAX_HSTATE];
- static struct hstate *kobj_to_node_hstate(struct kobject *kobj, int *nidp);
- static struct hstate *kobj_to_hstate(struct kobject *kobj, int *nidp)
- {
- int i;
- for (i = 0; i < HUGE_MAX_HSTATE; i++)
- if (hstate_kobjs[i] == kobj) {
- if (nidp)
- *nidp = NUMA_NO_NODE;
- return &hstates[i];
- }
- return kobj_to_node_hstate(kobj, nidp);
- }
- static ssize_t nr_hugepages_show_common(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf)
- {
- struct hstate *h;
- unsigned long nr_huge_pages;
- int nid;
- h = kobj_to_hstate(kobj, &nid);
- if (nid == NUMA_NO_NODE)
- nr_huge_pages = h->nr_huge_pages;
- else
- nr_huge_pages = h->nr_huge_pages_node[nid];
- return sysfs_emit(buf, "%lu\n", nr_huge_pages);
- }
- static ssize_t nr_hugepages_store_common(bool obey_mempolicy,
- struct kobject *kobj, const char *buf,
- size_t len)
- {
- struct hstate *h;
- unsigned long count;
- int nid;
- int err;
- err = kstrtoul(buf, 10, &count);
- if (err)
- return err;
- h = kobj_to_hstate(kobj, &nid);
- return __nr_hugepages_store_common(obey_mempolicy, h, nid, count, len);
- }
- static ssize_t nr_hugepages_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf)
- {
- return nr_hugepages_show_common(kobj, attr, buf);
- }
- static ssize_t nr_hugepages_store(struct kobject *kobj,
- struct kobj_attribute *attr, const char *buf, size_t len)
- {
- return nr_hugepages_store_common(false, kobj, buf, len);
- }
- HSTATE_ATTR(nr_hugepages);
- #ifdef CONFIG_NUMA
- /*
- * hstate attribute for optionally mempolicy-based constraint on persistent
- * huge page alloc/free.
- */
- static ssize_t nr_hugepages_mempolicy_show(struct kobject *kobj,
- struct kobj_attribute *attr,
- char *buf)
- {
- return nr_hugepages_show_common(kobj, attr, buf);
- }
- static ssize_t nr_hugepages_mempolicy_store(struct kobject *kobj,
- struct kobj_attribute *attr, const char *buf, size_t len)
- {
- return nr_hugepages_store_common(true, kobj, buf, len);
- }
- HSTATE_ATTR(nr_hugepages_mempolicy);
- #endif
- static ssize_t nr_overcommit_hugepages_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf)
- {
- struct hstate *h = kobj_to_hstate(kobj, NULL);
- return sysfs_emit(buf, "%lu\n", h->nr_overcommit_huge_pages);
- }
- static ssize_t nr_overcommit_hugepages_store(struct kobject *kobj,
- struct kobj_attribute *attr, const char *buf, size_t count)
- {
- int err;
- unsigned long input;
- struct hstate *h = kobj_to_hstate(kobj, NULL);
- if (hstate_is_gigantic_no_runtime(h))
- return -EINVAL;
- err = kstrtoul(buf, 10, &input);
- if (err)
- return err;
- spin_lock_irq(&hugetlb_lock);
- h->nr_overcommit_huge_pages = input;
- spin_unlock_irq(&hugetlb_lock);
- return count;
- }
- HSTATE_ATTR(nr_overcommit_hugepages);
- static ssize_t free_hugepages_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf)
- {
- struct hstate *h;
- unsigned long free_huge_pages;
- int nid;
- h = kobj_to_hstate(kobj, &nid);
- if (nid == NUMA_NO_NODE)
- free_huge_pages = h->free_huge_pages;
- else
- free_huge_pages = h->free_huge_pages_node[nid];
- return sysfs_emit(buf, "%lu\n", free_huge_pages);
- }
- HSTATE_ATTR_RO(free_hugepages);
- static ssize_t resv_hugepages_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf)
- {
- struct hstate *h = kobj_to_hstate(kobj, NULL);
- return sysfs_emit(buf, "%lu\n", h->resv_huge_pages);
- }
- HSTATE_ATTR_RO(resv_hugepages);
- static ssize_t surplus_hugepages_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf)
- {
- struct hstate *h;
- unsigned long surplus_huge_pages;
- int nid;
- h = kobj_to_hstate(kobj, &nid);
- if (nid == NUMA_NO_NODE)
- surplus_huge_pages = h->surplus_huge_pages;
- else
- surplus_huge_pages = h->surplus_huge_pages_node[nid];
- return sysfs_emit(buf, "%lu\n", surplus_huge_pages);
- }
- HSTATE_ATTR_RO(surplus_hugepages);
- static ssize_t demote_store(struct kobject *kobj,
- struct kobj_attribute *attr, const char *buf, size_t len)
- {
- unsigned long nr_demote;
- unsigned long nr_available;
- nodemask_t nodes_allowed, *n_mask;
- struct hstate *h;
- int err;
- int nid;
- err = kstrtoul(buf, 10, &nr_demote);
- if (err)
- return err;
- h = kobj_to_hstate(kobj, &nid);
- if (nid != NUMA_NO_NODE) {
- init_nodemask_of_node(&nodes_allowed, nid);
- n_mask = &nodes_allowed;
- } else {
- n_mask = &node_states[N_MEMORY];
- }
- /* Synchronize with other sysfs operations modifying huge pages */
- mutex_lock(&h->resize_lock);
- spin_lock_irq(&hugetlb_lock);
- while (nr_demote) {
- long rc;
- /*
- * Check for available pages to demote each time thorough the
- * loop as demote_pool_huge_page will drop hugetlb_lock.
- */
- if (nid != NUMA_NO_NODE)
- nr_available = h->free_huge_pages_node[nid];
- else
- nr_available = h->free_huge_pages;
- nr_available -= h->resv_huge_pages;
- if (!nr_available)
- break;
- rc = demote_pool_huge_page(h, n_mask, nr_demote);
- if (rc < 0) {
- err = rc;
- break;
- }
- nr_demote -= rc;
- }
- spin_unlock_irq(&hugetlb_lock);
- mutex_unlock(&h->resize_lock);
- if (err)
- return err;
- return len;
- }
- HSTATE_ATTR_WO(demote);
- static ssize_t demote_size_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf)
- {
- struct hstate *h = kobj_to_hstate(kobj, NULL);
- unsigned long demote_size = (PAGE_SIZE << h->demote_order) / SZ_1K;
- return sysfs_emit(buf, "%lukB\n", demote_size);
- }
- static ssize_t demote_size_store(struct kobject *kobj,
- struct kobj_attribute *attr,
- const char *buf, size_t count)
- {
- struct hstate *h, *demote_hstate;
- unsigned long demote_size;
- unsigned int demote_order;
- demote_size = (unsigned long)memparse(buf, NULL);
- demote_hstate = size_to_hstate(demote_size);
- if (!demote_hstate)
- return -EINVAL;
- demote_order = demote_hstate->order;
- if (demote_order < HUGETLB_PAGE_ORDER)
- return -EINVAL;
- /* demote order must be smaller than hstate order */
- h = kobj_to_hstate(kobj, NULL);
- if (demote_order >= h->order)
- return -EINVAL;
- /* resize_lock synchronizes access to demote size and writes */
- mutex_lock(&h->resize_lock);
- h->demote_order = demote_order;
- mutex_unlock(&h->resize_lock);
- return count;
- }
- HSTATE_ATTR(demote_size);
- static struct attribute *hstate_attrs[] = {
- &nr_hugepages_attr.attr,
- &nr_overcommit_hugepages_attr.attr,
- &free_hugepages_attr.attr,
- &resv_hugepages_attr.attr,
- &surplus_hugepages_attr.attr,
- #ifdef CONFIG_NUMA
- &nr_hugepages_mempolicy_attr.attr,
- #endif
- NULL,
- };
- static const struct attribute_group hstate_attr_group = {
- .attrs = hstate_attrs,
- };
- static struct attribute *hstate_demote_attrs[] = {
- &demote_size_attr.attr,
- &demote_attr.attr,
- NULL,
- };
- static const struct attribute_group hstate_demote_attr_group = {
- .attrs = hstate_demote_attrs,
- };
- static int hugetlb_sysfs_add_hstate(struct hstate *h, struct kobject *parent,
- struct kobject **hstate_kobjs,
- const struct attribute_group *hstate_attr_group)
- {
- int retval;
- int hi = hstate_index(h);
- hstate_kobjs[hi] = kobject_create_and_add(h->name, parent);
- if (!hstate_kobjs[hi])
- return -ENOMEM;
- retval = sysfs_create_group(hstate_kobjs[hi], hstate_attr_group);
- if (retval) {
- kobject_put(hstate_kobjs[hi]);
- hstate_kobjs[hi] = NULL;
- return retval;
- }
- if (h->demote_order) {
- retval = sysfs_create_group(hstate_kobjs[hi],
- &hstate_demote_attr_group);
- if (retval) {
- pr_warn("HugeTLB unable to create demote interfaces for %s\n", h->name);
- sysfs_remove_group(hstate_kobjs[hi], hstate_attr_group);
- kobject_put(hstate_kobjs[hi]);
- hstate_kobjs[hi] = NULL;
- return retval;
- }
- }
- return 0;
- }
- #ifdef CONFIG_NUMA
- static bool hugetlb_sysfs_initialized __ro_after_init;
- /*
- * node_hstate/s - associate per node hstate attributes, via their kobjects,
- * with node devices in node_devices[] using a parallel array. The array
- * index of a node device or _hstate == node id.
- * This is here to avoid any static dependency of the node device driver, in
- * the base kernel, on the hugetlb module.
- */
- struct node_hstate {
- struct kobject *hugepages_kobj;
- struct kobject *hstate_kobjs[HUGE_MAX_HSTATE];
- };
- static struct node_hstate node_hstates[MAX_NUMNODES];
- /*
- * A subset of global hstate attributes for node devices
- */
- static struct attribute *per_node_hstate_attrs[] = {
- &nr_hugepages_attr.attr,
- &free_hugepages_attr.attr,
- &surplus_hugepages_attr.attr,
- NULL,
- };
- static const struct attribute_group per_node_hstate_attr_group = {
- .attrs = per_node_hstate_attrs,
- };
- /*
- * kobj_to_node_hstate - lookup global hstate for node device hstate attr kobj.
- * Returns node id via non-NULL nidp.
- */
- static struct hstate *kobj_to_node_hstate(struct kobject *kobj, int *nidp)
- {
- int nid;
- for (nid = 0; nid < nr_node_ids; nid++) {
- struct node_hstate *nhs = &node_hstates[nid];
- int i;
- for (i = 0; i < HUGE_MAX_HSTATE; i++)
- if (nhs->hstate_kobjs[i] == kobj) {
- if (nidp)
- *nidp = nid;
- return &hstates[i];
- }
- }
- BUG();
- return NULL;
- }
- /*
- * Unregister hstate attributes from a single node device.
- * No-op if no hstate attributes attached.
- */
- void hugetlb_unregister_node(struct node *node)
- {
- struct hstate *h;
- struct node_hstate *nhs = &node_hstates[node->dev.id];
- if (!nhs->hugepages_kobj)
- return; /* no hstate attributes */
- for_each_hstate(h) {
- int idx = hstate_index(h);
- struct kobject *hstate_kobj = nhs->hstate_kobjs[idx];
- if (!hstate_kobj)
- continue;
- if (h->demote_order)
- sysfs_remove_group(hstate_kobj, &hstate_demote_attr_group);
- sysfs_remove_group(hstate_kobj, &per_node_hstate_attr_group);
- kobject_put(hstate_kobj);
- nhs->hstate_kobjs[idx] = NULL;
- }
- kobject_put(nhs->hugepages_kobj);
- nhs->hugepages_kobj = NULL;
- }
- /*
- * Register hstate attributes for a single node device.
- * No-op if attributes already registered.
- */
- void hugetlb_register_node(struct node *node)
- {
- struct hstate *h;
- struct node_hstate *nhs = &node_hstates[node->dev.id];
- int err;
- if (!hugetlb_sysfs_initialized)
- return;
- if (nhs->hugepages_kobj)
- return; /* already allocated */
- nhs->hugepages_kobj = kobject_create_and_add("hugepages",
- &node->dev.kobj);
- if (!nhs->hugepages_kobj)
- return;
- for_each_hstate(h) {
- err = hugetlb_sysfs_add_hstate(h, nhs->hugepages_kobj,
- nhs->hstate_kobjs,
- &per_node_hstate_attr_group);
- if (err) {
- pr_err("HugeTLB: Unable to add hstate %s for node %d\n",
- h->name, node->dev.id);
- hugetlb_unregister_node(node);
- break;
- }
- }
- }
- /*
- * hugetlb init time: register hstate attributes for all registered node
- * devices of nodes that have memory. All on-line nodes should have
- * registered their associated device by this time.
- */
- static void __init hugetlb_register_all_nodes(void)
- {
- int nid;
- for_each_online_node(nid)
- hugetlb_register_node(node_devices[nid]);
- }
- #else /* !CONFIG_NUMA */
- static struct hstate *kobj_to_node_hstate(struct kobject *kobj, int *nidp)
- {
- BUG();
- if (nidp)
- *nidp = -1;
- return NULL;
- }
- static void hugetlb_register_all_nodes(void) { }
- #endif
- void __init hugetlb_sysfs_init(void)
- {
- struct hstate *h;
- int err;
- hugepages_kobj = kobject_create_and_add("hugepages", mm_kobj);
- if (!hugepages_kobj)
- return;
- for_each_hstate(h) {
- err = hugetlb_sysfs_add_hstate(h, hugepages_kobj,
- hstate_kobjs, &hstate_attr_group);
- if (err)
- pr_err("HugeTLB: Unable to add hstate %s\n", h->name);
- }
- #ifdef CONFIG_NUMA
- hugetlb_sysfs_initialized = true;
- #endif
- hugetlb_register_all_nodes();
- }
|