From de6b0c1acc1411a67fb89f60ee7b526066da7bfb Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 29 Oct 2016 21:37:01 +0200 Subject: [PATCH 01/76] thermal: hwmon: use permission-specific DEVICE_ATTR variants Use DEVICE_ATTR_RO for read only attributes. This simplifies the source code, improves readbility, and reduces the chance of inconsistencies. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @ro@ declarer name DEVICE_ATTR; identifier x,x_show; @@ DEVICE_ATTR(x, \(0444\|S_IRUGO\), x_show, NULL); @script:ocaml@ x << ro.x; x_show << ro.x_show; @@ if not (x^"_show" = x_show) then Coccilib.include_match false @@ declarer name DEVICE_ATTR_RO; identifier ro.x,ro.x_show; @@ - DEVICE_ATTR(x, \(0444\|S_IRUGO\), x_show, NULL); + DEVICE_ATTR_RO(x); // Signed-off-by: Julia Lawall Signed-off-by: Zhang Rui --- drivers/thermal/thermal_hwmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c index c41c7742903a..8c45de6a9140 100644 --- a/drivers/thermal/thermal_hwmon.c +++ b/drivers/thermal/thermal_hwmon.c @@ -64,7 +64,7 @@ name_show(struct device *dev, struct device_attribute *attr, char *buf) struct thermal_hwmon_device *hwmon = dev_get_drvdata(dev); return sprintf(buf, "%s\n", hwmon->type); } -static DEVICE_ATTR(name, 0444, name_show, NULL); +static DEVICE_ATTR_RO(name); static ssize_t temp_input_show(struct device *dev, struct device_attribute *attr, char *buf) From 5184eeb3b016470956de4c89ca9b53c65ef41452 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 29 Oct 2016 21:37:00 +0200 Subject: [PATCH 02/76] thermal: int340x_thermal: use permission-specific DEVICE_ATTR variants Use DEVICE_ATTR_RW for read-write attributes. This simplifies the source code, improves readbility, and reduces the chance of inconsistencies. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @rw@ declarer name DEVICE_ATTR; identifier x,x_show,x_store; @@ DEVICE_ATTR(x, \(0644\|S_IRUGO|S_IWUSR\), x_show, x_store); @script:ocaml@ x << rw.x; x_show << rw.x_show; x_store << rw.x_store; @@ if not (x^"_show" = x_show && x^"_store" = x_store) then Coccilib.include_match false @@ declarer name DEVICE_ATTR_RW; identifier rw.x,rw.x_show,rw.x_store; @@ - DEVICE_ATTR(x, \(0644\|S_IRUGO|S_IWUSR\), x_show, x_store); + DEVICE_ATTR_RW(x); // Signed-off-by: Julia Lawall Signed-off-by: Zhang Rui --- drivers/thermal/int340x_thermal/int3400_thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/int340x_thermal/int3400_thermal.c b/drivers/thermal/int340x_thermal/int3400_thermal.c index 5836e5554433..9413c4abf0b9 100644 --- a/drivers/thermal/int340x_thermal/int3400_thermal.c +++ b/drivers/thermal/int340x_thermal/int3400_thermal.c @@ -96,7 +96,7 @@ static ssize_t current_uuid_store(struct device *dev, return -EINVAL; } -static DEVICE_ATTR(current_uuid, 0644, current_uuid_show, current_uuid_store); +static DEVICE_ATTR_RW(current_uuid); static DEVICE_ATTR_RO(available_uuids); static struct attribute *uuid_attrs[] = { &dev_attr_available_uuids.attr, From c6068a6edd50569fc34340c3abb30c24e24b8909 Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Wed, 19 Oct 2016 05:59:29 +0900 Subject: [PATCH 03/76] thermal: intel_pch_thermal: Support skylake PCH 100 series thermal This patch uses .driver_data and board_info[] to make per pci device behavior table (name and ops), instead of adding the code for each pci device in switch-case. This will make easier to add new pci device ids. Then this adds new device id actually for skylake PCH 100 series (using registers are compatible with currently driver, so no need to change except adding device id to table). Signed-off-by: OGAWA Hirofumi Reviewed-by: Srinivas Pandruvada Signed-off-by: Zhang Rui --- drivers/thermal/intel_pch_thermal.c | 64 ++++++++++++++++++----------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/drivers/thermal/intel_pch_thermal.c b/drivers/thermal/intel_pch_thermal.c index 19bf2028e508..2b49e8d0fe9e 100644 --- a/drivers/thermal/intel_pch_thermal.c +++ b/drivers/thermal/intel_pch_thermal.c @@ -29,6 +29,7 @@ #define PCH_THERMAL_DID_HSW_2 0x8C24 /* Haswell PCH */ #define PCH_THERMAL_DID_WPT 0x9CA4 /* Wildcat Point */ #define PCH_THERMAL_DID_SKL 0x9D31 /* Skylake PCH */ +#define PCH_THERMAL_DID_SKL_H 0xA131 /* Skylake PCH 100 series */ /* Wildcat Point-LP PCH Thermal registers */ #define WPT_TEMP 0x0000 /* Temperature */ @@ -273,37 +274,44 @@ static struct thermal_zone_device_ops tzd_ops = { .get_trip_temp = pch_get_trip_temp, }; +enum board_ids { + board_hsw, + board_wpt, + board_skl, +}; + +static const struct board_info { + const char *name; + const struct pch_dev_ops *ops; +} board_info[] = { + [board_hsw] = { + .name = "pch_haswell", + .ops = &pch_dev_ops_wpt, + }, + [board_wpt] = { + .name = "pch_wildcat_point", + .ops = &pch_dev_ops_wpt, + }, + [board_skl] = { + .name = "pch_skylake", + .ops = &pch_dev_ops_wpt, + }, +}; static int intel_pch_thermal_probe(struct pci_dev *pdev, const struct pci_device_id *id) { + enum board_ids board_id = id->driver_data; + const struct board_info *bi = &board_info[board_id]; struct pch_thermal_device *ptd; int err; int nr_trips; - char *dev_name; ptd = devm_kzalloc(&pdev->dev, sizeof(*ptd), GFP_KERNEL); if (!ptd) return -ENOMEM; - switch (pdev->device) { - case PCH_THERMAL_DID_WPT: - ptd->ops = &pch_dev_ops_wpt; - dev_name = "pch_wildcat_point"; - break; - case PCH_THERMAL_DID_SKL: - ptd->ops = &pch_dev_ops_wpt; - dev_name = "pch_skylake"; - break; - case PCH_THERMAL_DID_HSW_1: - case PCH_THERMAL_DID_HSW_2: - ptd->ops = &pch_dev_ops_wpt; - dev_name = "pch_haswell"; - break; - default: - dev_err(&pdev->dev, "unknown pch thermal device\n"); - return -ENODEV; - } + ptd->ops = bi->ops; pci_set_drvdata(pdev, ptd); ptd->pdev = pdev; @@ -331,11 +339,11 @@ static int intel_pch_thermal_probe(struct pci_dev *pdev, if (err) goto error_cleanup; - ptd->tzd = thermal_zone_device_register(dev_name, nr_trips, 0, ptd, + ptd->tzd = thermal_zone_device_register(bi->name, nr_trips, 0, ptd, &tzd_ops, NULL, 0, 0); if (IS_ERR(ptd->tzd)) { dev_err(&pdev->dev, "Failed to register thermal zone %s\n", - dev_name); + bi->name); err = PTR_ERR(ptd->tzd); goto error_cleanup; } @@ -380,10 +388,16 @@ static int intel_pch_thermal_resume(struct device *device) } static struct pci_device_id intel_pch_thermal_id[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_WPT) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_SKL) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_1) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_2) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_1), + .driver_data = board_hsw, }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_2), + .driver_data = board_hsw, }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_WPT), + .driver_data = board_wpt, }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_SKL), + .driver_data = board_skl, }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_SKL_H), + .driver_data = board_skl, }, { 0, }, }; MODULE_DEVICE_TABLE(pci, intel_pch_thermal_id); From 54fa38cc2eda43599b51695f9918208e7250ac46 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:08:39 -0800 Subject: [PATCH 04/76] thermal: core: prevent zones with no types to be registered There are APIs that rely on tz->type. This patch prevent thermal zones without it to be registered. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 226b0b4aced6..ec0d15805df2 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1873,6 +1873,9 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, int passive = 0; struct thermal_governor *governor; + if (!type || strlen(type) == 0) + return ERR_PTR(-EINVAL); + if (type && strlen(type) >= THERMAL_NAME_LENGTH) return ERR_PTR(-EINVAL); @@ -1898,7 +1901,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, return ERR_PTR(result); } - strlcpy(tz->type, type ? : "", sizeof(tz->type)); + strlcpy(tz->type, type, sizeof(tz->type)); tz->ops = ops; tz->tzp = tzp; tz->device.class = &thermal_class; @@ -1918,11 +1921,9 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, } /* sys I/F */ - if (type) { - result = device_create_file(&tz->device, &dev_attr_type); - if (result) - goto unregister; - } + result = device_create_file(&tz->device, &dev_attr_type); + if (result) + goto unregister; result = device_create_file(&tz->device, &dev_attr_temp); if (result) @@ -2071,8 +2072,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) thermal_zone_device_set_polling(tz, 0); - if (tz->type[0]) - device_remove_file(&tz->device, &dev_attr_type); + device_remove_file(&tz->device, &dev_attr_type); device_remove_file(&tz->device, &dev_attr_temp); if (tz->ops->get_mode) device_remove_file(&tz->device, &dev_attr_mode); From ef1d8bff72e15854fd7db9b56d807fcad36e16d9 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:08:40 -0800 Subject: [PATCH 05/76] thermal: core: group thermal_zone DEVICE_ATTR's declarations Simply reorganize the code to have all DEVICE_ATTR's in one point in the file. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index ec0d15805df2..97f314e39b3d 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -975,7 +975,6 @@ emul_temp_store(struct device *dev, struct device_attribute *attr, return ret ? ret : count; } -static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store); static ssize_t sustainable_power_show(struct device *dev, struct device_attribute *devattr, @@ -1006,8 +1005,6 @@ sustainable_power_store(struct device *dev, struct device_attribute *devattr, return count; } -static DEVICE_ATTR(sustainable_power, S_IWUSR | S_IRUGO, sustainable_power_show, - sustainable_power_store); #define create_s32_tzp_attr(name) \ static ssize_t \ @@ -1050,6 +1047,16 @@ create_s32_tzp_attr(slope); create_s32_tzp_attr(offset); #undef create_s32_tzp_attr +static DEVICE_ATTR(type, 0444, type_show, NULL); +static DEVICE_ATTR(temp, 0444, temp_show, NULL); +static DEVICE_ATTR(mode, 0644, mode_show, mode_store); +static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store); +static DEVICE_ATTR(policy, S_IRUGO | S_IWUSR, policy_show, policy_store); +static DEVICE_ATTR(available_policies, S_IRUGO, available_policies_show, NULL); +static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store); +static DEVICE_ATTR(sustainable_power, S_IWUSR | S_IRUGO, sustainable_power_show, + sustainable_power_store); + static struct device_attribute *dev_tzp_attrs[] = { &dev_attr_sustainable_power, &dev_attr_k_po, @@ -1159,13 +1166,6 @@ int power_actor_set_power(struct thermal_cooling_device *cdev, return 0; } -static DEVICE_ATTR(type, 0444, type_show, NULL); -static DEVICE_ATTR(temp, 0444, temp_show, NULL); -static DEVICE_ATTR(mode, 0644, mode_show, mode_store); -static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store); -static DEVICE_ATTR(policy, S_IRUGO | S_IWUSR, policy_show, policy_store); -static DEVICE_ATTR(available_policies, S_IRUGO, available_policies_show, NULL); - /* sys I/F for cooling device */ #define to_cooling_device(_dev) \ container_of(_dev, struct thermal_cooling_device, device) From 308f726ac872bee72ab0fccb0ef6a75364e47496 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:08:41 -0800 Subject: [PATCH 06/76] thermal: core: group device_create_file() calls that are always created Simple code reorganization to group files that are always created when registering a thermal zone. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 97f314e39b3d..3d43c297948a 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1921,14 +1921,6 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, } /* sys I/F */ - result = device_create_file(&tz->device, &dev_attr_type); - if (result) - goto unregister; - - result = device_create_file(&tz->device, &dev_attr_temp); - if (result) - goto unregister; - if (ops->get_mode) { result = device_create_file(&tz->device, &dev_attr_mode); if (result) @@ -1963,18 +1955,26 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, goto unregister; } + result = device_create_file(&tz->device, &dev_attr_type); + if (result) + goto unregister; + + result = device_create_file(&tz->device, &dev_attr_temp); + if (result) + goto unregister; + /* Create policy attribute */ result = device_create_file(&tz->device, &dev_attr_policy); if (result) goto unregister; - /* Add thermal zone params */ - result = create_tzp_attrs(&tz->device); + /* Create available_policies attribute */ + result = device_create_file(&tz->device, &dev_attr_available_policies); if (result) goto unregister; - /* Create available_policies attribute */ - result = device_create_file(&tz->device, &dev_attr_available_policies); + /* Add thermal zone params */ + result = create_tzp_attrs(&tz->device); if (result) goto unregister; From 1c600861fa6fd8891c3c6936e93288f912630f53 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:08:42 -0800 Subject: [PATCH 07/76] thermal: core: use dev.groups to manage always present tz attributes Thermal zones attributes are all being created using device_create_file(). This has the disadvantage of making the code complicated and sometimes we may miss the cleanup of them. This patch starts to move the thermal zone sysfs attributes to the dev.groups, so Linux device core manage them for us. For now, this patch only moves those attributes are always present regardless of thermal zone condition. This change has also the advantage of cleaning up the thermal zone parameters sysfs entries that are left unclean after device registration. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 87 +++++++++++++--------------------- 1 file changed, 34 insertions(+), 53 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 3d43c297948a..3da4cd3f7fb8 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1047,42 +1047,47 @@ create_s32_tzp_attr(slope); create_s32_tzp_attr(offset); #undef create_s32_tzp_attr +/* + * These are thermal zone device attributes that will always be present. + * All the attributes created for tzp (create_s32_tzp_attr) also are always + * present on the sysfs interface. + */ static DEVICE_ATTR(type, 0444, type_show, NULL); static DEVICE_ATTR(temp, 0444, temp_show, NULL); -static DEVICE_ATTR(mode, 0644, mode_show, mode_store); -static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store); static DEVICE_ATTR(policy, S_IRUGO | S_IWUSR, policy_show, policy_store); static DEVICE_ATTR(available_policies, S_IRUGO, available_policies_show, NULL); -static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store); static DEVICE_ATTR(sustainable_power, S_IWUSR | S_IRUGO, sustainable_power_show, sustainable_power_store); -static struct device_attribute *dev_tzp_attrs[] = { - &dev_attr_sustainable_power, - &dev_attr_k_po, - &dev_attr_k_pu, - &dev_attr_k_i, - &dev_attr_k_d, - &dev_attr_integral_cutoff, - &dev_attr_slope, - &dev_attr_offset, +/* These thermal zone device attributes are created based on conditions */ +static DEVICE_ATTR(mode, 0644, mode_show, mode_store); +static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store); +static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store); + +static struct attribute *thermal_zone_dev_attrs[] = { + &dev_attr_type.attr, + &dev_attr_temp.attr, + &dev_attr_policy.attr, + &dev_attr_available_policies.attr, + &dev_attr_sustainable_power.attr, + &dev_attr_k_po.attr, + &dev_attr_k_pu.attr, + &dev_attr_k_i.attr, + &dev_attr_k_d.attr, + &dev_attr_integral_cutoff.attr, + &dev_attr_slope.attr, + &dev_attr_offset.attr, + NULL, }; -static int create_tzp_attrs(struct device *dev) -{ - int i; +static struct attribute_group thermal_zone_attribute_group = { + .attrs = thermal_zone_dev_attrs, +}; - for (i = 0; i < ARRAY_SIZE(dev_tzp_attrs); i++) { - int ret; - struct device_attribute *dev_attr = dev_tzp_attrs[i]; - - ret = device_create_file(dev, dev_attr); - if (ret) - return ret; - } - - return 0; -} +static const struct attribute_group *thermal_zone_attribute_groups[] = { + &thermal_zone_attribute_group, + NULL +}; /** * power_actor_get_max_power() - get the maximum power that a cdev can consume @@ -1909,6 +1914,9 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, tz->trips = trips; tz->passive_delay = passive_delay; tz->polling_delay = polling_delay; + + /* Add nodes that are always present via .groups */ + tz->device.groups = thermal_zone_attribute_groups; /* A new thermal zone needs to be updated anyway. */ atomic_set(&tz->need_update, 1); @@ -1955,29 +1963,6 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, goto unregister; } - result = device_create_file(&tz->device, &dev_attr_type); - if (result) - goto unregister; - - result = device_create_file(&tz->device, &dev_attr_temp); - if (result) - goto unregister; - - /* Create policy attribute */ - result = device_create_file(&tz->device, &dev_attr_policy); - if (result) - goto unregister; - - /* Create available_policies attribute */ - result = device_create_file(&tz->device, &dev_attr_available_policies); - if (result) - goto unregister; - - /* Add thermal zone params */ - result = create_tzp_attrs(&tz->device); - if (result) - goto unregister; - /* Update 'this' zone's governor information */ mutex_lock(&thermal_governor_lock); @@ -2072,12 +2057,8 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) thermal_zone_device_set_polling(tz, 0); - device_remove_file(&tz->device, &dev_attr_type); - device_remove_file(&tz->device, &dev_attr_temp); if (tz->ops->get_mode) device_remove_file(&tz->device, &dev_attr_mode); - device_remove_file(&tz->device, &dev_attr_policy); - device_remove_file(&tz->device, &dev_attr_available_policies); remove_trip_attrs(tz); thermal_set_governor(tz, NULL); From 66e554bde9f879ea09379848acc4446effad9dfb Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:08:43 -0800 Subject: [PATCH 08/76] thermal: core: move emul_temp creation to tz->device.groups emul_temp creation is dependent on a compile time condition. Moving to tz->device.groups. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 3da4cd3f7fb8..ec15a600fbbb 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1054,6 +1054,7 @@ create_s32_tzp_attr(offset); */ static DEVICE_ATTR(type, 0444, type_show, NULL); static DEVICE_ATTR(temp, 0444, temp_show, NULL); +static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store); static DEVICE_ATTR(policy, S_IRUGO | S_IWUSR, policy_show, policy_store); static DEVICE_ATTR(available_policies, S_IRUGO, available_policies_show, NULL); static DEVICE_ATTR(sustainable_power, S_IWUSR | S_IRUGO, sustainable_power_show, @@ -1062,11 +1063,13 @@ static DEVICE_ATTR(sustainable_power, S_IWUSR | S_IRUGO, sustainable_power_show, /* These thermal zone device attributes are created based on conditions */ static DEVICE_ATTR(mode, 0644, mode_show, mode_store); static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store); -static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store); static struct attribute *thermal_zone_dev_attrs[] = { &dev_attr_type.attr, &dev_attr_temp.attr, +#if (IS_ENABLED(CONFIG_THERMAL_EMULATION)) + &dev_attr_emul_temp.attr, +#endif &dev_attr_policy.attr, &dev_attr_available_policies.attr, &dev_attr_sustainable_power.attr, @@ -1957,12 +1960,6 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, goto unregister; } - if (IS_ENABLED(CONFIG_THERMAL_EMULATION)) { - result = device_create_file(&tz->device, &dev_attr_emul_temp); - if (result) - goto unregister; - } - /* Update 'this' zone's governor information */ mutex_lock(&thermal_governor_lock); From 8baa5dae60adf95c44d52be3dae2367e3d5e9530 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:08:44 -0800 Subject: [PATCH 09/76] thermal: core: move mode attribute to tz->device.groups Moving mode attribute to tz->device.groups requires the implementation of a .is_visible() callback. The condition returned by .is_visible() of the mode attribute group is kept the same, we allow the attribute to be visible only if ops->get_mode() is set by the thermal driver. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 36 ++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index ec15a600fbbb..ec8551a83a87 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1064,6 +1064,7 @@ static DEVICE_ATTR(sustainable_power, S_IWUSR | S_IRUGO, sustainable_power_show, static DEVICE_ATTR(mode, 0644, mode_show, mode_store); static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store); +/* These attributes are unconditionally added to a thermal zone */ static struct attribute *thermal_zone_dev_attrs[] = { &dev_attr_type.attr, &dev_attr_temp.attr, @@ -1087,8 +1088,35 @@ static struct attribute_group thermal_zone_attribute_group = { .attrs = thermal_zone_dev_attrs, }; +/* We expose mode only if .get_mode is present */ +static struct attribute *thermal_zone_mode_attrs[] = { + &dev_attr_mode.attr, + NULL, +}; + +static umode_t thermal_zone_mode_is_visible(struct kobject *kobj, + struct attribute *attr, + int attrno) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct thermal_zone_device *tz; + + tz = container_of(dev, struct thermal_zone_device, device); + + if (tz->ops->get_mode) + return attr->mode; + + return 0; +} + +static struct attribute_group thermal_zone_mode_attribute_group = { + .attrs = thermal_zone_mode_attrs, + .is_visible = thermal_zone_mode_is_visible, +}; + static const struct attribute_group *thermal_zone_attribute_groups[] = { &thermal_zone_attribute_group, + &thermal_zone_mode_attribute_group, NULL }; @@ -1932,12 +1960,6 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, } /* sys I/F */ - if (ops->get_mode) { - result = device_create_file(&tz->device, &dev_attr_mode); - if (result) - goto unregister; - } - result = create_trip_attrs(tz, mask); if (result) goto unregister; @@ -2054,8 +2076,6 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) thermal_zone_device_set_polling(tz, 0); - if (tz->ops->get_mode) - device_remove_file(&tz->device, &dev_attr_mode); remove_trip_attrs(tz); thermal_set_governor(tz, NULL); From 3a836bfe9ff6342303bd2a146bcc42eebc3a259a Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:08:45 -0800 Subject: [PATCH 10/76] thermal: core: move passive attr to tz->device.groups This patch moves the passive attribute to tz->device.groups. Moving the passive attribute also requires a .is_visible() callback implementation for its attribute group. The logic behind the visibility of passive attribute is kept the same. We only expose the passive attribute if the thermal driver has exposed at least one passive trip point. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 45 +++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index ec8551a83a87..f3d9fe9d5c48 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1114,9 +1114,45 @@ static struct attribute_group thermal_zone_mode_attribute_group = { .is_visible = thermal_zone_mode_is_visible, }; +/* We expose passive only if passive trips are present */ +static struct attribute *thermal_zone_passive_attrs[] = { + &dev_attr_passive.attr, + NULL, +}; + +static umode_t thermal_zone_passive_is_visible(struct kobject *kobj, + struct attribute *attr, + int attrno) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct thermal_zone_device *tz; + enum thermal_trip_type trip_type; + int count, passive = 0; + + tz = container_of(dev, struct thermal_zone_device, device); + + for (count = 0; count < tz->trips && !passive; count++) { + tz->ops->get_trip_type(tz, count, &trip_type); + + if (trip_type == THERMAL_TRIP_PASSIVE) + passive = 1; + } + + if (!passive) + return attr->mode; + + return 0; +} + +static struct attribute_group thermal_zone_passive_attribute_group = { + .attrs = thermal_zone_passive_attrs, + .is_visible = thermal_zone_passive_is_visible, +}; + static const struct attribute_group *thermal_zone_attribute_groups[] = { &thermal_zone_attribute_group, &thermal_zone_mode_attribute_group, + &thermal_zone_passive_attribute_group, NULL }; @@ -1906,7 +1942,6 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, int trip_temp; int result; int count; - int passive = 0; struct thermal_governor *governor; if (!type || strlen(type) == 0) @@ -1967,8 +2002,6 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, for (count = 0; count < trips; count++) { if (tz->ops->get_trip_type(tz, count, &trip_type)) set_bit(count, &tz->trips_disabled); - if (trip_type == THERMAL_TRIP_PASSIVE) - passive = 1; if (tz->ops->get_trip_temp(tz, count, &trip_temp)) set_bit(count, &tz->trips_disabled); /* Check for bogus trip points */ @@ -1976,12 +2009,6 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, set_bit(count, &tz->trips_disabled); } - if (!passive) { - result = device_create_file(&tz->device, &dev_attr_passive); - if (result) - goto unregister; - } - /* Update 'this' zone's governor information */ mutex_lock(&thermal_governor_lock); From 059386f43ef96bd61af40715aff757b727c3105b Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:08:46 -0800 Subject: [PATCH 11/76] thermal: core: improve power actor documentation Simple improvement on clarity and removal of checkpatch warning in the documentation of power actor kernel doc. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index f3d9fe9d5c48..3d2adfac9578 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1206,12 +1206,13 @@ int power_actor_get_min_power(struct thermal_cooling_device *cdev, } /** - * power_actor_set_power() - limit the maximum power that a cooling device can consume + * power_actor_set_power() - limit the maximum power a cooling device consumes * @cdev: pointer to &thermal_cooling_device * @instance: thermal instance to update * @power: the power in milliwatts * - * Set the cooling device to consume at most @power milliwatts. + * Set the cooling device to consume at most @power milliwatts. The limit is + * expected to be a cap at the maximum power consumption. * * Return: 0 on success, -EINVAL if the cooling device does not * implement the power actor API or -E* for other failures. From 1a7e7cc03f172622792dafbbc6c93d8891fba569 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:08:47 -0800 Subject: [PATCH 12/76] thermal: core: move power actor code out of sysfs I/F section Simply reorganize code to keep only functions of sysfs interface of thermal zone device together. Therefore, move the power actor code out of the sysfs I/F section. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 166 ++++++++++++++++----------------- 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 3d2adfac9578..e8098f695c84 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -637,6 +637,89 @@ static void thermal_zone_device_check(struct work_struct *work) thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); } +/** + * power_actor_get_max_power() - get the maximum power that a cdev can consume + * @cdev: pointer to &thermal_cooling_device + * @tz: a valid thermal zone device pointer + * @max_power: pointer in which to store the maximum power + * + * Calculate the maximum power consumption in milliwats that the + * cooling device can currently consume and store it in @max_power. + * + * Return: 0 on success, -EINVAL if @cdev doesn't support the + * power_actor API or -E* on other error. + */ +int power_actor_get_max_power(struct thermal_cooling_device *cdev, + struct thermal_zone_device *tz, u32 *max_power) +{ + if (!cdev_is_power_actor(cdev)) + return -EINVAL; + + return cdev->ops->state2power(cdev, tz, 0, max_power); +} + +/** + * power_actor_get_min_power() - get the mainimum power that a cdev can consume + * @cdev: pointer to &thermal_cooling_device + * @tz: a valid thermal zone device pointer + * @min_power: pointer in which to store the minimum power + * + * Calculate the minimum power consumption in milliwatts that the + * cooling device can currently consume and store it in @min_power. + * + * Return: 0 on success, -EINVAL if @cdev doesn't support the + * power_actor API or -E* on other error. + */ +int power_actor_get_min_power(struct thermal_cooling_device *cdev, + struct thermal_zone_device *tz, u32 *min_power) +{ + unsigned long max_state; + int ret; + + if (!cdev_is_power_actor(cdev)) + return -EINVAL; + + ret = cdev->ops->get_max_state(cdev, &max_state); + if (ret) + return ret; + + return cdev->ops->state2power(cdev, tz, max_state, min_power); +} + +/** + * power_actor_set_power() - limit the maximum power a cooling device consumes + * @cdev: pointer to &thermal_cooling_device + * @instance: thermal instance to update + * @power: the power in milliwatts + * + * Set the cooling device to consume at most @power milliwatts. The limit is + * expected to be a cap at the maximum power consumption. + * + * Return: 0 on success, -EINVAL if the cooling device does not + * implement the power actor API or -E* for other failures. + */ +int power_actor_set_power(struct thermal_cooling_device *cdev, + struct thermal_instance *instance, u32 power) +{ + unsigned long state; + int ret; + + if (!cdev_is_power_actor(cdev)) + return -EINVAL; + + ret = cdev->ops->power2state(cdev, instance->tz, power, &state); + if (ret) + return ret; + + instance->target = state; + mutex_lock(&cdev->lock); + cdev->updated = false; + mutex_unlock(&cdev->lock); + thermal_cdev_update(cdev); + + return 0; +} + /* sys I/F for thermal zone */ #define to_thermal_zone(_dev) \ @@ -1156,89 +1239,6 @@ static const struct attribute_group *thermal_zone_attribute_groups[] = { NULL }; -/** - * power_actor_get_max_power() - get the maximum power that a cdev can consume - * @cdev: pointer to &thermal_cooling_device - * @tz: a valid thermal zone device pointer - * @max_power: pointer in which to store the maximum power - * - * Calculate the maximum power consumption in milliwats that the - * cooling device can currently consume and store it in @max_power. - * - * Return: 0 on success, -EINVAL if @cdev doesn't support the - * power_actor API or -E* on other error. - */ -int power_actor_get_max_power(struct thermal_cooling_device *cdev, - struct thermal_zone_device *tz, u32 *max_power) -{ - if (!cdev_is_power_actor(cdev)) - return -EINVAL; - - return cdev->ops->state2power(cdev, tz, 0, max_power); -} - -/** - * power_actor_get_min_power() - get the mainimum power that a cdev can consume - * @cdev: pointer to &thermal_cooling_device - * @tz: a valid thermal zone device pointer - * @min_power: pointer in which to store the minimum power - * - * Calculate the minimum power consumption in milliwatts that the - * cooling device can currently consume and store it in @min_power. - * - * Return: 0 on success, -EINVAL if @cdev doesn't support the - * power_actor API or -E* on other error. - */ -int power_actor_get_min_power(struct thermal_cooling_device *cdev, - struct thermal_zone_device *tz, u32 *min_power) -{ - unsigned long max_state; - int ret; - - if (!cdev_is_power_actor(cdev)) - return -EINVAL; - - ret = cdev->ops->get_max_state(cdev, &max_state); - if (ret) - return ret; - - return cdev->ops->state2power(cdev, tz, max_state, min_power); -} - -/** - * power_actor_set_power() - limit the maximum power a cooling device consumes - * @cdev: pointer to &thermal_cooling_device - * @instance: thermal instance to update - * @power: the power in milliwatts - * - * Set the cooling device to consume at most @power milliwatts. The limit is - * expected to be a cap at the maximum power consumption. - * - * Return: 0 on success, -EINVAL if the cooling device does not - * implement the power actor API or -E* for other failures. - */ -int power_actor_set_power(struct thermal_cooling_device *cdev, - struct thermal_instance *instance, u32 power) -{ - unsigned long state; - int ret; - - if (!cdev_is_power_actor(cdev)) - return -EINVAL; - - ret = cdev->ops->power2state(cdev, instance->tz, power, &state); - if (ret) - return ret; - - instance->target = state; - mutex_lock(&cdev->lock); - cdev->updated = false; - mutex_unlock(&cdev->lock); - thermal_cdev_update(cdev); - - return 0; -} - /* sys I/F for cooling device */ #define to_cooling_device(_dev) \ container_of(_dev, struct thermal_cooling_device, device) From 9d934fc8838b061cc02b8b7416e5defb6299dec1 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:08:48 -0800 Subject: [PATCH 13/76] thermal: core: remove useless empty line Fix style problem on create_trip_attrs(); Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index e8098f695c84..122eba77360c 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1833,7 +1833,6 @@ static int create_trip_attrs(struct thermal_zone_device *tz, int mask) } } - for (indx = 0; indx < tz->trips; indx++) { /* create trip type attribute */ snprintf(tz->trip_type_attrs[indx].name, THERMAL_NAME_LENGTH, From 3bafb5e2a683626387337eda28bbef927bce1a0d Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:08:49 -0800 Subject: [PATCH 14/76] thermal: core: fix style on remove_trip_attrs() Align to parentheses, removing checkpatch warning. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 122eba77360c..592c72bd5835 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1900,7 +1900,7 @@ static void remove_trip_attrs(struct thermal_zone_device *tz) &tz->trip_temp_attrs[indx].attr); if (tz->ops->get_trip_hyst) device_remove_file(&tz->device, - &tz->trip_hyst_attrs[indx].attr); + &tz->trip_hyst_attrs[indx].attr); } kfree(tz->trip_type_attrs); kfree(tz->trip_temp_attrs); From 0a9de81907e6dc2adaddedca91ee26a7479b3750 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:08:50 -0800 Subject: [PATCH 15/76] thermal: core: move the trip attrs to the tz sysfs I/F section Code reorganization to keep all the sysfs I/F of a thermal zone in the same section. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 216 ++++++++++++++++----------------- 1 file changed, 108 insertions(+), 108 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 592c72bd5835..e399ae67b18b 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1239,6 +1239,114 @@ static const struct attribute_group *thermal_zone_attribute_groups[] = { NULL }; +/** + * create_trip_attrs() - create attributes for trip points + * @tz: the thermal zone device + * @mask: Writeable trip point bitmap. + * + * helper function to instantiate sysfs entries for every trip + * point and its properties of a struct thermal_zone_device. + * + * Return: 0 on success, the proper error value otherwise. + */ +static int create_trip_attrs(struct thermal_zone_device *tz, int mask) +{ + int indx; + int size = sizeof(struct thermal_attr) * tz->trips; + + tz->trip_type_attrs = kzalloc(size, GFP_KERNEL); + if (!tz->trip_type_attrs) + return -ENOMEM; + + tz->trip_temp_attrs = kzalloc(size, GFP_KERNEL); + if (!tz->trip_temp_attrs) { + kfree(tz->trip_type_attrs); + return -ENOMEM; + } + + if (tz->ops->get_trip_hyst) { + tz->trip_hyst_attrs = kzalloc(size, GFP_KERNEL); + if (!tz->trip_hyst_attrs) { + kfree(tz->trip_type_attrs); + kfree(tz->trip_temp_attrs); + return -ENOMEM; + } + } + + for (indx = 0; indx < tz->trips; indx++) { + /* create trip type attribute */ + snprintf(tz->trip_type_attrs[indx].name, THERMAL_NAME_LENGTH, + "trip_point_%d_type", indx); + + sysfs_attr_init(&tz->trip_type_attrs[indx].attr.attr); + tz->trip_type_attrs[indx].attr.attr.name = + tz->trip_type_attrs[indx].name; + tz->trip_type_attrs[indx].attr.attr.mode = S_IRUGO; + tz->trip_type_attrs[indx].attr.show = trip_point_type_show; + + device_create_file(&tz->device, + &tz->trip_type_attrs[indx].attr); + + /* create trip temp attribute */ + snprintf(tz->trip_temp_attrs[indx].name, THERMAL_NAME_LENGTH, + "trip_point_%d_temp", indx); + + sysfs_attr_init(&tz->trip_temp_attrs[indx].attr.attr); + tz->trip_temp_attrs[indx].attr.attr.name = + tz->trip_temp_attrs[indx].name; + tz->trip_temp_attrs[indx].attr.attr.mode = S_IRUGO; + tz->trip_temp_attrs[indx].attr.show = trip_point_temp_show; + if (IS_ENABLED(CONFIG_THERMAL_WRITABLE_TRIPS) && + mask & (1 << indx)) { + tz->trip_temp_attrs[indx].attr.attr.mode |= S_IWUSR; + tz->trip_temp_attrs[indx].attr.store = + trip_point_temp_store; + } + + device_create_file(&tz->device, + &tz->trip_temp_attrs[indx].attr); + + /* create Optional trip hyst attribute */ + if (!tz->ops->get_trip_hyst) + continue; + snprintf(tz->trip_hyst_attrs[indx].name, THERMAL_NAME_LENGTH, + "trip_point_%d_hyst", indx); + + sysfs_attr_init(&tz->trip_hyst_attrs[indx].attr.attr); + tz->trip_hyst_attrs[indx].attr.attr.name = + tz->trip_hyst_attrs[indx].name; + tz->trip_hyst_attrs[indx].attr.attr.mode = S_IRUGO; + tz->trip_hyst_attrs[indx].attr.show = trip_point_hyst_show; + if (tz->ops->set_trip_hyst) { + tz->trip_hyst_attrs[indx].attr.attr.mode |= S_IWUSR; + tz->trip_hyst_attrs[indx].attr.store = + trip_point_hyst_store; + } + + device_create_file(&tz->device, + &tz->trip_hyst_attrs[indx].attr); + } + return 0; +} + +static void remove_trip_attrs(struct thermal_zone_device *tz) +{ + int indx; + + for (indx = 0; indx < tz->trips; indx++) { + device_remove_file(&tz->device, + &tz->trip_type_attrs[indx].attr); + device_remove_file(&tz->device, + &tz->trip_temp_attrs[indx].attr); + if (tz->ops->get_trip_hyst) + device_remove_file(&tz->device, + &tz->trip_hyst_attrs[indx].attr); + } + kfree(tz->trip_type_attrs); + kfree(tz->trip_temp_attrs); + kfree(tz->trip_hyst_attrs); +} + /* sys I/F for cooling device */ #define to_cooling_device(_dev) \ container_of(_dev, struct thermal_cooling_device, device) @@ -1799,114 +1907,6 @@ void thermal_notify_framework(struct thermal_zone_device *tz, int trip) } EXPORT_SYMBOL_GPL(thermal_notify_framework); -/** - * create_trip_attrs() - create attributes for trip points - * @tz: the thermal zone device - * @mask: Writeable trip point bitmap. - * - * helper function to instantiate sysfs entries for every trip - * point and its properties of a struct thermal_zone_device. - * - * Return: 0 on success, the proper error value otherwise. - */ -static int create_trip_attrs(struct thermal_zone_device *tz, int mask) -{ - int indx; - int size = sizeof(struct thermal_attr) * tz->trips; - - tz->trip_type_attrs = kzalloc(size, GFP_KERNEL); - if (!tz->trip_type_attrs) - return -ENOMEM; - - tz->trip_temp_attrs = kzalloc(size, GFP_KERNEL); - if (!tz->trip_temp_attrs) { - kfree(tz->trip_type_attrs); - return -ENOMEM; - } - - if (tz->ops->get_trip_hyst) { - tz->trip_hyst_attrs = kzalloc(size, GFP_KERNEL); - if (!tz->trip_hyst_attrs) { - kfree(tz->trip_type_attrs); - kfree(tz->trip_temp_attrs); - return -ENOMEM; - } - } - - for (indx = 0; indx < tz->trips; indx++) { - /* create trip type attribute */ - snprintf(tz->trip_type_attrs[indx].name, THERMAL_NAME_LENGTH, - "trip_point_%d_type", indx); - - sysfs_attr_init(&tz->trip_type_attrs[indx].attr.attr); - tz->trip_type_attrs[indx].attr.attr.name = - tz->trip_type_attrs[indx].name; - tz->trip_type_attrs[indx].attr.attr.mode = S_IRUGO; - tz->trip_type_attrs[indx].attr.show = trip_point_type_show; - - device_create_file(&tz->device, - &tz->trip_type_attrs[indx].attr); - - /* create trip temp attribute */ - snprintf(tz->trip_temp_attrs[indx].name, THERMAL_NAME_LENGTH, - "trip_point_%d_temp", indx); - - sysfs_attr_init(&tz->trip_temp_attrs[indx].attr.attr); - tz->trip_temp_attrs[indx].attr.attr.name = - tz->trip_temp_attrs[indx].name; - tz->trip_temp_attrs[indx].attr.attr.mode = S_IRUGO; - tz->trip_temp_attrs[indx].attr.show = trip_point_temp_show; - if (IS_ENABLED(CONFIG_THERMAL_WRITABLE_TRIPS) && - mask & (1 << indx)) { - tz->trip_temp_attrs[indx].attr.attr.mode |= S_IWUSR; - tz->trip_temp_attrs[indx].attr.store = - trip_point_temp_store; - } - - device_create_file(&tz->device, - &tz->trip_temp_attrs[indx].attr); - - /* create Optional trip hyst attribute */ - if (!tz->ops->get_trip_hyst) - continue; - snprintf(tz->trip_hyst_attrs[indx].name, THERMAL_NAME_LENGTH, - "trip_point_%d_hyst", indx); - - sysfs_attr_init(&tz->trip_hyst_attrs[indx].attr.attr); - tz->trip_hyst_attrs[indx].attr.attr.name = - tz->trip_hyst_attrs[indx].name; - tz->trip_hyst_attrs[indx].attr.attr.mode = S_IRUGO; - tz->trip_hyst_attrs[indx].attr.show = trip_point_hyst_show; - if (tz->ops->set_trip_hyst) { - tz->trip_hyst_attrs[indx].attr.attr.mode |= S_IWUSR; - tz->trip_hyst_attrs[indx].attr.store = - trip_point_hyst_store; - } - - device_create_file(&tz->device, - &tz->trip_hyst_attrs[indx].attr); - } - return 0; -} - -static void remove_trip_attrs(struct thermal_zone_device *tz) -{ - int indx; - - for (indx = 0; indx < tz->trips; indx++) { - device_remove_file(&tz->device, - &tz->trip_type_attrs[indx].attr); - device_remove_file(&tz->device, - &tz->trip_temp_attrs[indx].attr); - if (tz->ops->get_trip_hyst) - device_remove_file(&tz->device, - &tz->trip_hyst_attrs[indx].attr); - } - kfree(tz->trip_type_attrs); - kfree(tz->trip_temp_attrs); - kfree(tz->trip_hyst_attrs); -} - /** * thermal_zone_device_register() - register a new thermal zone device * @type: the thermal zone device type From e161aefb9a5fff038054231ffb80abbc2aa42238 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:08:51 -0800 Subject: [PATCH 16/76] thermal: core: create tz->device.groups dynamically This is a patch to allow adding groups created dynamically. For now we create only the existing group. However, this is a preparation to allow creating trip groups, which are determined only when the number of trips are known at runtime. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index e399ae67b18b..971f033368b2 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1236,7 +1236,7 @@ static const struct attribute_group *thermal_zone_attribute_groups[] = { &thermal_zone_attribute_group, &thermal_zone_mode_attribute_group, &thermal_zone_passive_attribute_group, - NULL + /* This is not NULL terminated as we create the group dynamically */ }; /** @@ -1347,6 +1347,25 @@ static void remove_trip_attrs(struct thermal_zone_device *tz) kfree(tz->trip_hyst_attrs); } +static int thermal_zone_create_device_groups(struct thermal_zone_device *tz) +{ + const struct attribute_group **groups; + int i, size; + + size = ARRAY_SIZE(thermal_zone_attribute_groups) + 1; + /* This also takes care of API requirement to be NULL terminated */ + groups = kcalloc(size, sizeof(*groups), GFP_KERNEL); + if (!groups) + return -ENOMEM; + + for (i = 0; i < size - 1; i++) + groups[i] = thermal_zone_attribute_groups[i]; + + tz->device.groups = groups; + + return 0; +} + /* sys I/F for cooling device */ #define to_cooling_device(_dev) \ container_of(_dev, struct thermal_cooling_device, device) @@ -1982,7 +2001,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, tz->polling_delay = polling_delay; /* Add nodes that are always present via .groups */ - tz->device.groups = thermal_zone_attribute_groups; + thermal_zone_create_device_groups(tz); /* A new thermal zone needs to be updated anyway. */ atomic_set(&tz->need_update, 1); @@ -2111,7 +2130,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) idr_destroy(&tz->idr); mutex_destroy(&tz->lock); device_unregister(&tz->device); - return; + kfree(tz->device.groups); } EXPORT_SYMBOL_GPL(thermal_zone_device_unregister); From 4d0fe7490d7f4d61b582acbae718328284f151b9 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:08:52 -0800 Subject: [PATCH 17/76] thermal: core: move trips attributes to tz->device.groups Finally, move the last thermal zone sysfs attributes to tz->device.groups: trips attributes. This requires adding a attribute_group to thermal_zone_device, creating it dynamically, and then setting all trips attributes in it. The trips attribute is then added to the tz->device.groups. As the removal of all attributes are handled by device core, the device remove calls are not needed anymore. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 88 +++++++++++++++++++--------------- include/linux/thermal.h | 2 + 2 files changed, 51 insertions(+), 39 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 971f033368b2..b1d6de9185f4 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1251,8 +1251,13 @@ static const struct attribute_group *thermal_zone_attribute_groups[] = { */ static int create_trip_attrs(struct thermal_zone_device *tz, int mask) { - int indx; int size = sizeof(struct thermal_attr) * tz->trips; + struct attribute **attrs; + int indx; + + /* This function works only for zones with at least one trip */ + if (tz->trips <= 0) + return -EINVAL; tz->trip_type_attrs = kzalloc(size, GFP_KERNEL); if (!tz->trip_type_attrs) @@ -1273,6 +1278,15 @@ static int create_trip_attrs(struct thermal_zone_device *tz, int mask) } } + attrs = kzalloc(sizeof(*attrs) * (tz->trips * 3 + 1), GFP_KERNEL); + if (!attrs) { + kfree(tz->trip_type_attrs); + kfree(tz->trip_temp_attrs); + if (tz->ops->get_trip_hyst) + kfree(tz->trip_hyst_attrs); + return -ENOMEM; + } + for (indx = 0; indx < tz->trips; indx++) { /* create trip type attribute */ snprintf(tz->trip_type_attrs[indx].name, THERMAL_NAME_LENGTH, @@ -1283,9 +1297,7 @@ static int create_trip_attrs(struct thermal_zone_device *tz, int mask) tz->trip_type_attrs[indx].name; tz->trip_type_attrs[indx].attr.attr.mode = S_IRUGO; tz->trip_type_attrs[indx].attr.show = trip_point_type_show; - - device_create_file(&tz->device, - &tz->trip_type_attrs[indx].attr); + attrs[indx] = &tz->trip_type_attrs[indx].attr.attr; /* create trip temp attribute */ snprintf(tz->trip_temp_attrs[indx].name, THERMAL_NAME_LENGTH, @@ -1302,9 +1314,7 @@ static int create_trip_attrs(struct thermal_zone_device *tz, int mask) tz->trip_temp_attrs[indx].attr.store = trip_point_temp_store; } - - device_create_file(&tz->device, - &tz->trip_temp_attrs[indx].attr); + attrs[indx + tz->trips] = &tz->trip_temp_attrs[indx].attr.attr; /* create Optional trip hyst attribute */ if (!tz->ops->get_trip_hyst) @@ -1322,45 +1332,43 @@ static int create_trip_attrs(struct thermal_zone_device *tz, int mask) tz->trip_hyst_attrs[indx].attr.store = trip_point_hyst_store; } - - device_create_file(&tz->device, - &tz->trip_hyst_attrs[indx].attr); + attrs[indx + tz->trips * 2] = + &tz->trip_hyst_attrs[indx].attr.attr; } + attrs[tz->trips * 3] = NULL; + + tz->trips_attribute_group.attrs = attrs; + return 0; } -static void remove_trip_attrs(struct thermal_zone_device *tz) -{ - int indx; - - for (indx = 0; indx < tz->trips; indx++) { - device_remove_file(&tz->device, - &tz->trip_type_attrs[indx].attr); - device_remove_file(&tz->device, - &tz->trip_temp_attrs[indx].attr); - if (tz->ops->get_trip_hyst) - device_remove_file(&tz->device, - &tz->trip_hyst_attrs[indx].attr); - } - kfree(tz->trip_type_attrs); - kfree(tz->trip_temp_attrs); - kfree(tz->trip_hyst_attrs); -} - -static int thermal_zone_create_device_groups(struct thermal_zone_device *tz) +static int thermal_zone_create_device_groups(struct thermal_zone_device *tz, + int mask) { const struct attribute_group **groups; - int i, size; + int i, size, result; - size = ARRAY_SIZE(thermal_zone_attribute_groups) + 1; + /* we need one extra for trips and the NULL to terminate the array */ + size = ARRAY_SIZE(thermal_zone_attribute_groups) + 2; /* This also takes care of API requirement to be NULL terminated */ groups = kcalloc(size, sizeof(*groups), GFP_KERNEL); if (!groups) return -ENOMEM; - for (i = 0; i < size - 1; i++) + for (i = 0; i < size - 2; i++) groups[i] = thermal_zone_attribute_groups[i]; + if (tz->trips) { + result = create_trip_attrs(tz, mask); + if (result) { + kfree(groups); + + return result; + } + + groups[size - 2] = &tz->trips_attribute_group; + } + tz->device.groups = groups; return 0; @@ -2000,8 +2008,12 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, tz->passive_delay = passive_delay; tz->polling_delay = polling_delay; + /* sys I/F */ /* Add nodes that are always present via .groups */ - thermal_zone_create_device_groups(tz); + result = thermal_zone_create_device_groups(tz, mask); + if (result) + goto unregister; + /* A new thermal zone needs to be updated anyway. */ atomic_set(&tz->need_update, 1); @@ -2013,11 +2025,6 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, return ERR_PTR(result); } - /* sys I/F */ - result = create_trip_attrs(tz, mask); - if (result) - goto unregister; - for (count = 0; count < trips; count++) { if (tz->ops->get_trip_type(tz, count, &trip_type)) set_bit(count, &tz->trips_disabled); @@ -2122,7 +2129,10 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) thermal_zone_device_set_polling(tz, 0); - remove_trip_attrs(tz); + kfree(tz->trip_type_attrs); + kfree(tz->trip_temp_attrs); + kfree(tz->trip_hyst_attrs); + kfree(tz->trips_attribute_group.attrs); thermal_set_governor(tz, NULL); thermal_remove_hwmon_sysfs(tz); diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 511182a88e76..e275e98bdceb 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -204,6 +205,7 @@ struct thermal_zone_device { int id; char type[THERMAL_NAME_LENGTH]; struct device device; + struct attribute_group trips_attribute_group; struct thermal_attr *trip_temp_attrs; struct thermal_attr *trip_type_attrs; struct thermal_attr *trip_hyst_attrs; From 2a4806bf7afd37c719b10099415318a9936a3293 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:08:53 -0800 Subject: [PATCH 18/76] thermal: core: remove unnecessary device_remove() calls Given that cdevs sysfs properties are already registered using the dev.groups, there is no need to explicitly call device_remove() for each property. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index b1d6de9185f4..6100ae70ccfb 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1876,11 +1876,6 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) mutex_unlock(&thermal_list_lock); - if (cdev->type[0]) - device_remove_file(&cdev->device, &dev_attr_cdev_type); - device_remove_file(&cdev->device, &dev_attr_max_state); - device_remove_file(&cdev->device, &dev_attr_cur_state); - release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id); device_unregister(&cdev->device); return; From 3d0055d2b252d9bbae8c064b37dd444b77f4c4c1 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:08:54 -0800 Subject: [PATCH 19/76] thermal: core: split passive_store Split passive_store between sysfs handling and thermal core internal data handling. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 62 ++++++++++++++++++++++------------ drivers/thermal/thermal_core.h | 4 +++ 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 6100ae70ccfb..4e315c979f89 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -720,6 +720,43 @@ int power_actor_set_power(struct thermal_cooling_device *cdev, return 0; } +void thermal_zone_device_rebind_exception(struct thermal_zone_device *tz, + const char *cdev_type, size_t size) +{ + struct thermal_cooling_device *cdev = NULL; + + mutex_lock(&thermal_list_lock); + list_for_each_entry(cdev, &thermal_cdev_list, node) { + /* skip non matching cdevs */ + if (strncmp(cdev_type, cdev->type, size)) + continue; + + /* re binding the exception matching the type pattern */ + thermal_zone_bind_cooling_device(tz, THERMAL_TRIPS_NONE, cdev, + THERMAL_NO_LIMIT, + THERMAL_NO_LIMIT, + THERMAL_WEIGHT_DEFAULT); + } + mutex_unlock(&thermal_list_lock); +} + +void thermal_zone_device_unbind_exception(struct thermal_zone_device *tz, + const char *cdev_type, size_t size) +{ + struct thermal_cooling_device *cdev = NULL; + + mutex_lock(&thermal_list_lock); + list_for_each_entry(cdev, &thermal_cdev_list, node) { + /* skip non matching cdevs */ + if (strncmp(cdev_type, cdev->type, size)) + continue; + /* unbinding the exception matching the type pattern */ + thermal_zone_unbind_cooling_device(tz, THERMAL_TRIPS_NONE, + cdev); + } + mutex_unlock(&thermal_list_lock); +} + /* sys I/F for thermal zone */ #define to_thermal_zone(_dev) \ @@ -922,7 +959,6 @@ passive_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct thermal_zone_device *tz = to_thermal_zone(dev); - struct thermal_cooling_device *cdev = NULL; int state; if (!sscanf(buf, "%d\n", &state)) @@ -935,30 +971,14 @@ passive_store(struct device *dev, struct device_attribute *attr, return -EINVAL; if (state && !tz->forced_passive) { - mutex_lock(&thermal_list_lock); - list_for_each_entry(cdev, &thermal_cdev_list, node) { - if (!strncmp("Processor", cdev->type, - sizeof("Processor"))) - thermal_zone_bind_cooling_device(tz, - THERMAL_TRIPS_NONE, cdev, - THERMAL_NO_LIMIT, - THERMAL_NO_LIMIT, - THERMAL_WEIGHT_DEFAULT); - } - mutex_unlock(&thermal_list_lock); if (!tz->passive_delay) tz->passive_delay = 1000; + thermal_zone_device_rebind_exception(tz, "Processor", + sizeof("Processor")); } else if (!state && tz->forced_passive) { - mutex_lock(&thermal_list_lock); - list_for_each_entry(cdev, &thermal_cdev_list, node) { - if (!strncmp("Processor", cdev->type, - sizeof("Processor"))) - thermal_zone_unbind_cooling_device(tz, - THERMAL_TRIPS_NONE, - cdev); - } - mutex_unlock(&thermal_list_lock); tz->passive_delay = 0; + thermal_zone_device_unbind_exception(tz, "Processor", + sizeof("Processor")); } tz->forced_passive = state; diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 749d41abfbab..a765c753c62c 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -56,6 +56,10 @@ struct thermal_instance { int thermal_register_governor(struct thermal_governor *); void thermal_unregister_governor(struct thermal_governor *); +void thermal_zone_device_rebind_exception(struct thermal_zone_device *, + const char *, size_t); +void thermal_zone_device_unbind_exception(struct thermal_zone_device *, + const char *, size_t); #ifdef CONFIG_THERMAL_GOV_STEP_WISE int thermal_gov_step_wise_register(void); From 6b885202d7c17157a44d5cb42793f795d6d7a58e Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:08:55 -0800 Subject: [PATCH 20/76] thermal: core: split policy_store Similarly to passive_store, policy_store now is split between thermal core data structure handling and sysfs handling. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 37 ++++++++++++++++++++++------------ drivers/thermal/thermal_core.h | 1 + 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 4e315c979f89..23f138f7eb68 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -757,6 +757,28 @@ void thermal_zone_device_unbind_exception(struct thermal_zone_device *tz, mutex_unlock(&thermal_list_lock); } +int thermal_zone_device_set_policy(struct thermal_zone_device *tz, + char *policy) +{ + struct thermal_governor *gov; + int ret = -EINVAL; + + mutex_lock(&thermal_governor_lock); + mutex_lock(&tz->lock); + + gov = __find_governor(strim(policy)); + if (!gov) + goto exit; + + ret = thermal_set_governor(tz, gov); + +exit: + mutex_unlock(&tz->lock); + mutex_unlock(&thermal_governor_lock); + + return ret; +} + /* sys I/F for thermal zone */ #define to_thermal_zone(_dev) \ @@ -1001,27 +1023,16 @@ static ssize_t policy_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int ret = -EINVAL; struct thermal_zone_device *tz = to_thermal_zone(dev); - struct thermal_governor *gov; char name[THERMAL_NAME_LENGTH]; + int ret; snprintf(name, sizeof(name), "%s", buf); - mutex_lock(&thermal_governor_lock); - mutex_lock(&tz->lock); - - gov = __find_governor(strim(name)); - if (!gov) - goto exit; - - ret = thermal_set_governor(tz, gov); + ret = thermal_zone_device_set_policy(tz, name); if (!ret) ret = count; -exit: - mutex_unlock(&tz->lock); - mutex_unlock(&thermal_governor_lock); return ret; } diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index a765c753c62c..db65d3b08cf7 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -60,6 +60,7 @@ void thermal_zone_device_rebind_exception(struct thermal_zone_device *, const char *, size_t); void thermal_zone_device_unbind_exception(struct thermal_zone_device *, const char *, size_t); +int thermal_zone_device_set_policy(struct thermal_zone_device *, char *); #ifdef CONFIG_THERMAL_GOV_STEP_WISE int thermal_gov_step_wise_register(void); From 97d2423bd9738bb75cd716d01c4c4e1d6fae8441 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:08:56 -0800 Subject: [PATCH 21/76] thermal: core: split available_policies_show() This patch creates a helper to build a list of available governors. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 35 +++++++++++++++++++--------------- drivers/thermal/thermal_core.h | 1 + 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 23f138f7eb68..3d5118473eb0 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -779,6 +779,25 @@ exit: return ret; } +int thermal_build_list_of_policies(char *buf) +{ + struct thermal_governor *pos; + ssize_t count = 0; + ssize_t size = PAGE_SIZE; + + mutex_lock(&thermal_governor_lock); + + list_for_each_entry(pos, &thermal_governor_list, governor_list) { + size = PAGE_SIZE - count; + count += scnprintf(buf + count, size, "%s ", pos->name); + } + count += scnprintf(buf + count, size, "\n"); + + mutex_unlock(&thermal_governor_lock); + + return count; +} + /* sys I/F for thermal zone */ #define to_thermal_zone(_dev) \ @@ -1048,21 +1067,7 @@ static ssize_t available_policies_show(struct device *dev, struct device_attribute *devattr, char *buf) { - struct thermal_governor *pos; - ssize_t count = 0; - ssize_t size = PAGE_SIZE; - - mutex_lock(&thermal_governor_lock); - - list_for_each_entry(pos, &thermal_governor_list, governor_list) { - size = PAGE_SIZE - count; - count += scnprintf(buf + count, size, "%s ", pos->name); - } - count += scnprintf(buf + count, size, "\n"); - - mutex_unlock(&thermal_governor_lock); - - return count; + return thermal_build_list_of_policies(buf); } static ssize_t diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index db65d3b08cf7..8994d2a379c3 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -61,6 +61,7 @@ void thermal_zone_device_rebind_exception(struct thermal_zone_device *, void thermal_zone_device_unbind_exception(struct thermal_zone_device *, const char *, size_t); int thermal_zone_device_set_policy(struct thermal_zone_device *, char *); +int thermal_build_list_of_policies(char *buf); #ifdef CONFIG_THERMAL_GOV_STEP_WISE int thermal_gov_step_wise_register(void); From ba78da443bd5cae5079d2b1e8174d3a3cb040adb Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:08:57 -0800 Subject: [PATCH 22/76] thermal: core: move to_thermal_zone() macro to header file Simply making this macro available to other thermal core files. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 3 --- drivers/thermal/thermal_core.h | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 3d5118473eb0..f5b519a9f567 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -800,9 +800,6 @@ int thermal_build_list_of_policies(char *buf) /* sys I/F for thermal zone */ -#define to_thermal_zone(_dev) \ - container_of(_dev, struct thermal_zone_device, device) - static ssize_t type_show(struct device *dev, struct device_attribute *attr, char *buf) { diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 8994d2a379c3..097abee34633 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -54,6 +54,9 @@ struct thermal_instance { unsigned int weight; /* The weight of the cooling device */ }; +#define to_thermal_zone(_dev) \ + container_of(_dev, struct thermal_zone_device, device) + int thermal_register_governor(struct thermal_governor *); void thermal_unregister_governor(struct thermal_governor *); void thermal_zone_device_rebind_exception(struct thermal_zone_device *, From 09544da9e8255ef0f3aafd1e7da067dd2b521b40 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:08:58 -0800 Subject: [PATCH 23/76] thermal: core: treat correctly the return value of *scanf calls This patch checks the return value of all calls to *scanf. The check is to simply match the number of expect inputs. The current code does not do any recovery in case the number of treated inputs are different than the expected. Therefore, keeping the same behavior. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index f5b519a9f567..e2c4fc4904d6 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -874,7 +874,7 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr, if (!tz->ops->get_trip_type) return -EPERM; - if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip)) + if (sscanf(attr->attr.name, "trip_point_%d_type", &trip) != 1) return -EINVAL; result = tz->ops->get_trip_type(tz, trip, &type); @@ -906,7 +906,7 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr, if (!tz->ops->set_trip_temp) return -EPERM; - if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip)) + if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip) != 1) return -EINVAL; if (kstrtoint(buf, 10, &temperature)) @@ -932,7 +932,7 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr, if (!tz->ops->get_trip_temp) return -EPERM; - if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip)) + if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip) != 1) return -EINVAL; ret = tz->ops->get_trip_temp(tz, trip, &temperature); @@ -954,7 +954,7 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr, if (!tz->ops->set_trip_hyst) return -EPERM; - if (!sscanf(attr->attr.name, "trip_point_%d_hyst", &trip)) + if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip) != 1) return -EINVAL; if (kstrtoint(buf, 10, &temperature)) @@ -984,7 +984,7 @@ trip_point_hyst_show(struct device *dev, struct device_attribute *attr, if (!tz->ops->get_trip_hyst) return -EPERM; - if (!sscanf(attr->attr.name, "trip_point_%d_hyst", &trip)) + if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip) != 1) return -EINVAL; ret = tz->ops->get_trip_hyst(tz, trip, &temperature); @@ -999,7 +999,7 @@ passive_store(struct device *dev, struct device_attribute *attr, struct thermal_zone_device *tz = to_thermal_zone(dev); int state; - if (!sscanf(buf, "%d\n", &state)) + if (sscanf(buf, "%d\n", &state) != 1) return -EINVAL; /* sanity check: values below 1000 millicelcius don't make sense @@ -1457,7 +1457,7 @@ thermal_cooling_device_cur_state_store(struct device *dev, unsigned long state; int result; - if (!sscanf(buf, "%ld\n", &state)) + if (sscanf(buf, "%ld\n", &state) != 1) return -EINVAL; if ((long)state < 0) From 72afe8e54974823454e4c10d4b29e486ea79f6b7 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:08:59 -0800 Subject: [PATCH 24/76] thermal: core: match parenthesis on code alignment Cosmetic change in the sysfs handling functions, as recommended by checkpatch.pl. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index e2c4fc4904d6..f66c19c7f201 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -897,7 +897,7 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr, static ssize_t trip_point_temp_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { struct thermal_zone_device *tz = to_thermal_zone(dev); int trip, ret; @@ -945,7 +945,7 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr, static ssize_t trip_point_hyst_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { struct thermal_zone_device *tz = to_thermal_zone(dev); int trip, ret; @@ -975,7 +975,7 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr, static ssize_t trip_point_hyst_show(struct device *dev, struct device_attribute *attr, - char *buf) + char *buf) { struct thermal_zone_device *tz = to_thermal_zone(dev); int trip, ret; @@ -994,7 +994,7 @@ trip_point_hyst_show(struct device *dev, struct device_attribute *attr, static ssize_t passive_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { struct thermal_zone_device *tz = to_thermal_zone(dev); int state; @@ -1028,7 +1028,7 @@ passive_store(struct device *dev, struct device_attribute *attr, static ssize_t passive_show(struct device *dev, struct device_attribute *attr, - char *buf) + char *buf) { struct thermal_zone_device *tz = to_thermal_zone(dev); @@ -1037,7 +1037,7 @@ passive_show(struct device *dev, struct device_attribute *attr, static ssize_t policy_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { struct thermal_zone_device *tz = to_thermal_zone(dev); char name[THERMAL_NAME_LENGTH]; @@ -1069,7 +1069,7 @@ available_policies_show(struct device *dev, struct device_attribute *devattr, static ssize_t emul_temp_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { struct thermal_zone_device *tz = to_thermal_zone(dev); int ret = 0; From a369ee88f7f6059a582087dc8bd9fc42b2731f5b Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:00 -0800 Subject: [PATCH 25/76] thermal: core: move thermal_zone sysfs to thermal_sysfs.c This is a code reorganization, simply to concentrate the code handling sysfs in a specific file: thermal_sysfs.c. Right now, moving only the sysfs entries of thermal_zone_device. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/Makefile | 2 +- drivers/thermal/thermal_core.c | 609 ------------------------------ drivers/thermal/thermal_core.h | 3 + drivers/thermal/thermal_sysfs.c | 633 ++++++++++++++++++++++++++++++++ 4 files changed, 637 insertions(+), 610 deletions(-) create mode 100644 drivers/thermal/thermal_sysfs.c diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index c92eb22a41ff..767c3c9aeeea 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -3,7 +3,7 @@ # obj-$(CONFIG_THERMAL) += thermal_sys.o -thermal_sys-y += thermal_core.o +thermal_sys-y += thermal_core.o thermal_sysfs.o # interface to/from other layers providing sensors thermal_sys-$(CONFIG_THERMAL_HWMON) += thermal_hwmon.o diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index f66c19c7f201..b14913ea5544 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -798,615 +798,6 @@ int thermal_build_list_of_policies(char *buf) return count; } -/* sys I/F for thermal zone */ - -static ssize_t -type_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - - return sprintf(buf, "%s\n", tz->type); -} - -static ssize_t -temp_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - int temperature, ret; - - ret = thermal_zone_get_temp(tz, &temperature); - - if (ret) - return ret; - - return sprintf(buf, "%d\n", temperature); -} - -static ssize_t -mode_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - enum thermal_device_mode mode; - int result; - - if (!tz->ops->get_mode) - return -EPERM; - - result = tz->ops->get_mode(tz, &mode); - if (result) - return result; - - return sprintf(buf, "%s\n", mode == THERMAL_DEVICE_ENABLED ? "enabled" - : "disabled"); -} - -static ssize_t -mode_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - int result; - - if (!tz->ops->set_mode) - return -EPERM; - - if (!strncmp(buf, "enabled", sizeof("enabled") - 1)) - result = tz->ops->set_mode(tz, THERMAL_DEVICE_ENABLED); - else if (!strncmp(buf, "disabled", sizeof("disabled") - 1)) - result = tz->ops->set_mode(tz, THERMAL_DEVICE_DISABLED); - else - result = -EINVAL; - - if (result) - return result; - - return count; -} - -static ssize_t -trip_point_type_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - enum thermal_trip_type type; - int trip, result; - - if (!tz->ops->get_trip_type) - return -EPERM; - - if (sscanf(attr->attr.name, "trip_point_%d_type", &trip) != 1) - return -EINVAL; - - result = tz->ops->get_trip_type(tz, trip, &type); - if (result) - return result; - - switch (type) { - case THERMAL_TRIP_CRITICAL: - return sprintf(buf, "critical\n"); - case THERMAL_TRIP_HOT: - return sprintf(buf, "hot\n"); - case THERMAL_TRIP_PASSIVE: - return sprintf(buf, "passive\n"); - case THERMAL_TRIP_ACTIVE: - return sprintf(buf, "active\n"); - default: - return sprintf(buf, "unknown\n"); - } -} - -static ssize_t -trip_point_temp_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - int trip, ret; - int temperature; - - if (!tz->ops->set_trip_temp) - return -EPERM; - - if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip) != 1) - return -EINVAL; - - if (kstrtoint(buf, 10, &temperature)) - return -EINVAL; - - ret = tz->ops->set_trip_temp(tz, trip, temperature); - if (ret) - return ret; - - thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); - - return count; -} - -static ssize_t -trip_point_temp_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - int trip, ret; - int temperature; - - if (!tz->ops->get_trip_temp) - return -EPERM; - - if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip) != 1) - return -EINVAL; - - ret = tz->ops->get_trip_temp(tz, trip, &temperature); - - if (ret) - return ret; - - return sprintf(buf, "%d\n", temperature); -} - -static ssize_t -trip_point_hyst_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - int trip, ret; - int temperature; - - if (!tz->ops->set_trip_hyst) - return -EPERM; - - if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip) != 1) - return -EINVAL; - - if (kstrtoint(buf, 10, &temperature)) - return -EINVAL; - - /* - * We are not doing any check on the 'temperature' value - * here. The driver implementing 'set_trip_hyst' has to - * take care of this. - */ - ret = tz->ops->set_trip_hyst(tz, trip, temperature); - - if (!ret) - thermal_zone_set_trips(tz); - - return ret ? ret : count; -} - -static ssize_t -trip_point_hyst_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - int trip, ret; - int temperature; - - if (!tz->ops->get_trip_hyst) - return -EPERM; - - if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip) != 1) - return -EINVAL; - - ret = tz->ops->get_trip_hyst(tz, trip, &temperature); - - return ret ? ret : sprintf(buf, "%d\n", temperature); -} - -static ssize_t -passive_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - int state; - - if (sscanf(buf, "%d\n", &state) != 1) - return -EINVAL; - - /* sanity check: values below 1000 millicelcius don't make sense - * and can cause the system to go into a thermal heart attack - */ - if (state && state < 1000) - return -EINVAL; - - if (state && !tz->forced_passive) { - if (!tz->passive_delay) - tz->passive_delay = 1000; - thermal_zone_device_rebind_exception(tz, "Processor", - sizeof("Processor")); - } else if (!state && tz->forced_passive) { - tz->passive_delay = 0; - thermal_zone_device_unbind_exception(tz, "Processor", - sizeof("Processor")); - } - - tz->forced_passive = state; - - thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); - - return count; -} - -static ssize_t -passive_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - - return sprintf(buf, "%d\n", tz->forced_passive); -} - -static ssize_t -policy_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - char name[THERMAL_NAME_LENGTH]; - int ret; - - snprintf(name, sizeof(name), "%s", buf); - - ret = thermal_zone_device_set_policy(tz, name); - if (!ret) - ret = count; - - return ret; -} - -static ssize_t -policy_show(struct device *dev, struct device_attribute *devattr, char *buf) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - - return sprintf(buf, "%s\n", tz->governor->name); -} - -static ssize_t -available_policies_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - return thermal_build_list_of_policies(buf); -} - -static ssize_t -emul_temp_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - int ret = 0; - int temperature; - - if (kstrtoint(buf, 10, &temperature)) - return -EINVAL; - - if (!tz->ops->set_emul_temp) { - mutex_lock(&tz->lock); - tz->emul_temperature = temperature; - mutex_unlock(&tz->lock); - } else { - ret = tz->ops->set_emul_temp(tz, temperature); - } - - if (!ret) - thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); - - return ret ? ret : count; -} - -static ssize_t -sustainable_power_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - - if (tz->tzp) - return sprintf(buf, "%u\n", tz->tzp->sustainable_power); - else - return -EIO; -} - -static ssize_t -sustainable_power_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - u32 sustainable_power; - - if (!tz->tzp) - return -EIO; - - if (kstrtou32(buf, 10, &sustainable_power)) - return -EINVAL; - - tz->tzp->sustainable_power = sustainable_power; - - return count; -} - -#define create_s32_tzp_attr(name) \ - static ssize_t \ - name##_show(struct device *dev, struct device_attribute *devattr, \ - char *buf) \ - { \ - struct thermal_zone_device *tz = to_thermal_zone(dev); \ - \ - if (tz->tzp) \ - return sprintf(buf, "%d\n", tz->tzp->name); \ - else \ - return -EIO; \ - } \ - \ - static ssize_t \ - name##_store(struct device *dev, struct device_attribute *devattr, \ - const char *buf, size_t count) \ - { \ - struct thermal_zone_device *tz = to_thermal_zone(dev); \ - s32 value; \ - \ - if (!tz->tzp) \ - return -EIO; \ - \ - if (kstrtos32(buf, 10, &value)) \ - return -EINVAL; \ - \ - tz->tzp->name = value; \ - \ - return count; \ - } \ - static DEVICE_ATTR(name, S_IWUSR | S_IRUGO, name##_show, name##_store) - -create_s32_tzp_attr(k_po); -create_s32_tzp_attr(k_pu); -create_s32_tzp_attr(k_i); -create_s32_tzp_attr(k_d); -create_s32_tzp_attr(integral_cutoff); -create_s32_tzp_attr(slope); -create_s32_tzp_attr(offset); -#undef create_s32_tzp_attr - -/* - * These are thermal zone device attributes that will always be present. - * All the attributes created for tzp (create_s32_tzp_attr) also are always - * present on the sysfs interface. - */ -static DEVICE_ATTR(type, 0444, type_show, NULL); -static DEVICE_ATTR(temp, 0444, temp_show, NULL); -static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store); -static DEVICE_ATTR(policy, S_IRUGO | S_IWUSR, policy_show, policy_store); -static DEVICE_ATTR(available_policies, S_IRUGO, available_policies_show, NULL); -static DEVICE_ATTR(sustainable_power, S_IWUSR | S_IRUGO, sustainable_power_show, - sustainable_power_store); - -/* These thermal zone device attributes are created based on conditions */ -static DEVICE_ATTR(mode, 0644, mode_show, mode_store); -static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store); - -/* These attributes are unconditionally added to a thermal zone */ -static struct attribute *thermal_zone_dev_attrs[] = { - &dev_attr_type.attr, - &dev_attr_temp.attr, -#if (IS_ENABLED(CONFIG_THERMAL_EMULATION)) - &dev_attr_emul_temp.attr, -#endif - &dev_attr_policy.attr, - &dev_attr_available_policies.attr, - &dev_attr_sustainable_power.attr, - &dev_attr_k_po.attr, - &dev_attr_k_pu.attr, - &dev_attr_k_i.attr, - &dev_attr_k_d.attr, - &dev_attr_integral_cutoff.attr, - &dev_attr_slope.attr, - &dev_attr_offset.attr, - NULL, -}; - -static struct attribute_group thermal_zone_attribute_group = { - .attrs = thermal_zone_dev_attrs, -}; - -/* We expose mode only if .get_mode is present */ -static struct attribute *thermal_zone_mode_attrs[] = { - &dev_attr_mode.attr, - NULL, -}; - -static umode_t thermal_zone_mode_is_visible(struct kobject *kobj, - struct attribute *attr, - int attrno) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct thermal_zone_device *tz; - - tz = container_of(dev, struct thermal_zone_device, device); - - if (tz->ops->get_mode) - return attr->mode; - - return 0; -} - -static struct attribute_group thermal_zone_mode_attribute_group = { - .attrs = thermal_zone_mode_attrs, - .is_visible = thermal_zone_mode_is_visible, -}; - -/* We expose passive only if passive trips are present */ -static struct attribute *thermal_zone_passive_attrs[] = { - &dev_attr_passive.attr, - NULL, -}; - -static umode_t thermal_zone_passive_is_visible(struct kobject *kobj, - struct attribute *attr, - int attrno) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct thermal_zone_device *tz; - enum thermal_trip_type trip_type; - int count, passive = 0; - - tz = container_of(dev, struct thermal_zone_device, device); - - for (count = 0; count < tz->trips && !passive; count++) { - tz->ops->get_trip_type(tz, count, &trip_type); - - if (trip_type == THERMAL_TRIP_PASSIVE) - passive = 1; - } - - if (!passive) - return attr->mode; - - return 0; -} - -static struct attribute_group thermal_zone_passive_attribute_group = { - .attrs = thermal_zone_passive_attrs, - .is_visible = thermal_zone_passive_is_visible, -}; - -static const struct attribute_group *thermal_zone_attribute_groups[] = { - &thermal_zone_attribute_group, - &thermal_zone_mode_attribute_group, - &thermal_zone_passive_attribute_group, - /* This is not NULL terminated as we create the group dynamically */ -}; - -/** - * create_trip_attrs() - create attributes for trip points - * @tz: the thermal zone device - * @mask: Writeable trip point bitmap. - * - * helper function to instantiate sysfs entries for every trip - * point and its properties of a struct thermal_zone_device. - * - * Return: 0 on success, the proper error value otherwise. - */ -static int create_trip_attrs(struct thermal_zone_device *tz, int mask) -{ - int size = sizeof(struct thermal_attr) * tz->trips; - struct attribute **attrs; - int indx; - - /* This function works only for zones with at least one trip */ - if (tz->trips <= 0) - return -EINVAL; - - tz->trip_type_attrs = kzalloc(size, GFP_KERNEL); - if (!tz->trip_type_attrs) - return -ENOMEM; - - tz->trip_temp_attrs = kzalloc(size, GFP_KERNEL); - if (!tz->trip_temp_attrs) { - kfree(tz->trip_type_attrs); - return -ENOMEM; - } - - if (tz->ops->get_trip_hyst) { - tz->trip_hyst_attrs = kzalloc(size, GFP_KERNEL); - if (!tz->trip_hyst_attrs) { - kfree(tz->trip_type_attrs); - kfree(tz->trip_temp_attrs); - return -ENOMEM; - } - } - - attrs = kzalloc(sizeof(*attrs) * (tz->trips * 3 + 1), GFP_KERNEL); - if (!attrs) { - kfree(tz->trip_type_attrs); - kfree(tz->trip_temp_attrs); - if (tz->ops->get_trip_hyst) - kfree(tz->trip_hyst_attrs); - return -ENOMEM; - } - - for (indx = 0; indx < tz->trips; indx++) { - /* create trip type attribute */ - snprintf(tz->trip_type_attrs[indx].name, THERMAL_NAME_LENGTH, - "trip_point_%d_type", indx); - - sysfs_attr_init(&tz->trip_type_attrs[indx].attr.attr); - tz->trip_type_attrs[indx].attr.attr.name = - tz->trip_type_attrs[indx].name; - tz->trip_type_attrs[indx].attr.attr.mode = S_IRUGO; - tz->trip_type_attrs[indx].attr.show = trip_point_type_show; - attrs[indx] = &tz->trip_type_attrs[indx].attr.attr; - - /* create trip temp attribute */ - snprintf(tz->trip_temp_attrs[indx].name, THERMAL_NAME_LENGTH, - "trip_point_%d_temp", indx); - - sysfs_attr_init(&tz->trip_temp_attrs[indx].attr.attr); - tz->trip_temp_attrs[indx].attr.attr.name = - tz->trip_temp_attrs[indx].name; - tz->trip_temp_attrs[indx].attr.attr.mode = S_IRUGO; - tz->trip_temp_attrs[indx].attr.show = trip_point_temp_show; - if (IS_ENABLED(CONFIG_THERMAL_WRITABLE_TRIPS) && - mask & (1 << indx)) { - tz->trip_temp_attrs[indx].attr.attr.mode |= S_IWUSR; - tz->trip_temp_attrs[indx].attr.store = - trip_point_temp_store; - } - attrs[indx + tz->trips] = &tz->trip_temp_attrs[indx].attr.attr; - - /* create Optional trip hyst attribute */ - if (!tz->ops->get_trip_hyst) - continue; - snprintf(tz->trip_hyst_attrs[indx].name, THERMAL_NAME_LENGTH, - "trip_point_%d_hyst", indx); - - sysfs_attr_init(&tz->trip_hyst_attrs[indx].attr.attr); - tz->trip_hyst_attrs[indx].attr.attr.name = - tz->trip_hyst_attrs[indx].name; - tz->trip_hyst_attrs[indx].attr.attr.mode = S_IRUGO; - tz->trip_hyst_attrs[indx].attr.show = trip_point_hyst_show; - if (tz->ops->set_trip_hyst) { - tz->trip_hyst_attrs[indx].attr.attr.mode |= S_IWUSR; - tz->trip_hyst_attrs[indx].attr.store = - trip_point_hyst_store; - } - attrs[indx + tz->trips * 2] = - &tz->trip_hyst_attrs[indx].attr.attr; - } - attrs[tz->trips * 3] = NULL; - - tz->trips_attribute_group.attrs = attrs; - - return 0; -} - -static int thermal_zone_create_device_groups(struct thermal_zone_device *tz, - int mask) -{ - const struct attribute_group **groups; - int i, size, result; - - /* we need one extra for trips and the NULL to terminate the array */ - size = ARRAY_SIZE(thermal_zone_attribute_groups) + 2; - /* This also takes care of API requirement to be NULL terminated */ - groups = kcalloc(size, sizeof(*groups), GFP_KERNEL); - if (!groups) - return -ENOMEM; - - for (i = 0; i < size - 2; i++) - groups[i] = thermal_zone_attribute_groups[i]; - - if (tz->trips) { - result = create_trip_attrs(tz, mask); - if (result) { - kfree(groups); - - return result; - } - - groups[size - 2] = &tz->trips_attribute_group; - } - - tz->device.groups = groups; - - return 0; -} - /* sys I/F for cooling device */ #define to_cooling_device(_dev) \ container_of(_dev, struct thermal_cooling_device, device) diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 097abee34633..bd8869333739 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -66,6 +66,9 @@ void thermal_zone_device_unbind_exception(struct thermal_zone_device *, int thermal_zone_device_set_policy(struct thermal_zone_device *, char *); int thermal_build_list_of_policies(char *buf); +/* sysfs I/F */ +int thermal_zone_create_device_groups(struct thermal_zone_device *, int); + #ifdef CONFIG_THERMAL_GOV_STEP_WISE int thermal_gov_step_wise_register(void); void thermal_gov_step_wise_unregister(void); diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c new file mode 100644 index 000000000000..8a7323f63b78 --- /dev/null +++ b/drivers/thermal/thermal_sysfs.c @@ -0,0 +1,633 @@ +/* + * thermal.c - sysfs interface of thermal devices + * + * Copyright (C) 2016 Eduardo Valentin + * + * Highly based on original thermal_core.c + * Copyright (C) 2008 Intel Corp + * Copyright (C) 2008 Zhang Rui + * Copyright (C) 2008 Sujith Thomas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#include "thermal_core.h" + +/* sys I/F for thermal zone */ + +static ssize_t +type_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + + return sprintf(buf, "%s\n", tz->type); +} + +static ssize_t +temp_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + int temperature, ret; + + ret = thermal_zone_get_temp(tz, &temperature); + + if (ret) + return ret; + + return sprintf(buf, "%d\n", temperature); +} + +static ssize_t +mode_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + enum thermal_device_mode mode; + int result; + + if (!tz->ops->get_mode) + return -EPERM; + + result = tz->ops->get_mode(tz, &mode); + if (result) + return result; + + return sprintf(buf, "%s\n", mode == THERMAL_DEVICE_ENABLED ? "enabled" + : "disabled"); +} + +static ssize_t +mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + int result; + + if (!tz->ops->set_mode) + return -EPERM; + + if (!strncmp(buf, "enabled", sizeof("enabled") - 1)) + result = tz->ops->set_mode(tz, THERMAL_DEVICE_ENABLED); + else if (!strncmp(buf, "disabled", sizeof("disabled") - 1)) + result = tz->ops->set_mode(tz, THERMAL_DEVICE_DISABLED); + else + result = -EINVAL; + + if (result) + return result; + + return count; +} + +static ssize_t +trip_point_type_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + enum thermal_trip_type type; + int trip, result; + + if (!tz->ops->get_trip_type) + return -EPERM; + + if (sscanf(attr->attr.name, "trip_point_%d_type", &trip) != 1) + return -EINVAL; + + result = tz->ops->get_trip_type(tz, trip, &type); + if (result) + return result; + + switch (type) { + case THERMAL_TRIP_CRITICAL: + return sprintf(buf, "critical\n"); + case THERMAL_TRIP_HOT: + return sprintf(buf, "hot\n"); + case THERMAL_TRIP_PASSIVE: + return sprintf(buf, "passive\n"); + case THERMAL_TRIP_ACTIVE: + return sprintf(buf, "active\n"); + default: + return sprintf(buf, "unknown\n"); + } +} + +static ssize_t +trip_point_temp_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + int trip, ret; + int temperature; + + if (!tz->ops->set_trip_temp) + return -EPERM; + + if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip) != 1) + return -EINVAL; + + if (kstrtoint(buf, 10, &temperature)) + return -EINVAL; + + ret = tz->ops->set_trip_temp(tz, trip, temperature); + if (ret) + return ret; + + thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); + + return count; +} + +static ssize_t +trip_point_temp_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + int trip, ret; + int temperature; + + if (!tz->ops->get_trip_temp) + return -EPERM; + + if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip) != 1) + return -EINVAL; + + ret = tz->ops->get_trip_temp(tz, trip, &temperature); + + if (ret) + return ret; + + return sprintf(buf, "%d\n", temperature); +} + +static ssize_t +trip_point_hyst_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + int trip, ret; + int temperature; + + if (!tz->ops->set_trip_hyst) + return -EPERM; + + if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip) != 1) + return -EINVAL; + + if (kstrtoint(buf, 10, &temperature)) + return -EINVAL; + + /* + * We are not doing any check on the 'temperature' value + * here. The driver implementing 'set_trip_hyst' has to + * take care of this. + */ + ret = tz->ops->set_trip_hyst(tz, trip, temperature); + + if (!ret) + thermal_zone_set_trips(tz); + + return ret ? ret : count; +} + +static ssize_t +trip_point_hyst_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + int trip, ret; + int temperature; + + if (!tz->ops->get_trip_hyst) + return -EPERM; + + if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip) != 1) + return -EINVAL; + + ret = tz->ops->get_trip_hyst(tz, trip, &temperature); + + return ret ? ret : sprintf(buf, "%d\n", temperature); +} + +static ssize_t +passive_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + int state; + + if (sscanf(buf, "%d\n", &state) != 1) + return -EINVAL; + + /* sanity check: values below 1000 millicelcius don't make sense + * and can cause the system to go into a thermal heart attack + */ + if (state && state < 1000) + return -EINVAL; + + if (state && !tz->forced_passive) { + if (!tz->passive_delay) + tz->passive_delay = 1000; + thermal_zone_device_rebind_exception(tz, "Processor", + sizeof("Processor")); + } else if (!state && tz->forced_passive) { + tz->passive_delay = 0; + thermal_zone_device_unbind_exception(tz, "Processor", + sizeof("Processor")); + } + + tz->forced_passive = state; + + thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); + + return count; +} + +static ssize_t +passive_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + + return sprintf(buf, "%d\n", tz->forced_passive); +} + +static ssize_t +policy_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + char name[THERMAL_NAME_LENGTH]; + int ret; + + snprintf(name, sizeof(name), "%s", buf); + + ret = thermal_zone_device_set_policy(tz, name); + if (!ret) + ret = count; + + return ret; +} + +static ssize_t +policy_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + + return sprintf(buf, "%s\n", tz->governor->name); +} + +static ssize_t +available_policies_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + return thermal_build_list_of_policies(buf); +} + +static ssize_t +emul_temp_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + int ret = 0; + int temperature; + + if (kstrtoint(buf, 10, &temperature)) + return -EINVAL; + + if (!tz->ops->set_emul_temp) { + mutex_lock(&tz->lock); + tz->emul_temperature = temperature; + mutex_unlock(&tz->lock); + } else { + ret = tz->ops->set_emul_temp(tz, temperature); + } + + if (!ret) + thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); + + return ret ? ret : count; +} + +static ssize_t +sustainable_power_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + + if (tz->tzp) + return sprintf(buf, "%u\n", tz->tzp->sustainable_power); + else + return -EIO; +} + +static ssize_t +sustainable_power_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + u32 sustainable_power; + + if (!tz->tzp) + return -EIO; + + if (kstrtou32(buf, 10, &sustainable_power)) + return -EINVAL; + + tz->tzp->sustainable_power = sustainable_power; + + return count; +} + +#define create_s32_tzp_attr(name) \ + static ssize_t \ + name##_show(struct device *dev, struct device_attribute *devattr, \ + char *buf) \ + { \ + struct thermal_zone_device *tz = to_thermal_zone(dev); \ + \ + if (tz->tzp) \ + return sprintf(buf, "%d\n", tz->tzp->name); \ + else \ + return -EIO; \ + } \ + \ + static ssize_t \ + name##_store(struct device *dev, struct device_attribute *devattr, \ + const char *buf, size_t count) \ + { \ + struct thermal_zone_device *tz = to_thermal_zone(dev); \ + s32 value; \ + \ + if (!tz->tzp) \ + return -EIO; \ + \ + if (kstrtos32(buf, 10, &value)) \ + return -EINVAL; \ + \ + tz->tzp->name = value; \ + \ + return count; \ + } \ + static DEVICE_ATTR(name, S_IWUSR | S_IRUGO, name##_show, name##_store) + +create_s32_tzp_attr(k_po); +create_s32_tzp_attr(k_pu); +create_s32_tzp_attr(k_i); +create_s32_tzp_attr(k_d); +create_s32_tzp_attr(integral_cutoff); +create_s32_tzp_attr(slope); +create_s32_tzp_attr(offset); +#undef create_s32_tzp_attr + +/* + * These are thermal zone device attributes that will always be present. + * All the attributes created for tzp (create_s32_tzp_attr) also are always + * present on the sysfs interface. + */ +static DEVICE_ATTR(type, 0444, type_show, NULL); +static DEVICE_ATTR(temp, 0444, temp_show, NULL); +static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store); +static DEVICE_ATTR(policy, S_IRUGO | S_IWUSR, policy_show, policy_store); +static DEVICE_ATTR(available_policies, S_IRUGO, available_policies_show, NULL); +static DEVICE_ATTR(sustainable_power, S_IWUSR | S_IRUGO, sustainable_power_show, + sustainable_power_store); + +/* These thermal zone device attributes are created based on conditions */ +static DEVICE_ATTR(mode, 0644, mode_show, mode_store); +static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store); + +/* These attributes are unconditionally added to a thermal zone */ +static struct attribute *thermal_zone_dev_attrs[] = { + &dev_attr_type.attr, + &dev_attr_temp.attr, +#if (IS_ENABLED(CONFIG_THERMAL_EMULATION)) + &dev_attr_emul_temp.attr, +#endif + &dev_attr_policy.attr, + &dev_attr_available_policies.attr, + &dev_attr_sustainable_power.attr, + &dev_attr_k_po.attr, + &dev_attr_k_pu.attr, + &dev_attr_k_i.attr, + &dev_attr_k_d.attr, + &dev_attr_integral_cutoff.attr, + &dev_attr_slope.attr, + &dev_attr_offset.attr, + NULL, +}; + +static struct attribute_group thermal_zone_attribute_group = { + .attrs = thermal_zone_dev_attrs, +}; + +/* We expose mode only if .get_mode is present */ +static struct attribute *thermal_zone_mode_attrs[] = { + &dev_attr_mode.attr, + NULL, +}; + +static umode_t thermal_zone_mode_is_visible(struct kobject *kobj, + struct attribute *attr, + int attrno) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct thermal_zone_device *tz; + + tz = container_of(dev, struct thermal_zone_device, device); + + if (tz->ops->get_mode) + return attr->mode; + + return 0; +} + +static struct attribute_group thermal_zone_mode_attribute_group = { + .attrs = thermal_zone_mode_attrs, + .is_visible = thermal_zone_mode_is_visible, +}; + +/* We expose passive only if passive trips are present */ +static struct attribute *thermal_zone_passive_attrs[] = { + &dev_attr_passive.attr, + NULL, +}; + +static umode_t thermal_zone_passive_is_visible(struct kobject *kobj, + struct attribute *attr, + int attrno) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct thermal_zone_device *tz; + enum thermal_trip_type trip_type; + int count, passive = 0; + + tz = container_of(dev, struct thermal_zone_device, device); + + for (count = 0; count < tz->trips && !passive; count++) { + tz->ops->get_trip_type(tz, count, &trip_type); + + if (trip_type == THERMAL_TRIP_PASSIVE) + passive = 1; + } + + if (!passive) + return attr->mode; + + return 0; +} + +static struct attribute_group thermal_zone_passive_attribute_group = { + .attrs = thermal_zone_passive_attrs, + .is_visible = thermal_zone_passive_is_visible, +}; + +static const struct attribute_group *thermal_zone_attribute_groups[] = { + &thermal_zone_attribute_group, + &thermal_zone_mode_attribute_group, + &thermal_zone_passive_attribute_group, + /* This is not NULL terminated as we create the group dynamically */ +}; + +/** + * create_trip_attrs() - create attributes for trip points + * @tz: the thermal zone device + * @mask: Writeable trip point bitmap. + * + * helper function to instantiate sysfs entries for every trip + * point and its properties of a struct thermal_zone_device. + * + * Return: 0 on success, the proper error value otherwise. + */ +static int create_trip_attrs(struct thermal_zone_device *tz, int mask) +{ + int size = sizeof(struct thermal_attr) * tz->trips; + struct attribute **attrs; + int indx; + + /* This function works only for zones with at least one trip */ + if (tz->trips <= 0) + return -EINVAL; + + tz->trip_type_attrs = kzalloc(size, GFP_KERNEL); + if (!tz->trip_type_attrs) + return -ENOMEM; + + tz->trip_temp_attrs = kzalloc(size, GFP_KERNEL); + if (!tz->trip_temp_attrs) { + kfree(tz->trip_type_attrs); + return -ENOMEM; + } + + if (tz->ops->get_trip_hyst) { + tz->trip_hyst_attrs = kzalloc(size, GFP_KERNEL); + if (!tz->trip_hyst_attrs) { + kfree(tz->trip_type_attrs); + kfree(tz->trip_temp_attrs); + return -ENOMEM; + } + } + + attrs = kzalloc(sizeof(*attrs) * tz->trips * 3 + 1, GFP_KERNEL); + if (!attrs) { + kfree(tz->trip_type_attrs); + kfree(tz->trip_temp_attrs); + if (tz->ops->get_trip_hyst) + kfree(tz->trip_hyst_attrs); + return -ENOMEM; + } + + for (indx = 0; indx < tz->trips; indx++) { + /* create trip type attribute */ + snprintf(tz->trip_type_attrs[indx].name, THERMAL_NAME_LENGTH, + "trip_point_%d_type", indx); + + sysfs_attr_init(&tz->trip_type_attrs[indx].attr.attr); + tz->trip_type_attrs[indx].attr.attr.name = + tz->trip_type_attrs[indx].name; + tz->trip_type_attrs[indx].attr.attr.mode = S_IRUGO; + tz->trip_type_attrs[indx].attr.show = trip_point_type_show; + attrs[indx] = &tz->trip_type_attrs[indx].attr.attr; + + /* create trip temp attribute */ + snprintf(tz->trip_temp_attrs[indx].name, THERMAL_NAME_LENGTH, + "trip_point_%d_temp", indx); + + sysfs_attr_init(&tz->trip_temp_attrs[indx].attr.attr); + tz->trip_temp_attrs[indx].attr.attr.name = + tz->trip_temp_attrs[indx].name; + tz->trip_temp_attrs[indx].attr.attr.mode = S_IRUGO; + tz->trip_temp_attrs[indx].attr.show = trip_point_temp_show; + if (IS_ENABLED(CONFIG_THERMAL_WRITABLE_TRIPS) && + mask & (1 << indx)) { + tz->trip_temp_attrs[indx].attr.attr.mode |= S_IWUSR; + tz->trip_temp_attrs[indx].attr.store = + trip_point_temp_store; + } + attrs[indx + tz->trips] = &tz->trip_temp_attrs[indx].attr.attr; + + /* create Optional trip hyst attribute */ + if (!tz->ops->get_trip_hyst) + continue; + snprintf(tz->trip_hyst_attrs[indx].name, THERMAL_NAME_LENGTH, + "trip_point_%d_hyst", indx); + + sysfs_attr_init(&tz->trip_hyst_attrs[indx].attr.attr); + tz->trip_hyst_attrs[indx].attr.attr.name = + tz->trip_hyst_attrs[indx].name; + tz->trip_hyst_attrs[indx].attr.attr.mode = S_IRUGO; + tz->trip_hyst_attrs[indx].attr.show = trip_point_hyst_show; + if (tz->ops->set_trip_hyst) { + tz->trip_hyst_attrs[indx].attr.attr.mode |= S_IWUSR; + tz->trip_hyst_attrs[indx].attr.store = + trip_point_hyst_store; + } + attrs[indx + tz->trips * 2] = + &tz->trip_hyst_attrs[indx].attr.attr; + } + attrs[tz->trips * 3] = NULL; + + tz->trips_attribute_group.attrs = attrs; + + return 0; +} + +int thermal_zone_create_device_groups(struct thermal_zone_device *tz, + int mask) +{ + const struct attribute_group **groups; + int i, size, result; + + /* we need one extra for trips and the NULL to terminate the array */ + size = ARRAY_SIZE(thermal_zone_attribute_groups) + 2; + /* This also takes care of API requirement to be NULL terminated */ + groups = kcalloc(size, sizeof(*groups), GFP_KERNEL); + if (!groups) + return -ENOMEM; + + for (i = 0; i < size - 2; i++) + groups[i] = thermal_zone_attribute_groups[i]; + + if (tz->trips) { + result = create_trip_attrs(tz, mask); + if (result) { + kfree(groups); + + return result; + } + + groups[size - 2] = &tz->trips_attribute_group; + } + + tz->device.groups = groups; + + return 0; +} From 99ea2eff91080740d91df6ce5bfdfba666a4a035 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:01 -0800 Subject: [PATCH 26/76] thermal: core: move to_cooling_device macro to header file Make the to_cooling_device() macro available across files in thermal core. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 3 --- drivers/thermal/thermal_core.h | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index b14913ea5544..d55a41a317b5 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -799,9 +799,6 @@ int thermal_build_list_of_policies(char *buf) } /* sys I/F for cooling device */ -#define to_cooling_device(_dev) \ - container_of(_dev, struct thermal_cooling_device, device) - static ssize_t thermal_cooling_device_type_show(struct device *dev, struct device_attribute *attr, char *buf) diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index bd8869333739..56192434ea81 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -57,6 +57,9 @@ struct thermal_instance { #define to_thermal_zone(_dev) \ container_of(_dev, struct thermal_zone_device, device) +#define to_cooling_device(_dev) \ + container_of(_dev, struct thermal_cooling_device, device) + int thermal_register_governor(struct thermal_governor *); void thermal_unregister_governor(struct thermal_governor *); void thermal_zone_device_rebind_exception(struct thermal_zone_device *, From 45cf2ec99c160e5c33402521bcff1a585629230a Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:02 -0800 Subject: [PATCH 27/76] thermal: core: move cooling device sysfs to thermal_sysfs.c This is a code reorganization, simply to concentrate the sysfs handling functions in thermal_sysfs.c. This patch moves the cooling device handling functions. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 128 +----------------------------- drivers/thermal/thermal_core.h | 11 +++ drivers/thermal/thermal_sysfs.c | 133 ++++++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+), 127 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index d55a41a317b5..fb1902135740 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -798,132 +798,6 @@ int thermal_build_list_of_policies(char *buf) return count; } -/* sys I/F for cooling device */ -static ssize_t -thermal_cooling_device_type_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct thermal_cooling_device *cdev = to_cooling_device(dev); - - return sprintf(buf, "%s\n", cdev->type); -} - -static ssize_t -thermal_cooling_device_max_state_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct thermal_cooling_device *cdev = to_cooling_device(dev); - unsigned long state; - int ret; - - ret = cdev->ops->get_max_state(cdev, &state); - if (ret) - return ret; - return sprintf(buf, "%ld\n", state); -} - -static ssize_t -thermal_cooling_device_cur_state_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct thermal_cooling_device *cdev = to_cooling_device(dev); - unsigned long state; - int ret; - - ret = cdev->ops->get_cur_state(cdev, &state); - if (ret) - return ret; - return sprintf(buf, "%ld\n", state); -} - -static ssize_t -thermal_cooling_device_cur_state_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct thermal_cooling_device *cdev = to_cooling_device(dev); - unsigned long state; - int result; - - if (sscanf(buf, "%ld\n", &state) != 1) - return -EINVAL; - - if ((long)state < 0) - return -EINVAL; - - result = cdev->ops->set_cur_state(cdev, state); - if (result) - return result; - return count; -} - -static struct device_attribute dev_attr_cdev_type = -__ATTR(type, 0444, thermal_cooling_device_type_show, NULL); -static DEVICE_ATTR(max_state, 0444, - thermal_cooling_device_max_state_show, NULL); -static DEVICE_ATTR(cur_state, 0644, - thermal_cooling_device_cur_state_show, - thermal_cooling_device_cur_state_store); - -static ssize_t -thermal_cooling_device_trip_point_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct thermal_instance *instance; - - instance = - container_of(attr, struct thermal_instance, attr); - - if (instance->trip == THERMAL_TRIPS_NONE) - return sprintf(buf, "-1\n"); - else - return sprintf(buf, "%d\n", instance->trip); -} - -static struct attribute *cooling_device_attrs[] = { - &dev_attr_cdev_type.attr, - &dev_attr_max_state.attr, - &dev_attr_cur_state.attr, - NULL, -}; - -static const struct attribute_group cooling_device_attr_group = { - .attrs = cooling_device_attrs, -}; - -static const struct attribute_group *cooling_device_attr_groups[] = { - &cooling_device_attr_group, - NULL, -}; - -static ssize_t -thermal_cooling_device_weight_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct thermal_instance *instance; - - instance = container_of(attr, struct thermal_instance, weight_attr); - - return sprintf(buf, "%d\n", instance->weight); -} - -static ssize_t -thermal_cooling_device_weight_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct thermal_instance *instance; - int ret, weight; - - ret = kstrtoint(buf, 0, &weight); - if (ret) - return ret; - - instance = container_of(attr, struct thermal_instance, weight_attr); - instance->weight = weight; - - return count; -} /* Device management */ /** @@ -1175,7 +1049,7 @@ __thermal_cooling_device_register(struct device_node *np, cdev->ops = ops; cdev->updated = false; cdev->device.class = &thermal_class; - cdev->device.groups = cooling_device_attr_groups; + thermal_cooling_device_setup_sysfs(cdev); cdev->devdata = devdata; dev_set_name(&cdev->device, "cooling_device%d", cdev->id); result = device_register(&cdev->device); diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 56192434ea81..2412b3759e16 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -71,6 +71,17 @@ int thermal_build_list_of_policies(char *buf); /* sysfs I/F */ int thermal_zone_create_device_groups(struct thermal_zone_device *, int); +void thermal_cooling_device_setup_sysfs(struct thermal_cooling_device *); +/* used only at binding time */ +ssize_t +thermal_cooling_device_trip_point_show(struct device *, + struct device_attribute *, char *); +ssize_t thermal_cooling_device_weight_show(struct device *, + struct device_attribute *, char *); + +ssize_t thermal_cooling_device_weight_store(struct device *, + struct device_attribute *, + const char *, size_t); #ifdef CONFIG_THERMAL_GOV_STEP_WISE int thermal_gov_step_wise_register(void); diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index 8a7323f63b78..87d9f4d3afaf 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -631,3 +631,136 @@ int thermal_zone_create_device_groups(struct thermal_zone_device *tz, return 0; } + +/* sys I/F for cooling device */ +static ssize_t +thermal_cooling_device_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct thermal_cooling_device *cdev = to_cooling_device(dev); + + return sprintf(buf, "%s\n", cdev->type); +} + +static ssize_t +thermal_cooling_device_max_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct thermal_cooling_device *cdev = to_cooling_device(dev); + unsigned long state; + int ret; + + ret = cdev->ops->get_max_state(cdev, &state); + if (ret) + return ret; + return sprintf(buf, "%ld\n", state); +} + +static ssize_t +thermal_cooling_device_cur_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct thermal_cooling_device *cdev = to_cooling_device(dev); + unsigned long state; + int ret; + + ret = cdev->ops->get_cur_state(cdev, &state); + if (ret) + return ret; + return sprintf(buf, "%ld\n", state); +} + +static ssize_t +thermal_cooling_device_cur_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct thermal_cooling_device *cdev = to_cooling_device(dev); + unsigned long state; + int result; + + if (sscanf(buf, "%ld\n", &state) != 1) + return -EINVAL; + + if ((long)state < 0) + return -EINVAL; + + result = cdev->ops->set_cur_state(cdev, state); + if (result) + return result; + return count; +} + +static struct device_attribute dev_attr_cdev_type = +__ATTR(type, 0444, thermal_cooling_device_type_show, NULL); +static DEVICE_ATTR(max_state, 0444, + thermal_cooling_device_max_state_show, NULL); +static DEVICE_ATTR(cur_state, 0644, + thermal_cooling_device_cur_state_show, + thermal_cooling_device_cur_state_store); + +static struct attribute *cooling_device_attrs[] = { + &dev_attr_cdev_type.attr, + &dev_attr_max_state.attr, + &dev_attr_cur_state.attr, + NULL, +}; + +static const struct attribute_group cooling_device_attr_group = { + .attrs = cooling_device_attrs, +}; + +static const struct attribute_group *cooling_device_attr_groups[] = { + &cooling_device_attr_group, + NULL, +}; + +void thermal_cooling_device_setup_sysfs(struct thermal_cooling_device *cdev) +{ + cdev->device.groups = cooling_device_attr_groups; +} + +/* these helper will be used only at the time of bindig */ +ssize_t +thermal_cooling_device_trip_point_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct thermal_instance *instance; + + instance = + container_of(attr, struct thermal_instance, attr); + + if (instance->trip == THERMAL_TRIPS_NONE) + return sprintf(buf, "-1\n"); + else + return sprintf(buf, "%d\n", instance->trip); +} + +ssize_t +thermal_cooling_device_weight_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct thermal_instance *instance; + + instance = container_of(attr, struct thermal_instance, weight_attr); + + return sprintf(buf, "%d\n", instance->weight); +} + +ssize_t +thermal_cooling_device_weight_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct thermal_instance *instance; + int ret, weight; + + ret = kstrtoint(buf, 0, &weight); + if (ret) + return ret; + + instance = container_of(attr, struct thermal_instance, weight_attr); + instance->weight = weight; + + return count; +} From 77dc4f9032008f097145e6cff7a7532bb9de4ea0 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:03 -0800 Subject: [PATCH 28/76] thermal: core: remove a couple of style issues on helpers Reorganizing the code of helper functions to improve readability and style, as recommended by checkpatch.pl. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index fb1902135740..3a189fd729b9 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -248,8 +248,9 @@ int get_tz_trend(struct thermal_zone_device *tz, int trip) } EXPORT_SYMBOL(get_tz_trend); -struct thermal_instance *get_thermal_instance(struct thermal_zone_device *tz, - struct thermal_cooling_device *cdev, int trip) +struct thermal_instance * +get_thermal_instance(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev, int trip) { struct thermal_instance *pos = NULL; struct thermal_instance *target_instance = NULL; @@ -513,7 +514,7 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp) if (!ret && *temp < crit_temp) *temp = tz->emul_temperature; } - + mutex_unlock(&tz->lock); exit: return ret; @@ -1192,7 +1193,7 @@ void thermal_cdev_update(struct thermal_cooling_device *cdev) /* Make sure cdev enters the deepest cooling state */ list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) { dev_dbg(&cdev->device, "zone%d->target=%lu\n", - instance->tz->id, instance->target); + instance->tz->id, instance->target); if (instance->target == THERMAL_NO_TARGET) continue; if (instance->target > target) From cd221c7b638f83193ffcacf9c2e3f087033fd4cf Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:04 -0800 Subject: [PATCH 29/76] thermal: core: introduce thermal_helpers.c Here we have a simple code organization. This patch moves functions that do not need to handle thermal core internal data structure to thermal_helpers.c file. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/Makefile | 3 +- drivers/thermal/thermal_core.c | 170 -------------------------- drivers/thermal/thermal_helpers.c | 196 ++++++++++++++++++++++++++++++ 3 files changed, 198 insertions(+), 171 deletions(-) create mode 100644 drivers/thermal/thermal_helpers.c diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 767c3c9aeeea..6a3d7b573036 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -3,7 +3,8 @@ # obj-$(CONFIG_THERMAL) += thermal_sys.o -thermal_sys-y += thermal_core.o thermal_sysfs.o +thermal_sys-y += thermal_core.o thermal_sysfs.o \ + thermal_helpers.o # interface to/from other layers providing sensors thermal_sys-$(CONFIG_THERMAL_HWMON) += thermal_hwmon.o diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 3a189fd729b9..519aa94d0837 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -230,48 +230,6 @@ static void release_idr(struct idr *idr, struct mutex *lock, int id) mutex_unlock(lock); } -int get_tz_trend(struct thermal_zone_device *tz, int trip) -{ - enum thermal_trend trend; - - if (tz->emul_temperature || !tz->ops->get_trend || - tz->ops->get_trend(tz, trip, &trend)) { - if (tz->temperature > tz->last_temperature) - trend = THERMAL_TREND_RAISING; - else if (tz->temperature < tz->last_temperature) - trend = THERMAL_TREND_DROPPING; - else - trend = THERMAL_TREND_STABLE; - } - - return trend; -} -EXPORT_SYMBOL(get_tz_trend); - -struct thermal_instance * -get_thermal_instance(struct thermal_zone_device *tz, - struct thermal_cooling_device *cdev, int trip) -{ - struct thermal_instance *pos = NULL; - struct thermal_instance *target_instance = NULL; - - mutex_lock(&tz->lock); - mutex_lock(&cdev->lock); - - list_for_each_entry(pos, &tz->thermal_instances, tz_node) { - if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { - target_instance = pos; - break; - } - } - - mutex_unlock(&cdev->lock); - mutex_unlock(&tz->lock); - - return target_instance; -} -EXPORT_SYMBOL(get_thermal_instance); - static void print_bind_err_msg(struct thermal_zone_device *tz, struct thermal_cooling_device *cdev, int ret) { @@ -472,105 +430,6 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip) monitor_thermal_zone(tz); } -/** - * thermal_zone_get_temp() - returns the temperature of a thermal zone - * @tz: a valid pointer to a struct thermal_zone_device - * @temp: a valid pointer to where to store the resulting temperature. - * - * When a valid thermal zone reference is passed, it will fetch its - * temperature and fill @temp. - * - * Return: On success returns 0, an error code otherwise - */ -int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp) -{ - int ret = -EINVAL; - int count; - int crit_temp = INT_MAX; - enum thermal_trip_type type; - - if (!tz || IS_ERR(tz) || !tz->ops->get_temp) - goto exit; - - mutex_lock(&tz->lock); - - ret = tz->ops->get_temp(tz, temp); - - if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) { - for (count = 0; count < tz->trips; count++) { - ret = tz->ops->get_trip_type(tz, count, &type); - if (!ret && type == THERMAL_TRIP_CRITICAL) { - ret = tz->ops->get_trip_temp(tz, count, - &crit_temp); - break; - } - } - - /* - * Only allow emulating a temperature when the real temperature - * is below the critical temperature so that the emulation code - * cannot hide critical conditions. - */ - if (!ret && *temp < crit_temp) - *temp = tz->emul_temperature; - } - - mutex_unlock(&tz->lock); -exit: - return ret; -} -EXPORT_SYMBOL_GPL(thermal_zone_get_temp); - -void thermal_zone_set_trips(struct thermal_zone_device *tz) -{ - int low = -INT_MAX; - int high = INT_MAX; - int trip_temp, hysteresis; - int i, ret; - - mutex_lock(&tz->lock); - - if (!tz->ops->set_trips || !tz->ops->get_trip_hyst) - goto exit; - - for (i = 0; i < tz->trips; i++) { - int trip_low; - - tz->ops->get_trip_temp(tz, i, &trip_temp); - tz->ops->get_trip_hyst(tz, i, &hysteresis); - - trip_low = trip_temp - hysteresis; - - if (trip_low < tz->temperature && trip_low > low) - low = trip_low; - - if (trip_temp > tz->temperature && trip_temp < high) - high = trip_temp; - } - - /* No need to change trip points */ - if (tz->prev_low_trip == low && tz->prev_high_trip == high) - goto exit; - - tz->prev_low_trip = low; - tz->prev_high_trip = high; - - dev_dbg(&tz->device, - "new temperature boundaries: %d < x < %d\n", low, high); - - /* - * Set a temperature window. When this window is left the driver - * must inform the thermal core via thermal_zone_device_update. - */ - ret = tz->ops->set_trips(tz, low, high); - if (ret) - dev_err(&tz->device, "Failed to set trips: %d\n", ret); - -exit: - mutex_unlock(&tz->lock); -} -EXPORT_SYMBOL_GPL(thermal_zone_set_trips); - static void update_temperature(struct thermal_zone_device *tz) { int temp, ret; @@ -1178,35 +1037,6 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) } EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister); -void thermal_cdev_update(struct thermal_cooling_device *cdev) -{ - struct thermal_instance *instance; - unsigned long target = 0; - - mutex_lock(&cdev->lock); - /* cooling device is updated*/ - if (cdev->updated) { - mutex_unlock(&cdev->lock); - return; - } - - /* Make sure cdev enters the deepest cooling state */ - list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) { - dev_dbg(&cdev->device, "zone%d->target=%lu\n", - instance->tz->id, instance->target); - if (instance->target == THERMAL_NO_TARGET) - continue; - if (instance->target > target) - target = instance->target; - } - cdev->ops->set_cur_state(cdev, target); - cdev->updated = true; - mutex_unlock(&cdev->lock); - trace_cdev_update(cdev, target); - dev_dbg(&cdev->device, "set to state %lu\n", target); -} -EXPORT_SYMBOL(thermal_cdev_update); - /** * thermal_notify_framework - Sensor drivers use this API to notify framework * @tz: thermal zone device diff --git a/drivers/thermal/thermal_helpers.c b/drivers/thermal/thermal_helpers.c new file mode 100644 index 000000000000..dfdba5a720cd --- /dev/null +++ b/drivers/thermal/thermal_helpers.c @@ -0,0 +1,196 @@ +/* + * thermal_helpers.c - helper functions to handle thermal devices + * + * Copyright (C) 2016 Eduardo Valentin + * + * Highly based on original thermal_core.c + * Copyright (C) 2008 Intel Corp + * Copyright (C) 2008 Zhang Rui + * Copyright (C) 2008 Sujith Thomas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#include + +#include "thermal_core.h" + +int get_tz_trend(struct thermal_zone_device *tz, int trip) +{ + enum thermal_trend trend; + + if (tz->emul_temperature || !tz->ops->get_trend || + tz->ops->get_trend(tz, trip, &trend)) { + if (tz->temperature > tz->last_temperature) + trend = THERMAL_TREND_RAISING; + else if (tz->temperature < tz->last_temperature) + trend = THERMAL_TREND_DROPPING; + else + trend = THERMAL_TREND_STABLE; + } + + return trend; +} +EXPORT_SYMBOL(get_tz_trend); + +struct thermal_instance * +get_thermal_instance(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev, int trip) +{ + struct thermal_instance *pos = NULL; + struct thermal_instance *target_instance = NULL; + + mutex_lock(&tz->lock); + mutex_lock(&cdev->lock); + + list_for_each_entry(pos, &tz->thermal_instances, tz_node) { + if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { + target_instance = pos; + break; + } + } + + mutex_unlock(&cdev->lock); + mutex_unlock(&tz->lock); + + return target_instance; +} +EXPORT_SYMBOL(get_thermal_instance); + +/** + * thermal_zone_get_temp() - returns the temperature of a thermal zone + * @tz: a valid pointer to a struct thermal_zone_device + * @temp: a valid pointer to where to store the resulting temperature. + * + * When a valid thermal zone reference is passed, it will fetch its + * temperature and fill @temp. + * + * Return: On success returns 0, an error code otherwise + */ +int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp) +{ + int ret = -EINVAL; + int count; + int crit_temp = INT_MAX; + enum thermal_trip_type type; + + if (!tz || IS_ERR(tz) || !tz->ops->get_temp) + goto exit; + + mutex_lock(&tz->lock); + + ret = tz->ops->get_temp(tz, temp); + + if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) { + for (count = 0; count < tz->trips; count++) { + ret = tz->ops->get_trip_type(tz, count, &type); + if (!ret && type == THERMAL_TRIP_CRITICAL) { + ret = tz->ops->get_trip_temp(tz, count, + &crit_temp); + break; + } + } + + /* + * Only allow emulating a temperature when the real temperature + * is below the critical temperature so that the emulation code + * cannot hide critical conditions. + */ + if (!ret && *temp < crit_temp) + *temp = tz->emul_temperature; + } + + mutex_unlock(&tz->lock); +exit: + return ret; +} +EXPORT_SYMBOL_GPL(thermal_zone_get_temp); + +void thermal_zone_set_trips(struct thermal_zone_device *tz) +{ + int low = -INT_MAX; + int high = INT_MAX; + int trip_temp, hysteresis; + int i, ret; + + mutex_lock(&tz->lock); + + if (!tz->ops->set_trips || !tz->ops->get_trip_hyst) + goto exit; + + for (i = 0; i < tz->trips; i++) { + int trip_low; + + tz->ops->get_trip_temp(tz, i, &trip_temp); + tz->ops->get_trip_hyst(tz, i, &hysteresis); + + trip_low = trip_temp - hysteresis; + + if (trip_low < tz->temperature && trip_low > low) + low = trip_low; + + if (trip_temp > tz->temperature && trip_temp < high) + high = trip_temp; + } + + /* No need to change trip points */ + if (tz->prev_low_trip == low && tz->prev_high_trip == high) + goto exit; + + tz->prev_low_trip = low; + tz->prev_high_trip = high; + + dev_dbg(&tz->device, + "new temperature boundaries: %d < x < %d\n", low, high); + + /* + * Set a temperature window. When this window is left the driver + * must inform the thermal core via thermal_zone_device_update. + */ + ret = tz->ops->set_trips(tz, low, high); + if (ret) + dev_err(&tz->device, "Failed to set trips: %d\n", ret); + +exit: + mutex_unlock(&tz->lock); +} +EXPORT_SYMBOL_GPL(thermal_zone_set_trips); + +void thermal_cdev_update(struct thermal_cooling_device *cdev) +{ + struct thermal_instance *instance; + unsigned long target = 0; + + mutex_lock(&cdev->lock); + /* cooling device is updated*/ + if (cdev->updated) { + mutex_unlock(&cdev->lock); + return; + } + + /* Make sure cdev enters the deepest cooling state */ + list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) { + dev_dbg(&cdev->device, "zone%d->target=%lu\n", + instance->tz->id, instance->target); + if (instance->target == THERMAL_NO_TARGET) + continue; + if (instance->target > target) + target = instance->target; + } + cdev->ops->set_cur_state(cdev, target); + cdev->updated = true; + mutex_unlock(&cdev->lock); + trace_cdev_update(cdev, target); + dev_dbg(&cdev->device, "set to state %lu\n", target); +} +EXPORT_SYMBOL(thermal_cdev_update); From 1b4f48494eb2886d42b034b57f0c4005add296b0 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:05 -0800 Subject: [PATCH 30/76] thermal: core: group functions related to governor handling Organize thermal core code to group the functions handling with governor manipulation in one single section. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 153 +++++++++++++++++---------------- 1 file changed, 80 insertions(+), 73 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 519aa94d0837..e36a6a6e58db 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -64,6 +64,13 @@ static atomic_t in_suspend; static struct thermal_governor *def_governor; +/* + * Governor section: set of functions to handle thermal governors + * + * Functions to help in the life cycle of thermal governors within + * the thermal core and by the thermal governor code. + */ + static struct thermal_governor *__find_governor(const char *name) { struct thermal_governor *pos; @@ -206,6 +213,79 @@ exit: return; } +int thermal_zone_device_set_policy(struct thermal_zone_device *tz, + char *policy) +{ + struct thermal_governor *gov; + int ret = -EINVAL; + + mutex_lock(&thermal_governor_lock); + mutex_lock(&tz->lock); + + gov = __find_governor(strim(policy)); + if (!gov) + goto exit; + + ret = thermal_set_governor(tz, gov); + +exit: + mutex_unlock(&tz->lock); + mutex_unlock(&thermal_governor_lock); + + return ret; +} + +int thermal_build_list_of_policies(char *buf) +{ + struct thermal_governor *pos; + ssize_t count = 0; + ssize_t size = PAGE_SIZE; + + mutex_lock(&thermal_governor_lock); + + list_for_each_entry(pos, &thermal_governor_list, governor_list) { + size = PAGE_SIZE - count; + count += scnprintf(buf + count, size, "%s ", pos->name); + } + count += scnprintf(buf + count, size, "\n"); + + mutex_unlock(&thermal_governor_lock); + + return count; +} + +static int __init thermal_register_governors(void) +{ + int result; + + result = thermal_gov_step_wise_register(); + if (result) + return result; + + result = thermal_gov_fair_share_register(); + if (result) + return result; + + result = thermal_gov_bang_bang_register(); + if (result) + return result; + + result = thermal_gov_user_space_register(); + if (result) + return result; + + return thermal_gov_power_allocator_register(); +} + +static void thermal_unregister_governors(void) +{ + thermal_gov_step_wise_unregister(); + thermal_gov_fair_share_unregister(); + thermal_gov_bang_bang_unregister(); + thermal_gov_user_space_unregister(); + thermal_gov_power_allocator_unregister(); +} + static int get_idr(struct idr *idr, struct mutex *lock, int *id) { int ret; @@ -617,47 +697,6 @@ void thermal_zone_device_unbind_exception(struct thermal_zone_device *tz, mutex_unlock(&thermal_list_lock); } -int thermal_zone_device_set_policy(struct thermal_zone_device *tz, - char *policy) -{ - struct thermal_governor *gov; - int ret = -EINVAL; - - mutex_lock(&thermal_governor_lock); - mutex_lock(&tz->lock); - - gov = __find_governor(strim(policy)); - if (!gov) - goto exit; - - ret = thermal_set_governor(tz, gov); - -exit: - mutex_unlock(&tz->lock); - mutex_unlock(&thermal_governor_lock); - - return ret; -} - -int thermal_build_list_of_policies(char *buf) -{ - struct thermal_governor *pos; - ssize_t count = 0; - ssize_t size = PAGE_SIZE; - - mutex_lock(&thermal_governor_lock); - - list_for_each_entry(pos, &thermal_governor_list, governor_list) { - size = PAGE_SIZE - count; - count += scnprintf(buf + count, size, "%s ", pos->name); - } - count += scnprintf(buf + count, size, "\n"); - - mutex_unlock(&thermal_governor_lock); - - return count; -} - /* Device management */ /** @@ -1424,38 +1463,6 @@ static inline int genetlink_init(void) { return 0; } static inline void genetlink_exit(void) {} #endif /* !CONFIG_NET */ -static int __init thermal_register_governors(void) -{ - int result; - - result = thermal_gov_step_wise_register(); - if (result) - return result; - - result = thermal_gov_fair_share_register(); - if (result) - return result; - - result = thermal_gov_bang_bang_register(); - if (result) - return result; - - result = thermal_gov_user_space_register(); - if (result) - return result; - - return thermal_gov_power_allocator_register(); -} - -static void thermal_unregister_governors(void) -{ - thermal_gov_step_wise_unregister(); - thermal_gov_fair_share_unregister(); - thermal_gov_bang_bang_unregister(); - thermal_gov_user_space_unregister(); - thermal_gov_power_allocator_unregister(); -} - static int thermal_pm_notify(struct notifier_block *nb, unsigned long mode, void *_unused) { From c30176fc6f483163affeb0f629c0e27b23c084a1 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:06 -0800 Subject: [PATCH 31/76] thermal: core: move idr handling to device management section Given that idr is only used to get id for thermal devices (zones and cooling), makes sense to move the code closer. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 48 +++++++++++++++++----------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index e36a6a6e58db..f6e24ef1d904 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -286,30 +286,6 @@ static void thermal_unregister_governors(void) thermal_gov_power_allocator_unregister(); } -static int get_idr(struct idr *idr, struct mutex *lock, int *id) -{ - int ret; - - if (lock) - mutex_lock(lock); - ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL); - if (lock) - mutex_unlock(lock); - if (unlikely(ret < 0)) - return ret; - *id = ret; - return 0; -} - -static void release_idr(struct idr *idr, struct mutex *lock, int id) -{ - if (lock) - mutex_lock(lock); - idr_remove(idr, id); - if (lock) - mutex_unlock(lock); -} - static void print_bind_err_msg(struct thermal_zone_device *tz, struct thermal_cooling_device *cdev, int ret) { @@ -699,6 +675,30 @@ void thermal_zone_device_unbind_exception(struct thermal_zone_device *tz, /* Device management */ +static int get_idr(struct idr *idr, struct mutex *lock, int *id) +{ + int ret; + + if (lock) + mutex_lock(lock); + ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL); + if (lock) + mutex_unlock(lock); + if (unlikely(ret < 0)) + return ret; + *id = ret; + return 0; +} + +static void release_idr(struct idr *idr, struct mutex *lock, int id) +{ + if (lock) + mutex_lock(lock); + idr_remove(idr, id); + if (lock) + mutex_unlock(lock); +} + /** * thermal_zone_bind_cooling_device() - bind a cooling device to a thermal zone * @tz: pointer to struct thermal_zone_device From 4f5163fac28efc4f4d7fefdaa6a7ed41b4febde9 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:07 -0800 Subject: [PATCH 32/76] thermal: core: small style fix on __unbind() helper Simply aligning to parenthesis. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index f6e24ef1d904..c31d0bbdfb2c 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -320,7 +320,7 @@ static void __bind(struct thermal_zone_device *tz, int mask, } static void __unbind(struct thermal_zone_device *tz, int mask, - struct thermal_cooling_device *cdev) + struct thermal_cooling_device *cdev) { int i; From f11997fa24220c91f8e951ebd047d69f47ab6759 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:08 -0800 Subject: [PATCH 33/76] thermal: core: move __unbind() helper to where it is used Simply moving the helper to closer where it is actually used. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index c31d0bbdfb2c..c12193315a15 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -319,16 +319,6 @@ static void __bind(struct thermal_zone_device *tz, int mask, } } -static void __unbind(struct thermal_zone_device *tz, int mask, - struct thermal_cooling_device *cdev) -{ - int i; - - for (i = 0; i < tz->trips; i++) - if (mask & (1 << i)) - thermal_zone_unbind_cooling_device(tz, i, cdev); -} - static void bind_cdev(struct thermal_cooling_device *cdev) { int i, ret; @@ -1021,6 +1011,16 @@ thermal_of_cooling_device_register(struct device_node *np, } EXPORT_SYMBOL_GPL(thermal_of_cooling_device_register); +static void __unbind(struct thermal_zone_device *tz, int mask, + struct thermal_cooling_device *cdev) +{ + int i; + + for (i = 0; i < tz->trips; i++) + if (mask & (1 << i)) + thermal_zone_unbind_cooling_device(tz, i, cdev); +} + /** * thermal_cooling_device_unregister - removes the registered thermal cooling device * @cdev: the thermal cooling device to remove. From 949aad839c33fc6aaace5b063a331a711876d499 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:09 -0800 Subject: [PATCH 34/76] thermal: core: move bind_cdev() to where it is used Moving the helper to closer where it is used. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 76 +++++++++++++++++----------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index c12193315a15..e397d88ce267 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -319,44 +319,6 @@ static void __bind(struct thermal_zone_device *tz, int mask, } } -static void bind_cdev(struct thermal_cooling_device *cdev) -{ - int i, ret; - const struct thermal_zone_params *tzp; - struct thermal_zone_device *pos = NULL; - - mutex_lock(&thermal_list_lock); - - list_for_each_entry(pos, &thermal_tz_list, node) { - if (!pos->tzp && !pos->ops->bind) - continue; - - if (pos->ops->bind) { - ret = pos->ops->bind(pos, cdev); - if (ret) - print_bind_err_msg(pos, cdev, ret); - continue; - } - - tzp = pos->tzp; - if (!tzp || !tzp->tbp) - continue; - - for (i = 0; i < tzp->num_tbps; i++) { - if (tzp->tbp[i].cdev || !tzp->tbp[i].match) - continue; - if (tzp->tbp[i].match(pos, cdev)) - continue; - tzp->tbp[i].cdev = cdev; - __bind(pos, tzp->tbp[i].trip_mask, cdev, - tzp->tbp[i].binding_limits, - tzp->tbp[i].weight); - } - } - - mutex_unlock(&thermal_list_lock); -} - static void bind_tz(struct thermal_zone_device *tz) { int i, ret; @@ -889,6 +851,44 @@ static struct class thermal_class = { .dev_release = thermal_release, }; +static void bind_cdev(struct thermal_cooling_device *cdev) +{ + int i, ret; + const struct thermal_zone_params *tzp; + struct thermal_zone_device *pos = NULL; + + mutex_lock(&thermal_list_lock); + + list_for_each_entry(pos, &thermal_tz_list, node) { + if (!pos->tzp && !pos->ops->bind) + continue; + + if (pos->ops->bind) { + ret = pos->ops->bind(pos, cdev); + if (ret) + print_bind_err_msg(pos, cdev, ret); + continue; + } + + tzp = pos->tzp; + if (!tzp || !tzp->tbp) + continue; + + for (i = 0; i < tzp->num_tbps; i++) { + if (tzp->tbp[i].cdev || !tzp->tbp[i].match) + continue; + if (tzp->tbp[i].match(pos, cdev)) + continue; + tzp->tbp[i].cdev = cdev; + __bind(pos, tzp->tbp[i].trip_mask, cdev, + tzp->tbp[i].binding_limits, + tzp->tbp[i].weight); + } + } + + mutex_unlock(&thermal_list_lock); +} + /** * __thermal_cooling_device_register() - register a new thermal cooling device * @np: a pointer to a device tree node. From 90f5b5bb7f4d8455a7965be4b13d50bea39ab6dc Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:10 -0800 Subject: [PATCH 35/76] thermal: core: move bind_tz() to where it is used Moving the helper to closer where it is used. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 80 +++++++++++++++++----------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index e397d88ce267..bff2905ddbed 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -319,46 +319,6 @@ static void __bind(struct thermal_zone_device *tz, int mask, } } -static void bind_tz(struct thermal_zone_device *tz) -{ - int i, ret; - struct thermal_cooling_device *pos = NULL; - const struct thermal_zone_params *tzp = tz->tzp; - - if (!tzp && !tz->ops->bind) - return; - - mutex_lock(&thermal_list_lock); - - /* If there is ops->bind, try to use ops->bind */ - if (tz->ops->bind) { - list_for_each_entry(pos, &thermal_cdev_list, node) { - ret = tz->ops->bind(tz, pos); - if (ret) - print_bind_err_msg(tz, pos, ret); - } - goto exit; - } - - if (!tzp || !tzp->tbp) - goto exit; - - list_for_each_entry(pos, &thermal_cdev_list, node) { - for (i = 0; i < tzp->num_tbps; i++) { - if (tzp->tbp[i].cdev || !tzp->tbp[i].match) - continue; - if (tzp->tbp[i].match(tz, pos)) - continue; - tzp->tbp[i].cdev = pos; - __bind(tz, tzp->tbp[i].trip_mask, pos, - tzp->tbp[i].binding_limits, - tzp->tbp[i].weight); - } - } -exit: - mutex_unlock(&thermal_list_lock); -} - static void thermal_zone_device_set_polling(struct thermal_zone_device *tz, int delay) { @@ -1094,6 +1054,46 @@ void thermal_notify_framework(struct thermal_zone_device *tz, int trip) } EXPORT_SYMBOL_GPL(thermal_notify_framework); +static void bind_tz(struct thermal_zone_device *tz) +{ + int i, ret; + struct thermal_cooling_device *pos = NULL; + const struct thermal_zone_params *tzp = tz->tzp; + + if (!tzp && !tz->ops->bind) + return; + + mutex_lock(&thermal_list_lock); + + /* If there is ops->bind, try to use ops->bind */ + if (tz->ops->bind) { + list_for_each_entry(pos, &thermal_cdev_list, node) { + ret = tz->ops->bind(tz, pos); + if (ret) + print_bind_err_msg(tz, pos, ret); + } + goto exit; + } + + if (!tzp || !tzp->tbp) + goto exit; + + list_for_each_entry(pos, &thermal_cdev_list, node) { + for (i = 0; i < tzp->num_tbps; i++) { + if (tzp->tbp[i].cdev || !tzp->tbp[i].match) + continue; + if (tzp->tbp[i].match(tz, pos)) + continue; + tzp->tbp[i].cdev = pos; + __bind(tz, tzp->tbp[i].trip_mask, pos, + tzp->tbp[i].binding_limits, + tzp->tbp[i].weight); + } + } +exit: + mutex_unlock(&thermal_list_lock); +} + /** * thermal_zone_device_register() - register a new thermal zone device * @type: the thermal zone device type From 9b68ef89c957c4089cfc3f8aa985b0a7bc0dfc37 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:11 -0800 Subject: [PATCH 36/76] thermal: core: fix couple of style issues on __bind() helper Removing style issues on __bind() and its helpers. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index bff2905ddbed..ebec7ce63abf 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -287,16 +287,16 @@ static void thermal_unregister_governors(void) } static void print_bind_err_msg(struct thermal_zone_device *tz, - struct thermal_cooling_device *cdev, int ret) + struct thermal_cooling_device *cdev, int ret) { dev_err(&tz->device, "binding zone %s with cdev %s failed:%d\n", - tz->type, cdev->type, ret); + tz->type, cdev->type, ret); } static void __bind(struct thermal_zone_device *tz, int mask, - struct thermal_cooling_device *cdev, - unsigned long *limits, - unsigned int weight) + struct thermal_cooling_device *cdev, + unsigned long *limits, + unsigned int weight) { int i, ret; From f502ab84401341be2726ffd747ad6131d5371507 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:12 -0800 Subject: [PATCH 37/76] thermal: core: move __bind() to where it is used Moving the helper to closer where it is used. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 66 +++++++++++++++++----------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index ebec7ce63abf..258962bc2813 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -286,39 +286,6 @@ static void thermal_unregister_governors(void) thermal_gov_power_allocator_unregister(); } -static void print_bind_err_msg(struct thermal_zone_device *tz, - struct thermal_cooling_device *cdev, int ret) -{ - dev_err(&tz->device, "binding zone %s with cdev %s failed:%d\n", - tz->type, cdev->type, ret); -} - -static void __bind(struct thermal_zone_device *tz, int mask, - struct thermal_cooling_device *cdev, - unsigned long *limits, - unsigned int weight) -{ - int i, ret; - - for (i = 0; i < tz->trips; i++) { - if (mask & (1 << i)) { - unsigned long upper, lower; - - upper = THERMAL_NO_LIMIT; - lower = THERMAL_NO_LIMIT; - if (limits) { - lower = limits[i * 2]; - upper = limits[i * 2 + 1]; - } - ret = thermal_zone_bind_cooling_device(tz, i, cdev, - upper, lower, - weight); - if (ret) - print_bind_err_msg(tz, cdev, ret); - } - } -} - static void thermal_zone_device_set_polling(struct thermal_zone_device *tz, int delay) { @@ -811,6 +778,39 @@ static struct class thermal_class = { .dev_release = thermal_release, }; +static void print_bind_err_msg(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev, int ret) +{ + dev_err(&tz->device, "binding zone %s with cdev %s failed:%d\n", + tz->type, cdev->type, ret); +} + +static void __bind(struct thermal_zone_device *tz, int mask, + struct thermal_cooling_device *cdev, + unsigned long *limits, + unsigned int weight) +{ + int i, ret; + + for (i = 0; i < tz->trips; i++) { + if (mask & (1 << i)) { + unsigned long upper, lower; + + upper = THERMAL_NO_LIMIT; + lower = THERMAL_NO_LIMIT; + if (limits) { + lower = limits[i * 2]; + upper = limits[i * 2 + 1]; + } + ret = thermal_zone_bind_cooling_device(tz, i, cdev, + upper, lower, + weight); + if (ret) + print_bind_err_msg(tz, cdev, ret); + } + } +} + static void bind_cdev(struct thermal_cooling_device *cdev) { int i, ret; From 4b0d3c2d3b60cf8f517684ab230e8fbeaec5c541 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:13 -0800 Subject: [PATCH 38/76] thermal: core: add inline to print_bind_err_msg() Given that this is simple wrapper, adding the inline flag. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 258962bc2813..bc9a4220e8b5 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -778,8 +778,9 @@ static struct class thermal_class = { .dev_release = thermal_release, }; -static void print_bind_err_msg(struct thermal_zone_device *tz, - struct thermal_cooling_device *cdev, int ret) +static inline +void print_bind_err_msg(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev, int ret) { dev_err(&tz->device, "binding zone %s with cdev %s failed:%d\n", tz->type, cdev->type, ret); From 106339ab7e98da3e51c109926af6f394dcc06812 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:14 -0800 Subject: [PATCH 39/76] thermal: core: move notify to the zone update section moving the helper function to closer to similar functions. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 36 +++++++++++++++++----------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index bc9a4220e8b5..884235f216bd 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -424,6 +424,24 @@ void thermal_zone_device_update(struct thermal_zone_device *tz, } EXPORT_SYMBOL_GPL(thermal_zone_device_update); +/** + * thermal_notify_framework - Sensor drivers use this API to notify framework + * @tz: thermal zone device + * @trip: indicates which trip point has been crossed + * + * This function handles the trip events from sensor drivers. It starts + * throttling the cooling devices according to the policy configured. + * For CRITICAL and HOT trip points, this notifies the respective drivers, + * and does actual throttling for other trip points i.e ACTIVE and PASSIVE. + * The throttling policy is based on the configured platform data; if no + * platform data is provided, this uses the step_wise throttling policy. + */ +void thermal_notify_framework(struct thermal_zone_device *tz, int trip) +{ + handle_thermal_trip(tz, trip); +} +EXPORT_SYMBOL_GPL(thermal_notify_framework); + static void thermal_zone_device_check(struct work_struct *work) { struct thermal_zone_device *tz = container_of(work, struct @@ -1037,24 +1055,6 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) } EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister); -/** - * thermal_notify_framework - Sensor drivers use this API to notify framework - * @tz: thermal zone device - * @trip: indicates which trip point has been crossed - * - * This function handles the trip events from sensor drivers. It starts - * throttling the cooling devices according to the policy configured. - * For CRITICAL and HOT trip points, this notifies the respective drivers, - * and does actual throttling for other trip points i.e ACTIVE and PASSIVE. - * The throttling policy is based on the configured platform data; if no - * platform data is provided, this uses the step_wise throttling policy. - */ -void thermal_notify_framework(struct thermal_zone_device *tz, int trip) -{ - handle_thermal_trip(tz, trip); -} -EXPORT_SYMBOL_GPL(thermal_notify_framework); - static void bind_tz(struct thermal_zone_device *tz) { int i, ret; From 8772e185f191bf1280445dfe6b4f9c59e725c473 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:15 -0800 Subject: [PATCH 40/76] thermal: core: add a comment describing the main update loop Simply marking the main update loop section and adding a comment describing it. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 884235f216bd..bf108db6ae5d 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -286,6 +286,17 @@ static void thermal_unregister_governors(void) thermal_gov_power_allocator_unregister(); } +/* + * Zone update section: main control loop applied to each zone while monitoring + * + * in polling mode. The monitoring is done using a workqueue. + * Same update may be done on a zone by calling thermal_zone_device_update(). + * + * An update means: + * - Non-critical trips will invoke the governor responsible for that zone; + * - Hot trips will produce a notification to userspace; + * - Critical trip point will cause a system shutdown. + */ static void thermal_zone_device_set_polling(struct thermal_zone_device *tz, int delay) { From 712afbdfdf0d4137f7b14877d01309b76577d90a Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:16 -0800 Subject: [PATCH 41/76] thermal: core: add a comment describing the power actor section Simply marking the power actor section and adding a comment describing it. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index bf108db6ae5d..e9e6bb9085a1 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -461,6 +461,13 @@ static void thermal_zone_device_check(struct work_struct *work) thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); } +/* + * Power actor section: interface to power actors to estimate power + * + * Set of functions used to interact to cooling devices that know + * how to estimate their devices power consumption. + */ + /** * power_actor_get_max_power() - get the maximum power that a cdev can consume * @cdev: pointer to &thermal_cooling_device From 81193e2e6b473734bcb531a25206780d4f2dbea9 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:17 -0800 Subject: [PATCH 42/76] thermal: core: add a comment describing the device management section comment describing the section with function to handle registration, unregistration, binding, and unbinding of thermal devices. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index e9e6bb9085a1..01b1a4e064c0 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -588,8 +588,15 @@ void thermal_zone_device_unbind_exception(struct thermal_zone_device *tz, mutex_unlock(&thermal_list_lock); } -/* Device management */ - +/* + * Device management section: cooling devices, zones devices, and binding + * + * Set of functions provided by the thermal core for: + * - cooling devices lifecycle: registration, unregistration, + * binding, and unbinding. + * - thermal zone devices lifecycle: registration, unregistration, + * binding, and unbinding. + */ static int get_idr(struct idr *idr, struct mutex *lock, int *id) { int ret; From 698db4fdd3e8ef65db2cbde447443c1dc5807b95 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:18 -0800 Subject: [PATCH 43/76] thermal: sysfs: remove symbols of emul_temp when config is disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the following warning: In file included from drivers/thermal/thermal_sysfs.c:19:0: include/linux/device.h:575:26: warning: ‘dev_attr_emul_temp’ defined but not used [-Wunused-variable] struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store) ^ drivers/thermal/thermal_sysfs.c:395:8: note: in expansion of macro ‘DEVICE_ATTR’ when emul temp is disabled at Kconfig. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_sysfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index 87d9f4d3afaf..4807015c26de 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -292,6 +292,7 @@ available_policies_show(struct device *dev, struct device_attribute *devattr, return thermal_build_list_of_policies(buf); } +#if (IS_ENABLED(CONFIG_THERMAL_EMULATION)) static ssize_t emul_temp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -316,6 +317,8 @@ emul_temp_store(struct device *dev, struct device_attribute *attr, return ret ? ret : count; } +static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store); +#endif static ssize_t sustainable_power_show(struct device *dev, struct device_attribute *devattr, @@ -395,7 +398,6 @@ create_s32_tzp_attr(offset); */ static DEVICE_ATTR(type, 0444, type_show, NULL); static DEVICE_ATTR(temp, 0444, temp_show, NULL); -static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store); static DEVICE_ATTR(policy, S_IRUGO | S_IWUSR, policy_show, policy_store); static DEVICE_ATTR(available_policies, S_IRUGO, available_policies_show, NULL); static DEVICE_ATTR(sustainable_power, S_IWUSR | S_IRUGO, sustainable_power_show, From 7eb4bd723e113d709d04121869964fcd3eb8cb4d Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:19 -0800 Subject: [PATCH 44/76] thermal: core: remove FSF address in the GPL notice Simplify the GPL notice by removing the FSF address. No need to track FSF location in this file. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 01b1a4e064c0..09d521a56c6f 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -5,22 +5,9 @@ * Copyright (C) 2008 Zhang Rui * Copyright (C) 2008 Sujith Thomas * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt From 5027ba36cda6924488b5752820503cdf7a79dbe9 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:20 -0800 Subject: [PATCH 45/76] thermal: core: small style fix when checking for __find_governor() Remove style issue: CHECK: Comparison to NULL could be written "!__find_governor" + if (__find_governor(governor->name) == NULL) { Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 09d521a56c6f..f19fe03e919f 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -136,7 +136,7 @@ int thermal_register_governor(struct thermal_governor *governor) mutex_lock(&thermal_governor_lock); err = -EBUSY; - if (__find_governor(governor->name) == NULL) { + if (!__find_governor(governor->name)) { err = 0; list_add(&governor->governor_list, &thermal_governor_list); if (!def_governor && !strncmp(governor->name, @@ -182,7 +182,7 @@ void thermal_unregister_governor(struct thermal_governor *governor) mutex_lock(&thermal_governor_lock); - if (__find_governor(governor->name) == NULL) + if (!__find_governor(governor->name)) goto exit; mutex_lock(&thermal_list_lock); From eb7be329bd93b7ab09f442ffe3862b014ab84477 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:21 -0800 Subject: [PATCH 46/76] thermal: core: standardize line breaking alignment Pass through the code to remove check suggested by checkpatch.pl (alignment to parenthesis): CHECK: Alignment should match open parenthesis Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index f19fe03e919f..d33b29a1553f 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -137,10 +137,15 @@ int thermal_register_governor(struct thermal_governor *governor) err = -EBUSY; if (!__find_governor(governor->name)) { + bool match_default; + err = 0; list_add(&governor->governor_list, &thermal_governor_list); - if (!def_governor && !strncmp(governor->name, - DEFAULT_THERMAL_GOVERNOR, THERMAL_NAME_LENGTH)) + match_default = !strncmp(governor->name, + DEFAULT_THERMAL_GOVERNOR, + THERMAL_NAME_LENGTH); + + if (!def_governor && match_default) def_governor = governor; } @@ -189,7 +194,7 @@ void thermal_unregister_governor(struct thermal_governor *governor) list_for_each_entry(pos, &thermal_tz_list, node) { if (!strncasecmp(pos->governor->name, governor->name, - THERMAL_NAME_LENGTH)) + THERMAL_NAME_LENGTH)) thermal_set_governor(pos, NULL); } @@ -312,14 +317,15 @@ static void monitor_thermal_zone(struct thermal_zone_device *tz) } static void handle_non_critical_trips(struct thermal_zone_device *tz, - int trip, enum thermal_trip_type trip_type) + int trip, + enum thermal_trip_type trip_type) { tz->governor ? tz->governor->throttle(tz, trip) : def_governor->throttle(tz, trip); } static void handle_critical_trips(struct thermal_zone_device *tz, - int trip, enum thermal_trip_type trip_type) + int trip, enum thermal_trip_type trip_type) { int trip_temp; @@ -797,7 +803,7 @@ static void thermal_release(struct device *dev) tz = to_thermal_zone(dev); kfree(tz); } else if(!strncmp(dev_name(dev), "cooling_device", - sizeof("cooling_device") - 1)){ + sizeof("cooling_device") - 1)) { cdev = to_cooling_device(dev); kfree(cdev); } @@ -1131,11 +1137,11 @@ exit: * in case of error, an ERR_PTR. Caller must check return value with * IS_ERR*() helpers. */ -struct thermal_zone_device *thermal_zone_device_register(const char *type, - int trips, int mask, void *devdata, - struct thermal_zone_device_ops *ops, - struct thermal_zone_params *tzp, - int passive_delay, int polling_delay) +struct thermal_zone_device * +thermal_zone_device_register(const char *type, int trips, int mask, + void *devdata, struct thermal_zone_device_ops *ops, + struct thermal_zone_params *tzp, int passive_delay, + int polling_delay) { struct thermal_zone_device *tz; enum thermal_trip_type trip_type; @@ -1400,7 +1406,7 @@ static struct genl_family thermal_event_genl_family = { }; int thermal_generate_netlink_event(struct thermal_zone_device *tz, - enum events event) + enum events event) { struct sk_buff *skb; struct nlattr *attr; @@ -1477,7 +1483,7 @@ static inline void genetlink_exit(void) {} #endif /* !CONFIG_NET */ static int thermal_pm_notify(struct notifier_block *nb, - unsigned long mode, void *_unused) + unsigned long mode, void *_unused) { struct thermal_zone_device *tz; From 2a0b4c44ce345f5ea8c240d7d3317974301032cd Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:22 -0800 Subject: [PATCH 47/76] thermal: core: remove void function return statements Simply removing useless returns of void functions. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index d33b29a1553f..8365d0b81f77 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -202,7 +202,6 @@ void thermal_unregister_governor(struct thermal_governor *governor) list_del(&governor->governor_list); exit: mutex_unlock(&thermal_governor_lock); - return; } int thermal_zone_device_set_policy(struct thermal_zone_device *tz, @@ -1069,7 +1068,6 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id); device_unregister(&cdev->device); - return; } EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister); From b659a30d7bdd5d9aa0d36b0215bfe3f9961ddf78 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:23 -0800 Subject: [PATCH 48/76] thermal: core: remove style warnings and checks Removing several style issues in thermal code code. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 8365d0b81f77..e3cdba530418 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -718,10 +718,10 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, mutex_lock(&tz->lock); mutex_lock(&cdev->lock); list_for_each_entry(pos, &tz->thermal_instances, tz_node) - if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { - result = -EEXIST; - break; - } + if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { + result = -EEXIST; + break; + } if (!result) { list_add_tail(&dev->tz_node, &tz->thermal_instances); list_add_tail(&dev->cdev_node, &cdev->thermal_instances); @@ -801,8 +801,8 @@ static void thermal_release(struct device *dev) sizeof("thermal_zone") - 1)) { tz = to_thermal_zone(dev); kfree(tz); - } else if(!strncmp(dev_name(dev), "cooling_device", - sizeof("cooling_device") - 1)) { + } else if (!strncmp(dev_name(dev), "cooling_device", + sizeof("cooling_device") - 1)) { cdev = to_cooling_device(dev); kfree(cdev); } @@ -1036,8 +1036,8 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) mutex_lock(&thermal_list_lock); list_for_each_entry(pos, &thermal_cdev_list, node) - if (pos == cdev) - break; + if (pos == cdev) + break; if (pos != cdev) { /* thermal cooling device not found */ mutex_unlock(&thermal_list_lock); @@ -1241,7 +1241,7 @@ thermal_zone_device_register(const char *type, int trips, int mask, /* Bind cooling devices for this zone */ bind_tz(tz); - INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check); + INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check); thermal_zone_device_reset(tz); /* Update the new thermal zone and mark it as already updated. */ @@ -1275,8 +1275,8 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) mutex_lock(&thermal_list_lock); list_for_each_entry(pos, &thermal_tz_list, node) - if (pos == tz) - break; + if (pos == tz) + break; if (pos != tz) { /* thermal zone device not found */ mutex_unlock(&thermal_list_lock); From 38e7b549af829ad2abd94b60e555227a15f9ecff Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:24 -0800 Subject: [PATCH 49/76] thermal: core: improve kerneldoc entry of thermal_cooling_device_unregister Improve description and keep 80 columns limit. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index e3cdba530418..2db21e53d0d0 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1018,11 +1018,11 @@ static void __unbind(struct thermal_zone_device *tz, int mask, } /** - * thermal_cooling_device_unregister - removes the registered thermal cooling device + * thermal_cooling_device_unregister - removes a thermal cooling device * @cdev: the thermal cooling device to remove. * - * thermal_cooling_device_unregister() must be called when the device is no - * longer needed. + * thermal_cooling_device_unregister() must be called when a registered + * thermal cooling device is no longer needed. */ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) { From 95e3ed1513494aa2d0aaba7a99fb7aa8b51dcfc8 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:25 -0800 Subject: [PATCH 50/76] thermal: core: use kzalloc(sizeof(*ptr),...) As a safety check, this patch changes thermal core to check for pointer content size, instead of type size, while allocating memory. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 2db21e53d0d0..4f9384482818 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -674,8 +674,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, if (lower > upper || upper > max_state) return -EINVAL; - dev = - kzalloc(sizeof(struct thermal_instance), GFP_KERNEL); + dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; dev->tz = tz; @@ -917,7 +916,7 @@ __thermal_cooling_device_register(struct device_node *np, !ops->set_cur_state) return ERR_PTR(-EINVAL); - cdev = kzalloc(sizeof(struct thermal_cooling_device), GFP_KERNEL); + cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); if (!cdev) return ERR_PTR(-ENOMEM); @@ -1163,7 +1162,7 @@ thermal_zone_device_register(const char *type, int trips, int mask, if (trips > 0 && (!ops->get_trip_type || !ops->get_trip_temp)) return ERR_PTR(-EINVAL); - tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL); + tz = kzalloc(sizeof(*tz), GFP_KERNEL); if (!tz) return ERR_PTR(-ENOMEM); From b819dc9ef064430118634160330eaa93708aad28 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:26 -0800 Subject: [PATCH 51/76] thermal: sysfs: use kcalloc() instead of kzalloc() Simplify size computation by using kcalloc() for allocating memory for arrays. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_sysfs.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index 4807015c26de..a694de907a26 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -511,7 +511,6 @@ static const struct attribute_group *thermal_zone_attribute_groups[] = { */ static int create_trip_attrs(struct thermal_zone_device *tz, int mask) { - int size = sizeof(struct thermal_attr) * tz->trips; struct attribute **attrs; int indx; @@ -519,18 +518,22 @@ static int create_trip_attrs(struct thermal_zone_device *tz, int mask) if (tz->trips <= 0) return -EINVAL; - tz->trip_type_attrs = kzalloc(size, GFP_KERNEL); + tz->trip_type_attrs = kcalloc(tz->trips, sizeof(*tz->trip_type_attrs), + GFP_KERNEL); if (!tz->trip_type_attrs) return -ENOMEM; - tz->trip_temp_attrs = kzalloc(size, GFP_KERNEL); + tz->trip_temp_attrs = kcalloc(tz->trips, sizeof(*tz->trip_temp_attrs), + GFP_KERNEL); if (!tz->trip_temp_attrs) { kfree(tz->trip_type_attrs); return -ENOMEM; } if (tz->ops->get_trip_hyst) { - tz->trip_hyst_attrs = kzalloc(size, GFP_KERNEL); + tz->trip_hyst_attrs = kcalloc(tz->trips, + sizeof(*tz->trip_hyst_attrs), + GFP_KERNEL); if (!tz->trip_hyst_attrs) { kfree(tz->trip_type_attrs); kfree(tz->trip_temp_attrs); @@ -538,7 +541,7 @@ static int create_trip_attrs(struct thermal_zone_device *tz, int mask) } } - attrs = kzalloc(sizeof(*attrs) * tz->trips * 3 + 1, GFP_KERNEL); + attrs = kcalloc(tz->trips * 3 + 1, sizeof(*attrs), GFP_KERNEL); if (!attrs) { kfree(tz->trip_type_attrs); kfree(tz->trip_temp_attrs); From 373f91d1256db7d8f494fd473c05dbc961a37f23 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 7 Nov 2016 21:09:27 -0800 Subject: [PATCH 52/76] thermal: core: move slop and offset helpers to thermal_helpers.c Reorganize code to reflect better placement. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 30 ------------------------------ drivers/thermal/thermal_helpers.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 4f9384482818..7daffc14949a 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1358,36 +1358,6 @@ exit: } EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name); -/** - * thermal_zone_get_slope - return the slope attribute of the thermal zone - * @tz: thermal zone device with the slope attribute - * - * Return: If the thermal zone device has a slope attribute, return it, else - * return 1. - */ -int thermal_zone_get_slope(struct thermal_zone_device *tz) -{ - if (tz && tz->tzp) - return tz->tzp->slope; - return 1; -} -EXPORT_SYMBOL_GPL(thermal_zone_get_slope); - -/** - * thermal_zone_get_offset - return the offset attribute of the thermal zone - * @tz: thermal zone device with the offset attribute - * - * Return: If the thermal zone device has a offset attribute, return it, else - * return 0. - */ -int thermal_zone_get_offset(struct thermal_zone_device *tz) -{ - if (tz && tz->tzp) - return tz->tzp->offset; - return 0; -} -EXPORT_SYMBOL_GPL(thermal_zone_get_offset); - #ifdef CONFIG_NET static const struct genl_multicast_group thermal_event_mcgrps[] = { { .name = THERMAL_GENL_MCAST_GROUP_NAME, }, diff --git a/drivers/thermal/thermal_helpers.c b/drivers/thermal/thermal_helpers.c index dfdba5a720cd..8cdf75adcce1 100644 --- a/drivers/thermal/thermal_helpers.c +++ b/drivers/thermal/thermal_helpers.c @@ -194,3 +194,33 @@ void thermal_cdev_update(struct thermal_cooling_device *cdev) dev_dbg(&cdev->device, "set to state %lu\n", target); } EXPORT_SYMBOL(thermal_cdev_update); + +/** + * thermal_zone_get_slope - return the slope attribute of the thermal zone + * @tz: thermal zone device with the slope attribute + * + * Return: If the thermal zone device has a slope attribute, return it, else + * return 1. + */ +int thermal_zone_get_slope(struct thermal_zone_device *tz) +{ + if (tz && tz->tzp) + return tz->tzp->slope; + return 1; +} +EXPORT_SYMBOL_GPL(thermal_zone_get_slope); + +/** + * thermal_zone_get_offset - return the offset attribute of the thermal zone + * @tz: thermal zone device with the offset attribute + * + * Return: If the thermal zone device has a offset attribute, return it, else + * return 0. + */ +int thermal_zone_get_offset(struct thermal_zone_device *tz) +{ + if (tz && tz->tzp) + return tz->tzp->offset; + return 0; +} +EXPORT_SYMBOL_GPL(thermal_zone_get_offset); From 882f5815de0c8983010c20ea8ef3b204701cb6b0 Mon Sep 17 00:00:00 2001 From: Luis Henriques Date: Wed, 16 Nov 2016 22:15:22 +0000 Subject: [PATCH 53/76] thermal: ti-soc-thermal: add missing clk_put() This patch fixes the following Coccinelle error: ./drivers/thermal/ti-soc-thermal/ti-bandgap.c:1441:1-7: \ ERROR: missing clk_put; clk_get on line 1290 \ and execution via conditional on line 1298 Signed-off-by: Luis Henriques Signed-off-by: Eduardo Valentin --- drivers/thermal/ti-soc-thermal/ti-bandgap.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/ti-soc-thermal/ti-bandgap.c b/drivers/thermal/ti-soc-thermal/ti-bandgap.c index 06ea9766a70a..ba9c302454fb 100644 --- a/drivers/thermal/ti-soc-thermal/ti-bandgap.c +++ b/drivers/thermal/ti-soc-thermal/ti-bandgap.c @@ -1298,7 +1298,7 @@ int ti_bandgap_probe(struct platform_device *pdev) if (IS_ERR(bgp->div_clk)) { dev_err(&pdev->dev, "failed to request div_ts_ck clock ref\n"); ret = PTR_ERR(bgp->div_clk); - goto free_irqs; + goto put_fclock; } for (i = 0; i < bgp->conf->sensor_count; i++) { @@ -1430,8 +1430,9 @@ disable_clk: if (TI_BANDGAP_HAS(bgp, CLK_CTRL)) clk_disable_unprepare(bgp->fclock); put_clks: - clk_put(bgp->fclock); clk_put(bgp->div_clk); +put_fclock: + clk_put(bgp->fclock); free_irqs: if (TI_BANDGAP_HAS(bgp, TSHUT)) { free_irq(gpio_to_irq(bgp->tshut_gpio), NULL); From f05f4821268d9c5904fe60c859390360b2898783 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Wed, 31 Aug 2016 16:50:16 +0800 Subject: [PATCH 54/76] thermal: hisilicon: fix for dependency The thermal driver is standalone driver which is used to enable thermal sensors, so it can be used with any cooling device and should not bind with CPU cooling device driver. This original patch is suggested by Amit Kucheria; so it's to polish the dependency in Kconfig, and remove the dependency with CPU_THERMAL. Signed-off-by: Leo Yan Signed-off-by: Eduardo Valentin --- drivers/thermal/Kconfig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index a13541bdc726..c2c056cc7ea5 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -177,8 +177,10 @@ config THERMAL_EMULATION config HISI_THERMAL tristate "Hisilicon thermal driver" - depends on (ARCH_HISI && CPU_THERMAL && OF) || COMPILE_TEST + depends on ARCH_HISI || COMPILE_TEST depends on HAS_IOMEM + depends on OF + default y help Enable this to plug hisilicon's thermal sensor driver into the Linux thermal framework. cpufreq is used as the cooling device to throttle From cd323b2b5aba0d9920b116866efc7ebfd2d2052c Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 18 Oct 2016 16:40:19 -0700 Subject: [PATCH 55/76] thermal: qcom-spmi: Treat reg property as a single cell We only read the first element of the reg property to figure out the offset of the temperature sensor inside the PMIC. Furthermore, we want to remove the second element in DT, so just don't read the second element so that probe keeps working if we change the DT in the future. Cc: Ivan T. Ivanov Signed-off-by: Stephen Boyd Signed-off-by: Eduardo Valentin --- drivers/thermal/qcom-spmi-temp-alarm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/thermal/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom-spmi-temp-alarm.c index 819c6d5d7aa7..f50241962ad2 100644 --- a/drivers/thermal/qcom-spmi-temp-alarm.c +++ b/drivers/thermal/qcom-spmi-temp-alarm.c @@ -200,7 +200,7 @@ static int qpnp_tm_probe(struct platform_device *pdev) struct qpnp_tm_chip *chip; struct device_node *node; u8 type, subtype; - u32 res[2]; + u32 res; int ret, irq; node = pdev->dev.of_node; @@ -215,7 +215,7 @@ static int qpnp_tm_probe(struct platform_device *pdev) if (!chip->map) return -ENXIO; - ret = of_property_read_u32_array(node, "reg", res, 2); + ret = of_property_read_u32(node, "reg", &res); if (ret < 0) return ret; @@ -228,7 +228,7 @@ static int qpnp_tm_probe(struct platform_device *pdev) if (PTR_ERR(chip->adc) == -EPROBE_DEFER) return PTR_ERR(chip->adc); - chip->base = res[0]; + chip->base = res; ret = qpnp_tm_read(chip, QPNP_TM_REG_TYPE, &type); if (ret < 0) { From 3f2883e5bf12e2920f451d1ae831e44583e00364 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Fri, 14 Oct 2016 11:35:00 -0300 Subject: [PATCH 56/76] thermal: max77620: Fix module autoload If the driver is built as a module, autoload won't work because the module alias information is not filled. So user-space can't match the registered device with the corresponding module. Export the module alias information using the MODULE_DEVICE_TABLE() macro. Before this patch: $ modinfo drivers/thermal/max77620_thermal.ko | grep alias $ After this patch: $ modinfo drivers/thermal/max77620_thermal.ko | grep alias alias: platform:max77620-thermal Signed-off-by: Javier Martinez Canillas Signed-off-by: Eduardo Valentin --- drivers/thermal/max77620_thermal.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/thermal/max77620_thermal.c b/drivers/thermal/max77620_thermal.c index 83905ff46e40..e9a1fe342760 100644 --- a/drivers/thermal/max77620_thermal.c +++ b/drivers/thermal/max77620_thermal.c @@ -149,6 +149,7 @@ static struct platform_device_id max77620_thermal_devtype[] = { { .name = "max77620-thermal", }, {}, }; +MODULE_DEVICE_TABLE(platform, max77620_thermal_devtype); static struct platform_driver max77620_thermal_driver = { .driver = { From 26d9339db03aca7dbf71c794d2d07c97e6c8ef00 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Fri, 14 Oct 2016 11:35:01 -0300 Subject: [PATCH 57/76] thermal: tango: Fix module autoload If the driver is built as a module, autoload won't work because the module alias information is not filled. So user-space can't match the registered device with the corresponding module. Export the module alias information using the MODULE_DEVICE_TABLE() macro. Before this patch: $ modinfo drivers/thermal/tango_thermal.ko | grep alias $ After this patch: $ modinfo drivers/thermal/tango_thermal.ko | grep alias alias: of:N*T*Csigma,smp8758-thermalC* alias: of:N*T*Csigma,smp8758-thermal Acked-by: Marc Gonzalez Signed-off-by: Javier Martinez Canillas Signed-off-by: Eduardo Valentin --- drivers/thermal/tango_thermal.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/thermal/tango_thermal.c b/drivers/thermal/tango_thermal.c index 201304aeafeb..4e67795cb6ce 100644 --- a/drivers/thermal/tango_thermal.c +++ b/drivers/thermal/tango_thermal.c @@ -107,6 +107,7 @@ static const struct of_device_id tango_sensor_ids[] = { }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, tango_sensor_ids); static struct platform_driver tango_thermal_driver = { .probe = tango_thermal_probe, From 8093a116b4be47169b9094e341ad8af7a659d79c Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Fri, 14 Oct 2016 11:35:02 -0300 Subject: [PATCH 58/76] thermal: db8500: Fix module autoload If the driver is built as a module, autoload won't work because the module alias information is not filled. So user-space can't match the registered device with the corresponding module. Export the module alias information using the MODULE_DEVICE_TABLE() macro. Before this patch: $ modinfo drivers/thermal/db8500_thermal.ko | grep alias $ After this patch: $ modinfo drivers/thermal/db8500_thermal.ko | grep alias alias: of:N*T*Cstericsson,db8500-thermalC* alias: of:N*T*Cstericsson,db8500-thermal Signed-off-by: Javier Martinez Canillas Signed-off-by: Eduardo Valentin --- drivers/thermal/db8500_thermal.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/thermal/db8500_thermal.c b/drivers/thermal/db8500_thermal.c index e776cea80cfc..f491faf16592 100644 --- a/drivers/thermal/db8500_thermal.c +++ b/drivers/thermal/db8500_thermal.c @@ -512,6 +512,7 @@ static const struct of_device_id db8500_thermal_match[] = { { .compatible = "stericsson,db8500-thermal" }, {}, }; +MODULE_DEVICE_TABLE(of, db8500_thermal_match); #endif static struct platform_driver db8500_thermal_driver = { From ce62abaebed23f987382a41284a7788bfb9c8143 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 11 Oct 2016 10:07:34 +0800 Subject: [PATCH 59/76] thermal: rockchip: improve the warning log It is no necessary to print warning agian and again if we don't add rockchip,grf for dt, otherwise I saw the following log when doing suspend-2-resume. We only need to print it once when parsing dt. It looks quite trivial but the log is apparently verbose. [ 26.615415] PM: early resume of devices complete after 1.539 msecs [ 26.622002] rk_tsadcv2_initialize: Missing rockchip,grf property [ 26.629359] rk_gmac-dwmac ff290000.ethernet: init for RGMII [ 26.639794] PM: resume of devices complete after 18.109 msecs [ 26.646925] Restarting tasks ... done. Reviewed-by: Caesar Wang Reviewed-by: Heiko Stuebner Signed-off-by: Shawn Lin Signed-off-by: Eduardo Valentin --- drivers/thermal/rockchip_thermal.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c index e227a9f0acf7..b811b0fb61b1 100644 --- a/drivers/thermal/rockchip_thermal.c +++ b/drivers/thermal/rockchip_thermal.c @@ -524,11 +524,6 @@ static void rk_tsadcv2_initialize(struct regmap *grf, void __iomem *regs, regs + TSADCV2_AUTO_PERIOD_HT); writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT, regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE); - - if (IS_ERR(grf)) { - pr_warn("%s: Missing rockchip,grf property\n", __func__); - return; - } } /** @@ -971,6 +966,8 @@ static int rockchip_configure_from_dt(struct device *dev, * need this property. */ thermal->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(thermal->grf)) + dev_warn(dev, "Missing rockchip,grf property\n"); return 0; } From ecaa015c7ab3e4e8522c181a5e211b6bd043946f Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 11 Oct 2016 10:07:35 +0800 Subject: [PATCH 60/76] dt-bindings: rockchip-thermal: fix the misleading description "rockchip,hw-tshut-temp", "rockchip,hw-tshut-mode" and "rockchip,hw-tshut-polarity" are not a required properties actually as the code could also work by loading the default settings there. So it is apprently misleading, although we prefer to get these from DT. And it seems we miss the 'rockchip,grf' here which should also be an optional property. Acked-by: Rob Herring Reviewed-by: Heiko Stuebner Signed-off-by: Shawn Lin Signed-off-by: Eduardo Valentin --- Documentation/devicetree/bindings/thermal/rockchip-thermal.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt b/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt index 08efe6bc2193..43003aec94bd 100644 --- a/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt +++ b/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt @@ -22,10 +22,13 @@ Required properties: TSADC controller. - pinctrl-2 : The "sleep" pinctrl state, it will be in for suspend. - #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description. + +Optional properties: - rockchip,hw-tshut-temp : The hardware-controlled shutdown temperature value. - rockchip,hw-tshut-mode : The hardware-controlled shutdown mode 0:CRU 1:GPIO. - rockchip,hw-tshut-polarity : The hardware-controlled active polarity 0:LOW 1:HIGH. +- rockchip,grf : The phandle of the syscon node for the general register file. Exiample: tsadc: tsadc@ff280000 { From 1cea4e7776b7e9096b696eeb66364f51a8d321ec Mon Sep 17 00:00:00 2001 From: Lukasz Luba Date: Thu, 15 Sep 2016 15:44:22 +0100 Subject: [PATCH 61/76] devfreq_cooling: make the structs devfreq_cooling_xxx visible for all MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the protection #ifdef CONFIG_DEVFREQ_THERMAL cuts the needed structures devfreq_cooling_ops and devfreq_cooling_device. The functions which are supposed to provide the empty implementation complain about unknown structures. Similar solution is present in include/linux/devfreq.h. Reviewed-by: Ørjan Eide Signed-off-by: Lukasz Luba Signed-off-by: Eduardo Valentin --- include/linux/devfreq_cooling.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/devfreq_cooling.h b/include/linux/devfreq_cooling.h index 7adf6cc4b305..3049f9422507 100644 --- a/include/linux/devfreq_cooling.h +++ b/include/linux/devfreq_cooling.h @@ -20,7 +20,6 @@ #include #include -#ifdef CONFIG_DEVFREQ_THERMAL /** * struct devfreq_cooling_power - Devfreq cooling power ops @@ -43,6 +42,8 @@ struct devfreq_cooling_power { unsigned long dyn_power_coeff; }; +#ifdef CONFIG_DEVFREQ_THERMAL + struct thermal_cooling_device * of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df, struct devfreq_cooling_power *dfc_power); From 3aa5374376746f20a27be8682be7f91c8b71d1d8 Mon Sep 17 00:00:00 2001 From: Javi Merino Date: Thu, 15 Sep 2016 15:44:23 +0100 Subject: [PATCH 62/76] devfreq_cooling: pass a pointer to devfreq in the power model callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the devfreq cooling device was designed, it was an oversight not to pass a pointer to the struct devfreq as the first parameters of the callbacks. The design patterns of the kernel suggest it for a good reason. By passing a pointer to struct devfreq, the driver can register one function that works with multiple devices. With the current implementation, a driver that can work with multiple devices has to create multiple copies of the same function with different parameters so that each devfreq_cooling_device can use the appropriate one. By passing a pointer to struct devfreq, the driver can identify which device it's referring to. Cc: Zhang Rui Cc: Eduardo Valentin Reviewed-by: Punit Agrawal Reviewed-by: Ørjan Eide Reviewed-by: Lukasz Luba Signed-off-by: Javi Merino Signed-off-by: Eduardo Valentin --- drivers/thermal/devfreq_cooling.c | 5 +++-- include/linux/devfreq_cooling.h | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/thermal/devfreq_cooling.c b/drivers/thermal/devfreq_cooling.c index 81631b110e17..5a737fd5f1aa 100644 --- a/drivers/thermal/devfreq_cooling.c +++ b/drivers/thermal/devfreq_cooling.c @@ -238,7 +238,7 @@ get_static_power(struct devfreq_cooling_device *dfc, unsigned long freq) return 0; } - return dfc->power_ops->get_static_power(voltage); + return dfc->power_ops->get_static_power(df, voltage); } /** @@ -262,7 +262,8 @@ get_dynamic_power(struct devfreq_cooling_device *dfc, unsigned long freq, struct devfreq_cooling_power *dfc_power = dfc->power_ops; if (dfc_power->get_dynamic_power) - return dfc_power->get_dynamic_power(freq, voltage); + return dfc_power->get_dynamic_power(dfc->devfreq, freq, + voltage); freq_mhz = freq / 1000000; power = (u64)dfc_power->dyn_power_coeff * freq_mhz * voltage * voltage; diff --git a/include/linux/devfreq_cooling.h b/include/linux/devfreq_cooling.h index 3049f9422507..c35d0c0e0ada 100644 --- a/include/linux/devfreq_cooling.h +++ b/include/linux/devfreq_cooling.h @@ -36,8 +36,10 @@ * @dyn_power_coeff * frequency * voltage^2 */ struct devfreq_cooling_power { - unsigned long (*get_static_power)(unsigned long voltage); - unsigned long (*get_dynamic_power)(unsigned long freq, + unsigned long (*get_static_power)(struct devfreq *devfreq, + unsigned long voltage); + unsigned long (*get_dynamic_power)(struct devfreq *devfreq, + unsigned long freq, unsigned long voltage); unsigned long dyn_power_coeff; }; From f37fabb8643eaf8e3b613333a72f683770c85eca Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 22 Nov 2016 19:22:44 +0200 Subject: [PATCH 63/76] thermal: hwmon: Properly report critical temperature in sysfs In the critical sysfs entry the thermal hwmon was returning wrong temperature to the user-space. It was reporting the temperature of the first trip point instead of the temperature of critical trip point. For example: /sys/class/hwmon/hwmon0/temp1_crit:50000 /sys/class/thermal/thermal_zone0/trip_point_0_temp:50000 /sys/class/thermal/thermal_zone0/trip_point_0_type:active /sys/class/thermal/thermal_zone0/trip_point_3_temp:120000 /sys/class/thermal/thermal_zone0/trip_point_3_type:critical Since commit e68b16abd91d ("thermal: add hwmon sysfs I/F") the driver have been registering a sysfs entry if get_crit_temp() callback was provided. However when accessed, it was calling get_trip_temp() instead of the get_crit_temp(). Fixes: e68b16abd91d ("thermal: add hwmon sysfs I/F") Cc: Signed-off-by: Krzysztof Kozlowski Signed-off-by: Zhang Rui --- drivers/thermal/thermal_hwmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c index 8c45de6a9140..541af5946203 100644 --- a/drivers/thermal/thermal_hwmon.c +++ b/drivers/thermal/thermal_hwmon.c @@ -98,7 +98,7 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf) int temperature; int ret; - ret = tz->ops->get_trip_temp(tz, 0, &temperature); + ret = tz->ops->get_crit_temp(tz, &temperature); if (ret) return ret; From 768bd13c93c043a7793dca6d98ba75572bb9b956 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 22 Nov 2016 17:57:04 +0000 Subject: [PATCH 64/76] thermal/x86_pkg_temp: Cleanup thermal interrupt handling Wenn a package is removed nothing restores the thermal interrupt MSR so the content will be stale when a CPU of that package becomes online again. Aside of that the work function reenables interrupts before acknowledging the current one, which is the wrong order to begin with. Signed-off-by: Thomas Gleixner Tested-by: Srinivas Pandruvada Signed-off-by: Zhang Rui --- drivers/thermal/x86_pkg_temp_thermal.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c index 95f4c1bcdb4c..bbaade89b8ad 100644 --- a/drivers/thermal/x86_pkg_temp_thermal.c +++ b/drivers/thermal/x86_pkg_temp_thermal.c @@ -334,7 +334,6 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work) pkg_work_scheduled[phy_id] = 0; spin_unlock_irqrestore(&pkg_work_lock, flags); - enable_pkg_thres_interrupt(); rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr_val); if (msr_val & THERM_LOG_THRESHOLD0) { wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS, @@ -346,6 +345,9 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work) msr_val & ~THERM_LOG_THRESHOLD1); notify = true; } + + enable_pkg_thres_interrupt(); + if (notify) { pr_debug("thermal_zone_device_update\n"); thermal_zone_device_update(phdev->tzone, @@ -505,6 +507,13 @@ static int pkg_temp_thermal_device_remove(unsigned int cpu) list_for_each_entry_safe(phdev, n, &phy_dev_list, list) { if (phdev->phys_proc_id == phys_proc_id) { thermal_zone_device_unregister(phdev->tzone); + /* + * Restore original MSR value for package + * thermal interrupt. + */ + wrmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, + phdev->start_pkg_therm_low, + phdev->start_pkg_therm_high); list_del(&phdev->list); kfree(phdev); break; From 89baa56be7fad333db0a91198bf4026017ab737d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 22 Nov 2016 17:57:05 +0000 Subject: [PATCH 65/76] thermal/x86_pkg_temp: Remove redundant package search In pkg_temp_thermal_device_remove() the package device is searched at the beginning of the function. When the device refcount becomes zero another search for the same device is conducted. Remove the pointless loop and use the device pointer which was retrieved at the beginning of the function. Signed-off-by: Thomas Gleixner Tested-by: Srinivas Pandruvada Signed-off-by: Zhang Rui --- drivers/thermal/x86_pkg_temp_thermal.c | 33 +++++++++++--------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c index bbaade89b8ad..95573b2ef201 100644 --- a/drivers/thermal/x86_pkg_temp_thermal.c +++ b/drivers/thermal/x86_pkg_temp_thermal.c @@ -479,10 +479,8 @@ err_ret: static int pkg_temp_thermal_device_remove(unsigned int cpu) { - struct phy_dev_entry *n; + struct phy_dev_entry *phdev = pkg_temp_thermal_get_phy_entry(cpu); u16 phys_proc_id = topology_physical_package_id(cpu); - struct phy_dev_entry *phdev = - pkg_temp_thermal_get_phy_entry(cpu); if (!phdev) return -ENODEV; @@ -503,22 +501,19 @@ static int pkg_temp_thermal_device_remove(unsigned int cpu) --phdev->ref_cnt; pr_debug("thermal_device_remove: pkg: %d cpu %d ref_cnt %d\n", phys_proc_id, cpu, phdev->ref_cnt); - if (!phdev->ref_cnt) - list_for_each_entry_safe(phdev, n, &phy_dev_list, list) { - if (phdev->phys_proc_id == phys_proc_id) { - thermal_zone_device_unregister(phdev->tzone); - /* - * Restore original MSR value for package - * thermal interrupt. - */ - wrmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, - phdev->start_pkg_therm_low, - phdev->start_pkg_therm_high); - list_del(&phdev->list); - kfree(phdev); - break; - } - } + + if (!phdev->ref_cnt) { + thermal_zone_device_unregister(phdev->tzone); + /* + * Restore original MSR value for package thermal + * interrupt. + */ + wrmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, + phdev->start_pkg_therm_low, + phdev->start_pkg_therm_high); + list_del(&phdev->list); + kfree(phdev); + } mutex_unlock(&phy_dev_list_mutex); return 0; From 21a3d3d4c8f373054421443e244c75a94a40b1ae Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 22 Nov 2016 17:57:06 +0000 Subject: [PATCH 66/76] thermal/x86_pkg_temp: Replace open coded cpu search find_next_sibling() iterates over the online cpus and searches for a cpu with the same package id as the current cpu. This is a pointless exercise as topology_core_cpumask() allows a simple cpumask search for an online cpu on the same package. Signed-off-by: Thomas Gleixner Tested-by: Srinivas Pandruvada Signed-off-by: Zhang Rui --- drivers/thermal/x86_pkg_temp_thermal.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c index 95573b2ef201..eea94aabff6f 100644 --- a/drivers/thermal/x86_pkg_temp_thermal.c +++ b/drivers/thermal/x86_pkg_temp_thermal.c @@ -384,18 +384,6 @@ static int pkg_temp_thermal_platform_thermal_notify(__u64 msr_val) return 0; } -static int find_siblings_cpu(int cpu) -{ - int i; - int id = topology_physical_package_id(cpu); - - for_each_online_cpu(i) - if (i != cpu && topology_physical_package_id(i) == id) - return i; - - return 0; -} - static int pkg_temp_thermal_device_add(unsigned int cpu) { int err; @@ -488,9 +476,10 @@ static int pkg_temp_thermal_device_remove(unsigned int cpu) mutex_lock(&phy_dev_list_mutex); /* If we are loosing the first cpu for this package, we need change */ if (phdev->first_cpu == cpu) { - phdev->first_cpu = find_siblings_cpu(cpu); - pr_debug("thermal_device_remove: first cpu switched %d\n", - phdev->first_cpu); + int target = cpumask_any_but(topology_core_cpumask(cpu), cpu); + + phdev->first_cpu = target; + pr_debug("CPU %d down. New first_cpu%d\n", cpu, target); } /* * It is possible that no siblings left as this was the last cpu From 09a674cd695f93bbbd09f24b5e13127c5907ef0c Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 22 Nov 2016 17:57:06 +0000 Subject: [PATCH 67/76] thermal/x86_pkg_temp: Sanitize callback (de)initialization The threshold callbacks are installed before the initialization of the online cpus has succeeded and removed after the teardown has been done. That's both wrong as callbacks might be invoked into a half initialized or torn down state. Move them to the proper places: Last in init() and first in exit(). While at it shorten the insane long and horrible named function names. Signed-off-by: Thomas Gleixner Tested-by: Srinivas Pandruvada Signed-off-by: Zhang Rui --- drivers/thermal/x86_pkg_temp_thermal.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c index eea94aabff6f..78b59fef255a 100644 --- a/drivers/thermal/x86_pkg_temp_thermal.c +++ b/drivers/thermal/x86_pkg_temp_thermal.c @@ -281,7 +281,7 @@ static struct thermal_zone_device_ops tzone_ops = { .set_trip_temp = sys_set_trip_temp, }; -static bool pkg_temp_thermal_platform_thermal_rate_control(void) +static bool pkg_thermal_rate_control(void) { return true; } @@ -355,7 +355,7 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work) } } -static int pkg_temp_thermal_platform_thermal_notify(__u64 msr_val) +static int pkg_thermal_notify(__u64 msr_val) { unsigned long flags; int cpu = smp_processor_id(); @@ -579,10 +579,6 @@ static int __init pkg_temp_thermal_init(void) return -ENODEV; spin_lock_init(&pkg_work_lock); - platform_thermal_package_notify = - pkg_temp_thermal_platform_thermal_notify; - platform_thermal_package_rate_control = - pkg_temp_thermal_platform_thermal_rate_control; cpu_notifier_register_begin(); for_each_online_cpu(i) @@ -591,6 +587,9 @@ static int __init pkg_temp_thermal_init(void) __register_hotcpu_notifier(&pkg_temp_thermal_notifier); cpu_notifier_register_done(); + platform_thermal_package_notify = pkg_thermal_notify; + platform_thermal_package_rate_control = pkg_thermal_rate_control; + pkg_temp_debugfs_init(); /* Don't care if fails */ return 0; @@ -600,9 +599,6 @@ err_ret: put_core_offline(i); cpu_notifier_register_done(); kfree(pkg_work_scheduled); - platform_thermal_package_notify = NULL; - platform_thermal_package_rate_control = NULL; - return -ENODEV; } @@ -611,6 +607,9 @@ static void __exit pkg_temp_thermal_exit(void) struct phy_dev_entry *phdev, *n; int i; + platform_thermal_package_notify = NULL; + platform_thermal_package_rate_control = NULL; + cpu_notifier_register_begin(); __unregister_hotcpu_notifier(&pkg_temp_thermal_notifier); mutex_lock(&phy_dev_list_mutex); @@ -625,8 +624,6 @@ static void __exit pkg_temp_thermal_exit(void) kfree(phdev); } mutex_unlock(&phy_dev_list_mutex); - platform_thermal_package_notify = NULL; - platform_thermal_package_rate_control = NULL; for_each_online_cpu(i) cancel_delayed_work_sync( &per_cpu(pkg_temp_thermal_threshold_work, i)); From b6badbea30bb839624d8bed43e8b7711bc3dd317 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 22 Nov 2016 17:57:07 +0000 Subject: [PATCH 68/76] thermal/x86_pkg_temp: Get rid of ref counting There is no point in the whole package data refcounting dance because topology_core_cpumask tells us whether this is the last cpu in the package. If yes, then the package can go, if not it stays. It's already serialized via the hotplug code. While at it rename the first_cpu member of the package structure to cpu. The first has absolutely no meaning. Signed-off-by: Thomas Gleixner Tested-by: Srinivas Pandruvada Signed-off-by: Zhang Rui --- drivers/thermal/x86_pkg_temp_thermal.c | 62 +++++++++----------------- 1 file changed, 20 insertions(+), 42 deletions(-) diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c index 78b59fef255a..7fdf50e43d40 100644 --- a/drivers/thermal/x86_pkg_temp_thermal.c +++ b/drivers/thermal/x86_pkg_temp_thermal.c @@ -60,9 +60,8 @@ MODULE_PARM_DESC(notify_delay_ms, struct phy_dev_entry { struct list_head list; u16 phys_proc_id; - u16 first_cpu; + u16 cpu; u32 tj_max; - int ref_cnt; u32 start_pkg_therm_low; u32 start_pkg_therm_high; struct thermal_zone_device *tzone; @@ -170,8 +169,8 @@ static int sys_get_curr_temp(struct thermal_zone_device *tzd, int *temp) struct phy_dev_entry *phy_dev_entry; phy_dev_entry = tzd->devdata; - rdmsr_on_cpu(phy_dev_entry->first_cpu, MSR_IA32_PACKAGE_THERM_STATUS, - &eax, &edx); + rdmsr_on_cpu(phy_dev_entry->cpu, MSR_IA32_PACKAGE_THERM_STATUS, + &eax, &edx); if (eax & 0x80000000) { *temp = phy_dev_entry->tj_max - ((eax >> 16) & 0x7f) * 1000; @@ -204,8 +203,8 @@ static int sys_get_trip_temp(struct thermal_zone_device *tzd, shift = THERM_SHIFT_THRESHOLD0; } - ret = rdmsr_on_cpu(phy_dev_entry->first_cpu, - MSR_IA32_PACKAGE_THERM_INTERRUPT, &eax, &edx); + ret = rdmsr_on_cpu(phy_dev_entry->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, + &eax, &edx); if (ret < 0) return -EINVAL; @@ -232,9 +231,8 @@ static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, if (trip >= MAX_NUMBER_OF_TRIPS || temp >= phy_dev_entry->tj_max) return -EINVAL; - ret = rdmsr_on_cpu(phy_dev_entry->first_cpu, - MSR_IA32_PACKAGE_THERM_INTERRUPT, - &l, &h); + ret = rdmsr_on_cpu(phy_dev_entry->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, + &l, &h); if (ret < 0) return -EINVAL; @@ -259,9 +257,8 @@ static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, l |= intr; } - return wrmsr_on_cpu(phy_dev_entry->first_cpu, - MSR_IA32_PACKAGE_THERM_INTERRUPT, - l, h); + return wrmsr_on_cpu(phy_dev_entry->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, + l, h); } static int sys_get_trip_type(struct thermal_zone_device *thermal, @@ -431,9 +428,8 @@ static int pkg_temp_thermal_device_add(unsigned int cpu) spin_unlock_irqrestore(&pkg_work_lock, flags); phy_dev_entry->phys_proc_id = topology_physical_package_id(cpu); - phy_dev_entry->first_cpu = cpu; + phy_dev_entry->cpu = cpu; phy_dev_entry->tj_max = tj_max; - phy_dev_entry->ref_cnt = 1; phy_dev_entry->tzone = thermal_zone_device_register("x86_pkg_temp", thres_count, (thres_count == MAX_NUMBER_OF_TRIPS) ? @@ -468,30 +464,16 @@ err_ret: static int pkg_temp_thermal_device_remove(unsigned int cpu) { struct phy_dev_entry *phdev = pkg_temp_thermal_get_phy_entry(cpu); - u16 phys_proc_id = topology_physical_package_id(cpu); + int target; if (!phdev) return -ENODEV; mutex_lock(&phy_dev_list_mutex); - /* If we are loosing the first cpu for this package, we need change */ - if (phdev->first_cpu == cpu) { - int target = cpumask_any_but(topology_core_cpumask(cpu), cpu); - phdev->first_cpu = target; - pr_debug("CPU %d down. New first_cpu%d\n", cpu, target); - } - /* - * It is possible that no siblings left as this was the last cpu - * going offline. We don't need to worry about this assignment - * as the phydev entry will be removed in this case and - * thermal zone is removed. - */ - --phdev->ref_cnt; - pr_debug("thermal_device_remove: pkg: %d cpu %d ref_cnt %d\n", - phys_proc_id, cpu, phdev->ref_cnt); - - if (!phdev->ref_cnt) { + target = cpumask_any_but(topology_core_cpumask(cpu), cpu); + /* This might be the last cpu in this package */ + if (target >= nr_cpu_ids) { thermal_zone_device_unregister(phdev->tzone); /* * Restore original MSR value for package thermal @@ -502,7 +484,10 @@ static int pkg_temp_thermal_device_remove(unsigned int cpu) phdev->start_pkg_therm_high); list_del(&phdev->list); kfree(phdev); + } else if (phdev->cpu == cpu) { + phdev->cpu = target; } + mutex_unlock(&phy_dev_list_mutex); return 0; @@ -520,12 +505,6 @@ static int get_core_online(unsigned int cpu) return -ENODEV; if (pkg_temp_thermal_device_add(cpu)) return -ENODEV; - } else { - mutex_lock(&phy_dev_list_mutex); - ++phdev->ref_cnt; - pr_debug("get_core_online: cpu %d ref_cnt %d\n", - cpu, phdev->ref_cnt); - mutex_unlock(&phy_dev_list_mutex); } INIT_DELAYED_WORK(&per_cpu(pkg_temp_thermal_threshold_work, cpu), pkg_temp_thermal_threshold_work_fn); @@ -615,10 +594,9 @@ static void __exit pkg_temp_thermal_exit(void) mutex_lock(&phy_dev_list_mutex); list_for_each_entry_safe(phdev, n, &phy_dev_list, list) { /* Retore old MSR value for package thermal interrupt */ - wrmsr_on_cpu(phdev->first_cpu, - MSR_IA32_PACKAGE_THERM_INTERRUPT, - phdev->start_pkg_therm_low, - phdev->start_pkg_therm_high); + wrmsr_on_cpu(phdev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, + phdev->start_pkg_therm_low, + phdev->start_pkg_therm_high); thermal_zone_device_unregister(phdev->tzone); list_del(&phdev->list); kfree(phdev); From 3883a64e3811981d3fcaf6621a84cd0fa63f700d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 22 Nov 2016 17:57:08 +0000 Subject: [PATCH 69/76] thermal/x86_pkg_temp: Cleanup namespace Any randomly chosen struct name is more descriptive than phy_dev_entry. Rename the whole thing to struct pkg_device, which describes the content reasonably well and use the same variable name throughout the code so it gets readable. Rename the msr struct members as well. No functional change. Signed-off-by: Thomas Gleixner Tested-by: Srinivas Pandruvada Signed-off-by: Zhang Rui --- drivers/thermal/x86_pkg_temp_thermal.c | 166 +++++++++++-------------- 1 file changed, 76 insertions(+), 90 deletions(-) diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c index 7fdf50e43d40..f89a86f5c982 100644 --- a/drivers/thermal/x86_pkg_temp_thermal.c +++ b/drivers/thermal/x86_pkg_temp_thermal.c @@ -57,14 +57,14 @@ MODULE_PARM_DESC(notify_delay_ms, /* Limit number of package temp zones */ #define MAX_PKG_TEMP_ZONE_IDS 256 -struct phy_dev_entry { - struct list_head list; - u16 phys_proc_id; - u16 cpu; - u32 tj_max; - u32 start_pkg_therm_low; - u32 start_pkg_therm_high; - struct thermal_zone_device *tzone; +struct pkg_device { + struct list_head list; + u16 phys_proc_id; + u16 cpu; + u32 tj_max; + u32 msr_pkg_therm_low; + u32 msr_pkg_therm_high; + struct thermal_zone_device *tzone; }; static struct thermal_zone_params pkg_temp_tz_params = { @@ -115,18 +115,17 @@ err_out: return -ENOENT; } -static struct phy_dev_entry - *pkg_temp_thermal_get_phy_entry(unsigned int cpu) +static struct pkg_device *pkg_temp_thermal_get_dev(unsigned int cpu) { u16 phys_proc_id = topology_physical_package_id(cpu); - struct phy_dev_entry *phy_ptr; + struct pkg_device *pkgdev; mutex_lock(&phy_dev_list_mutex); - list_for_each_entry(phy_ptr, &phy_dev_list, list) - if (phy_ptr->phys_proc_id == phys_proc_id) { + list_for_each_entry(pkgdev, &phy_dev_list, list) + if (pkgdev->phys_proc_id == phys_proc_id) { mutex_unlock(&phy_dev_list_mutex); - return phy_ptr; + return pkgdev; } mutex_unlock(&phy_dev_list_mutex); @@ -165,36 +164,29 @@ err_ret: static int sys_get_curr_temp(struct thermal_zone_device *tzd, int *temp) { + struct pkg_device *pkgdev = tzd->devdata; u32 eax, edx; - struct phy_dev_entry *phy_dev_entry; - phy_dev_entry = tzd->devdata; - rdmsr_on_cpu(phy_dev_entry->cpu, MSR_IA32_PACKAGE_THERM_STATUS, - &eax, &edx); + rdmsr_on_cpu(pkgdev->cpu, MSR_IA32_PACKAGE_THERM_STATUS, &eax, &edx); if (eax & 0x80000000) { - *temp = phy_dev_entry->tj_max - - ((eax >> 16) & 0x7f) * 1000; + *temp = pkgdev->tj_max - ((eax >> 16) & 0x7f) * 1000; pr_debug("sys_get_curr_temp %d\n", *temp); return 0; } - return -EINVAL; } static int sys_get_trip_temp(struct thermal_zone_device *tzd, - int trip, int *temp) + int trip, int *temp) { - u32 eax, edx; - struct phy_dev_entry *phy_dev_entry; - u32 mask, shift; + struct pkg_device *pkgdev = tzd->devdata; unsigned long thres_reg_value; + u32 mask, shift, eax, edx; int ret; if (trip >= MAX_NUMBER_OF_TRIPS) return -EINVAL; - phy_dev_entry = tzd->devdata; - if (trip) { mask = THERM_MASK_THRESHOLD1; shift = THERM_SHIFT_THRESHOLD1; @@ -203,14 +195,14 @@ static int sys_get_trip_temp(struct thermal_zone_device *tzd, shift = THERM_SHIFT_THRESHOLD0; } - ret = rdmsr_on_cpu(phy_dev_entry->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, + ret = rdmsr_on_cpu(pkgdev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, &eax, &edx); if (ret < 0) return -EINVAL; thres_reg_value = (eax & mask) >> shift; if (thres_reg_value) - *temp = phy_dev_entry->tj_max - thres_reg_value * 1000; + *temp = pkgdev->tj_max - thres_reg_value * 1000; else *temp = 0; pr_debug("sys_get_trip_temp %d\n", *temp); @@ -218,20 +210,17 @@ static int sys_get_trip_temp(struct thermal_zone_device *tzd, return 0; } -static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, - int temp) +static int +sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp) { - u32 l, h; - struct phy_dev_entry *phy_dev_entry; - u32 mask, shift, intr; + struct pkg_device *pkgdev = tzd->devdata; + u32 l, h, mask, shift, intr; int ret; - phy_dev_entry = tzd->devdata; - - if (trip >= MAX_NUMBER_OF_TRIPS || temp >= phy_dev_entry->tj_max) + if (trip >= MAX_NUMBER_OF_TRIPS || temp >= pkgdev->tj_max) return -EINVAL; - ret = rdmsr_on_cpu(phy_dev_entry->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, + ret = rdmsr_on_cpu(pkgdev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, &l, &h); if (ret < 0) return -EINVAL; @@ -250,19 +239,18 @@ static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, * When users space sets a trip temperature == 0, which is indication * that, it is no longer interested in receiving notifications. */ - if (!temp) + if (!temp) { l &= ~intr; - else { - l |= (phy_dev_entry->tj_max - temp)/1000 << shift; + } else { + l |= (pkgdev->tj_max - temp)/1000 << shift; l |= intr; } - return wrmsr_on_cpu(phy_dev_entry->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, - l, h); + return wrmsr_on_cpu(pkgdev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h); } -static int sys_get_trip_type(struct thermal_zone_device *thermal, - int trip, enum thermal_trip_type *type) +static int sys_get_trip_type(struct thermal_zone_device *thermal, int trip, + enum thermal_trip_type *type) { *type = THERMAL_TRIP_PASSIVE; @@ -315,11 +303,11 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work) __u64 msr_val; int cpu = smp_processor_id(); int phy_id = topology_physical_package_id(cpu); - struct phy_dev_entry *phdev = pkg_temp_thermal_get_phy_entry(cpu); + struct pkg_device *pkgdev = pkg_temp_thermal_get_dev(cpu); bool notify = false; unsigned long flags; - if (!phdev) + if (!pkgdev) return; spin_lock_irqsave(&pkg_work_lock, flags); @@ -347,7 +335,7 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work) if (notify) { pr_debug("thermal_zone_device_update\n"); - thermal_zone_device_update(phdev->tzone, + thermal_zone_device_update(pkgdev->tzone, THERMAL_EVENT_UNSPECIFIED); } } @@ -383,13 +371,11 @@ static int pkg_thermal_notify(__u64 msr_val) static int pkg_temp_thermal_device_add(unsigned int cpu) { - int err; - u32 tj_max; - struct phy_dev_entry *phy_dev_entry; - int thres_count; - u32 eax, ebx, ecx, edx; - u8 *temp; + u32 tj_max, eax, ebx, ecx, edx; + struct pkg_device *pkgdev; + int thres_count, err; unsigned long flags; + u8 *temp; cpuid(6, &eax, &ebx, &ecx, &edx); thres_count = ebx & 0x07; @@ -407,8 +393,8 @@ static int pkg_temp_thermal_device_add(unsigned int cpu) mutex_lock(&phy_dev_list_mutex); - phy_dev_entry = kzalloc(sizeof(*phy_dev_entry), GFP_KERNEL); - if (!phy_dev_entry) { + pkgdev = kzalloc(sizeof(*pkgdev), GFP_KERNEL); + if (!pkgdev) { err = -ENOMEM; goto err_ret_unlock; } @@ -427,33 +413,32 @@ static int pkg_temp_thermal_device_add(unsigned int cpu) pkg_work_scheduled[topology_physical_package_id(cpu)] = 0; spin_unlock_irqrestore(&pkg_work_lock, flags); - phy_dev_entry->phys_proc_id = topology_physical_package_id(cpu); - phy_dev_entry->cpu = cpu; - phy_dev_entry->tj_max = tj_max; - phy_dev_entry->tzone = thermal_zone_device_register("x86_pkg_temp", + pkgdev->phys_proc_id = topology_physical_package_id(cpu); + pkgdev->cpu = cpu; + pkgdev->tj_max = tj_max; + pkgdev->tzone = thermal_zone_device_register("x86_pkg_temp", thres_count, - (thres_count == MAX_NUMBER_OF_TRIPS) ? - 0x03 : 0x01, - phy_dev_entry, &tzone_ops, &pkg_temp_tz_params, 0, 0); - if (IS_ERR(phy_dev_entry->tzone)) { - err = PTR_ERR(phy_dev_entry->tzone); + (thres_count == MAX_NUMBER_OF_TRIPS) ? 0x03 : 0x01, + pkgdev, &tzone_ops, &pkg_temp_tz_params, 0, 0); + if (IS_ERR(pkgdev->tzone)) { + err = PTR_ERR(pkgdev->tzone); goto err_ret_free; } /* Store MSR value for package thermal interrupt, to restore at exit */ rdmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, - &phy_dev_entry->start_pkg_therm_low, - &phy_dev_entry->start_pkg_therm_high); + &pkgdev->msr_pkg_therm_low, + &pkgdev->msr_pkg_therm_high); - list_add_tail(&phy_dev_entry->list, &phy_dev_list); + list_add_tail(&pkgdev->list, &phy_dev_list); pr_debug("pkg_temp_thermal_device_add :phy_id %d cpu %d\n", - phy_dev_entry->phys_proc_id, cpu); + pkgdev->phys_proc_id, cpu); mutex_unlock(&phy_dev_list_mutex); return 0; err_ret_free: - kfree(phy_dev_entry); + kfree(pkgdev); err_ret_unlock: mutex_unlock(&phy_dev_list_mutex); @@ -463,10 +448,10 @@ err_ret: static int pkg_temp_thermal_device_remove(unsigned int cpu) { - struct phy_dev_entry *phdev = pkg_temp_thermal_get_phy_entry(cpu); + struct pkg_device *pkgdev = pkg_temp_thermal_get_dev(cpu); int target; - if (!phdev) + if (!pkgdev) return -ENODEV; mutex_lock(&phy_dev_list_mutex); @@ -474,18 +459,18 @@ static int pkg_temp_thermal_device_remove(unsigned int cpu) target = cpumask_any_but(topology_core_cpumask(cpu), cpu); /* This might be the last cpu in this package */ if (target >= nr_cpu_ids) { - thermal_zone_device_unregister(phdev->tzone); + thermal_zone_device_unregister(pkgdev->tzone); /* * Restore original MSR value for package thermal * interrupt. */ wrmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, - phdev->start_pkg_therm_low, - phdev->start_pkg_therm_high); - list_del(&phdev->list); - kfree(phdev); - } else if (phdev->cpu == cpu) { - phdev->cpu = target; + pkgdev->msr_pkg_therm_low, + pkgdev->msr_pkg_therm_high); + list_del(&pkgdev->list); + kfree(pkgdev); + } else if (pkgdev->cpu == cpu) { + pkgdev->cpu = target; } mutex_unlock(&phy_dev_list_mutex); @@ -495,19 +480,20 @@ static int pkg_temp_thermal_device_remove(unsigned int cpu) static int get_core_online(unsigned int cpu) { + struct pkg_device *pkgdev = pkg_temp_thermal_get_dev(cpu); struct cpuinfo_x86 *c = &cpu_data(cpu); - struct phy_dev_entry *phdev = pkg_temp_thermal_get_phy_entry(cpu); /* Check if there is already an instance for this package */ - if (!phdev) { + if (!pkgdev) { if (!cpu_has(c, X86_FEATURE_DTHERM) || !cpu_has(c, X86_FEATURE_PTS)) return -ENODEV; if (pkg_temp_thermal_device_add(cpu)) return -ENODEV; } + INIT_DELAYED_WORK(&per_cpu(pkg_temp_thermal_threshold_work, cpu), - pkg_temp_thermal_threshold_work_fn); + pkg_temp_thermal_threshold_work_fn); pr_debug("get_core_online: cpu %d successful\n", cpu); @@ -583,7 +569,7 @@ err_ret: static void __exit pkg_temp_thermal_exit(void) { - struct phy_dev_entry *phdev, *n; + struct pkg_device *pkgdev, *n; int i; platform_thermal_package_notify = NULL; @@ -592,14 +578,14 @@ static void __exit pkg_temp_thermal_exit(void) cpu_notifier_register_begin(); __unregister_hotcpu_notifier(&pkg_temp_thermal_notifier); mutex_lock(&phy_dev_list_mutex); - list_for_each_entry_safe(phdev, n, &phy_dev_list, list) { + list_for_each_entry_safe(pkgdev, n, &phy_dev_list, list) { /* Retore old MSR value for package thermal interrupt */ - wrmsr_on_cpu(phdev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, - phdev->start_pkg_therm_low, - phdev->start_pkg_therm_high); - thermal_zone_device_unregister(phdev->tzone); - list_del(&phdev->list); - kfree(phdev); + wrmsr_on_cpu(pkgdev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, + pkgdev->msr_pkg_therm_low, + pkgdev->msr_pkg_therm_high); + thermal_zone_device_unregister(pkgdev->tzone); + list_del(&pkgdev->list); + kfree(pkgdev); } mutex_unlock(&phy_dev_list_mutex); for_each_online_cpu(i) From 8079a4bdcb0731af1f147cf043227b01b91884a4 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 22 Nov 2016 17:57:09 +0000 Subject: [PATCH 70/76] thermal/x86_pkg_temp: Cleanup code some more Coding style fixups and replacement of overly complex constructs and random error codes instead of returning the real ones. This mess makes the eyes bleeding. Signed-off-by: Thomas Gleixner Tested-by: Srinivas Pandruvada Signed-off-by: Zhang Rui --- drivers/thermal/x86_pkg_temp_thermal.c | 81 ++++++++++---------------- 1 file changed, 30 insertions(+), 51 deletions(-) diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c index f89a86f5c982..39303bcf09e3 100644 --- a/drivers/thermal/x86_pkg_temp_thermal.c +++ b/drivers/thermal/x86_pkg_temp_thermal.c @@ -139,27 +139,17 @@ static struct pkg_device *pkg_temp_thermal_get_dev(unsigned int cpu) */ static int get_tj_max(int cpu, u32 *tj_max) { - u32 eax, edx; - u32 val; + u32 eax, edx, val; int err; err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); if (err) - goto err_ret; - else { - val = (eax >> 16) & 0xff; - if (val) - *tj_max = val * 1000; - else { - err = -EINVAL; - goto err_ret; - } - } + return err; - return 0; -err_ret: - *tj_max = 0; - return err; + val = (eax >> 16) & 0xff; + *tj_max = val * 1000; + + return val ? 0 : -EINVAL; } static int sys_get_curr_temp(struct thermal_zone_device *tzd, int *temp) @@ -198,7 +188,7 @@ static int sys_get_trip_temp(struct thermal_zone_device *tzd, ret = rdmsr_on_cpu(pkgdev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, &eax, &edx); if (ret < 0) - return -EINVAL; + return ret; thres_reg_value = (eax & mask) >> shift; if (thres_reg_value) @@ -223,7 +213,7 @@ sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp) ret = rdmsr_on_cpu(pkgdev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, &l, &h); if (ret < 0) - return -EINVAL; + return ret; if (trip) { mask = THERM_MASK_THRESHOLD1; @@ -252,9 +242,7 @@ sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp) static int sys_get_trip_type(struct thermal_zone_device *thermal, int trip, enum thermal_trip_type *type) { - *type = THERMAL_TRIP_PASSIVE; - return 0; } @@ -274,8 +262,8 @@ static bool pkg_thermal_rate_control(void) /* Enable threshold interrupt on local package/cpu */ static inline void enable_pkg_thres_interrupt(void) { - u32 l, h; u8 thres_0, thres_1; + u32 l, h; rdmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h); /* only enable/disable if it had valid threshold value */ @@ -292,20 +280,21 @@ static inline void enable_pkg_thres_interrupt(void) static inline void disable_pkg_thres_interrupt(void) { u32 l, h; + rdmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h); - wrmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, - l & (~THERM_INT_THRESHOLD0_ENABLE) & - (~THERM_INT_THRESHOLD1_ENABLE), h); + + l &= ~(THERM_INT_THRESHOLD0_ENABLE | THERM_INT_THRESHOLD1_ENABLE); + wrmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h); } static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work) { - __u64 msr_val; int cpu = smp_processor_id(); - int phy_id = topology_physical_package_id(cpu); struct pkg_device *pkgdev = pkg_temp_thermal_get_dev(cpu); + int phy_id = topology_physical_package_id(cpu); bool notify = false; unsigned long flags; + u64 msr_val, wr_val; if (!pkgdev) return; @@ -320,14 +309,9 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work) spin_unlock_irqrestore(&pkg_work_lock, flags); rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr_val); - if (msr_val & THERM_LOG_THRESHOLD0) { - wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS, - msr_val & ~THERM_LOG_THRESHOLD0); - notify = true; - } - if (msr_val & THERM_LOG_THRESHOLD1) { - wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS, - msr_val & ~THERM_LOG_THRESHOLD1); + wr_val = msr_val & ~(THERM_LOG_THRESHOLD0 | THERM_LOG_THRESHOLD1); + if (wr_val != msr_val) { + wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS, wr_val); notify = true; } @@ -340,11 +324,11 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work) } } -static int pkg_thermal_notify(__u64 msr_val) +static int pkg_thermal_notify(u64 msr_val) { - unsigned long flags; int cpu = smp_processor_id(); int phy_id = topology_physical_package_id(cpu); + unsigned long flags; /* * When a package is in interrupted state, all CPU's in that package @@ -483,21 +467,17 @@ static int get_core_online(unsigned int cpu) struct pkg_device *pkgdev = pkg_temp_thermal_get_dev(cpu); struct cpuinfo_x86 *c = &cpu_data(cpu); - /* Check if there is already an instance for this package */ - if (!pkgdev) { - if (!cpu_has(c, X86_FEATURE_DTHERM) || - !cpu_has(c, X86_FEATURE_PTS)) - return -ENODEV; - if (pkg_temp_thermal_device_add(cpu)) - return -ENODEV; - } + /* Paranoia check */ + if (!cpu_has(c, X86_FEATURE_DTHERM) || !cpu_has(c, X86_FEATURE_PTS)) + return -ENODEV; INIT_DELAYED_WORK(&per_cpu(pkg_temp_thermal_threshold_work, cpu), pkg_temp_thermal_threshold_work_fn); - pr_debug("get_core_online: cpu %d successful\n", cpu); - - return 0; + /* If the package exists, nothing to do */ + if (pkgdev) + return 0; + return pkg_temp_thermal_device_add(cpu); } static void put_core_offline(unsigned int cpu) @@ -555,8 +535,8 @@ static int __init pkg_temp_thermal_init(void) platform_thermal_package_notify = pkg_thermal_notify; platform_thermal_package_rate_control = pkg_thermal_rate_control; - pkg_temp_debugfs_init(); /* Don't care if fails */ - + /* Don't care if it fails */ + pkg_temp_debugfs_init(); return 0; err_ret: @@ -566,6 +546,7 @@ err_ret: kfree(pkg_work_scheduled); return -ENODEV; } +module_init(pkg_temp_thermal_init) static void __exit pkg_temp_thermal_exit(void) { @@ -597,8 +578,6 @@ static void __exit pkg_temp_thermal_exit(void) debugfs_remove_recursive(debugfs); } - -module_init(pkg_temp_thermal_init) module_exit(pkg_temp_thermal_exit) MODULE_DESCRIPTION("X86 PKG TEMP Thermal Driver"); From ab47bd964af698a014590a17cf9f902128a57a48 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 22 Nov 2016 17:57:10 +0000 Subject: [PATCH 71/76] thermal/x86_pkg_temp: Sanitize locking The work cancellation code, the thermal zone unregistering, the work code and the interrupt notification function are racy against each other and against cpu hotplug and module exit. The random locking sprinkeled all over the place does not help anything and probably exists to make people feel good. The resulting issues (mainly use after free) are probably hard to trigger, but they clearly exist Protect the package list with a spinlock so it can be accessed from the interrupt notifier and also from the work function. The add/removal code in the hotplug callbacks take the lock for list manipulation. That makes sure that on removal neither the interrupt notifier nor the work function can access the about to be freed package structure anymore. The thermal zone unregistering is another trainwreck. It's not serialized against the work function. So unregistering the zone device can race with the work function and cause havoc. Protect the thermal zone with a mutex, which is held in the work function to make sure that the zone device is not being unregistered concurrently. To solve the module exit issues, we simply invoke the cpu offline callback and let it work its magic. For that it's required to keep track of the participating cpus in a package, because topology_core_mask is not affected by calling the offline callback for teardown of the driver, so it would never free the package as there is always a valid target in topology_core_mask. Use proper names for the locks so it's clear what they are for and add a pile of comments to explain the protection rules. It's amazing that fixing the locking and adding 30 lines of comments explaining it still removes more lines than it adds. Signed-off-by: Thomas Gleixner Tested-by: Srinivas Pandruvada Signed-off-by: Zhang Rui --- drivers/thermal/x86_pkg_temp_thermal.c | 222 ++++++++++++------------- 1 file changed, 110 insertions(+), 112 deletions(-) diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c index 39303bcf09e3..2c8b7e718060 100644 --- a/drivers/thermal/x86_pkg_temp_thermal.c +++ b/drivers/thermal/x86_pkg_temp_thermal.c @@ -65,6 +65,7 @@ struct pkg_device { u32 msr_pkg_therm_low; u32 msr_pkg_therm_high; struct thermal_zone_device *tzone; + struct cpumask cpumask; }; static struct thermal_zone_params pkg_temp_tz_params = { @@ -73,7 +74,10 @@ static struct thermal_zone_params pkg_temp_tz_params = { /* List maintaining number of package instances */ static LIST_HEAD(phy_dev_list); -static DEFINE_MUTEX(phy_dev_list_mutex); +/* Serializes interrupt notification, work and hotplug */ +static DEFINE_SPINLOCK(pkg_temp_lock); +/* Protects zone operation in the work function against hotplug removal */ +static DEFINE_MUTEX(thermal_zone_mutex); /* Interrupt to work function schedule queue */ static DEFINE_PER_CPU(struct delayed_work, pkg_temp_thermal_threshold_work); @@ -81,8 +85,6 @@ static DEFINE_PER_CPU(struct delayed_work, pkg_temp_thermal_threshold_work); /* To track if the work is already scheduled on a package */ static u8 *pkg_work_scheduled; -/* Spin lock to prevent races with pkg_work_scheduled */ -static spinlock_t pkg_work_lock; static u16 max_phy_id; /* Debug counters to show using debugfs */ @@ -115,21 +117,23 @@ err_out: return -ENOENT; } +/* + * Protection: + * + * - cpu hotplug: Read serialized by cpu hotplug lock + * Write must hold pkg_temp_lock + * + * - Other callsites: Must hold pkg_temp_lock + */ static struct pkg_device *pkg_temp_thermal_get_dev(unsigned int cpu) { u16 phys_proc_id = topology_physical_package_id(cpu); struct pkg_device *pkgdev; - mutex_lock(&phy_dev_list_mutex); - - list_for_each_entry(pkgdev, &phy_dev_list, list) - if (pkgdev->phys_proc_id == phys_proc_id) { - mutex_unlock(&phy_dev_list_mutex); + list_for_each_entry(pkgdev, &phy_dev_list, list) { + if (pkgdev->phys_proc_id == phys_proc_id) return pkgdev; - } - - mutex_unlock(&phy_dev_list_mutex); - + } return NULL; } @@ -289,67 +293,66 @@ static inline void disable_pkg_thres_interrupt(void) static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work) { - int cpu = smp_processor_id(); - struct pkg_device *pkgdev = pkg_temp_thermal_get_dev(cpu); - int phy_id = topology_physical_package_id(cpu); - bool notify = false; - unsigned long flags; + struct thermal_zone_device *tzone = NULL; + int phy_id, cpu = smp_processor_id(); + struct pkg_device *pkgdev; u64 msr_val, wr_val; - if (!pkgdev) - return; - - spin_lock_irqsave(&pkg_work_lock, flags); + mutex_lock(&thermal_zone_mutex); + spin_lock_irq(&pkg_temp_lock); ++pkg_work_cnt; - if (unlikely(phy_id > max_phy_id)) { - spin_unlock_irqrestore(&pkg_work_lock, flags); + + pkgdev = pkg_temp_thermal_get_dev(cpu); + if (!pkgdev) { + spin_unlock_irq(&pkg_temp_lock); + mutex_unlock(&thermal_zone_mutex); return; } + pkg_work_scheduled[phy_id] = 0; - spin_unlock_irqrestore(&pkg_work_lock, flags); rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr_val); wr_val = msr_val & ~(THERM_LOG_THRESHOLD0 | THERM_LOG_THRESHOLD1); if (wr_val != msr_val) { wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS, wr_val); - notify = true; + tzone = pkgdev->tzone; } enable_pkg_thres_interrupt(); + spin_unlock_irq(&pkg_temp_lock); - if (notify) { - pr_debug("thermal_zone_device_update\n"); - thermal_zone_device_update(pkgdev->tzone, - THERMAL_EVENT_UNSPECIFIED); - } + /* + * If tzone is not NULL, then thermal_zone_mutex will prevent the + * concurrent removal in the cpu offline callback. + */ + if (tzone) + thermal_zone_device_update(tzone, THERMAL_EVENT_UNSPECIFIED); + + mutex_unlock(&thermal_zone_mutex); } static int pkg_thermal_notify(u64 msr_val) { int cpu = smp_processor_id(); int phy_id = topology_physical_package_id(cpu); + struct pkg_device *pkgdev; unsigned long flags; - /* - * When a package is in interrupted state, all CPU's in that package - * are in the same interrupt state. So scheduling on any one CPU in - * the package is enough and simply return for others. - */ - spin_lock_irqsave(&pkg_work_lock, flags); + spin_lock_irqsave(&pkg_temp_lock, flags); ++pkg_interrupt_cnt; - if (unlikely(phy_id > max_phy_id) || unlikely(!pkg_work_scheduled) || - pkg_work_scheduled[phy_id]) { - disable_pkg_thres_interrupt(); - spin_unlock_irqrestore(&pkg_work_lock, flags); - return -EINVAL; - } - pkg_work_scheduled[phy_id] = 1; - spin_unlock_irqrestore(&pkg_work_lock, flags); disable_pkg_thres_interrupt(); - schedule_delayed_work_on(cpu, + + /* Work is per package, so scheduling it once is enough. */ + pkgdev = pkg_temp_thermal_get_dev(cpu); + if (pkgdev && pkg_work_scheduled && !pkg_work_scheduled[phy_id]) { + pkg_work_scheduled[phy_id] = 1; + schedule_delayed_work_on(cpu, &per_cpu(pkg_temp_thermal_threshold_work, cpu), msecs_to_jiffies(notify_delay_ms)); + } + + spin_unlock_irqrestore(&pkg_temp_lock, flags); return 0; } @@ -373,29 +376,25 @@ static int pkg_temp_thermal_device_add(unsigned int cpu) err = get_tj_max(cpu, &tj_max); if (err) - goto err_ret; - - mutex_lock(&phy_dev_list_mutex); + return err; pkgdev = kzalloc(sizeof(*pkgdev), GFP_KERNEL); - if (!pkgdev) { - err = -ENOMEM; - goto err_ret_unlock; - } + if (!pkgdev) + return -ENOMEM; - spin_lock_irqsave(&pkg_work_lock, flags); + spin_lock_irqsave(&pkg_temp_lock, flags); if (topology_physical_package_id(cpu) > max_phy_id) max_phy_id = topology_physical_package_id(cpu); temp = krealloc(pkg_work_scheduled, (max_phy_id+1) * sizeof(u8), GFP_ATOMIC); if (!temp) { - spin_unlock_irqrestore(&pkg_work_lock, flags); - err = -ENOMEM; - goto err_ret_free; + spin_unlock_irqrestore(&pkg_temp_lock, flags); + kfree(pkgdev); + return -ENOMEM; } pkg_work_scheduled = temp; pkg_work_scheduled[topology_physical_package_id(cpu)] = 0; - spin_unlock_irqrestore(&pkg_work_lock, flags); + spin_unlock_irqrestore(&pkg_temp_lock, flags); pkgdev->phys_proc_id = topology_physical_package_id(cpu); pkgdev->cpu = cpu; @@ -406,60 +405,81 @@ static int pkg_temp_thermal_device_add(unsigned int cpu) pkgdev, &tzone_ops, &pkg_temp_tz_params, 0, 0); if (IS_ERR(pkgdev->tzone)) { err = PTR_ERR(pkgdev->tzone); - goto err_ret_free; + kfree(pkgdev); + return err; } /* Store MSR value for package thermal interrupt, to restore at exit */ rdmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, &pkgdev->msr_pkg_therm_low, &pkgdev->msr_pkg_therm_high); + cpumask_set_cpu(cpu, &pkgdev->cpumask); + spin_lock_irq(&pkg_temp_lock); list_add_tail(&pkgdev->list, &phy_dev_list); - pr_debug("pkg_temp_thermal_device_add :phy_id %d cpu %d\n", - pkgdev->phys_proc_id, cpu); - - mutex_unlock(&phy_dev_list_mutex); - + spin_unlock_irq(&pkg_temp_lock); return 0; - -err_ret_free: - kfree(pkgdev); -err_ret_unlock: - mutex_unlock(&phy_dev_list_mutex); - -err_ret: - return err; } -static int pkg_temp_thermal_device_remove(unsigned int cpu) +static void put_core_offline(unsigned int cpu) { struct pkg_device *pkgdev = pkg_temp_thermal_get_dev(cpu); + bool lastcpu; int target; if (!pkgdev) - return -ENODEV; + return; - mutex_lock(&phy_dev_list_mutex); + target = cpumask_any_but(&pkgdev->cpumask, cpu); + cpumask_clear_cpu(cpu, &pkgdev->cpumask); + lastcpu = target >= nr_cpu_ids; + /* + * Remove the sysfs files, if this is the last cpu in the package + * before doing further cleanups. + */ + if (lastcpu) { + struct thermal_zone_device *tzone = pkgdev->tzone; - target = cpumask_any_but(topology_core_cpumask(cpu), cpu); - /* This might be the last cpu in this package */ - if (target >= nr_cpu_ids) { - thermal_zone_device_unregister(pkgdev->tzone); /* - * Restore original MSR value for package thermal - * interrupt. + * We must protect against a work function calling + * thermal_zone_update, after/while unregister. We null out + * the pointer under the zone mutex, so the worker function + * won't try to call. */ + mutex_lock(&thermal_zone_mutex); + pkgdev->tzone = NULL; + mutex_unlock(&thermal_zone_mutex); + + thermal_zone_device_unregister(tzone); + } + + /* + * If this is the last CPU in the package, restore the interrupt + * MSR and remove the package reference from the array. + */ + if (lastcpu) { + /* Protect against work and interrupts */ + spin_lock_irq(&pkg_temp_lock); + list_del(&pkgdev->list); + /* + * After this point nothing touches the MSR anymore. We + * must drop the lock to make the cross cpu call. This goes + * away once we move that code to the hotplug state machine. + */ + spin_unlock_irq(&pkg_temp_lock); wrmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, pkgdev->msr_pkg_therm_low, pkgdev->msr_pkg_therm_high); - list_del(&pkgdev->list); kfree(pkgdev); - } else if (pkgdev->cpu == cpu) { - pkgdev->cpu = target; } - mutex_unlock(&phy_dev_list_mutex); - - return 0; + /* + * Note, this is broken when work was really scheduled on the + * outgoing cpu because this will leave the work_scheduled flag set + * and the thermal interrupts disabled. Will be fixed in the next + * step as there is no way to fix it in a sane way with the per cpu + * work nonsense. + */ + cancel_delayed_work_sync(&per_cpu(pkg_temp_thermal_threshold_work, cpu)); } static int get_core_online(unsigned int cpu) @@ -475,20 +495,13 @@ static int get_core_online(unsigned int cpu) pkg_temp_thermal_threshold_work_fn); /* If the package exists, nothing to do */ - if (pkgdev) + if (pkgdev) { + cpumask_set_cpu(cpu, &pkgdev->cpumask); return 0; + } return pkg_temp_thermal_device_add(cpu); } -static void put_core_offline(unsigned int cpu) -{ - if (!pkg_temp_thermal_device_remove(cpu)) - cancel_delayed_work_sync( - &per_cpu(pkg_temp_thermal_threshold_work, cpu)); - - pr_debug("put_core_offline: cpu %d\n", cpu); -} - static int pkg_temp_thermal_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { @@ -523,8 +536,6 @@ static int __init pkg_temp_thermal_init(void) if (!x86_match_cpu(pkg_temp_thermal_ids)) return -ENODEV; - spin_lock_init(&pkg_work_lock); - cpu_notifier_register_begin(); for_each_online_cpu(i) if (get_core_online(i)) @@ -550,7 +561,6 @@ module_init(pkg_temp_thermal_init) static void __exit pkg_temp_thermal_exit(void) { - struct pkg_device *pkgdev, *n; int i; platform_thermal_package_notify = NULL; @@ -558,20 +568,8 @@ static void __exit pkg_temp_thermal_exit(void) cpu_notifier_register_begin(); __unregister_hotcpu_notifier(&pkg_temp_thermal_notifier); - mutex_lock(&phy_dev_list_mutex); - list_for_each_entry_safe(pkgdev, n, &phy_dev_list, list) { - /* Retore old MSR value for package thermal interrupt */ - wrmsr_on_cpu(pkgdev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, - pkgdev->msr_pkg_therm_low, - pkgdev->msr_pkg_therm_high); - thermal_zone_device_unregister(pkgdev->tzone); - list_del(&pkgdev->list); - kfree(pkgdev); - } - mutex_unlock(&phy_dev_list_mutex); for_each_online_cpu(i) - cancel_delayed_work_sync( - &per_cpu(pkg_temp_thermal_threshold_work, i)); + put_core_offline(i); cpu_notifier_register_done(); kfree(pkg_work_scheduled); From 64ca738f1fba850fa522cd88b0b935492d846fff Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 22 Nov 2016 17:57:12 +0000 Subject: [PATCH 72/76] thermal/x86_pkg_temp: Move work scheduled flag into package struct Storage for a boolean information whether work is scheduled for a package is kept in separate allocated storage, which is resized when the number of detected packages grows. With the proper locking in place this is a completely pointless exercise because we can simply stick it into the per package struct. Signed-off-by: Thomas Gleixner Tested-by: Srinivas Pandruvada Signed-off-by: Zhang Rui --- drivers/thermal/x86_pkg_temp_thermal.c | 35 ++++---------------------- 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c index 2c8b7e718060..91f267a4b1b8 100644 --- a/drivers/thermal/x86_pkg_temp_thermal.c +++ b/drivers/thermal/x86_pkg_temp_thermal.c @@ -61,6 +61,7 @@ struct pkg_device { struct list_head list; u16 phys_proc_id; u16 cpu; + bool work_scheduled; u32 tj_max; u32 msr_pkg_therm_low; u32 msr_pkg_therm_high; @@ -82,11 +83,6 @@ static DEFINE_MUTEX(thermal_zone_mutex); /* Interrupt to work function schedule queue */ static DEFINE_PER_CPU(struct delayed_work, pkg_temp_thermal_threshold_work); -/* To track if the work is already scheduled on a package */ -static u8 *pkg_work_scheduled; - -static u16 max_phy_id; - /* Debug counters to show using debugfs */ static struct dentry *debugfs; static unsigned int pkg_interrupt_cnt; @@ -294,7 +290,7 @@ static inline void disable_pkg_thres_interrupt(void) static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work) { struct thermal_zone_device *tzone = NULL; - int phy_id, cpu = smp_processor_id(); + int cpu = smp_processor_id(); struct pkg_device *pkgdev; u64 msr_val, wr_val; @@ -308,8 +304,7 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work) mutex_unlock(&thermal_zone_mutex); return; } - - pkg_work_scheduled[phy_id] = 0; + pkgdev->work_scheduled = false; rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr_val); wr_val = msr_val & ~(THERM_LOG_THRESHOLD0 | THERM_LOG_THRESHOLD1); @@ -334,7 +329,6 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work) static int pkg_thermal_notify(u64 msr_val) { int cpu = smp_processor_id(); - int phy_id = topology_physical_package_id(cpu); struct pkg_device *pkgdev; unsigned long flags; @@ -345,8 +339,8 @@ static int pkg_thermal_notify(u64 msr_val) /* Work is per package, so scheduling it once is enough. */ pkgdev = pkg_temp_thermal_get_dev(cpu); - if (pkgdev && pkg_work_scheduled && !pkg_work_scheduled[phy_id]) { - pkg_work_scheduled[phy_id] = 1; + if (pkgdev && !pkgdev->work_scheduled) { + pkgdev->work_scheduled = true; schedule_delayed_work_on(cpu, &per_cpu(pkg_temp_thermal_threshold_work, cpu), msecs_to_jiffies(notify_delay_ms)); @@ -361,8 +355,6 @@ static int pkg_temp_thermal_device_add(unsigned int cpu) u32 tj_max, eax, ebx, ecx, edx; struct pkg_device *pkgdev; int thres_count, err; - unsigned long flags; - u8 *temp; cpuid(6, &eax, &ebx, &ecx, &edx); thres_count = ebx & 0x07; @@ -382,20 +374,6 @@ static int pkg_temp_thermal_device_add(unsigned int cpu) if (!pkgdev) return -ENOMEM; - spin_lock_irqsave(&pkg_temp_lock, flags); - if (topology_physical_package_id(cpu) > max_phy_id) - max_phy_id = topology_physical_package_id(cpu); - temp = krealloc(pkg_work_scheduled, - (max_phy_id+1) * sizeof(u8), GFP_ATOMIC); - if (!temp) { - spin_unlock_irqrestore(&pkg_temp_lock, flags); - kfree(pkgdev); - return -ENOMEM; - } - pkg_work_scheduled = temp; - pkg_work_scheduled[topology_physical_package_id(cpu)] = 0; - spin_unlock_irqrestore(&pkg_temp_lock, flags); - pkgdev->phys_proc_id = topology_physical_package_id(cpu); pkgdev->cpu = cpu; pkgdev->tj_max = tj_max; @@ -554,7 +532,6 @@ err_ret: for_each_online_cpu(i) put_core_offline(i); cpu_notifier_register_done(); - kfree(pkg_work_scheduled); return -ENODEV; } module_init(pkg_temp_thermal_init) @@ -572,8 +549,6 @@ static void __exit pkg_temp_thermal_exit(void) put_core_offline(i); cpu_notifier_register_done(); - kfree(pkg_work_scheduled); - debugfs_remove_recursive(debugfs); } module_exit(pkg_temp_thermal_exit) From 411bb3835f473d1b50676b31abb16f1a464ea7e3 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 22 Nov 2016 17:57:13 +0000 Subject: [PATCH 73/76] thermal/x86_pkg_temp: Move work into package struct Delayed work structs are held in a static percpu storage, which makes no sense at all because work is strictly per package and we never schedule more than one work per package. Aside of that the work cancelation in the hotplug is broken when the work is queued on the outgoing cpu and canceled. Nothing reschedules the work on another online cpu in the package, so the interrupts stay disabled and the work_scheduled flag stays active. Move the delayed work struct into the package struct, which is the only sensible place to have it. To simplify the cancelation logic schedule the work always on the cpu which is the target for the sysfs files. This is required so the cancelation logic in the cpu offline path cancels only when the outgoing cpu is the current target and reschedule the work when there is still a online CPU in the package. Signed-off-by: Thomas Gleixner Tested-by: Srinivas Pandruvada Signed-off-by: Zhang Rui --- drivers/thermal/x86_pkg_temp_thermal.c | 73 ++++++++++++++++++-------- 1 file changed, 52 insertions(+), 21 deletions(-) diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c index 91f267a4b1b8..07db08cabc12 100644 --- a/drivers/thermal/x86_pkg_temp_thermal.c +++ b/drivers/thermal/x86_pkg_temp_thermal.c @@ -65,6 +65,7 @@ struct pkg_device { u32 tj_max; u32 msr_pkg_therm_low; u32 msr_pkg_therm_high; + struct delayed_work work; struct thermal_zone_device *tzone; struct cpumask cpumask; }; @@ -80,9 +81,6 @@ static DEFINE_SPINLOCK(pkg_temp_lock); /* Protects zone operation in the work function against hotplug removal */ static DEFINE_MUTEX(thermal_zone_mutex); -/* Interrupt to work function schedule queue */ -static DEFINE_PER_CPU(struct delayed_work, pkg_temp_thermal_threshold_work); - /* Debug counters to show using debugfs */ static struct dentry *debugfs; static unsigned int pkg_interrupt_cnt; @@ -326,6 +324,13 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work) mutex_unlock(&thermal_zone_mutex); } +static void pkg_thermal_schedule_work(int cpu, struct delayed_work *work) +{ + unsigned long ms = msecs_to_jiffies(notify_delay_ms); + + schedule_delayed_work_on(cpu, work, ms); +} + static int pkg_thermal_notify(u64 msr_val) { int cpu = smp_processor_id(); @@ -341,9 +346,7 @@ static int pkg_thermal_notify(u64 msr_val) pkgdev = pkg_temp_thermal_get_dev(cpu); if (pkgdev && !pkgdev->work_scheduled) { pkgdev->work_scheduled = true; - schedule_delayed_work_on(cpu, - &per_cpu(pkg_temp_thermal_threshold_work, cpu), - msecs_to_jiffies(notify_delay_ms)); + pkg_thermal_schedule_work(pkgdev->cpu, &pkgdev->work); } spin_unlock_irqrestore(&pkg_temp_lock, flags); @@ -374,6 +377,7 @@ static int pkg_temp_thermal_device_add(unsigned int cpu) if (!pkgdev) return -ENOMEM; + INIT_DELAYED_WORK(&pkgdev->work, pkg_temp_thermal_threshold_work_fn); pkgdev->phys_proc_id = topology_physical_package_id(cpu); pkgdev->cpu = cpu; pkgdev->tj_max = tj_max; @@ -401,7 +405,7 @@ static int pkg_temp_thermal_device_add(unsigned int cpu) static void put_core_offline(unsigned int cpu) { struct pkg_device *pkgdev = pkg_temp_thermal_get_dev(cpu); - bool lastcpu; + bool lastcpu, was_target; int target; if (!pkgdev) @@ -430,13 +434,24 @@ static void put_core_offline(unsigned int cpu) thermal_zone_device_unregister(tzone); } + /* Protect against work and interrupts */ + spin_lock_irq(&pkg_temp_lock); + /* - * If this is the last CPU in the package, restore the interrupt - * MSR and remove the package reference from the array. + * Check whether this cpu was the current target and store the new + * one. When we drop the lock, then the interrupt notify function + * will see the new target. + */ + was_target = pkgdev->cpu == cpu; + pkgdev->cpu = target; + + /* + * If this is the last CPU in the package remove the package + * reference from the list and restore the interrupt MSR. When we + * drop the lock neither the interrupt notify function nor the + * worker will see the package anymore. */ if (lastcpu) { - /* Protect against work and interrupts */ - spin_lock_irq(&pkg_temp_lock); list_del(&pkgdev->list); /* * After this point nothing touches the MSR anymore. We @@ -447,17 +462,36 @@ static void put_core_offline(unsigned int cpu) wrmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, pkgdev->msr_pkg_therm_low, pkgdev->msr_pkg_therm_high); - kfree(pkgdev); + spin_lock_irq(&pkg_temp_lock); } /* - * Note, this is broken when work was really scheduled on the - * outgoing cpu because this will leave the work_scheduled flag set - * and the thermal interrupts disabled. Will be fixed in the next - * step as there is no way to fix it in a sane way with the per cpu - * work nonsense. + * Check whether there is work scheduled and whether the work is + * targeted at the outgoing CPU. */ - cancel_delayed_work_sync(&per_cpu(pkg_temp_thermal_threshold_work, cpu)); + if (pkgdev->work_scheduled && was_target) { + /* + * To cancel the work we need to drop the lock, otherwise + * we might deadlock if the work needs to be flushed. + */ + spin_unlock_irq(&pkg_temp_lock); + cancel_delayed_work_sync(&pkgdev->work); + spin_lock_irq(&pkg_temp_lock); + /* + * If this is not the last cpu in the package and the work + * did not run after we dropped the lock above, then we + * need to reschedule the work, otherwise the interrupt + * stays disabled forever. + */ + if (!lastcpu && pkgdev->work_scheduled) + pkg_thermal_schedule_work(target, &pkgdev->work); + } + + spin_unlock_irq(&pkg_temp_lock); + + /* Final cleanup if this is the last cpu */ + if (lastcpu) + kfree(pkgdev); } static int get_core_online(unsigned int cpu) @@ -469,9 +503,6 @@ static int get_core_online(unsigned int cpu) if (!cpu_has(c, X86_FEATURE_DTHERM) || !cpu_has(c, X86_FEATURE_PTS)) return -ENODEV; - INIT_DELAYED_WORK(&per_cpu(pkg_temp_thermal_threshold_work, cpu), - pkg_temp_thermal_threshold_work_fn); - /* If the package exists, nothing to do */ if (pkgdev) { cpumask_set_cpu(cpu, &pkgdev->cpumask); From 556238e45c8deacb665fbee3944201b65cead594 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 22 Nov 2016 17:57:14 +0000 Subject: [PATCH 74/76] thermal/x86_pkg_temp: Sanitize package management Packages are kept in a list, which must be searched over and over. We can be smarter than that and just store the package pointers in an array which is allocated at init time. Sizing of the array is determined from the topology information. That makes the package search a simple array lookup. Signed-off-by: Thomas Gleixner Tested-by: Srinivas Pandruvada Signed-off-by: Zhang Rui --- drivers/thermal/x86_pkg_temp_thermal.c | 42 ++++++++++++++------------ 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c index 07db08cabc12..13f37d212164 100644 --- a/drivers/thermal/x86_pkg_temp_thermal.c +++ b/drivers/thermal/x86_pkg_temp_thermal.c @@ -54,13 +54,9 @@ MODULE_PARM_DESC(notify_delay_ms, * is some wrong values returned by cpuid for number of thresholds. */ #define MAX_NUMBER_OF_TRIPS 2 -/* Limit number of package temp zones */ -#define MAX_PKG_TEMP_ZONE_IDS 256 struct pkg_device { - struct list_head list; - u16 phys_proc_id; - u16 cpu; + int cpu; bool work_scheduled; u32 tj_max; u32 msr_pkg_therm_low; @@ -74,8 +70,10 @@ static struct thermal_zone_params pkg_temp_tz_params = { .no_hwmon = true, }; -/* List maintaining number of package instances */ -static LIST_HEAD(phy_dev_list); +/* Keep track of how many package pointers we allocated in init() */ +static int max_packages __read_mostly; +/* Array of package pointers */ +static struct pkg_device **packages; /* Serializes interrupt notification, work and hotplug */ static DEFINE_SPINLOCK(pkg_temp_lock); /* Protects zone operation in the work function against hotplug removal */ @@ -121,13 +119,10 @@ err_out: */ static struct pkg_device *pkg_temp_thermal_get_dev(unsigned int cpu) { - u16 phys_proc_id = topology_physical_package_id(cpu); - struct pkg_device *pkgdev; + int pkgid = topology_logical_package_id(cpu); - list_for_each_entry(pkgdev, &phy_dev_list, list) { - if (pkgdev->phys_proc_id == phys_proc_id) - return pkgdev; - } + if (pkgid >= 0 && pkgid < max_packages) + return packages[pkgid]; return NULL; } @@ -355,18 +350,19 @@ static int pkg_thermal_notify(u64 msr_val) static int pkg_temp_thermal_device_add(unsigned int cpu) { + int pkgid = topology_logical_package_id(cpu); u32 tj_max, eax, ebx, ecx, edx; struct pkg_device *pkgdev; int thres_count, err; + if (pkgid >= max_packages) + return -ENOMEM; + cpuid(6, &eax, &ebx, &ecx, &edx); thres_count = ebx & 0x07; if (!thres_count) return -ENODEV; - if (topology_physical_package_id(cpu) > MAX_PKG_TEMP_ZONE_IDS) - return -ENODEV; - thres_count = clamp_val(thres_count, 0, MAX_NUMBER_OF_TRIPS); err = get_tj_max(cpu, &tj_max); @@ -378,7 +374,6 @@ static int pkg_temp_thermal_device_add(unsigned int cpu) return -ENOMEM; INIT_DELAYED_WORK(&pkgdev->work, pkg_temp_thermal_threshold_work_fn); - pkgdev->phys_proc_id = topology_physical_package_id(cpu); pkgdev->cpu = cpu; pkgdev->tj_max = tj_max; pkgdev->tzone = thermal_zone_device_register("x86_pkg_temp", @@ -397,7 +392,7 @@ static int pkg_temp_thermal_device_add(unsigned int cpu) cpumask_set_cpu(cpu, &pkgdev->cpumask); spin_lock_irq(&pkg_temp_lock); - list_add_tail(&pkgdev->list, &phy_dev_list); + packages[pkgid] = pkgdev; spin_unlock_irq(&pkg_temp_lock); return 0; } @@ -447,12 +442,12 @@ static void put_core_offline(unsigned int cpu) /* * If this is the last CPU in the package remove the package - * reference from the list and restore the interrupt MSR. When we + * reference from the array and restore the interrupt MSR. When we * drop the lock neither the interrupt notify function nor the * worker will see the package anymore. */ if (lastcpu) { - list_del(&pkgdev->list); + packages[topology_logical_package_id(cpu)] = NULL; /* * After this point nothing touches the MSR anymore. We * must drop the lock to make the cross cpu call. This goes @@ -545,6 +540,11 @@ static int __init pkg_temp_thermal_init(void) if (!x86_match_cpu(pkg_temp_thermal_ids)) return -ENODEV; + max_packages = topology_max_packages(); + packages = kzalloc(max_packages * sizeof(struct pkg_device *), GFP_KERNEL); + if (!packages) + return -ENOMEM; + cpu_notifier_register_begin(); for_each_online_cpu(i) if (get_core_online(i)) @@ -563,6 +563,7 @@ err_ret: for_each_online_cpu(i) put_core_offline(i); cpu_notifier_register_done(); + kfree(packages); return -ENODEV; } module_init(pkg_temp_thermal_init) @@ -581,6 +582,7 @@ static void __exit pkg_temp_thermal_exit(void) cpu_notifier_register_done(); debugfs_remove_recursive(debugfs); + kfree(packages); } module_exit(pkg_temp_thermal_exit) From 7646ff2e7a33ef097c0e8d189c63a60764f757dc Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 22 Nov 2016 17:57:15 +0000 Subject: [PATCH 75/76] thermal/x86 pkg temp: Convert to hotplug state machine Install the callbacks via the state machine and let the core invoke the callbacks on the already online CPUs. Replace the wrmsr/rdmrs_on_cpu() calls in the hotplug callbacks as they are guaranteed to be invoked on the incoming/outgoing cpu. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Thomas Gleixner Tested-by: Srinivas Pandruvada Signed-off-by: Zhang Rui --- drivers/thermal/x86_pkg_temp_thermal.c | 80 ++++++++------------------ 1 file changed, 23 insertions(+), 57 deletions(-) diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c index 13f37d212164..d93eee2f101b 100644 --- a/drivers/thermal/x86_pkg_temp_thermal.c +++ b/drivers/thermal/x86_pkg_temp_thermal.c @@ -79,6 +79,9 @@ static DEFINE_SPINLOCK(pkg_temp_lock); /* Protects zone operation in the work function against hotplug removal */ static DEFINE_MUTEX(thermal_zone_mutex); +/* The dynamically assigned cpu hotplug state for module_exit() */ +static enum cpuhp_state pkg_thermal_hp_state __read_mostly; + /* Debug counters to show using debugfs */ static struct dentry *debugfs; static unsigned int pkg_interrupt_cnt; @@ -386,9 +389,8 @@ static int pkg_temp_thermal_device_add(unsigned int cpu) return err; } /* Store MSR value for package thermal interrupt, to restore at exit */ - rdmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, - &pkgdev->msr_pkg_therm_low, - &pkgdev->msr_pkg_therm_high); + rdmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, pkgdev->msr_pkg_therm_low, + pkgdev->msr_pkg_therm_high); cpumask_set_cpu(cpu, &pkgdev->cpumask); spin_lock_irq(&pkg_temp_lock); @@ -397,14 +399,14 @@ static int pkg_temp_thermal_device_add(unsigned int cpu) return 0; } -static void put_core_offline(unsigned int cpu) +static int pkg_thermal_cpu_offline(unsigned int cpu) { struct pkg_device *pkgdev = pkg_temp_thermal_get_dev(cpu); bool lastcpu, was_target; int target; if (!pkgdev) - return; + return 0; target = cpumask_any_but(&pkgdev->cpumask, cpu); cpumask_clear_cpu(cpu, &pkgdev->cpumask); @@ -448,16 +450,9 @@ static void put_core_offline(unsigned int cpu) */ if (lastcpu) { packages[topology_logical_package_id(cpu)] = NULL; - /* - * After this point nothing touches the MSR anymore. We - * must drop the lock to make the cross cpu call. This goes - * away once we move that code to the hotplug state machine. - */ - spin_unlock_irq(&pkg_temp_lock); - wrmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, - pkgdev->msr_pkg_therm_low, - pkgdev->msr_pkg_therm_high); - spin_lock_irq(&pkg_temp_lock); + /* After this point nothing touches the MSR anymore. */ + wrmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, + pkgdev->msr_pkg_therm_low, pkgdev->msr_pkg_therm_high); } /* @@ -487,9 +482,10 @@ static void put_core_offline(unsigned int cpu) /* Final cleanup if this is the last cpu */ if (lastcpu) kfree(pkgdev); + return 0; } -static int get_core_online(unsigned int cpu) +static int pkg_thermal_cpu_online(unsigned int cpu) { struct pkg_device *pkgdev = pkg_temp_thermal_get_dev(cpu); struct cpuinfo_x86 *c = &cpu_data(cpu); @@ -506,27 +502,6 @@ static int get_core_online(unsigned int cpu) return pkg_temp_thermal_device_add(cpu); } -static int pkg_temp_thermal_cpu_callback(struct notifier_block *nfb, - unsigned long action, void *hcpu) -{ - unsigned int cpu = (unsigned long) hcpu; - - switch (action & ~CPU_TASKS_FROZEN) { - case CPU_ONLINE: - case CPU_DOWN_FAILED: - get_core_online(cpu); - break; - case CPU_DOWN_PREPARE: - put_core_offline(cpu); - break; - } - return NOTIFY_OK; -} - -static struct notifier_block pkg_temp_thermal_notifier __refdata = { - .notifier_call = pkg_temp_thermal_cpu_callback, -}; - static const struct x86_cpu_id __initconst pkg_temp_thermal_ids[] = { { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_PTS }, {} @@ -535,7 +510,7 @@ MODULE_DEVICE_TABLE(x86cpu, pkg_temp_thermal_ids); static int __init pkg_temp_thermal_init(void) { - int i; + int ret; if (!x86_match_cpu(pkg_temp_thermal_ids)) return -ENODEV; @@ -545,12 +520,13 @@ static int __init pkg_temp_thermal_init(void) if (!packages) return -ENOMEM; - cpu_notifier_register_begin(); - for_each_online_cpu(i) - if (get_core_online(i)) - goto err_ret; - __register_hotcpu_notifier(&pkg_temp_thermal_notifier); - cpu_notifier_register_done(); + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "thermal/x86_pkg:online", + pkg_thermal_cpu_online, pkg_thermal_cpu_offline); + if (ret < 0) + goto err; + + /* Store the state for module exit */ + pkg_thermal_hp_state = ret; platform_thermal_package_notify = pkg_thermal_notify; platform_thermal_package_rate_control = pkg_thermal_rate_control; @@ -559,28 +535,18 @@ static int __init pkg_temp_thermal_init(void) pkg_temp_debugfs_init(); return 0; -err_ret: - for_each_online_cpu(i) - put_core_offline(i); - cpu_notifier_register_done(); +err: kfree(packages); - return -ENODEV; + return ret; } module_init(pkg_temp_thermal_init) static void __exit pkg_temp_thermal_exit(void) { - int i; - platform_thermal_package_notify = NULL; platform_thermal_package_rate_control = NULL; - cpu_notifier_register_begin(); - __unregister_hotcpu_notifier(&pkg_temp_thermal_notifier); - for_each_online_cpu(i) - put_core_offline(i); - cpu_notifier_register_done(); - + cpuhp_remove_state(pkg_thermal_hp_state); debugfs_remove_recursive(debugfs); kfree(packages); } From 0faf7dd5a947006978b549dfe29a01b710becf4a Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 12 Dec 2016 16:09:45 +0100 Subject: [PATCH 76/76] MAINTAINERS: Samsung: Update maintainer for PWM FAN and SAMSUNG THERMAL Since I leave Samsung, I would like to step down from maintenance duties. Bartek Zolnierkiewicz will replace. Signed-off-by: Lukasz Majewski Acked-by: Guenter Roeck Signed-off-by: Zhang Rui --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index ad9b965e5e44..d07ed0317b6f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9805,7 +9805,7 @@ F: drivers/media/usb/pwc/* PWM FAN DRIVER M: Kamil Debski -M: Lukasz Majewski +M: Bartlomiej Zolnierkiewicz L: linux-hwmon@vger.kernel.org S: Supported F: Documentation/devicetree/bindings/hwmon/pwm-fan.txt @@ -10576,7 +10576,7 @@ L: netdev@vger.kernel.org F: drivers/net/ethernet/samsung/sxgbe/ SAMSUNG THERMAL DRIVER -M: Lukasz Majewski +M: Bartlomiej Zolnierkiewicz L: linux-pm@vger.kernel.org L: linux-samsung-soc@vger.kernel.org S: Supported