forked from mirrors/linux
		
	hugetlb: support larger than MAX_ORDER
This is needed on x86-64 to handle GB pages in hugetlbfs, because it is not practical to enlarge MAX_ORDER to 1GB. Instead the 1GB pages are only allocated at boot using the bootmem allocator using the hugepages=... option. These 1G bootmem pages are never freed. In theory it would be possible to implement that with some complications, but since it would be a one-way street (>= MAX_ORDER pages cannot be allocated later) I decided not to currently. The >= MAX_ORDER code is not ifdef'ed per architecture. It is not very big and the ifdef uglyness seemed not be worth it. Known problems: /proc/meminfo and "free" do not display the memory allocated for gb pages in "Total". This is a little confusing for the user. Acked-by: Andrew Hastings <abh@cray.com> Signed-off-by: Andi Kleen <ak@suse.de> Signed-off-by: Nick Piggin <npiggin@suse.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
		
							parent
							
								
									01ad1c0827
								
							
						
					
					
						commit
						aa888a7497
					
				
					 1 changed files with 81 additions and 2 deletions
				
			
		
							
								
								
									
										83
									
								
								mm/hugetlb.c
									
									
									
									
									
								
							
							
						
						
									
										83
									
								
								mm/hugetlb.c
									
									
									
									
									
								
							| 
						 | 
					@ -14,6 +14,7 @@
 | 
				
			||||||
#include <linux/mempolicy.h>
 | 
					#include <linux/mempolicy.h>
 | 
				
			||||||
#include <linux/cpuset.h>
 | 
					#include <linux/cpuset.h>
 | 
				
			||||||
#include <linux/mutex.h>
 | 
					#include <linux/mutex.h>
 | 
				
			||||||
 | 
					#include <linux/bootmem.h>
 | 
				
			||||||
#include <linux/sysfs.h>
 | 
					#include <linux/sysfs.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <asm/page.h>
 | 
					#include <asm/page.h>
 | 
				
			||||||
| 
						 | 
					@ -489,7 +490,7 @@ static void free_huge_page(struct page *page)
 | 
				
			||||||
	INIT_LIST_HEAD(&page->lru);
 | 
						INIT_LIST_HEAD(&page->lru);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spin_lock(&hugetlb_lock);
 | 
						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);
 | 
							update_and_free_page(h, page);
 | 
				
			||||||
		h->surplus_huge_pages--;
 | 
							h->surplus_huge_pages--;
 | 
				
			||||||
		h->surplus_huge_pages_node[nid]--;
 | 
							h->surplus_huge_pages_node[nid]--;
 | 
				
			||||||
| 
						 | 
					@ -550,6 +551,9 @@ static struct page *alloc_fresh_huge_page_node(struct hstate *h, int nid)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct page *page;
 | 
						struct page *page;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (h->order >= MAX_ORDER)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	page = alloc_pages_node(nid,
 | 
						page = alloc_pages_node(nid,
 | 
				
			||||||
		htlb_alloc_mask|__GFP_COMP|__GFP_THISNODE|
 | 
							htlb_alloc_mask|__GFP_COMP|__GFP_THISNODE|
 | 
				
			||||||
						__GFP_REPEAT|__GFP_NOWARN,
 | 
											__GFP_REPEAT|__GFP_NOWARN,
 | 
				
			||||||
| 
						 | 
					@ -616,6 +620,9 @@ static struct page *alloc_buddy_huge_page(struct hstate *h,
 | 
				
			||||||
	struct page *page;
 | 
						struct page *page;
 | 
				
			||||||
	unsigned int nid;
 | 
						unsigned int nid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (h->order >= MAX_ORDER)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Assume we will successfully allocate the surplus page to
 | 
						 * Assume we will successfully allocate the surplus page to
 | 
				
			||||||
	 * prevent racing processes from causing the surplus to exceed
 | 
						 * prevent racing processes from causing the surplus to exceed
 | 
				
			||||||
| 
						 | 
					@ -792,6 +799,10 @@ static void return_unused_surplus_pages(struct hstate *h,
 | 
				
			||||||
	/* Uncommit the reservation */
 | 
						/* Uncommit the reservation */
 | 
				
			||||||
	h->resv_huge_pages -= unused_resv_pages;
 | 
						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);
 | 
						nr_pages = min(unused_resv_pages, h->surplus_huge_pages);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (remaining_iterations-- && nr_pages) {
 | 
						while (remaining_iterations-- && nr_pages) {
 | 
				
			||||||
| 
						 | 
					@ -913,6 +924,63 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma,
 | 
				
			||||||
	return page;
 | 
						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)
 | 
					static void __init hugetlb_init_one_hstate(struct hstate *h)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	unsigned long i;
 | 
						unsigned long i;
 | 
				
			||||||
| 
						 | 
					@ -923,7 +991,10 @@ static void __init hugetlb_init_one_hstate(struct hstate *h)
 | 
				
			||||||
	h->hugetlb_next_nid = first_node(node_online_map);
 | 
						h->hugetlb_next_nid = first_node(node_online_map);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = 0; i < h->max_huge_pages; ++i) {
 | 
						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;
 | 
								break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	h->max_huge_pages = h->free_huge_pages = h->nr_huge_pages = i;
 | 
						h->max_huge_pages = h->free_huge_pages = h->nr_huge_pages = i;
 | 
				
			||||||
| 
						 | 
					@ -956,6 +1027,9 @@ static void try_to_free_low(struct hstate *h, unsigned long count)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int i;
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (h->order >= MAX_ORDER)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = 0; i < MAX_NUMNODES; ++i) {
 | 
						for (i = 0; i < MAX_NUMNODES; ++i) {
 | 
				
			||||||
		struct page *page, *next;
 | 
							struct page *page, *next;
 | 
				
			||||||
		struct list_head *freel = &h->hugepage_freelists[i];
 | 
							struct list_head *freel = &h->hugepage_freelists[i];
 | 
				
			||||||
| 
						 | 
					@ -982,6 +1056,9 @@ static unsigned long set_max_huge_pages(struct hstate *h, unsigned long count)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	unsigned long min_count, ret;
 | 
						unsigned long min_count, ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (h->order >= MAX_ORDER)
 | 
				
			||||||
 | 
							return h->max_huge_pages;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Increase the pool size
 | 
						 * Increase the pool size
 | 
				
			||||||
	 * First take pages out of surplus state.  Then make up the
 | 
						 * First take pages out of surplus state.  Then make up the
 | 
				
			||||||
| 
						 | 
					@ -1210,6 +1287,8 @@ static int __init hugetlb_init(void)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	hugetlb_init_hstates();
 | 
						hugetlb_init_hstates();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gather_bootmem_prealloc();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	report_hugepages();
 | 
						report_hugepages();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	hugetlb_sysfs_init();
 | 
						hugetlb_sysfs_init();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue