From: Suparna Bhattacharya <suparna@in.ibm.com>

The AIO patches have a tricky problem with unlock_page(), described in the
next patch.

To fix it we need to change the page gang-lookup API so that it can return to
the caller the page index at which the next search should commence.

This patch make that change, and updates callers.


---

 25-akpm/fs/hugetlbfs/inode.c    |    5 +----
 25-akpm/include/linux/pagemap.h |    2 +-
 25-akpm/include/linux/pagevec.h |    2 +-
 25-akpm/mm/filemap.c            |   12 +++++++++---
 25-akpm/mm/swap.c               |    9 ++++++---
 25-akpm/mm/truncate.c           |   20 ++++----------------
 6 files changed, 22 insertions(+), 28 deletions(-)

diff -puN fs/hugetlbfs/inode.c~gang_lookup_next fs/hugetlbfs/inode.c
--- 25/fs/hugetlbfs/inode.c~gang_lookup_next	Thu Jan  8 15:26:42 2004
+++ 25-akpm/fs/hugetlbfs/inode.c	Thu Jan  8 15:26:42 2004
@@ -165,7 +165,7 @@ void truncate_hugepages(struct address_s
 	pagevec_init(&pvec, 0);
 	next = start;
 	while (1) {
-		if (!pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) {
+		if (!pagevec_lookup(&pvec, mapping, &next, PAGEVEC_SIZE)) {
 			if (next == start)
 				break;
 			next = start;
@@ -176,9 +176,6 @@ void truncate_hugepages(struct address_s
 			struct page *page = pvec.pages[i];
 
 			lock_page(page);
-			if (page->index > next)
-				next = page->index;
-			++next;
 			truncate_huge_page(page);
 			unlock_page(page);
 			hugetlb_put_quota(mapping);
diff -puN include/linux/pagemap.h~gang_lookup_next include/linux/pagemap.h
--- 25/include/linux/pagemap.h~gang_lookup_next	Thu Jan  8 15:26:42 2004
+++ 25-akpm/include/linux/pagemap.h	Thu Jan  8 15:26:42 2004
@@ -70,7 +70,7 @@ extern struct page * find_trylock_page(s
 extern struct page * find_or_create_page(struct address_space *mapping,
 				unsigned long index, unsigned int gfp_mask);
 extern unsigned int find_get_pages(struct address_space *mapping,
-				pgoff_t start, unsigned int nr_pages,
+				pgoff_t *next, unsigned int nr_pages,
 				struct page **pages);
 
 /*
diff -puN include/linux/pagevec.h~gang_lookup_next include/linux/pagevec.h
--- 25/include/linux/pagevec.h~gang_lookup_next	Thu Jan  8 15:26:42 2004
+++ 25-akpm/include/linux/pagevec.h	Thu Jan  8 15:26:42 2004
@@ -23,7 +23,7 @@ void __pagevec_lru_add(struct pagevec *p
 void __pagevec_lru_add_active(struct pagevec *pvec);
 void pagevec_strip(struct pagevec *pvec);
 unsigned int pagevec_lookup(struct pagevec *pvec, struct address_space *mapping,
-		pgoff_t start, unsigned int nr_pages);
+		pgoff_t *next, unsigned int nr_pages);
 
 static inline void pagevec_init(struct pagevec *pvec, int cold)
 {
diff -puN mm/filemap.c~gang_lookup_next mm/filemap.c
--- 25/mm/filemap.c~gang_lookup_next	Thu Jan  8 15:26:42 2004
+++ 25-akpm/mm/filemap.c	Thu Jan  8 15:26:42 2004
@@ -592,9 +592,12 @@ EXPORT_SYMBOL(find_or_create_page);
  * The search returns a group of mapping-contiguous pages with ascending
  * indexes.  There may be holes in the indices due to not-present pages.
  *
- * find_get_pages() returns the number of pages which were found.
+ * find_get_pages() returns the number of pages which were found
+ * and also atomically sets the next offset to continue looking up
+ * mapping contiguous pages from (useful when doing a range of
+ * pagevec lookups in chunks of PAGEVEC_SIZE).
  */
-unsigned int find_get_pages(struct address_space *mapping, pgoff_t start,
+unsigned int find_get_pages(struct address_space *mapping, pgoff_t *next,
 			    unsigned int nr_pages, struct page **pages)
 {
 	unsigned int i;
@@ -602,9 +605,12 @@ unsigned int find_get_pages(struct addre
 
 	spin_lock(&mapping->page_lock);
 	ret = radix_tree_gang_lookup(&mapping->page_tree,
-				(void **)pages, start, nr_pages);
+				(void **)pages, *next, nr_pages);
 	for (i = 0; i < ret; i++)
 		page_cache_get(pages[i]);
+	if (ret)
+		*next = pages[ret - 1]->index + 1;
+
 	spin_unlock(&mapping->page_lock);
 	return ret;
 }
diff -puN mm/swap.c~gang_lookup_next mm/swap.c
--- 25/mm/swap.c~gang_lookup_next	Thu Jan  8 15:26:42 2004
+++ 25-akpm/mm/swap.c	Thu Jan  8 15:26:42 2004
@@ -348,12 +348,15 @@ void pagevec_strip(struct pagevec *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.
  *
- * pagevec_lookup() returns the number of pages which were found.
+ * pagevec_lookup() returns the number of pages which were found
+ * and also atomically sets the next offset to continue looking up
+ * mapping contiguous pages from (useful when doing a range of
+ * pagevec lookups in chunks of PAGEVEC_SIZE).
  */
 unsigned int pagevec_lookup(struct pagevec *pvec, struct address_space *mapping,
-		pgoff_t start, unsigned int nr_pages)
+		pgoff_t *next, unsigned int nr_pages)
 {
-	pvec->nr = find_get_pages(mapping, start, nr_pages, pvec->pages);
+	pvec->nr = find_get_pages(mapping, next, nr_pages, pvec->pages);
 	return pagevec_count(pvec);
 }
 
diff -puN mm/truncate.c~gang_lookup_next mm/truncate.c
--- 25/mm/truncate.c~gang_lookup_next	Thu Jan  8 15:26:42 2004
+++ 25-akpm/mm/truncate.c	Thu Jan  8 15:26:42 2004
@@ -122,14 +122,10 @@ void truncate_inode_pages(struct address
 
 	pagevec_init(&pvec, 0);
 	next = start;
-	while (pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) {
+	while (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 > next)
-				next = page_index;
-			next++;
 			if (TestSetPageLocked(page))
 				continue;
 			if (PageWriteback(page)) {
@@ -155,7 +151,7 @@ void truncate_inode_pages(struct address
 
 	next = start;
 	for ( ; ; ) {
-		if (!pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) {
+		if (!pagevec_lookup(&pvec, mapping, &next, PAGEVEC_SIZE)) {
 			if (next == start)
 				break;
 			next = start;
@@ -166,9 +162,6 @@ void truncate_inode_pages(struct address
 
 			lock_page(page);
 			wait_on_page_writeback(page);
-			if (page->index > next)
-				next = page->index;
-			next++;
 			truncate_complete_page(mapping, page);
 			unlock_page(page);
 		}
@@ -209,17 +202,13 @@ unsigned long invalidate_mapping_pages(s
 
 	pagevec_init(&pvec, 0);
 	while (next <= end &&
-			pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) {
+			pagevec_lookup(&pvec, mapping, &next, PAGEVEC_SIZE)) {
 		for (i = 0; i < pagevec_count(&pvec); i++) {
 			struct page *page = pvec.pages[i];
 
 			if (TestSetPageLocked(page)) {
-				next++;
 				continue;
 			}
-			if (page->index > next)
-				next = page->index;
-			next++;
 			if (PageDirty(page) || PageWriteback(page))
 				goto unlock;
 			if (page_mapped(page))
@@ -258,14 +247,13 @@ void invalidate_inode_pages2(struct addr
 	int i;
 
 	pagevec_init(&pvec, 0);
-	while (pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) {
+	while (pagevec_lookup(&pvec, mapping, &next, PAGEVEC_SIZE)) {
 		for (i = 0; i < pagevec_count(&pvec); i++) {
 			struct page *page = pvec.pages[i];
 
 			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

_