Merge to Fedora kernel-2.6.18-1.2239_FC5 patched with stable patch-2.6.18.2-vs2.0...
[linux-2.6.git] / mm / truncate.c
index 1dd32a2..6772c41 100644 (file)
 #include <linux/pagemap.h>
 #include <linux/pagevec.h>
 #include <linux/buffer_head.h> /* grr. try_to_release_page,
-                                  block_invalidatepage */
+                                  do_invalidatepage */
 
 
-static int do_invalidatepage(struct page *page, unsigned long offset)
-{
-       int (*invalidatepage)(struct page *, unsigned long);
-       invalidatepage = page->mapping->a_ops->invalidatepage;
-       if (invalidatepage == NULL)
-               invalidatepage = block_invalidatepage;
-       return (*invalidatepage)(page, offset);
-}
-
 static inline void truncate_partial_page(struct page *page, unsigned partial)
 {
        memclear_highpage_flush(page, partial, PAGE_CACHE_SIZE-partial);
@@ -64,6 +55,8 @@ truncate_complete_page(struct address_space *mapping, struct page *page)
  * be marked dirty at any time too.  So we re-check the dirtiness inside
  * ->tree_lock.  That provides exclusion against the __set_page_dirty
  * functions.
+ *
+ * Returns non-zero if the page was successfully invalidated.
  */
 static int
 invalidate_complete_page(struct address_space *mapping, struct page *page)
@@ -74,25 +67,33 @@ invalidate_complete_page(struct address_space *mapping, struct page *page)
        if (PagePrivate(page) && !try_to_release_page(page, 0))
                return 0;
 
-       spin_lock_irq(&mapping->tree_lock);
-       if (PageDirty(page)) {
-               spin_unlock_irq(&mapping->tree_lock);
-               return 0;
-       }
+       write_lock_irq(&mapping->tree_lock);
+       if (PageDirty(page))
+               goto failed;
+       if (page_count(page) != 2)      /* caller's ref + pagecache ref */
+               goto failed;
+
+       BUG_ON(PagePrivate(page));
        __remove_from_page_cache(page);
-       spin_unlock_irq(&mapping->tree_lock);
+       write_unlock_irq(&mapping->tree_lock);
        ClearPageUptodate(page);
        page_cache_release(page);       /* pagecache ref */
        return 1;
+failed:
+       write_unlock_irq(&mapping->tree_lock);
+       return 0;
 }
 
 /**
- * truncate_inode_pages - truncate *all* the pages from an offset
+ * truncate_inode_pages - truncate range of pages specified by start and
+ * end byte offsets
  * @mapping: mapping to truncate
  * @lstart: offset from which to truncate
+ * @lend: offset to which to truncate
  *
- * Truncate the page cache at a set offset, removing the pages that are beyond
- * that offset (and zeroing out partial pages).
+ * Truncate the page cache, removing the pages that are between
+ * specified offsets (and zeroing out partial page
+ * (if lstart is not page aligned)).
  *
  * Truncate takes two passes - the first pass is nonblocking.  It will not
  * block on page locks and it will not block on writeback.  The second pass
@@ -106,12 +107,12 @@ invalidate_complete_page(struct address_space *mapping, struct page *page)
  * We pass down the cache-hot hint to the page freeing code.  Even if the
  * mapping is large, it is probably the case that the final pages are the most
  * recently touched, and freeing happens in ascending file offset order.
- *
- * Called under (and serialised by) inode->i_sem.
  */
