[PATCH] readahead: ->prev_page can overrun the ahead window
[safe/jmp/linux-2.6] / mm / hugetlb.c
index b21d78c..20117a4 100644 (file)
@@ -18,6 +18,7 @@
 #include <asm/pgtable.h>
 
 #include <linux/hugetlb.h>
+#include "internal.h"
 
 const unsigned long hugetlb_zero = 0, hugetlb_infinity = ~0UL;
 static unsigned long nr_huge_pages, free_huge_pages;
@@ -64,7 +65,7 @@ static struct page *dequeue_huge_page(struct vm_area_struct *vma,
        return page;
 }
 
-static struct page *alloc_fresh_huge_page(void)
+static int alloc_fresh_huge_page(void)
 {
        static int nid = 0;
        struct page *page;
@@ -72,12 +73,15 @@ static struct page *alloc_fresh_huge_page(void)
                                        HUGETLB_PAGE_ORDER);
        nid = (nid + 1) % num_online_nodes();
        if (page) {
+               page[1].lru.next = (void *)free_huge_page;      /* dtor */
                spin_lock(&hugetlb_lock);
                nr_huge_pages++;
                nr_huge_pages_node[page_to_nid(page)]++;
                spin_unlock(&hugetlb_lock);
+               put_page(page); /* free it into the hugepage allocator */
+               return 1;
        }
-       return page;
+       return 0;
 }
 
 void free_huge_page(struct page *page)
@@ -85,7 +89,6 @@ void free_huge_page(struct page *page)
        BUG_ON(page_count(page));
 
        INIT_LIST_HEAD(&page->lru);
-       page[1].mapping = NULL;
 
        spin_lock(&hugetlb_lock);
        enqueue_huge_page(page);
@@ -104,17 +107,15 @@ struct page *alloc_huge_page(struct vm_area_struct *vma, unsigned long addr)
                return NULL;
        }
        spin_unlock(&hugetlb_lock);
-       set_page_count(page, 1);
-       page[1].mapping = (void *)free_huge_page;
+       set_page_refcounted(page);
        for (i = 0; i < (HPAGE_SIZE/PAGE_SIZE); ++i)
-               clear_highpage(&page[i]);
+               clear_user_highpage(&page[i], addr);
        return page;
 }
 
 static int __init hugetlb_init(void)
 {
        unsigned long i;
-       struct page *page;
 
        if (HPAGE_SHIFT == 0)
                return 0;
@@ -123,12 +124,8 @@ static int __init hugetlb_init(void)
                INIT_LIST_HEAD(&hugepage_freelists[i]);
 
        for (i = 0; i < max_huge_pages; ++i) {
-               page = alloc_fresh_huge_page();
-               if (!page)
+               if (!alloc_fresh_huge_page())
                        break;
-               spin_lock(&hugetlb_lock);
-               enqueue_huge_page(page);
-               spin_unlock(&hugetlb_lock);
        }
        max_huge_pages = free_huge_pages = nr_huge_pages = i;
        printk("Total HugeTLB memory allocated, %ld\n", free_huge_pages);
@@ -154,9 +151,9 @@ static void update_and_free_page(struct page *page)
                page[i].flags &= ~(1 << PG_locked | 1 << PG_error | 1 << PG_referenced |
                                1 << PG_dirty | 1 << PG_active | 1 << PG_reserved |
                                1 << PG_private | 1<< PG_writeback);
-               set_page_count(&page[i], 0);
        }
-       set_page_count(page, 1);
+       page[1].lru.next = NULL;
+       set_page_refcounted(page);
        __free_pages(page, HUGETLB_PAGE_ORDER);
 }
 
@@ -188,12 +185,8 @@ static inline void try_to_free_low(unsigned long count)
 static unsigned long set_max_huge_pages(unsigned long count)
 {
        while (count > nr_huge_pages) {
-               struct page *page = alloc_fresh_huge_page();
-               if (!page)
+               if (!alloc_fresh_huge_page())
                        return nr_huge_pages;
-               spin_lock(&hugetlb_lock);
-               enqueue_huge_page(page);
-               spin_unlock(&hugetlb_lock);
        }
        if (count >= nr_huge_pages)
                return nr_huge_pages;
@@ -391,12 +384,7 @@ static int hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma,
 
        if (!new_page) {
                page_cache_release(old_page);
-
-               /* Logically this is OOM, not a SIGBUS, but an OOM
-                * could cause the kernel to go killing other
-                * processes which won't help the hugepage situation
-                * at all (?) */
-               return VM_FAULT_SIGBUS;
+               return VM_FAULT_OOM;
        }
 
        spin_unlock(&mm->page_table_lock);
@@ -444,6 +432,7 @@ retry:
                page = alloc_huge_page(vma, address);
                if (!page) {
                        hugetlb_put_quota(mapping);
+                       ret = VM_FAULT_OOM;
                        goto out;
                }