[PATCH] Add nobh_writepage() support
Hi Andrew, Here is the patch to add nobh_wripage() support for the filesystems which uses nobh_prepare_write/nobh_commit_write(). Idea here is to reduce unnecessary bufferhead creation/attachment to the page through block_write_full_page(). nobh_wripage() tries to operate by directly creating bios, but it falls back to __block_write_full_page() if it can't make progress. Note that this is not really generic routine and can't be used for filesystems which uses page->Private for anything other than buffer heads. BTW, my next set of patches are to add ext3_writepages() support for writeback mode and to add "nobh" support for ext3 writeback mode - which are based on some of this work (These are already discussed on ext2-devel). And also, this needs some airtime in -mm tree before hitting mainline. Thanks, Badari Signed-off-by: Badari Pulavarty <[EMAIL PROTECTED]> diff -Narup -X dontdiff linux-2.6.10/fs/buffer.c linux-2.6.10.nobh/fs/buffer.c --- linux-2.6.10/fs/buffer.c2004-12-24 13:34:58.0 -0800 +++ linux-2.6.10.nobh/fs/buffer.c 2005-02-18 14:52:20.707345056 -0800 @@ -39,6 +39,7 @@ #include #include #include +#include static int fsync_buffers_list(spinlock_t *lock, struct list_head *list); static void invalidate_bh_lrus(void); @@ -2492,6 +2493,62 @@ int nobh_commit_write(struct file *file, EXPORT_SYMBOL(nobh_commit_write); /* + * nobh_writepage() - based on block_full_write_page() except + * that it tries to operate without attaching bufferheads to + * the page. + */ +int nobh_writepage(struct page *page, get_block_t *get_block, + struct writeback_control *wbc) +{ + struct inode * const inode = page->mapping->host; + loff_t i_size = i_size_read(inode); + const pgoff_t end_index = i_size >> PAGE_CACHE_SHIFT; + unsigned offset; + void *kaddr; + int ret; + + /* Is the page fully inside i_size? */ + if (page->index < end_index) { + goto out; + } + + /* Is the page fully outside i_size? (truncate in progress) */ + offset = i_size & (PAGE_CACHE_SIZE-1); + if (page->index >= end_index+1 || !offset) { + /* +* The page may have dirty, unmapped buffers. For example, +* they may have been added in ext3_writepage(). Make them +* freeable here, so the page does not leak. +*/ +#if 0 + /* Not really sure about this - do we need this ? */ + if (page->mapping->a_ops->invalidatepage) + page->mapping->a_ops->invalidatepage(page, offset); +#endif + unlock_page(page); + return 0; /* don't care */ + } + + /* +* The page straddles i_size. It must be zeroed out on each and every +* writepage invocation because it may be mmapped. "A file is mapped +* in multiples of the page size. For a file that is not a multiple of +* the page size, the remaining memory is zeroed when mapped, and +* writes to that region are not written out to the file." +*/ + kaddr = kmap_atomic(page, KM_USER0); + memset(kaddr + offset, 0, PAGE_CACHE_SIZE - offset); + flush_dcache_page(page); + kunmap_atomic(kaddr, KM_USER0); +out: + ret = mpage_writepage(page, get_block, wbc); + if (ret == -EAGAIN) + ret = __block_write_full_page(inode, page, get_block, wbc); + return ret; +} +EXPORT_SYMBOL(nobh_writepage); + +/* * This function assumes that ->prepare_write() uses nobh_prepare_write(). */ int nobh_truncate_page(struct address_space *mapping, loff_t from) diff -Narup -X dontdiff linux-2.6.10/fs/ext2/inode.c linux-2.6.10.nobh/fs/ext2/inode.c --- linux-2.6.10/fs/ext2/inode.c2004-12-24 13:33:51.0 -0800 +++ linux-2.6.10.nobh/fs/ext2/inode.c 2005-02-16 16:27:32.0 -0800 @@ -626,6 +626,12 @@ ext2_nobh_prepare_write(struct file *fil return nobh_prepare_write(page,from,to,ext2_get_block); } +static int ext2_nobh_writepage(struct page *page, + struct writeback_control *wbc) +{ + return nobh_writepage(page, ext2_get_block, wbc); +} + static sector_t ext2_bmap(struct address_space *mapping, sector_t block) { return generic_block_bmap(mapping,block,ext2_get_block); @@ -675,7 +681,7 @@ struct address_space_operations ext2_aop struct address_space_operations ext2_nobh_aops = { .readpage = ext2_readpage, .readpages = ext2_readpages, - .writepage = ext2_writepage, + .writepage = ext2_nobh_writepage, .sync_page = block_sync_page, .prepare_write = ext2_nobh_prepare_write, .commit_write = nobh_commit_write, diff -Narup -X dontdiff linux-2.6.10/fs/jfs/inode.c linux-2.6.10.nobh/fs/jfs/inode.c --- linux-2.6.10/fs/jfs/inode.c 2004-12-24
[PATCH] Add nobh_writepage() support
Hi Andrew, Here is the patch to add nobh_wripage() support for the filesystems which uses nobh_prepare_write/nobh_commit_write(). Idea here is to reduce unnecessary bufferhead creation/attachment to the page through block_write_full_page(). nobh_wripage() tries to operate by directly creating bios, but it falls back to __block_write_full_page() if it can't make progress. Note that this is not really generic routine and can't be used for filesystems which uses page-Private for anything other than buffer heads. BTW, my next set of patches are to add ext3_writepages() support for writeback mode and to add nobh support for ext3 writeback mode - which are based on some of this work (These are already discussed on ext2-devel). And also, this needs some airtime in -mm tree before hitting mainline. Thanks, Badari Signed-off-by: Badari Pulavarty [EMAIL PROTECTED] diff -Narup -X dontdiff linux-2.6.10/fs/buffer.c linux-2.6.10.nobh/fs/buffer.c --- linux-2.6.10/fs/buffer.c2004-12-24 13:34:58.0 -0800 +++ linux-2.6.10.nobh/fs/buffer.c 2005-02-18 14:52:20.707345056 -0800 @@ -39,6 +39,7 @@ #include linux/notifier.h #include linux/cpu.h #include linux/bitops.h +#include linux/mpage.h static int fsync_buffers_list(spinlock_t *lock, struct list_head *list); static void invalidate_bh_lrus(void); @@ -2492,6 +2493,62 @@ int nobh_commit_write(struct file *file, EXPORT_SYMBOL(nobh_commit_write); /* + * nobh_writepage() - based on block_full_write_page() except + * that it tries to operate without attaching bufferheads to + * the page. + */ +int nobh_writepage(struct page *page, get_block_t *get_block, + struct writeback_control *wbc) +{ + struct inode * const inode = page-mapping-host; + loff_t i_size = i_size_read(inode); + const pgoff_t end_index = i_size PAGE_CACHE_SHIFT; + unsigned offset; + void *kaddr; + int ret; + + /* Is the page fully inside i_size? */ + if (page-index end_index) { + goto out; + } + + /* Is the page fully outside i_size? (truncate in progress) */ + offset = i_size (PAGE_CACHE_SIZE-1); + if (page-index = end_index+1 || !offset) { + /* +* The page may have dirty, unmapped buffers. For example, +* they may have been added in ext3_writepage(). Make them +* freeable here, so the page does not leak. +*/ +#if 0 + /* Not really sure about this - do we need this ? */ + if (page-mapping-a_ops-invalidatepage) + page-mapping-a_ops-invalidatepage(page, offset); +#endif + unlock_page(page); + return 0; /* don't care */ + } + + /* +* The page straddles i_size. It must be zeroed out on each and every +* writepage invocation because it may be mmapped. A file is mapped +* in multiples of the page size. For a file that is not a multiple of +* the page size, the remaining memory is zeroed when mapped, and +* writes to that region are not written out to the file. +*/ + kaddr = kmap_atomic(page, KM_USER0); + memset(kaddr + offset, 0, PAGE_CACHE_SIZE - offset); + flush_dcache_page(page); + kunmap_atomic(kaddr, KM_USER0); +out: + ret = mpage_writepage(page, get_block, wbc); + if (ret == -EAGAIN) + ret = __block_write_full_page(inode, page, get_block, wbc); + return ret; +} +EXPORT_SYMBOL(nobh_writepage); + +/* * This function assumes that -prepare_write() uses nobh_prepare_write(). */ int nobh_truncate_page(struct address_space *mapping, loff_t from) diff -Narup -X dontdiff linux-2.6.10/fs/ext2/inode.c linux-2.6.10.nobh/fs/ext2/inode.c --- linux-2.6.10/fs/ext2/inode.c2004-12-24 13:33:51.0 -0800 +++ linux-2.6.10.nobh/fs/ext2/inode.c 2005-02-16 16:27:32.0 -0800 @@ -626,6 +626,12 @@ ext2_nobh_prepare_write(struct file *fil return nobh_prepare_write(page,from,to,ext2_get_block); } +static int ext2_nobh_writepage(struct page *page, + struct writeback_control *wbc) +{ + return nobh_writepage(page, ext2_get_block, wbc); +} + static sector_t ext2_bmap(struct address_space *mapping, sector_t block) { return generic_block_bmap(mapping,block,ext2_get_block); @@ -675,7 +681,7 @@ struct address_space_operations ext2_aop struct address_space_operations ext2_nobh_aops = { .readpage = ext2_readpage, .readpages = ext2_readpages, - .writepage = ext2_writepage, + .writepage = ext2_nobh_writepage, .sync_page = block_sync_page, .prepare_write = ext2_nobh_prepare_write, .commit_write = nobh_commit_write, diff -Narup -X dontdiff linux-2.6.10/fs/jfs/inode.c linux-2.6.10.nobh/fs/jfs/inode.c ---