diff options
Diffstat (limited to 'drivers/thermal/thermal_debugfs.c')
-rw-r--r-- | drivers/thermal/thermal_debugfs.c | 171 |
1 files changed, 126 insertions, 45 deletions
diff --git a/drivers/thermal/thermal_debugfs.c b/drivers/thermal/thermal_debugfs.c index c617e8b9f0dd..91f9c21235a8 100644 --- a/drivers/thermal/thermal_debugfs.c +++ b/drivers/thermal/thermal_debugfs.c @@ -139,11 +139,13 @@ struct tz_episode { * we keep track of the current position in the history array. * * @tz_episodes: a list of thermal mitigation episodes + * @tz: thermal zone this object belongs to * @trips_crossed: an array of trip points crossed by id * @nr_trips: the number of trip points currently being crossed */ struct tz_debugfs { struct list_head tz_episodes; + struct thermal_zone_device *tz; int *trips_crossed; int nr_trips; }; @@ -433,6 +435,14 @@ void thermal_debug_cdev_state_update(const struct thermal_cooling_device *cdev, } cdev_dbg->current_state = new_state; + + /* + * Create a record for the new state if it is not there, so its + * duration will be printed by cdev_dt_seq_show() as expected if it + * runs before the next state transition. + */ + thermal_debugfs_cdev_record_get(thermal_dbg, cdev_dbg->durations, new_state); + transition = (old_state << 16) | new_state; /* @@ -458,8 +468,9 @@ void thermal_debug_cdev_state_update(const struct thermal_cooling_device *cdev, * Allocates a cooling device object for debug, initializes the * statistics and create the entries in sysfs. * @cdev: a pointer to a cooling device + * @state: current state of the cooling device */ -void thermal_debug_cdev_add(struct thermal_cooling_device *cdev) +void thermal_debug_cdev_add(struct thermal_cooling_device *cdev, int state) { struct thermal_debugfs *thermal_dbg; struct cdev_debugfs *cdev_dbg; @@ -476,9 +487,16 @@ void thermal_debug_cdev_add(struct thermal_cooling_device *cdev) INIT_LIST_HEAD(&cdev_dbg->durations[i]); } - cdev_dbg->current_state = 0; + cdev_dbg->current_state = state; cdev_dbg->timestamp = ktime_get(); + /* + * Create a record for the initial cooling device state, so its + * duration will be printed by cdev_dt_seq_show() as expected if it + * runs before the first state transition. + */ + thermal_debugfs_cdev_record_get(thermal_dbg, cdev_dbg->durations, state); + debugfs_create_file("trans_table", 0400, thermal_dbg->d_top, thermal_dbg, &tt_fops); @@ -503,15 +521,23 @@ void thermal_debug_cdev_add(struct thermal_cooling_device *cdev) */ void thermal_debug_cdev_remove(struct thermal_cooling_device *cdev) { - struct thermal_debugfs *thermal_dbg = cdev->debugfs; + struct thermal_debugfs *thermal_dbg; - if (!thermal_dbg) + mutex_lock(&cdev->lock); + + thermal_dbg = cdev->debugfs; + if (!thermal_dbg) { + mutex_unlock(&cdev->lock); return; + } + + cdev->debugfs = NULL; + + mutex_unlock(&cdev->lock); mutex_lock(&thermal_dbg->lock); thermal_debugfs_cdev_clear(&thermal_dbg->cdev_dbg); - cdev->debugfs = NULL; mutex_unlock(&thermal_dbg->lock); @@ -530,6 +556,7 @@ static struct tz_episode *thermal_debugfs_tz_event_alloc(struct thermal_zone_dev INIT_LIST_HEAD(&tze->node); tze->timestamp = now; + tze->duration = KTIME_MIN; for (i = 0; i < tz->num_trips; i++) { tze->trip_stats[i].min = INT_MAX; @@ -545,7 +572,6 @@ void thermal_debug_tz_trip_up(struct thermal_zone_device *tz, struct tz_episode *tze; struct tz_debugfs *tz_dbg; struct thermal_debugfs *thermal_dbg = tz->debugfs; - int temperature = tz->temperature; int trip_id = thermal_zone_trip_id(tz, trip); ktime_t now = ktime_get(); @@ -614,11 +640,6 @@ void thermal_debug_tz_trip_up(struct thermal_zone_device *tz, tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node); tze->trip_stats[trip_id].timestamp = now; - tze->trip_stats[trip_id].max = max(tze->trip_stats[trip_id].max, temperature); - tze->trip_stats[trip_id].min = min(tze->trip_stats[trip_id].min, temperature); - tze->trip_stats[trip_id].avg = tze->trip_stats[trip_id].avg + - (temperature - tze->trip_stats[trip_id].avg) / - tze->trip_stats[trip_id].count; unlock: mutex_unlock(&thermal_dbg->lock); @@ -671,6 +692,9 @@ void thermal_debug_tz_trip_down(struct thermal_zone_device *tz, tze->trip_stats[trip_id].duration = ktime_add(delta, tze->trip_stats[trip_id].duration); + /* Mark the end of mitigation for this trip point. */ + tze->trip_stats[trip_id].timestamp = KTIME_MAX; + /* * This event closes the mitigation as we are crossing the * last trip point the way down. @@ -682,12 +706,12 @@ out: mutex_unlock(&thermal_dbg->lock); } -void thermal_debug_update_temp(struct thermal_zone_device *tz) +void thermal_debug_update_trip_stats(struct thermal_zone_device *tz) { struct thermal_debugfs *thermal_dbg = tz->debugfs; - struct tz_episode *tze; struct tz_debugfs *tz_dbg; - int trip_id, i; + struct tz_episode *tze; + int i; if (!thermal_dbg) return; @@ -699,15 +723,16 @@ void thermal_debug_update_temp(struct thermal_zone_device *tz) if (!tz_dbg->nr_trips) goto out; + tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node); + for (i = 0; i < tz_dbg->nr_trips; i++) { - trip_id = tz_dbg->trips_crossed[i]; - tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node); - tze->trip_stats[trip_id].count++; - tze->trip_stats[trip_id].max = max(tze->trip_stats[trip_id].max, tz->temperature); - tze->trip_stats[trip_id].min = min(tze->trip_stats[trip_id].min, tz->temperature); - tze->trip_stats[trip_id].avg = tze->trip_stats[trip_id].avg + - (tz->temperature - tze->trip_stats[trip_id].avg) / - tze->trip_stats[trip_id].count; + int trip_id = tz_dbg->trips_crossed[i]; + struct trip_stats *trip_stats = &tze->trip_stats[trip_id]; + + trip_stats->max = max(trip_stats->max, tz->temperature); + trip_stats->min = min(trip_stats->min, tz->temperature); + trip_stats->avg += (tz->temperature - trip_stats->avg) / + ++trip_stats->count; } out: mutex_unlock(&thermal_dbg->lock); @@ -715,8 +740,7 @@ out: static void *tze_seq_start(struct seq_file *s, loff_t *pos) { - struct thermal_zone_device *tz = s->private; - struct thermal_debugfs *thermal_dbg = tz->debugfs; + struct thermal_debugfs *thermal_dbg = s->private; struct tz_debugfs *tz_dbg = &thermal_dbg->tz_dbg; mutex_lock(&thermal_dbg->lock); @@ -726,8 +750,7 @@ static void *tze_seq_start(struct seq_file *s, loff_t *pos) static void *tze_seq_next(struct seq_file *s, void *v, loff_t *pos) { - struct thermal_zone_device *tz = s->private; - struct thermal_debugfs *thermal_dbg = tz->debugfs; + struct thermal_debugfs *thermal_dbg = s->private; struct tz_debugfs *tz_dbg = &thermal_dbg->tz_dbg; return seq_list_next(v, &tz_dbg->tz_episodes, pos); @@ -735,29 +758,46 @@ static void *tze_seq_next(struct seq_file *s, void *v, loff_t *pos) static void tze_seq_stop(struct seq_file *s, void *v) { - struct thermal_zone_device *tz = s->private; - struct thermal_debugfs *thermal_dbg = tz->debugfs; + struct thermal_debugfs *thermal_dbg = s->private; mutex_unlock(&thermal_dbg->lock); } static int tze_seq_show(struct seq_file *s, void *v) { - struct thermal_zone_device *tz = s->private; - struct thermal_trip *trip; + struct thermal_debugfs *thermal_dbg = s->private; + struct thermal_zone_device *tz = thermal_dbg->tz_dbg.tz; + struct thermal_trip_desc *td; struct tz_episode *tze; const char *type; + u64 duration_ms; int trip_id; + char c; tze = list_entry((struct list_head *)v, struct tz_episode, node); - seq_printf(s, ",-Mitigation at %lluus, duration=%llums\n", - ktime_to_us(tze->timestamp), - ktime_to_ms(tze->duration)); + if (tze->duration == KTIME_MIN) { + /* Mitigation in progress. */ + duration_ms = ktime_to_ms(ktime_sub(ktime_get(), tze->timestamp)); + c = '>'; + } else { + duration_ms = ktime_to_ms(tze->duration); + c = '='; + } + + seq_printf(s, ",-Mitigation at %lluus, duration%c%llums\n", + ktime_to_us(tze->timestamp), c, duration_ms); + + seq_printf(s, "| trip | type | temp(°mC) | hyst(°mC) | duration | avg(°mC) | min(°mC) | max(°mC) |\n"); - seq_printf(s, "| trip | type | temp(°mC) | hyst(°mC) | duration | avg(°mC) | min(°mC) | max(°mC) |\n"); + for_each_trip_desc(tz, td) { + const struct thermal_trip *trip = &td->trip; + struct trip_stats *trip_stats; + + /* Skip invalid trips. */ + if (trip->temperature == THERMAL_TEMP_INVALID) + continue; - for_each_trip(tz, trip) { /* * There is no possible mitigation happening at the * critical trip point, so the stats will be always @@ -766,6 +806,13 @@ static int tze_seq_show(struct seq_file *s, void *v) if (trip->type == THERMAL_TRIP_CRITICAL) continue; + trip_id = thermal_zone_trip_id(tz, trip); + trip_stats = &tze->trip_stats[trip_id]; + + /* Skip trips without any stats. */ + if (trip_stats->min > trip_stats->max) + continue; + if (trip->type == THERMAL_TRIP_PASSIVE) type = "passive"; else if (trip->type == THERMAL_TRIP_ACTIVE) @@ -773,17 +820,28 @@ static int tze_seq_show(struct seq_file *s, void *v) else type = "hot"; - trip_id = thermal_zone_trip_id(tz, trip); + if (trip_stats->timestamp != KTIME_MAX) { + /* Mitigation in progress. */ + ktime_t delta = ktime_sub(ktime_get(), + trip_stats->timestamp); + + delta = ktime_add(delta, trip_stats->duration); + duration_ms = ktime_to_ms(delta); + c = '>'; + } else { + duration_ms = ktime_to_ms(trip_stats->duration); + c = ' '; + } - seq_printf(s, "| %*d | %*s | %*d | %*d | %*lld | %*d | %*d | %*d |\n", + seq_printf(s, "| %*d | %*s | %*d | %*d | %c%*lld | %*d | %*d | %*d |\n", 4 , trip_id, 8, type, 9, trip->temperature, 9, trip->hysteresis, - 10, ktime_to_ms(tze->trip_stats[trip_id].duration), - 9, tze->trip_stats[trip_id].avg, - 9, tze->trip_stats[trip_id].min, - 9, tze->trip_stats[trip_id].max); + c, 10, duration_ms, + 9, trip_stats->avg, + 9, trip_stats->min, + 9, trip_stats->max); } return 0; @@ -809,6 +867,8 @@ void thermal_debug_tz_add(struct thermal_zone_device *tz) tz_dbg = &thermal_dbg->tz_dbg; + tz_dbg->tz = tz; + tz_dbg->trips_crossed = kzalloc(sizeof(int) * tz->num_trips, GFP_KERNEL); if (!tz_dbg->trips_crossed) { thermal_debugfs_remove_id(thermal_dbg); @@ -817,23 +877,44 @@ void thermal_debug_tz_add(struct thermal_zone_device *tz) INIT_LIST_HEAD(&tz_dbg->tz_episodes); - debugfs_create_file("mitigations", 0400, thermal_dbg->d_top, tz, &tze_fops); + debugfs_create_file("mitigations", 0400, thermal_dbg->d_top, + thermal_dbg, &tze_fops); tz->debugfs = thermal_dbg; } void thermal_debug_tz_remove(struct thermal_zone_device *tz) { - struct thermal_debugfs *thermal_dbg = tz->debugfs; + struct thermal_debugfs *thermal_dbg; + struct tz_episode *tze, *tmp; + struct tz_debugfs *tz_dbg; + int *trips_crossed; - if (!thermal_dbg) + mutex_lock(&tz->lock); + + thermal_dbg = tz->debugfs; + if (!thermal_dbg) { + mutex_unlock(&tz->lock); return; + } + + tz->debugfs = NULL; + + mutex_unlock(&tz->lock); + + tz_dbg = &thermal_dbg->tz_dbg; mutex_lock(&thermal_dbg->lock); - tz->debugfs = NULL; + trips_crossed = tz_dbg->trips_crossed; + + list_for_each_entry_safe(tze, tmp, &tz_dbg->tz_episodes, node) { + list_del(&tze->node); + kfree(tze); + } mutex_unlock(&thermal_dbg->lock); thermal_debugfs_remove_id(thermal_dbg); + kfree(trips_crossed); } |