]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - mm/backing-dev.c
PCI: PM: Skip devices in D0 for suspend-to-idle
[mirror_ubuntu-bionic-kernel.git] / mm / backing-dev.c
index b5f940ce0143ba061a183db0df3ef0dc17f57c72..da28e35592f4b410e3c1f7dcbe7ac01cc895713e 100644 (file)
@@ -126,6 +126,7 @@ static int bdi_debug_register(struct backing_dev_info *bdi, const char *name)
                                               bdi, &bdi_debug_stats_fops);
        if (!bdi->debug_stats) {
                debugfs_remove(bdi->debug_dir);
+               bdi->debug_dir = NULL;
                return -ENOMEM;
        }
 
@@ -369,15 +370,8 @@ static void wb_shutdown(struct bdi_writeback *wb)
        spin_lock_bh(&wb->work_lock);
        if (!test_and_clear_bit(WB_registered, &wb->state)) {
                spin_unlock_bh(&wb->work_lock);
-               /*
-                * Wait for wb shutdown to finish if someone else is just
-                * running wb_shutdown(). Otherwise we could proceed to wb /
-                * bdi destruction before wb_shutdown() is finished.
-                */
-               wait_on_bit(&wb->state, WB_shutting_down, TASK_UNINTERRUPTIBLE);
                return;
        }
-       set_bit(WB_shutting_down, &wb->state);
        spin_unlock_bh(&wb->work_lock);
 
        cgwb_remove_from_bdi_list(wb);
@@ -389,12 +383,6 @@ static void wb_shutdown(struct bdi_writeback *wb)
        mod_delayed_work(bdi_wq, &wb->dwork, 0);
        flush_delayed_work(&wb->dwork);
        WARN_ON(!list_empty(&wb->work_list));
-       /*
-        * Make sure bit gets cleared after shutdown is finished. Matches with
-        * the barrier provided by test_and_clear_bit() above.
-        */
-       smp_wmb();
-       clear_bit(WB_shutting_down, &wb->state);
 }
 
 static void wb_exit(struct bdi_writeback *wb)
@@ -422,6 +410,7 @@ static void wb_exit(struct bdi_writeback *wb)
  * protected.
  */
 static DEFINE_SPINLOCK(cgwb_lock);
+static struct workqueue_struct *cgwb_release_wq;
 
 /**
  * wb_congested_get_create - get or create a wb_congested
@@ -517,10 +506,12 @@ static void cgwb_release_workfn(struct work_struct *work)
        struct bdi_writeback *wb = container_of(work, struct bdi_writeback,
                                                release_work);
 
+       mutex_lock(&wb->bdi->cgwb_release_mutex);
        wb_shutdown(wb);
 
        css_put(wb->memcg_css);
        css_put(wb->blkcg_css);
+       mutex_unlock(&wb->bdi->cgwb_release_mutex);
 
        fprop_local_destroy_percpu(&wb->memcg_completions);
        percpu_ref_exit(&wb->refcnt);
@@ -532,7 +523,7 @@ static void cgwb_release(struct percpu_ref *refcnt)
 {
        struct bdi_writeback *wb = container_of(refcnt, struct bdi_writeback,
                                                refcnt);
-       schedule_work(&wb->release_work);
+       queue_work(cgwb_release_wq, &wb->release_work);
 }
 
 static void cgwb_kill(struct bdi_writeback *wb)
@@ -706,6 +697,7 @@ static int cgwb_bdi_init(struct backing_dev_info *bdi)
 
        INIT_RADIX_TREE(&bdi->cgwb_tree, GFP_ATOMIC);
        bdi->cgwb_congested_tree = RB_ROOT;
+       mutex_init(&bdi->cgwb_release_mutex);
 
        ret = wb_init(&bdi->wb, bdi, 1, GFP_KERNEL);
        if (!ret) {
@@ -726,7 +718,10 @@ static void cgwb_bdi_unregister(struct backing_dev_info *bdi)
        spin_lock_irq(&cgwb_lock);
        radix_tree_for_each_slot(slot, &bdi->cgwb_tree, &iter, 0)
                cgwb_kill(*slot);
+       spin_unlock_irq(&cgwb_lock);
 
+       mutex_lock(&bdi->cgwb_release_mutex);
+       spin_lock_irq(&cgwb_lock);
        while (!list_empty(&bdi->wb_list)) {
                wb = list_first_entry(&bdi->wb_list, struct bdi_writeback,
                                      bdi_node);
@@ -735,6 +730,7 @@ static void cgwb_bdi_unregister(struct backing_dev_info *bdi)
                spin_lock_irq(&cgwb_lock);
        }
        spin_unlock_irq(&cgwb_lock);
+       mutex_unlock(&bdi->cgwb_release_mutex);
 }
 
 /**
@@ -796,6 +792,21 @@ static void cgwb_bdi_register(struct backing_dev_info *bdi)
        spin_unlock_irq(&cgwb_lock);
 }
 
+static int __init cgwb_init(void)
+{
+       /*
+        * There can be many concurrent release work items overwhelming
+        * system_wq.  Put them in a separate wq and limit concurrency.
+        * There's no point in executing many of these in parallel.
+        */
+       cgwb_release_wq = alloc_workqueue("cgwb_release", 0, 1);
+       if (!cgwb_release_wq)
+               return -ENOMEM;
+
+       return 0;
+}
+subsys_initcall(cgwb_init);
+
 #else  /* CONFIG_CGROUP_WRITEBACK */
 
 static int cgwb_bdi_init(struct backing_dev_info *bdi)