diff -ruNp linux-2.4.23/fs/reiserfs/inode.c linux-2.4.23.xattr/fs/reiserfs/inode.c --- linux-2.4.23/fs/reiserfs/inode.c 2003-12-09 14:09:41.231329328 -0500 +++ linux-2.4.23.xattr/fs/reiserfs/inode.c 2003-12-09 14:23:04.146267704 -0500 @@ -206,6 +206,10 @@ static int file_capable (struct inode * struct super_block *s = th->t_super ; int len = th->t_blocks_allocated ; + /* we cannot restart while nested */ + if (th->t_refcount > 1) { + return ; + } pathrelse(path) ; reiserfs_update_sd(th, inode) ; journal_end(th, s, len) ; diff -ruNp linux-2.4.23/fs/reiserfs/journal.c linux-2.4.23.xattr/fs/reiserfs/journal.c --- linux-2.4.23/fs/reiserfs/journal.c 2003-12-09 14:09:41.238328264 -0500 +++ linux-2.4.23.xattr/fs/reiserfs/journal.c 2003-12-09 14:23:04.151266944 -0500 @@ -2232,6 +2232,9 @@ int journal_transaction_should_end(struc time_t now = CURRENT_TIME ; if (reiserfs_dont_log(th->t_super)) return 0 ; + /* cannot restart while nested */ + if (th->t_refcount > 1) + return 0 ; if ( SB_JOURNAL(th->t_super)->j_must_wait > 0 || (SB_JOURNAL(th->t_super)->j_len_alloc + new_alloc) >= SB_JOURNAL_MAX_BATCH(th->t_super) || atomic_read(&(SB_JOURNAL(th->t_super)->j_jlock)) || @@ -2287,6 +2290,9 @@ static int do_journal_begin_r(struct rei return 0 ; } PROC_INFO_INC( p_s_sb, journal.journal_being ); + /* set here for journal_join */ + th->t_refcount = 1; + th->t_super = p_s_sb ; relock: lock_journal(p_s_sb) ; @@ -2343,9 +2349,7 @@ relock: SB_JOURNAL(p_s_sb)->j_len_alloc += nblocks ; th->t_blocks_logged = 0 ; th->t_blocks_allocated = nblocks ; - th->t_super = p_s_sb ; th->t_trans_id = SB_JOURNAL(p_s_sb)->j_trans_id ; - th->t_caller = "Unknown" ; unlock_journal(p_s_sb) ; p_s_sb->s_dirt = 1; return 0 ; @@ -2353,11 +2357,47 @@ relock: static int journal_join(struct reiserfs_transaction_handle *th, struct super_block *p_s_sb, unsigned long nblocks) { + struct reiserfs_transaction_handle *cur_th = current->journal_info; + + /* this keeps do_journal_end from NULLing out the current->journal_info + ** pointer + */ + th->t_handle_save = cur_th ; + if (cur_th && cur_th->t_refcount > 1) { + BUG() ; + } return do_journal_begin_r(th, p_s_sb, nblocks, 1) ; } int journal_begin(struct reiserfs_transaction_handle *th, struct super_block * p_s_sb, unsigned long nblocks) { - return do_journal_begin_r(th, p_s_sb, nblocks, 0) ; + struct reiserfs_transaction_handle *cur_th = current->journal_info ; + int ret ; + + th->t_handle_save = NULL ; + if (cur_th) { + /* we are nesting into the current transaction */ + if (cur_th->t_super == p_s_sb) { + cur_th->t_refcount++ ; + memcpy(th, cur_th, sizeof(*th)); + if (th->t_refcount <= 1) + printk("BAD: refcount <= 1, but journal_info != 0\n"); + return 0; + } else { + /* we've ended up with a handle from a different filesystem. + ** save it and restore on journal_end. This should never + ** really happen... + */ + reiserfs_warning(p_s_sb, "clm-2100: nesting info a different FS\n") ; + th->t_handle_save = current->journal_info ; + current->journal_info = th; + } + } else { + current->journal_info = th; + } + ret = do_journal_begin_r(th, p_s_sb, nblocks, 0) ; + if (current->journal_info != th) + BUG() ; + return ret ; } /* not used at all */ @@ -2497,7 +2537,23 @@ int journal_mark_dirty_nolog(struct reis } int journal_end(struct reiserfs_transaction_handle *th, struct super_block *p_s_sb, unsigned long nblocks) { - return do_journal_end(th, p_s_sb, nblocks, 0) ; + if (!current->journal_info && th->t_refcount > 1) + printk("REISER-NESTING: th NULL, refcount %d\n", th->t_refcount); + if (th->t_refcount > 1) { + struct reiserfs_transaction_handle *cur_th = current->journal_info ; + + /* we aren't allowed to close a nested transaction on a different + ** filesystem from the one in the task struct + */ + if (cur_th->t_super != th->t_super) + BUG() ; + + th->t_refcount--; + memcpy(current->journal_info, th, sizeof(*th)); + return 0; + } else { + return do_journal_end(th, p_s_sb, nblocks, 0) ; + } } /* removes from the current transaction, relsing and descrementing any counters. @@ -2600,6 +2656,10 @@ static int can_dirty(struct reiserfs_jou */ int journal_end_sync(struct reiserfs_transaction_handle *th, struct super_block *p_s_sb, unsigned long nblocks) { + /* you can sync while nested, very, very bad */ + if (th->t_refcount > 1) { + BUG() ; + } if (SB_JOURNAL(p_s_sb)->j_len == 0) { reiserfs_prepare_for_journal(p_s_sb, SB_BUFFER_WITH_SB(p_s_sb), 1) ; journal_mark_dirty(th, p_s_sb, SB_BUFFER_WITH_SB(p_s_sb)) ; @@ -3000,6 +3060,10 @@ static int do_journal_end(struct reiserf int wait_on_commit = flags & WAIT ; struct reiserfs_super_block *rs ; + if (th->t_refcount > 1) + BUG() ; + + current->journal_info = th->t_handle_save; if (reiserfs_dont_log(th->t_super)) { return 0 ; } @@ -3037,8 +3101,11 @@ static int do_journal_end(struct reiserf } #ifdef REISERFS_PREALLOCATE + /* quota ops might need to nest, setup the journal_info pointer for them */ + current->journal_info = th ; reiserfs_discard_all_prealloc(th); /* it should not involve new blocks into * the transaction */ + current->journal_info = th->t_handle_save ; #endif rs = SB_DISK_SUPER_BLOCK(p_s_sb) ; diff -ruNp linux-2.4.23/fs/reiserfs/namei.c linux-2.4.23.xattr/fs/reiserfs/namei.c --- linux-2.4.23/fs/reiserfs/namei.c 2003-12-09 14:09:41.240327960 -0500 +++ linux-2.4.23.xattr/fs/reiserfs/namei.c 2003-12-09 14:23:04.152266792 -0500 @@ -536,7 +536,6 @@ static int reiserfs_create (struct inode return retval ; journal_begin(&th, dir->i_sb, jbegin_count) ; - th.t_caller = "create" ; retval = reiserfs_new_inode (&th, dir, mode, 0, 0/*i_size*/, dentry, inode); if (retval) { goto out_failed ; diff -ruNp linux-2.4.23/include/linux/reiserfs_fs_sb.h linux-2.4.23.xattr/include/linux/reiserfs_fs_sb.h --- linux-2.4.23/include/linux/reiserfs_fs_sb.h 2003-12-09 14:09:41.880230680 -0500 +++ linux-2.4.23.xattr/include/linux/reiserfs_fs_sb.h 2003-12-09 14:23:04.153266640 -0500 @@ -171,13 +171,17 @@ struct reiserfs_list_bitmap { ** transaction handle which is passed around for all journal calls */ struct reiserfs_transaction_handle { - /* ifdef it. -Hans */ - char *t_caller ; /* debugging use */ + struct super_block *t_super ; /* super for this FS when journal_begin was + called. saves calls to reiserfs_get_super + also used by nested transactions to make + sure they are nesting on the right FS + _must_ be first in the handle + */ + int t_refcount; int t_blocks_logged ; /* number of blocks this writer has logged */ int t_blocks_allocated ; /* number of blocks this writer allocated */ unsigned long t_trans_id ; /* sanity check, equals the current trans id */ - struct super_block *t_super ; /* super for this FS when journal_begin was - called. saves calls to reiserfs_get_super */ + void *t_handle_save ; /* save existing current->journal_info */ int displace_new_blocks:1; /* if new block allocation occurres, that block should be displaced from others */ } ;