forked from torvalds/linux
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
cpupower: Introduce powercap intel-rapl library and powercap-info com…
…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
1 parent
eb70814
commit bdc275e
Showing
7 changed files
with
499 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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__ */ |
Oops, something went wrong.