[PATCH] USB: console: fix oops
[pandora-kernel.git] / mm / hugetlb.c
index 27fad5d..832f676 100644 (file)
@@ -88,13 +88,26 @@ static struct page *dequeue_huge_page(struct vm_area_struct *vma,
        return page;
 }
 
+static void free_huge_page(struct page *page)
+{
+       BUG_ON(page_count(page));
+
+       INIT_LIST_HEAD(&page->lru);
+
+       spin_lock(&hugetlb_lock);
+       enqueue_huge_page(page);
+       spin_unlock(&hugetlb_lock);
+}
+
 static int alloc_fresh_huge_page(void)
 {
        static int nid = 0;
        struct page *page;
        page = alloc_pages_node(nid, GFP_HIGHUSER|__GFP_COMP|__GFP_NOWARN,
                                        HUGETLB_PAGE_ORDER);
-       nid = (nid + 1) % num_online_nodes();
+       nid = next_node(nid, node_online_map);
+       if (nid == MAX_NUMNODES)
+               nid = first_node(node_online_map);
        if (page) {
                page[1].lru.next = (void *)free_huge_page;      /* dtor */
                spin_lock(&hugetlb_lock);
@@ -107,18 +120,8 @@ static int alloc_fresh_huge_page(void)
        return 0;
 }
 
-void free_huge_page(struct page *page)
-{
-       BUG_ON(page_count(page));
-
-       INIT_LIST_HEAD(&page->lru);
-
-       spin_lock(&hugetlb_lock);
-       enqueue_huge_page(page);
-       spin_unlock(&hugetlb_lock);
-}
-
-struct page *alloc_huge_page(struct vm_area_struct *vma, unsigned long addr)
+static struct page *alloc_huge_page(struct vm_area_struct *vma,
+                                   unsigned long addr)
 {
        struct inode *inode = vma->vm_file->f_dentry->d_inode;
        struct page *page;
@@ -331,6 +334,7 @@ static unsigned long set_max_huge_pages(unsigned long count)
                return nr_huge_pages;
 
        spin_lock(&hugetlb_lock);
+       count = max(count, reserved_huge_pages);
        try_to_free_low(count);
        while (count < nr_huge_pages) {
                struct page *page = dequeue_huge_page(NULL, 0);
@@ -660,10 +664,10 @@ int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
                        struct page **pages, struct vm_area_struct **vmas,
                        unsigned long *position, int *length, int i)
 {
-       unsigned long vpfn, vaddr = *position;
+       unsigned long pfn_offset;
+       unsigned long vaddr = *position;
        int remainder = *length;
 
-       vpfn = vaddr/PAGE_SIZE;
        spin_lock(&mm->page_table_lock);
        while (vaddr < vma->vm_end && remainder) {
                pte_t *pte;
@@ -691,19 +695,29 @@ int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
                        break;
                }
 
+               pfn_offset = (vaddr & ~HPAGE_MASK) >> PAGE_SHIFT;
+               page = pte_page(*pte);
+same_page:
                if (pages) {
-                       page = &pte_page(*pte)[vpfn % (HPAGE_SIZE/PAGE_SIZE)];
                        get_page(page);
-                       pages[i] = page;
+                       pages[i] = page + pfn_offset;
                }
 
                if (vmas)
                        vmas[i] = vma;
 
                vaddr += PAGE_SIZE;
-               ++vpfn;
+               ++pfn_offset;
                --remainder;
                ++i;
+               if (vaddr < vma->vm_end && remainder &&
+                               pfn_offset < HPAGE_SIZE/PAGE_SIZE) {
+                       /*
+                        * We use pfn_offset to avoid touching the pageframes
+                        * of this compound page.
+                        */
+                       goto same_page;
+               }
        }
        spin_unlock(&mm->page_table_lock);
        *length = remainder;