diff options
-rw-r--r-- | fs/xfs/linux-2.6/xfs_aops.c | 144 |
1 files changed, 88 insertions, 56 deletions
diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index e99d04d3fe8..e998009c0f5 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -40,6 +40,7 @@ #include "xfs_rw.h" #include "xfs_iomap.h" #include <linux/mpage.h> +#include <linux/pagevec.h> #include <linux/writeback.h> STATIC void xfs_count_page_state(struct page *, int *, int *, int *); @@ -501,18 +502,13 @@ xfs_map_at_offset( */ STATIC unsigned int xfs_probe_unmapped_page( - struct address_space *mapping, - pgoff_t index, + struct page *page, unsigned int pg_offset) { - struct page *page; int ret = 0; - page = find_trylock_page(mapping, index); - if (!page) - return 0; if (PageWriteback(page)) - goto out; + return 0; if (page->mapping && PageDirty(page)) { if (page_has_buffers(page)) { @@ -530,8 +526,6 @@ xfs_probe_unmapped_page( ret = PAGE_CACHE_SIZE; } -out: - unlock_page(page); return ret; } @@ -542,59 +536,75 @@ xfs_probe_unmapped_cluster( struct buffer_head *bh, struct buffer_head *head) { - size_t len, total = 0; + struct pagevec pvec; pgoff_t tindex, tlast, tloff; - unsigned int pg_offset; - struct address_space *mapping = inode->i_mapping; + size_t total = 0; + int done = 0, i; /* First sum forwards in this page */ do { if (buffer_mapped(bh)) - break; + return total; total += bh->b_size; } while ((bh = bh->b_this_page) != head); - /* If we reached the end of the page, sum forwards in - * following pages. - */ - if (bh == head) { - tlast = i_size_read(inode) >> PAGE_CACHE_SHIFT; - /* Prune this back to avoid pathological behavior */ - tloff = min(tlast, startpage->index + 64); - for (tindex = startpage->index + 1; tindex < tloff; tindex++) { - len = xfs_probe_unmapped_page(mapping, tindex, - PAGE_CACHE_SIZE); - if (!len) - return total; + /* if we reached the end of the page, sum forwards in following pages */ + tlast = i_size_read(inode) >> PAGE_CACHE_SHIFT; + tindex = startpage->index + 1; + + /* Prune this back to avoid pathological behavior */ + tloff = min(tlast, startpage->index + 64); + + pagevec_init(&pvec, 0); + while (!done && tindex <= tloff) { + unsigned len = min_t(pgoff_t, PAGEVEC_SIZE, tlast - tindex + 1); + + if (!pagevec_lookup(&pvec, inode->i_mapping, tindex, len)) + break; + + for (i = 0; i < pagevec_count(&pvec); i++) { + struct page *page = pvec.pages[i]; + size_t pg_offset, len = 0; + + if (tindex == tlast) { + pg_offset = + i_size_read(inode) & (PAGE_CACHE_SIZE - 1); + if (!pg_offset) + break; + } else + pg_offset = PAGE_CACHE_SIZE; + + if (page->index == tindex && !TestSetPageLocked(page)) { + len = xfs_probe_unmapped_page(page, pg_offset); + unlock_page(page); + } + + if (!len) { + done = 1; + break; + } + total += len; } - if (tindex == tlast && - (pg_offset = i_size_read(inode) & (PAGE_CACHE_SIZE - 1))) { - total += xfs_probe_unmapped_page(mapping, - tindex, pg_offset); - } + + pagevec_release(&pvec); + cond_resched(); } + return total; } /* - * Probe for a given page (index) in the inode and test if it is suitable - * for writing as part of an unwritten or delayed allocate extent. - * Returns page locked and with an extra reference count if so, else NULL. + * Test if a given page is suitable for writing as part of an unwritten + * or delayed allocate extent. */ -STATIC struct page * -xfs_probe_delayed_page( - struct inode *inode, - pgoff_t index, +STATIC int +xfs_is_delayed_page( + struct page *page, unsigned int type) { - struct page *page; - - page = find_trylock_page(inode->i_mapping, index); - if (!page) - return NULL; if (PageWriteback(page)) - goto out; + return 0; if (page->mapping && page_has_buffers(page)) { struct buffer_head *bh, *head; @@ -611,12 +621,10 @@ xfs_probe_delayed_page( } while ((bh = bh->b_this_page) != head); if (acceptable) - return page; + return 1; } -out: - unlock_page(page); - return NULL; + return 0; } /* @@ -629,10 +637,10 @@ STATIC int xfs_convert_page( struct inode *inode, struct page *page, + loff_t tindex, xfs_iomap_t *iomapp, xfs_ioend_t **ioendp, struct writeback_control *wbc, - void *private, int startio, int all_bh) { @@ -644,6 +652,17 @@ xfs_convert_page( int len, page_dirty; int count = 0, done = 0, uptodate = 1; + if (page->index != tindex) + goto fail; + if (TestSetPageLocked(page)) + goto fail; + if (PageWriteback(page)) + goto fail_unlock_page; + if (page->mapping != inode->i_mapping) + goto fail_unlock_page; + if (!xfs_is_delayed_page(page, (*ioendp)->io_type)) + goto fail_unlock_page; + end_offset = (i_size_read(inode) & (PAGE_CACHE_SIZE - 1)); /* @@ -715,6 +734,10 @@ xfs_convert_page( } return done; + fail_unlock_page: + unlock_page(page); + fail: + return 1; } /* @@ -732,16 +755,25 @@ xfs_cluster_write( int all_bh, pgoff_t tlast) { - struct page *page; - unsigned int type = (*ioendp)->io_type; - int done; + struct pagevec pvec; + int done = 0, i; - for (done = 0; tindex <= tlast && !done; tindex++) { - page = xfs_probe_delayed_page(inode, tindex, type); - if (!page) + pagevec_init(&pvec, 0); + while (!done && tindex <= tlast) { + unsigned len = min_t(pgoff_t, PAGEVEC_SIZE, tlast - tindex + 1); + + if (!pagevec_lookup(&pvec, inode->i_mapping, tindex, len)) break; - done = xfs_convert_page(inode, page, iomapp, ioendp, - wbc, NULL, startio, all_bh); + + for (i = 0; i < pagevec_count(&pvec); i++) { + done = xfs_convert_page(inode, pvec.pages[i], tindex++, + iomapp, ioendp, wbc, startio, all_bh); + if (done) + break; + } + + pagevec_release(&pvec); + cond_resched(); } } |