From: Greg Kroah-Hartman Date: Mon, 8 Oct 2012 19:47:57 +0000 (-0700) Subject: 3.5-stable patches X-Git-Tag: v3.0.46~35 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e4c4991350f25106ee98a4cd2f52edcf494024ef;p=thirdparty%2Fkernel%2Fstable-queue.git 3.5-stable patches added patches: ext4-always-set-i_op-in-ext4_mknod.patch ext4-fix-crash-when-accessing-proc-mounts-concurrently.patch ext4-fix-fdatasync-for-files-with-only-i_size-changes.patch ext4-move_extent-code-cleanup.patch ext4-online-defrag-is-not-supported-for-journaled-files.patch --- diff --git a/queue-3.5/ext4-always-set-i_op-in-ext4_mknod.patch b/queue-3.5/ext4-always-set-i_op-in-ext4_mknod.patch new file mode 100644 index 00000000000..cfd512830ba --- /dev/null +++ b/queue-3.5/ext4-always-set-i_op-in-ext4_mknod.patch @@ -0,0 +1,33 @@ +From 6a08f447facb4f9e29fcc30fb68060bb5a0d21c2 Mon Sep 17 00:00:00 2001 +From: Bernd Schubert +Date: Wed, 26 Sep 2012 21:24:57 -0400 +Subject: ext4: always set i_op in ext4_mknod() + +From: Bernd Schubert + +commit 6a08f447facb4f9e29fcc30fb68060bb5a0d21c2 upstream. + +ext4_special_inode_operations have their own ifdef CONFIG_EXT4_FS_XATTR +to mask those methods. And ext4_iget also always sets it, so there is +an inconsistency. + +Signed-off-by: Bernd Schubert +Signed-off-by: "Theodore Ts'o" +Signed-off-by: Greg Kroah-Hartman + +--- + fs/ext4/namei.c | 2 -- + 1 file changed, 2 deletions(-) + +--- a/fs/ext4/namei.c ++++ b/fs/ext4/namei.c +@@ -2149,9 +2149,7 @@ retry: + err = PTR_ERR(inode); + if (!IS_ERR(inode)) { + init_special_inode(inode, inode->i_mode, rdev); +-#ifdef CONFIG_EXT4_FS_XATTR + inode->i_op = &ext4_special_inode_operations; +-#endif + err = ext4_add_nondir(handle, dentry, inode); + } + ext4_journal_stop(handle); diff --git a/queue-3.5/ext4-fix-crash-when-accessing-proc-mounts-concurrently.patch b/queue-3.5/ext4-fix-crash-when-accessing-proc-mounts-concurrently.patch new file mode 100644 index 00000000000..d5528fa792f --- /dev/null +++ b/queue-3.5/ext4-fix-crash-when-accessing-proc-mounts-concurrently.patch @@ -0,0 +1,47 @@ +From 50df9fd55e4271e89a7adf3b1172083dd0ca199d Mon Sep 17 00:00:00 2001 +From: Herton Ronaldo Krzesinski +Date: Sun, 23 Sep 2012 22:49:12 -0400 +Subject: ext4: fix crash when accessing /proc/mounts concurrently + +From: Herton Ronaldo Krzesinski + +commit 50df9fd55e4271e89a7adf3b1172083dd0ca199d upstream. + +The crash was caused by a variable being erronously declared static in +token2str(). + +In addition to /proc/mounts, the problem can also be easily replicated +by accessing /proc/fs/ext4//options in parallel: + +$ cat /proc/fs/ext4//options > options.txt + +... and then running the following command in two different terminals: + +$ while diff /proc/fs/ext4//options options.txt; do true; done + +This is also the cause of the following a crash while running xfstests +#234, as reported in the following bug reports: + + https://bugs.launchpad.net/bugs/1053019 + https://bugzilla.kernel.org/show_bug.cgi?id=47731 + +Signed-off-by: Herton Ronaldo Krzesinski +Signed-off-by: "Theodore Ts'o" +Cc: Brad Figg +Signed-off-by: Greg Kroah-Hartman + +--- + fs/ext4/super.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/fs/ext4/super.c ++++ b/fs/ext4/super.c +@@ -1748,7 +1748,7 @@ static inline void ext4_show_quota_optio + + static const char *token2str(int token) + { +- static const struct match_token *t; ++ const struct match_token *t; + + for (t = tokens; t->token != Opt_err; t++) + if (t->token == token && !strchr(t->pattern, '=')) diff --git a/queue-3.5/ext4-fix-fdatasync-for-files-with-only-i_size-changes.patch b/queue-3.5/ext4-fix-fdatasync-for-files-with-only-i_size-changes.patch new file mode 100644 index 00000000000..c626f27ef83 --- /dev/null +++ b/queue-3.5/ext4-fix-fdatasync-for-files-with-only-i_size-changes.patch @@ -0,0 +1,56 @@ +From b71fc079b5d8f42b2a52743c8d2f1d35d655b1c5 Mon Sep 17 00:00:00 2001 +From: Jan Kara +Date: Wed, 26 Sep 2012 21:52:20 -0400 +Subject: ext4: fix fdatasync() for files with only i_size changes + +From: Jan Kara + +commit b71fc079b5d8f42b2a52743c8d2f1d35d655b1c5 upstream. + +Code tracking when transaction needs to be committed on fdatasync(2) forgets +to handle a situation when only inode's i_size is changed. Thus in such +situations fdatasync(2) doesn't force transaction with new i_size to disk +and that can result in wrong i_size after a crash. + +Fix the issue by updating inode's i_datasync_tid whenever its size is +updated. + +Reported-by: Kristian Nielsen +Signed-off-by: Jan Kara +Signed-off-by: Greg Kroah-Hartman + +--- + fs/ext4/inode.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +--- a/fs/ext4/inode.c ++++ b/fs/ext4/inode.c +@@ -3988,6 +3988,7 @@ static int ext4_do_update_inode(handle_t + struct ext4_inode_info *ei = EXT4_I(inode); + struct buffer_head *bh = iloc->bh; + int err = 0, rc, block; ++ int need_datasync = 0; + uid_t i_uid; + gid_t i_gid; + +@@ -4038,7 +4039,10 @@ static int ext4_do_update_inode(handle_t + raw_inode->i_file_acl_high = + cpu_to_le16(ei->i_file_acl >> 32); + raw_inode->i_file_acl_lo = cpu_to_le32(ei->i_file_acl); +- ext4_isize_set(raw_inode, ei->i_disksize); ++ if (ei->i_disksize != ext4_isize(raw_inode)) { ++ ext4_isize_set(raw_inode, ei->i_disksize); ++ need_datasync = 1; ++ } + if (ei->i_disksize > 0x7fffffffULL) { + struct super_block *sb = inode->i_sb; + if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, +@@ -4091,7 +4095,7 @@ static int ext4_do_update_inode(handle_t + err = rc; + ext4_clear_inode_state(inode, EXT4_STATE_NEW); + +- ext4_update_inode_fsync_trans(handle, inode, 0); ++ ext4_update_inode_fsync_trans(handle, inode, need_datasync); + out_brelse: + brelse(bh); + ext4_std_error(inode->i_sb, err); diff --git a/queue-3.5/ext4-move_extent-code-cleanup.patch b/queue-3.5/ext4-move_extent-code-cleanup.patch new file mode 100644 index 00000000000..2b9409c1a51 --- /dev/null +++ b/queue-3.5/ext4-move_extent-code-cleanup.patch @@ -0,0 +1,342 @@ +From 03bd8b9b896c8e940f282f540e6b4de90d666b7c Mon Sep 17 00:00:00 2001 +From: Dmitry Monakhov +Date: Wed, 26 Sep 2012 12:32:19 -0400 +Subject: ext4: move_extent code cleanup + +From: Dmitry Monakhov + +commit 03bd8b9b896c8e940f282f540e6b4de90d666b7c upstream. + +- Remove usless checks, because it is too late to check that inode != NULL + at the moment it was referenced several times. +- Double lock routines looks very ugly and locking ordering relays on + order of i_ino, but other kernel code rely on order of pointers. + Let's make them simple and clean. +- check that inodes belongs to the same SB as soon as possible. + +Signed-off-by: Dmitry Monakhov +Signed-off-by: "Theodore Ts'o" +Signed-off-by: Greg Kroah-Hartman + +--- + fs/ext4/move_extent.c | 167 ++++++++++++++------------------------------------ + 1 file changed, 47 insertions(+), 120 deletions(-) + +--- a/fs/ext4/move_extent.c ++++ b/fs/ext4/move_extent.c +@@ -141,55 +141,21 @@ mext_next_extent(struct inode *inode, st + } + + /** +- * mext_check_null_inode - NULL check for two inodes +- * +- * If inode1 or inode2 is NULL, return -EIO. Otherwise, return 0. +- */ +-static int +-mext_check_null_inode(struct inode *inode1, struct inode *inode2, +- const char *function, unsigned int line) +-{ +- int ret = 0; +- +- if (inode1 == NULL) { +- __ext4_error(inode2->i_sb, function, line, +- "Both inodes should not be NULL: " +- "inode1 NULL inode2 %lu", inode2->i_ino); +- ret = -EIO; +- } else if (inode2 == NULL) { +- __ext4_error(inode1->i_sb, function, line, +- "Both inodes should not be NULL: " +- "inode1 %lu inode2 NULL", inode1->i_ino); +- ret = -EIO; +- } +- return ret; +-} +- +-/** + * double_down_write_data_sem - Acquire two inodes' write lock of i_data_sem + * +- * @orig_inode: original inode structure +- * @donor_inode: donor inode structure +- * Acquire write lock of i_data_sem of the two inodes (orig and donor) by +- * i_ino order. ++ * Acquire write lock of i_data_sem of the two inodes + */ + static void +-double_down_write_data_sem(struct inode *orig_inode, struct inode *donor_inode) ++double_down_write_data_sem(struct inode *first, struct inode *second) + { +- struct inode *first = orig_inode, *second = donor_inode; ++ if (first < second) { ++ down_write(&EXT4_I(first)->i_data_sem); ++ down_write_nested(&EXT4_I(second)->i_data_sem, SINGLE_DEPTH_NESTING); ++ } else { ++ down_write(&EXT4_I(second)->i_data_sem); ++ down_write_nested(&EXT4_I(first)->i_data_sem, SINGLE_DEPTH_NESTING); + +- /* +- * Use the inode number to provide the stable locking order instead +- * of its address, because the C language doesn't guarantee you can +- * compare pointers that don't come from the same array. +- */ +- if (donor_inode->i_ino < orig_inode->i_ino) { +- first = donor_inode; +- second = orig_inode; + } +- +- down_write(&EXT4_I(first)->i_data_sem); +- down_write_nested(&EXT4_I(second)->i_data_sem, SINGLE_DEPTH_NESTING); + } + + /** +@@ -969,14 +935,6 @@ mext_check_arguments(struct inode *orig_ + return -EINVAL; + } + +- /* Files should be in the same ext4 FS */ +- if (orig_inode->i_sb != donor_inode->i_sb) { +- ext4_debug("ext4 move extent: The argument files " +- "should be in same FS [ino:orig %lu, donor %lu]\n", +- orig_inode->i_ino, donor_inode->i_ino); +- return -EINVAL; +- } +- + /* Ext4 move extent supports only extent based file */ + if (!(ext4_test_inode_flag(orig_inode, EXT4_INODE_EXTENTS))) { + ext4_debug("ext4 move extent: orig file is not extents " +@@ -1072,35 +1030,19 @@ mext_check_arguments(struct inode *orig_ + * @inode1: the inode structure + * @inode2: the inode structure + * +- * Lock two inodes' i_mutex by i_ino order. +- * If inode1 or inode2 is NULL, return -EIO. Otherwise, return 0. ++ * Lock two inodes' i_mutex + */ +-static int ++static void + mext_inode_double_lock(struct inode *inode1, struct inode *inode2) + { +- int ret = 0; +- +- BUG_ON(inode1 == NULL && inode2 == NULL); +- +- ret = mext_check_null_inode(inode1, inode2, __func__, __LINE__); +- if (ret < 0) +- goto out; +- +- if (inode1 == inode2) { +- mutex_lock(&inode1->i_mutex); +- goto out; +- } +- +- if (inode1->i_ino < inode2->i_ino) { ++ BUG_ON(inode1 == inode2); ++ if (inode1 < inode2) { + mutex_lock_nested(&inode1->i_mutex, I_MUTEX_PARENT); + mutex_lock_nested(&inode2->i_mutex, I_MUTEX_CHILD); + } else { + mutex_lock_nested(&inode2->i_mutex, I_MUTEX_PARENT); + mutex_lock_nested(&inode1->i_mutex, I_MUTEX_CHILD); + } +- +-out: +- return ret; + } + + /** +@@ -1109,28 +1051,13 @@ out: + * @inode1: the inode that is released first + * @inode2: the inode that is released second + * +- * If inode1 or inode2 is NULL, return -EIO. Otherwise, return 0. + */ + +-static int ++static void + mext_inode_double_unlock(struct inode *inode1, struct inode *inode2) + { +- int ret = 0; +- +- BUG_ON(inode1 == NULL && inode2 == NULL); +- +- ret = mext_check_null_inode(inode1, inode2, __func__, __LINE__); +- if (ret < 0) +- goto out; +- +- if (inode1) +- mutex_unlock(&inode1->i_mutex); +- +- if (inode2 && inode2 != inode1) +- mutex_unlock(&inode2->i_mutex); +- +-out: +- return ret; ++ mutex_unlock(&inode1->i_mutex); ++ mutex_unlock(&inode2->i_mutex); + } + + /** +@@ -1187,16 +1114,23 @@ ext4_move_extents(struct file *o_filp, s + ext4_lblk_t block_end, seq_start, add_blocks, file_end, seq_blocks = 0; + ext4_lblk_t rest_blocks; + pgoff_t orig_page_offset = 0, seq_end_page; +- int ret1, ret2, depth, last_extent = 0; ++ int ret, depth, last_extent = 0; + int blocks_per_page = PAGE_CACHE_SIZE >> orig_inode->i_blkbits; + int data_offset_in_page; + int block_len_in_page; + int uninit; + +- /* orig and donor should be different file */ +- if (orig_inode->i_ino == donor_inode->i_ino) { ++ if (orig_inode->i_sb != donor_inode->i_sb) { ++ ext4_debug("ext4 move extent: The argument files " ++ "should be in same FS [ino:orig %lu, donor %lu]\n", ++ orig_inode->i_ino, donor_inode->i_ino); ++ return -EINVAL; ++ } ++ ++ /* orig and donor should be different inodes */ ++ if (orig_inode == donor_inode) { + ext4_debug("ext4 move extent: The argument files should not " +- "be same file [ino:orig %lu, donor %lu]\n", ++ "be same inode [ino:orig %lu, donor %lu]\n", + orig_inode->i_ino, donor_inode->i_ino); + return -EINVAL; + } +@@ -1210,16 +1144,14 @@ ext4_move_extents(struct file *o_filp, s + } + + /* Protect orig and donor inodes against a truncate */ +- ret1 = mext_inode_double_lock(orig_inode, donor_inode); +- if (ret1 < 0) +- return ret1; ++ mext_inode_double_lock(orig_inode, donor_inode); + + /* Protect extent tree against block allocations via delalloc */ + double_down_write_data_sem(orig_inode, donor_inode); + /* Check the filesystem environment whether move_extent can be done */ +- ret1 = mext_check_arguments(orig_inode, donor_inode, orig_start, ++ ret = mext_check_arguments(orig_inode, donor_inode, orig_start, + donor_start, &len); +- if (ret1) ++ if (ret) + goto out; + + file_end = (i_size_read(orig_inode) - 1) >> orig_inode->i_blkbits; +@@ -1227,13 +1159,13 @@ ext4_move_extents(struct file *o_filp, s + if (file_end < block_end) + len -= block_end - file_end; + +- ret1 = get_ext_path(orig_inode, block_start, &orig_path); +- if (ret1) ++ ret = get_ext_path(orig_inode, block_start, &orig_path); ++ if (ret) + goto out; + + /* Get path structure to check the hole */ +- ret1 = get_ext_path(orig_inode, block_start, &holecheck_path); +- if (ret1) ++ ret = get_ext_path(orig_inode, block_start, &holecheck_path); ++ if (ret) + goto out; + + depth = ext_depth(orig_inode); +@@ -1252,13 +1184,13 @@ ext4_move_extents(struct file *o_filp, s + last_extent = mext_next_extent(orig_inode, + holecheck_path, &ext_cur); + if (last_extent < 0) { +- ret1 = last_extent; ++ ret = last_extent; + goto out; + } + last_extent = mext_next_extent(orig_inode, orig_path, + &ext_dummy); + if (last_extent < 0) { +- ret1 = last_extent; ++ ret = last_extent; + goto out; + } + seq_start = le32_to_cpu(ext_cur->ee_block); +@@ -1272,7 +1204,7 @@ ext4_move_extents(struct file *o_filp, s + if (le32_to_cpu(ext_cur->ee_block) > block_end) { + ext4_debug("ext4 move extent: The specified range of file " + "may be the hole\n"); +- ret1 = -EINVAL; ++ ret = -EINVAL; + goto out; + } + +@@ -1292,7 +1224,7 @@ ext4_move_extents(struct file *o_filp, s + last_extent = mext_next_extent(orig_inode, holecheck_path, + &ext_cur); + if (last_extent < 0) { +- ret1 = last_extent; ++ ret = last_extent; + break; + } + add_blocks = ext4_ext_get_actual_len(ext_cur); +@@ -1349,18 +1281,18 @@ ext4_move_extents(struct file *o_filp, s + orig_page_offset, + data_offset_in_page, + block_len_in_page, uninit, +- &ret1); ++ &ret); + + /* Count how many blocks we have exchanged */ + *moved_len += block_len_in_page; +- if (ret1 < 0) ++ if (ret < 0) + break; + if (*moved_len > len) { + EXT4_ERROR_INODE(orig_inode, + "We replaced blocks too much! " + "sum of replaced: %llu requested: %llu", + *moved_len, len); +- ret1 = -EIO; ++ ret = -EIO; + break; + } + +@@ -1374,22 +1306,22 @@ ext4_move_extents(struct file *o_filp, s + } + + double_down_write_data_sem(orig_inode, donor_inode); +- if (ret1 < 0) ++ if (ret < 0) + break; + + /* Decrease buffer counter */ + if (holecheck_path) + ext4_ext_drop_refs(holecheck_path); +- ret1 = get_ext_path(orig_inode, seq_start, &holecheck_path); +- if (ret1) ++ ret = get_ext_path(orig_inode, seq_start, &holecheck_path); ++ if (ret) + break; + depth = holecheck_path->p_depth; + + /* Decrease buffer counter */ + if (orig_path) + ext4_ext_drop_refs(orig_path); +- ret1 = get_ext_path(orig_inode, seq_start, &orig_path); +- if (ret1) ++ ret = get_ext_path(orig_inode, seq_start, &orig_path); ++ if (ret) + break; + + ext_cur = holecheck_path[depth].p_ext; +@@ -1412,12 +1344,7 @@ out: + kfree(holecheck_path); + } + double_up_write_data_sem(orig_inode, donor_inode); +- ret2 = mext_inode_double_unlock(orig_inode, donor_inode); ++ mext_inode_double_unlock(orig_inode, donor_inode); + +- if (ret1) +- return ret1; +- else if (ret2) +- return ret2; +- +- return 0; ++ return ret; + } diff --git a/queue-3.5/ext4-online-defrag-is-not-supported-for-journaled-files.patch b/queue-3.5/ext4-online-defrag-is-not-supported-for-journaled-files.patch new file mode 100644 index 00000000000..41433726bd9 --- /dev/null +++ b/queue-3.5/ext4-online-defrag-is-not-supported-for-journaled-files.patch @@ -0,0 +1,37 @@ +From f066055a3449f0e5b0ae4f3ceab4445bead47638 Mon Sep 17 00:00:00 2001 +From: Dmitry Monakhov +Date: Wed, 26 Sep 2012 12:32:54 -0400 +Subject: ext4: online defrag is not supported for journaled files + +From: Dmitry Monakhov + +commit f066055a3449f0e5b0ae4f3ceab4445bead47638 upstream. + +Proper block swap for inodes with full journaling enabled is +truly non obvious task. In order to be on a safe side let's +explicitly disable it for now. + +Signed-off-by: Dmitry Monakhov +Signed-off-by: "Theodore Ts'o" +Signed-off-by: Greg Kroah-Hartman + +--- + fs/ext4/move_extent.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/fs/ext4/move_extent.c ++++ b/fs/ext4/move_extent.c +@@ -1142,7 +1142,12 @@ ext4_move_extents(struct file *o_filp, s + orig_inode->i_ino, donor_inode->i_ino); + return -EINVAL; + } +- ++ /* TODO: This is non obvious task to swap blocks for inodes with full ++ jornaling enabled */ ++ if (ext4_should_journal_data(orig_inode) || ++ ext4_should_journal_data(donor_inode)) { ++ return -EINVAL; ++ } + /* Protect orig and donor inodes against a truncate */ + mext_inode_double_lock(orig_inode, donor_inode); + diff --git a/queue-3.5/series b/queue-3.5/series index 32b47dff0f3..0f4bdfbdeec 100644 --- a/queue-3.5/series +++ b/queue-3.5/series @@ -69,3 +69,8 @@ ext4-ignore-last-group-w-o-enough-space-when-resizing-instead-of-bug-ing.patch ext4-don-t-copy-non-existent-gdt-blocks-when-resizing.patch ext4-avoid-duplicate-writes-of-the-backup-bg-descriptor-blocks.patch ext4-fix-potential-deadlock-in-ext4_nonda_switch.patch +ext4-fix-crash-when-accessing-proc-mounts-concurrently.patch +ext4-move_extent-code-cleanup.patch +ext4-online-defrag-is-not-supported-for-journaled-files.patch +ext4-always-set-i_op-in-ext4_mknod.patch +ext4-fix-fdatasync-for-files-with-only-i_size-changes.patch