[PATCH] sem2mutex: mm/slab.c
[safe/jmp/linux-2.6] / mm / memory.c
index 107b619..7a11ddd 100644 (file)
@@ -333,9 +333,9 @@ static inline void add_mm_rss(struct mm_struct *mm, int file_rss, int anon_rss)
 }
 
 /*
- * This function is called to print an error when a pte in a
- * !VM_UNPAGED region is found pointing to an invalid pfn (which
- * is an error.
+ * This function is called to print an error when a bad pte
+ * is found. For example, we might have a PFN-mapped pte in
+ * a region that doesn't allow it.
  *
  * The calling function must still handle the error.
  */
@@ -349,6 +349,66 @@ void print_bad_pte(struct vm_area_struct *vma, pte_t pte, unsigned long vaddr)
        dump_stack();
 }
 
+static inline int is_cow_mapping(unsigned int flags)
+{
+       return (flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE;
+}
+
+/*
+ * This function gets the "struct page" associated with a pte.
+ *
+ * NOTE! Some mappings do not have "struct pages". A raw PFN mapping
+ * will have each page table entry just pointing to a raw page frame
+ * number, and as far as the VM layer is concerned, those do not have
+ * pages associated with them - even if the PFN might point to memory
+ * that otherwise is perfectly fine and has a "struct page".
+ *
+ * The way we recognize those mappings is through the rules set up
+ * by "remap_pfn_range()": the vma will have the VM_PFNMAP bit set,
+ * and the vm_pgoff will point to the first PFN mapped: thus every
+ * page that is a raw mapping will always honor the rule
+ *
+ *     pfn_of_page == vma->vm_pgoff + ((addr - vma->vm_start) >> PAGE_SHIFT)
+ *
+ * and if that isn't true, the page has been COW'ed (in which case it
+ * _does_ have a "struct page" associated with it even if it is in a
+ * VM_PFNMAP range).
+ */
+struct page *vm_normal_page(struct vm_area_struct *vma, unsigned long addr, pte_t pte)
+{
+       unsigned long pfn = pte_pfn(pte);
+
+       if (vma->vm_flags & VM_PFNMAP) {
+               unsigned long off = (addr - vma->vm_start) >> PAGE_SHIFT;
+               if (pfn == vma->vm_pgoff + off)
+                       return NULL;
+               if (!is_cow_mapping(vma->vm_flags))
+                       return NULL;
+       }
+
+       /*
+        * Add some anal sanity checks for now. Eventually,
+        * we should just do "return pfn_to_page(pfn)", but
+        * in the meantime we check that we get a valid pfn,
+        * and that the resulting page looks ok.
+        *
+        * Remove this test eventually!
+        */
+       if (unlikely(!pfn_valid(pfn))) {
+               print_bad_pte(vma, pte, addr);
+               return NULL;
+       }
+
+       /*
+        * NOTE! We still have PageReserved() pages in the page 
+        * tables. 
+        *
+        * The PAGE_ZERO() pages and various VDSO mappings can
+        * cause them to exist.
+        */
+       return pfn_to_page(pfn);
+}
+
 /*
  * copy one vm_area from one task to the other. Assumes the page tables
  * already present in the new task to be cleared in the whole range
@@ -363,7 +423,6 @@ copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
        unsigned long vm_flags = vma->vm_flags;
        pte_t pte = *src_pte;
        struct page *page;
-       unsigned long pfn;
 
        /* pte contains position in swap or file, so copy. */
        if (unlikely(!pte_present(pte))) {
@@ -381,28 +440,11 @@ copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
                goto out_set_pte;
        }
 
-       /* If the region is VM_UNPAGED, the mapping is not
-        * mapped via rmap - duplicate the pte as is.
-        */
-       if (vm_flags & VM_UNPAGED)
-               goto out_set_pte;
-
-       pfn = pte_pfn(pte);
-       /* If the pte points outside of valid memory but
-        * the region is not VM_UNPAGED, we have a problem.
-        */
-       if (unlikely(!pfn_valid(pfn))) {
-               print_bad_pte(vma, pte, addr);
-               goto out_set_pte; /* try to do something sane */
-       }
-
-       page = pfn_to_page(pfn);
-
        /*
         * If it's a COW mapping, write protect it both
         * in the parent and the child
         */
-       if ((vm_flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE) {
+       if (is_cow_mapping(vm_flags)) {
                ptep_set_wrprotect(src_mm, addr, src_pte);
                pte = *src_pte;
        }
@@ -414,9 +456,13 @@ copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
        if (vm_flags & VM_SHARED)
                pte = pte_mkclean(pte);
        pte = pte_mkold(pte);
-       get_page(page);
-       page_dup_rmap(page);
-       rss[!!PageAnon(page)]++;
+
+       page = vm_normal_page(vma, addr, pte);
+       if (page) {
+               get_page(page);
+               page_dup_rmap(page);
+               rss[!!PageAnon(page)]++;
+       }
 
 out_set_pte:
        set_pte_at(dst_mm, addr, dst_pte, pte);
@@ -528,7 +574,7 @@ int copy_page_range(struct mm_struct *dst_mm, struct mm_struct *src_mm,
         * readonly mappings. The tradeoff is that copy_page_range is more
         * efficient than faulting.
         */
-       if (!(vma->vm_flags & (VM_HUGETLB|VM_NONLINEAR|VM_UNPAGED))) {
+       if (!(vma->vm_flags & (VM_HUGETLB|VM_NONLINEAR|VM_PFNMAP|VM_INSERTPAGE))) {
                if (!vma->anon_vma)
                        return 0;
        }
@@ -568,17 +614,11 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb,
                        continue;
                }
                if (pte_present(ptent)) {
-                       struct page *page = NULL;
+                       struct page *page;
 
                        (*zap_work) -= PAGE_SIZE;
 
-                       if (!(vma->vm_flags & VM_UNPAGED)) {
-                               unsigned long pfn = pte_pfn(ptent);
-                               if (unlikely(!pfn_valid(pfn)))
-                                       print_bad_pte(vma, ptent, addr);
-                               else
-                                       page = pfn_to_page(pfn);
-                       }
+                       page = vm_normal_page(vma, addr, ptent);
                        if (unlikely(details) && page) {
                                /*
                                 * unmap_shared_mapping_pages() wants to
@@ -834,7 +874,7 @@ unsigned long zap_page_range(struct vm_area_struct *vma, unsigned long address,
 /*
  * Do a quick page-table lookup for a single page.
  */
-struct page *follow_page(struct mm_struct *mm, unsigned long address,
+struct page *follow_page(struct vm_area_struct *vma, unsigned long address,
                        unsigned int flags)
 {
        pgd_t *pgd;
@@ -842,8 +882,8 @@ struct page *follow_page(struct mm_struct *mm, unsigned long address,
        pmd_t *pmd;
        pte_t *ptep, pte;
        spinlock_t *ptl;
-       unsigned long pfn;
        struct page *page;
+       struct mm_struct *mm = vma->vm_mm;
 
        page = follow_huge_addr(mm, address, flags & FOLL_WRITE);
        if (!IS_ERR(page)) {
@@ -879,11 +919,10 @@ struct page *follow_page(struct mm_struct *mm, unsigned long address,
                goto unlock;
        if ((flags & FOLL_WRITE) && !pte_write(pte))
                goto unlock;
-       pfn = pte_pfn(pte);
-       if (!pfn_valid(pfn))
+       page = vm_normal_page(vma, address, pte);
+       if (unlikely(!page))
                goto unlock;
 
-       page = pfn_to_page(pfn);
        if (flags & FOLL_GET)
                get_page(page);
        if (flags & FOLL_TOUCH) {
@@ -956,8 +995,10 @@ int get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
                                return i ? : -EFAULT;
                        }
                        if (pages) {
-                               pages[i] = pte_page(*pte);
-                               get_page(pages[i]);
+                               struct page *page = vm_normal_page(gate_vma, start, *pte);
+                               pages[i] = page;
+                               if (page)
+                                       get_page(page);
                        }
                        pte_unmap(pte);
                        if (vmas)
@@ -968,7 +1009,7 @@ int get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
                        continue;
                }
 
-               if (!vma || (vma->vm_flags & VM_IO)
+               if (!vma || (vma->vm_flags & (VM_IO | VM_PFNMAP))
                                || !(vm_flags & vma->vm_flags))
                        return i ? : -EFAULT;
 
@@ -992,7 +1033,7 @@ int get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
                                foll_flags |= FOLL_WRITE;
 
                        cond_resched();
-                       while (!(page = follow_page(mm, start, foll_flags))) {
+                       while (!(page = follow_page(vma, start, foll_flags))) {
                                int ret;
                                ret = __handle_mm_fault(mm, vma, start,
                                                foll_flags & FOLL_WRITE);
@@ -1112,6 +1153,86 @@ int zeromap_page_range(struct vm_area_struct *vma,
        return err;
 }
 
+pte_t * fastcall get_locked_pte(struct mm_struct *mm, unsigned long addr, spinlock_t **ptl)
+{
+       pgd_t * pgd = pgd_offset(mm, addr);
+       pud_t * pud = pud_alloc(mm, pgd, addr);
+       if (pud) {
+               pmd_t * pmd = pmd_alloc(mm, pud, addr);
+               if (pmd)
+                       return pte_alloc_map_lock(mm, pmd, addr, ptl);
+       }
+       return NULL;
+}
+
+/*
+ * This is the old fallback for page remapping.
+ *
+ * For historical reasons, it only allows reserved pages. Only
+ * old drivers should use this, and they needed to mark their
+ * pages reserved for the old functions anyway.
+ */
+static int insert_page(struct mm_struct *mm, unsigned long addr, struct page *page, pgprot_t prot)
+{
+       int retval;
+       pte_t *pte;
+       spinlock_t *ptl;  
+
+       retval = -EINVAL;
+       if (PageAnon(page))
+               goto out;
+       retval = -ENOMEM;
+       flush_dcache_page(page);
+       pte = get_locked_pte(mm, addr, &ptl);
+       if (!pte)
+               goto out;
+       retval = -EBUSY;
+       if (!pte_none(*pte))
+               goto out_unlock;
+
+       /* Ok, finally just insert the thing.. */
+       get_page(page);
+       inc_mm_counter(mm, file_rss);
+       page_add_file_rmap(page);
+       set_pte_at(mm, addr, pte, mk_pte(page, prot));
+
+       retval = 0;
+out_unlock:
+       pte_unmap_unlock(pte, ptl);
+out:
+       return retval;
+}
+
+/*
+ * This allows drivers to insert individual pages they've allocated
+ * into a user vma.
+ *
+ * The page has to be a nice clean _individual_ kernel allocation.
+ * If you allocate a compound page, you need to have marked it as
+ * such (__GFP_COMP), or manually just split the page up yourself
+ * (which is mainly an issue of doing "set_page_count(page, 1)" for
+ * each sub-page, and then freeing them one by one when you free
+ * them rather than freeing it as a compound page).
+ *
+ * NOTE! Traditionally this was done with "remap_pfn_range()" which
+ * took an arbitrary page protection parameter. This doesn't allow
+ * that. Your vma protection will have to be set up correctly, which
+ * means that if you want a shared writable mapping, you'd better
+ * ask for a shared writable mapping!
+ *
+ * The page does not need to be reserved.
+ */
+int vm_insert_page(struct vm_area_struct *vma, unsigned long addr, struct page *page)
+{
+       if (addr < vma->vm_start || addr >= vma->vm_end)
+               return -EFAULT;
+       if (!page_count(page))
+               return -EINVAL;
+       vma->vm_flags |= VM_INSERTPAGE;
+       return insert_page(vma->vm_mm, addr, page, vma->vm_page_prot);
+}
+EXPORT_SYMBOL(vm_insert_page);
+
 /*
  * maps a range of physical memory into the requested pages. the old
  * mappings are removed. any references to nonexistent pages results
@@ -1196,11 +1317,21 @@ int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
         *      in 2.6 the LRU scan won't even find its pages, so this
         *      flag means no more than count its pages in reserved_vm,
         *      and omit it from core dump, even when VM_IO turned off.
-        *   VM_UNPAGED tells the core MM not to "manage" these pages
-         *     (e.g. refcount, mapcount, try to swap them out): in
-        *      particular, zap_pte_range does not try to free them.
+        *   VM_PFNMAP tells the core MM that the base pages are just
+        *      raw PFN mappings, and do not have a "struct page" associated
+        *      with them.
+        *
+        * There's a horrible special case to handle copy-on-write
+        * behaviour that some programs depend on. We mark the "original"
+        * un-COW'ed pages by matching them up with "vma->vm_pgoff".
         */
-       vma->vm_flags |= VM_IO | VM_RESERVED | VM_UNPAGED;
+       if (is_cow_mapping(vma->vm_flags)) {
+               if (addr != vma->vm_start || end != vma->vm_end)
+                       return -EINVAL;
+               vma->vm_pgoff = pfn;
+       }
+
+       vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP;
 
        BUG_ON(addr >= end);
        pfn -= addr >> PAGE_SHIFT;
@@ -1255,6 +1386,33 @@ static inline pte_t maybe_mkwrite(pte_t pte, struct vm_area_struct *vma)
        return pte;
 }
 
+static inline void cow_user_page(struct page *dst, struct page *src, unsigned long va)
+{
+       /*
+        * If the source page was a PFN mapping, we don't have
+        * a "struct page" for it. We do a best-effort copy by
+        * just copying from the original user address. If that
+        * fails, we just zero-fill it. Live with it.
+        */
+       if (unlikely(!src)) {
+               void *kaddr = kmap_atomic(dst, KM_USER0);
+               void __user *uaddr = (void __user *)(va & PAGE_MASK);
+
+               /*
+                * This really shouldn't fail, because the page is there
+                * in the page tables. But it might just be unreadable,
+                * in which case we just give up and fill the result with
+                * zeroes.
+                */
+               if (__copy_from_user_inatomic(kaddr, uaddr, PAGE_SIZE))
+                       memset(kaddr, 0, PAGE_SIZE);
+               kunmap_atomic(kaddr, KM_USER0);
+               return;
+               
+       }
+       copy_user_highpage(dst, src, va);
+}
+
 /*
  * This routine handles present pages, when users try to write
  * to a shared page. It is done by copying the page to a new address
@@ -1277,34 +1435,19 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
                unsigned long address, pte_t *page_table, pmd_t *pmd,
                spinlock_t *ptl, pte_t orig_pte)
 {
-       struct page *old_page, *src_page, *new_page;
-       unsigned long pfn = pte_pfn(orig_pte);
+       struct page *old_page, *new_page;
        pte_t entry;
        int ret = VM_FAULT_MINOR;
 
-       if (unlikely(!pfn_valid(pfn))) {
-               /*
-                * Page table corrupted: show pte and kill process.
-                * Or it's an attempt to COW an out-of-map VM_UNPAGED
-                * entry, which copy_user_highpage does not support.
-                */
-               print_bad_pte(vma, orig_pte, address);
-               ret = VM_FAULT_OOM;
-               goto unlock;
-       }
-       old_page = pfn_to_page(pfn);
-       src_page = old_page;
-
-       if (unlikely(vma->vm_flags & VM_UNPAGED)) {
-               old_page = NULL;
+       old_page = vm_normal_page(vma, address, orig_pte);
+       if (!old_page)
                goto gotten;
-       }
 
        if (PageAnon(old_page) && !TestSetPageLocked(old_page)) {
                int reuse = can_share_swap_page(old_page);
                unlock_page(old_page);
                if (reuse) {
-                       flush_cache_page(vma, address, pfn);
+                       flush_cache_page(vma, address, pte_pfn(orig_pte));
                        entry = pte_mkyoung(orig_pte);
                        entry = maybe_mkwrite(pte_mkdirty(entry), vma);
                        ptep_set_access_flags(vma, address, page_table, entry, 1);
@@ -1324,7 +1467,7 @@ gotten:
 
        if (unlikely(anon_vma_prepare(vma)))
                goto oom;
-       if (src_page == ZERO_PAGE(address)) {
+       if (old_page == ZERO_PAGE(address)) {
                new_page = alloc_zeroed_user_highpage(vma, address);
                if (!new_page)
                        goto oom;
@@ -1332,7 +1475,7 @@ gotten:
                new_page = alloc_page_vma(GFP_HIGHUSER, vma, address);
                if (!new_page)
                        goto oom;
-               copy_user_highpage(new_page, src_page, address);
+               cow_user_page(new_page, old_page, address);
        }
 
        /*
@@ -1348,14 +1491,14 @@ gotten:
                        }
                } else
                        inc_mm_counter(mm, anon_rss);
-               flush_cache_page(vma, address, pfn);
+               flush_cache_page(vma, address, pte_pfn(orig_pte));
                entry = mk_pte(new_page, vma->vm_page_prot);
                entry = maybe_mkwrite(pte_mkdirty(entry), vma);
                ptep_establish(vma, address, page_table, entry);
                update_mmu_cache(vma, address, entry);
                lazy_mmu_prot_update(entry);
                lru_cache_add_active(new_page);
-               page_add_anon_rmap(new_page, vma, address);
+               page_add_new_anon_rmap(new_page, vma, address);
 
                /* Free the old page.. */
                new_page = old_page;
@@ -1627,9 +1770,32 @@ out_big:
 out_busy:
        return -ETXTBSY;
 }
-
 EXPORT_SYMBOL(vmtruncate);
 
+int vmtruncate_range(struct inode *inode, loff_t offset, loff_t end)
+{
+       struct address_space *mapping = inode->i_mapping;
+
+       /*
+        * If the underlying filesystem is not going to provide
+        * a way to truncate a range of blocks (punch a hole) -
+        * we should return failure right now.
+        */
+       if (!inode->i_op || !inode->i_op->truncate_range)
+               return -ENOSYS;
+
+       mutex_lock(&inode->i_mutex);
+       down_write(&inode->i_alloc_sem);
+       unmap_mapping_range(mapping, offset, (end - offset), 1);
+       truncate_inode_pages_range(mapping, offset, end);
+       inode->i_op->truncate_range(inode, offset, end);
+       up_write(&inode->i_alloc_sem);
+       mutex_unlock(&inode->i_mutex);
+
+       return 0;
+}
+EXPORT_SYMBOL(vmtruncate_range);
+
 /* 
  * Primitive swap readahead code. We simply read an aligned block of
  * (1 << page_cluster) entries in the swap area. This method is chosen
@@ -1811,8 +1977,7 @@ static int do_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma,
                        goto release;
                inc_mm_counter(mm, anon_rss);
                lru_cache_add_active(page);
-               SetPageReferenced(page);
-               page_add_anon_rmap(page, vma, address);
+               page_add_new_anon_rmap(page, vma, address);
        } else {
                /* Map the ZERO_PAGE - vm_page_prot is readonly */
                page = ZERO_PAGE(address);
@@ -1868,6 +2033,7 @@ static int do_no_page(struct mm_struct *mm, struct vm_area_struct *vma,
        int anon = 0;
 
        pte_unmap(page_table);
+       BUG_ON(vma->vm_flags & VM_PFNMAP);
 
        if (vma->vm_file) {
                mapping = vma->vm_file->f_mapping;
@@ -1942,8 +2108,8 @@ retry:
                if (anon) {
                        inc_mm_counter(mm, anon_rss);
                        lru_cache_add_active(new_page);
-                       page_add_anon_rmap(new_page, vma, address);
-               } else if (!(vma->vm_flags & VM_UNPAGED)) {
+                       page_add_new_anon_rmap(new_page, vma, address);
+               } else {
                        inc_mm_counter(mm, file_rss);
                        page_add_file_rmap(new_page);
                }
@@ -2101,6 +2267,8 @@ int __handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,
        return handle_pte_fault(mm, vma, address, pte, pmd, write_access);
 }
 
+EXPORT_SYMBOL_GPL(__handle_mm_fault);
+
 #ifndef __PAGETABLE_PUD_FOLDED
 /*
  * Allocate page upper directory.
@@ -2120,6 +2288,12 @@ int __pud_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address)
        spin_unlock(&mm->page_table_lock);
        return 0;
 }
+#else
+/* Workaround for gcc 2.96 */
+int __pud_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address)
+{
+       return 0;
+}
 #endif /* __PAGETABLE_PUD_FOLDED */
 
 #ifndef __PAGETABLE_PMD_FOLDED
@@ -2148,6 +2322,12 @@ int __pmd_alloc(struct mm_struct *mm, pud_t *pud, unsigned long address)
        spin_unlock(&mm->page_table_lock);
        return 0;
 }
+#else
+/* Workaround for gcc 2.96 */
+int __pmd_alloc(struct mm_struct *mm, pud_t *pud, unsigned long address)
+{
+       return 0;
+}
 #endif /* __PAGETABLE_PMD_FOLDED */
 
 int make_pages_present(unsigned long addr, unsigned long end)