Make alloc_page_buffers to create circular buffer list instead linear list. Remove unnecessary traversal in link_dev_buffers to create circular buffer list. Make nobh_write_begin and nobh_write_end to support circular buffer list traversal. Signed-off-by: Sean Fu <fxinrong@xxxxxxxxx> --- fs/buffer.c | 48 +++++++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/fs/buffer.c b/fs/buffer.c index 0736a6a..7e62c75 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -842,29 +842,36 @@ int remove_inode_buffers(struct inode *inode) struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size, bool retry) { - struct buffer_head *bh, *head; + struct buffer_head *bh, *head, *tail; gfp_t gfp = GFP_NOFS; long offset; if (retry) gfp |= __GFP_NOFAIL; - head = NULL; + head = tail = NULL; offset = PAGE_SIZE; while ((offset -= size) >= 0) { bh = alloc_buffer_head(gfp); if (!bh) goto no_grow; - bh->b_this_page = head; + if (unlikely(!head)) + tail = bh; + else + bh->b_this_page = head; + bh->b_blocknr = -1; head = bh; - bh->b_size = size; /* Link the buffer to its page */ set_bh_page(bh, page, offset); } + + if (tail) + tail->b_this_page = head; + return head; /* * In case anything failed, we just free everything we got. @@ -882,20 +889,6 @@ struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size, } EXPORT_SYMBOL_GPL(alloc_page_buffers); -static inline void -link_dev_buffers(struct page *page, struct buffer_head *head) -{ - struct buffer_head *bh, *tail; - - bh = head; - do { - tail = bh; - bh = bh->b_this_page; - } while (bh); - tail->b_this_page = head; - attach_page_buffers(page, head); -} - static sector_t blkdev_max_block(struct block_device *bdev, unsigned int size) { sector_t retval = ~((sector_t)0); @@ -993,7 +986,7 @@ grow_dev_page(struct block_device *bdev, sector_t block, * run under the page lock. */ spin_lock(&inode->i_mapping->private_lock); - link_dev_buffers(page, bh); + attach_page_buffers(page, bh); end_block = init_page_buffers(page, bdev, (sector_t)index << sizebits, size); spin_unlock(&inode->i_mapping->private_lock); @@ -1533,16 +1526,14 @@ EXPORT_SYMBOL(block_invalidatepage); void create_empty_buffers(struct page *page, unsigned long blocksize, unsigned long b_state) { - struct buffer_head *bh, *head, *tail; + struct buffer_head *bh, *head; head = alloc_page_buffers(page, blocksize, true); bh = head; do { bh->b_state |= b_state; - tail = bh; bh = bh->b_this_page; - } while (bh); - tail->b_this_page = head; + } while (bh != head); spin_lock(&page->mapping->private_lock); if (PageUptodate(page) || PageDirty(page)) { @@ -2655,11 +2646,14 @@ int nobh_write_begin(struct address_space *mapping, * any VM or truncate activity. Hence we don't need to care * for the buffer_head refcounts. */ - for (bh = head; bh; bh = bh->b_this_page) { + bh = head; + do { wait_on_buffer(bh); if (!buffer_uptodate(bh)) ret = -EIO; - } + bh = bh->b_this_page; + } while (bh != head); + if (ret) goto failed; } @@ -2717,11 +2711,11 @@ int nobh_write_end(struct file *file, struct address_space *mapping, unlock_page(page); put_page(page); - while (head) { + do { bh = head; head = head->b_this_page; free_buffer_head(bh); - } + } while (head != fsdata); return copied; } -- 2.6.2