Merge tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[sfrench/cifs-2.6.git] / fs / btrfs / disk-io.c
index b0ab41da91d122b8cd8c8010cd56973af2afd240..3f0b6d1936e8ecd05dc0bf4957bb6aa691204dd4 100644 (file)
@@ -1664,9 +1664,8 @@ static int cleaner_kthread(void *arg)
        struct btrfs_root *root = arg;
        struct btrfs_fs_info *fs_info = root->fs_info;
        int again;
-       struct btrfs_trans_handle *trans;
 
-       do {
+       while (1) {
                again = 0;
 
                /* Make the cleaner go to sleep early. */
@@ -1715,42 +1714,16 @@ static int cleaner_kthread(void *arg)
                 */
                btrfs_delete_unused_bgs(fs_info);
 sleep:
+               if (kthread_should_park())
+                       kthread_parkme();
+               if (kthread_should_stop())
+                       return 0;
                if (!again) {
                        set_current_state(TASK_INTERRUPTIBLE);
-                       if (!kthread_should_stop())
-                               schedule();
+                       schedule();
                        __set_current_state(TASK_RUNNING);
                }
-       } while (!kthread_should_stop());
-
-       /*
-        * Transaction kthread is stopped before us and wakes us up.
-        * However we might have started a new transaction and COWed some
-        * tree blocks when deleting unused block groups for example. So
-        * make sure we commit the transaction we started to have a clean
-        * shutdown when evicting the btree inode - if it has dirty pages
-        * when we do the final iput() on it, eviction will trigger a
-        * writeback for it which will fail with null pointer dereferences
-        * since work queues and other resources were already released and
-        * destroyed by the time the iput/eviction/writeback is made.
-        */
-       trans = btrfs_attach_transaction(root);
-       if (IS_ERR(trans)) {
-               if (PTR_ERR(trans) != -ENOENT)
-                       btrfs_err(fs_info,
-                                 "cleaner transaction attach returned %ld",
-                                 PTR_ERR(trans));
-       } else {
-               int ret;
-
-               ret = btrfs_commit_transaction(trans);
-               if (ret)
-                       btrfs_err(fs_info,
-                                 "cleaner open transaction commit returned %d",
-                                 ret);
        }
-
-       return 0;
 }
 
 static int transaction_kthread(void *arg)
@@ -3931,6 +3904,13 @@ void close_ctree(struct btrfs_fs_info *fs_info)
        int ret;
 
        set_bit(BTRFS_FS_CLOSING_START, &fs_info->flags);
+       /*
+        * We don't want the cleaner to start new transactions, add more delayed
+        * iputs, etc. while we're closing. We can't use kthread_stop() yet
+        * because that frees the task_struct, and the transaction kthread might
+        * still try to wake up the cleaner.
+        */
+       kthread_park(fs_info->cleaner_kthread);
 
        /* wait for the qgroup rescan worker to stop */
        btrfs_qgroup_wait_for_completion(fs_info, false);
@@ -3958,9 +3938,8 @@ void close_ctree(struct btrfs_fs_info *fs_info)
 
        if (!sb_rdonly(fs_info->sb)) {
                /*
-                * If the cleaner thread is stopped and there are
-                * block groups queued for removal, the deletion will be
-                * skipped when we quit the cleaner thread.
+                * The cleaner kthread is stopped, so do one final pass over
+                * unused block groups.
                 */
                btrfs_delete_unused_bgs(fs_info);
 
@@ -4359,13 +4338,23 @@ static int btrfs_destroy_pinned_extent(struct btrfs_fs_info *fs_info,
        unpin = pinned_extents;
 again:
        while (1) {
+               /*
+                * The btrfs_finish_extent_commit() may get the same range as
+                * ours between find_first_extent_bit and clear_extent_dirty.
+                * Hence, hold the unused_bg_unpin_mutex to avoid double unpin
+                * the same extent range.
+                */
+               mutex_lock(&fs_info->unused_bg_unpin_mutex);
                ret = find_first_extent_bit(unpin, 0, &start, &end,
                                            EXTENT_DIRTY, NULL);
-               if (ret)
+               if (ret) {
+                       mutex_unlock(&fs_info->unused_bg_unpin_mutex);
                        break;
+               }
 
                clear_extent_dirty(unpin, start, end);
                btrfs_error_unpin_extent_range(fs_info, start, end);
+               mutex_unlock(&fs_info->unused_bg_unpin_mutex);
                cond_resched();
        }