--- /dev/null
+
+--- linux-2.4.18/fs/ext3/inode.c~ext3-o_direct-1-2.4.18-chaos Mon Jul 28 14:13:05 2003
++++ linux-2.4.18-alexey/fs/ext3/inode.c Mon Jul 28 15:50:14 2003
+@@ -27,6 +27,7 @@
+ #include <linux/ext3_jbd.h>
+ #include <linux/jbd.h>
+ #include <linux/locks.h>
++#include <linux/iobuf.h>
+ #include <linux/smp_lock.h>
+ #include <linux/highuid.h>
+ #include <linux/quotaops.h>
+@@ -733,9 +734,9 @@ err_out:
+ * 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];
+@@ -816,16 +817,18 @@ out:
+ 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 > ei->i_disksize)
+- ei->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;
+
+@@ -852,7 +855,38 @@ static int ext3_get_block(struct inode *
+ 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;
+ }
+
+@@ -870,7 +904,7 @@ struct buffer_head *ext3_getblk(handle_t
+ 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);
+@@ -1380,6 +1414,67 @@ static int ext3_releasepage(struct page
+ 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 */
+@@ -1390,6 +1485,7 @@ struct address_space_operations ext3_aop
+ 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 */
+ };
+
+ /*
+@@ -2987,7 +3083,7 @@ int ext3_prep_san_write(struct inode *in
+ /* 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;
+
+@@ -3047,7 +3143,7 @@ int ext3_map_inode_page(struct inode *in
+ if (blocks[i] != 0)
+ continue;
+
+- rc = ext3_get_block_handle(handle, inode, iblock, &dummy, 1);
++ rc = ext3_get_block_handle(handle, inode, iblock, &dummy, 1, 1);
+ if (rc) {
+ printk(KERN_INFO "ext3_map_inode_page: error reading "
+ "block %ld\n", iblock);
+
+_