thermal_debugfs.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968
  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_obj(*thermal_dbg);
  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_obj(*cdev_record);
  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\tOccurrences\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. static struct thermal_debugfs *thermal_debug_cdev_clear(struct thermal_cooling_device *cdev)
  429. {
  430. struct thermal_debugfs *thermal_dbg;
  431. guard(cooling_dev)(cdev);
  432. thermal_dbg = cdev->debugfs;
  433. if (thermal_dbg)
  434. cdev->debugfs = NULL;
  435. return thermal_dbg;
  436. }
  437. /**
  438. * thermal_debug_cdev_remove - Remove a cooling device debugfs entry
  439. *
  440. * Frees the statistics memory data and remove the debugfs entry
  441. *
  442. * @cdev: a pointer to a cooling device
  443. */
  444. void thermal_debug_cdev_remove(struct thermal_cooling_device *cdev)
  445. {
  446. struct thermal_debugfs *thermal_dbg;
  447. thermal_dbg = thermal_debug_cdev_clear(cdev);
  448. if (!thermal_dbg)
  449. return;
  450. mutex_lock(&thermal_dbg->lock);
  451. thermal_debugfs_cdev_clear(&thermal_dbg->cdev_dbg);
  452. mutex_unlock(&thermal_dbg->lock);
  453. thermal_debugfs_remove_id(thermal_dbg);
  454. }
  455. static struct tz_episode *thermal_debugfs_tz_event_alloc(struct thermal_zone_device *tz,
  456. ktime_t now)
  457. {
  458. struct tz_episode *tze;
  459. int i;
  460. tze = kzalloc_flex(*tze, trip_stats, tz->num_trips);
  461. if (!tze)
  462. return NULL;
  463. INIT_LIST_HEAD(&tze->node);
  464. tze->timestamp = now;
  465. tze->duration = KTIME_MIN;
  466. tze->max_temp = INT_MIN;
  467. for (i = 0; i < tz->num_trips; i++) {
  468. tze->trip_stats[i].trip_temp = THERMAL_TEMP_INVALID;
  469. tze->trip_stats[i].min = INT_MAX;
  470. }
  471. return tze;
  472. }
  473. void thermal_debug_tz_trip_up(struct thermal_zone_device *tz,
  474. const struct thermal_trip *trip)
  475. {
  476. struct thermal_debugfs *thermal_dbg = tz->debugfs;
  477. int trip_id = thermal_zone_trip_id(tz, trip);
  478. ktime_t now = ktime_get();
  479. struct trip_stats *trip_stats;
  480. struct tz_debugfs *tz_dbg;
  481. struct tz_episode *tze;
  482. if (!thermal_dbg)
  483. return;
  484. tz_dbg = &thermal_dbg->tz_dbg;
  485. mutex_lock(&thermal_dbg->lock);
  486. /*
  487. * The mitigation is starting. A mitigation can contain
  488. * several episodes where each of them is related to a
  489. * temperature crossing a trip point. The episodes are
  490. * nested. That means when the temperature is crossing the
  491. * first trip point, the duration begins to be measured. If
  492. * the temperature continues to increase and reaches the
  493. * second trip point, the duration of the first trip must be
  494. * also accumulated.
  495. *
  496. * eg.
  497. *
  498. * temp
  499. * ^
  500. * | --------
  501. * trip 2 / \ ------
  502. * | /| |\ /| |\
  503. * trip 1 / | | `---- | | \
  504. * | /| | | | | |\
  505. * trip 0 / | | | | | | \
  506. * | /| | | | | | | |\
  507. * | / | | | | | | | | `--
  508. * | / | | | | | | | |
  509. * |----- | | | | | | | |
  510. * | | | | | | | | |
  511. * --------|-|-|--------|--------|------|-|-|------------------> time
  512. * | | |<--t2-->| |<-t2'>| | |
  513. * | | | |
  514. * | |<------------t1------------>| |
  515. * | |
  516. * |<-------------t0--------------->|
  517. *
  518. */
  519. if (!tz_dbg->nr_trips) {
  520. tze = thermal_debugfs_tz_event_alloc(tz, now);
  521. if (!tze)
  522. goto unlock;
  523. list_add(&tze->node, &tz_dbg->tz_episodes);
  524. }
  525. /*
  526. * Each time a trip point is crossed the way up, the trip_id
  527. * is stored in the trip_crossed array and the nr_trips is
  528. * incremented. A nr_trips equal to zero means we are entering
  529. * a mitigation episode.
  530. *
  531. * The trip ids may not be in the ascending order but the
  532. * result in the array trips_crossed will be in the ascending
  533. * temperature order. The function detecting when a trip point
  534. * is crossed the way down will handle the very rare case when
  535. * the trip points may have been reordered during this
  536. * mitigation episode.
  537. */
  538. tz_dbg->trips_crossed[tz_dbg->nr_trips++] = trip_id;
  539. tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node);
  540. trip_stats = &tze->trip_stats[trip_id];
  541. trip_stats->trip_temp = trip->temperature;
  542. trip_stats->trip_hyst = trip->hysteresis;
  543. trip_stats->timestamp = now;
  544. unlock:
  545. mutex_unlock(&thermal_dbg->lock);
  546. }
  547. static void tz_episode_close_trip(struct tz_episode *tze, int trip_id, ktime_t now)
  548. {
  549. struct trip_stats *trip_stats = &tze->trip_stats[trip_id];
  550. ktime_t delta = ktime_sub(now, trip_stats->timestamp);
  551. trip_stats->duration = ktime_add(delta, trip_stats->duration);
  552. /* Mark the end of mitigation for this trip point. */
  553. trip_stats->timestamp = KTIME_MAX;
  554. }
  555. void thermal_debug_tz_trip_down(struct thermal_zone_device *tz,
  556. const struct thermal_trip *trip)
  557. {
  558. struct thermal_debugfs *thermal_dbg = tz->debugfs;
  559. int trip_id = thermal_zone_trip_id(tz, trip);
  560. ktime_t now = ktime_get();
  561. struct tz_episode *tze;
  562. struct tz_debugfs *tz_dbg;
  563. int i;
  564. if (!thermal_dbg)
  565. return;
  566. tz_dbg = &thermal_dbg->tz_dbg;
  567. mutex_lock(&thermal_dbg->lock);
  568. /*
  569. * The temperature crosses the way down but there was not
  570. * mitigation detected before. That may happen when the
  571. * temperature is greater than a trip point when registering a
  572. * thermal zone, which is a common use case as the kernel has
  573. * no mitigation mechanism yet at boot time.
  574. */
  575. if (!tz_dbg->nr_trips)
  576. goto out;
  577. for (i = tz_dbg->nr_trips - 1; i >= 0; i--) {
  578. if (tz_dbg->trips_crossed[i] == trip_id)
  579. break;
  580. }
  581. if (i < 0)
  582. goto out;
  583. tz_dbg->nr_trips--;
  584. if (i < tz_dbg->nr_trips)
  585. tz_dbg->trips_crossed[i] = tz_dbg->trips_crossed[tz_dbg->nr_trips];
  586. tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node);
  587. tz_episode_close_trip(tze, trip_id, now);
  588. /*
  589. * This event closes the mitigation as we are crossing the
  590. * last trip point the way down.
  591. */
  592. if (!tz_dbg->nr_trips)
  593. tze->duration = ktime_sub(now, tze->timestamp);
  594. out:
  595. mutex_unlock(&thermal_dbg->lock);
  596. }
  597. void thermal_debug_update_trip_stats(struct thermal_zone_device *tz)
  598. {
  599. struct thermal_debugfs *thermal_dbg = tz->debugfs;
  600. struct tz_debugfs *tz_dbg;
  601. struct tz_episode *tze;
  602. int i;
  603. if (!thermal_dbg)
  604. return;
  605. tz_dbg = &thermal_dbg->tz_dbg;
  606. mutex_lock(&thermal_dbg->lock);
  607. if (!tz_dbg->nr_trips)
  608. goto out;
  609. tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node);
  610. if (tz->temperature > tze->max_temp)
  611. tze->max_temp = tz->temperature;
  612. for (i = 0; i < tz_dbg->nr_trips; i++) {
  613. int trip_id = tz_dbg->trips_crossed[i];
  614. struct trip_stats *trip_stats = &tze->trip_stats[trip_id];
  615. trip_stats->min = min(trip_stats->min, tz->temperature);
  616. trip_stats->avg += (tz->temperature - trip_stats->avg) /
  617. ++trip_stats->count;
  618. }
  619. out:
  620. mutex_unlock(&thermal_dbg->lock);
  621. }
  622. static void *tze_seq_start(struct seq_file *s, loff_t *pos)
  623. {
  624. struct thermal_debugfs *thermal_dbg = s->private;
  625. struct tz_debugfs *tz_dbg = &thermal_dbg->tz_dbg;
  626. mutex_lock(&thermal_dbg->lock);
  627. return seq_list_start(&tz_dbg->tz_episodes, *pos);
  628. }
  629. static void *tze_seq_next(struct seq_file *s, void *v, loff_t *pos)
  630. {
  631. struct thermal_debugfs *thermal_dbg = s->private;
  632. struct tz_debugfs *tz_dbg = &thermal_dbg->tz_dbg;
  633. return seq_list_next(v, &tz_dbg->tz_episodes, pos);
  634. }
  635. static void tze_seq_stop(struct seq_file *s, void *v)
  636. {
  637. struct thermal_debugfs *thermal_dbg = s->private;
  638. mutex_unlock(&thermal_dbg->lock);
  639. }
  640. static int tze_seq_show(struct seq_file *s, void *v)
  641. {
  642. struct thermal_debugfs *thermal_dbg = s->private;
  643. struct thermal_zone_device *tz = thermal_dbg->tz_dbg.tz;
  644. struct thermal_trip_desc *td;
  645. struct tz_episode *tze;
  646. u64 duration_ms;
  647. int trip_id;
  648. char c;
  649. tze = list_entry((struct list_head *)v, struct tz_episode, node);
  650. if (tze->duration == KTIME_MIN) {
  651. /* Mitigation in progress. */
  652. duration_ms = ktime_to_ms(ktime_sub(ktime_get(), tze->timestamp));
  653. c = '>';
  654. } else {
  655. duration_ms = ktime_to_ms(tze->duration);
  656. c = '=';
  657. }
  658. seq_printf(s, ",-Mitigation at %llums, duration%c%llums, max. temp=%dm°C\n",
  659. ktime_to_ms(tze->timestamp), c, duration_ms, tze->max_temp);
  660. seq_puts(s, "| trip | type | temp(m°C) | hyst(m°C) | duration(ms) | avg(m°C) | min(m°C) |\n");
  661. for_each_trip_desc(tz, td) {
  662. const struct thermal_trip *trip = &td->trip;
  663. struct trip_stats *trip_stats;
  664. /*
  665. * There is no possible mitigation happening at the
  666. * critical trip point, so the stats will be always
  667. * zero, skip this trip point
  668. */
  669. if (trip->type == THERMAL_TRIP_CRITICAL)
  670. continue;
  671. trip_id = thermal_zone_trip_id(tz, trip);
  672. trip_stats = &tze->trip_stats[trip_id];
  673. /* Skip trips without any stats. */
  674. if (trip_stats->trip_temp == THERMAL_TEMP_INVALID)
  675. continue;
  676. if (trip_stats->timestamp != KTIME_MAX) {
  677. /* Mitigation in progress. */
  678. ktime_t delta = ktime_sub(ktime_get(),
  679. trip_stats->timestamp);
  680. delta = ktime_add(delta, trip_stats->duration);
  681. duration_ms = ktime_to_ms(delta);
  682. c = '>';
  683. } else {
  684. duration_ms = ktime_to_ms(trip_stats->duration);
  685. c = ' ';
  686. }
  687. seq_printf(s, "| %*d | %*s | %*d | %*d | %c%*lld | %*d | %*d |\n",
  688. 4 , trip_id,
  689. 8, thermal_trip_type_name(trip->type),
  690. 9, trip_stats->trip_temp,
  691. 9, trip_stats->trip_hyst,
  692. c, 11, duration_ms,
  693. 9, trip_stats->avg,
  694. 9, trip_stats->min);
  695. }
  696. return 0;
  697. }
  698. static const struct seq_operations tze_sops = {
  699. .start = tze_seq_start,
  700. .next = tze_seq_next,
  701. .stop = tze_seq_stop,
  702. .show = tze_seq_show,
  703. };
  704. DEFINE_SEQ_ATTRIBUTE(tze);
  705. void thermal_debug_tz_add(struct thermal_zone_device *tz)
  706. {
  707. struct thermal_debugfs *thermal_dbg;
  708. struct tz_debugfs *tz_dbg;
  709. thermal_dbg = thermal_debugfs_add_id(d_tz, tz->id);
  710. if (!thermal_dbg)
  711. return;
  712. tz_dbg = &thermal_dbg->tz_dbg;
  713. tz_dbg->tz = tz;
  714. tz_dbg->trips_crossed = kzalloc_objs(int, tz->num_trips);
  715. if (!tz_dbg->trips_crossed) {
  716. thermal_debugfs_remove_id(thermal_dbg);
  717. return;
  718. }
  719. INIT_LIST_HEAD(&tz_dbg->tz_episodes);
  720. debugfs_create_file("mitigations", 0400, thermal_dbg->d_top,
  721. thermal_dbg, &tze_fops);
  722. tz->debugfs = thermal_dbg;
  723. }
  724. static struct thermal_debugfs *thermal_debug_tz_clear(struct thermal_zone_device *tz)
  725. {
  726. struct thermal_debugfs *thermal_dbg;
  727. guard(thermal_zone)(tz);
  728. thermal_dbg = tz->debugfs;
  729. if (thermal_dbg)
  730. tz->debugfs = NULL;
  731. return thermal_dbg;
  732. }
  733. void thermal_debug_tz_remove(struct thermal_zone_device *tz)
  734. {
  735. struct thermal_debugfs *thermal_dbg;
  736. struct tz_episode *tze, *tmp;
  737. struct tz_debugfs *tz_dbg;
  738. int *trips_crossed;
  739. thermal_dbg = thermal_debug_tz_clear(tz);
  740. if (!thermal_dbg)
  741. return;
  742. tz_dbg = &thermal_dbg->tz_dbg;
  743. mutex_lock(&thermal_dbg->lock);
  744. trips_crossed = tz_dbg->trips_crossed;
  745. list_for_each_entry_safe(tze, tmp, &tz_dbg->tz_episodes, node) {
  746. list_del(&tze->node);
  747. kfree(tze);
  748. }
  749. mutex_unlock(&thermal_dbg->lock);
  750. thermal_debugfs_remove_id(thermal_dbg);
  751. kfree(trips_crossed);
  752. }
  753. void thermal_debug_tz_resume(struct thermal_zone_device *tz)
  754. {
  755. struct thermal_debugfs *thermal_dbg = tz->debugfs;
  756. ktime_t now = ktime_get();
  757. struct tz_debugfs *tz_dbg;
  758. struct tz_episode *tze;
  759. int i;
  760. if (!thermal_dbg)
  761. return;
  762. mutex_lock(&thermal_dbg->lock);
  763. tz_dbg = &thermal_dbg->tz_dbg;
  764. if (!tz_dbg->nr_trips)
  765. goto out;
  766. /*
  767. * A mitigation episode was in progress before the preceding system
  768. * suspend transition, so close it because the zone handling is starting
  769. * over from scratch.
  770. */
  771. tze = list_first_entry(&tz_dbg->tz_episodes, struct tz_episode, node);
  772. for (i = 0; i < tz_dbg->nr_trips; i++)
  773. tz_episode_close_trip(tze, tz_dbg->trips_crossed[i], now);
  774. tze->duration = ktime_sub(now, tze->timestamp);
  775. tz_dbg->nr_trips = 0;
  776. out:
  777. mutex_unlock(&thermal_dbg->lock);
  778. }