vserver 1.9.5.x5
[linux-2.6.git] / arch / i386 / mm / hugetlbpage.c
index 6e2fa1f..ade8efb 100644 (file)
 #include <linux/err.h>
 #include <linux/sysctl.h>
 #include <asm/mman.h>
-#include <asm/pgalloc.h>
 #include <asm/tlb.h>
 #include <asm/tlbflush.h>
 
 static pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr)
 {
        pgd_t *pgd;
+       pud_t *pud;
        pmd_t *pmd = NULL;
 
        pgd = pgd_offset(mm, addr);
-       pmd = pmd_alloc(mm, pgd, addr);
+       pud = pud_alloc(mm, pgd, addr);
+       pmd = pmd_alloc(mm, pud, addr);
        return (pte_t *) pmd;
 }
 
 static pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr)
 {
        pgd_t *pgd;
+       pud_t *pud;
        pmd_t *pmd = NULL;
 
        pgd = pgd_offset(mm, addr);
-       pmd = pmd_offset(pgd, addr);
+       pud = pud_offset(pgd, addr);
+       pmd = pmd_offset(pud, addr);
        return (pte_t *) pmd;
 }
 
@@ -43,7 +46,8 @@ static void set_huge_pte(struct mm_struct *mm, struct vm_area_struct *vma, struc
 {
        pte_t entry;
 
-       mm->rss += (HPAGE_SIZE / PAGE_SIZE);
+       // mm->rss += (HPAGE_SIZE / PAGE_SIZE);
+       vx_rsspages_add(mm, HPAGE_SIZE / PAGE_SIZE);
        if (write_access) {
                entry =
                    pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
@@ -83,7 +87,8 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
                ptepage = pte_page(entry);
                get_page(ptepage);
                set_pte(dst_pte, entry);
-               dst->rss += (HPAGE_SIZE / PAGE_SIZE);
+               // dst->rss += (HPAGE_SIZE / PAGE_SIZE);
+               vx_rsspages_add(dst, HPAGE_SIZE / PAGE_SIZE);
                addr += HPAGE_SIZE;
        }
        return 0;
@@ -147,9 +152,6 @@ follow_huge_addr(struct mm_struct *mm, unsigned long address, int write)
        struct page *page;
        struct vm_area_struct *vma;
 
-       if (! mm->used_hugetlb)
-               return ERR_PTR(-EINVAL);
-
        vma = find_vma(mm, addr);
        if (!vma || !is_vm_hugetlb_page(vma))
                return ERR_PTR(-EINVAL);
@@ -222,7 +224,8 @@ void unmap_hugepage_range(struct vm_area_struct *vma,
                page = pte_page(pte);
                put_page(page);
        }
-       mm->rss -= (end - start) >> PAGE_SHIFT;
+       // mm->rss -= (end - start) >> PAGE_SHIFT;
+       vx_rsspages_sub(mm, (end - start) >> PAGE_SHIFT);
        flush_tlb_range(vma, start, end);
 }
 
@@ -245,8 +248,16 @@ int hugetlb_prefault(struct address_space *mapping, struct vm_area_struct *vma)
                        ret = -ENOMEM;
                        goto out;
                }
-               if (!pte_none(*pte))
-                       continue;
+
+               if (!pte_none(*pte)) {
+                       pmd_t *pmd = (pmd_t *) pte;
+
+                       page = pmd_page(*pmd);
+                       pmd_clear(pmd);
+                       mm->nr_ptes--;
+                       dec_page_state(nr_page_table_pages);
+                       page_cache_release(page);
+               }
 
                idx = ((addr - vma->vm_start) >> HPAGE_SHIFT)
                        + (vma->vm_pgoff >> (HPAGE_SHIFT - PAGE_SHIFT));
@@ -264,8 +275,9 @@ int hugetlb_prefault(struct address_space *mapping, struct vm_area_struct *vma)
                                goto out;
                        }
                        ret = add_to_page_cache(page, mapping, idx, GFP_ATOMIC);
