command.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright 2024, Intel Corporation
  4. *
  5. * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
  6. *
  7. * Thermal subsystem testing facility.
  8. *
  9. * This facility allows the thermal core functionality to be exercised in a
  10. * controlled way in order to verify its behavior.
  11. *
  12. * It resides in the "thermal-testing" directory under the debugfs root and
  13. * starts with a single file called "command" which can be written a string
  14. * representing a thermal testing facility command.
  15. *
  16. * The currently supported commands are listed in the tt_commands enum below.
  17. *
  18. * The "addtz" command causes a new test thermal zone template to be created,
  19. * for example:
  20. *
  21. * # echo addtz > /sys/kernel/debug/thermal-testing/command
  22. *
  23. * That template will be represented as a subdirectory in the "thermal-testing"
  24. * directory, for example
  25. *
  26. * # ls /sys/kernel/debug/thermal-testing/
  27. * command tz0
  28. *
  29. * The thermal zone template can be populated with trip points with the help of
  30. * the "tzaddtrip" command, for example:
  31. *
  32. * # echo tzaddtrip:0 > /sys/kernel/debug/thermal-testing/command
  33. *
  34. * which causes a trip point template to be added to the test thermal zone
  35. * template 0 (represented by the tz0 subdirectory in "thermal-testing").
  36. *
  37. * # ls /sys/kernel/debug/thermal-testing/tz0
  38. * init_temp temp trip_0_temp trip_0_hyst
  39. *
  40. * The temperature of a trip point template is initially THERMAL_TEMP_INVALID
  41. * and its hysteresis is initially 0. They can be adjusted by writing to the
  42. * "trip_x_temp" and "trip_x_hyst" files correspoinding to that trip point
  43. * template, respectively.
  44. *
  45. * The initial temperature of a thermal zone based on a template can be set by
  46. * writing to the "init_temp" file in its directory under "thermal-testing", for
  47. * example:
  48. *
  49. * echo 50000 > /sys/kernel/debug/thermal-testing/tz0/init_temp
  50. *
  51. * When ready, "tzreg" command can be used for registering and enabling a
  52. * thermal zone based on a given template with the thermal core, for example
  53. *
  54. * # echo tzreg:0 > /sys/kernel/debug/thermal-testing/command
  55. *
  56. * In this case, test thermal zone template 0 is used for registering a new
  57. * thermal zone and the set of trip point templates associated with it is used
  58. * for populating the new thermal zone's trip points table. The type of the new
  59. * thermal zone is "test_tz".
  60. *
  61. * The temperature and hysteresis of all of the trip points in that new thermal
  62. * zone are adjustable via sysfs, so they can be updated at any time.
  63. *
  64. * The current temperature of the new thermal zone can be set by writing to the
  65. * "temp" file in the corresponding thermal zone template's directory under
  66. * "thermal-testing", for example
  67. *
  68. * echo 10000 > /sys/kernel/debug/thermal-testing/tz0/temp
  69. *
  70. * which will also trigger a temperature update for this zone in the thermal
  71. * core, including checking its trip points, sending notifications to user space
  72. * if any of them have been crossed and so on.
  73. *
  74. * When it is not needed any more, a test thermal zone template can be deleted
  75. * with the help of the "deltz" command, for example
  76. *
  77. * # echo deltz:0 > /sys/kernel/debug/thermal-testing/command
  78. *
  79. * which will also unregister the thermal zone based on it, if present.
  80. */
  81. #define pr_fmt(fmt) "thermal-testing: " fmt
  82. #include <linux/debugfs.h>
  83. #include <linux/module.h>
  84. #include "thermal_testing.h"
  85. struct dentry *d_testing;
  86. #define TT_COMMAND_SIZE 16
  87. enum tt_commands {
  88. TT_CMD_ADDTZ,
  89. TT_CMD_DELTZ,
  90. TT_CMD_TZADDTRIP,
  91. TT_CMD_TZREG,
  92. TT_CMD_TZUNREG,
  93. };
  94. static const char *tt_command_strings[] = {
  95. [TT_CMD_ADDTZ] = "addtz",
  96. [TT_CMD_DELTZ] = "deltz",
  97. [TT_CMD_TZADDTRIP] = "tzaddtrip",
  98. [TT_CMD_TZREG] = "tzreg",
  99. [TT_CMD_TZUNREG] = "tzunreg",
  100. };
  101. static int tt_command_exec(int index, const char *arg)
  102. {
  103. int ret;
  104. switch (index) {
  105. case TT_CMD_ADDTZ:
  106. ret = tt_add_tz();
  107. break;
  108. case TT_CMD_DELTZ:
  109. ret = tt_del_tz(arg);
  110. break;
  111. case TT_CMD_TZADDTRIP:
  112. ret = tt_zone_add_trip(arg);
  113. break;
  114. case TT_CMD_TZREG:
  115. ret = tt_zone_reg(arg);
  116. break;
  117. case TT_CMD_TZUNREG:
  118. ret = tt_zone_unreg(arg);
  119. break;
  120. default:
  121. ret = -EINVAL;
  122. break;
  123. }
  124. return ret;
  125. }
  126. static ssize_t tt_command_process(struct dentry *dentry, const char __user *user_buf,
  127. size_t count)
  128. {
  129. char *buf __free(kfree);
  130. char *arg;
  131. int i;
  132. buf = kmalloc(count + 1, GFP_KERNEL);
  133. if (!buf)
  134. return -ENOMEM;
  135. if (copy_from_user(buf, user_buf, count))
  136. return -EFAULT;
  137. buf[count] = '\0';
  138. strim(buf);
  139. arg = strstr(buf, ":");
  140. if (arg) {
  141. *arg = '\0';
  142. arg++;
  143. }
  144. for (i = 0; i < ARRAY_SIZE(tt_command_strings); i++) {
  145. if (!strcmp(buf, tt_command_strings[i]))
  146. return tt_command_exec(i, arg);
  147. }
  148. return -EINVAL;
  149. }
  150. static ssize_t tt_command_write(struct file *file, const char __user *user_buf,
  151. size_t count, loff_t *ppos)
  152. {
  153. struct dentry *dentry = file->f_path.dentry;
  154. ssize_t ret;
  155. if (*ppos)
  156. return -EINVAL;
  157. if (count + 1 > TT_COMMAND_SIZE)
  158. return -E2BIG;
  159. ret = debugfs_file_get(dentry);
  160. if (unlikely(ret))
  161. return ret;
  162. ret = tt_command_process(dentry, user_buf, count);
  163. if (ret)
  164. return ret;
  165. return count;
  166. }
  167. static const struct file_operations tt_command_fops = {
  168. .write = tt_command_write,
  169. .open = simple_open,
  170. .llseek = default_llseek,
  171. };
  172. static int __init thermal_testing_init(void)
  173. {
  174. d_testing = debugfs_create_dir("thermal-testing", NULL);
  175. if (!IS_ERR(d_testing))
  176. debugfs_create_file("command", 0200, d_testing, NULL,
  177. &tt_command_fops);
  178. return 0;
  179. }
  180. module_init(thermal_testing_init);
  181. static void __exit thermal_testing_exit(void)
  182. {
  183. debugfs_remove(d_testing);
  184. tt_zone_cleanup();
  185. }
  186. module_exit(thermal_testing_exit);
  187. MODULE_DESCRIPTION("Thermal core testing facility");
  188. MODULE_LICENSE("GPL v2");