Merge branch 'linux-2.6'
[powerpc.git] / mm / filemap_xip.c
index 8c199f5..cbb3358 100644 (file)
 #include <asm/tlbflush.h>
 #include "filemap.h"
 
+/*
+ * We do use our own empty page to avoid interference with other users
+ * of ZERO_PAGE(), such as /dev/zero
+ */
+static struct page *__xip_sparse_page;
+
+static struct page *xip_sparse_page(void)
+{
+       if (!__xip_sparse_page) {
+               unsigned long zeroes = get_zeroed_page(GFP_HIGHUSER);
+               if (zeroes) {
+                       static DEFINE_SPINLOCK(xip_alloc_lock);
+                       spin_lock(&xip_alloc_lock);
+                       if (!__xip_sparse_page)
+                               __xip_sparse_page = virt_to_page(zeroes);
+                       else
+                               free_page(zeroes);
+                       spin_unlock(&xip_alloc_lock);
+               }
+       }
+       return __xip_sparse_page;
+}
+
 /*
  * This is a file read routine for execute in place files, and uses
  * the mapping->a_ops->get_xip_page() function for the actual low-level
@@ -162,7 +185,7 @@ EXPORT_SYMBOL_GPL(xip_file_sendfile);
  * xip_write
  *
  * This function walks all vmas of the address_space and unmaps the
- * ZERO_PAGE when found at pgoff. Should it go in rmap.c?
+ * __xip_sparse_page when found at pgoff.
  */
 static void
 __xip_unmap (struct address_space * mapping,
@@ -174,6 +197,12 @@ __xip_unmap (struct address_space * mapping,
        unsigned long address;
        pte_t *pte;
        pte_t pteval;
+       spinlock_t *ptl;
+       struct page *page;
+
+       page = __xip_sparse_page;
+       if (!page)
+               return;
 
        spin_lock(&mapping->i_mmap_lock);
        vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) {
@@ -181,19 +210,16 @@ __xip_unmap (struct address_space * mapping,
                address = vma->vm_start +
                        ((pgoff - vma->vm_pgoff) << PAGE_SHIFT);
                BUG_ON(address < vma->vm_start || address >= vma->vm_end);
-               /*
-                * We need the page_table_lock to protect us from page faults,
-                * munmap, fork, etc...
-                */
-               pte = page_check_address(ZERO_PAGE(address), mm,
-                                        address);
-               if (!IS_ERR(pte)) {
+               pte = page_check_address(page, mm, address, &ptl);
+               if (pte) {
                        /* Nuke the page table entry. */
                        flush_cache_page(vma, address, pte_pfn(*pte));
                        pteval = ptep_clear_flush(vma, address, pte);
+                       page_remove_rmap(page, vma);
+                       dec_mm_counter(mm, file_rss);
                        BUG_ON(pte_dirty(pteval));
-                       pte_unmap(pte);
-                       spin_unlock(&mm->page_table_lock);
+                       pte_unmap_unlock(pte, ptl);
+                       page_cache_release(page);
                }
        }
        spin_unlock(&mapping->i_mmap_lock);
@@ -222,16 +248,14 @@ xip_file_nopage(struct vm_area_struct * area,
                + area->vm_pgoff;
 
        size = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
-       if (pgoff >= size) {
-               return NULL;
-       }
+       if (pgoff >= size)
+               return NOPAGE_SIGBUS;
 
        page = mapping->a_ops->get_xip_page(mapping, pgoff*(PAGE_SIZE/512), 0);
-       if (!IS_ERR(page)) {
-               return page;
-       }
+       if (!IS_ERR(page))
+               goto out;
        if (PTR_ERR(page) != -ENODATA)
-               return NULL;
+               return NOPAGE_SIGBUS;
 
        /* sparse block */
        if ((area->vm_flags & (VM_WRITE | VM_MAYWRITE)) &&
@@ -241,14 +265,18 @@ xip_file_nopage(struct vm_area_struct * area,
                page = mapping->a_ops->get_xip_page (mapping,
                        pgoff*(PAGE_SIZE/512), 1);
                if (IS_ERR(page))
-                       return NULL;
+                       return NOPAGE_SIGBUS;
                /* unmap page at pgoff from all other vmas */
                __xip_unmap(mapping, pgoff);
        } else {
-               /* not shared and writable, use ZERO_PAGE() */
-               page = ZERO_PAGE(address);
+               /* not shared and writable, use xip_sparse_page() */
+               page = xip_sparse_page();
+               if (!page)
+                       return NOPAGE_OOM;
        }
 
+out:
+       page_cache_get(page);
        return page;
 }
 
@@ -271,7 +299,7 @@ __xip_file_write(struct file *filp, const char __user *buf,
                  size_t count, loff_t pos, loff_t *ppos)
 {
        struct address_space * mapping = filp->f_mapping;
-       struct address_space_operations *a_ops = mapping->a_ops;
+       const struct address_space_operations *a_ops = mapping->a_ops;
        struct inode    *inode = mapping->host;
        long            status = 0;
        struct page     *page;
@@ -336,7 +364,7 @@ __xip_file_write(struct file *filp, const char __user *buf,
        *ppos = pos;
        /*
         * No need to use i_size_read() here, the i_size
-        * cannot change under us because we hold i_sem.
+        * cannot change under us because we hold i_mutex.
         */
        if (pos > inode->i_size) {
                i_size_write(inode, pos);
@@ -356,7 +384,7 @@ xip_file_write(struct file *filp, const char __user *buf, size_t len,
        loff_t pos;
        ssize_t ret;
 
-       down(&inode->i_sem);
+       mutex_lock(&inode->i_mutex);
 
        if (!access_ok(VERIFY_READ, buf, len)) {
                ret=-EFAULT;
@@ -377,18 +405,18 @@ xip_file_write(struct file *filp, const char __user *buf, size_t len,
        if (count == 0)
                goto out_backing;
 
-       ret = remove_suid(filp->f_dentry);
+       ret = remove_suid(filp->f_path.dentry);
        if (ret)
                goto out_backing;
 
-       inode_update_time(inode, 1);
+       file_update_time(filp);
 
        ret = __xip_file_write (filp, buf, count, pos, ppos);
 
  out_backing:
        current->backing_dev_info = NULL;
  out_up:
-       up(&inode->i_sem);
+       mutex_unlock(&inode->i_mutex);
        return ret;
 }
 EXPORT_SYMBOL_GPL(xip_file_write);