mm: factor out page cache page freeing into a separate function
[sfrench/cifs-2.6.git] / mm / filemap.c
index 594d73fef8b43bae852f4f7ace1e8cfc46b23690..ecf7565ff435778788a7f4b89dea8865f7323700 100644 (file)
@@ -254,6 +254,23 @@ void __delete_from_page_cache(struct page *page, void *shadow)
                account_page_cleaned(page, mapping, inode_to_wb(mapping->host));
 }
 
+static void page_cache_free_page(struct address_space *mapping,
+                               struct page *page)
+{
+       void (*freepage)(struct page *);
+
+       freepage = mapping->a_ops->freepage;
+       if (freepage)
+               freepage(page);
+
+       if (PageTransHuge(page) && !PageHuge(page)) {
+               page_ref_sub(page, HPAGE_PMD_NR);
+               VM_BUG_ON_PAGE(page_count(page) <= 0, page);
+       } else {
+               put_page(page);
+       }
+}
+
 /**
  * delete_from_page_cache - delete page from page cache
  * @page: the page which the kernel is trying to remove from page cache
@@ -266,25 +283,13 @@ void delete_from_page_cache(struct page *page)
 {
        struct address_space *mapping = page_mapping(page);
        unsigned long flags;
-       void (*freepage)(struct page *);
 
        BUG_ON(!PageLocked(page));
-
-       freepage = mapping->a_ops->freepage;
-
        spin_lock_irqsave(&mapping->tree_lock, flags);
        __delete_from_page_cache(page, NULL);
        spin_unlock_irqrestore(&mapping->tree_lock, flags);
 
-       if (freepage)
-               freepage(page);
-
-       if (PageTransHuge(page) && !PageHuge(page)) {
-               page_ref_sub(page, HPAGE_PMD_NR);
-               VM_BUG_ON_PAGE(page_count(page) <= 0, page);
-       } else {
-               put_page(page);
-       }
+       page_cache_free_page(mapping, page);
 }
 EXPORT_SYMBOL(delete_from_page_cache);
 
@@ -420,19 +425,17 @@ static void __filemap_fdatawait_range(struct address_space *mapping,
                return;
 
        pagevec_init(&pvec, 0);
-       while ((index <= end) &&
-                       (nr_pages = pagevec_lookup_tag(&pvec, mapping, &index,
-                       PAGECACHE_TAG_WRITEBACK,
-                       min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1)) != 0) {
+       while (index <= end) {
                unsigned i;
 
+               nr_pages = pagevec_lookup_range_tag(&pvec, mapping, &index,
+                               end, PAGECACHE_TAG_WRITEBACK);
+               if (!nr_pages)
+                       break;
+
                for (i = 0; i < nr_pages; i++) {
                        struct page *page = pvec.pages[i];
 
-                       /* until radix tree lookup accepts end_index */
-                       if (page->index > end)
-                               continue;
-
                        wait_on_page_writeback(page);
                        ClearPageError(page);
                }
@@ -1754,9 +1757,10 @@ repeat:
 EXPORT_SYMBOL(find_get_pages_contig);
 
 /**
- * find_get_pages_tag - find and return pages that match @tag
+ * find_get_pages_range_tag - find and return pages in given range matching @tag
  * @mapping:   the address_space to search
  * @index:     the starting page index
+ * @end:       The final page index (inclusive)
  * @tag:       the tag index
  * @nr_pages:  the maximum number of pages
  * @pages:     where the resulting pages are placed
@@ -1764,8 +1768,9 @@ EXPORT_SYMBOL(find_get_pages_contig);
  * Like find_get_pages, except we only return pages which are tagged with
  * @tag.   We update @index to index the next page for the traversal.
  */
-unsigned find_get_pages_tag(struct address_space *mapping, pgoff_t *index,
-                       int tag, unsigned int nr_pages, struct page **pages)
+unsigned find_get_pages_range_tag(struct address_space *mapping, pgoff_t *index,
+                       pgoff_t end, int tag, unsigned int nr_pages,
+                       struct page **pages)
 {
        struct radix_tree_iter iter;
        void **slot;
@@ -1778,6 +1783,9 @@ unsigned find_get_pages_tag(struct address_space *mapping, pgoff_t *index,
        radix_tree_for_each_tagged(slot, &mapping->page_tree,
                                   &iter, *index, tag) {
                struct page *head, *page;
+
+               if (iter.index > end)
+                       break;
 repeat:
                page = radix_tree_deref_slot(slot);
                if (unlikely(!page))
@@ -1819,18 +1827,28 @@ repeat:
                }
 
                pages[ret] = page;
-               if (++ret == nr_pages)
-                       break;
+               if (++ret == nr_pages) {
+                       *index = pages[ret - 1]->index + 1;
+                       goto out;
+               }
        }
 
+       /*
+        * We come here when we got at @end. We take care to not overflow the
+        * index @index as it confuses some of the callers. This breaks the
+        * iteration when there is page at index -1 but that is already broken
+        * anyway.
+        */
+       if (end == (pgoff_t)-1)
+               *index = (pgoff_t)-1;
+       else
+               *index = end + 1;
+out:
        rcu_read_unlock();
 
-       if (ret)
-               *index = pages[ret - 1]->index + 1;
-
        return ret;
 }
-EXPORT_SYMBOL(find_get_pages_tag);
+EXPORT_SYMBOL(find_get_pages_range_tag);
 
 /**
  * find_get_entries_tag - find and return entries that match @tag