On Mon, 2007-04-02 at 14:09 +0200, Nick Piggin wrote: > Updated aops patchset against 2.6.21-rc5. > > http://www.kernel.org/pub/linux/kernel/people/npiggin/patches/new-aops/ > > Files/dirs are 2.6.21-rc5-new-aops* Here is the ext4 support for it. This is a simple port from ext3 code. Ran fsx without any problems :) BTW, I never clearly understood what exactly the problem these new interfaces are solving and how :( I can dig through the archives and try to figure out. Would you care to put a small description of the *actual* problem and how these new aops are needed (vs hacking the existing methods). Thanks, Badari --- fs/ext4/inode.c | 171 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 113 insertions(+), 58 deletions(-) Index: linux-2.6.21-rc5/fs/ext4/inode.c =================================================================== --- linux-2.6.21-rc5.orig/fs/ext4/inode.c 2007-03-25 15:56:23.000000000 -0700 +++ linux-2.6.21-rc5/fs/ext4/inode.c 2007-04-03 10:01:34.000000000 -0700 @@ -1154,23 +1154,30 @@ static int do_journal_get_write_access(h * This content is expected to be set to zeroes by block_prepare_write(). * 2006/10/14 SAW */ -static int ext4_prepare_failure(struct file *file, struct page *page, - unsigned from, unsigned to) +static int ext4_write_failure(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, + struct page *page, void *fsdata) { - struct address_space *mapping; struct buffer_head *bh, *head, *next; unsigned block_start, block_end; unsigned blocksize; + unsigned from, to; int ret; handle_t *handle = ext4_journal_current_handle(); - mapping = page->mapping; if (ext4_should_writeback_data(mapping->host)) { /* optimization: no constraints about data */ +skip_and_stop: + ret = ext4_journal_stop(handle); skip: - return ext4_journal_stop(handle); + unlock_page(page); + page_cache_release(page); + return ret; } + from = pos & (PAGE_CACHE_SIZE - 1); + to = from + len; + head = page_buffers(page); blocksize = head->b_size; for ( bh = head, block_start = 0; @@ -1192,7 +1199,7 @@ skip: ret = do_journal_get_write_access(handle, bh); if (ret) { ext4_journal_stop(handle); - return ret; + goto skip; } } /* @@ -1201,43 +1208,64 @@ skip: */ } if (block_start <= from) - goto skip; + goto skip_and_stop; /* commit allocated and zeroed buffers */ - return mapping->a_ops->commit_write(file, page, from, block_start); + return mapping->a_ops->write_end(file, mapping, pos, len, + block_start - from, page, fsdata); } -static int ext4_prepare_write(struct file *file, struct page *page, - unsigned from, unsigned to) +static int ext4_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned flags, + struct page **pagep, void **fsdata) { - struct inode *inode = page->mapping->host; - int ret, ret2; + struct inode *inode = mapping->host; int needed_blocks = ext4_writepage_trans_blocks(inode); + int ret, ret2; handle_t *handle; int retries = 0; + struct page *page; + pgoff_t index; + unsigned start, end; + + index = pos >> PAGE_CACHE_SHIFT; + start = pos * (PAGE_CACHE_SIZE - 1); + end = start + len; retry: + page = __grab_cache_page(mapping, index); + if (!page) + return -ENOMEM; + *pagep = page; + handle = ext4_journal_start(inode, needed_blocks); - if (IS_ERR(handle)) + if (IS_ERR(handle)) { + unlock_page(page); + page_cache_release(page); return PTR_ERR(handle); - if (test_opt(inode->i_sb, NOBH) && ext4_should_writeback_data(inode)) - ret = nobh_prepare_write(page, from, to, ext4_get_block); - else - ret = block_prepare_write(page, from, to, ext4_get_block); + } + ret = block_write_begin(file, mapping, pos, len, flags, pagep, fsdata, + ext4_get_block); if (ret) goto failure; if (ext4_should_journal_data(inode)) { ret = walk_page_buffers(handle, page_buffers(page), - from, to, NULL, do_journal_get_write_access); - if (ret) + start, end, NULL, do_journal_get_write_access); + if (ret) { /* fatal error, just put the handle and return */ ext4_journal_stop(handle); + unlock_page(page); + page_cache_release(page); + } } return ret; failure: - ret2 = ext4_prepare_failure(file, page, from, to); + ret2 = ext4_write_failure(file, mapping, pos, len, page, *fsdata); + /* trim off blocks (XXX: need better helpers than vmtruncate) */ + if (pos + len > inode->i_size) + vmtruncate(inode, inode->i_size); if (ret2 < 0) return ret2; if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)) @@ -1251,12 +1279,12 @@ int ext4_journal_dirty_data(handle_t *ha int err = jbd2_journal_dirty_data(handle, bh); if (err) ext4_journal_abort_handle(__FUNCTION__, __FUNCTION__, - bh, handle,err); + bh, handle, err); return err; } -/* For commit_write() in data=journal mode */ -static int commit_write_fn(handle_t *handle, struct buffer_head *bh) +/* For write_end() in data=journal mode */ +static int write_end_fn(handle_t *handle, struct buffer_head *bh) { if (!buffer_mapped(bh) || buffer_freed(bh)) return 0; @@ -1271,78 +1299,103 @@ static int commit_write_fn(handle_t *han * ext4 never places buffers on inode->i_mapping->private_list. metadata * buffers are managed internally. */ -static int ext4_ordered_commit_write(struct file *file, struct page *page, - unsigned from, unsigned to) +static int ext4_ordered_write_end(struct file *file, + struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *page, void *fsdata) { handle_t *handle = ext4_journal_current_handle(); - struct inode *inode = page->mapping->host; + struct inode *inode = file->f_mapping->host; + unsigned from, to; int ret = 0, ret2; + from = pos & (PAGE_CACHE_SIZE - 1); + to = from + len; + ret = walk_page_buffers(handle, page_buffers(page), from, to, NULL, ext4_journal_dirty_data); if (ret == 0) { /* - * generic_commit_write() will run mark_inode_dirty() if i_size + * block_write_end() will run mark_inode_dirty() if i_size * changes. So let's piggyback the i_disksize mark_inode_dirty * into that. */ loff_t new_i_size; - new_i_size = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to; + new_i_size = pos + copied; if (new_i_size > EXT4_I(inode)->i_disksize) EXT4_I(inode)->i_disksize = new_i_size; - ret = generic_commit_write(file, page, from, to); + copied = block_write_end(file, mapping, pos, len, copied, page, fsdata); + if (copied < 0) + ret = copied; } ret2 = ext4_journal_stop(handle); if (!ret) ret = ret2; - return ret; + return ret ? ret : copied; } -static int ext4_writeback_commit_write(struct file *file, struct page *page, - unsigned from, unsigned to) +static int ext4_writeback_write_end(struct file *file, + struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *page, void *fsdata) { handle_t *handle = ext4_journal_current_handle(); - struct inode *inode = page->mapping->host; + struct inode *inode = file->f_mapping->host; int ret = 0, ret2; loff_t new_i_size; - new_i_size = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to; + new_i_size = pos + copied; if (new_i_size > EXT4_I(inode)->i_disksize) EXT4_I(inode)->i_disksize = new_i_size; - if (test_opt(inode->i_sb, NOBH) && ext4_should_writeback_data(inode)) - ret = nobh_commit_write(file, page, from, to); - else - ret = generic_commit_write(file, page, from, to); + copied = block_write_end(file, mapping, pos, len, copied, page, fsdata); + if (copied < 0) + ret = copied; ret2 = ext4_journal_stop(handle); if (!ret) ret = ret2; - return ret; + return ret ? ret : copied; } -static int ext4_journalled_commit_write(struct file *file, - struct page *page, unsigned from, unsigned to) +static int ext4_journalled_write_end(struct file *file, + struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *page, void *fsdata) { handle_t *handle = ext4_journal_current_handle(); - struct inode *inode = page->mapping->host; + struct inode *inode = mapping->host; int ret = 0, ret2; int partial = 0; - loff_t pos; + unsigned from, to; - /* - * Here we duplicate the generic_commit_write() functionality - */ - pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to; + from = pos & (PAGE_CACHE_SIZE - 1); + to = from + len; + + if (copied < len) { + if (PageUptodate(page)) + copied = len; + else { + /* XXX: don't need to zero new buffers because we abort? */ + copied = 0; + if (!is_handle_aborted(handle)) + jbd2_journal_abort_handle(handle); + unlock_page(page); + page_cache_release(page); + goto out; + } + } ret = walk_page_buffers(handle, page_buffers(page), from, - to, &partial, commit_write_fn); + to, &partial, write_end_fn); if (!partial) SetPageUptodate(page); - if (pos > inode->i_size) - i_size_write(inode, pos); + unlock_page(page); + page_cache_release(page); + if (pos+copied > inode->i_size) + i_size_write(inode, pos+copied); EXT4_I(inode)->i_state |= EXT4_STATE_JDATA; if (inode->i_size > EXT4_I(inode)->i_disksize) { EXT4_I(inode)->i_disksize = inode->i_size; @@ -1350,10 +1403,12 @@ static int ext4_journalled_commit_write( if (!ret) ret = ret2; } + +out: ret2 = ext4_journal_stop(handle); if (!ret) ret = ret2; - return ret; + return ret ? ret : copied; } /* @@ -1611,7 +1666,7 @@ static int ext4_journalled_writepage(str PAGE_CACHE_SIZE, NULL, do_journal_get_write_access); err = walk_page_buffers(handle, page_buffers(page), 0, - PAGE_CACHE_SIZE, NULL, commit_write_fn); + PAGE_CACHE_SIZE, NULL, write_end_fn); if (ret == 0) ret = err; EXT4_I(inode)->i_state |= EXT4_STATE_JDATA; @@ -1771,8 +1826,8 @@ static const struct address_space_operat .readpages = ext4_readpages, .writepage = ext4_ordered_writepage, .sync_page = block_sync_page, - .prepare_write = ext4_prepare_write, - .commit_write = ext4_ordered_commit_write, + .write_begin = ext4_write_begin, + .write_end = ext4_ordered_write_end, .bmap = ext4_bmap, .invalidatepage = ext4_invalidatepage, .releasepage = ext4_releasepage, @@ -1785,8 +1840,8 @@ static const struct address_space_operat .readpages = ext4_readpages, .writepage = ext4_writeback_writepage, .sync_page = block_sync_page, - .prepare_write = ext4_prepare_write, - .commit_write = ext4_writeback_commit_write, + .write_begin = ext4_write_begin, + .write_end = ext4_writeback_write_end, .bmap = ext4_bmap, .invalidatepage = ext4_invalidatepage, .releasepage = ext4_releasepage, @@ -1799,8 +1854,8 @@ static const struct address_space_operat .readpages = ext4_readpages, .writepage = ext4_journalled_writepage, .sync_page = block_sync_page, - .prepare_write = ext4_prepare_write, - .commit_write = ext4_journalled_commit_write, + .write_begin = ext4_write_begin, + .write_end = ext4_journalled_write_end, .set_page_dirty = ext4_journalled_set_page_dirty, .bmap = ext4_bmap, .invalidatepage = ext4_invalidatepage, - To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html