]> err.no Git - linux-2.6/blobdiff - mm/hugetlb.c
[PATCH] mm: fix-up schedule_timeout() usage
[linux-2.6] / mm / hugetlb.c
index 4eb5ae3fbe10d3f34aca918c3325c62f76bdd8ee..901ac523a1c39fc17cc15d626ffb9cc8aa7f083d 100644 (file)
@@ -7,10 +7,14 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
-#include <linux/hugetlb.h>
 #include <linux/sysctl.h>
 #include <linux/highmem.h>
 #include <linux/nodemask.h>
+#include <linux/pagemap.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+#include <linux/hugetlb.h>
 
 const unsigned long hugetlb_zero = 0, hugetlb_infinity = ~0UL;
 static unsigned long nr_huge_pages, free_huge_pages;
@@ -249,6 +253,81 @@ struct vm_operations_struct hugetlb_vm_ops = {
        .nopage = hugetlb_nopage,
 };
 
+static pte_t make_huge_pte(struct vm_area_struct *vma, struct page *page)
+{
+       pte_t entry;
+
+       if (vma->vm_flags & VM_WRITE) {
+               entry =
+                   pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
+       } else {
+               entry = pte_wrprotect(mk_pte(page, vma->vm_page_prot));
+       }
+       entry = pte_mkyoung(entry);
+       entry = pte_mkhuge(entry);
+
+       return entry;
+}
+
+int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
+                           struct vm_area_struct *vma)
+{
+       pte_t *src_pte, *dst_pte, entry;
+       struct page *ptepage;
+       unsigned long addr = vma->vm_start;
+       unsigned long end = vma->vm_end;
+
+       while (addr < end) {
+               dst_pte = huge_pte_alloc(dst, addr);
+               if (!dst_pte)
+                       goto nomem;
+               src_pte = huge_pte_offset(src, addr);
+               BUG_ON(!src_pte || pte_none(*src_pte)); /* prefaulted */
+               entry = *src_pte;
+               ptepage = pte_page(entry);
+               get_page(ptepage);
+               add_mm_counter(dst, rss, HPAGE_SIZE / PAGE_SIZE);
+               set_huge_pte_at(dst, addr, dst_pte, entry);
+               addr += HPAGE_SIZE;
+       }
+       return 0;
+
+nomem:
+       return -ENOMEM;
+}
+
+void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start,
+                         unsigned long end)
+{
+       struct mm_struct *mm = vma->vm_mm;
+       unsigned long address;
+       pte_t *ptep;
+       pte_t pte;
+       struct page *page;
+
+       WARN_ON(!is_vm_hugetlb_page(vma));
+       BUG_ON(start & ~HPAGE_MASK);
+       BUG_ON(end & ~HPAGE_MASK);
+
+       for (address = start; address < end; address += HPAGE_SIZE) {
+               ptep = huge_pte_offset(mm, address);
+               if (! ptep)
+                       /* This can happen on truncate, or if an
+                        * mmap() is aborted due to an error before
+                        * the prefault */
+                       continue;
+
+               pte = huge_ptep_get_and_clear(mm, address, ptep);
+               if (pte_none(pte))
+                       continue;
+
+               page = pte_page(pte);
+               put_page(page);
+       }
+       add_mm_counter(mm, rss,  -((end - start) >> PAGE_SHIFT));
+       flush_tlb_range(vma, start, end);
+}
+
 void zap_hugepage_range(struct vm_area_struct *vma,
                        unsigned long start, unsigned long length)
 {
@@ -258,3 +337,106 @@ void zap_hugepage_range(struct vm_area_struct *vma,
        unmap_hugepage_range(vma, start, start + length);
        spin_unlock(&mm->page_table_lock);
 }
+
+int hugetlb_prefault(struct address_space *mapping, struct vm_area_struct *vma)
+{
+       struct mm_struct *mm = current->mm;
+       unsigned long addr;
+       int ret = 0;
+
+       WARN_ON(!is_vm_hugetlb_page(vma));
+       BUG_ON(vma->vm_start & ~HPAGE_MASK);
+       BUG_ON(vma->vm_end & ~HPAGE_MASK);
+
+       hugetlb_prefault_arch_hook(mm);
+
+       spin_lock(&mm->page_table_lock);
+       for (addr = vma->vm_start; addr < vma->vm_end; addr += HPAGE_SIZE) {
+               unsigned long idx;
+               pte_t *pte = huge_pte_alloc(mm, addr);
+               struct page *page;
+
+               if (!pte) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+
+               idx = ((addr - vma->vm_start) >> HPAGE_SHIFT)
+                       + (vma->vm_pgoff >> (HPAGE_SHIFT - PAGE_SHIFT));
+               page = find_get_page(mapping, idx);
+               if (!page) {
+                       /* charge the fs quota first */
+                       if (hugetlb_get_quota(mapping)) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
+                       page = alloc_huge_page();
+                       if (!page) {
+                               hugetlb_put_quota(mapping);
+                               ret = -ENOMEM;
+                               goto out;
+                       }
+                       ret = add_to_page_cache(page, mapping, idx, GFP_ATOMIC);
+                       if (! ret) {
+                               unlock_page(page);
+                       } else {
+                               hugetlb_put_quota(mapping);
+                               free_huge_page(page);
+                               goto out;
+                       }
+               }
+               add_mm_counter(mm, rss, HPAGE_SIZE / PAGE_SIZE);
+               set_huge_pte_at(mm, addr, pte, make_huge_pte(vma, page));
+       }
+out:
+       spin_unlock(&mm->page_table_lock);
+       return ret;
+}
+
+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;
+       int remainder = *length;
+
+       BUG_ON(!is_vm_hugetlb_page(vma));
+
+       vpfn = vaddr/PAGE_SIZE;
+       while (vaddr < vma->vm_end && remainder) {
+
+               if (pages) {
+                       pte_t *pte;
+                       struct page *page;
+
+                       /* Some archs (sparc64, sh*) have multiple
+                        * pte_ts to each hugepage.  We have to make
+                        * sure we get the first, for the page
+                        * indexing below to work. */
+                       pte = huge_pte_offset(mm, vaddr & HPAGE_MASK);
+
+                       /* hugetlb should be locked, and hence, prefaulted */
+                       WARN_ON(!pte || pte_none(*pte));
+
+                       page = &pte_page(*pte)[vpfn % (HPAGE_SIZE/PAGE_SIZE)];
+
+                       WARN_ON(!PageCompound(page));
+
+                       get_page(page);
+                       pages[i] = page;
+               }
+
+               if (vmas)
+                       vmas[i] = vma;
+
+               vaddr += PAGE_SIZE;
+               ++vpfn;
+               --remainder;
+               ++i;
+       }
+
+       *length = remainder;
+       *position = vaddr;
+
+       return i;
+}