replace snprintf with strlcpy
[dpdk.git] / lib / librte_power / power_acpi_cpufreq.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2014 Intel Corporation
3  */
4
5 #include <stdio.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <fcntl.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <signal.h>
13 #include <limits.h>
14
15 #include <rte_string_fns.h>
16 #include <rte_memcpy.h>
17 #include <rte_atomic.h>
18
19 #include "power_acpi_cpufreq.h"
20 #include "power_common.h"
21
22 #ifdef RTE_LIBRTE_POWER_DEBUG
23 #define POWER_DEBUG_TRACE(fmt, args...) do { \
24                 RTE_LOG(ERR, POWER, "%s: " fmt, __func__, ## args); \
25 } while (0)
26 #else
27 #define POWER_DEBUG_TRACE(fmt, args...)
28 #endif
29
30 #define FOPEN_OR_ERR_RET(f, retval) do { \
31                 if ((f) == NULL) { \
32                         RTE_LOG(ERR, POWER, "File not openned\n"); \
33                         return retval; \
34                 } \
35 } while (0)
36
37 #define FOPS_OR_NULL_GOTO(ret, label) do { \
38                 if ((ret) == NULL) { \
39                         RTE_LOG(ERR, POWER, "fgets returns nothing\n"); \
40                         goto label; \
41                 } \
42 } while (0)
43
44 #define FOPS_OR_ERR_GOTO(ret, label) do { \
45                 if ((ret) < 0) { \
46                         RTE_LOG(ERR, POWER, "File operations failed\n"); \
47                         goto label; \
48                 } \
49 } while (0)
50
51 #define STR_SIZE     1024
52 #define POWER_CONVERT_TO_DECIMAL 10
53
54 #define POWER_GOVERNOR_USERSPACE "userspace"
55 #define POWER_SYSFILE_GOVERNOR   \
56                 "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_governor"
57 #define POWER_SYSFILE_AVAIL_FREQ \
58                 "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_available_frequencies"
59 #define POWER_SYSFILE_SETSPEED   \
60                 "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_setspeed"
61
62 /*
63  * MSR related
64  */
65 #define PLATFORM_INFO     0x0CE
66 #define TURBO_RATIO_LIMIT 0x1AD
67 #define IA32_PERF_CTL     0x199
68 #define CORE_TURBO_DISABLE_BIT ((uint64_t)1<<32)
69
70 enum power_state {
71         POWER_IDLE = 0,
72         POWER_ONGOING,
73         POWER_USED,
74         POWER_UNKNOWN
75 };
76
77 /**
78  * Power info per lcore.
79  */
80 struct rte_power_info {
81         unsigned int lcore_id;                   /**< Logical core id */
82         uint32_t freqs[RTE_MAX_LCORE_FREQS]; /**< Frequency array */
83         uint32_t nb_freqs;                   /**< number of available freqs */
84         FILE *f;                             /**< FD of scaling_setspeed */
85         char governor_ori[32];               /**< Original governor name */
86         uint32_t curr_idx;                   /**< Freq index in freqs array */
87         volatile uint32_t state;             /**< Power in use state */
88         uint16_t turbo_available;            /**< Turbo Boost available */
89         uint16_t turbo_enable;               /**< Turbo Boost enable/disable */
90 } __rte_cache_aligned;
91
92 static struct rte_power_info lcore_power_info[RTE_MAX_LCORE];
93
94 /**
95  * It is to set specific freq for specific logical core, according to the index
96  * of supported frequencies.
97  */
98 static int
99 set_freq_internal(struct rte_power_info *pi, uint32_t idx)
100 {
101         if (idx >= RTE_MAX_LCORE_FREQS || idx >= pi->nb_freqs) {
102                 RTE_LOG(ERR, POWER, "Invalid frequency index %u, which "
103                                 "should be less than %u\n", idx, pi->nb_freqs);
104                 return -1;
105         }
106
107         /* Check if it is the same as current */
108         if (idx == pi->curr_idx)
109                 return 0;
110
111         POWER_DEBUG_TRACE("Freqency[%u] %u to be set for lcore %u\n",
112                         idx, pi->freqs[idx], pi->lcore_id);
113         if (fseek(pi->f, 0, SEEK_SET) < 0) {
114                 RTE_LOG(ERR, POWER, "Fail to set file position indicator to 0 "
115                                 "for setting frequency for lcore %u\n", pi->lcore_id);
116                 return -1;
117         }
118         if (fprintf(pi->f, "%u", pi->freqs[idx]) < 0) {
119                 RTE_LOG(ERR, POWER, "Fail to write new frequency for "
120                                 "lcore %u\n", pi->lcore_id);
121                 return -1;
122         }
123         fflush(pi->f);
124         pi->curr_idx = idx;
125
126         return 1;
127 }
128
129 /**
130  * It is to check the current scaling governor by reading sys file, and then
131  * set it into 'userspace' if it is not by writing the sys file. The original
132  * governor will be saved for rolling back.
133  */
134 static int
135 power_set_governor_userspace(struct rte_power_info *pi)
136 {
137         FILE *f;
138         int ret = -1;
139         char buf[BUFSIZ];
140         char fullpath[PATH_MAX];
141         char *s;
142         int val;
143
144         snprintf(fullpath, sizeof(fullpath), POWER_SYSFILE_GOVERNOR,
145                         pi->lcore_id);
146         f = fopen(fullpath, "rw+");
147         FOPEN_OR_ERR_RET(f, ret);
148
149         s = fgets(buf, sizeof(buf), f);
150         FOPS_OR_NULL_GOTO(s, out);
151         /* Strip off terminating '\n' */
152         strtok(buf, "\n");
153
154         /* Check if current governor is userspace */
155         if (strncmp(buf, POWER_GOVERNOR_USERSPACE,
156                         sizeof(POWER_GOVERNOR_USERSPACE)) == 0) {
157                 ret = 0;
158                 POWER_DEBUG_TRACE("Power management governor of lcore %u is "
159                                 "already userspace\n", pi->lcore_id);
160                 goto out;
161         }
162         /* Save the original governor */
163         strlcpy(pi->governor_ori, buf, sizeof(pi->governor_ori));
164
165         /* Write 'userspace' to the governor */
166         val = fseek(f, 0, SEEK_SET);
167         FOPS_OR_ERR_GOTO(val, out);
168
169         val = fputs(POWER_GOVERNOR_USERSPACE, f);
170         FOPS_OR_ERR_GOTO(val, out);
171
172         /* We need to flush to see if the fputs succeeds */
173         val = fflush(f);
174         FOPS_OR_ERR_GOTO(val, out);
175
176         ret = 0;
177         RTE_LOG(INFO, POWER, "Power management governor of lcore %u has been "
178                         "set to user space successfully\n", pi->lcore_id);
179 out:
180         fclose(f);
181
182         return ret;
183 }
184
185 /**
186  * It is to get the available frequencies of the specific lcore by reading the
187  * sys file.
188  */
189 static int
190 power_get_available_freqs(struct rte_power_info *pi)
191 {
192         FILE *f;
193         int ret = -1, i, count;
194         char *p;
195         char buf[BUFSIZ];
196         char fullpath[PATH_MAX];
197         char *freqs[RTE_MAX_LCORE_FREQS];
198         char *s;
199
200         snprintf(fullpath, sizeof(fullpath), POWER_SYSFILE_AVAIL_FREQ,
201                         pi->lcore_id);
202         f = fopen(fullpath, "r");
203         FOPEN_OR_ERR_RET(f, ret);
204
205         s = fgets(buf, sizeof(buf), f);
206         FOPS_OR_NULL_GOTO(s, out);
207
208         /* Strip the line break if there is */
209         p = strchr(buf, '\n');
210         if (p != NULL)
211                 *p = 0;
212
213         /* Split string into at most RTE_MAX_LCORE_FREQS frequencies */
214         count = rte_strsplit(buf, sizeof(buf), freqs,
215                         RTE_MAX_LCORE_FREQS, ' ');
216         if (count <= 0) {
217                 RTE_LOG(ERR, POWER, "No available frequency in "
218                                 ""POWER_SYSFILE_AVAIL_FREQ"\n", pi->lcore_id);
219                 goto out;
220         }
221         if (count >= RTE_MAX_LCORE_FREQS) {
222                 RTE_LOG(ERR, POWER, "Too many available frequencies : %d\n",
223                                 count);
224                 goto out;
225         }
226
227         /* Store the available frequncies into power context */
228         for (i = 0, pi->nb_freqs = 0; i < count; i++) {
229                 POWER_DEBUG_TRACE("Lcore %u frequency[%d]: %s\n", pi->lcore_id,
230                                 i, freqs[i]);
231                 pi->freqs[pi->nb_freqs++] = strtoul(freqs[i], &p,
232                                 POWER_CONVERT_TO_DECIMAL);
233         }
234
235         if ((pi->freqs[0]-1000) == pi->freqs[1]) {
236                 pi->turbo_available = 1;
237                 pi->turbo_enable = 1;
238                 POWER_DEBUG_TRACE("Lcore %u Can do Turbo Boost\n",
239                                 pi->lcore_id);
240         } else {
241                 pi->turbo_available = 0;
242                 pi->turbo_enable = 0;
243                 POWER_DEBUG_TRACE("Turbo Boost not available on Lcore %u\n",
244                                 pi->lcore_id);
245         }
246
247         ret = 0;
248         POWER_DEBUG_TRACE("%d frequency(s) of lcore %u are available\n",
249                         count, pi->lcore_id);
250 out:
251         fclose(f);
252
253         return ret;
254 }
255
256 /**
257  * It is to fopen the sys file for the future setting the lcore frequency.
258  */
259 static int
260 power_init_for_setting_freq(struct rte_power_info *pi)
261 {
262         FILE *f;
263         char fullpath[PATH_MAX];
264         char buf[BUFSIZ];
265         uint32_t i, freq;
266         char *s;
267
268         snprintf(fullpath, sizeof(fullpath), POWER_SYSFILE_SETSPEED,
269                         pi->lcore_id);
270         f = fopen(fullpath, "rw+");
271         FOPEN_OR_ERR_RET(f, -1);
272
273         s = fgets(buf, sizeof(buf), f);
274         FOPS_OR_NULL_GOTO(s, out);
275
276         freq = strtoul(buf, NULL, POWER_CONVERT_TO_DECIMAL);
277         for (i = 0; i < pi->nb_freqs; i++) {
278                 if (freq == pi->freqs[i]) {
279                         pi->curr_idx = i;
280                         pi->f = f;
281                         return 0;
282                 }
283         }
284
285 out:
286         fclose(f);
287
288         return -1;
289 }
290
291 int
292 power_acpi_cpufreq_init(unsigned int lcore_id)
293 {
294         struct rte_power_info *pi;
295
296         if (lcore_id >= RTE_MAX_LCORE) {
297                 RTE_LOG(ERR, POWER, "Lcore id %u can not exceeds %u\n",
298                                 lcore_id, RTE_MAX_LCORE - 1U);
299                 return -1;
300         }
301
302         pi = &lcore_power_info[lcore_id];
303         if (rte_atomic32_cmpset(&(pi->state), POWER_IDLE, POWER_ONGOING)
304                         == 0) {
305                 RTE_LOG(INFO, POWER, "Power management of lcore %u is "
306                                 "in use\n", lcore_id);
307                 return -1;
308         }
309
310         pi->lcore_id = lcore_id;
311         /* Check and set the governor */
312         if (power_set_governor_userspace(pi) < 0) {
313                 RTE_LOG(ERR, POWER, "Cannot set governor of lcore %u to "
314                                 "userspace\n", lcore_id);
315                 goto fail;
316         }
317
318         /* Get the available frequencies */
319         if (power_get_available_freqs(pi) < 0) {
320                 RTE_LOG(ERR, POWER, "Cannot get available frequencies of "
321                                 "lcore %u\n", lcore_id);
322                 goto fail;
323         }
324
325         /* Init for setting lcore frequency */
326         if (power_init_for_setting_freq(pi) < 0) {
327                 RTE_LOG(ERR, POWER, "Cannot init for setting frequency for "
328                                 "lcore %u\n", lcore_id);
329                 goto fail;
330         }
331
332         /* Set freq to max by default */
333         if (power_acpi_cpufreq_freq_max(lcore_id) < 0) {
334                 RTE_LOG(ERR, POWER, "Cannot set frequency of lcore %u "
335                                 "to max\n", lcore_id);
336                 goto fail;
337         }
338
339         RTE_LOG(INFO, POWER, "Initialized successfully for lcore %u "
340                         "power management\n", lcore_id);
341         rte_atomic32_cmpset(&(pi->state), POWER_ONGOING, POWER_USED);
342
343         return 0;
344
345 fail:
346         rte_atomic32_cmpset(&(pi->state), POWER_ONGOING, POWER_UNKNOWN);
347
348         return -1;
349 }
350
351 /**
352  * It is to check the governor and then set the original governor back if
353  * needed by writing the sys file.
354  */
355 static int
356 power_set_governor_original(struct rte_power_info *pi)
357 {
358         FILE *f;
359         int ret = -1;
360         char buf[BUFSIZ];
361         char fullpath[PATH_MAX];
362         char *s;
363         int val;
364
365         snprintf(fullpath, sizeof(fullpath), POWER_SYSFILE_GOVERNOR,
366                         pi->lcore_id);
367         f = fopen(fullpath, "rw+");
368         FOPEN_OR_ERR_RET(f, ret);
369
370         s = fgets(buf, sizeof(buf), f);
371         FOPS_OR_NULL_GOTO(s, out);
372
373         /* Check if the governor to be set is the same as current */
374         if (strncmp(buf, pi->governor_ori, sizeof(pi->governor_ori)) == 0) {
375                 ret = 0;
376                 POWER_DEBUG_TRACE("Power management governor of lcore %u "
377                                 "has already been set to %s\n",
378                                 pi->lcore_id, pi->governor_ori);
379                 goto out;
380         }
381
382         /* Write back the original governor */
383         val = fseek(f, 0, SEEK_SET);
384         FOPS_OR_ERR_GOTO(val, out);
385
386         val = fputs(pi->governor_ori, f);
387         FOPS_OR_ERR_GOTO(val, out);
388
389         ret = 0;
390         RTE_LOG(INFO, POWER, "Power management governor of lcore %u "
391                         "has been set back to %s successfully\n",
392                         pi->lcore_id, pi->governor_ori);
393 out:
394         fclose(f);
395
396         return ret;
397 }
398
399 int
400 power_acpi_cpufreq_exit(unsigned int lcore_id)
401 {
402         struct rte_power_info *pi;
403
404         if (lcore_id >= RTE_MAX_LCORE) {
405                 RTE_LOG(ERR, POWER, "Lcore id %u can not exceeds %u\n",
406                                 lcore_id, RTE_MAX_LCORE - 1U);
407                 return -1;
408         }
409         pi = &lcore_power_info[lcore_id];
410         if (rte_atomic32_cmpset(&(pi->state), POWER_USED, POWER_ONGOING)
411                         == 0) {
412                 RTE_LOG(INFO, POWER, "Power management of lcore %u is "
413                                 "not used\n", lcore_id);
414                 return -1;
415         }
416
417         /* Close FD of setting freq */
418         fclose(pi->f);
419         pi->f = NULL;
420
421         /* Set the governor back to the original */
422         if (power_set_governor_original(pi) < 0) {
423                 RTE_LOG(ERR, POWER, "Cannot set the governor of %u back "
424                                 "to the original\n", lcore_id);
425                 goto fail;
426         }
427
428         RTE_LOG(INFO, POWER, "Power management of lcore %u has exited from "
429                         "'userspace' mode and been set back to the "
430                         "original\n", lcore_id);
431         rte_atomic32_cmpset(&(pi->state), POWER_ONGOING, POWER_IDLE);
432
433         return 0;
434
435 fail:
436         rte_atomic32_cmpset(&(pi->state), POWER_ONGOING, POWER_UNKNOWN);
437
438         return -1;
439 }
440
441 uint32_t
442 power_acpi_cpufreq_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t num)
443 {
444         struct rte_power_info *pi;
445
446         if (lcore_id >= RTE_MAX_LCORE) {
447                 RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
448                 return 0;
449         }
450
451         if (freqs == NULL) {
452                 RTE_LOG(ERR, POWER, "NULL buffer supplied\n");
453                 return 0;
454         }
455
456         pi = &lcore_power_info[lcore_id];
457         if (num < pi->nb_freqs) {
458                 RTE_LOG(ERR, POWER, "Buffer size is not enough\n");
459                 return 0;
460         }
461         rte_memcpy(freqs, pi->freqs, pi->nb_freqs * sizeof(uint32_t));
462
463         return pi->nb_freqs;
464 }
465
466 uint32_t
467 power_acpi_cpufreq_get_freq(unsigned int lcore_id)
468 {
469         if (lcore_id >= RTE_MAX_LCORE) {
470                 RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
471                 return RTE_POWER_INVALID_FREQ_INDEX;
472         }
473
474         return lcore_power_info[lcore_id].curr_idx;
475 }
476
477 int
478 power_acpi_cpufreq_set_freq(unsigned int lcore_id, uint32_t index)
479 {
480         if (lcore_id >= RTE_MAX_LCORE) {
481                 RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
482                 return -1;
483         }
484
485         return set_freq_internal(&(lcore_power_info[lcore_id]), index);
486 }
487
488 int
489 power_acpi_cpufreq_freq_down(unsigned int lcore_id)
490 {
491         struct rte_power_info *pi;
492
493         if (lcore_id >= RTE_MAX_LCORE) {
494                 RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
495                 return -1;
496         }
497
498         pi = &lcore_power_info[lcore_id];
499         if (pi->curr_idx + 1 == pi->nb_freqs)
500                 return 0;
501
502         /* Frequencies in the array are from high to low. */
503         return set_freq_internal(pi, pi->curr_idx + 1);
504 }
505
506 int
507 power_acpi_cpufreq_freq_up(unsigned int lcore_id)
508 {
509         struct rte_power_info *pi;
510
511         if (lcore_id >= RTE_MAX_LCORE) {
512                 RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
513                 return -1;
514         }
515
516         pi = &lcore_power_info[lcore_id];
517         if (pi->curr_idx == 0)
518                 return 0;
519
520         /* Frequencies in the array are from high to low. */
521         return set_freq_internal(pi, pi->curr_idx - 1);
522 }
523
524 int
525 power_acpi_cpufreq_freq_max(unsigned int lcore_id)
526 {
527         if (lcore_id >= RTE_MAX_LCORE) {
528                 RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
529                 return -1;
530         }
531
532         /* Frequencies in the array are from high to low. */
533         if (lcore_power_info[lcore_id].turbo_available) {
534                 if (lcore_power_info[lcore_id].turbo_enable)
535                         /* Set to Turbo */
536                         return set_freq_internal(
537                                         &lcore_power_info[lcore_id], 0);
538                 else
539                         /* Set to max non-turbo */
540                         return set_freq_internal(
541                                         &lcore_power_info[lcore_id], 1);
542         } else
543                 return set_freq_internal(&lcore_power_info[lcore_id], 0);
544 }
545
546 int
547 power_acpi_cpufreq_freq_min(unsigned int lcore_id)
548 {
549         struct rte_power_info *pi;
550
551         if (lcore_id >= RTE_MAX_LCORE) {
552                 RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
553                 return -1;
554         }
555
556         pi = &lcore_power_info[lcore_id];
557
558         /* Frequencies in the array are from high to low. */
559         return set_freq_internal(pi, pi->nb_freqs - 1);
560 }
561
562
563 int
564 power_acpi_turbo_status(unsigned int lcore_id)
565 {
566         struct rte_power_info *pi;
567
568         if (lcore_id >= RTE_MAX_LCORE) {
569                 RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
570                 return -1;
571         }
572
573         pi = &lcore_power_info[lcore_id];
574
575         return pi->turbo_enable;
576 }
577
578
579 int
580 power_acpi_enable_turbo(unsigned int lcore_id)
581 {
582         struct rte_power_info *pi;
583
584         if (lcore_id >= RTE_MAX_LCORE) {
585                 RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
586                 return -1;
587         }
588
589         pi = &lcore_power_info[lcore_id];
590
591         if (pi->turbo_available)
592                 pi->turbo_enable = 1;
593         else {
594                 pi->turbo_enable = 0;
595                 RTE_LOG(ERR, POWER,
596                         "Failed to enable turbo on lcore %u\n",
597                         lcore_id);
598                         return -1;
599         }
600
601         /* Max may have changed, so call to max function */
602         if (power_acpi_cpufreq_freq_max(lcore_id) < 0) {
603                 RTE_LOG(ERR, POWER,
604                         "Failed to set frequency of lcore %u to max\n",
605                         lcore_id);
606                         return -1;
607         }
608
609         return 0;
610 }
611
612 int
613 power_acpi_disable_turbo(unsigned int lcore_id)
614 {
615         struct rte_power_info *pi;
616
617         if (lcore_id >= RTE_MAX_LCORE) {
618                 RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
619                 return -1;
620         }
621
622         pi = &lcore_power_info[lcore_id];
623
624          pi->turbo_enable = 0;
625
626         if ((pi->turbo_available) && (pi->curr_idx <= 1)) {
627                 /* Try to set freq to max by default coming out of turbo */
628                 if (power_acpi_cpufreq_freq_max(lcore_id) < 0) {
629                         RTE_LOG(ERR, POWER,
630                                 "Failed to set frequency of lcore %u to max\n",
631                                 lcore_id);
632                         return -1;
633                 }
634         }
635
636         return 0;
637 }
638
639 int power_acpi_get_capabilities(unsigned int lcore_id,
640                 struct rte_power_core_capabilities *caps)
641 {
642         struct rte_power_info *pi;
643
644         if (lcore_id >= RTE_MAX_LCORE) {
645                 RTE_LOG(ERR, POWER, "Invalid lcore ID\n");
646                 return -1;
647         }
648         if (caps == NULL) {
649                 RTE_LOG(ERR, POWER, "Invalid argument\n");
650                 return -1;
651         }
652
653         pi = &lcore_power_info[lcore_id];
654         caps->capabilities = 0;
655         caps->turbo = !!(pi->turbo_available);
656
657         return 0;
658 }