-void truncate_inode_pages(struct address_space *mapping, loff_t lstart)
+void truncate_inode_pages_range(struct address_space *mapping,
+                               loff_t lstart, loff_t lend)
 {
        const pgoff_t start = (lstart + PAGE_CACHE_SIZE-1) >> PAGE_CACHE_SHIFT;
+       pgoff_t end;
        const unsigned partial = lstart & (PAGE_CACHE_SIZE - 1);
        struct pagevec pvec;
        pgoff_t next;
@@ -120,13 +121,22 @@ void truncate_inode_pages(struct address_space *mapping, loff_t lstart)
        if (mapping->nrpages == 0)
                return;
 
+       BUG_ON((lend & (PAGE_CACHE_SIZE - 1)) != (PAGE_CACHE_SIZE - 1));
+       end = (lend >> PAGE_CACHE_SHIFT);
+
        pagevec_init(&pvec, 0);
        next = start;
-       while (pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) {
+       while (next <= end &&
+              pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) {
                for (i = 0; i < pagevec_count(&pvec); i++) {
                        struct page *page = pvec.pages[i];
                        pgoff_t page_index = page->index;
 
+                       if (page_index > end) {
+                               next = page_index;
+                               break;
+                       }
+
                        if (page_index > next)
                                next = page_index;
                        next++;
@@ -155,15 +165,22 @@ void truncate_inode_pages(struct address_space *mapping, loff_t lstart)
 
        next = start;
        for ( ; ; ) {
+               cond_resched();
                if (!pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) {
                        if (next == start)
                                break;
                        next = start;
                        continue;
                }
+               if (pvec.pages[0]->index > end) {
+                       pagevec_release(&pvec);
+                       break;
+               }
                for (i = 0; i < pagevec_count(&pvec); i++) {
                        struct page *page = pvec.pages[i];
 
+                       if (page->index > end)
+                               break;
                        lock_page(page);
                        wait_on_page_writeback(page);
                        if (page->index > next)
@@ -175,7 +192,19 @@ void truncate_inode_pages(struct address_space *mapping, loff_t lstart)
                pagevec_release(&pvec);
        }
 }
+EXPORT_SYMBOL(truncate_inode_pages_range);
 
+/**
+ * truncate_inode_pages - truncate *all* the pages from an offset
+ * @mapping: mapping to truncate
+ * @lstart: offset from which to truncate
+ *
+ * Called under (and serialised by) inode->i_mutex.
+ */
+void truncate_inode_pages(struct address_space *mapping, loff_t lstart)
+{
+       truncate_inode_pages_range(mapping, lstart, (loff_t)-1);
+}
 EXPORT_SYMBOL(truncate_inode_pages);
 
 /**
@@ -204,14 +233,24 @@ unsigned long invalidate_mapping_pages(struct address_space *mapping,
                        pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) {
                for (i = 0; i < pagevec_count(&pvec); i++) {
                        struct page *page = pvec.pages[i];
+                       pgoff_t index;
+                       int lock_failed;
 
-                       if (TestSetPageLocked(page)) {
-                               next++;
-                               continue;
-                       }
-                       if (page->index > next)
-                               next = page->index;
+                       lock_failed = TestSetPageLocked(page);
+
+                       /*
+                        * We really shouldn't be looking at the ->index of an
+                        * unlocked page.  But we're not allowed to lock these
+                        * pages.  So we rely upon nobody altering the ->index
+                        * of this (pinned-by-us) page.
+                        */
+                       index = page->index;
+                       if (index > next)
+                               next = index;
                        next++;
+                       if (lock_failed)
+                               continue;
+
                        if (PageDirty(page) || PageWriteback(page))
                                goto unlock;
                        if (page_mapped(page))
@@ -223,53 +262,140 @@ unlock:
                                break;
                }
                pagevec_release(&pvec);
-               cond_resched();
        }
        return ret;
 }
 
+EXPORT_SYMBOL_GPL(invalidate_mapping_pages);
+
 unsigned long invalidate_inode_pages(struct address_space *mapping)
 {
        return invalidate_mapping_pages(mapping, 0, ~0UL);
 }
-
 EXPORT_SYMBOL(invalidate_inode_pages);
 
+/*
+ * This is like invalidate_complete_page(), except it ignores the page's
+ * refcount.  We do this because invalidate_inode_pages2() needs stronger
+ * invalidation guarantees, and cannot afford to leave pages behind because
+ * shrink_list() has a temp ref on them, or because they're transiently sitting
+ * in the lru_cache_add() pagevecs.
+ */
+static int
+invalidate_complete_page2(struct address_space *mapping, struct page *page)
+{
+       if (page->mapping != mapping)
+               return 0;
+
+       if (PagePrivate(page) && !try_to_release_page(page, GFP_KERNEL))
+               return 0;
+
+       write_lock_irq(&mapping->tree_lock);
+       if (PageDirty(page))
+               goto failed;
+
+       BUG_ON(PagePrivate(page));
+       __remove_from_page_cache(page);
+       write_unlock_irq(&mapping->tree_lock);
+       ClearPageUptodate(page);
+       page_cache_release(page);       /* pagecache ref */
+       return 1;
+failed:
+       write_unlock_irq(&mapping->tree_lock);
+       return 0;
+}
+
 /**
- * invalidate_inode_pages2 - remove all unmapped pages from an address_space
- * @mapping - the address_space
+ * invalidate_inode_pages2_range - remove range of pages from an address_space
+ * @mapping: the address_space
+ * @start: the page offset 'from' which to invalidate
+ * @end: the page offset 'to' which to invalidate (inclusive)
  *
- * invalidate_inode_pages2() is like truncate_inode_pages(), except for the case
- * where the page is seen to be mapped into process pagetables.  In that case,
- * the page is marked clean but is left attached to its address_space.
+ * Any pages which are found to be mapped into pagetables are unmapped prior to
+ * invalidation.
  *
- * FIXME: invalidate_inode_pages2() is probably trivially livelockable.
+ * Returns -EIO if any pages could not be invalidated.
  */
-void invalidate_inode_pages2(struct address_space *mapping)
+int invalidate_inode_pages2_range(struct address_space *mapping,
+                                 pgoff_t start, pgoff_t end)
 {
        struct pagevec pvec;
-       pgoff_t next = 0;
+       pgoff_t next;
        int i;
+       int ret = 0;
+       int did_range_unmap = 0;
+       int wrapped = 0;
 
        pagevec_init(&pvec, 0);
-       while (pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) {
-               for (i = 0; i < pagevec_count(&pvec); i++) {
+       next = start;
+       while (next <= end && !ret && !wrapped &&
+               pagevec_lookup(&pvec, mapping, next,
+                       min(end - next, (pgoff_t)PAGEVEC_SIZE - 1) + 1)) {
+               for (i = 0; !ret && i < pagevec_count(&pvec); i++) {
                        struct page *page = pvec.pages[i];
+                       pgoff_t page_index;
+                       int was_dirty;
 
                        lock_page(page);
-                       if (page->mapping == mapping) { /* truncate race? */
-                               wait_on_page_writeback(page);
-                               next = page->index + 1;
-                               if (page_mapped(page))
-                                       clear_page_dirty(page);
-                               else
-                                       invalidate_complete_page(mapping, page);
+                       if (page->mapping != mapping) {
+                               unlock_page(page);
+                               continue;
+                       }
+                       page_index = page->index;
+                       next = page_index + 1;
+                       if (next == 0)
+                               wrapped = 1;
+                       if (page_index > end) {
+                               unlock_page(page);
+                               break;
+                       }
+                       wait_on_page_writeback(page);
+                       while (page_mapped(page)) {
+                               if (!did_range_unmap) {
+                                       /*
+                                        * Zap the rest of the file in one hit.
+                                        */
+                                       unmap_mapping_range(mapping,
+                                          (loff_t)page_index<<PAGE_CACHE_SHIFT,
+                                          (loff_t)(end - page_index + 1)
+                                                       << PAGE_CACHE_SHIFT,
+                                           0);
+                                       did_range_unmap = 1;
+                               } else {
+                                       /*
+                                        * Just zap this page
+                                        */
+                                       unmap_mapping_range(mapping,
+                                         (loff_t)page_index<<PAGE_CACHE_SHIFT,
+                                         PAGE_CACHE_SIZE, 0);
+                               }
+                       }
+                       was_dirty = test_clear_page_dirty(page);
+                       if (!invalidate_complete_page2(mapping, page)) {
+                               if (was_dirty)
+                                       set_page_dirty(page);
+                               ret = -EIO;
                        }
                        unlock_page(page);
                }
                pagevec_release(&pvec);
                cond_resched();
        }
+       return ret;
 }
+EXPORT_SYMBOL_GPL(invalidate_inode_pages2_range);
 
+/**
+ * invalidate_inode_pages2 - remove all pages from an address_space
+ * @mapping: the address_space
+ *
+ * Any pages which are found to be mapped into pagetables are unmapped prior to
+ * invalidation.
+ *
+ * Returns -EIO if any pages could not be invalidated.
+ */
+int invalidate_inode_pages2(struct address_space *mapping)
+{
+       return invalidate_inode_pages2_range(mapping, 0, -1);
+}
 EXPORT_SYMBOL_GPL(invalidate_inode_pages2);