mm: implement find_get_pages_range()
authorJan Kara <jack@suse.cz>
Wed, 6 Sep 2017 23:21:21 +0000 (16:21 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 7 Sep 2017 00:27:26 +0000 (17:27 -0700)
Implement a variant of find_get_pages() that stops iterating at given
index.  This may be substantial performance gain if the mapping is
sparse.  See following commit for details.  Furthermore lots of users of
this function (through pagevec_lookup()) actually want a range lookup
and all of them are currently open-coding this.

Also create corresponding pagevec_lookup_range() function.

Link: http://lkml.kernel.org/r/20170726114704.7626-4-jack@suse.cz
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
include/linux/pagemap.h
include/linux/pagevec.h
mm/filemap.c
mm/swap.c

index 249b1b5964c762bd7515d5045f771d5d26cd9cfe..5bbd6780f205103066bbf8b7d0d96959d5cbaed3 100644 (file)
@@ -353,8 +353,16 @@ struct page *find_lock_entry(struct address_space *mapping, pgoff_t offset);
 unsigned find_get_entries(struct address_space *mapping, pgoff_t start,
                          unsigned int nr_entries, struct page **entries,
                          pgoff_t *indices);
-unsigned find_get_pages(struct address_space *mapping, pgoff_t *start,
-                       unsigned int nr_pages, struct page **pages);
+unsigned find_get_pages_range(struct address_space *mapping, pgoff_t *start,
+                       pgoff_t end, unsigned int nr_pages,
+                       struct page **pages);
+static inline unsigned find_get_pages(struct address_space *mapping,
+                       pgoff_t *start, unsigned int nr_pages,
+                       struct page **pages)
+{
+       return find_get_pages_range(mapping, start, (pgoff_t)-1, nr_pages,
+                                   pages);
+}
 unsigned find_get_pages_contig(struct address_space *mapping, pgoff_t start,
                               unsigned int nr_pages, struct page **pages);
 unsigned find_get_pages_tag(struct address_space *mapping, pgoff_t *index,
index c395a5bb58b265cf3569441a820b79f52acb2fc0..7df056910437d1e4feb595fc2744532770b02b0f 100644 (file)
@@ -27,8 +27,17 @@ unsigned pagevec_lookup_entries(struct pagevec *pvec,
                                pgoff_t start, unsigned nr_entries,
                                pgoff_t *indices);
 void pagevec_remove_exceptionals(struct pagevec *pvec);
-unsigned pagevec_lookup(struct pagevec *pvec, struct address_space *mapping,
-               pgoff_t *start, unsigned nr_pages);
+unsigned pagevec_lookup_range(struct pagevec *pvec,
+                             struct address_space *mapping,
+                             pgoff_t *start, pgoff_t end, unsigned nr_pages);
+static inline unsigned pagevec_lookup(struct pagevec *pvec,
+                                     struct address_space *mapping,
+                                     pgoff_t *start, unsigned nr_pages)
+{
+       return pagevec_lookup_range(pvec, mapping, start, (pgoff_t)-1,
+                                   nr_pages);
+}
+
 unsigned pagevec_lookup_tag(struct pagevec *pvec,
                struct address_space *mapping, pgoff_t *index, int tag,
                unsigned nr_pages);
index ab9011408d81f116def93f45dd6da41081ddfbec..129883f160a7ba12e4b327dcf07a58bab4af0ce1 100644 (file)
@@ -1557,24 +1557,29 @@ export:
 }
 
 /**
- * find_get_pages - gang pagecache lookup
+ * find_get_pages_range - gang pagecache lookup
  * @mapping:   The address_space to search
  * @start:     The starting page index
+ * @end:       The final page index (inclusive)
  * @nr_pages:  The maximum number of pages
  * @pages:     Where the resulting pages are placed
  *
- * find_get_pages() will search for and return a group of up to
- * @nr_pages pages in the mapping.  The pages are placed at @pages.
- * find_get_pages() takes a reference against the returned pages.
+ * find_get_pages_range() will search for and return a group of up to @nr_pages
+ * pages in the mapping starting at index @start and up to index @end
+ * (inclusive).  The pages are placed at @pages.  find_get_pages_range() takes
+ * a reference against the returned pages.
  *
  * The search returns a group of mapping-contiguous pages with ascending
  * indexes.  There may be holes in the indices due to not-present pages.
  * We also update @start to index the next page for the traversal.
  *
- * find_get_pages() returns the number of pages which were found.
+ * find_get_pages_range() returns the number of pages which were found. If this
+ * number is smaller than @nr_pages, the end of specified range has been
+ * reached.
  */
-unsigned find_get_pages(struct address_space *mapping, pgoff_t *start,
-                           unsigned int nr_pages, struct page **pages)
+unsigned find_get_pages_range(struct address_space *mapping, pgoff_t *start,
+                             pgoff_t end, unsigned int nr_pages,
+                             struct page **pages)
 {
        struct radix_tree_iter iter;
        void **slot;
@@ -1586,6 +1591,9 @@ unsigned find_get_pages(struct address_space *mapping, pgoff_t *start,
        rcu_read_lock();
        radix_tree_for_each_slot(slot, &mapping->page_tree, &iter, *start) {
                struct page *head, *page;
+
+               if (iter.index > end)
+                       break;
 repeat:
                page = radix_tree_deref_slot(slot);
                if (unlikely(!page))
@@ -1621,15 +1629,25 @@ repeat:
                }
 
                pages[ret] = page;
-               if (++ret == nr_pages)
-                       break;
+               if (++ret == nr_pages) {
+                       *start = pages[ret - 1]->index + 1;
+                       goto out;
+               }
        }
 
+       /*
+        * We come here when there is no page beyond @end. We take care to not
+        * overflow the index @start 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)
+               *start = (pgoff_t)-1;
+       else
+               *start = end + 1;
+out:
        rcu_read_unlock();
 
-       if (ret)
-               *start = pages[ret - 1]->index + 1;
-
        return ret;
 }
 
index 4bffd1198ce50365352ed2d1f8ce6104cf5e95e2..e06e9aa2478ea13dac02a1d5206a8bc54fb4f4ee 100644 (file)
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -946,29 +946,35 @@ void pagevec_remove_exceptionals(struct pagevec *pvec)
 }
 
 /**
- * pagevec_lookup - gang pagecache lookup
+ * pagevec_lookup_range - gang pagecache lookup
  * @pvec:      Where the resulting pages are placed
  * @mapping:   The address_space to search
  * @start:     The starting page index
+ * @end:       The final page index
  * @nr_pages:  The maximum number of pages
  *
- * pagevec_lookup() will search for and return a group of up to @nr_pages pages
- * in the mapping.  The pages are placed in @pvec.  pagevec_lookup() takes a
+ * pagevec_lookup_range() will search for and return a group of up to @nr_pages
+ * pages in the mapping starting from index @start and upto index @end
+ * (inclusive).  The pages are placed in @pvec.  pagevec_lookup() takes a
  * reference against the pages in @pvec.
  *
  * The search returns a group of mapping-contiguous pages with ascending
  * indexes.  There may be holes in the indices due to not-present pages. We
  * also update @start to index the next page for the traversal.
  *
- * pagevec_lookup() returns the number of pages which were found.
+ * pagevec_lookup_range() returns the number of pages which were found. If this
+ * number is smaller than @nr_pages, the end of specified range has been
+ * reached.
  */
-unsigned pagevec_lookup(struct pagevec *pvec, struct address_space *mapping,
-               pgoff_t *start, unsigned nr_pages)
+unsigned pagevec_lookup_range(struct pagevec *pvec,
+               struct address_space *mapping, pgoff_t *start, pgoff_t end,
+               unsigned nr_pages)
 {
-       pvec->nr = find_get_pages(mapping, start, nr_pages, pvec->pages);
+       pvec->nr = find_get_pages_range(mapping, start, end, nr_pages,
+                                       pvec->pages);
        return pagevec_count(pvec);
 }
-EXPORT_SYMBOL(pagevec_lookup);
+EXPORT_SYMBOL(pagevec_lookup_range);
 
 unsigned pagevec_lookup_tag(struct pagevec *pvec, struct address_space *mapping,
                pgoff_t *index, int tag, unsigned nr_pages)