summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/btrfs/disk-io.c29
1 files changed, 29 insertions, 0 deletions
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index b59deb2c63f4..e5aad7f535aa 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -1751,6 +1751,7 @@ static int cleaner_kthread(void *arg)
1751{ 1751{
1752 struct btrfs_root *root = arg; 1752 struct btrfs_root *root = arg;
1753 int again; 1753 int again;
1754 struct btrfs_trans_handle *trans;
1754 1755
1755 do { 1756 do {
1756 again = 0; 1757 again = 0;
@@ -1798,6 +1799,34 @@ sleep:
1798 __set_current_state(TASK_RUNNING); 1799 __set_current_state(TASK_RUNNING);
1799 } 1800 }
1800 } while (!kthread_should_stop()); 1801 } while (!kthread_should_stop());
1802
1803 /*
1804 * Transaction kthread is stopped before us and wakes us up.
1805 * However we might have started a new transaction and COWed some
1806 * tree blocks when deleting unused block groups for example. So
1807 * make sure we commit the transaction we started to have a clean
1808 * shutdown when evicting the btree inode - if it has dirty pages
1809 * when we do the final iput() on it, eviction will trigger a
1810 * writeback for it which will fail with null pointer dereferences
1811 * since work queues and other resources were already released and
1812 * destroyed by the time the iput/eviction/writeback is made.
1813 */
1814 trans = btrfs_attach_transaction(root);
1815 if (IS_ERR(trans)) {
1816 if (PTR_ERR(trans) != -ENOENT)
1817 btrfs_err(root->fs_info,
1818 "cleaner transaction attach returned %ld",
1819 PTR_ERR(trans));
1820 } else {
1821 int ret;
1822
1823 ret = btrfs_commit_transaction(trans, root);
1824 if (ret)
1825 btrfs_err(root->fs_info,
1826 "cleaner open transaction commit returned %d",
1827 ret);
1828 }
1829
1801 return 0; 1830 return 0;
1802} 1831}
1803 1832