Skip to content

Commit

Permalink
cpupower: Introduce powercap intel-rapl library and powercap-info com…
Browse files Browse the repository at this point in the history
…mand

Read out powercap zone information via:
cpupower powercap-info
and show the zone hierarchy to the user:

./cpupower powercap-info
Driver: intel-rapl
Powercap domain hierarchy:

Zone: package-0 (enabled)
Power consumption can be monitored in micro Watts

        Zone: core (disabled)
        Power consumption can be monitored in micro Watts

        Zone: uncore (disabled)
        Power consumption can be monitored in micro Watts

        Zone: dram (disabled)
        Power consumption can be monitored in micro Watts

There is a dummy -a option for powercap-info which can/should be used to
show more detailed info later. Like that other args can be added easily
later as well.

A enable/disable option via powercap-set subcommand is also an enhancement
for later.

Also not all RAPL domains are shown. The func walking through RAPL
subdomains is restricted and hardcoded to: "intel-rapl/intel-rapl:0"
On my system above powercap domains map to:
intel-rapl/intel-rapl:0
-> pack (age-0)
intel-rapl/intel-rapl:0/intel-rapl:0:0
-> core
intel-rapl/intel-rapl:0/intel-rapl:0:1
-> uncore

Missing ones on my system are:
intel-rapl-mmio/intel-rapl-mmio:0
-> pack (age-0)

intel-rapl/intel-rapl:1
-> psys

This could get enhanced in:
struct powercap_zone *powercap_init_zones()
and adopted to walk through all intel-rapl zones, but
also to other powercap drivers like dtpm
(Dynamic Thermal Power Management framework),
cmp with: drivers/powercap/dtpm_*

Signed-off-by: Thomas Renninger <trenn@suse.de>
CC: Shuah Khan <skhan@linuxfoundation.org>
  • Loading branch information
watologo1 authored and intel-lab-lkp committed Nov 23, 2022
1 parent eb70814 commit bdc275e
Show file tree
Hide file tree
Showing 7 changed files with 499 additions and 4 deletions.
14 changes: 10 additions & 4 deletions tools/power/cpupower/Makefile
Expand Up @@ -133,7 +133,7 @@ UTIL_OBJS = utils/helpers/amd.o utils/helpers/msr.o \
utils/idle_monitor/mperf_monitor.o utils/idle_monitor/cpupower-monitor.o \
utils/cpupower.o utils/cpufreq-info.o utils/cpufreq-set.o \
utils/cpupower-set.o utils/cpupower-info.o utils/cpuidle-info.o \
utils/cpuidle-set.o
utils/cpuidle-set.o utils/powercap-info.o

UTIL_SRC := $(UTIL_OBJS:.o=.c)

Expand All @@ -143,9 +143,12 @@ UTIL_HEADERS = utils/helpers/helpers.h utils/idle_monitor/cpupower-monitor.h \
utils/helpers/bitmask.h \
utils/idle_monitor/idle_monitors.h utils/idle_monitor/idle_monitors.def

LIB_HEADERS = lib/cpufreq.h lib/cpupower.h lib/cpuidle.h lib/acpi_cppc.h
LIB_SRC = lib/cpufreq.c lib/cpupower.c lib/cpuidle.c lib/acpi_cppc.c
LIB_OBJS = lib/cpufreq.o lib/cpupower.o lib/cpuidle.o lib/acpi_cppc.o
LIB_HEADERS = lib/cpufreq.h lib/cpupower.h lib/cpuidle.h lib/acpi_cppc.h \
lib/powercap.h
LIB_SRC = lib/cpufreq.c lib/cpupower.c lib/cpuidle.c lib/acpi_cppc.c \
lib/powercap.c
LIB_OBJS = lib/cpufreq.o lib/cpupower.o lib/cpuidle.o lib/acpi_cppc.o \
lib/powercap.o
LIB_OBJS := $(addprefix $(OUTPUT),$(LIB_OBJS))

