thermal_debugfs.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright 2023 Linaro Limited
  4. *
  5. * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
  6. *
  7. * Thermal subsystem debug support
  8. */
  9. #include <linux/debugfs.h>
  10. #include <linux/ktime.h>
  11. #include <linux/list.h>
  12. #include <linux/minmax.h>
  13. #include <linux/mutex.h>
  14. #include <linux/thermal.h>
  15. #include "thermal_core.h"
  16. static struct dentry *d_root;
  17. static struct dentry *d_cdev;
  18. static struct dentry *d_tz;
  19. /*
  20. * Length of the string containing the thermal zone id or the cooling
  21. * device id, including the ending nul character. We can reasonably
  22. * assume there won't be more than 256 thermal zones as the maximum
  23. * observed today is around 32.
  24. */
  25. #define IDSLENGTH 4
  26. /*
  27. * The cooling device transition list is stored in a hash table where
  28. * the size is CDEVSTATS_HASH_SIZE. The majority of cooling devices
  29. * have dozen of states but some can have much more, so a hash table
  30. * is more adequate in this case, because the cost of browsing the entire
  31. * list when storing the transitions may not be negligible.
  32. */
  33. #define CDEVSTATS_HASH_SIZE 16
  34. /**
  35. * struct cdev_debugfs - per cooling device statistics structure
  36. * A cooling device can have a high number of states. Showing the
  37. * transitions on a matrix based representation can be overkill given
  38. * most of the transitions won't happen and we end up with a matrix
  39. * filled with zero. Instead, we show the transitions which actually
  40. * happened.
  41. *
  42. * Every transition updates the current_state and the timestamp. The
  43. * transitions and the durations are stored in lists.
  44. *
  45. * @total: the number of transitions for this cooling device
  46. * @current_state: the current cooling device state
  47. * @timestamp: the state change timestamp
  48. * @transitions: an array of lists containing the state transitions
  49. * @durations: an array of lists containing the residencies of each state
  50. */
  51. struct cdev_debugfs {
  52. u32 total;
  53. int current_state;
  54. ktime_t timestamp;
  55. struct list_head transitions[CDEVSTATS_HASH_SIZE];
  56. struct list_head durations[CDEVSTATS_HASH_SIZE];
  57. };
  58. /**
  59. * struct cdev_record - Common structure for cooling device entry
  60. *
  61. * The following common structure allows to store the information
  62. * related to the transitions and to the state residencies. They are
  63. * identified with a id which is associated to a value. It is used as
  64. * nodes for the "transitions" and "durations" above.
  65. *
  66. * @node: node to insert the structure in a list
  67. * @id: identifier of the value which can be a state or a transition
  68. * @residency: a ktime_t representing a state residency duration
  69. * @count: a number of occurrences
  70. */
  71. struct cdev_record {
  72. struct list_head node;
  73. int id;
  74. union {
  75. ktime_t residency;
  76. u64 count;
  77. };
  78. };
  79. /**
  80. * struct trip_stats - Thermal trip statistics
  81. *
  82. * The trip_stats structure has the relevant information to show the
  83. * statistics related to temperature going above a trip point.
  84. *
  85. * @timestamp: the trip crossing timestamp
  86. * @duration: total time when the zone temperature was above the trip point
  87. * @trip_temp: trip temperature at mitigation start
  88. * @trip_hyst: trip hysteresis at mitigation start
  89. * @count: the number of times the zone temperature was above the trip point
  90. * @min: minimum recorded temperature above the trip point
  91. * @avg: average temperature above the trip point
  92. */
  93. struct trip_stats {
  94. ktime_t timestamp;
  95. ktime_t duration;
  96. int trip_temp;
  97. int trip_hyst;
  98. int count;
  99. int min;
  100. int avg;
  101. };
  102. /**
  103. * struct tz_episode - A mitigation episode information
  104. *
  105. * The tz_episode structure describes a mitigation episode. A
  106. * mitigation episode begins the trip point with the lower temperature
  107. * is crossed the way up and ends when it is crossed the way
  108. * down. During this episode we can have multiple trip points crossed
  109. * the way up and down if there are multiple trip described in the
  110. * firmware after the lowest temperature trip point.
  111. *
  112. * @timestamp: first trip point crossed the way up
  113. * @duration: total duration of the mitigation episode
  114. * @node: a list element to be added to the list of tz events
  115. * @max_temp: maximum zone temperature during this episode
  116. * @trip_stats: per trip point statistics, flexible array
  117. */
  118. struct tz_episode {
  119. ktime_t timestamp;
  120. ktime_t duration;
  121. struct list_head node;
  122. int max_temp;
  123. struct trip_stats trip_stats[];
  124. };
  125. /**
  126. * struct tz_debugfs - Store all mitigation episodes for a thermal zone
  127. *
  128. * The tz_debugfs structure contains the list of the mitigation
  129. * episodes and has to track which trip point has been crossed in
  130. * order to handle correctly nested trip point mitigation episodes.
  131. *
  132. * We keep the history of the trip point crossed in an array and as we
  133. * can go back and forth inside this history, eg. trip 0,1,2,1,2,1,0,
  134. * we keep track of the current position in the history array.
  135. *
  136. * @tz_episodes: a list of thermal mitigation episodes
  137. * @tz: thermal zone this object belongs to
  138. * @trips_crossed: an array of trip points crossed by id
  139. * @nr_trips: the number of trip points currently being crossed
  140. */
  141. struct tz_debugfs {
  142. struct list_head tz_episodes;
  143. struct thermal_zone_device *tz;
  144. int *trips_crossed;
  145. int nr_trips;
  146. };
  147. /**
  148. * struct thermal_debugfs - High level structure for a thermal object in debugfs
  149. *
  150. * The thermal_debugfs structure is the common structure used by the
  151. * cooling device or the thermal zone to store the statistics.
  152. *
  153. * @d_top: top directory of the thermal object directory
  154. * @lock: per object lock to protect the internals
  155. *
  156. * @cdev_dbg: a cooling device debug structure
  157. * @tz_dbg: a thermal zone debug structure
  158. */
  159. struct thermal_debugfs {
  160. struct dentry *d_top;
  161. struct mutex lock;
  162. union {
  163. struct cdev_debugfs cdev_dbg;
  164. struct tz_debugfs tz_dbg;
  165. };
  166. };
  167. void thermal_debug_init(void)
  168. {
  169. d_root = debugfs_create_dir("thermal", NULL);
  170. if (IS_ERR(d_root))
  171. return;
  172. d_cdev = debugfs_create_dir("cooling_devices", d_root);
  173. if (IS_ERR(d_cdev))
  174. return;
  175. d_tz = debugfs_create_dir("thermal_zones", d_root);
  176. }
  177. static struct thermal_debugfs *thermal_debugfs_add_id(struct dentry *d, int id)
  178. {
  179. struct thermal_debugfs *thermal_dbg;
  180. char ids[IDSLENGTH];
  181. thermal_dbg = kzalloc(sizeof(*thermal_dbg), GFP_KERNEL);
  182. if (!thermal_dbg)
  183. return NULL;
  184. mutex_init(&thermal_dbg->lock);
  185. snprintf(ids, IDSLENGTH, "%d", id);
  186. thermal_dbg->d_top = debugfs_create_dir(ids, d);
  187. if (IS_ERR(thermal_dbg->d_top)) {
  188. kfree(thermal_dbg);
  189. return NULL;
  190. }
  191. return thermal_dbg;
  192. }
  193. static void thermal_debugfs_remove_id(struct thermal_debugfs *thermal_dbg)
  194. {
  195. if (!thermal_dbg)
  196. return;
  197. debugfs_remove(thermal_dbg->d_top);
  198. kfree(thermal_dbg);
  199. }
  200. static struct cdev_record *
  201. thermal_debugfs_cdev_record_alloc(struct thermal_debugfs *thermal_dbg,
  202. struct list_head *lists, int id)
  203. {
  204. struct cdev_record *cdev_record;
  205. cdev_record = kzalloc(sizeof(*cdev_record), GFP_KERNEL);
  206. if (!cdev_record)
  207. return NULL;
  208. cdev_record->id = id;
  209. INIT_LIST_HEAD(&cdev_record->node);
  210. list_add_tail(&cdev_record->node,
  211. &lists[cdev_record->id % CDEVSTATS_HASH_SIZE]);
  212. return cdev_record;
  213. }
  214. static struct cdev_record *
  215. thermal_debugfs_cdev_record_find(struct thermal_debugfs *thermal_dbg,
  216. struct list_head *lists, int id)
  217. {
  218. struct cdev_record *entry;
  219. list_for_each_entry(entry, &lists[id % CDEVSTATS_HASH_SIZE], node)
  220. if (entry->id == id)
  221. return entry;
  222. return NULL;
  223. }
  224. static struct cdev_record *
  225. thermal_debugfs_cdev_record_get(struct thermal_debugfs *thermal_dbg,
  226. struct list_head *lists, int id)
  227. {
  228. struct cdev_record *cdev_record;
  229. cdev_record = thermal_debugfs_cdev_record_find(thermal_dbg, lists, id);
  230. if (cdev_record)
  231. return cdev_record;
  232. return thermal_debugfs_cdev_record_alloc(thermal_dbg, lists, id);
  233. }
  234. static void thermal_debugfs_cdev_clear(struct cdev_debugfs *cdev_dbg)
  235. {
  236. int i;
  237. struct cdev_record *entry, *tmp;
  238. for (i = 0; i < CDEVSTATS_HASH_SIZE; i++) {
  239. list_for_each_entry_safe(entry, tmp,
  240. &cdev_dbg->transitions[i], node) {
  241. list_del(&entry->node);
  242. kfree(entry);
  243. }
  244. list_for_each_entry_safe(entry, tmp,
  245. &cdev_dbg->durations[i], node) {
  246. list_del(&entry->node);
  247. kfree(entry);
  248. }
  249. }
  250. cdev_dbg->total = 0;
  251. }
  252. static void *cdev_seq_start(struct seq_file *s, loff_t *pos)
  253. {
  254. struct thermal_debugfs *thermal_dbg = s->private;
  255. mutex_lock(&thermal_dbg->lock);
  256. return (*pos < CDEVSTATS_HASH_SIZE) ? pos : NULL;
  257. }
  258. static void *cdev_seq_next(struct seq_file *s, void *v, loff_t *pos)
  259. {
  260. (*pos)++;
  261. return (*pos < CDEVSTATS_HASH_SIZE) ? pos : NULL;
  262. }
  263. static void cdev_seq_stop(struct seq_file *s, void *v)
  264. {
  265. struct thermal_debugfs *thermal_dbg = s->private;
  266. mutex_unlock(&thermal_dbg->lock);
  267. }
  268. static int cdev_tt_seq_show(struct seq_file *s, void *v)
  269. {
  270. struct thermal_debugfs *thermal_dbg = s->private;
  271. struct cdev_debugfs *cdev_dbg = &thermal_dbg->cdev_dbg;
  272. struct list_head *transitions = cdev_dbg->transitions;
  273. struct cdev_record *entry;
  274. int i = *(loff_t *)v;
  275. if (!i)
  276. seq_puts(s, "Transition\tOccurences\n");
  277. list_for_each_entry(entry, &transitions[i], node) {
  278. /*
  279. * Assuming maximum cdev states is 1024, the longer
  280. * string for a transition would be "1024->1024\0"
  281. */
  282. char buffer[11];
  283. snprintf(buffer, ARRAY_SIZE(buffer), "%d->%d",
  284. entry->id >> 16, entry->id & 0xFFFF);
  285. seq_printf(s, "%-10s\t%-10llu\n", buffer, entry->count);
  286. }
  287. return 0;
  288. }
  289. static const struct seq_operations tt_sops = {
  290. .start = cdev_seq_start,
  291. .next = cdev_seq_next,
  292. .stop = cdev_seq_stop,
  293. .show = cdev_tt_seq_show,
  294. };
  295. DEFINE_SEQ_ATTRIBUTE(tt);
  296. static int cdev_dt_seq_show(struct seq_file *s, void *v)
  297. {
  298. struct thermal_debugfs *thermal_dbg = s->private;
  299. struct cdev_debugfs *cdev_dbg = &thermal_dbg->cdev_dbg;
  300. struct list_head *durations = cdev_dbg->durations;
  301. struct cdev_record *entry;
  302. int i = *(loff_t *)v;
  303. if (!i)
  304. seq_puts(s, "State\tResidency\n");
  305. list_for_each_entry(entry, &durations[i], node) {
  306. s64 duration = ktime_to_ms(entry->residency);
  307. if (entry->id == cdev_dbg->current_state)
  308. duration += ktime_ms_delta(ktime_get(),
  309. cdev_dbg->timestamp);
  310. seq_printf(s, "%-5d\t%-10llu\n", entry->id, duration);
  311. }
  312. return 0;
  313. }
  314. static const struct seq_operations dt_sops = {
  315. .start = cdev_seq_start,
  316. .next = cdev_seq_next,
  317. .stop = cdev_seq_stop,
  318. .show = cdev_dt_seq_show,
  319. };
  320. DEFINE_SEQ_ATTRIBUTE(dt);
  321. static int cdev_clear_set(void *data, u64 val)
  322. {
  323. struct thermal_debugfs *thermal_dbg = data;
  324. if (!val)
  325. return -EINVAL;
  326. mutex_lock(&thermal_dbg->lock);
  327. thermal_debugfs_cdev_clear(&thermal_dbg->cdev_dbg);
  328. mutex_unlock(&thermal_dbg->lock);
  329. return 0;
  330. }
  331. DEFINE_DEBUGFS_ATTRIBUTE(cdev_clear_fops, NULL, cdev_clear_set, "%llu\n");
  332. /**
  333. * thermal_debug_cdev_state_update - Update a cooling device state change
  334. *
  335. * Computes a transition and the duration of the previous state residency.
  336. *
  337. * @cdev : a pointer to a cooling device
  338. * @new_state: an integer corresponding to the new cooling device state
  339. */
  340. void thermal_debug_cdev_state_update(const struct thermal_cooling_device *cdev,
  341. int new_state)
  342. {
  343. struct thermal_debugfs *thermal_dbg = cdev->debugfs;
  344. struct cdev_debugfs *cdev_dbg;
  345. struct cdev_record *cdev_record;
  346. int transition, old_state;
  347. if (!thermal_dbg || (thermal_dbg->cdev_dbg.current_state == new_state))
  348. return;
  349. mutex_lock(&thermal_dbg->lock);
  350. cdev_dbg = &thermal_dbg->cdev_dbg;
  351. old_state = cdev_dbg->current_state;
  352. /*
  353. * Get the old state information in the durations list. If
  354. * this one does not exist, a new allocated one will be
  355. * returned. Recompute the total duration in the old state and
  356. * get a new timestamp for the new state.
  357. */
  358. cdev_record = thermal_debugfs_cdev_record_get(thermal_dbg,
  359. cdev_dbg->durations,
  360. old_state);
  361. if (cdev_record) {
  362. ktime_t now = ktime_get();
  363. ktime_t delta = ktime_sub(now, cdev_dbg->timestamp);
  364. cdev_record->residency = ktime_add(cdev_record->residency, delta);
  365. cdev_dbg->timestamp = now;
  366. }
  367. cdev_dbg->current_state = new_state;
  368. /*
  369. * Create a record for the new state if it is not there, so its
  370. * duration will be printed by cdev_dt_seq_show() as expected if it
  371. * runs before the next state transition.
  372. */
  373. thermal_debugfs_cdev_record_get(thermal_dbg, cdev_dbg->durations, new_state);
  374. transition = (old_state << 16) | new_state;
  375. /*
  376. * Get the transition in the transitions list. If this one
  377. * does not exist, a new allocated one will be returned.
  378. * Increment the occurrence of this transition which is stored
  379. * in the value field.
  380. */
  381. cdev_record = thermal_debugfs_cdev_record_get(thermal_dbg,
  382. cdev_dbg->transitions,
  383. transition);
  384. if (cdev_record)
  385. cdev_record->count++;
  386. cdev_dbg->total++;
  387. mutex_unlock(&thermal_dbg->lock);
  388. }
  389. /**
  390. * thermal_debug_cdev_add - Add a cooling device debugfs entry
  391. *
  392. * Allocates a cooling device object for debug, initializes the
  393. * statistics and create the entries in sysfs.
  394. * @cdev: a pointer to a cooling device
  395. * @state: current state of the cooling device
  396. */
  397. void thermal_debug_cdev_add(struct thermal_cooling_device *cdev, int state)
  398. {
  399. struct thermal_debugfs *thermal_dbg;
  400. struct cdev_debugfs *cdev_dbg;
  401. int i;
  402. thermal_dbg = thermal_debugfs_add_id(d_cdev, cdev->id);
  403. if (!thermal_dbg)
  404. return;
  405. cdev_dbg = &thermal_dbg->cdev_dbg;
  406. for (i = 0; i < CDEVSTATS_HASH_SIZE; i++) {
  407. INIT_LIST_HEAD(&cdev_dbg->transitions[i]);
  408. INIT_LIST_HEAD(&cdev_dbg->durations[i]);
  409. }
  410. cdev_dbg->current_state = state;
  411. cdev_dbg->timestamp = ktime_get();
  412. /*
  413. * Create a record for the initial cooling device state, so its
  414. * duration will be printed by cdev_dt_seq_show() as expected if it
  415. * runs before the first state transition.
  416. */
  417. thermal_debugfs_cdev_record_get(thermal_dbg, cdev_dbg->durations, state);
  418. debugfs_create_file("trans_table", 0400, thermal_dbg->d_top,
  419. thermal_dbg, &tt_fops);
  420. debugfs_create_file("time_in_state_ms", 0400, thermal_dbg->d_top,
  421. thermal_dbg, &dt_fops);
  422. debugfs_create_file("clear", 0200, thermal_dbg->d_top,
  423. thermal_dbg, &cdev_clear_fops);
  424. debugfs_create_u32("total_trans", 0400, thermal_dbg->d_top,
  425. &cdev_dbg->total);
  426. cdev->debugfs = thermal_dbg;
  427. }
  428. /**
  429. * thermal_debug_cdev_remove - Remove a cooling device debugfs entry
  430. *
  431. * Frees the statistics memory data and remove the debugfs entry
  432. *
  433. * @cdev: a pointer to a cooling device
  434. */
  435. void thermal_debug_cdev_remove(struct thermal_cooling_device *cdev)
  436. {
  437. struct thermal_debugfs *thermal_dbg;
  438. mutex_lock(&cdev->lock);
  439. thermal_dbg = cdev->debugfs;
  440. if (!thermal_dbg) {
  441. mutex_unlock(&cdev->lock);
  442. return;
  443. }
  444. cdev->debugfs = NULL;
  445. mutex_unlock(&cdev->lock);
  446. mutex_lock(&thermal_dbg->lock);
  447. thermal_debugfs_cdev_clear(&thermal_dbg->cdev_dbg);
  448. mutex_unlock(&thermal_dbg->lock);
  449. thermal_debugfs_remove_id(thermal_dbg);
  450. }
  451. static struct tz_episode *thermal_debugfs_tz_event_alloc(struct thermal_zone_device *tz,
  452. ktime_t now)
  453. {
  454. struct tz_episode *tze;
  455. int i;
  456. tze = kzalloc(struct_size(tze, trip_stats, tz->num_trips), GFP_KERNEL);
  457. if (!tze)
  458. return NULL;
  459. INIT_LIST_HEAD(&tze->node);
  460. tze->timestamp = now;
  461. tze->duration = KTIME_MIN;
  462. tze->max_temp = INT_MIN;
  463. for (i = 0; i < tz->num_trips; i++) {
  464. tze->trip_stats[i].trip_temp = THERMAL_TEMP_INVALID;
  465. tze->trip_stats[i].min = INT_MAX;
  466. }
  467. return tze;
  468. }
  469. void thermal_debug_tz_trip_up(struct thermal_zone_device *tz,
  470. const struct thermal_trip *trip)
  471. {
  472. struct thermal_debugfs *thermal_dbg = tz->debugfs;
  473. int trip_id = thermal_zone_trip_id(tz, trip);
  474. ktime_t now = ktime_get();
  475. struct trip_stats *trip_stats;
  476. struct tz_debugfs *tz_dbg;
  477. struct tz_episode *tze;
  478. if (!thermal_dbg)
  479. return;
  480. tz_dbg = &thermal_dbg->tz_dbg;
  481. mutex_lock(&thermal_dbg->lock);
  482. /*
  483. * The mitigation is starting. A mitigation can contain
  484. * several episodes where each of them is related to a
  485. * temperature crossing a trip point. The episodes are
  486. * nested. That means when the temperature is crossing the
  487. * first trip point, the duration begins to be measured. If
  488. * the temperature continues to increase and reaches the
  489. * second trip point, the duration of the first trip must be
  490. * also accumulated.
  491. *
  492. * eg.
  493. *
  494. * temp
  495. * ^
  496. * | --------
  497. * trip 2 / \ ------
  498. * | /| |\ /| |\
  499. * trip 1 / | | `---- | | \
  500. * | /| | | | | |\
  501. * trip 0 / | | | | | | \
  502. * | /| | | | | | | |\
  503. * | / | | | | | | | | `--
  504. * | / | | | | | | | |
  505. * |----- | | | | | | | |
  506. * | | | | | | | | |
  507. * --------|-|-|--------|--------|------|-|-|------------------> time
  508. * | | |<--t2-->| |<-t2'>| | |
  509. * | | | |
  510. * | |<------------t1------------>| |
  511. * | |
  512. * |<-------------t0--------------->|
  513. *
  514. */
  515. if (!tz_dbg->nr_trips) {
  516. tze = thermal_debugfs_tz_event_alloc(tz, now);
  517. if (!tze)
  518. goto unlock;
  519. list_add(&tze->node, &tz_dbg->tz_episodes);
  520. }
  521. /*
  522. * Each time a trip point is crossed the way up, the trip_id
  523. * is stored in the trip_crossed array and the nr_trips is
  524. * incremented. A nr_trips equal to zero means we are entering
  525. * a mitigation episode.
  526. *
  527. * The trip ids may not be in the ascending order but the
  528. * result in the array trips_crossed will be in the ascending
  529. * temperature order. The function detecting when a trip point
  530. * is crossed the way down will handle the very rare case when
  531. * the trip points may have been reordered during this
  532. * mitigation episode.
  533. */
  534. tz_dbg->trips_crossed[tz_dbg->nr_trips++] = trip_id;
  535. tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node);
  536. trip_stats = &tze->trip_stats[trip_id];
  537. trip_stats->trip_temp = trip->temperature;
  538. trip_stats->trip_hyst = trip->hysteresis;
  539. trip_stats->timestamp = now;
  540. unlock:
  541. mutex_unlock(&thermal_dbg->lock);
  542. }
  543. static void tz_episode_close_trip(struct tz_episode *tze, int trip_id, ktime_t now)
  544. {
  545. struct trip_stats *trip_stats = &tze->trip_stats[trip_id];
  546. ktime_t delta = ktime_sub(now, trip_stats->timestamp);
  547. trip_stats->duration = ktime_add(delta, trip_stats->duration);
  548. /* Mark the end of mitigation for this trip point. */
  549. trip_stats->timestamp = KTIME_MAX;
  550. }
  551. void thermal_debug_tz_trip_down(struct thermal_zone_device *tz,
  552. const struct thermal_trip *trip)
  553. {
  554. struct thermal_debugfs *thermal_dbg = tz->debugfs;
  555. int trip_id = thermal_zone_trip_id(tz, trip);
  556. ktime_t now = ktime_get();
  557. struct tz_episode *tze;
  558. struct tz_debugfs *tz_dbg;
  559. int i;
  560. if (!thermal_dbg)
  561. return;
  562. tz_dbg = &thermal_dbg->tz_dbg;
  563. mutex_lock(&thermal_dbg->lock);
  564. /*
  565. * The temperature crosses the way down but there was not
  566. * mitigation detected before. That may happen when the
  567. * temperature is greater than a trip point when registering a
  568. * thermal zone, which is a common use case as the kernel has
  569. * no mitigation mechanism yet at boot time.
  570. */
  571. if (!tz_dbg->nr_trips)
  572. goto out;
  573. for (i = tz_dbg->nr_trips - 1; i >= 0; i--) {
  574. if (tz_dbg->trips_crossed[i] == trip_id)
  575. break;
  576. }
  577. if (i < 0)
  578. goto out;
  579. tz_dbg->nr_trips--;
  580. if (i < tz_dbg->nr_trips)
  581. tz_dbg->trips_crossed[i] = tz_dbg->trips_crossed[tz_dbg->nr_trips];
  582. tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node);
  583. tz_episode_close_trip(tze, trip_id, now);
  584. /*
  585. * This event closes the mitigation as we are crossing the
  586. * last trip point the way down.
  587. */
  588. if (!tz_dbg->nr_trips)
  589. tze->duration = ktime_sub(now, tze->timestamp);
  590. out:
  591. mutex_unlock(&thermal_dbg->lock);
  592. }
  593. void thermal_debug_update_trip_stats(struct thermal_zone_device *tz)
  594. {
  595. struct thermal_debugfs *thermal_dbg = tz->debugfs;
  596. struct tz_debugfs *tz_dbg;
  597. struct tz_episode *tze;
  598. int i;
  599. if (!thermal_dbg)
  600. return;
  601. tz_dbg = &thermal_dbg->tz_dbg;
  602. mutex_lock(&thermal_dbg->lock);
  603. if (!tz_dbg->nr_trips)
  604. goto out;
  605. tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node);
  606. if (tz->temperature > tze->max_temp)
  607. tze->max_temp = tz->temperature;
  608. for (i = 0; i < tz_dbg->nr_trips; i++) {
  609. int trip_id = tz_dbg->trips_crossed[i];
  610. struct trip_stats *trip_stats = &tze->trip_stats[trip_id];
  611. trip_stats->min = min(trip_stats->min, tz->temperature);
  612. trip_stats->avg += (tz->temperature - trip_stats->avg) /
  613. ++trip_stats->count;
  614. }
  615. out:
  616. mutex_unlock(&thermal_dbg->lock);
  617. }
  618. static void *tze_seq_start(struct seq_file *s, loff_t *pos)
  619. {
  620. struct thermal_debugfs *thermal_dbg = s->private;
  621. struct tz_debugfs *tz_dbg = &thermal_dbg->tz_dbg;
  622. mutex_lock(&thermal_dbg->lock);
  623. return seq_list_start(&tz_dbg->tz_episodes, *pos);
  624. }
  625. static void *tze_seq_next(struct seq_file *s, void *v, loff_t *pos)
  626. {
  627. struct thermal_debugfs *thermal_dbg = s->private;
  628. struct tz_debugfs *tz_dbg = &thermal_dbg->tz_dbg;
  629. return seq_list_next(v, &tz_dbg->tz_episodes, pos);
  630. }
  631. static void tze_seq_stop(struct seq_file *s, void *v)
  632. {
  633. struct thermal_debugfs *thermal_dbg = s->private;
  634. mutex_unlock(&thermal_dbg->lock);
  635. }
  636. static int tze_seq_show(struct seq_file *s, void *v)
  637. {
  638. struct thermal_debugfs *thermal_dbg = s->private;
  639. struct thermal_zone_device *tz = thermal_dbg->tz_dbg.tz;
  640. struct thermal_trip_desc *td;
  641. struct tz_episode *tze;
  642. u64 duration_ms;
  643. int trip_id;
  644. char c;
  645. tze = list_entry((struct list_head *)v, struct tz_episode, node);
  646. if (tze->duration == KTIME_MIN) {
  647. /* Mitigation in progress. */
  648. duration_ms = ktime_to_ms(ktime_sub(ktime_get(), tze->timestamp));
  649. c = '>';
  650. } else {
  651. duration_ms = ktime_to_ms(tze->duration);
  652. c = '=';
  653. }
  654. seq_printf(s, ",-Mitigation at %llums, duration%c%llums, max. temp=%dm°C\n",
  655. ktime_to_ms(tze->timestamp), c, duration_ms, tze->max_temp);
  656. seq_printf(s, "| trip | type | temp(m°C) | hyst(m°C) | duration(ms) | avg(m°C) | min(m°C) |\n");
  657. for_each_trip_desc(tz, td) {
  658. const struct thermal_trip *trip = &td->trip;
  659. struct trip_stats *trip_stats;
  660. /*
  661. * There is no possible mitigation happening at the
  662. * critical trip point, so the stats will be always
  663. * zero, skip this trip point
  664. */
  665. if (trip->type == THERMAL_TRIP_CRITICAL)
  666. continue;
  667. trip_id = thermal_zone_trip_id(tz, trip);
  668. trip_stats = &tze->trip_stats[trip_id];
  669. /* Skip trips without any stats. */
  670. if (trip_stats->trip_temp == THERMAL_TEMP_INVALID)
  671. continue;
  672. if (trip_stats->timestamp != KTIME_MAX) {
  673. /* Mitigation in progress. */
  674. ktime_t delta = ktime_sub(ktime_get(),
  675. trip_stats->timestamp);
  676. delta = ktime_add(delta, trip_stats->duration);
  677. duration_ms = ktime_to_ms(delta);
  678. c = '>';
  679. } else {
  680. duration_ms = ktime_to_ms(trip_stats->duration);
  681. c = ' ';
  682. }
  683. seq_printf(s, "| %*d | %*s | %*d | %*d | %c%*lld | %*d | %*d |\n",
  684. 4 , trip_id,
  685. 8, thermal_trip_type_name(trip->type),
  686. 9, trip_stats->trip_temp,
  687. 9, trip_stats->trip_hyst,
  688. c, 11, duration_ms,
  689. 9, trip_stats->avg,
  690. 9, trip_stats->min);
  691. }
  692. return 0;
  693. }
  694. static const struct seq_operations tze_sops = {
  695. .start = tze_seq_start,
  696. .next = tze_seq_next,
  697. .stop = tze_seq_stop,
  698. .show = tze_seq_show,
  699. };
  700. DEFINE_SEQ_ATTRIBUTE(tze);
  701. void thermal_debug_tz_add(struct thermal_zone_device *tz)
  702. {
  703. struct thermal_debugfs *thermal_dbg;
  704. struct tz_debugfs *tz_dbg;
  705. thermal_dbg = thermal_debugfs_add_id(d_tz, tz->id);
  706. if (!thermal_dbg)
  707. return;
  708. tz_dbg = &thermal_dbg->tz_dbg;
  709. tz_dbg->tz = tz;
  710. tz_dbg->trips_crossed = kzalloc(sizeof(int) * tz->num_trips, GFP_KERNEL);
  711. if (!tz_dbg->trips_crossed) {
  712. thermal_debugfs_remove_id(thermal_dbg);
  713. return;
  714. }
  715. INIT_LIST_HEAD(&tz_dbg->tz_episodes);
  716. debugfs_create_file("mitigations", 0400, thermal_dbg->d_top,
  717. thermal_dbg, &tze_fops);
  718. tz->debugfs = thermal_dbg;
  719. }
  720. void thermal_debug_tz_remove(struct thermal_zone_device *tz)
  721. {
  722. struct thermal_debugfs *thermal_dbg;
  723. struct tz_episode *tze, *tmp;
  724. struct tz_debugfs *tz_dbg;
  725. int *trips_crossed;
  726. mutex_lock(&tz->lock);
  727. thermal_dbg = tz->debugfs;
  728. if (!thermal_dbg) {
  729. mutex_unlock(&tz->lock);
  730. return;
  731. }
  732. tz->debugfs = NULL;
  733. mutex_unlock(&tz->lock);
  734. tz_dbg = &thermal_dbg->tz_dbg;
  735. mutex_lock(&thermal_dbg->lock);
  736. trips_crossed = tz_dbg->trips_crossed;
  737. list_for_each_entry_safe(tze, tmp, &tz_dbg->tz_episodes, node) {
  738. list_del(&tze->node);
  739. kfree(tze);
  740. }
  741. mutex_unlock(&thermal_dbg->lock);
  742. thermal_debugfs_remove_id(thermal_dbg);
  743. kfree(trips_crossed);
  744. }
  745. void thermal_debug_tz_resume(struct thermal_zone_device *tz)
  746. {
  747. struct thermal_debugfs *thermal_dbg = tz->debugfs;
  748. ktime_t now = ktime_get();
  749. struct tz_debugfs *tz_dbg;
  750. struct tz_episode *tze;
  751. int i;
  752. if (!thermal_dbg)
  753. return;
  754. mutex_lock(&thermal_dbg->lock);
  755. tz_dbg = &thermal_dbg->tz_dbg;
  756. if (!tz_dbg->nr_trips)
  757. goto out;
  758. /*
  759. * A mitigation episode was in progress before the preceding system
  760. * suspend transition, so close it because the zone handling is starting
  761. * over from scratch.
  762. */
  763. tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node);
  764. for (i = 0; i < tz_dbg->nr_trips; i++)
  765. tz_episode_close_trip(tze, tz_dbg->trips_crossed[i], now);
  766. tze->duration = ktime_sub(now, tze->timestamp);
  767. tz_dbg->nr_trips = 0;
  768. out:
  769. mutex_unlock(&thermal_dbg->lock);
  770. }