summaryrefslogtreecommitdiff
path: root/tools/power/cpupower/lib/powercap.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2022-12-05 19:47:07 +0300
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2022-12-05 19:47:07 +0300
commit2ed6b4c26669966f2905d9a3afcd487e15bfdbce (patch)
tree012a9c22b7d12815ecccf3ff6ec2c970d7400e17 /tools/power/cpupower/lib/powercap.c
parent76dcd734eca23168cb008912c0f69ff408905235 (diff)
parent8c37df3d635eac64a8fbb4b26672fb881e0cb673 (diff)
downloadlinux-2ed6b4c26669966f2905d9a3afcd487e15bfdbce.tar.xz
Merge tag 'linux-cpupower-6.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux
Pull cpupower utility updates for 6.2-rc1 from Shuah Khan: "This cpupower update for Linux 6.2-rc1 consists of: - enhancement to choose base_cpu to display default cpupower details instead of picking cpu 0 and failing show information when it is offline. This change ensure user will see power information on the cpu the tool runs on. - adds Georgian translation to cpupower documentation. - introduces powercap intel-rapl library, powercap-info command, and rapl monitor. This adds the ability to show the used power consumption in for each rapl domain" * tag 'linux-cpupower-6.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux: cpupower: rapl monitor - shows the used power consumption in uj for each rapl domain cpupower: Introduce powercap intel-rapl library and powercap-info command cpupower: Add Georgian translation tools/cpupower: Choose base_cpu to display default cpupower details
Diffstat (limited to 'tools/power/cpupower/lib/powercap.c')
-rw-r--r--tools/power/cpupower/lib/powercap.c290
1 files changed, 290 insertions, 0 deletions
diff --git a/tools/power/cpupower/lib/powercap.c b/tools/power/cpupower/lib/powercap.c
new file mode 100644
index 000000000000..0ce29ee4c2e4
--- /dev/null
+++ b/tools/power/cpupower/lib/powercap.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * (C) 2016 SUSE Software Solutions GmbH
+ * Thomas Renninger <trenn@suse.de>
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <dirent.h>
+
+#include "powercap.h"
+
+static unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen)
+{
+ int fd;
+ ssize_t numread;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return 0;
+
+ numread = read(fd, buf, buflen - 1);
+ if (numread < 1) {
+ close(fd);
+ return 0;
+ }
+
+ buf[numread] = '\0';
+ close(fd);
+
+ return (unsigned int) numread;
+}
+
+static int sysfs_get_enabled(char *path, int *mode)
+{
+ int fd;
+ char yes_no;
+
+ *mode = 0;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ if (read(fd, &yes_no, 1) != 1) {
+ close(fd);
+ return -1;
+ }
+
+ if (yes_no == '1') {
+ *mode = 1;
+ return 0;
+ } else if (yes_no == '0') {
+ return 0;
+ }
+ return -1;
+}
+
+int powercap_get_enabled(int *mode)
+{
+ char path[SYSFS_PATH_MAX] = PATH_TO_POWERCAP "/intel-rapl/enabled";
+
+ return sysfs_get_enabled(path, mode);
+}
+
+/*
+ * Hardcoded, because rapl is the only powercap implementation
+- * this needs to get more generic if more powercap implementations
+ * should show up
+ */
+int powercap_get_driver(char *driver, int buflen)
+{
+ char file[SYSFS_PATH_MAX] = PATH_TO_RAPL;
+
+ struct stat statbuf;
+
+ if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) {
+ driver = "";
+ return -1;
+ } else if (buflen > 10) {
+ strcpy(driver, "intel-rapl");
+ return 0;
+ } else
+ return -1;
+}
+
+enum powercap_get64 {
+ GET_ENERGY_UJ,
+ GET_MAX_ENERGY_RANGE_UJ,
+ GET_POWER_UW,
+ GET_MAX_POWER_RANGE_UW,
+ MAX_GET_64_FILES
+};
+
+static const char *powercap_get64_files[MAX_GET_64_FILES] = {
+ [GET_POWER_UW] = "power_uw",
+ [GET_MAX_POWER_RANGE_UW] = "max_power_range_uw",
+ [GET_ENERGY_UJ] = "energy_uj",
+ [GET_MAX_ENERGY_RANGE_UJ] = "max_energy_range_uj",
+};
+
+static int sysfs_powercap_get64_val(struct powercap_zone *zone,
+ enum powercap_get64 which,
+ uint64_t *val)
+{
+ char file[SYSFS_PATH_MAX] = PATH_TO_POWERCAP "/";
+ int ret;
+ char buf[MAX_LINE_LEN];
+
+ strcat(file, zone->sys_name);
+ strcat(file, "/");
+ strcat(file, powercap_get64_files[which]);
+
+ ret = sysfs_read_file(file, buf, MAX_LINE_LEN);
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ return -1;
+
+ *val = strtoll(buf, NULL, 10);
+ return 0;
+}
+
+int powercap_get_max_energy_range_uj(struct powercap_zone *zone, uint64_t *val)
+{
+ return sysfs_powercap_get64_val(zone, GET_MAX_ENERGY_RANGE_UJ, val);
+}
+
+int powercap_get_energy_uj(struct powercap_zone *zone, uint64_t *val)
+{
+ return sysfs_powercap_get64_val(zone, GET_ENERGY_UJ, val);
+}
+
+int powercap_get_max_power_range_uw(struct powercap_zone *zone, uint64_t *val)
+{
+ return sysfs_powercap_get64_val(zone, GET_MAX_POWER_RANGE_UW, val);
+}
+
+int powercap_get_power_uw(struct powercap_zone *zone, uint64_t *val)
+{
+ return sysfs_powercap_get64_val(zone, GET_POWER_UW, val);
+}
+
+int powercap_zone_get_enabled(struct powercap_zone *zone, int *mode)
+{
+ char path[SYSFS_PATH_MAX] = PATH_TO_POWERCAP;
+
+ if ((strlen(PATH_TO_POWERCAP) + strlen(zone->sys_name)) +
+ strlen("/enabled") + 1 >= SYSFS_PATH_MAX)
+ return -1;
+
+ strcat(path, "/");
+ strcat(path, zone->sys_name);
+ strcat(path, "/enabled");
+
+ return sysfs_get_enabled(path, mode);
+}
+
+int powercap_zone_set_enabled(struct powercap_zone *zone, int mode)
+{
+ /* To be done if needed */
+ return 0;
+}
+
+
+int powercap_read_zone(struct powercap_zone *zone)
+{
+ struct dirent *dent;
+ DIR *zone_dir;
+ char sysfs_dir[SYSFS_PATH_MAX] = PATH_TO_POWERCAP;
+ struct powercap_zone *child_zone;
+ char file[SYSFS_PATH_MAX] = PATH_TO_POWERCAP;
+ int i, ret = 0;
+ uint64_t val = 0;
+
+ strcat(sysfs_dir, "/");
+ strcat(sysfs_dir, zone->sys_name);
+
+ zone_dir = opendir(sysfs_dir);
+ if (zone_dir == NULL)
+ return -1;
+
+ strcat(file, "/");
+ strcat(file, zone->sys_name);
+ strcat(file, "/name");
+ sysfs_read_file(file, zone->name, MAX_LINE_LEN);
+ if (zone->parent)
+ zone->tree_depth = zone->parent->tree_depth + 1;
+ ret = powercap_get_energy_uj(zone, &val);
+ if (ret == 0)
+ zone->has_energy_uj = 1;
+ ret = powercap_get_power_uw(zone, &val);
+ if (ret == 0)
+ zone->has_power_uw = 1;
+
+ while ((dent = readdir(zone_dir)) != NULL) {
+ struct stat st;
+
+ if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
+ continue;
+
+ if (stat(dent->d_name, &st) != 0 || !S_ISDIR(st.st_mode))
+ if (fstatat(dirfd(zone_dir), dent->d_name, &st, 0) < 0)
+ continue;
+
+ if (strncmp(dent->d_name, "intel-rapl:", 11) != 0)
+ continue;
+
+ child_zone = calloc(1, sizeof(struct powercap_zone));
+ if (child_zone == NULL)
+ return -1;
+ for (i = 0; i < POWERCAP_MAX_CHILD_ZONES; i++) {
+ if (zone->children[i] == NULL) {
+ zone->children[i] = child_zone;
+ break;
+ }
+ if (i == POWERCAP_MAX_CHILD_ZONES - 1) {
+ free(child_zone);
+ fprintf(stderr, "Reached POWERCAP_MAX_CHILD_ZONES %d\n",
+ POWERCAP_MAX_CHILD_ZONES);
+ return -1;
+ }
+ }
+ strcpy(child_zone->sys_name, zone->sys_name);
+ strcat(child_zone->sys_name, "/");
+ strcat(child_zone->sys_name, dent->d_name);
+ child_zone->parent = zone;
+ if (zone->tree_depth >= POWERCAP_MAX_TREE_DEPTH) {
+ fprintf(stderr, "Maximum zone hierarchy depth[%d] reached\n",
+ POWERCAP_MAX_TREE_DEPTH);
+ ret = -1;
+ break;
+ }
+ powercap_read_zone(child_zone);
+ }
+ closedir(zone_dir);
+ return ret;
+}
+
+struct powercap_zone *powercap_init_zones(void)
+{
+ int enabled;
+ struct powercap_zone *root_zone;
+ int ret;
+ char file[SYSFS_PATH_MAX] = PATH_TO_RAPL "/enabled";
+
+ ret = sysfs_get_enabled(file, &enabled);
+
+ if (ret)
+ return NULL;
+
+ if (!enabled)
+ return NULL;
+
+ root_zone = calloc(1, sizeof(struct powercap_zone));
+ if (!root_zone)
+ return NULL;
+
+ strcpy(root_zone->sys_name, "intel-rapl/intel-rapl:0");
+
+ powercap_read_zone(root_zone);
+
+ return root_zone;
+}
+
+/* Call function *f on the passed zone and all its children */
+
+int powercap_walk_zones(struct powercap_zone *zone,
+ int (*f)(struct powercap_zone *zone))
+{
+ int i, ret;
+
+ if (!zone)
+ return -1;
+
+ ret = f(zone);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < POWERCAP_MAX_CHILD_ZONES; i++) {
+ if (zone->children[i] != NULL)
+ powercap_walk_zones(zone->children[i], f);
+ }
+ return 0;
+}