From: Chris Mason reiserfs support for nested transactions. This originally came from Peter Braam for 2.4.x and was ported forward by Jeff Mahoney. --- 25-akpm/fs/reiserfs/inode.c | 4 + 25-akpm/fs/reiserfs/journal.c | 78 +++++++++++++++++++++++++++++++-- 25-akpm/fs/reiserfs/namei.c | 1 25-akpm/include/linux/reiserfs_fs_sb.h | 12 +++-- 4 files changed, 86 insertions(+), 9 deletions(-) diff -puN fs/reiserfs/inode.c~reiserfs-nesting-02 fs/reiserfs/inode.c --- 25/fs/reiserfs/inode.c~reiserfs-nesting-02 Wed Mar 24 15:14:28 2004 +++ 25-akpm/fs/reiserfs/inode.c Wed Mar 24 15:14:28 2004 @@ -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 -puN fs/reiserfs/journal.c~reiserfs-nesting-02 fs/reiserfs/journal.c --- 25/fs/reiserfs/journal.c~reiserfs-nesting-02 Wed Mar 24 15:14:28 2004 +++ 25-akpm/fs/reiserfs/journal.c Wed Mar 24 15:14:28 2004 @@ -2157,6 +2157,9 @@ int journal_transaction_should_end(struc time_t now = get_seconds() ; 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)) || @@ -2212,6 +2215,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) ; @@ -2268,9 +2274,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 ; @@ -2278,11 +2282,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("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 */ @@ -2422,7 +2462,26 @@ 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--; + if (th != cur_th) { + memcpy(current->journal_info, th, sizeof(*th)); + th->t_trans_id = 0; + } + return 0; + } else { + return do_journal_end(th, p_s_sb, nblocks, 0) ; + } } /* removes from the current transaction, relsing and descrementing any counters. @@ -2520,6 +2579,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)) ; @@ -2901,6 +2964,10 @@ static int do_journal_end(struct reiserf struct reiserfs_super_block *rs ; int trans_half ; + if (th->t_refcount > 1) + BUG() ; + + current->journal_info = th->t_handle_save; if (reiserfs_dont_log(th->t_super)) { return 0 ; } @@ -2938,8 +3005,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 -puN fs/reiserfs/namei.c~reiserfs-nesting-02 fs/reiserfs/namei.c --- 25/fs/reiserfs/namei.c~reiserfs-nesting-02 Wed Mar 24 15:14:28 2004 +++ 25-akpm/fs/reiserfs/namei.c Wed Mar 24 15:14:28 2004 @@ -575,7 +575,6 @@ static int reiserfs_create (struct inode reiserfs_write_lock(dir->i_sb); 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 -puN include/linux/reiserfs_fs_sb.h~reiserfs-nesting-02 include/linux/reiserfs_fs_sb.h --- 25/include/linux/reiserfs_fs_sb.h~reiserfs-nesting-02 Wed Mar 24 15:14:28 2004 +++ 25-akpm/include/linux/reiserfs_fs_sb.h Wed Mar 24 15:14:28 2004 @@ -157,13 +157,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 */ _