btrfs: scrub: add assertions for worker pointers
[sfrench/cifs-2.6.git] / fs / btrfs / scrub.c
index 6dcd36d7b84906bc4585c861ea7b55659d365fe3..669bedfec4a9ab7be2fa3a1a5a82f924e8dd965b 100644 (file)
@@ -3741,25 +3741,33 @@ static noinline_for_stack int scrub_workers_get(struct btrfs_fs_info *fs_info,
        unsigned int flags = WQ_FREEZABLE | WQ_UNBOUND;
        int max_active = fs_info->thread_pool_size;
 
-       if (fs_info->scrub_workers_refcnt == 0) {
+       lockdep_assert_held(&fs_info->scrub_lock);
+
+       if (refcount_read(&fs_info->scrub_workers_refcnt) == 0) {
+               ASSERT(fs_info->scrub_workers == NULL);
                fs_info->scrub_workers = btrfs_alloc_workqueue(fs_info, "scrub",
                                flags, is_dev_replace ? 1 : max_active, 4);
                if (!fs_info->scrub_workers)
                        goto fail_scrub_workers;
 
+               ASSERT(fs_info->scrub_wr_completion_workers == NULL);
                fs_info->scrub_wr_completion_workers =
                        btrfs_alloc_workqueue(fs_info, "scrubwrc", flags,
                                              max_active, 2);
                if (!fs_info->scrub_wr_completion_workers)
                        goto fail_scrub_wr_completion_workers;
 
+               ASSERT(fs_info->scrub_parity_workers == NULL);
                fs_info->scrub_parity_workers =
                        btrfs_alloc_workqueue(fs_info, "scrubparity", flags,
                                              max_active, 2);
                if (!fs_info->scrub_parity_workers)
                        goto fail_scrub_parity_workers;
+
+               refcount_set(&fs_info->scrub_workers_refcnt, 1);
+       } else {
+               refcount_inc(&fs_info->scrub_workers_refcnt);
        }
-       ++fs_info->scrub_workers_refcnt;
        return 0;
 
 fail_scrub_parity_workers:
@@ -3770,16 +3778,6 @@ fail_scrub_workers:
        return -ENOMEM;
 }
 
-static noinline_for_stack void scrub_workers_put(struct btrfs_fs_info *fs_info)
-{
-       if (--fs_info->scrub_workers_refcnt == 0) {
-               btrfs_destroy_workqueue(fs_info->scrub_workers);
-               btrfs_destroy_workqueue(fs_info->scrub_wr_completion_workers);
-               btrfs_destroy_workqueue(fs_info->scrub_parity_workers);
-       }
-       WARN_ON(fs_info->scrub_workers_refcnt < 0);
-}
-
 int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start,
                    u64 end, struct btrfs_scrub_progress *progress,
                    int readonly, int is_dev_replace)
@@ -3788,6 +3786,9 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start,
        int ret;
        struct btrfs_device *dev;
        unsigned int nofs_flag;
+       struct btrfs_workqueue *scrub_workers = NULL;
+       struct btrfs_workqueue *scrub_wr_comp = NULL;
+       struct btrfs_workqueue *scrub_parity = NULL;
 
        if (btrfs_fs_closing(fs_info))
                return -EINVAL;
@@ -3835,7 +3836,7 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start,
                return PTR_ERR(sctx);
 
        mutex_lock(&fs_info->fs_devices->device_list_mutex);
-       dev = btrfs_find_device(fs_info, devid, NULL, NULL);
+       dev = btrfs_find_device(fs_info->fs_devices, devid, NULL, NULL, true);
        if (!dev || (test_bit(BTRFS_DEV_STATE_MISSING, &dev->dev_state) &&
                     !is_dev_replace)) {
                mutex_unlock(&fs_info->fs_devices->device_list_mutex);
@@ -3903,6 +3904,7 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start,
         */
        nofs_flag = memalloc_nofs_save();
        if (!is_dev_replace) {
+               btrfs_info(fs_info, "scrub: started on devid %llu", devid);
                /*
                 * by holding device list mutex, we can
                 * kick off writing super in log tree sync.
@@ -3925,11 +3927,26 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start,
        if (progress)
                memcpy(progress, &sctx->stat, sizeof(*progress));
 
+       if (!is_dev_replace)
+               btrfs_info(fs_info, "scrub: %s on devid %llu with status: %d",
+                       ret ? "not finished" : "finished", devid, ret);
+
        mutex_lock(&fs_info->scrub_lock);
        dev->scrub_ctx = NULL;
-       scrub_workers_put(fs_info);
+       if (refcount_dec_and_test(&fs_info->scrub_workers_refcnt)) {
+               scrub_workers = fs_info->scrub_workers;
+               scrub_wr_comp = fs_info->scrub_wr_completion_workers;
+               scrub_parity = fs_info->scrub_parity_workers;
+
+               fs_info->scrub_workers = NULL;
+               fs_info->scrub_wr_completion_workers = NULL;
+               fs_info->scrub_parity_workers = NULL;
+       }
        mutex_unlock(&fs_info->scrub_lock);
 
+       btrfs_destroy_workqueue(scrub_workers);
+       btrfs_destroy_workqueue(scrub_wr_comp);
+       btrfs_destroy_workqueue(scrub_parity);
        scrub_put_ctx(sctx);
 
        return ret;
@@ -4012,7 +4029,7 @@ int btrfs_scrub_progress(struct btrfs_fs_info *fs_info, u64 devid,
        struct scrub_ctx *sctx = NULL;
 
        mutex_lock(&fs_info->fs_devices->device_list_mutex);
-       dev = btrfs_find_device(fs_info, devid, NULL, NULL);
+       dev = btrfs_find_device(fs_info->fs_devices, devid, NULL, NULL, true);
        if (dev)
                sctx = dev->scrub_ctx;
        if (sctx)