arm64: configs: rockchip_defconfig: enable rk3368 thermal
[firefly-linux-kernel-4.4.55.git] / mm / backing-dev.c
index 8ed2ffd963c53b910f91e1b60b04c56385a3129f..a988d4ef39da00732848fdbc9111ecde62716a68 100644 (file)
@@ -757,15 +757,20 @@ static int cgwb_bdi_init(struct backing_dev_info *bdi)
        if (!bdi->wb_congested)
                return -ENOMEM;
 
+       atomic_set(&bdi->wb_congested->refcnt, 1);
+
        err = wb_init(&bdi->wb, bdi, 1, GFP_KERNEL);
        if (err) {
-               kfree(bdi->wb_congested);
+               wb_congested_put(bdi->wb_congested);
                return err;
        }
        return 0;
 }
 
-static void cgwb_bdi_destroy(struct backing_dev_info *bdi) { }
+static void cgwb_bdi_destroy(struct backing_dev_info *bdi)
+{
+       wb_congested_put(bdi->wb_congested);
+}
 
 #endif /* CONFIG_CGROUP_WRITEBACK */
 
@@ -825,6 +830,20 @@ int bdi_register_dev(struct backing_dev_info *bdi, dev_t dev)
 }
 EXPORT_SYMBOL(bdi_register_dev);
 
+int bdi_register_owner(struct backing_dev_info *bdi, struct device *owner)
+{
+       int rc;
+
+       rc = bdi_register(bdi, NULL, "%u:%u", MAJOR(owner->devt),
+                       MINOR(owner->devt));
+       if (rc)
+               return rc;
+       bdi->owner = owner;
+       get_device(owner);
+       return 0;
+}
+EXPORT_SYMBOL(bdi_register_owner);
+
 /*
  * Remove bdi from bdi_list, and ensure that it is no longer visible
  */
@@ -849,6 +868,11 @@ void bdi_unregister(struct backing_dev_info *bdi)
                device_unregister(bdi->dev);
                bdi->dev = NULL;
        }
+
+       if (bdi->owner) {
+               put_device(bdi->owner);
+               bdi->owner = NULL;
+       }
 }
 
 void bdi_exit(struct backing_dev_info *bdi)
@@ -957,8 +981,9 @@ EXPORT_SYMBOL(congestion_wait);
  * jiffies for either a BDI to exit congestion of the given @sync queue
  * or a write to complete.
  *
- * In the absence of zone congestion, cond_resched() is called to yield
- * the processor if necessary but otherwise does not sleep.
+ * In the absence of zone congestion, a short sleep or a cond_resched is
+ * performed to yield the processor and to allow other subsystems to make
+ * a forward progress.
  *
  * The return value is 0 if the sleep is for the full timeout. Otherwise,
  * it is the number of jiffies that were still remaining when the function
@@ -978,7 +1003,19 @@ long wait_iff_congested(struct zone *zone, int sync, long timeout)
         */
        if (atomic_read(&nr_wb_congested[sync]) == 0 ||
            !test_bit(ZONE_CONGESTED, &zone->flags)) {
-               cond_resched();
+
+               /*
+                * Memory allocation/reclaim might be called from a WQ
+                * context and the current implementation of the WQ
+                * concurrency control doesn't recognize that a particular
+                * WQ is congested if the worker thread is looping without
+                * ever sleeping. Therefore we have to do a short sleep
+                * here rather than calling cond_resched().
+                */
+               if (current->flags & PF_WQ_WORKER)
+                       schedule_timeout_uninterruptible(1);
+               else
+                       cond_resched();
 
                /* In case we scheduled, work out time remaining */
                ret = timeout - (jiffies - start);