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. diff -upN reference/fs/hugetlbfs/inode.c current/fs/hugetlbfs/inode.c --- reference/fs/hugetlbfs/inode.c 2004-04-07 14:54:29.000000000 -0700 +++ current/fs/hugetlbfs/inode.c 2004-04-29 10:39:24.000000000 -0700 @@ -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 -upN reference/include/linux/pagemap.h current/include/linux/pagemap.h --- reference/include/linux/pagemap.h 2004-04-29 10:39:23.000000000 -0700 +++ current/include/linux/pagemap.h 2004-04-29 10:39:24.000000000 -0700 @@ -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 -upN reference/include/linux/pagevec.h current/include/linux/pagevec.h --- reference/include/linux/pagevec.h 2002-12-09 18:46:25.000000000 -0800 +++ current/include/linux/pagevec.h 2004-04-29 10:39:24.000000000 -0700 @@ -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 -upN reference/mm/filemap.c current/mm/filemap.c --- reference/mm/filemap.c 2004-04-29 10:39:24.000000000 -0700 +++ current/mm/filemap.c 2004-04-29 10:39:24.000000000 -0700 @@ -572,9 +572,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; @@ -582,9 +585,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 -upN reference/mm/swap.c current/mm/swap.c --- reference/mm/swap.c 2004-04-07 14:54:38.000000000 -0700 +++ current/mm/swap.c 2004-04-29 10:39:24.000000000 -0700 @@ -351,12 +351,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 -upN reference/mm/truncate.c current/mm/truncate.c --- reference/mm/truncate.c 2004-04-07 14:54:38.000000000 -0700 +++ current/mm/truncate.c 2004-04-29 10:39:24.000000000 -0700 @@ -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); } @@ -201,17 +194,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)) @@ -252,14 +241,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