thp_settings.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <fcntl.h>
  3. #include <limits.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <unistd.h>
  8. #include "thp_settings.h"
  9. #define THP_SYSFS "/sys/kernel/mm/transparent_hugepage/"
  10. #define MAX_SETTINGS_DEPTH 4
  11. static struct thp_settings settings_stack[MAX_SETTINGS_DEPTH];
  12. static int settings_index;
  13. static struct thp_settings saved_settings;
  14. static char dev_queue_read_ahead_path[PATH_MAX];
  15. static const char * const thp_enabled_strings[] = {
  16. "never",
  17. "always",
  18. "inherit",
  19. "madvise",
  20. NULL
  21. };
  22. static const char * const thp_defrag_strings[] = {
  23. "always",
  24. "defer",
  25. "defer+madvise",
  26. "madvise",
  27. "never",
  28. NULL
  29. };
  30. static const char * const shmem_enabled_strings[] = {
  31. "never",
  32. "always",
  33. "within_size",
  34. "advise",
  35. "inherit",
  36. "deny",
  37. "force",
  38. NULL
  39. };
  40. int read_file(const char *path, char *buf, size_t buflen)
  41. {
  42. int fd;
  43. ssize_t numread;
  44. fd = open(path, O_RDONLY);
  45. if (fd == -1)
  46. return 0;
  47. numread = read(fd, buf, buflen - 1);
  48. if (numread < 1) {
  49. close(fd);
  50. return 0;
  51. }
  52. buf[numread] = '\0';
  53. close(fd);
  54. return (unsigned int) numread;
  55. }
  56. int write_file(const char *path, const char *buf, size_t buflen)
  57. {
  58. int fd;
  59. ssize_t numwritten;
  60. fd = open(path, O_WRONLY);
  61. if (fd == -1) {
  62. printf("open(%s)\n", path);
  63. exit(EXIT_FAILURE);
  64. return 0;
  65. }
  66. numwritten = write(fd, buf, buflen - 1);
  67. close(fd);
  68. if (numwritten < 1) {
  69. printf("write(%s)\n", buf);
  70. exit(EXIT_FAILURE);
  71. return 0;
  72. }
  73. return (unsigned int) numwritten;
  74. }
  75. unsigned long read_num(const char *path)
  76. {
  77. char buf[21];
  78. if (read_file(path, buf, sizeof(buf)) < 0) {
  79. perror("read_file()");
  80. exit(EXIT_FAILURE);
  81. }
  82. return strtoul(buf, NULL, 10);
  83. }
  84. void write_num(const char *path, unsigned long num)
  85. {
  86. char buf[21];
  87. sprintf(buf, "%ld", num);
  88. if (!write_file(path, buf, strlen(buf) + 1)) {
  89. perror(path);
  90. exit(EXIT_FAILURE);
  91. }
  92. }
  93. int thp_read_string(const char *name, const char * const strings[])
  94. {
  95. char path[PATH_MAX];
  96. char buf[256];
  97. char *c;
  98. int ret;
  99. ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
  100. if (ret >= PATH_MAX) {
  101. printf("%s: Pathname is too long\n", __func__);
  102. exit(EXIT_FAILURE);
  103. }
  104. if (!read_file(path, buf, sizeof(buf))) {
  105. perror(path);
  106. exit(EXIT_FAILURE);
  107. }
  108. c = strchr(buf, '[');
  109. if (!c) {
  110. printf("%s: Parse failure\n", __func__);
  111. exit(EXIT_FAILURE);
  112. }
  113. c++;
  114. memmove(buf, c, sizeof(buf) - (c - buf));
  115. c = strchr(buf, ']');
  116. if (!c) {
  117. printf("%s: Parse failure\n", __func__);
  118. exit(EXIT_FAILURE);
  119. }
  120. *c = '\0';
  121. ret = 0;
  122. while (strings[ret]) {
  123. if (!strcmp(strings[ret], buf))
  124. return ret;
  125. ret++;
  126. }
  127. printf("Failed to parse %s\n", name);
  128. exit(EXIT_FAILURE);
  129. }
  130. void thp_write_string(const char *name, const char *val)
  131. {
  132. char path[PATH_MAX];
  133. int ret;
  134. ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
  135. if (ret >= PATH_MAX) {
  136. printf("%s: Pathname is too long\n", __func__);
  137. exit(EXIT_FAILURE);
  138. }
  139. if (!write_file(path, val, strlen(val) + 1)) {
  140. perror(path);
  141. exit(EXIT_FAILURE);
  142. }
  143. }
  144. unsigned long thp_read_num(const char *name)
  145. {
  146. char path[PATH_MAX];
  147. int ret;
  148. ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
  149. if (ret >= PATH_MAX) {
  150. printf("%s: Pathname is too long\n", __func__);
  151. exit(EXIT_FAILURE);
  152. }
  153. return read_num(path);
  154. }
  155. void thp_write_num(const char *name, unsigned long num)
  156. {
  157. char path[PATH_MAX];
  158. int ret;
  159. ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
  160. if (ret >= PATH_MAX) {
  161. printf("%s: Pathname is too long\n", __func__);
  162. exit(EXIT_FAILURE);
  163. }
  164. write_num(path, num);
  165. }
  166. void thp_read_settings(struct thp_settings *settings)
  167. {
  168. unsigned long orders = thp_supported_orders();
  169. unsigned long shmem_orders = thp_shmem_supported_orders();
  170. char path[PATH_MAX];
  171. int i;
  172. *settings = (struct thp_settings) {
  173. .thp_enabled = thp_read_string("enabled", thp_enabled_strings),
  174. .thp_defrag = thp_read_string("defrag", thp_defrag_strings),
  175. .shmem_enabled =
  176. thp_read_string("shmem_enabled", shmem_enabled_strings),
  177. .use_zero_page = thp_read_num("use_zero_page"),
  178. };
  179. settings->khugepaged = (struct khugepaged_settings) {
  180. .defrag = thp_read_num("khugepaged/defrag"),
  181. .alloc_sleep_millisecs =
  182. thp_read_num("khugepaged/alloc_sleep_millisecs"),
  183. .scan_sleep_millisecs =
  184. thp_read_num("khugepaged/scan_sleep_millisecs"),
  185. .max_ptes_none = thp_read_num("khugepaged/max_ptes_none"),
  186. .max_ptes_swap = thp_read_num("khugepaged/max_ptes_swap"),
  187. .max_ptes_shared = thp_read_num("khugepaged/max_ptes_shared"),
  188. .pages_to_scan = thp_read_num("khugepaged/pages_to_scan"),
  189. };
  190. if (dev_queue_read_ahead_path[0])
  191. settings->read_ahead_kb = read_num(dev_queue_read_ahead_path);
  192. for (i = 0; i < NR_ORDERS; i++) {
  193. if (!((1 << i) & orders)) {
  194. settings->hugepages[i].enabled = THP_NEVER;
  195. continue;
  196. }
  197. snprintf(path, PATH_MAX, "hugepages-%ukB/enabled",
  198. (getpagesize() >> 10) << i);
  199. settings->hugepages[i].enabled =
  200. thp_read_string(path, thp_enabled_strings);
  201. }
  202. for (i = 0; i < NR_ORDERS; i++) {
  203. if (!((1 << i) & shmem_orders)) {
  204. settings->shmem_hugepages[i].enabled = SHMEM_NEVER;
  205. continue;
  206. }
  207. snprintf(path, PATH_MAX, "hugepages-%ukB/shmem_enabled",
  208. (getpagesize() >> 10) << i);
  209. settings->shmem_hugepages[i].enabled =
  210. thp_read_string(path, shmem_enabled_strings);
  211. }
  212. }
  213. void thp_write_settings(struct thp_settings *settings)
  214. {
  215. struct khugepaged_settings *khugepaged = &settings->khugepaged;
  216. unsigned long orders = thp_supported_orders();
  217. unsigned long shmem_orders = thp_shmem_supported_orders();
  218. char path[PATH_MAX];
  219. int enabled;
  220. int i;
  221. thp_write_string("enabled", thp_enabled_strings[settings->thp_enabled]);
  222. thp_write_string("defrag", thp_defrag_strings[settings->thp_defrag]);
  223. thp_write_string("shmem_enabled",
  224. shmem_enabled_strings[settings->shmem_enabled]);
  225. thp_write_num("use_zero_page", settings->use_zero_page);
  226. thp_write_num("khugepaged/defrag", khugepaged->defrag);
  227. thp_write_num("khugepaged/alloc_sleep_millisecs",
  228. khugepaged->alloc_sleep_millisecs);
  229. thp_write_num("khugepaged/scan_sleep_millisecs",
  230. khugepaged->scan_sleep_millisecs);
  231. thp_write_num("khugepaged/max_ptes_none", khugepaged->max_ptes_none);
  232. thp_write_num("khugepaged/max_ptes_swap", khugepaged->max_ptes_swap);
  233. thp_write_num("khugepaged/max_ptes_shared", khugepaged->max_ptes_shared);
  234. thp_write_num("khugepaged/pages_to_scan", khugepaged->pages_to_scan);
  235. if (dev_queue_read_ahead_path[0])
  236. write_num(dev_queue_read_ahead_path, settings->read_ahead_kb);
  237. for (i = 0; i < NR_ORDERS; i++) {
  238. if (!((1 << i) & orders))
  239. continue;
  240. snprintf(path, PATH_MAX, "hugepages-%ukB/enabled",
  241. (getpagesize() >> 10) << i);
  242. enabled = settings->hugepages[i].enabled;
  243. thp_write_string(path, thp_enabled_strings[enabled]);
  244. }
  245. for (i = 0; i < NR_ORDERS; i++) {
  246. if (!((1 << i) & shmem_orders))
  247. continue;
  248. snprintf(path, PATH_MAX, "hugepages-%ukB/shmem_enabled",
  249. (getpagesize() >> 10) << i);
  250. enabled = settings->shmem_hugepages[i].enabled;
  251. thp_write_string(path, shmem_enabled_strings[enabled]);
  252. }
  253. }
  254. struct thp_settings *thp_current_settings(void)
  255. {
  256. if (!settings_index) {
  257. printf("Fail: No settings set");
  258. exit(EXIT_FAILURE);
  259. }
  260. return settings_stack + settings_index - 1;
  261. }
  262. void thp_push_settings(struct thp_settings *settings)
  263. {
  264. if (settings_index >= MAX_SETTINGS_DEPTH) {
  265. printf("Fail: Settings stack exceeded");
  266. exit(EXIT_FAILURE);
  267. }
  268. settings_stack[settings_index++] = *settings;
  269. thp_write_settings(thp_current_settings());
  270. }
  271. void thp_pop_settings(void)
  272. {
  273. if (settings_index <= 0) {
  274. printf("Fail: Settings stack empty");
  275. exit(EXIT_FAILURE);
  276. }
  277. --settings_index;
  278. thp_write_settings(thp_current_settings());
  279. }
  280. void thp_restore_settings(void)
  281. {
  282. thp_write_settings(&saved_settings);
  283. }
  284. void thp_save_settings(void)
  285. {
  286. thp_read_settings(&saved_settings);
  287. }
  288. void thp_set_read_ahead_path(char *path)
  289. {
  290. if (!path) {
  291. dev_queue_read_ahead_path[0] = '\0';
  292. return;
  293. }
  294. strncpy(dev_queue_read_ahead_path, path,
  295. sizeof(dev_queue_read_ahead_path));
  296. dev_queue_read_ahead_path[sizeof(dev_queue_read_ahead_path) - 1] = '\0';
  297. }
  298. static unsigned long __thp_supported_orders(bool is_shmem)
  299. {
  300. unsigned long orders = 0;
  301. char path[PATH_MAX];
  302. char buf[256];
  303. int ret, i;
  304. char anon_dir[] = "enabled";
  305. char shmem_dir[] = "shmem_enabled";
  306. for (i = 0; i < NR_ORDERS; i++) {
  307. ret = snprintf(path, PATH_MAX, THP_SYSFS "hugepages-%ukB/%s",
  308. (getpagesize() >> 10) << i, is_shmem ? shmem_dir : anon_dir);
  309. if (ret >= PATH_MAX) {
  310. printf("%s: Pathname is too long\n", __func__);
  311. exit(EXIT_FAILURE);
  312. }
  313. ret = read_file(path, buf, sizeof(buf));
  314. if (ret)
  315. orders |= 1UL << i;
  316. }
  317. return orders;
  318. }
  319. unsigned long thp_supported_orders(void)
  320. {
  321. return __thp_supported_orders(false);
  322. }
  323. unsigned long thp_shmem_supported_orders(void)
  324. {
  325. return __thp_supported_orders(true);
  326. }
  327. bool thp_available(void)
  328. {
  329. if (access(THP_SYSFS, F_OK) != 0)
  330. return false;
  331. return true;
  332. }
  333. bool thp_is_enabled(void)
  334. {
  335. if (!thp_available())
  336. return false;
  337. int mode = thp_read_string("enabled", thp_enabled_strings);
  338. /* THP is considered enabled if it's either "always" or "madvise" */
  339. return mode == 1 || mode == 3;
  340. }