--- /dev/null
+From 3d2b158262826e8b75bbbfb7b97010838dd92ac7 Mon Sep 17 00:00:00 2001
+From: Lukas Czerner <lczerner@redhat.com>
+Date: Mon, 20 Feb 2012 17:53:00 -0500
+Subject: ext4: ignore EXT4_INODE_JOURNAL_DATA flag with delalloc
+
+From: Lukas Czerner <lczerner@redhat.com>
+
+commit 3d2b158262826e8b75bbbfb7b97010838dd92ac7 upstream.
+
+Ext4 does not support data journalling with delayed allocation enabled.
+We even do not allow to mount the file system with delayed allocation
+and data journalling enabled, however it can be set via FS_IOC_SETFLAGS
+so we can hit the inode with EXT4_INODE_JOURNAL_DATA set even on file
+system mounted with delayed allocation (default) and that's where
+problem arises. The easies way to reproduce this problem is with the
+following set of commands:
+
+ mkfs.ext4 /dev/sdd
+ mount /dev/sdd /mnt/test1
+ dd if=/dev/zero of=/mnt/test1/file bs=1M count=4
+ chattr +j /mnt/test1/file
+ dd if=/dev/zero of=/mnt/test1/file bs=1M count=4 conv=notrunc
+ chattr -j /mnt/test1/file
+
+Additionally it can be reproduced quite reliably with xfstests 272 and
+269. In fact the above reproducer is a part of test 272.
+
+To fix this we should ignore the EXT4_INODE_JOURNAL_DATA inode flag if
+the file system is mounted with delayed allocation. This can be easily
+done by fixing ext4_should_*_data() functions do ignore data journal
+flag when delalloc is set (suggested by Ted). We also have to set the
+appropriate address space operations for the inode (again, ignoring data
+journal flag if delalloc enabled).
+
+Additionally this commit introduces ext4_inode_journal_mode() function
+because ext4_should_*_data() has already had a lot of common code and
+this change is putting it all into one function so it is easier to
+read.
+
+Successfully tested with xfstests in following configurations:
+
+delalloc + data=ordered
+delalloc + data=writeback
+data=journal
+nodelalloc + data=ordered
+nodelalloc + data=writeback
+nodelalloc + data=journal
+
+Signed-off-by: Lukas Czerner <lczerner@redhat.com>
+Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/ext4/ext4_jbd2.h | 56 ++++++++++++++++++++++++++--------------------------
+ fs/ext4/inode.c | 36 ++++++++++++++++++++-------------
+ 2 files changed, 51 insertions(+), 41 deletions(-)
+
+--- a/fs/ext4/ext4_jbd2.h
++++ b/fs/ext4/ext4_jbd2.h
+@@ -261,43 +261,45 @@ static inline void ext4_update_inode_fsy
+ /* super.c */
+ int ext4_force_commit(struct super_block *sb);
+
+-static inline int ext4_should_journal_data(struct inode *inode)
++/*
++ * Ext4 inode journal modes
++ */
++#define EXT4_INODE_JOURNAL_DATA_MODE 0x01 /* journal data mode */
++#define EXT4_INODE_ORDERED_DATA_MODE 0x02 /* ordered data mode */
++#define EXT4_INODE_WRITEBACK_DATA_MODE 0x04 /* writeback data mode */
++
++static inline int ext4_inode_journal_mode(struct inode *inode)
+ {
+ if (EXT4_JOURNAL(inode) == NULL)
+- return 0;
+- if (!S_ISREG(inode->i_mode))
+- return 1;
+- if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA)
+- return 1;
+- if (ext4_test_inode_flag(inode, EXT4_INODE_JOURNAL_DATA))
+- return 1;
+- return 0;
++ return EXT4_INODE_WRITEBACK_DATA_MODE; /* writeback */
++ /* We do not support data journalling with delayed allocation */
++ if (!S_ISREG(inode->i_mode) ||
++ test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA)
++ return EXT4_INODE_JOURNAL_DATA_MODE; /* journal data */
++ if (ext4_test_inode_flag(inode, EXT4_INODE_JOURNAL_DATA) &&
++ !test_opt(inode->i_sb, DELALLOC))
++ return EXT4_INODE_JOURNAL_DATA_MODE; /* journal data */
++ if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_ORDERED_DATA)
++ return EXT4_INODE_ORDERED_DATA_MODE; /* ordered */
++ if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_WRITEBACK_DATA)
++ return EXT4_INODE_WRITEBACK_DATA_MODE; /* writeback */
++ else
++ BUG();
++}
++
++static inline int ext4_should_journal_data(struct inode *inode)
++{
++ return ext4_inode_journal_mode(inode) & EXT4_INODE_JOURNAL_DATA_MODE;
+ }
+
+ static inline int ext4_should_order_data(struct inode *inode)
+ {
+- if (EXT4_JOURNAL(inode) == NULL)
+- return 0;
+- if (!S_ISREG(inode->i_mode))
+- return 0;
+- if (ext4_test_inode_flag(inode, EXT4_INODE_JOURNAL_DATA))
+- return 0;
+- if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_ORDERED_DATA)
+- return 1;
+- return 0;
++ return ext4_inode_journal_mode(inode) & EXT4_INODE_ORDERED_DATA_MODE;
+ }
+
+ static inline int ext4_should_writeback_data(struct inode *inode)
+ {
+- if (EXT4_JOURNAL(inode) == NULL)
+- return 1;
+- if (!S_ISREG(inode->i_mode))
+- return 0;
+- if (ext4_test_inode_flag(inode, EXT4_INODE_JOURNAL_DATA))
+- return 0;
+- if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_WRITEBACK_DATA)
+- return 1;
+- return 0;
++ return ext4_inode_journal_mode(inode) & EXT4_INODE_WRITEBACK_DATA_MODE;
+ }
+
+ /*
+--- a/fs/ext4/inode.c
++++ b/fs/ext4/inode.c
+@@ -3212,13 +3212,14 @@ static int ext4_da_write_end(struct file
+ int write_mode = (int)(unsigned long)fsdata;
+
+ if (write_mode == FALL_BACK_TO_NONDELALLOC) {
+- if (ext4_should_order_data(inode)) {
++ switch (ext4_inode_journal_mode(inode)) {
++ case EXT4_INODE_ORDERED_DATA_MODE:
+ return ext4_ordered_write_end(file, mapping, pos,
+ len, copied, page, fsdata);
+- } else if (ext4_should_writeback_data(inode)) {
++ case EXT4_INODE_WRITEBACK_DATA_MODE:
+ return ext4_writeback_write_end(file, mapping, pos,
+ len, copied, page, fsdata);
+- } else {
++ default:
+ BUG();
+ }
+ }
+@@ -3918,18 +3919,25 @@ static const struct address_space_operat
+
+ void ext4_set_aops(struct inode *inode)
+ {
+- if (ext4_should_order_data(inode) &&
+- test_opt(inode->i_sb, DELALLOC))
+- inode->i_mapping->a_ops = &ext4_da_aops;
+- else if (ext4_should_order_data(inode))
+- inode->i_mapping->a_ops = &ext4_ordered_aops;
+- else if (ext4_should_writeback_data(inode) &&
+- test_opt(inode->i_sb, DELALLOC))
+- inode->i_mapping->a_ops = &ext4_da_aops;
+- else if (ext4_should_writeback_data(inode))
+- inode->i_mapping->a_ops = &ext4_writeback_aops;
+- else
++ switch (ext4_inode_journal_mode(inode)) {
++ case EXT4_INODE_ORDERED_DATA_MODE:
++ if (test_opt(inode->i_sb, DELALLOC))
++ inode->i_mapping->a_ops = &ext4_da_aops;
++ else
++ inode->i_mapping->a_ops = &ext4_ordered_aops;
++ break;
++ case EXT4_INODE_WRITEBACK_DATA_MODE:
++ if (test_opt(inode->i_sb, DELALLOC))
++ inode->i_mapping->a_ops = &ext4_da_aops;
++ else
++ inode->i_mapping->a_ops = &ext4_writeback_aops;
++ break;
++ case EXT4_INODE_JOURNAL_DATA_MODE:
+ inode->i_mapping->a_ops = &ext4_journalled_aops;
++ break;
++ default:
++ BUG();
++ }
+ }
+
+ /*
--- /dev/null
+From b18dafc86bb879d2f38a1743985d7ceb283c2f4d Mon Sep 17 00:00:00 2001
+From: Michel Lespinasse <walken@google.com>
+Date: Mon, 26 Mar 2012 17:32:44 -0700
+Subject: vfs: fix d_ancestor() case in d_materialize_unique
+
+From: Michel Lespinasse <walken@google.com>
+
+commit b18dafc86bb879d2f38a1743985d7ceb283c2f4d upstream.
+
+In d_materialise_unique() there are 3 subcases to the 'aliased dentry'
+case; in two subcases the inode i_lock is properly released but this
+does not occur in the -ELOOP subcase.
+
+This seems to have been introduced by commit 1836750115f2 ("fix loop
+checks in d_materialise_unique()").
+
+Signed-off-by: Michel Lespinasse <walken@google.com>
+[ Added a comment, and moved the unlock to where we generate the -ELOOP,
+ which seems to be more natural.
+
+ You probably can't actually trigger this without a buggy network file
+ server - d_materialize_unique() is for finding aliases on non-local
+ filesystems, and the d_ancestor() case is for a hardlinked directory
+ loop.
+
+ But we should be robust in the case of such buggy servers anyway. ]
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/dcache.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/fs/dcache.c
++++ b/fs/dcache.c
+@@ -2433,6 +2433,7 @@ struct dentry *d_materialise_unique(stru
+ if (d_ancestor(alias, dentry)) {
+ /* Check for loops */
+ actual = ERR_PTR(-ELOOP);
++ spin_unlock(&inode->i_lock);
+ } else if (IS_ROOT(alias)) {
+ /* Is this an anonymous mountpoint that we
+ * could splice into our tree? */
+@@ -2442,7 +2443,7 @@ struct dentry *d_materialise_unique(stru
+ goto found;
+ } else {
+ /* Nope, but we must(!) avoid directory
+- * aliasing */
++ * aliasing. This drops inode->i_lock */
+ actual = __d_unalias(inode, dentry, alias);
+ }
+ write_sequnlock(&rename_lock);