-                       unlock_page(page);
-                       if (ret) {
+                       if (! ret) {
+                               unlock_page(page);
+                       } else {
                                hugetlb_put_quota(mapping);
                                free_huge_page(page);
                                goto out;
@@ -277,3 +289,143 @@ out:
        spin_unlock(&mm->page_table_lock);
        return ret;
 }
+
+/* x86_64 also uses this file */
+
+#ifdef HAVE_ARCH_HUGETLB_UNMAPPED_AREA
+static unsigned long hugetlb_get_unmapped_area_bottomup(struct file *file,
+               unsigned long addr, unsigned long len,
+               unsigned long pgoff, unsigned long flags)
+{
+       struct mm_struct *mm = current->mm;
+       struct vm_area_struct *vma;
+       unsigned long start_addr;
+
+       start_addr = mm->free_area_cache;
+
+full_search:
+       addr = ALIGN(start_addr, HPAGE_SIZE);
+
+       for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
+               /* At this point:  (!vma || addr < vma->vm_end). */
+               if (TASK_SIZE - len < addr) {
+                       /*
+                        * Start a new search - just in case we missed
+                        * some holes.
+                        */
+                       if (start_addr != TASK_UNMAPPED_BASE) {
+                               start_addr = TASK_UNMAPPED_BASE;
+                               goto full_search;
+                       }
+                       return -ENOMEM;
+               }
+               if (!vma || addr + len <= vma->vm_start) {
+                       mm->free_area_cache = addr + len;
+                       return addr;
+               }
+               addr = ALIGN(vma->vm_end, HPAGE_SIZE);
+       }
+}
+
+static unsigned long hugetlb_get_unmapped_area_topdown(struct file *file,
+               unsigned long addr0, unsigned long len,
+               unsigned long pgoff, unsigned long flags)
+{
+       struct mm_struct *mm = current->mm;
+       struct vm_area_struct *vma, *prev_vma;
+       unsigned long base = mm->mmap_base, addr = addr0;
+       int first_time = 1;
+
+       /* don't allow allocations above current base */
+       if (mm->free_area_cache > base)
+               mm->free_area_cache = base;
+
+try_again:
+       /* make sure it can fit in the remaining address space */
+       if (mm->free_area_cache < len)
+               goto fail;
+
+       /* either no address requested or cant fit in requested address hole */
+       addr = (mm->free_area_cache - len) & HPAGE_MASK;
+       do {
+               /*
+                * Lookup failure means no vma is above this address,
+                * i.e. return with success:
+                */
+               if (!(vma = find_vma_prev(mm, addr, &prev_vma)))
+                       return addr;
+
+               /*
+                * new region fits between prev_vma->vm_end and
+                * vma->vm_start, use it:
+                */
+               if (addr + len <= vma->vm_start &&
+                               (!prev_vma || (addr >= prev_vma->vm_end)))
+                       /* remember the address as a hint for next time */
+                       return (mm->free_area_cache = addr);
+               else
+                       /* pull free_area_cache down to the first hole */
+                       if (mm->free_area_cache == vma->vm_end)
+                               mm->free_area_cache = vma->vm_start;
+
+               /* try just below the current vma->vm_start */
+               addr = (vma->vm_start - len) & HPAGE_MASK;
+       } while (len <= vma->vm_start);
+
+fail:
+       /*
+        * if hint left us with no space for the requested
+        * mapping then try again:
+        */
+       if (first_time) {
+               mm->free_area_cache = base;
+               first_time = 0;
+               goto try_again;
+       }
+       /*
+        * A failed mmap() very likely causes application failure,
+        * so fall back to the bottom-up function here. This scenario
+        * can happen with large stack limits and large mmap()
+        * allocations.
+        */
+       mm->free_area_cache = TASK_UNMAPPED_BASE;
+       addr = hugetlb_get_unmapped_area_bottomup(file, addr0,
+                       len, pgoff, flags);
+
+       /*
+        * Restore the topdown base:
+        */
+       mm->free_area_cache = base;
+
+       return addr;
+}
+
+unsigned long
+hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
+               unsigned long len, unsigned long pgoff, unsigned long flags)
+{
+       struct mm_struct *mm = current->mm;
+       struct vm_area_struct *vma;
+
+       if (len & ~HPAGE_MASK)
+               return -EINVAL;
+       if (len > TASK_SIZE)
+               return -ENOMEM;
+
+       if (addr) {
+               addr = ALIGN(addr, HPAGE_SIZE);
+               vma = find_vma(mm, addr);
+               if (TASK_SIZE - len >= addr &&
+                   (!vma || addr + len <= vma->vm_start))
+                       return addr;
+       }
+       if (mm->get_unmapped_area == arch_get_unmapped_area)
+               return hugetlb_get_unmapped_area_bottomup(file, addr, len,
+                               pgoff, flags);
+       else
+               return hugetlb_get_unmapped_area_topdown(file, addr, len,
+                               pgoff, flags);
+}
+
+#endif /*HAVE_ARCH_HUGETLB_UNMAPPED_AREA*/
+