From: Greg Kroah-Hartman Date: Mon, 21 May 2018 08:24:16 +0000 (+0200) Subject: 4.14-stable patches X-Git-Tag: v4.9.102~13 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=58279df79bc31ec788dc90c19cf066c35491c5cf;p=thirdparty%2Fkernel%2Fstable-queue.git 4.14-stable patches added patches: btrfs-fix-delalloc-inodes-invalidation-during-transaction-abort.patch btrfs-split-btrfs_del_delalloc_inode-into-2-functions.patch --- diff --git a/queue-4.14/btrfs-fix-delalloc-inodes-invalidation-during-transaction-abort.patch b/queue-4.14/btrfs-fix-delalloc-inodes-invalidation-during-transaction-abort.patch new file mode 100644 index 00000000000..ee15a679ab9 --- /dev/null +++ b/queue-4.14/btrfs-fix-delalloc-inodes-invalidation-during-transaction-abort.patch @@ -0,0 +1,131 @@ +From fe816d0f1d4c31c4c31d42ca78a87660565fc800 Mon Sep 17 00:00:00 2001 +From: Nikolay Borisov +Date: Fri, 27 Apr 2018 12:21:53 +0300 +Subject: btrfs: Fix delalloc inodes invalidation during transaction abort + +From: Nikolay Borisov + +commit fe816d0f1d4c31c4c31d42ca78a87660565fc800 upstream. + +When a transaction is aborted btrfs_cleanup_transaction is called to +cleanup all the various in-flight bits and pieces which migth be +active. One of those is delalloc inodes - inodes which have dirty +pages which haven't been persisted yet. Currently the process of +freeing such delalloc inodes in exceptional circumstances such as +transaction abort boiled down to calling btrfs_invalidate_inodes whose +sole job is to invalidate the dentries for all inodes related to a +root. This is in fact wrong and insufficient since such delalloc inodes +will likely have pending pages or ordered-extents and will be linked to +the sb->s_inode_list. This means that unmounting a btrfs instance with +an aborted transaction could potentially lead inodes/their pages +visible to the system long after their superblock has been freed. This +in turn leads to a "use-after-free" situation once page shrink is +triggered. This situation could be simulated by running generic/019 +which would cause such inodes to be left hanging, followed by +generic/176 which causes memory pressure and page eviction which lead +to touching the freed super block instance. This situation is +additionally detected by the unmount code of VFS with the following +message: + +"VFS: Busy inodes after unmount of Self-destruct in 5 seconds. Have a nice day..." + +Additionally btrfs hits WARN_ON(!RB_EMPTY_ROOT(&root->inode_tree)); +in free_fs_root for the same reason. + +This patch aims to rectify the sitaution by doing the following: + +1. Change btrfs_destroy_delalloc_inodes so that it calls +invalidate_inode_pages2 for every inode on the delalloc list, this +ensures that all the pages of the inode are released. This function +boils down to calling btrfs_releasepage. During test I observed cases +where inodes on the delalloc list were having an i_count of 0, so this +necessitates using igrab to be sure we are working on a non-freed inode. + +2. Since calling btrfs_releasepage might queue delayed iputs move the +call out to btrfs_cleanup_transaction in btrfs_error_commit_super before +calling run_delayed_iputs for the last time. This is necessary to ensure +that delayed iputs are run. + +Note: this patch is tagged for 4.14 stable but the fix applies to older +versions too but needs to be backported manually due to conflicts. + +CC: stable@vger.kernel.org # 4.14.x: 2b8773313494: btrfs: Split btrfs_del_delalloc_inode into 2 functions +CC: stable@vger.kernel.org # 4.14.x +Signed-off-by: Nikolay Borisov +Reviewed-by: David Sterba +[ add comment to igrab ] +Signed-off-by: David Sterba +Signed-off-by: Greg Kroah-Hartman + +--- + fs/btrfs/disk-io.c | 26 +++++++++++++++----------- + 1 file changed, 15 insertions(+), 11 deletions(-) + +--- a/fs/btrfs/disk-io.c ++++ b/fs/btrfs/disk-io.c +@@ -3905,6 +3905,7 @@ void close_ctree(struct btrfs_fs_info *f + set_bit(BTRFS_FS_CLOSING_DONE, &fs_info->flags); + + btrfs_free_qgroup_config(fs_info); ++ ASSERT(list_empty(&fs_info->delalloc_roots)); + + if (percpu_counter_sum(&fs_info->delalloc_bytes)) { + btrfs_info(fs_info, "at unmount delalloc count %lld", +@@ -4203,15 +4204,15 @@ static int btrfs_check_super_valid(struc + + static void btrfs_error_commit_super(struct btrfs_fs_info *fs_info) + { ++ /* cleanup FS via transaction */ ++ btrfs_cleanup_transaction(fs_info); ++ + mutex_lock(&fs_info->cleaner_mutex); + btrfs_run_delayed_iputs(fs_info); + mutex_unlock(&fs_info->cleaner_mutex); + + down_write(&fs_info->cleanup_work_sem); + up_write(&fs_info->cleanup_work_sem); +- +- /* cleanup FS via transaction */ +- btrfs_cleanup_transaction(fs_info); + } + + static void btrfs_destroy_ordered_extents(struct btrfs_root *root) +@@ -4334,19 +4335,23 @@ static void btrfs_destroy_delalloc_inode + list_splice_init(&root->delalloc_inodes, &splice); + + while (!list_empty(&splice)) { ++ struct inode *inode = NULL; + btrfs_inode = list_first_entry(&splice, struct btrfs_inode, + delalloc_inodes); +- +- list_del_init(&btrfs_inode->delalloc_inodes); +- clear_bit(BTRFS_INODE_IN_DELALLOC_LIST, +- &btrfs_inode->runtime_flags); ++ __btrfs_del_delalloc_inode(root, btrfs_inode); + spin_unlock(&root->delalloc_lock); + +- btrfs_invalidate_inodes(btrfs_inode->root); +- ++ /* ++ * Make sure we get a live inode and that it'll not disappear ++ * meanwhile. ++ */ ++ inode = igrab(&btrfs_inode->vfs_inode); ++ if (inode) { ++ invalidate_inode_pages2(inode->i_mapping); ++ iput(inode); ++ } + spin_lock(&root->delalloc_lock); + } +- + spin_unlock(&root->delalloc_lock); + } + +@@ -4362,7 +4367,6 @@ static void btrfs_destroy_all_delalloc_i + while (!list_empty(&splice)) { + root = list_first_entry(&splice, struct btrfs_root, + delalloc_root); +- list_del_init(&root->delalloc_root); + root = btrfs_grab_fs_root(root); + BUG_ON(!root); + spin_unlock(&fs_info->delalloc_root_lock); diff --git a/queue-4.14/btrfs-split-btrfs_del_delalloc_inode-into-2-functions.patch b/queue-4.14/btrfs-split-btrfs_del_delalloc_inode-into-2-functions.patch new file mode 100644 index 00000000000..40e3cad0bf8 --- /dev/null +++ b/queue-4.14/btrfs-split-btrfs_del_delalloc_inode-into-2-functions.patch @@ -0,0 +1,66 @@ +From 2b8773313494ede83a26fb372466e634564002ed Mon Sep 17 00:00:00 2001 +From: Nikolay Borisov +Date: Fri, 27 Apr 2018 12:21:51 +0300 +Subject: btrfs: Split btrfs_del_delalloc_inode into 2 functions + +From: Nikolay Borisov + +commit 2b8773313494ede83a26fb372466e634564002ed upstream. + +This is in preparation of fixing delalloc inodes leakage on transaction +abort. Also export the new function. + +Signed-off-by: Nikolay Borisov +Reviewed-by: David Sterba +Reviewed-by: Anand Jain +Signed-off-by: David Sterba +Signed-off-by: Greg Kroah-Hartman + +--- + fs/btrfs/ctree.h | 2 ++ + fs/btrfs/inode.c | 13 ++++++++++--- + 2 files changed, 12 insertions(+), 3 deletions(-) + +--- a/fs/btrfs/ctree.h ++++ b/fs/btrfs/ctree.h +@@ -3150,6 +3150,8 @@ noinline int can_nocow_extent(struct ino + u64 *orig_start, u64 *orig_block_len, + u64 *ram_bytes); + ++void __btrfs_del_delalloc_inode(struct btrfs_root *root, ++ struct btrfs_inode *inode); + struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry); + int btrfs_set_inode_index(struct btrfs_inode *dir, u64 *index); + int btrfs_unlink_inode(struct btrfs_trans_handle *trans, +--- a/fs/btrfs/inode.c ++++ b/fs/btrfs/inode.c +@@ -1754,12 +1754,12 @@ static void btrfs_add_delalloc_inodes(st + spin_unlock(&root->delalloc_lock); + } + +-static void btrfs_del_delalloc_inode(struct btrfs_root *root, +- struct btrfs_inode *inode) ++ ++void __btrfs_del_delalloc_inode(struct btrfs_root *root, ++ struct btrfs_inode *inode) + { + struct btrfs_fs_info *fs_info = btrfs_sb(inode->vfs_inode.i_sb); + +- spin_lock(&root->delalloc_lock); + if (!list_empty(&inode->delalloc_inodes)) { + list_del_init(&inode->delalloc_inodes); + clear_bit(BTRFS_INODE_IN_DELALLOC_LIST, +@@ -1772,6 +1772,13 @@ static void btrfs_del_delalloc_inode(str + spin_unlock(&fs_info->delalloc_root_lock); + } + } ++} ++ ++static void btrfs_del_delalloc_inode(struct btrfs_root *root, ++ struct btrfs_inode *inode) ++{ ++ spin_lock(&root->delalloc_lock); ++ __btrfs_del_delalloc_inode(root, inode); + spin_unlock(&root->delalloc_lock); + } + diff --git a/queue-4.14/series b/queue-4.14/series index d9d590b6be5..2d0f1ac73fc 100644 --- a/queue-4.14/series +++ b/queue-4.14/series @@ -42,3 +42,5 @@ btrfs-fix-xattr-loss-after-power-failure.patch btrfs-send-fix-invalid-access-to-commit-roots-due-to-concurrent-snapshotting.patch btrfs-property-set-incompat-flag-if-lzo-zstd-compression-is-set.patch btrfs-fix-crash-when-trying-to-resume-balance-without-the-resume-flag.patch +btrfs-split-btrfs_del_delalloc_inode-into-2-functions.patch +btrfs-fix-delalloc-inodes-invalidation-during-transaction-abort.patch