sysfs.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  1. /*
  2. * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
  3. *
  4. * Licensed under the terms of the GNU GPL License version 2.
  5. */
  6. #include <stdio.h>
  7. #include <errno.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <limits.h>
  11. #include <sys/types.h>
  12. #include <sys/stat.h>
  13. #include <fcntl.h>
  14. #include <unistd.h>
  15. #include "cpufreq.h"
  16. #define PATH_TO_CPU "/sys/devices/system/cpu/"
  17. #define MAX_LINE_LEN 4096
  18. #define SYSFS_PATH_MAX 255
  19. static unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen)
  20. {
  21. int fd;
  22. ssize_t numread;
  23. fd = open(path, O_RDONLY);
  24. if (fd == -1)
  25. return 0;
  26. numread = read(fd, buf, buflen - 1);
  27. if (numread < 1) {
  28. close(fd);
  29. return 0;
  30. }
  31. buf[numread] = '\0';
  32. close(fd);
  33. return (unsigned int) numread;
  34. }
  35. /* CPUFREQ sysfs access **************************************************/
  36. /* helper function to read file from /sys into given buffer */
  37. /* fname is a relative path under "cpuX/cpufreq" dir */
  38. static unsigned int sysfs_cpufreq_read_file(unsigned int cpu, const char *fname,
  39. char *buf, size_t buflen)
  40. {
  41. char path[SYSFS_PATH_MAX];
  42. snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",
  43. cpu, fname);
  44. return sysfs_read_file(path, buf, buflen);
  45. }
  46. /* helper function to write a new value to a /sys file */
  47. /* fname is a relative path under "cpuX/cpufreq" dir */
  48. static unsigned int sysfs_cpufreq_write_file(unsigned int cpu,
  49. const char *fname,
  50. const char *value, size_t len)
  51. {
  52. char path[SYSFS_PATH_MAX];
  53. int fd;
  54. ssize_t numwrite;
  55. snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",
  56. cpu, fname);
  57. fd = open(path, O_WRONLY);
  58. if (fd == -1)
  59. return 0;
  60. numwrite = write(fd, value, len);
  61. if (numwrite < 1) {
  62. close(fd);
  63. return 0;
  64. }
  65. close(fd);
  66. return (unsigned int) numwrite;
  67. }
  68. /* read access to files which contain one numeric value */
  69. enum cpufreq_value {
  70. CPUINFO_CUR_FREQ,
  71. CPUINFO_MIN_FREQ,
  72. CPUINFO_MAX_FREQ,
  73. CPUINFO_LATENCY,
  74. SCALING_CUR_FREQ,
  75. SCALING_MIN_FREQ,
  76. SCALING_MAX_FREQ,
  77. STATS_NUM_TRANSITIONS,
  78. MAX_CPUFREQ_VALUE_READ_FILES
  79. };
  80. static const char *cpufreq_value_files[MAX_CPUFREQ_VALUE_READ_FILES] = {
  81. [CPUINFO_CUR_FREQ] = "cpuinfo_cur_freq",
  82. [CPUINFO_MIN_FREQ] = "cpuinfo_min_freq",
  83. [CPUINFO_MAX_FREQ] = "cpuinfo_max_freq",
  84. [CPUINFO_LATENCY] = "cpuinfo_transition_latency",
  85. [SCALING_CUR_FREQ] = "scaling_cur_freq",
  86. [SCALING_MIN_FREQ] = "scaling_min_freq",
  87. [SCALING_MAX_FREQ] = "scaling_max_freq",
  88. [STATS_NUM_TRANSITIONS] = "stats/total_trans"
  89. };
  90. static unsigned long sysfs_cpufreq_get_one_value(unsigned int cpu,
  91. enum cpufreq_value which)
  92. {
  93. unsigned long value;
  94. unsigned int len;
  95. char linebuf[MAX_LINE_LEN];
  96. char *endp;
  97. if (which >= MAX_CPUFREQ_VALUE_READ_FILES)
  98. return 0;
  99. len = sysfs_cpufreq_read_file(cpu, cpufreq_value_files[which],
  100. linebuf, sizeof(linebuf));
  101. if (len == 0)
  102. return 0;
  103. value = strtoul(linebuf, &endp, 0);
  104. if (endp == linebuf || errno == ERANGE)
  105. return 0;
  106. return value;
  107. }
  108. /* read access to files which contain one string */
  109. enum cpufreq_string {
  110. SCALING_DRIVER,
  111. SCALING_GOVERNOR,
  112. MAX_CPUFREQ_STRING_FILES
  113. };
  114. static const char *cpufreq_string_files[MAX_CPUFREQ_STRING_FILES] = {
  115. [SCALING_DRIVER] = "scaling_driver",
  116. [SCALING_GOVERNOR] = "scaling_governor",
  117. };
  118. static char *sysfs_cpufreq_get_one_string(unsigned int cpu,
  119. enum cpufreq_string which)
  120. {
  121. char linebuf[MAX_LINE_LEN];
  122. char *result;
  123. unsigned int len;
  124. if (which >= MAX_CPUFREQ_STRING_FILES)
  125. return NULL;
  126. len = sysfs_cpufreq_read_file(cpu, cpufreq_string_files[which],
  127. linebuf, sizeof(linebuf));
  128. if (len == 0)
  129. return NULL;
  130. result = strdup(linebuf);
  131. if (result == NULL)
  132. return NULL;
  133. if (result[strlen(result) - 1] == '\n')
  134. result[strlen(result) - 1] = '\0';
  135. return result;
  136. }
  137. /* write access */
  138. enum cpufreq_write {
  139. WRITE_SCALING_MIN_FREQ,
  140. WRITE_SCALING_MAX_FREQ,
  141. WRITE_SCALING_GOVERNOR,
  142. WRITE_SCALING_SET_SPEED,
  143. MAX_CPUFREQ_WRITE_FILES
  144. };
  145. static const char *cpufreq_write_files[MAX_CPUFREQ_WRITE_FILES] = {
  146. [WRITE_SCALING_MIN_FREQ] = "scaling_min_freq",
  147. [WRITE_SCALING_MAX_FREQ] = "scaling_max_freq",
  148. [WRITE_SCALING_GOVERNOR] = "scaling_governor",
  149. [WRITE_SCALING_SET_SPEED] = "scaling_setspeed",
  150. };
  151. static int sysfs_cpufreq_write_one_value(unsigned int cpu,
  152. enum cpufreq_write which,
  153. const char *new_value, size_t len)
  154. {
  155. if (which >= MAX_CPUFREQ_WRITE_FILES)
  156. return 0;
  157. if (sysfs_cpufreq_write_file(cpu, cpufreq_write_files[which],
  158. new_value, len) != len)
  159. return -ENODEV;
  160. return 0;
  161. };
  162. unsigned long sysfs_get_freq_kernel(unsigned int cpu)
  163. {
  164. return sysfs_cpufreq_get_one_value(cpu, SCALING_CUR_FREQ);
  165. }
  166. unsigned long sysfs_get_freq_hardware(unsigned int cpu)
  167. {
  168. return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ);
  169. }
  170. unsigned long sysfs_get_freq_transition_latency(unsigned int cpu)
  171. {
  172. return sysfs_cpufreq_get_one_value(cpu, CPUINFO_LATENCY);
  173. }
  174. int sysfs_get_freq_hardware_limits(unsigned int cpu,
  175. unsigned long *min,
  176. unsigned long *max)
  177. {
  178. if ((!min) || (!max))
  179. return -EINVAL;
  180. *min = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MIN_FREQ);
  181. if (!*min)
  182. return -ENODEV;
  183. *max = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MAX_FREQ);
  184. if (!*max)
  185. return -ENODEV;
  186. return 0;
  187. }
  188. char *sysfs_get_freq_driver(unsigned int cpu)
  189. {
  190. return sysfs_cpufreq_get_one_string(cpu, SCALING_DRIVER);
  191. }
  192. struct cpufreq_policy *sysfs_get_freq_policy(unsigned int cpu)
  193. {
  194. struct cpufreq_policy *policy;
  195. policy = malloc(sizeof(struct cpufreq_policy));
  196. if (!policy)
  197. return NULL;
  198. policy->governor = sysfs_cpufreq_get_one_string(cpu, SCALING_GOVERNOR);
  199. if (!policy->governor) {
  200. free(policy);
  201. return NULL;
  202. }
  203. policy->min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);
  204. policy->max = sysfs_cpufreq_get_one_value(cpu, SCALING_MAX_FREQ);
  205. if ((!policy->min) || (!policy->max)) {
  206. free(policy->governor);
  207. free(policy);
  208. return NULL;
  209. }
  210. return policy;
  211. }
  212. struct cpufreq_available_governors *
  213. sysfs_get_freq_available_governors(unsigned int cpu) {
  214. struct cpufreq_available_governors *first = NULL;
  215. struct cpufreq_available_governors *current = NULL;
  216. char linebuf[MAX_LINE_LEN];
  217. unsigned int pos, i;
  218. unsigned int len;
  219. len = sysfs_cpufreq_read_file(cpu, "scaling_available_governors",
  220. linebuf, sizeof(linebuf));
  221. if (len == 0)
  222. return NULL;
  223. pos = 0;
  224. for (i = 0; i < len; i++) {
  225. if (linebuf[i] == ' ' || linebuf[i] == '\n') {
  226. if (i - pos < 2)
  227. continue;
  228. if (current) {
  229. current->next = malloc(sizeof(*current));
  230. if (!current->next)
  231. goto error_out;
  232. current = current->next;
  233. } else {
  234. first = malloc(sizeof(*first));
  235. if (!first)
  236. goto error_out;
  237. current = first;
  238. }
  239. current->first = first;
  240. current->next = NULL;
  241. current->governor = malloc(i - pos + 1);
  242. if (!current->governor)
  243. goto error_out;
  244. memcpy(current->governor, linebuf + pos, i - pos);
  245. current->governor[i - pos] = '\0';
  246. pos = i + 1;
  247. }
  248. }
  249. return first;
  250. error_out:
  251. while (first) {
  252. current = first->next;
  253. if (first->governor)
  254. free(first->governor);
  255. free(first);
  256. first = current;
  257. }
  258. return NULL;
  259. }
  260. struct cpufreq_available_frequencies *
  261. sysfs_get_available_frequencies(unsigned int cpu) {
  262. struct cpufreq_available_frequencies *first = NULL;
  263. struct cpufreq_available_frequencies *current = NULL;
  264. char one_value[SYSFS_PATH_MAX];
  265. char linebuf[MAX_LINE_LEN];
  266. unsigned int pos, i;
  267. unsigned int len;
  268. len = sysfs_cpufreq_read_file(cpu, "scaling_available_frequencies",
  269. linebuf, sizeof(linebuf));
  270. if (len == 0)
  271. return NULL;
  272. pos = 0;
  273. for (i = 0; i < len; i++) {
  274. if (linebuf[i] == ' ' || linebuf[i] == '\n') {
  275. if (i - pos < 2)
  276. continue;
  277. if (i - pos >= SYSFS_PATH_MAX)
  278. goto error_out;
  279. if (current) {
  280. current->next = malloc(sizeof(*current));
  281. if (!current->next)
  282. goto error_out;
  283. current = current->next;
  284. } else {
  285. first = malloc(sizeof(*first));
  286. if (!first)
  287. goto error_out;
  288. current = first;
  289. }
  290. current->first = first;
  291. current->next = NULL;
  292. memcpy(one_value, linebuf + pos, i - pos);
  293. one_value[i - pos] = '\0';
  294. if (sscanf(one_value, "%lu", &current->frequency) != 1)
  295. goto error_out;
  296. pos = i + 1;
  297. }
  298. }
  299. return first;
  300. error_out:
  301. while (first) {
  302. current = first->next;
  303. free(first);
  304. first = current;
  305. }
  306. return NULL;
  307. }
  308. static struct cpufreq_affected_cpus *sysfs_get_cpu_list(unsigned int cpu,
  309. const char *file)
  310. {
  311. struct cpufreq_affected_cpus *first = NULL;
  312. struct cpufreq_affected_cpus *current = NULL;
  313. char one_value[SYSFS_PATH_MAX];
  314. char linebuf[MAX_LINE_LEN];
  315. unsigned int pos, i;
  316. unsigned int len;
  317. len = sysfs_cpufreq_read_file(cpu, file, linebuf, sizeof(linebuf));
  318. if (len == 0)
  319. return NULL;
  320. pos = 0;
  321. for (i = 0; i < len; i++) {
  322. if (i == len || linebuf[i] == ' ' || linebuf[i] == '\n') {
  323. if (i - pos < 1)
  324. continue;
  325. if (i - pos >= SYSFS_PATH_MAX)
  326. goto error_out;
  327. if (current) {
  328. current->next = malloc(sizeof(*current));
  329. if (!current->next)
  330. goto error_out;
  331. current = current->next;
  332. } else {
  333. first = malloc(sizeof(*first));
  334. if (!first)
  335. goto error_out;
  336. current = first;
  337. }
  338. current->first = first;
  339. current->next = NULL;
  340. memcpy(one_value, linebuf + pos, i - pos);
  341. one_value[i - pos] = '\0';
  342. if (sscanf(one_value, "%u", &current->cpu) != 1)
  343. goto error_out;
  344. pos = i + 1;
  345. }
  346. }
  347. return first;
  348. error_out:
  349. while (first) {
  350. current = first->next;
  351. free(first);
  352. first = current;
  353. }
  354. return NULL;
  355. }
  356. struct cpufreq_affected_cpus *sysfs_get_freq_affected_cpus(unsigned int cpu)
  357. {
  358. return sysfs_get_cpu_list(cpu, "affected_cpus");
  359. }
  360. struct cpufreq_affected_cpus *sysfs_get_freq_related_cpus(unsigned int cpu)
  361. {
  362. return sysfs_get_cpu_list(cpu, "related_cpus");
  363. }
  364. struct cpufreq_stats *sysfs_get_freq_stats(unsigned int cpu,
  365. unsigned long long *total_time) {
  366. struct cpufreq_stats *first = NULL;
  367. struct cpufreq_stats *current = NULL;
  368. char one_value[SYSFS_PATH_MAX];
  369. char linebuf[MAX_LINE_LEN];
  370. unsigned int pos, i;
  371. unsigned int len;
  372. len = sysfs_cpufreq_read_file(cpu, "stats/time_in_state",
  373. linebuf, sizeof(linebuf));
  374. if (len == 0)
  375. return NULL;
  376. *total_time = 0;
  377. pos = 0;
  378. for (i = 0; i < len; i++) {
  379. if (i == strlen(linebuf) || linebuf[i] == '\n') {
  380. if (i - pos < 2)
  381. continue;
  382. if ((i - pos) >= SYSFS_PATH_MAX)
  383. goto error_out;
  384. if (current) {
  385. current->next = malloc(sizeof(*current));
  386. if (!current->next)
  387. goto error_out;
  388. current = current->next;
  389. } else {
  390. first = malloc(sizeof(*first));
  391. if (!first)
  392. goto error_out;
  393. current = first;
  394. }
  395. current->first = first;
  396. current->next = NULL;
  397. memcpy(one_value, linebuf + pos, i - pos);
  398. one_value[i - pos] = '\0';
  399. if (sscanf(one_value, "%lu %llu",
  400. &current->frequency,
  401. &current->time_in_state) != 2)
  402. goto error_out;
  403. *total_time = *total_time + current->time_in_state;
  404. pos = i + 1;
  405. }
  406. }
  407. return first;
  408. error_out:
  409. while (first) {
  410. current = first->next;
  411. free(first);
  412. first = current;
  413. }
  414. return NULL;
  415. }
  416. unsigned long sysfs_get_freq_transitions(unsigned int cpu)
  417. {
  418. return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS);
  419. }
  420. static int verify_gov(char *new_gov, char *passed_gov)
  421. {
  422. unsigned int i, j = 0;
  423. if (!passed_gov || (strlen(passed_gov) > 19))
  424. return -EINVAL;
  425. strncpy(new_gov, passed_gov, 20);
  426. for (i = 0; i < 20; i++) {
  427. if (j) {
  428. new_gov[i] = '\0';
  429. continue;
  430. }
  431. if ((new_gov[i] >= 'a') && (new_gov[i] <= 'z'))
  432. continue;
  433. if ((new_gov[i] >= 'A') && (new_gov[i] <= 'Z'))
  434. continue;
  435. if (new_gov[i] == '-')
  436. continue;
  437. if (new_gov[i] == '_')
  438. continue;
  439. if (new_gov[i] == '\0') {
  440. j = 1;
  441. continue;
  442. }
  443. return -EINVAL;
  444. }
  445. new_gov[19] = '\0';
  446. return 0;
  447. }
  448. int sysfs_modify_freq_policy_governor(unsigned int cpu, char *governor)
  449. {
  450. char new_gov[SYSFS_PATH_MAX];
  451. if (!governor)
  452. return -EINVAL;
  453. if (verify_gov(new_gov, governor))
  454. return -EINVAL;
  455. return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
  456. new_gov, strlen(new_gov));
  457. };
  458. int sysfs_modify_freq_policy_max(unsigned int cpu, unsigned long max_freq)
  459. {
  460. char value[SYSFS_PATH_MAX];
  461. snprintf(value, SYSFS_PATH_MAX, "%lu", max_freq);
  462. return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
  463. value, strlen(value));
  464. };
  465. int sysfs_modify_freq_policy_min(unsigned int cpu, unsigned long min_freq)
  466. {
  467. char value[SYSFS_PATH_MAX];
  468. snprintf(value, SYSFS_PATH_MAX, "%lu", min_freq);
  469. return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ,
  470. value, strlen(value));
  471. };
  472. int sysfs_set_freq_policy(unsigned int cpu, struct cpufreq_policy *policy)
  473. {
  474. char min[SYSFS_PATH_MAX];
  475. char max[SYSFS_PATH_MAX];
  476. char gov[SYSFS_PATH_MAX];
  477. int ret;
  478. unsigned long old_min;
  479. int write_max_first;
  480. if (!policy || !(policy->governor))
  481. return -EINVAL;
  482. if (policy->max < policy->min)
  483. return -EINVAL;
  484. if (verify_gov(gov, policy->governor))
  485. return -EINVAL;
  486. snprintf(min, SYSFS_PATH_MAX, "%lu", policy->min);
  487. snprintf(max, SYSFS_PATH_MAX, "%lu", policy->max);
  488. old_min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);
  489. write_max_first = (old_min && (policy->max < old_min) ? 0 : 1);
  490. if (write_max_first) {
  491. ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
  492. max, strlen(max));
  493. if (ret)
  494. return ret;
  495. }
  496. ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, min,
  497. strlen(min));
  498. if (ret)
  499. return ret;
  500. if (!write_max_first) {
  501. ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
  502. max, strlen(max));
  503. if (ret)
  504. return ret;
  505. }
  506. return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
  507. gov, strlen(gov));
  508. }
  509. int sysfs_set_frequency(unsigned int cpu, unsigned long target_frequency)
  510. {
  511. struct cpufreq_policy *pol = sysfs_get_freq_policy(cpu);
  512. char userspace_gov[] = "userspace";
  513. char freq[SYSFS_PATH_MAX];
  514. int ret;
  515. if (!pol)
  516. return -ENODEV;
  517. if (strncmp(pol->governor, userspace_gov, 9) != 0) {
  518. ret = sysfs_modify_freq_policy_governor(cpu, userspace_gov);
  519. if (ret) {
  520. cpufreq_put_policy(pol);
  521. return ret;
  522. }
  523. }
  524. cpufreq_put_policy(pol);
  525. snprintf(freq, SYSFS_PATH_MAX, "%lu", target_frequency);
  526. return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_SET_SPEED,
  527. freq, strlen(freq));
  528. }
  529. /* CPUFREQ sysfs access **************************************************/
  530. /* General sysfs access **************************************************/
  531. int sysfs_cpu_exists(unsigned int cpu)
  532. {
  533. char file[SYSFS_PATH_MAX];
  534. struct stat statbuf;
  535. snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/", cpu);
  536. if (stat(file, &statbuf) != 0)
  537. return -ENOSYS;
  538. return S_ISDIR(statbuf.st_mode) ? 0 : -ENOSYS;
  539. }
  540. /* General sysfs access **************************************************/