cpufreq.c 15 KB

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