override CFLAGS += -pipe
Expand Down Expand Up @@ -276,6 +279,7 @@ install-lib: libcpupower
$(INSTALL) -d $(DESTDIR)${includedir}
$(INSTALL_DATA) lib/cpufreq.h $(DESTDIR)${includedir}/cpufreq.h
$(INSTALL_DATA) lib/cpuidle.h $(DESTDIR)${includedir}/cpuidle.h
$(INSTALL_DATA) lib/powercap.h $(DESTDIR)${includedir}/powercap.h

install-tools: $(OUTPUT)cpupower
$(INSTALL) -d $(DESTDIR)${bindir}
Expand All @@ -292,6 +296,7 @@ install-man:
$(INSTALL_DATA) -D man/cpupower-set.1 $(DESTDIR)${mandir}/man1/cpupower-set.1
$(INSTALL_DATA) -D man/cpupower-info.1 $(DESTDIR)${mandir}/man1/cpupower-info.1
$(INSTALL_DATA) -D man/cpupower-monitor.1 $(DESTDIR)${mandir}/man1/cpupower-monitor.1
$(INSTALL_DATA) -D man/cpupower-powercap-info.1 $(DESTDIR)${mandir}/man1/cpupower-powercap-info.1

install-gmo: create-gmo
$(INSTALL) -d $(DESTDIR)${localedir}
Expand Down Expand Up @@ -321,6 +326,7 @@ uninstall:
- rm -f $(DESTDIR)${mandir}/man1/cpupower-set.1
- rm -f $(DESTDIR)${mandir}/man1/cpupower-info.1
- rm -f $(DESTDIR)${mandir}/man1/cpupower-monitor.1
- rm -f $(DESTDIR)${mandir}/man1/cpupower-powercap-info.1
- for HLANG in $(LANGUAGES); do \
rm -f $(DESTDIR)${localedir}/$$HLANG/LC_MESSAGES/cpupower.mo; \
done;
Expand Down
290 changes: 290 additions & 0 deletions 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;
}
54 changes: 54 additions & 0 deletions tools/power/cpupower/lib/powercap.h
@@ -0,0 +1,54 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* (C) 2016 SUSE Software Solutions GmbH
* Thomas Renninger <trenn@suse.de>
*/

#ifndef __CPUPOWER_RAPL_H__
#define __CPUPOWER_RAPL_H__

#define PATH_TO_POWERCAP "/sys/devices/virtual/powercap"
#define PATH_TO_RAPL "/sys/devices/virtual/powercap/intel-rapl"
#define PATH_TO_RAPL_CLASS "/sys/devices/virtual/powercap/intel-rapl"

#define POWERCAP_MAX_CHILD_ZONES 10
#define POWERCAP_MAX_TREE_DEPTH 10

#define MAX_LINE_LEN 4096
#define SYSFS_PATH_MAX 255

#include <stdint.h>

struct powercap_zone {
char name[MAX_LINE_LEN];
/*
* sys_name relative to PATH_TO_POWERCAP,
* do not forget the / in between
*/
char sys_name[SYSFS_PATH_MAX];
int tree_depth;
struct powercap_zone *parent;
struct powercap_zone *children[POWERCAP_MAX_CHILD_ZONES];
/* More possible caps or attributes to be added? */
uint32_t has_power_uw:1,
has_energy_uj:1;

};

int powercap_walk_zones(struct powercap_zone *zone,
int (*f)(struct powercap_zone *zone));

struct powercap_zone *powercap_init_zones(void);
int powercap_get_enabled(int *mode);
int powercap_set_enabled(int mode);
int powercap_get_driver(char *driver, int buflen);

int powercap_get_max_energy_range_uj(struct powercap_zone *zone, uint64_t *val);
int powercap_get_energy_uj(struct powercap_zone *zone, uint64_t *val);
int powercap_get_max_power_range_uw(struct powercap_zone *zone, uint64_t *val);
int powercap_get_power_uw(struct powercap_zone *zone, uint64_t *val);
int powercap_zone_get_enabled(struct powercap_zone *zone, int *mode);
int powercap_zone_set_enabled(struct powercap_zone *zone, int mode);


#endif /* __CPUPOWER_RAPL_H__ */

0 comments on commit bdc275e

Please sign in to comment.