From: Alex Tomas under some quite high load, jbd can hit J_ASSERT(journal->j_free > 1) in journal_next_log_block(). The cause is the following: journal_commit_transaction() { struct buffer_head *wbuf[64]; .... /* If there's no more to do, or if the descriptor is full, let the IO rip! */ if (bufs == ARRAY_SIZE(wbuf) || commit_transaction->t_buffers == NULL || space_left < sizeof(journal_block_tag_t) + 16) { so, the real limit isn't size of journal descriptor, but wbuf. __log_space_left() { /* * Be pessimistic here about the number of those free blocks which * might be required for log descriptor control blocks. */ #define MIN_LOG_RESERVED_BLOCKS 32 /* Allow for rounding errors */ left -= MIN_LOG_RESERVED_BLOCKS; so, jbd expects upto 32 blocks to be used for internal purpose. but with 64 blocks a descriptor limit we easily can break the limit. The fix allocates wbuf in journal_init_dev(). That's enough because we have only commit thread per filesystem. Signed-off-by: Alex Tomas Signed-off-by: Andrew Morton --- 25-akpm/fs/jbd/commit.c | 6 +++--- 25-akpm/fs/jbd/journal.c | 29 ++++++++++++++++++++++++++++- 25-akpm/include/linux/jbd.h | 6 ++++++ 3 files changed, 37 insertions(+), 4 deletions(-) diff -puN fs/jbd/commit.c~jbd-fix-against-journal-overflow fs/jbd/commit.c --- 25/fs/jbd/commit.c~jbd-fix-against-journal-overflow 2005-01-22 18:03:55.994689424 -0800 +++ 25-akpm/fs/jbd/commit.c 2005-01-22 18:03:56.003688056 -0800 @@ -103,7 +103,7 @@ void journal_commit_transaction(journal_ { transaction_t *commit_transaction; struct journal_head *jh, *new_jh, *descriptor; - struct buffer_head *wbuf[64]; + struct buffer_head **wbuf = journal->j_wbuf; int bufs; int flags; int err; @@ -271,7 +271,7 @@ write_out_data: BUFFER_TRACE(bh, "start journal writeout"); get_bh(bh); wbuf[bufs++] = bh; - if (bufs == ARRAY_SIZE(wbuf)) { + if (bufs == journal->j_wbufsize) { jbd_debug(2, "submit %d writes\n", bufs); spin_unlock(&journal->j_list_lock); @@ -487,7 +487,7 @@ write_out_data: /* If there's no more to do, or if the descriptor is full, let the IO rip! */ - if (bufs == ARRAY_SIZE(wbuf) || + if (bufs == journal->j_wbufsize || commit_transaction->t_buffers == NULL || space_left < sizeof(journal_block_tag_t) + 16) { diff -puN fs/jbd/journal.c~jbd-fix-against-journal-overflow fs/jbd/journal.c --- 25/fs/jbd/journal.c~jbd-fix-against-journal-overflow 2005-01-22 18:03:55.996689120 -0800 +++ 25-akpm/fs/jbd/journal.c 2005-01-22 18:03:56.006687600 -0800 @@ -721,6 +721,7 @@ journal_t * journal_init_dev(struct bloc { journal_t *journal = journal_init_common(); struct buffer_head *bh; + int n; if (!journal) return NULL; @@ -736,6 +737,17 @@ journal_t * journal_init_dev(struct bloc journal->j_sb_buffer = bh; journal->j_superblock = (journal_superblock_t *)bh->b_data; + /* journal descriptor can store upto n blocks -bzzz */ + n = journal->j_blocksize / sizeof(journal_block_tag_t); + journal->j_wbufsize = n; + journal->j_wbuf = kmalloc(n * sizeof(struct buffer_head*), GFP_KERNEL); + if (!journal->j_wbuf) { + printk(KERN_ERR "%s: Cant allocate bhs for commit thread\n", + __FUNCTION__); + kfree(journal); + journal = NULL; + } + return journal; } @@ -751,7 +763,7 @@ journal_t * journal_init_inode (struct i { struct buffer_head *bh; journal_t *journal = journal_init_common(); - int err; + int err, n; unsigned long blocknr; if (!journal) @@ -768,6 +780,17 @@ journal_t * journal_init_inode (struct i journal->j_maxlen = inode->i_size >> inode->i_sb->s_blocksize_bits; journal->j_blocksize = inode->i_sb->s_blocksize; + /* journal descriptor can store upto n blocks -bzzz */ + n = journal->j_blocksize / sizeof(journal_block_tag_t); + journal->j_wbufsize = n; + journal->j_wbuf = kmalloc(n * sizeof(struct buffer_head*), GFP_KERNEL); + if (!journal->j_wbuf) { + printk(KERN_ERR "%s: Cant allocate bhs for commit thread\n", + __FUNCTION__); + kfree(journal); + return NULL; + } + err = journal_bmap(journal, 0, &blocknr); /* If that failed, give up */ if (err) { @@ -1141,6 +1164,10 @@ void journal_destroy(journal_t *journal) iput(journal->j_inode); if (journal->j_revoke) journal_destroy_revoke(journal); + if (journal->j_wbuf) { + kfree(journal->j_wbuf); + journal->j_wbuf = NULL; + } kfree(journal); } diff -puN include/linux/jbd.h~jbd-fix-against-journal-overflow include/linux/jbd.h --- 25/include/linux/jbd.h~jbd-fix-against-journal-overflow 2005-01-22 18:03:55.997688968 -0800 +++ 25-akpm/include/linux/jbd.h 2005-01-22 18:03:56.002688208 -0800 @@ -789,6 +789,12 @@ struct journal_s struct jbd_revoke_table_s *j_revoke_table[2]; /* + * array of bhs for journal_commit_transaction + */ + struct buffer_head **j_wbuf; + int j_wbufsize; + + /* * An opaque pointer to fs-private information. ext3 puts its * superblock pointer here */ _