ACPI / thermal: make acpi_thermal_check asynchronous on resume
authorAaron Lu <aaron.lu@intel.com>
Tue, 4 Mar 2014 06:24:46 +0000 (14:24 +0800)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Wed, 5 Mar 2014 00:43:44 +0000 (01:43 +0100)
On resume we do not need to wait for acpi_thermal_check to finish.
Instead, we can run it asynchronously and not block the whole system
resume. Also, we make sure when we are suspending again, previously
queued work for acpi_thermal_check is done.

Signed-off-by: Aaron Lu <aaron.lu@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/acpi/thermal.c

index 08626c851be7eef72c5a639522b708586736adc5..9640685533345340bbff00bc67abeecac4e9c7d1 100644 (file)
@@ -43,6 +43,7 @@
 #include <linux/device.h>
 #include <linux/thermal.h>
 #include <linux/acpi.h>
+#include <linux/workqueue.h>
 #include <asm/uaccess.h>
 
 #define PREFIX "ACPI: "
@@ -90,6 +91,8 @@ static int psv;
 module_param(psv, int, 0644);
 MODULE_PARM_DESC(psv, "Disable or override all passive trip points.");
 
+static struct workqueue_struct *acpi_thermal_pm_queue;
+
 static int acpi_thermal_add(struct acpi_device *device);
 static int acpi_thermal_remove(struct acpi_device *device);
 static void acpi_thermal_notify(struct acpi_device *device, u32 event);
@@ -101,11 +104,13 @@ static const struct acpi_device_id  thermal_device_ids[] = {
 MODULE_DEVICE_TABLE(acpi, thermal_device_ids);
 
 #ifdef CONFIG_PM_SLEEP
+static int acpi_thermal_suspend(struct device *dev);
 static int acpi_thermal_resume(struct device *dev);
 #else
+#define acpi_thermal_suspend NULL
 #define acpi_thermal_resume NULL
 #endif
-static SIMPLE_DEV_PM_OPS(acpi_thermal_pm, NULL, acpi_thermal_resume);
+static SIMPLE_DEV_PM_OPS(acpi_thermal_pm, acpi_thermal_suspend, acpi_thermal_resume);
 
 static struct acpi_driver acpi_thermal_driver = {
        .name = "thermal",
@@ -186,6 +191,7 @@ struct acpi_thermal {
        struct thermal_zone_device *thermal_zone;
        int tz_enabled;
        int kelvin_offset;
+       struct work_struct thermal_check_work;
 };
 
 /* --------------------------------------------------------------------------
@@ -1064,6 +1070,13 @@ static void acpi_thermal_guess_offset(struct acpi_thermal *tz)
                tz->kelvin_offset = 2732;
 }
 
+static void acpi_thermal_check_fn(struct work_struct *work)
+{
+       struct acpi_thermal *tz = container_of(work, struct acpi_thermal,
+                                              thermal_check_work);
+       acpi_thermal_check(tz);
+}
+
 static int acpi_thermal_add(struct acpi_device *device)
 {
        int result = 0;
@@ -1093,6 +1106,8 @@ static int acpi_thermal_add(struct acpi_device *device)
        if (result)
                goto free_memory;
 
+       INIT_WORK(&tz->thermal_check_work, acpi_thermal_check_fn);
+
        pr_info(PREFIX "%s [%s] (%ld C)\n", acpi_device_name(device),
                acpi_device_bid(device), KELVIN_TO_CELSIUS(tz->temperature));
        goto end;
@@ -1110,6 +1125,7 @@ static int acpi_thermal_remove(struct acpi_device *device)
        if (!device || !acpi_driver_data(device))
                return -EINVAL;
 
+       flush_workqueue(acpi_thermal_pm_queue);
        tz = acpi_driver_data(device);
 
        acpi_thermal_unregister_thermal_zone(tz);
@@ -1118,6 +1134,13 @@ static int acpi_thermal_remove(struct acpi_device *device)
 }
 
 #ifdef CONFIG_PM_SLEEP
+static int acpi_thermal_suspend(struct device *dev)
+{
+       /* Make sure the previously queued thermal check work has been done */
+       flush_workqueue(acpi_thermal_pm_queue);
+       return 0;
+}
+
 static int acpi_thermal_resume(struct device *dev)
 {
        struct acpi_thermal *tz;
@@ -1148,7 +1171,7 @@ static int acpi_thermal_resume(struct device *dev)
                tz->state.active |= tz->trips.active[i].flags.enabled;
        }
 
-       acpi_thermal_check(tz);
+       queue_work(acpi_thermal_pm_queue, &tz->thermal_check_work);
 
        return AE_OK;
 }
@@ -1240,16 +1263,22 @@ static int __init acpi_thermal_init(void)
                return -ENODEV;
        }
 
+       acpi_thermal_pm_queue = create_workqueue("acpi_thermal_pm");
+       if (!acpi_thermal_pm_queue)
+               return -ENODEV;
+
        result = acpi_bus_register_driver(&acpi_thermal_driver);
-       if (result < 0)
+       if (result < 0) {
+               destroy_workqueue(acpi_thermal_pm_queue);
                return -ENODEV;
+       }
 
        return 0;
 }
 
 static void __exit acpi_thermal_exit(void)
 {
-
+       destroy_workqueue(acpi_thermal_pm_queue);
        acpi_bus_unregister_driver(&acpi_thermal_driver);
 
        return;