power: refactor ACPI and intel_pstate support
[dpdk.git] / lib / power / power_common.c
index 67e3318..1e09fac 100644 (file)
@@ -3,19 +3,25 @@
  */
 
 #include <limits.h>
+#include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 
+#include <rte_log.h>
+#include <rte_string_fns.h>
+
 #include "power_common.h"
 
 #define POWER_SYSFILE_SCALING_DRIVER   \
                "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_driver"
+#define POWER_SYSFILE_GOVERNOR  \
+               "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_governor"
+#define POWER_CONVERT_TO_DECIMAL 10
 
 int
 cpufreq_check_scaling_driver(const char *driver_name)
 {
        unsigned int lcore_id = 0; /* always check core 0 */
-       char fullpath[PATH_MAX];
        char readbuf[PATH_MAX];
        size_t end_idx;
        char *s;
@@ -24,10 +30,8 @@ cpufreq_check_scaling_driver(const char *driver_name)
        /*
         * Check if scaling driver matches what we expect.
         */
-       snprintf(fullpath, sizeof(fullpath), POWER_SYSFILE_SCALING_DRIVER,
+       open_core_sysfs_file(&f, "r", POWER_SYSFILE_SCALING_DRIVER,
                        lcore_id);
-       f = fopen(fullpath, "r");
-
        /* if there's no driver at all, bail out */
        if (f == NULL)
                return 0;
@@ -58,3 +62,143 @@ cpufreq_check_scaling_driver(const char *driver_name)
         */
        return 1;
 }
+
+int
+open_core_sysfs_file(FILE **f, const char *mode, const char *format, ...)
+{
+       char fullpath[PATH_MAX];
+       va_list ap;
+       FILE *tmpf;
+
+       va_start(ap, format);
+       vsnprintf(fullpath, sizeof(fullpath), format, ap);
+       va_end(ap);
+       tmpf = fopen(fullpath, mode);
+       *f = tmpf;
+       if (tmpf == NULL)
+               return -1;
+
+       return 0;
+}
+
+int
+read_core_sysfs_u32(FILE *f, uint32_t *val)
+{
+       char buf[BUFSIZ];
+       uint32_t fval;
+       char *s;
+
+       s = fgets(buf, sizeof(buf), f);
+       if (s == NULL)
+               return -1;
+
+       /* fgets puts null terminator in, but do this just in case */
+       buf[BUFSIZ - 1] = '\0';
+
+       /* strip off any terminating newlines */
+       *strchrnul(buf, '\n') = '\0';
+
+       fval = strtoul(buf, NULL, POWER_CONVERT_TO_DECIMAL);
+
+       /* write the value */
+       *val = fval;
+
+       return 0;
+}
+
+int
+read_core_sysfs_s(FILE *f, char *buf, unsigned int len)
+{
+       char *s;
+
+       s = fgets(buf, len, f);
+       if (s == NULL)
+               return -1;
+
+       /* fgets puts null terminator in, but do this just in case */
+       buf[len - 1] = '\0';
+
+       /* strip off any terminating newlines */
+       *strchrnul(buf, '\n') = '\0';
+
+       return 0;
+}
+
+int
+write_core_sysfs_s(FILE *f, const char *str)
+{
+       int ret;
+
+       ret = fseek(f, 0, SEEK_SET);
+       if (ret != 0)
+               return -1;
+
+       ret = fputs(str, f);
+       if (ret < 0)
+               return -1;
+
+       /* flush the output */
+       ret = fflush(f);
+       if (ret != 0)
+               return -1;
+
+       return 0;
+}
+
+/**
+ * It is to check the current scaling governor by reading sys file, and then
+ * set it into 'performance' if it is not by writing the sys file. The original
+ * governor will be saved for rolling back.
+ */
+int
+power_set_governor(unsigned int lcore_id, const char *new_governor,
+               char *orig_governor, size_t orig_governor_len)
+{
+       FILE *f_governor = NULL;
+       int ret = -1;
+       char buf[BUFSIZ];
+
+       open_core_sysfs_file(&f_governor, "rw+", POWER_SYSFILE_GOVERNOR,
+                       lcore_id);
+       if (f_governor == NULL) {
+               RTE_LOG(ERR, POWER, "failed to open %s\n",
+                               POWER_SYSFILE_GOVERNOR);
+               goto out;
+       }
+
+       ret = read_core_sysfs_s(f_governor, buf, sizeof(buf));
+       if (ret < 0) {
+               RTE_LOG(ERR, POWER, "Failed to read %s\n",
+                               POWER_SYSFILE_GOVERNOR);
+               goto out;
+       }
+
+       /* Save the original governor, if it was provided */
+       if (orig_governor)
+               rte_strscpy(orig_governor, buf, orig_governor_len);
+
+       /* Check if current governor is already what we want */
+       if (strcmp(buf, new_governor) == 0) {
+               ret = 0;
+               POWER_DEBUG_TRACE("Power management governor of lcore %u is "
+                               "already %s\n", lcore_id, new_governor);
+               goto out;
+       }
+
+       /* Write the new governor */
+       ret = write_core_sysfs_s(f_governor, new_governor);
+       if (ret < 0) {
+               RTE_LOG(ERR, POWER, "Failed to write %s\n",
+                               POWER_SYSFILE_GOVERNOR);
+               goto out;
+       }
+
+       ret = 0;
+       RTE_LOG(INFO, POWER, "Power management governor of lcore %u has been "
+                       "set to '%s' successfully\n", lcore_id, new_governor);
+out:
+       if (f_governor != NULL)
+               fclose(f_governor);
+
+       return ret;
+}