Index: linux-2.4.20-rh/fs/ext3/inode.c =================================================================== --- linux-2.4.20-rh.orig/fs/ext3/inode.c 2003-09-04 18:01:41.000000000 +0800 +++ linux-2.4.20-rh/fs/ext3/inode.c 2003-09-04 18:18:54.000000000 +0800 @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -743,9 +744,9 @@ * The BKL may not be held on entry here. Be sure to take it early. */ -static int ext3_get_block_handle(handle_t *handle, struct inode *inode, - long iblock, - struct buffer_head *bh_result, int create) +static int +ext3_get_block_handle(handle_t *handle, struct inode *inode, long iblock, + struct buffer_head *bh_result, int create, int extend_disksize) { int err = -EIO; int offsets[4]; @@ -825,15 +826,18 @@ if (err) goto cleanup; - new_size = inode->i_size; - /* - * This is not racy against ext3_truncate's modification of i_disksize - * because VM/VFS ensures that the file cannot be extended while - * truncate is in progress. It is racy between multiple parallel - * instances of get_block, but we have the BKL. - */ - if (new_size > inode->u.ext3_i.i_disksize) - inode->u.ext3_i.i_disksize = new_size; + if (extend_disksize) { + /* + * This is not racy against ext3_truncate's modification of + * i_disksize because VM/VFS ensures that the file cannot be + * extended while truncate is in progress. It is racy between + * multiple parallel instances of get_block, but we have BKL. + */ + struct ext3_inode_info *ei = EXT3_I(inode); + new_size = inode->i_size; + if (new_size > ei->i_disksize) + ei->i_disksize = new_size; + } bh_result->b_state |= (1UL << BH_New); goto got_it; @@ -861,7 +865,38 @@ handle = ext3_journal_current_handle(); J_ASSERT(handle != 0); } - ret = ext3_get_block_handle(handle, inode, iblock, bh_result, create); + ret = ext3_get_block_handle(handle, inode, iblock, + bh_result, create, 1); + return ret; +} + +#define DIO_CREDITS (EXT3_RESERVE_TRANS_BLOCKS + 32) + +static int +ext3_direct_io_get_block(struct inode *inode, long iblock, + struct buffer_head *bh_result, int create) +{ + handle_t *handle = journal_current_handle(); + int ret = 0; + + lock_kernel(); + if (handle && handle->h_buffer_credits <= EXT3_RESERVE_TRANS_BLOCKS) { + /* + * Getting low on buffer credits... + */ + if (!ext3_journal_extend(handle, DIO_CREDITS)) { + /* + * Couldn't extend the transaction. Start a new one + */ + ret = ext3_journal_restart(handle, DIO_CREDITS); + } + } + if (ret == 0) + ret = ext3_get_block_handle(handle, inode, iblock, + bh_result, create, 0); + if (ret == 0) + bh_result->b_size = (1 << inode->i_blkbits); + unlock_kernel(); return ret; } @@ -879,7 +914,7 @@ dummy.b_state = 0; dummy.b_blocknr = -1000; buffer_trace_init(&dummy.b_history); - *errp = ext3_get_block_handle(handle, inode, block, &dummy, create); + *errp = ext3_get_block_handle(handle, inode, block, &dummy, create, 1); if (!*errp && buffer_mapped(&dummy)) { struct buffer_head *bh; bh = sb_getblk(inode->i_sb, dummy.b_blocknr); @@ -1387,6 +1422,67 @@ return journal_try_to_free_buffers(journal, page, wait); } +static int +ext3_direct_IO(int rw, struct inode *inode, struct kiobuf *iobuf, + unsigned long blocknr, int blocksize) +{ + struct ext3_inode_info *ei = EXT3_I(inode); + handle_t *handle = NULL; + int ret; + int orphan = 0; + loff_t offset = blocknr << inode->i_blkbits; /* ugh */ + ssize_t count = iobuf->length; /* ditto */ + + if (rw == WRITE) { + loff_t final_size = offset + count; + + lock_kernel(); + handle = ext3_journal_start(inode, DIO_CREDITS); + unlock_kernel(); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + goto out; + } + if (final_size > inode->i_size) { + lock_kernel(); + ret = ext3_orphan_add(handle, inode); + unlock_kernel(); + if (ret) + goto out_stop; + orphan = 1; + ei->i_disksize = inode->i_size; + } + } + + ret = generic_direct_IO(rw, inode, iobuf, blocknr, + blocksize, ext3_direct_io_get_block); + +out_stop: + if (handle) { + int err; + + lock_kernel(); + if (orphan) + ext3_orphan_del(handle, inode); + if (orphan && ret > 0) { + loff_t end = offset + ret; + if (end > inode->i_size) { + ei->i_disksize = end; + inode->i_size = end; + err = ext3_mark_inode_dirty(handle, inode); + if (!ret) + ret = err; + } + } + err = ext3_journal_stop(handle, inode); + if (ret == 0) + ret = err; + unlock_kernel(); + } +out: + return ret; + +} struct address_space_operations ext3_aops = { readpage: ext3_readpage, /* BKL not held. Don't need */ @@ -1397,6 +1493,7 @@ bmap: ext3_bmap, /* BKL held */ flushpage: ext3_flushpage, /* BKL not held. Don't need */ releasepage: ext3_releasepage, /* BKL not held. Don't need */ + direct_IO: ext3_direct_IO, /* BKL not held. Don't need */ }; /* @@ -2970,7 +3067,7 @@ /* alloc blocks one by one */ for (i = 0; i < nblocks; i++) { ret = ext3_get_block_handle(handle, inode, blocks[i], - &bh_tmp, 1); + &bh_tmp, 1, 1); if (ret) break; @@ -3030,7 +3127,7 @@ if (blocks[i] != 0) continue; - rc = ext3_get_block_handle(handle, inode, iblock, &bh, 1); + rc = ext3_get_block_handle(handle, inode, iblock, &bh, 1, 1); if (rc) { printk(KERN_INFO "ext3_map_inode_page: error %d " "allocating block %ld\n", rc, iblock);