ext3_direct_io_get_blocks() is misinterpreting the return value from ext3_journal_extend(), and is consequently running out of buffer credits and going BUG on tremendously large direct-io writes. Fix that up. Also, I note that the really large direct-io writes can hold a transaction open for the entire duration, which can be minutes. This violates ext3's attempt to commit data at regular intervals. Fix that up by looking at the transaction state: if it's T_LOCKED, shut off the current handle so the pending commit can complete. Signed-off-by: Andrew Morton --- 25-akpm/fs/ext3/inode.c | 28 ++++++++++++++++++++++------ 1 files changed, 22 insertions(+), 6 deletions(-) diff -puN fs/ext3/inode.c~ext3-direct-io-credits-overflow-fix fs/ext3/inode.c --- 25/fs/ext3/inode.c~ext3-direct-io-credits-overflow-fix 2004-06-26 02:56:35.355673480 -0700 +++ 25-akpm/fs/ext3/inode.c 2004-06-26 03:12:37.918341712 -0700 @@ -818,26 +818,42 @@ ext3_direct_io_get_blocks(struct inode * handle_t *handle = journal_current_handle(); int ret = 0; - if (handle && handle->h_buffer_credits <= EXT3_RESERVE_TRANS_BLOCKS) { + if (!handle) + goto get_block; /* A read */ + + if (handle->h_transaction->t_state == T_LOCKED) { + /* + * Huge direct-io writes can hold off commits for long + * periods of time. Let this commit run. + */ + ext3_journal_stop(handle); + handle = ext3_journal_start(inode, DIO_CREDITS); + if (IS_ERR(handle)) + ret = PTR_ERR(handle); + goto get_block; + } + + if (handle->h_buffer_credits <= EXT3_RESERVE_TRANS_BLOCKS) { /* * Getting low on buffer credits... */ - if (!ext3_journal_extend(handle, DIO_CREDITS)) { + ret = ext3_journal_extend(handle, DIO_CREDITS); + if (ret > 0) { /* - * Couldn't extend the transaction. Start a new one + * Couldn't extend the transaction. Start a new one. */ ret = ext3_journal_restart(handle, DIO_CREDITS); } } + +get_block: 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); + bh_result->b_size = (1 << inode->i_blkbits); return ret; } - /* * `handle' can be NULL if create is zero */ _