From: Chris Mason <mason@suse.com>

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 */
 

_