From patchwork Fri May 19 03:27:13 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduardo Valentin X-Patchwork-Id: 684437 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3D832C7EE23 for ; Fri, 19 May 2023 03:27:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229661AbjESD1n (ORCPT ); Thu, 18 May 2023 23:27:43 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60486 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229485AbjESD1n (ORCPT ); Thu, 18 May 2023 23:27:43 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3F59010D8; Thu, 18 May 2023 20:27:41 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id C14FF653C9; Fri, 19 May 2023 03:27:40 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id B3906C4339B; Fri, 19 May 2023 03:27:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1684466860; bh=4asWSzWthCuWva4vbm2BVZjitrrI9jnAgkO5N4CiQ/M=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=FxPsrxAbmBFyE6Mhl8fb+fnO453wXVCmss/sM6ypIT6Mv4x6p0oeR9ESn1PFxNa4y LTbjvVoqQ646yEpsOGZZB13Qx30c5f4gQdQFmSv23pcZIpGYCkm/w3PNOKojBfzH6S DvYPU8R3fSXjVho3qa6FCm/KYgHyHajut+3KM498UVdGC8NcbipMXMY4kRHsiBGLVF i+G1vKXmVC/BDltyj1o7Hivv8Xh2NoC+831w/7BjQgzOECSoc/i1gfhPNLXrCkECN/ XOikShuFGYmlm9n2OTds7JFpDJieUlKTIO+2qZradvgZLivjMtGx50LagGsjxDSisE aFr5m6C4AN+CA== From: Eduardo Valentin To: eduval@amazon.com, linux-pm@vger.kernel.org Cc: "Rafael J. Wysocki" , Daniel Lezcano , Amit Kucheria , Zhang Rui , Jonathan Corbet , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 1/7] thermal: stats: track time each dev changes due to tz Date: Thu, 18 May 2023 20:27:13 -0700 Message-Id: <20230519032719.2581689-2-evalenti@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230519032719.2581689-1-evalenti@kernel.org> References: <20230519032719.2581689-1-evalenti@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org From: Eduardo Valentin This patch improves the current cooling device statistics by adding a new file under cdev/stats/time_in_thermal_zone_ms to represent the time in milliseconds that the cooling device was driven by each associated thermal zone. The file format is: thermal_zone: Samples: $ cat /sys/class/thermal/cooling_device0/stats/time_in_thermal_zone_ms thermal_zone: amb0 117496 Cc: "Rafael J. Wysocki" (supporter:THERMAL) Cc: Daniel Lezcano (supporter:THERMAL) Cc: Amit Kucheria (reviewer:THERMAL) Cc: Zhang Rui (reviewer:THERMAL) Cc: Jonathan Corbet (maintainer:DOCUMENTATION) Cc: linux-pm@vger.kernel.org (open list:THERMAL) Cc: linux-doc@vger.kernel.org (open list:DOCUMENTATION) Cc: linux-kernel@vger.kernel.org (open list) Signed-off-by: Eduardo Valentin --- .../driver-api/thermal/sysfs-api.rst | 2 + drivers/thermal/thermal_core.c | 2 +- drivers/thermal/thermal_core.h | 5 + drivers/thermal/thermal_helpers.c | 11 +- drivers/thermal/thermal_sysfs.c | 128 +++++++++++++++++- 5 files changed, 139 insertions(+), 9 deletions(-) diff --git a/Documentation/driver-api/thermal/sysfs-api.rst b/Documentation/driver-api/thermal/sysfs-api.rst index 6c1175c6afba..caa50d61a5bc 100644 --- a/Documentation/driver-api/thermal/sysfs-api.rst +++ b/Documentation/driver-api/thermal/sysfs-api.rst @@ -367,6 +367,8 @@ Thermal cooling device sys I/F, created once it's registered:: |---stats/time_in_state_ms: Time (msec) spent in various cooling states |---stats/total_trans: Total number of times cooling state is changed |---stats/trans_table: Cooling state transition table + |---stats/time_in_thermal_zone_ms: Time that this cooling device was driven + each associated thermal zone. Then next two dynamic attributes are created/removed in pairs. They represent diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 842f678c1c3e..4bb77af6a6f4 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1078,7 +1078,7 @@ void thermal_cooling_device_update(struct thermal_cooling_device *cdev) if (cdev->ops->get_cur_state(cdev, &state) || state > cdev->max_state) goto unlock; - thermal_cooling_device_stats_update(cdev, state); + thermal_cooling_device_stats_update(cdev, NULL, state); unlock: mutex_unlock(&cdev->lock); diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 3d4a787c6b28..3cce60c6e065 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -102,6 +102,9 @@ struct thermal_instance { struct list_head cdev_node; /* node in cdev->thermal_instances */ unsigned int weight; /* The weight of the cooling device */ bool upper_no_limit; +#if IS_ENABLED(CONFIG_THERMAL_STATISTICS) + ktime_t time_in; /* time spent in this instance */ +#endif }; #define to_thermal_zone(_dev) \ @@ -137,10 +140,12 @@ ssize_t weight_store(struct device *, struct device_attribute *, const char *, #ifdef CONFIG_THERMAL_STATISTICS void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev, + struct thermal_instance *instance, unsigned long new_state); #else static inline void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev, + struct thermal_instance *instance, unsigned long new_state) {} #endif /* CONFIG_THERMAL_STATISTICS */ diff --git a/drivers/thermal/thermal_helpers.c b/drivers/thermal/thermal_helpers.c index cfba0965a22d..ec8e86394977 100644 --- a/drivers/thermal/thermal_helpers.c +++ b/drivers/thermal/thermal_helpers.c @@ -149,18 +149,19 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp) EXPORT_SYMBOL_GPL(thermal_zone_get_temp); static void thermal_cdev_set_cur_state(struct thermal_cooling_device *cdev, + struct thermal_instance *target_instance, int target) { if (cdev->ops->set_cur_state(cdev, target)) return; thermal_notify_cdev_state_update(cdev->id, target); - thermal_cooling_device_stats_update(cdev, target); + thermal_cooling_device_stats_update(cdev, target_instance, target); } void __thermal_cdev_update(struct thermal_cooling_device *cdev) { - struct thermal_instance *instance; + struct thermal_instance *instance, *target_instance = NULL; unsigned long target = 0; /* Make sure cdev enters the deepest cooling state */ @@ -169,11 +170,13 @@ void __thermal_cdev_update(struct thermal_cooling_device *cdev) instance->tz->id, instance->target); if (instance->target == THERMAL_NO_TARGET) continue; - if (instance->target > target) + if (instance->target > target) { target = instance->target; + target_instance = instance; + } } - thermal_cdev_set_cur_state(cdev, target); + thermal_cdev_set_cur_state(cdev, target_instance, target); trace_cdev_update(cdev, target); dev_dbg(&cdev->device, "set to state %lu\n", target); diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index 6c20c9f90a05..a3b71f03db75 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -632,7 +632,7 @@ cur_state_store(struct device *dev, struct device_attribute *attr, result = cdev->ops->set_cur_state(cdev, state); if (!result) - thermal_cooling_device_stats_update(cdev, state); + thermal_cooling_device_stats_update(cdev, NULL, state); mutex_unlock(&cdev->lock); return result ? result : count; @@ -661,6 +661,7 @@ static const struct attribute_group *cooling_device_attr_groups[] = { }; #ifdef CONFIG_THERMAL_STATISTICS +/* thermal cooling device statistics handling */ struct cooling_dev_stats { spinlock_t lock; unsigned int total_trans; @@ -668,9 +669,29 @@ struct cooling_dev_stats { ktime_t last_time; ktime_t *time_in_state; unsigned int *trans_table; + struct thermal_instance *last_instance; + struct thermal_instance *curr_instance; }; -static void update_time_in_state(struct cooling_dev_stats *stats) +static void update_time_in_instance(struct cooling_dev_stats *stats, + struct thermal_instance *instance, + ktime_t now, ktime_t delta) +{ + if (!instance) + return; + + stats->last_instance = stats->curr_instance; + stats->curr_instance = instance; + + if (!stats->last_instance) + stats->last_instance = instance; + + stats->last_instance->time_in = + ktime_add(stats->last_instance->time_in, delta); +} + +static void update_time_in_state(struct cooling_dev_stats *stats, + struct thermal_instance *instance) { ktime_t now = ktime_get(), delta; @@ -678,9 +699,12 @@ static void update_time_in_state(struct cooling_dev_stats *stats) stats->time_in_state[stats->state] = ktime_add(stats->time_in_state[stats->state], delta); stats->last_time = now; + + update_time_in_instance(stats, instance, now, delta); } void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev, + struct thermal_instance *instance, unsigned long new_state) { struct cooling_dev_stats *stats = cdev->stats; @@ -695,7 +719,7 @@ void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev, if (stats->state == new_state) goto unlock; - update_time_in_state(stats); + update_time_in_state(stats, instance); stats->trans_table[stats->state * (cdev->max_state + 1) + new_state]++; stats->state = new_state; stats->total_trans++; @@ -744,7 +768,7 @@ time_in_state_ms_show(struct device *dev, struct device_attribute *attr, spin_lock(&stats->lock); - update_time_in_state(stats); + update_time_in_state(stats, stats->curr_instance); for (i = 0; i <= cdev->max_state; i++) { len += sprintf(buf + len, "state%u\t%llu\n", i, @@ -758,12 +782,98 @@ time_in_state_ms_show(struct device *dev, struct device_attribute *attr, return len; } +struct cdev_thermal_zone_residency { + char thermal_zone[THERMAL_NAME_LENGTH]; + ktime_t time_in; + unsigned long counter; + struct list_head node; /* we build this as we go */ +}; + +static void +build_cdev_thermal_zone_residency(struct list_head *list, + struct thermal_cooling_device *cdev) +{ + struct cdev_thermal_zone_residency *res, *update_res; + struct thermal_instance *instance; + + /* + * Build an array of pairs to represent + * how this cooling device was driven by each thermal zone + */ + list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) { + update_res = NULL; + + list_for_each_entry(res, list, node) { + if (strncmp(res->thermal_zone, instance->tz->type, + THERMAL_NAME_LENGTH) == 0) + update_res = res; + } + if (!update_res) { + update_res = kzalloc(sizeof(*update_res), GFP_KERNEL); + strscpy(update_res->thermal_zone, + instance->tz->type, THERMAL_NAME_LENGTH); + list_add_tail(&update_res->node, list); + } + } +} + +static ssize_t +time_in_thermal_zone_ms_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + LIST_HEAD(cdev_thermal_zone_list); + struct thermal_cooling_device *cdev = to_cooling_device(dev); + struct cooling_dev_stats *stats = cdev->stats; + struct cdev_thermal_zone_residency *res, *next; + struct thermal_instance *instance; + ssize_t len = 0, ret = 0; + + mutex_lock(&cdev->lock); + + spin_lock(&stats->lock); + update_time_in_state(stats, stats->curr_instance); + spin_unlock(&stats->lock); + + build_cdev_thermal_zone_residency(&cdev_thermal_zone_list, cdev); + + list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) + list_for_each_entry(res, &cdev_thermal_zone_list, node) + if (strncmp(res->thermal_zone, instance->tz->type, + THERMAL_NAME_LENGTH) == 0) + res->time_in = ktime_add(res->time_in, + instance->time_in); + + mutex_unlock(&cdev->lock); + + list_for_each_entry_safe(res, next, &cdev_thermal_zone_list, node) { + ret = snprintf(buf + len, PAGE_SIZE - len, + "thermal_zone: %s\t%llu\n", + res->thermal_zone, ktime_to_ms(res->time_in)); + + if (ret == 0) + ret = -EOVERFLOW; + + if (ret < 0) + break; + + len += ret; + } + + list_for_each_entry_safe(res, next, &cdev_thermal_zone_list, node) { + list_del(&res->node); + kfree(res); + } + + return ret < 0 ? ret : len; +} + static ssize_t reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct thermal_cooling_device *cdev = to_cooling_device(dev); struct cooling_dev_stats *stats; + struct thermal_instance *instance; int i, states; mutex_lock(&cdev->lock); @@ -774,6 +884,7 @@ reset_store(struct device *dev, struct device_attribute *attr, const char *buf, states = cdev->max_state + 1; + mutex_lock(&cdev->lock); spin_lock(&stats->lock); stats->total_trans = 0; @@ -784,7 +895,14 @@ reset_store(struct device *dev, struct device_attribute *attr, const char *buf, for (i = 0; i < states; i++) stats->time_in_state[i] = ktime_set(0, 0); + /* Make sure we reset all counters per instance */ + list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) { + instance->time_in = ktime_set(0, 0); + } + spin_unlock(&stats->lock); + mutex_unlock(&cdev->lock); + unlock: mutex_unlock(&cdev->lock); @@ -852,12 +970,14 @@ static ssize_t trans_table_show(struct device *dev, static DEVICE_ATTR_RO(total_trans); static DEVICE_ATTR_RO(time_in_state_ms); +static DEVICE_ATTR_RO(time_in_thermal_zone_ms); static DEVICE_ATTR_WO(reset); static DEVICE_ATTR_RO(trans_table); static struct attribute *cooling_device_stats_attrs[] = { &dev_attr_total_trans.attr, &dev_attr_time_in_state_ms.attr, + &dev_attr_time_in_thermal_zone_ms.attr, &dev_attr_reset.attr, &dev_attr_trans_table.attr, NULL From patchwork Fri May 19 03:27:14 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduardo Valentin X-Patchwork-Id: 684237 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3D483C7EE2C for ; Fri, 19 May 2023 03:27:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229825AbjESD1r (ORCPT ); Thu, 18 May 2023 23:27:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60498 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229648AbjESD1n (ORCPT ); Thu, 18 May 2023 23:27:43 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BB5AA10D9; Thu, 18 May 2023 20:27:41 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 51612653C7; Fri, 19 May 2023 03:27:41 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 579A2C433EF; Fri, 19 May 2023 03:27:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1684466860; bh=JeGq5bHAyBk0/sBP8+VGNu9E8xx/n54+KnUQPBhLFEw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=P+bo5OpEFRDeSS0JtAu63TcLpa6qhTAqyq7Nd8cSo7t1oJr4CPouWeyA7eTaZ2i6l Pjc1+YrrtVIdriGBdn/tHMuMyREsgb+41bmSf/GjtsBd9Y8ajXkcdYG9pWltDY0xgx GsmTGqaEfv9G62QWVzBf8lbmaXwk++BC+kLFAn8XbxE4XpcHt5f/XwMw/QQFbUOUhk hy8xwk5dTZPhfZMKbPLNPhgUTB8fgN7u4a1KSLNIPJswS51gO9JKvlqIimA9q+pG1O kX2YAc6JEZumTGRjcXWUdD704b5iDiHXNlDJLWi6VE2ito+4Azx0wgKE9Ge9XLDa6Y 33XZiy87H8KSA== From: Eduardo Valentin To: eduval@amazon.com, linux-pm@vger.kernel.org Cc: "Rafael J. Wysocki" , Daniel Lezcano , Amit Kucheria , Zhang Rui , Jonathan Corbet , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 2/7] thermal: stats: track number of change requests due to tz Date: Thu, 18 May 2023 20:27:14 -0700 Message-Id: <20230519032719.2581689-3-evalenti@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230519032719.2581689-1-evalenti@kernel.org> References: <20230519032719.2581689-1-evalenti@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org From: Eduardo Valentin This patch improves the current cooling device statistics by adding a new file under cdev/stats/requests_of_thermal_zone to represent the number of times each thermal zone requested the cooling device to effectively change. If the request associated was not serviced because another thermal zone asked for a higher cooling level, this counter does not increase. The file format is: thermal_zone: Samples: $ cat cdev0/stats/requests_of_thermal_zone thermal_zone: amb0 2 In this example, it means the thermal zone 'amb0' has requested 2 times for cdev0 to change state. Cc: "Rafael J. Wysocki" (supporter:THERMAL) Cc: Daniel Lezcano (supporter:THERMAL) Cc: Amit Kucheria (reviewer:THERMAL) Cc: Zhang Rui (reviewer:THERMAL) Cc: Jonathan Corbet (maintainer:DOCUMENTATION) Cc: linux-pm@vger.kernel.org (open list:THERMAL) Cc: linux-doc@vger.kernel.org (open list:DOCUMENTATION) Cc: linux-kernel@vger.kernel.org (open list) Signed-off-by: Eduardo Valentin --- .../driver-api/thermal/sysfs-api.rst | 2 + drivers/thermal/thermal_core.h | 1 + drivers/thermal/thermal_sysfs.c | 52 +++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/Documentation/driver-api/thermal/sysfs-api.rst b/Documentation/driver-api/thermal/sysfs-api.rst index caa50d61a5bc..75309a51d9b3 100644 --- a/Documentation/driver-api/thermal/sysfs-api.rst +++ b/Documentation/driver-api/thermal/sysfs-api.rst @@ -369,6 +369,8 @@ Thermal cooling device sys I/F, created once it's registered:: |---stats/trans_table: Cooling state transition table |---stats/time_in_thermal_zone_ms: Time that this cooling device was driven each associated thermal zone. + |---stats/requests_of_thermal_zone: Total number of times this cooling device + changed due to each associated thermal zone. Then next two dynamic attributes are created/removed in pairs. They represent diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 3cce60c6e065..ed6511c3b794 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -103,6 +103,7 @@ struct thermal_instance { unsigned int weight; /* The weight of the cooling device */ bool upper_no_limit; #if IS_ENABLED(CONFIG_THERMAL_STATISTICS) + unsigned long total_requests; ktime_t time_in; /* time spent in this instance */ #endif }; diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index a3b71f03db75..0bce1415f7e8 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -723,6 +723,7 @@ void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev, stats->trans_table[stats->state * (cdev->max_state + 1) + new_state]++; stats->state = new_state; stats->total_trans++; + stats->curr_instance->total_requests++; unlock: spin_unlock(&stats->lock); @@ -867,6 +868,54 @@ time_in_thermal_zone_ms_show(struct device *dev, struct device_attribute *attr, return ret < 0 ? ret : len; } +static ssize_t +requests_of_thermal_zone_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + LIST_HEAD(cdev_thermal_zone_list); + struct thermal_cooling_device *cdev = to_cooling_device(dev); + struct cooling_dev_stats *stats = cdev->stats; + struct cdev_thermal_zone_residency *res, *next; + struct thermal_instance *instance; + ssize_t len = 0, ret = 0; + + mutex_lock(&cdev->lock); + + spin_lock(&stats->lock); + update_time_in_state(stats, stats->curr_instance); + spin_unlock(&stats->lock); + + build_cdev_thermal_zone_residency(&cdev_thermal_zone_list, cdev); + + list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) + list_for_each_entry(res, &cdev_thermal_zone_list, node) + if (strncmp(res->thermal_zone, instance->tz->type, + THERMAL_NAME_LENGTH) == 0) + res->counter += instance->total_requests; + + mutex_unlock(&cdev->lock); + + list_for_each_entry_safe(res, next, &cdev_thermal_zone_list, node) { + ret = sprintf(buf + len, "thermal_zone: %s\t%lu\n", + res->thermal_zone, res->counter); + + if (ret == 0) + ret = -EOVERFLOW; + + if (ret < 0) + break; + + len += ret; + } + + list_for_each_entry_safe(res, next, &cdev_thermal_zone_list, node) { + list_del(&res->node); + kfree(res); + } + + return ret < 0 ? ret : len; +} + static ssize_t reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -897,6 +946,7 @@ reset_store(struct device *dev, struct device_attribute *attr, const char *buf, /* Make sure we reset all counters per instance */ list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) { + instance->total_requests = 0; instance->time_in = ktime_set(0, 0); } @@ -971,6 +1021,7 @@ static ssize_t trans_table_show(struct device *dev, static DEVICE_ATTR_RO(total_trans); static DEVICE_ATTR_RO(time_in_state_ms); static DEVICE_ATTR_RO(time_in_thermal_zone_ms); +static DEVICE_ATTR_RO(requests_of_thermal_zone); static DEVICE_ATTR_WO(reset); static DEVICE_ATTR_RO(trans_table); @@ -978,6 +1029,7 @@ static struct attribute *cooling_device_stats_attrs[] = { &dev_attr_total_trans.attr, &dev_attr_time_in_state_ms.attr, &dev_attr_time_in_thermal_zone_ms.attr, + &dev_attr_requests_of_thermal_zone.attr, &dev_attr_reset.attr, &dev_attr_trans_table.attr, NULL From patchwork Fri May 19 03:27:15 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduardo Valentin X-Patchwork-Id: 684436 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 70DFBC7EE23 for ; Fri, 19 May 2023 03:27:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229452AbjESD1t (ORCPT ); Thu, 18 May 2023 23:27:49 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60504 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229691AbjESD1o (ORCPT ); Thu, 18 May 2023 23:27:44 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5CE7610DD; Thu, 18 May 2023 20:27:42 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id E967D653B7; Fri, 19 May 2023 03:27:41 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id E3605C433D2; Fri, 19 May 2023 03:27:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1684466861; bh=YQ8esjaUlDPz/HxA3zI19y4t2Erk1u1PDHI45b/FAeQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=gTblL0ZRymq9Z9SHMp6VVNQpGAQ4UTw7GleisCltuJuFe1mZhQLo1+iwAW+PmIR11 VRoKelBaL1mQUnrSgoIQQAJJyeA8fHfXh4QgNyAjjYkWJSLMeU7EDdSRz9OyqGlq7q tCpIX5qcRLBr8iLGaDx+BAcSxK9BPgj9prQTfBXDmfFYUtVkUUeYIFKkf1626/w3Zo kB5Us0ZSP8cp7hUqlpul9s/klriqvkWMkOlLC1JO6/Ovd05vNAu4uSY6qyHu+2lRcm 356X3MCuQcoCN/bcBRB2HxgHj5DxmbAqZDrzCvUyBkmV+w+Ft4OB0x0ITZrmdvsbOF eKC12yR2ymnvA== From: Eduardo Valentin To: eduval@amazon.com, linux-pm@vger.kernel.org Cc: "Rafael J. Wysocki" , Daniel Lezcano , Amit Kucheria , Zhang Rui , Jonathan Corbet , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 3/7] thermal: stats: introduce thermal zone stats/ directory Date: Thu, 18 May 2023 20:27:15 -0700 Message-Id: <20230519032719.2581689-4-evalenti@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230519032719.2581689-1-evalenti@kernel.org> References: <20230519032719.2581689-1-evalenti@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org From: Eduardo Valentin This patch adds a new sysfs interface to report statistics of each thermal zone. This interface follows the existing thermal statistics for cooling devices design, in which it is provided a reset mechanism. The patch adds a statistic to track the maximum gradient (dT/dt) to the thermal zone stats/ folder. Sample, how gradient changes using emulation: $ cat stats/max_gradient 0 $ echo 2000 > emul_temp $ cat temp 2000 $ cat stats/max_gradient 2849 $ cat temp 2000 Cc: "Rafael J. Wysocki" (supporter:THERMAL) Cc: Daniel Lezcano (supporter:THERMAL) Cc: Amit Kucheria (reviewer:THERMAL) Cc: Zhang Rui (reviewer:THERMAL) Cc: Jonathan Corbet (maintainer:DOCUMENTATION) Cc: linux-pm@vger.kernel.org (open list:THERMAL) Cc: linux-doc@vger.kernel.org (open list:DOCUMENTATION) Cc: linux-kernel@vger.kernel.org (open list) Signed-off-by: Eduardo Valentin --- .../driver-api/thermal/sysfs-api.rst | 3 + drivers/thermal/thermal_core.c | 1 + drivers/thermal/thermal_core.h | 3 + drivers/thermal/thermal_sysfs.c | 142 +++++++++++++++++- include/linux/thermal.h | 2 + 5 files changed, 147 insertions(+), 4 deletions(-) diff --git a/Documentation/driver-api/thermal/sysfs-api.rst b/Documentation/driver-api/thermal/sysfs-api.rst index 75309a51d9b3..18140dbb1ce1 100644 --- a/Documentation/driver-api/thermal/sysfs-api.rst +++ b/Documentation/driver-api/thermal/sysfs-api.rst @@ -355,6 +355,9 @@ Thermal zone device sys I/F, created once it's registered:: |---integral_cutoff: Offset above which errors are accumulated |---slope: Slope constant applied as linear extrapolation |---offset: Offset constant applied as linear extrapolation + |---stats: Directory containing thermal zone device's stats + |---stats/reset_tz_stats: Writes to this file resets the statistics. + |---stats/max_gradient: The maximum recorded dT/dt in uC/ms. Thermal cooling device sys I/F, created once it's registered:: diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 4bb77af6a6f4..3ba970c0744f 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -385,6 +385,7 @@ static void update_temperature(struct thermal_zone_device *tz) tz->last_temperature = tz->temperature; tz->temperature = temp; + thermal_zone_device_stats_update(tz); trace_thermal_temperature(tz); diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index ed6511c3b794..ef37b92bbb7c 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -140,10 +140,13 @@ ssize_t weight_store(struct device *, struct device_attribute *, const char *, size_t); #ifdef CONFIG_THERMAL_STATISTICS +void thermal_zone_device_stats_update(struct thermal_zone_device *tz); void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev, struct thermal_instance *instance, unsigned long new_state); #else +static inline +void thermal_zone_device_stats_update(struct thermal_zone_device *tz) {} static inline void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev, struct thermal_instance *instance, diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index 0bce1415f7e8..aa28c1cae916 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -537,20 +537,147 @@ static void destroy_trip_attrs(struct thermal_zone_device *tz) kfree(tz->trips_attribute_group.attrs); } +#ifdef CONFIG_THERMAL_STATISTICS +/* thermal zone device statistics handling */ +struct thermal_zone_device_stats { + spinlock_t lock; /* protects this struct */ + s64 max_gradient; + ktime_t last_time; +}; + +#define DELTA_MILLI_C_TO_MICRO_C(t0, t1) (((t0) - (t1)) * 1000) +static void temperature_stats_update(struct thermal_zone_device *tz) +{ + struct thermal_zone_device_stats *stats = tz->stats; + ktime_t now = ktime_get(), delta; + s64 cur_gradient, delta_temp; + + delta = ktime_sub(now, stats->last_time); + stats->last_time = now; + + /* to see sub 1C/s, use uC/ms */ + delta_temp = DELTA_MILLI_C_TO_MICRO_C(tz->temperature, + tz->last_temperature); + cur_gradient = ktime_to_ms(delta); + if (cur_gradient) + cur_gradient = div64_s64(delta_temp, cur_gradient); + + /* Corner case of initialization, first temp read */ + if (tz->last_temperature == 0) + cur_gradient = 0; + + /* update fastest temperature rise from our perspective */ + if (cur_gradient > stats->max_gradient) + stats->max_gradient = cur_gradient; +} + +void thermal_zone_device_stats_update(struct thermal_zone_device *tz) +{ + struct thermal_zone_device_stats *stats = tz->stats; + + spin_lock(&stats->lock); + temperature_stats_update(tz); + spin_unlock(&stats->lock); +} + +static ssize_t max_gradient_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + struct thermal_zone_device_stats *stats = tz->stats; + int ret; + + spin_lock(&stats->lock); + temperature_stats_update(tz); + ret = snprintf(buf, PAGE_SIZE, "%lld\n", stats->max_gradient); + spin_unlock(&stats->lock); + + return ret; +} + +static ssize_t +reset_tz_stats_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_zone_device_stats *stats = tz->stats; + + spin_lock(&stats->lock); + + stats->max_gradient = 0; + stats->last_time = ktime_get(); + + spin_unlock(&stats->lock); + + return count; +} + +static DEVICE_ATTR_RO(max_gradient); +static DEVICE_ATTR_WO(reset_tz_stats); + +static struct attribute *thermal_zone_device_stats_attrs[] = { + &dev_attr_max_gradient.attr, + &dev_attr_reset_tz_stats.attr, + NULL +}; + +static const struct attribute_group thermal_zone_device_stats_attr_group = { + .attrs = thermal_zone_device_stats_attrs, + .name = "stats" +}; + +static void +thermal_zone_device_stats_setup(struct thermal_zone_device *tz, + const struct attribute_group **group) +{ + struct thermal_zone_device_stats *stats; + int var; + + var = sizeof(*stats); + + stats = kzalloc(var, GFP_KERNEL); + if (!stats) + return; + + stats->last_time = ktime_get(); + spin_lock_init(&stats->lock); + tz->stats = stats; + + /* Fill the empty slot left in thermal_zone_device_attr_groups */ + *group = &thermal_zone_device_stats_attr_group; +} + +static void thermal_zone_device_stats_destroy(struct thermal_zone_device *tz) +{ + kfree(tz->stats); + tz->stats = NULL; +} +#else + +static inline void +thermal_zone_device_stats_setup(struct thermal_zone_device *tz, + const struct attribute_group **group) {} +static inline void +thermal_zone_device_stats_destroy(struct thermal_zone_device *tz) {} + +#endif + int thermal_zone_create_device_groups(struct thermal_zone_device *tz, int mask) { const struct attribute_group **groups; - int i, size, result; + int i, size, result, extras = 2; + if (IS_ENABLED(CONFIG_THERMAL_STATISTIC)) + extras++; /* we need one extra for trips and the NULL to terminate the array */ - size = ARRAY_SIZE(thermal_zone_attribute_groups) + 2; + size = ARRAY_SIZE(thermal_zone_attribute_groups) + extras; /* 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++) + for (i = 0; i < size - extras; i++) groups[i] = thermal_zone_attribute_groups[i]; if (tz->num_trips) { @@ -561,9 +688,15 @@ int thermal_zone_create_device_groups(struct thermal_zone_device *tz, return result; } - groups[size - 2] = &tz->trips_attribute_group; + groups[size - extras] = &tz->trips_attribute_group; } + /* + * This will be nop if CONFIG_THERMAL_STATISTICS is not defined, + * otherwise, it is always the last entry in the group prior to NULL + */ + thermal_zone_device_stats_setup(tz, &groups[size - 2]); + tz->device.groups = groups; return 0; @@ -578,6 +711,7 @@ void thermal_zone_destroy_device_groups(struct thermal_zone_device *tz) destroy_trip_attrs(tz); kfree(tz->device.groups); + thermal_zone_device_stats_destroy(tz); } /* sys I/F for cooling device */ diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 87837094d549..9dc8292f0314 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -121,6 +121,7 @@ struct thermal_cooling_device { * @trip_type_attrs: attributes for trip points for sysfs: trip type * @trip_hyst_attrs: attributes for trip points for sysfs: trip hysteresis * @mode: current mode of this thermal zone + * @stats: private pointer to stats collected on this thermal zone * @devdata: private pointer for device private data * @trips: an array of struct thermal_trip * @num_trips: number of trip points the thermal zone supports @@ -162,6 +163,7 @@ struct thermal_zone_device { struct thermal_attr *trip_type_attrs; struct thermal_attr *trip_hyst_attrs; enum thermal_device_mode mode; + void *stats; void *devdata; struct thermal_trip *trips; int num_trips; From patchwork Fri May 19 03:27:16 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduardo Valentin X-Patchwork-Id: 684236 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2785BC7EE2D for ; Fri, 19 May 2023 03:27:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229940AbjESD1u (ORCPT ); Thu, 18 May 2023 23:27:50 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60506 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229701AbjESD1o (ORCPT ); Thu, 18 May 2023 23:27:44 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E7FBC12F; Thu, 18 May 2023 20:27:42 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 853F465262; Fri, 19 May 2023 03:27:42 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 81C28C4339C; Fri, 19 May 2023 03:27:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1684466861; bh=XfBSJ+X/hBSEcMCTITGsqxEQnjkEMITopqVn93fGedI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hijIT/G4cLMoceAMJWoOax1lD38yWOlDuanQf/5Ma0JVzn2fFW4GBC1lrKQ0kZdfJ Q7i3LuOemSgKNuOmgPPYhYyFR8q0B+vA9MbwKdcTNT4czkEYIr1FSi37HxeriQTYTh 9/vgHtutRSXXI3x6aFTn1z1WAVLb3L/g6/839lfRTL/GJVzgzpN2/M4wM9esQHX1QS MOTMqj1nuC3PHe+iSiDnziqmZa/i95kTvycmAOjKWxAxKrk/4XFR57ngSS2fxHRqxz VT4OXnOaC2510i/VMGW8vGAuO9xK66hM0u8MXOOac+42+SIQu/NcrjaQzk4Pm+hsPa 4icRWTquW9U7A== From: Eduardo Valentin To: eduval@amazon.com, linux-pm@vger.kernel.org Cc: "Rafael J. Wysocki" , Daniel Lezcano , Amit Kucheria , Zhang Rui , Jonathan Corbet , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 4/7] thermal: stats: introduce thermal zone stats/min_gradient Date: Thu, 18 May 2023 20:27:16 -0700 Message-Id: <20230519032719.2581689-5-evalenti@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230519032719.2581689-1-evalenti@kernel.org> References: <20230519032719.2581689-1-evalenti@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org From: Eduardo Valentin The patch adds a statistic to track the minimum gradient (dT/dt) to the thermal zone stats/ folder. Samples: $ echo 1000 > emul_temp $ cat stats/min_gradient 0 $ echo 2000 > emul_temp $ echo 1000 > emul_temp $ cat stats/min_gradient -3460 Cc: "Rafael J. Wysocki" (supporter:THERMAL) Cc: Daniel Lezcano (supporter:THERMAL) Cc: Amit Kucheria (reviewer:THERMAL) Cc: Zhang Rui (reviewer:THERMAL) Cc: Jonathan Corbet (maintainer:DOCUMENTATION) Cc: linux-pm@vger.kernel.org (open list:THERMAL) Cc: linux-doc@vger.kernel.org (open list:DOCUMENTATION) Cc: linux-kernel@vger.kernel.org (open list) Signed-off-by: Eduardo Valentin --- .../driver-api/thermal/sysfs-api.rst | 1 + drivers/thermal/thermal_sysfs.c | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/Documentation/driver-api/thermal/sysfs-api.rst b/Documentation/driver-api/thermal/sysfs-api.rst index 18140dbb1ce1..ed5e6ba4e0d7 100644 --- a/Documentation/driver-api/thermal/sysfs-api.rst +++ b/Documentation/driver-api/thermal/sysfs-api.rst @@ -358,6 +358,7 @@ Thermal zone device sys I/F, created once it's registered:: |---stats: Directory containing thermal zone device's stats |---stats/reset_tz_stats: Writes to this file resets the statistics. |---stats/max_gradient: The maximum recorded dT/dt in uC/ms. + |---stats/min_gradient: The minimum recorded dT/dt in uC/ms. Thermal cooling device sys I/F, created once it's registered:: diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index aa28c1cae916..f89ec9a7e8c8 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -542,6 +542,7 @@ static void destroy_trip_attrs(struct thermal_zone_device *tz) struct thermal_zone_device_stats { spinlock_t lock; /* protects this struct */ s64 max_gradient; + s64 min_gradient; ktime_t last_time; }; @@ -569,6 +570,10 @@ static void temperature_stats_update(struct thermal_zone_device *tz) /* update fastest temperature rise from our perspective */ if (cur_gradient > stats->max_gradient) stats->max_gradient = cur_gradient; + + /* update fastest temperature decay from our perspective */ + if (cur_gradient < stats->min_gradient) + stats->min_gradient = cur_gradient; } void thermal_zone_device_stats_update(struct thermal_zone_device *tz) @@ -595,6 +600,21 @@ static ssize_t max_gradient_show(struct device *dev, return ret; } +static ssize_t min_gradient_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + struct thermal_zone_device_stats *stats = tz->stats; + int ret; + + spin_lock(&stats->lock); + temperature_stats_update(tz); + ret = snprintf(buf, PAGE_SIZE, "%lld\n", stats->min_gradient); + spin_unlock(&stats->lock); + + return ret; +} + static ssize_t reset_tz_stats_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -604,6 +624,7 @@ reset_tz_stats_store(struct device *dev, struct device_attribute *attr, spin_lock(&stats->lock); + stats->min_gradient = 0; stats->max_gradient = 0; stats->last_time = ktime_get(); @@ -612,10 +633,12 @@ reset_tz_stats_store(struct device *dev, struct device_attribute *attr, return count; } +static DEVICE_ATTR_RO(min_gradient); static DEVICE_ATTR_RO(max_gradient); static DEVICE_ATTR_WO(reset_tz_stats); static struct attribute *thermal_zone_device_stats_attrs[] = { + &dev_attr_min_gradient.attr, &dev_attr_max_gradient.attr, &dev_attr_reset_tz_stats.attr, NULL From patchwork Fri May 19 03:27:17 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduardo Valentin X-Patchwork-Id: 684235 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 77B73C7EE2F for ; Fri, 19 May 2023 03:27:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229880AbjESD1w (ORCPT ); Thu, 18 May 2023 23:27:52 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60522 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229780AbjESD1p (ORCPT ); Thu, 18 May 2023 23:27:45 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2650610DF; Thu, 18 May 2023 20:27:43 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id AD2BD61D72; Fri, 19 May 2023 03:27:42 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 1CCD9C4339B; Fri, 19 May 2023 03:27:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1684466862; bh=j5rfFoVzEr1r1mhCyIHkEbkNMEE39ME6y6wkLNwDhXU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lYhbQcr2Jh0r1DJKiqXu81vy4FDODEAouLPYOQABwTLfDo5ARGG7zmpVHVW8Ja15U qOx/sUSIFsPUZzoanVTeDqOYJPT9XZE9EaM0LDvqypCJFeL1NUfQJXsSQU3sTW1FuN +wzBk1YKQR0ordWKRan4/pvgDWF3VOzF0coGpo6/MTu9hdHNsaXb0uJA0ZJ4f1X+Xj z/qLkyq6RLAGY2GH/HvcB3pjESiOGcQswd+t0beEToG23RdfUm2ZSxII8Z6vzOahrp 3xME5TdzDtWUqfOWRRTJgZqiQ/qG4Tp2NfXz16Wz7NXBz+oYR1WEZ0E1PnQ2GzQgtw Q5JT6MKAwo5ew== From: Eduardo Valentin To: eduval@amazon.com, linux-pm@vger.kernel.org Cc: "Rafael J. Wysocki" , Daniel Lezcano , Amit Kucheria , Zhang Rui , Jonathan Corbet , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 5/7] thermal: stats: introduce tz time in trip Date: Thu, 18 May 2023 20:27:17 -0700 Message-Id: <20230519032719.2581689-6-evalenti@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230519032719.2581689-1-evalenti@kernel.org> References: <20230519032719.2581689-1-evalenti@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org From: Eduardo Valentin This patch adds a statistic to report how long the thermal zone spent on temperature intervals created by each trip point. The first interval is the range below the first trip point. All subsequent intervals are accounted when temperature is above the trip point temperature value. Samples: $ cat /sys//class/thermal/thermal_zone0/stats/time_in_trip_ms trip-1 0 0 trip0 -10000 35188 trip1 25000 0 $ cat /sys//class/thermal/thermal_zone0/stats/time_in_trip_ms trip-1 0 0 trip0 -10000 36901 trip1 25000 0 $ echo 25001 > /sys//class/thermal/thermal_zone0/emul_temp $ cat /sys//class/thermal/thermal_zone0/stats/time_in_trip_ms trip-1 0 0 trip0 -10000 47810 trip1 25000 2259 $ cat /sys//class/thermal/thermal_zone0/stats/time_in_trip_ms trip-1 0 0 trip0 -10000 47810 trip1 25000 3224 $ echo 24001 > /sys//class/thermal/thermal_zone0/emul_temp $ cat /sys//class/thermal/thermal_zone0/stats/time_in_trip_ms trip-1 0 0 trip0 -10000 48960 trip1 25000 10080 $ cat /sys//class/thermal/thermal_zone0/stats/time_in_trip_ms trip-1 0 0 trip0 -10000 49844 trip1 25000 10080 Cc: "Rafael J. Wysocki" (supporter:THERMAL) Cc: Daniel Lezcano (supporter:THERMAL) Cc: Amit Kucheria (reviewer:THERMAL) Cc: Zhang Rui (reviewer:THERMAL) Cc: Jonathan Corbet (maintainer:DOCUMENTATION) Cc: linux-pm@vger.kernel.org (open list:THERMAL) Cc: linux-doc@vger.kernel.org (open list:DOCUMENTATION) Cc: linux-kernel@vger.kernel.org (open list) Signed-off-by: Eduardo Valentin --- .../driver-api/thermal/sysfs-api.rst | 2 + drivers/thermal/thermal_sysfs.c | 86 +++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/Documentation/driver-api/thermal/sysfs-api.rst b/Documentation/driver-api/thermal/sysfs-api.rst index ed5e6ba4e0d7..4a2b92a7488c 100644 --- a/Documentation/driver-api/thermal/sysfs-api.rst +++ b/Documentation/driver-api/thermal/sysfs-api.rst @@ -359,6 +359,8 @@ Thermal zone device sys I/F, created once it's registered:: |---stats/reset_tz_stats: Writes to this file resets the statistics. |---stats/max_gradient: The maximum recorded dT/dt in uC/ms. |---stats/min_gradient: The minimum recorded dT/dt in uC/ms. + |---stats/time_in_trip_ms: Time spent on each temperature interval of + trip points. Thermal cooling device sys I/F, created once it's registered:: diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index f89ec9a7e8c8..25851fe073c3 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -544,6 +544,7 @@ struct thermal_zone_device_stats { s64 max_gradient; s64 min_gradient; ktime_t last_time; + ktime_t *time_in_trip; }; #define DELTA_MILLI_C_TO_MICRO_C(t0, t1) (((t0) - (t1)) * 1000) @@ -552,6 +553,7 @@ static void temperature_stats_update(struct thermal_zone_device *tz) struct thermal_zone_device_stats *stats = tz->stats; ktime_t now = ktime_get(), delta; s64 cur_gradient, delta_temp; + int i, trip_id = -1; delta = ktime_sub(now, stats->last_time); stats->last_time = now; @@ -567,6 +569,29 @@ static void temperature_stats_update(struct thermal_zone_device *tz) if (tz->last_temperature == 0) cur_gradient = 0; + for (i = 0; i < tz->num_trips; i++) { + struct thermal_trip trip; + int ret; + + ret = __thermal_zone_get_trip(tz, i, &trip); + if (ret) + continue; + + if (tz->temperature > trip.temperature) + trip_id = i; + } + + /* + * Update how long we spend in each temperature interval. + * This array is like as follows: + * time_in_trip[0] == time spent with temperature <= trip[0] + * time_in_trip[1] == time spent with temperature > trip[0] + * time_in_trip[2] == time spent with temperature > trip[1] + * ... + */ + stats->time_in_trip[trip_id + 1] = + ktime_add(stats->time_in_trip[trip_id + 1], delta); + /* update fastest temperature rise from our perspective */ if (cur_gradient > stats->max_gradient) stats->max_gradient = cur_gradient; @@ -615,12 +640,66 @@ static ssize_t min_gradient_show(struct device *dev, return ret; } +static ssize_t +time_in_trip_ms_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + struct thermal_zone_device_stats *stats = tz->stats; + ssize_t len = 0, ret = 0; + int i; + + spin_lock(&stats->lock); + temperature_stats_update(tz); + + /* + * This should look like this: + * trip-1 X + * trip0 Y + * trip1 Z + * ... + * and the way to read is. This thermal zone temperature was seen + * X ms with tz->temperature <= trip0, Y ms with tz->temperature > trip0 + * and Z ms with tz->temperature > trip1. + */ + for (i = 0; i <= tz->num_trips; i++) { + int trip_temp = 0, r = 0; + + if (i > 0) { + struct thermal_trip trip; + + r = __thermal_zone_get_trip(tz, i - 1, &trip); + if (r < 0) { + ret = r; + break; + } + trip_temp = trip.temperature; + } + + ret = snprintf(buf + len, PAGE_SIZE - len, "trip%d\t%d\t%llu\n", + i - 1, trip_temp, + ktime_to_ms(stats->time_in_trip[i])); + + if (ret == 0) + ret = -EOVERFLOW; + + if (ret < 0) + break; + + len += ret; + } + spin_unlock(&stats->lock); + + return ret < 0 ? ret : len; +} + static ssize_t reset_tz_stats_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_zone_device_stats *stats = tz->stats; + int i; spin_lock(&stats->lock); @@ -628,6 +707,9 @@ reset_tz_stats_store(struct device *dev, struct device_attribute *attr, stats->max_gradient = 0; stats->last_time = ktime_get(); + for (i = 0; i <= tz->num_trips; i++) + stats->time_in_trip[i] = ktime_set(0, 0); + spin_unlock(&stats->lock); return count; @@ -635,11 +717,13 @@ reset_tz_stats_store(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR_RO(min_gradient); static DEVICE_ATTR_RO(max_gradient); +static DEVICE_ATTR_RO(time_in_trip_ms); static DEVICE_ATTR_WO(reset_tz_stats); static struct attribute *thermal_zone_device_stats_attrs[] = { &dev_attr_min_gradient.attr, &dev_attr_max_gradient.attr, + &dev_attr_time_in_trip_ms.attr, &dev_attr_reset_tz_stats.attr, NULL }; @@ -657,11 +741,13 @@ thermal_zone_device_stats_setup(struct thermal_zone_device *tz, int var; var = sizeof(*stats); + var += sizeof(*stats->time_in_trip) * (tz->num_trips + 1); stats = kzalloc(var, GFP_KERNEL); if (!stats) return; + stats->time_in_trip = (ktime_t *)(stats + 1); stats->last_time = ktime_get(); spin_lock_init(&stats->lock); tz->stats = stats; From patchwork Fri May 19 03:27:18 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduardo Valentin X-Patchwork-Id: 684435 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3AB0AC77B7F for ; Fri, 19 May 2023 03:27:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229945AbjESD1w (ORCPT ); Thu, 18 May 2023 23:27:52 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60520 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229789AbjESD1p (ORCPT ); Thu, 18 May 2023 23:27:45 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0CF5910D8; Thu, 18 May 2023 20:27:44 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 9F457653CA; Fri, 19 May 2023 03:27:43 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id A83B9C433D2; Fri, 19 May 2023 03:27:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1684466863; bh=HdWlDRXipx3TpYgWVO+5t2tBpNikk3svror7f2vLIJ4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=mGLvwm9S2HUt+CyrnOTyMXhDZ/Kdo5rDHzNg4qkW5J5/oGt/tQrUYdoBYyOxtBTQc aBDFlWd+aur0KEkcIBs+4nXKSS9bM8EusrvgClZO+eSf1MCff/rAZaYgR8igZiSgyR pn9siByJIJb1lzgUOWkefklJkdkWPNoPZ/zOAyhLGfY0JP2mHYM2OKoci/Wgp296or FEoL1lk7+KOyo2T3qKAXZF7wqOBGRa0Mn9s13j10kf6GVwZnAvf1sYzu5nrFhDtUw7 bhTFTL074RdcEgnewdfe2qTST91YUXtfbn7i/pVTIZ9EpBglvFuLdHXWX0Vez92rzK iH5oZnht9Fshg== From: Eduardo Valentin To: eduval@amazon.com, linux-pm@vger.kernel.org Cc: "Rafael J. Wysocki" , Daniel Lezcano , Amit Kucheria , Zhang Rui , Jonathan Corbet , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 6/7] ythermal: core: report errors to governors Date: Thu, 18 May 2023 20:27:18 -0700 Message-Id: <20230519032719.2581689-7-evalenti@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230519032719.2581689-1-evalenti@kernel.org> References: <20230519032719.2581689-1-evalenti@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org From: Eduardo Valentin Currently the thermal governors are not allowed to react on temperature error events as the thermal core skips the handling and logs an error on kernel buffer. This patch adds the opportunity to report the errors when they happen to governors. Now, if a governor wants to react on temperature read errors, they can implement the .check_error() callback. Cc: "Rafael J. Wysocki" (supporter:THERMAL) Cc: Daniel Lezcano (supporter:THERMAL) Cc: Amit Kucheria (reviewer:THERMAL) Cc: Zhang Rui (reviewer:THERMAL) Cc: Jonathan Corbet (maintainer:DOCUMENTATION) Cc: linux-pm@vger.kernel.org (open list:THERMAL) Cc: linux-doc@vger.kernel.org (open list:DOCUMENTATION) Cc: linux-kernel@vger.kernel.org (open list) Signed-off-by: Eduardo Valentin --- drivers/thermal/thermal_core.c | 9 +++++++++ include/linux/thermal.h | 3 +++ 2 files changed, 12 insertions(+) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 3ba970c0744f..2ff7d9c7c973 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -313,6 +313,12 @@ static void handle_non_critical_trips(struct thermal_zone_device *tz, int trip) def_governor->throttle(tz, trip); } +static void handle_error_temperature(struct thermal_zone_device *tz, int error) +{ + if (tz->governor && tz->governor->check_error) + tz->governor->check_error(tz, error); +} + void thermal_zone_device_critical(struct thermal_zone_device *tz) { /* @@ -380,6 +386,9 @@ static void update_temperature(struct thermal_zone_device *tz) dev_warn(&tz->device, "failed to read out thermal zone (%d)\n", ret); + /* tell the governor its source is hosed */ + handle_error_temperature(tz, ret); + return; } diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 9dc8292f0314..82c8e09a63e0 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -199,6 +199,8 @@ struct thermal_zone_device { * thermal zone. * @throttle: callback called for every trip point even if temperature is * below the trip point temperature + * @check_error: callback called whenever temperature updates fail. + * Opportunity for the governor to react on errors. * @governor_list: node in thermal_governor_list (in thermal_core.c) */ struct thermal_governor { @@ -206,6 +208,7 @@ struct thermal_governor { int (*bind_to_tz)(struct thermal_zone_device *tz); void (*unbind_from_tz)(struct thermal_zone_device *tz); int (*throttle)(struct thermal_zone_device *tz, int trip); + void (*check_error)(struct thermal_zone_device *tz, int error); struct list_head governor_list; }; From patchwork Fri May 19 03:27:19 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduardo Valentin X-Patchwork-Id: 684434 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id BD658C7EE30 for ; Fri, 19 May 2023 03:27:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230074AbjESD1y (ORCPT ); Thu, 18 May 2023 23:27:54 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60530 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229812AbjESD1q (ORCPT ); Thu, 18 May 2023 23:27:46 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B278E10E9; Thu, 18 May 2023 20:27:44 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 4112E653C3; Fri, 19 May 2023 03:27:44 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3F121C4339E; Fri, 19 May 2023 03:27:43 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1684466863; bh=ujYvyZ0mZ6AlXxLS56qjiYCxAcNxvL+EYzl8ihJpbwg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=B4wgFIUdZPMp2aSIvY0Dtwi1ytQPZlrke9rEsq2Nv3a7vcSzwbzO/UYxJ0HkiP0fK 9C82l58vw22sST2p8bZdYTlf3nbfce9LvPZe0GBGyEqTjrkdYrqEaPyoH/AtRDK3Bz xpzvuW5KCW6RdYhaPMrmYlSyNfz0wSwnObqZqdw3dw+eajk1R6NESCAMwv5yWVNq/j irs+aDFBWTPEgEKzEFE2C802KDE6jE3WDtja8I9IFFPa15nPzA+OPvFdpQrA7l3U99 RCCqmcYwLA0o93mZ6Br/zJ7Tgz48iqkgNP3o5uQJXX7V/4g2g8AphLiSFfVGyOp/nZ 1wthwW0b7FpNg== From: Eduardo Valentin To: eduval@amazon.com, linux-pm@vger.kernel.org Cc: "Rafael J. Wysocki" , Daniel Lezcano , Amit Kucheria , Zhang Rui , Jonathan Corbet , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 7/7] thermal: stats: add error accounting to thermal zone Date: Thu, 18 May 2023 20:27:19 -0700 Message-Id: <20230519032719.2581689-8-evalenti@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230519032719.2581689-1-evalenti@kernel.org> References: <20230519032719.2581689-1-evalenti@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org From: Eduardo Valentin This patch adds an extra stat to report how many temperature update failures were detected. Error count is increase whenever the thermal driver returns an actual error or when the temperature is non positive. Sample: $ cat /sys/class/thermal/thermal_zone0/stats/error_count 0 $ echo -1 > /sys/class/thermal/thermal_zone0/emul_temp $ cat /sys/class/thermal/thermal_zone0/stats/error_count 3 Cc: "Rafael J. Wysocki" (supporter:THERMAL) Cc: Daniel Lezcano (supporter:THERMAL) Cc: Amit Kucheria (reviewer:THERMAL) Cc: Zhang Rui (reviewer:THERMAL) Cc: Jonathan Corbet (maintainer:DOCUMENTATION) Cc: linux-pm@vger.kernel.org (open list:THERMAL) Cc: linux-doc@vger.kernel.org (open list:DOCUMENTATION) Cc: linux-kernel@vger.kernel.org (open list) Signed-off-by: Eduardo Valentin --- drivers/thermal/thermal_core.c | 3 ++ drivers/thermal/thermal_core.h | 7 ++++ drivers/thermal/thermal_sysfs.c | 64 +++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 2ff7d9c7c973..359e7b2ff0e3 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -389,6 +389,9 @@ static void update_temperature(struct thermal_zone_device *tz) /* tell the governor its source is hosed */ handle_error_temperature(tz, ret); + /* book keeping */ + thermal_zone_device_error_stats_update(tz, ret); + return; } diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index ef37b92bbb7c..612f93e6c257 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -141,12 +141,19 @@ ssize_t weight_store(struct device *, struct device_attribute *, const char *, #ifdef CONFIG_THERMAL_STATISTICS void thermal_zone_device_stats_update(struct thermal_zone_device *tz); +void thermal_zone_device_error_stats_update(struct thermal_zone_device *tz, + int error); void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev, struct thermal_instance *instance, unsigned long new_state); #else static inline void thermal_zone_device_stats_update(struct thermal_zone_device *tz) {} +static inline +void thermal_zone_device_error_stats_update(struct thermal_zone_device *tz, + int error) +{ +} static inline void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev, struct thermal_instance *instance, diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index 25851fe073c3..e511042e9dab 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -541,12 +541,21 @@ static void destroy_trip_attrs(struct thermal_zone_device *tz) /* thermal zone device statistics handling */ struct thermal_zone_device_stats { spinlock_t lock; /* protects this struct */ + unsigned int error_count; /* just account them */ + int max_temperature; s64 max_gradient; s64 min_gradient; ktime_t last_time; ktime_t *time_in_trip; }; +static void error_stats_update(struct thermal_zone_device *tz, int error) +{ + struct thermal_zone_device_stats *stats = tz->stats; + + stats->error_count++; +} + #define DELTA_MILLI_C_TO_MICRO_C(t0, t1) (((t0) - (t1)) * 1000) static void temperature_stats_update(struct thermal_zone_device *tz) { @@ -555,6 +564,15 @@ static void temperature_stats_update(struct thermal_zone_device *tz) s64 cur_gradient, delta_temp; int i, trip_id = -1; + if (tz->temperature <= 0) { + /* probably a wrong reading */ + error_stats_update(tz, tz->temperature); + return; + } + + if (tz->temperature > stats->max_temperature) + stats->max_temperature = tz->temperature; + delta = ktime_sub(now, stats->last_time); stats->last_time = now; @@ -610,6 +628,31 @@ void thermal_zone_device_stats_update(struct thermal_zone_device *tz) spin_unlock(&stats->lock); } +void thermal_zone_device_error_stats_update(struct thermal_zone_device *tz, + int error) +{ + struct thermal_zone_device_stats *stats = tz->stats; + + spin_lock(&stats->lock); + error_stats_update(tz, error); + spin_unlock(&stats->lock); +} + +static ssize_t max_temperature_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + struct thermal_zone_device_stats *stats = tz->stats; + int ret; + + spin_lock(&stats->lock); + temperature_stats_update(tz); + ret = snprintf(buf, PAGE_SIZE, "%d\n", stats->max_temperature); + spin_unlock(&stats->lock); + + return ret; +} + static ssize_t max_gradient_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -640,6 +683,21 @@ static ssize_t min_gradient_show(struct device *dev, return ret; } +static ssize_t error_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + struct thermal_zone_device_stats *stats = tz->stats; + int ret; + + spin_lock(&stats->lock); + temperature_stats_update(tz); + ret = snprintf(buf, PAGE_SIZE, "%u\n", stats->error_count); + spin_unlock(&stats->lock); + + return ret; +} + static ssize_t time_in_trip_ms_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -705,6 +763,8 @@ reset_tz_stats_store(struct device *dev, struct device_attribute *attr, stats->min_gradient = 0; stats->max_gradient = 0; + stats->max_temperature = 0; + stats->error_count = 0; stats->last_time = ktime_get(); for (i = 0; i <= tz->num_trips; i++) @@ -717,13 +777,17 @@ reset_tz_stats_store(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR_RO(min_gradient); static DEVICE_ATTR_RO(max_gradient); +static DEVICE_ATTR_RO(max_temperature); static DEVICE_ATTR_RO(time_in_trip_ms); +static DEVICE_ATTR_RO(error_count); static DEVICE_ATTR_WO(reset_tz_stats); static struct attribute *thermal_zone_device_stats_attrs[] = { &dev_attr_min_gradient.attr, &dev_attr_max_gradient.attr, + &dev_attr_max_temperature.attr, &dev_attr_time_in_trip_ms.attr, + &dev_attr_error_count.attr, &dev_attr_reset_tz_stats.attr, NULL };