From: Suparna Bhattacharya 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 _