ext4: Handle nested ext4_journal_start/stop calls without a journal
authorCurt Wohlgemuth <curtw@google.com>
Tue, 29 Sep 2009 15:01:03 +0000 (11:01 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Tue, 29 Sep 2009 15:01:03 +0000 (11:01 -0400)
This patch fixes a problem with handling nested calls to
ext4_journal_start/ext4_journal_stop, when there is no journal present.

Signed-off-by: Curt Wohlgemuth <curtw@google.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
fs/ext4/ext4_jbd2.h
fs/ext4/namei.c
fs/ext4/super.c

index 139fb8c..a286598 100644 (file)
@@ -161,11 +161,13 @@ int __ext4_handle_dirty_metadata(const char *where, handle_t *handle,
 handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks);
 int __ext4_journal_stop(const char *where, handle_t *handle);
 
-#define EXT4_NOJOURNAL_HANDLE  ((handle_t *) 0x1)
+#define EXT4_NOJOURNAL_MAX_REF_COUNT ((unsigned long) 4096)
 
+/* Note:  Do not use this for NULL handles.  This is only to determine if
+ * a properly allocated handle is using a journal or not. */
 static inline int ext4_handle_valid(handle_t *handle)
 {
-       if (handle == EXT4_NOJOURNAL_HANDLE)
+       if ((unsigned long)handle < EXT4_NOJOURNAL_MAX_REF_COUNT)
                return 0;
        return 1;
 }
index 42f81d2..7c8fe80 100644 (file)
@@ -2076,7 +2076,8 @@ int ext4_orphan_del(handle_t *handle, struct inode *inode)
        struct ext4_iloc iloc;
        int err = 0;
 
-       if (!ext4_handle_valid(handle))
+       /* ext4_handle_valid() assumes a valid handle_t pointer */
+       if (handle && !ext4_handle_valid(handle))
                return 0;
 
        mutex_lock(&EXT4_SB(inode->i_sb)->s_orphan_lock);
index f095c60..3f7e701 100644 (file)
@@ -189,6 +189,36 @@ void ext4_itable_unused_set(struct super_block *sb,
                bg->bg_itable_unused_hi = cpu_to_le16(count >> 16);
 }
 
+
+/* Just increment the non-pointer handle value */
+static handle_t *ext4_get_nojournal(void)
+{
+       handle_t *handle = current->journal_info;
+       unsigned long ref_cnt = (unsigned long)handle;
+
+       BUG_ON(ref_cnt >= EXT4_NOJOURNAL_MAX_REF_COUNT);
+
+       ref_cnt++;
+       handle = (handle_t *)ref_cnt;
+
+       current->journal_info = handle;
+       return handle;
+}
+
+
+/* Decrement the non-pointer handle value */
+static void ext4_put_nojournal(handle_t *handle)
+{
+       unsigned long ref_cnt = (unsigned long)handle;
+
+       BUG_ON(ref_cnt == 0);
+
+       ref_cnt--;
+       handle = (handle_t *)ref_cnt;
+
+       current->journal_info = handle;
+}
+
 /*
  * Wrappers for jbd2_journal_start/end.
  *
@@ -215,11 +245,7 @@ handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks)
                }
                return jbd2_journal_start(journal, nblocks);
        }
-       /*
-        * We're not journaling, return the appropriate indication.
-        */
-       current->journal_info = EXT4_NOJOURNAL_HANDLE;
-       return current->journal_info;
+       return ext4_get_nojournal();
 }
 
 /*
@@ -235,11 +261,7 @@ int __ext4_journal_stop(const char *where, handle_t *handle)
        int rc;
 
        if (!ext4_handle_valid(handle)) {
-               /*
-                * Do this here since we don't call jbd2_journal_stop() in
-                * no-journal mode.
-                */
-               current->journal_info = NULL;
+               ext4_put_nojournal(handle);
                return 0;
        }
        sb = handle->h_transaction->t_journal->j_private;