]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - mm/hugetlb.c
hugetlb: support larger than MAX_ORDER
[linux-2.6-omap-h63xx.git] / mm / hugetlb.c
index 82378d44a0c5aded21a84686ac56052d588cea37..1a6fe87555b286d23c5d6ce770873f062ff1c3e3 100644 (file)
@@ -14,6 +14,8 @@
 #include <linux/mempolicy.h>
 #include <linux/cpuset.h>
 #include <linux/mutex.h>
+#include <linux/bootmem.h>
+#include <linux/sysfs.h>
 
 #include <asm/page.h>
 #include <asm/pgtable.h>
@@ -488,7 +490,7 @@ static void free_huge_page(struct page *page)
        INIT_LIST_HEAD(&page->lru);
 
        spin_lock(&hugetlb_lock);
-       if (h->surplus_huge_pages_node[nid]) {
+       if (h->surplus_huge_pages_node[nid] && huge_page_order(h) < MAX_ORDER) {
                update_and_free_page(h, page);
                h->surplus_huge_pages--;
                h->surplus_huge_pages_node[nid]--;
@@ -549,6 +551,9 @@ static struct page *alloc_fresh_huge_page_node(struct hstate *h, int nid)
 {
        struct page *page;
 
+       if (h->order >= MAX_ORDER)
+               return NULL;
+
        page = alloc_pages_node(nid,
                htlb_alloc_mask|__GFP_COMP|__GFP_THISNODE|
                                                __GFP_REPEAT|__GFP_NOWARN,
@@ -564,6 +569,27 @@ static struct page *alloc_fresh_huge_page_node(struct hstate *h, int nid)
        return page;
 }
 
+/*
+ * Use a helper variable to find the next node and then
+ * copy it back to hugetlb_next_nid afterwards:
+ * otherwise there's a window in which a racer might
+ * pass invalid nid MAX_NUMNODES to alloc_pages_node.
+ * But we don't need to use a spin_lock here: it really
+ * doesn't matter if occasionally a racer chooses the
+ * same nid as we do.  Move nid forward in the mask even
+ * if we just successfully allocated a hugepage so that
+ * the next caller gets hugepages on the next node.
+ */
+static int hstate_next_node(struct hstate *h)
+{
+       int next_nid;
+       next_nid = next_node(h->hugetlb_next_nid, node_online_map);
+       if (next_nid == MAX_NUMNODES)
+               next_nid = first_node(node_online_map);
+       h->hugetlb_next_nid = next_nid;
+       return next_nid;
+}
+
 static int alloc_fresh_huge_page(struct hstate *h)
 {
        struct page *page;
@@ -577,21 +603,7 @@ static int alloc_fresh_huge_page(struct hstate *h)
                page = alloc_fresh_huge_page_node(h, h->hugetlb_next_nid);
                if (page)
                        ret = 1;
-               /*
-                * Use a helper variable to find the next node and then
-                * copy it back to hugetlb_next_nid afterwards:
-                * otherwise there's a window in which a racer might
-                * pass invalid nid MAX_NUMNODES to alloc_pages_node.
-                * But we don't need to use a spin_lock here: it really
-                * doesn't matter if occasionally a racer chooses the
-                * same nid as we do.  Move nid forward in the mask even
-                * if we just successfully allocated a hugepage so that
-                * the next caller gets hugepages on the next node.
-                */
-               next_nid = next_node(h->hugetlb_next_nid, node_online_map);
-               if (next_nid == MAX_NUMNODES)
-                       next_nid = first_node(node_online_map);
-               h->hugetlb_next_nid = next_nid;
+               next_nid = hstate_next_node(h);
        } while (!page && h->hugetlb_next_nid != start_nid);
 
        if (ret)
@@ -608,6 +620,9 @@ static struct page *alloc_buddy_huge_page(struct hstate *h,
        struct page *page;
        unsigned int nid;
 
+       if (h->order >= MAX_ORDER)
+               return NULL;
+
        /*
         * Assume we will successfully allocate the surplus page to
         * prevent racing processes from causing the surplus to exceed
@@ -784,6 +799,10 @@ static void return_unused_surplus_pages(struct hstate *h,
        /* Uncommit the reservation */
        h->resv_huge_pages -= unused_resv_pages;
 
+       /* Cannot return gigantic pages currently */
+       if (h->order >= MAX_ORDER)
+               return;
+
        nr_pages = min(unused_resv_pages, h->surplus_huge_pages);
 
        while (remaining_iterations-- && nr_pages) {
@@ -905,6 +924,63 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma,
        return page;
 }
 
+static __initdata LIST_HEAD(huge_boot_pages);
+
+struct huge_bootmem_page {
+       struct list_head list;
+       struct hstate *hstate;
+};
+
+static int __init alloc_bootmem_huge_page(struct hstate *h)
+{
+       struct huge_bootmem_page *m;
+       int nr_nodes = nodes_weight(node_online_map);
+
+       while (nr_nodes) {
+               void *addr;
+
+               addr = __alloc_bootmem_node_nopanic(
+                               NODE_DATA(h->hugetlb_next_nid),
+                               huge_page_size(h), huge_page_size(h), 0);
+
+               if (addr) {
+                       /*
+                        * Use the beginning of the huge page to store the
+                        * huge_bootmem_page struct (until gather_bootmem
+                        * puts them into the mem_map).
+                        */
+                       m = addr;
+                       if (m)
+                               goto found;
+               }
+               hstate_next_node(h);
+               nr_nodes--;
+       }
+       return 0;
+
+found:
+       BUG_ON((unsigned long)virt_to_phys(m) & (huge_page_size(h) - 1));
+       /* Put them into a private list first because mem_map is not up yet */
+       list_add(&m->list, &huge_boot_pages);
+       m->hstate = h;
+       return 1;
+}
+
+/* Put bootmem huge pages into the standard lists after mem_map is up */
+static void __init gather_bootmem_prealloc(void)
+{
+       struct huge_bootmem_page *m;
+
+       list_for_each_entry(m, &huge_boot_pages, list) {
+               struct page *page = virt_to_page(m);
+               struct hstate *h = m->hstate;
+               __ClearPageReserved(page);
+               WARN_ON(page_count(page) != 1);
+               prep_compound_page(page, h->order);
+               prep_new_huge_page(h, page, page_to_nid(page));
+       }
+}
+
 static void __init hugetlb_init_one_hstate(struct hstate *h)
 {
        unsigned long i;
@@ -915,7 +991,10 @@ static void __init hugetlb_init_one_hstate(struct hstate *h)
        h->hugetlb_next_nid = first_node(node_online_map);
 
        for (i = 0; i < h->max_huge_pages; ++i) {
-               if (!alloc_fresh_huge_page(h))
+               if (h->order >= MAX_ORDER) {
+                       if (!alloc_bootmem_huge_page(h))
+                               break;
+               } else if (!alloc_fresh_huge_page(h))
                        break;
        }
        h->max_huge_pages = h->free_huge_pages = h->nr_huge_pages = i;
@@ -942,78 +1021,15 @@ static void __init report_hugepages(void)
        }
 }
 
-static int __init hugetlb_init(void)
-{
-       BUILD_BUG_ON(HPAGE_SHIFT == 0);
-
-       if (!size_to_hstate(HPAGE_SIZE)) {
-               hugetlb_add_hstate(HUGETLB_PAGE_ORDER);
-               parsed_hstate->max_huge_pages = default_hstate_max_huge_pages;
-       }
-       default_hstate_idx = size_to_hstate(HPAGE_SIZE) - hstates;
-
-       hugetlb_init_hstates();
-
-       report_hugepages();
-
-       return 0;
-}
-module_init(hugetlb_init);
-
-/* Should be called on processing a hugepagesz=... option */
-void __init hugetlb_add_hstate(unsigned order)
-{
-       struct hstate *h;
-       if (size_to_hstate(PAGE_SIZE << order)) {
-               printk(KERN_WARNING "hugepagesz= specified twice, ignoring\n");
-               return;
-       }
-       BUG_ON(max_hstate >= HUGE_MAX_HSTATE);
-       BUG_ON(order == 0);
-       h = &hstates[max_hstate++];
-       h->order = order;
-       h->mask = ~((1ULL << (order + PAGE_SHIFT)) - 1);
-       hugetlb_init_one_hstate(h);
-       parsed_hstate = h;
-}
-
-static int __init hugetlb_setup(char *s)
-{
-       unsigned long *mhp;
-
-       /*
-        * !max_hstate means we haven't parsed a hugepagesz= parameter yet,
-        * so this hugepages= parameter goes to the "default hstate".
-        */
-       if (!max_hstate)
-               mhp = &default_hstate_max_huge_pages;
-       else
-               mhp = &parsed_hstate->max_huge_pages;
-
-       if (sscanf(s, "%lu", mhp) <= 0)
-               *mhp = 0;
-
-       return 1;
-}
-__setup("hugepages=", hugetlb_setup);
-
-static unsigned int cpuset_mems_nr(unsigned int *array)
-{
-       int node;
-       unsigned int nr = 0;
-
-       for_each_node_mask(node, cpuset_current_mems_allowed)
-               nr += array[node];
-
-       return nr;
-}
-
 #ifdef CONFIG_SYSCTL
 #ifdef CONFIG_HIGHMEM
 static void try_to_free_low(struct hstate *h, unsigned long count)
 {
        int i;
 
+       if (h->order >= MAX_ORDER)
+               return;
+
        for (i = 0; i < MAX_NUMNODES; ++i) {
                struct page *page, *next;
                struct list_head *freel = &h->hugepage_freelists[i];
@@ -1040,6 +1056,9 @@ static unsigned long set_max_huge_pages(struct hstate *h, unsigned long count)
 {
        unsigned long min_count, ret;
 
+       if (h->order >= MAX_ORDER)
+               return h->max_huge_pages;
+
        /*
         * Increase the pool size
         * First take pages out of surplus state.  Then make up the
@@ -1105,6 +1124,229 @@ out:
        return ret;
 }
 
+#define HSTATE_ATTR_RO(_name) \
+       static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
+
+#define HSTATE_ATTR(_name) \
+       static struct kobj_attribute _name##_attr = \
+               __ATTR(_name, 0644, _name##_show, _name##_store)
+
+static struct kobject *hugepages_kobj;
+static struct kobject *hstate_kobjs[HUGE_MAX_HSTATE];
+
+static struct hstate *kobj_to_hstate(struct kobject *kobj)
+{
+       int i;
+       for (i = 0; i < HUGE_MAX_HSTATE; i++)
+               if (hstate_kobjs[i] == kobj)
+                       return &hstates[i];
+       BUG();
+       return NULL;
+}
+
+static ssize_t nr_hugepages_show(struct kobject *kobj,
+                                       struct kobj_attribute *attr, char *buf)
+{
+       struct hstate *h = kobj_to_hstate(kobj);
+       return sprintf(buf, "%lu\n", h->nr_huge_pages);
+}
+static ssize_t nr_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);
+
+       err = strict_strtoul(buf, 10, &input);
+       if (err)
+               return 0;
+
+       h->max_huge_pages = set_max_huge_pages(h, input);
+
+       return count;
+}
+HSTATE_ATTR(nr_hugepages);
+
+static ssize_t nr_overcommit_hugepages_show(struct kobject *kobj,
+                                       struct kobj_attribute *attr, char *buf)
+{
+       struct hstate *h = kobj_to_hstate(kobj);
+       return sprintf(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);
+
+       err = strict_strtoul(buf, 10, &input);
+       if (err)
+               return 0;
+
+       spin_lock(&hugetlb_lock);
+       h->nr_overcommit_huge_pages = input;
+       spin_unlock(&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 = kobj_to_hstate(kobj);
+       return sprintf(buf, "%lu\n", h->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);
+       return sprintf(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 = kobj_to_hstate(kobj);
+       return sprintf(buf, "%lu\n", h->surplus_huge_pages);
+}
+HSTATE_ATTR_RO(surplus_hugepages);
+
+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,
+       NULL,
+};
+
+static struct attribute_group hstate_attr_group = {
+       .attrs = hstate_attrs,
+};
+
+static int __init hugetlb_sysfs_add_hstate(struct hstate *h)
+{
+       int retval;
+
+       hstate_kobjs[h - hstates] = kobject_create_and_add(h->name,
+                                                       hugepages_kobj);
+       if (!hstate_kobjs[h - hstates])
+               return -ENOMEM;
+
+       retval = sysfs_create_group(hstate_kobjs[h - hstates],
+                                                       &hstate_attr_group);
+       if (retval)
+               kobject_put(hstate_kobjs[h - hstates]);
+
+       return retval;
+}
+
+static 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);
+               if (err)
+                       printk(KERN_ERR "Hugetlb: Unable to add hstate %s",
+                                                               h->name);
+       }
+}
+
+static void __exit hugetlb_exit(void)
+{
+       struct hstate *h;
+
+       for_each_hstate(h) {
+               kobject_put(hstate_kobjs[h - hstates]);
+       }
+
+       kobject_put(hugepages_kobj);
+}
+module_exit(hugetlb_exit);
+
+static int __init hugetlb_init(void)
+{
+       BUILD_BUG_ON(HPAGE_SHIFT == 0);
+
+       if (!size_to_hstate(HPAGE_SIZE)) {
+               hugetlb_add_hstate(HUGETLB_PAGE_ORDER);
+               parsed_hstate->max_huge_pages = default_hstate_max_huge_pages;
+       }
+       default_hstate_idx = size_to_hstate(HPAGE_SIZE) - hstates;
+
+       hugetlb_init_hstates();
+
+       gather_bootmem_prealloc();
+
+       report_hugepages();
+
+       hugetlb_sysfs_init();
+
+       return 0;
+}
+module_init(hugetlb_init);
+
+/* Should be called on processing a hugepagesz=... option */
+void __init hugetlb_add_hstate(unsigned order)
+{
+       struct hstate *h;
+       if (size_to_hstate(PAGE_SIZE << order)) {
+               printk(KERN_WARNING "hugepagesz= specified twice, ignoring\n");
+               return;
+       }
+       BUG_ON(max_hstate >= HUGE_MAX_HSTATE);
+       BUG_ON(order == 0);
+       h = &hstates[max_hstate++];
+       h->order = order;
+       h->mask = ~((1ULL << (order + PAGE_SHIFT)) - 1);
+       snprintf(h->name, HSTATE_NAME_LEN, "hugepages-%lukB",
+                                       huge_page_size(h)/1024);
+       hugetlb_init_one_hstate(h);
+       parsed_hstate = h;
+}
+
+static int __init hugetlb_setup(char *s)
+{
+       unsigned long *mhp;
+
+       /*
+        * !max_hstate means we haven't parsed a hugepagesz= parameter yet,
+        * so this hugepages= parameter goes to the "default hstate".
+        */
+       if (!max_hstate)
+               mhp = &default_hstate_max_huge_pages;
+       else
+               mhp = &parsed_hstate->max_huge_pages;
+
+       if (sscanf(s, "%lu", mhp) <= 0)
+               *mhp = 0;
+
+       return 1;
+}
+__setup("hugepages=", hugetlb_setup);
+
+static unsigned int cpuset_mems_nr(unsigned int *array)
+{
+       int node;
+       unsigned int nr = 0;
+
+       for_each_node_mask(node, cpuset_current_mems_allowed)
+               nr += array[node];
+
+       return nr;
+}
+
 int hugetlb_sysctl_handler(struct ctl_table *table, int write,
                           struct file *file, void __user *buffer,
                           size_t *length, loff_t *ppos)
@@ -1439,19 +1681,9 @@ void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start,
 void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start,
                          unsigned long end, struct page *ref_page)
 {
-       /*
-        * It is undesirable to test vma->vm_file as it should be non-null
-        * for valid hugetlb area. However, vm_file will be NULL in the error
-        * cleanup path of do_mmap_pgoff. When hugetlbfs ->mmap method fails,
-        * do_mmap_pgoff() nullifies vma->vm_file before calling this function
-        * to clean up. Since no pte has actually been setup, it is safe to
-        * do nothing in this case.
-        */
-       if (vma->vm_file) {
-               spin_lock(&vma->vm_file->f_mapping->i_mmap_lock);
-               __unmap_hugepage_range(vma, start, end, ref_page);
-               spin_unlock(&vma->vm_file->f_mapping->i_mmap_lock);
-       }
+       spin_lock(&vma->vm_file->f_mapping->i_mmap_lock);
+       __unmap_hugepage_range(vma, start, end, ref_page);
+       spin_unlock(&vma->vm_file->f_mapping->i_mmap_lock);
 }
 
 /*