mm: fix fault vs invalidate race for linear mappings
[deliverable/linux.git] / mm / filemap.c
index 5d5449f3d41c83810e002d7fc41cf0cb55b88536..462cda58a18e0f1863d73637c2165e722ff3f531 100644 (file)
@@ -1325,9 +1325,10 @@ struct page *filemap_nopage(struct vm_area_struct *area,
        unsigned long size, pgoff;
        int did_readaround = 0, majmin = VM_FAULT_MINOR;
 
+       BUG_ON(!(area->vm_flags & VM_CAN_INVALIDATE));
+
        pgoff = ((address-area->vm_start) >> PAGE_CACHE_SHIFT) + area->vm_pgoff;
 
-retry_all:
        size = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
        if (pgoff >= size)
                goto outside_data_content;
@@ -1349,7 +1350,7 @@ retry_all:
         * Do we have something in the page cache already?
         */
 retry_find:
-       page = find_get_page(mapping, pgoff);
+       page = find_lock_page(mapping, pgoff);
        if (!page) {
                unsigned long ra_pages;
 
@@ -1383,7 +1384,7 @@ retry_find:
                                start = pgoff - ra_pages / 2;
                        do_page_cache_readahead(mapping, file, start, ra_pages);
                }
-               page = find_get_page(mapping, pgoff);
+               page = find_lock_page(mapping, pgoff);
                if (!page)
                        goto no_cached_page;
        }
@@ -1392,13 +1393,19 @@ retry_find:
                ra->mmap_hit++;
 
        /*
-        * Ok, found a page in the page cache, now we need to check
-        * that it's up-to-date.
+        * We have a locked page in the page cache, now we need to check
+        * that it's up-to-date. If not, it is going to be due to an error.
         */
-       if (!PageUptodate(page))
+       if (unlikely(!PageUptodate(page)))
                goto page_not_uptodate;
 
-success:
+       /* Must recheck i_size under page lock */
+       size = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+       if (unlikely(pgoff >= size)) {
+               unlock_page(page);
+               goto outside_data_content;
+       }
+
        /*
         * Found the page and have a reference on it.
         */
@@ -1440,6 +1447,7 @@ no_cached_page:
        return NOPAGE_SIGBUS;
 
 page_not_uptodate:
+       /* IO error path */
        if (!did_readaround) {
                majmin = VM_FAULT_MAJOR;
                count_vm_event(PGMAJFAULT);
@@ -1451,37 +1459,15 @@ page_not_uptodate:
         * because there really aren't any performance issues here
         * and we need to check for errors.
         */
-       lock_page(page);
-
-       /* Somebody truncated the page on us? */
-       if (!page->mapping) {
-               unlock_page(page);
-               page_cache_release(page);
-               goto retry_all;
-       }
-
-       /* Somebody else successfully read it in? */
-       if (PageUptodate(page)) {
-               unlock_page(page);
-               goto success;
-       }
        ClearPageError(page);
        error = mapping->a_ops->readpage(file, page);
-       if (!error) {
-               wait_on_page_locked(page);
-               if (PageUptodate(page))
-                       goto success;
-       } else if (error == AOP_TRUNCATED_PAGE) {
-               page_cache_release(page);
+       page_cache_release(page);
+
+       if (!error || error == AOP_TRUNCATED_PAGE)
                goto retry_find;
-       }
 
-       /*
-        * Things didn't work out. Return zero to tell the
-        * mm layer so, possibly freeing the page cache page first.
-        */
+       /* Things didn't work out. Return zero to tell the mm layer so. */
        shrink_readahead_size_eio(file, ra);
-       page_cache_release(page);
        return NOPAGE_SIGBUS;
 }
 EXPORT_SYMBOL(filemap_nopage);
@@ -1674,6 +1660,7 @@ int generic_file_mmap(struct file * file, struct vm_area_struct * vma)
                return -ENOEXEC;
        file_accessed(file);
        vma->vm_ops = &generic_file_vm_ops;
+       vma->vm_flags |= VM_CAN_INVALIDATE;
        return 0;
 }
 
This page took 0.045655 seconds and 5 git commands to generate.