diff options
Diffstat (limited to 'src/cpu/rapl/rapl_interface.cpp')
-rw-r--r-- | src/cpu/rapl/rapl_interface.cpp | 699 |
1 files changed, 699 insertions, 0 deletions
diff --git a/src/cpu/rapl/rapl_interface.cpp b/src/cpu/rapl/rapl_interface.cpp new file mode 100644 index 0000000..d6bd4c8 --- /dev/null +++ b/src/cpu/rapl/rapl_interface.cpp @@ -0,0 +1,699 @@ +/* rapl_interface.cpp: rapl interface for power top implementation + * + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * Author Name <Srinivas.Pandruvada@linux.intel.com> + * + */ +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <math.h> +#include <stdlib.h> +#include <dirent.h> +#include "lib.h" +#include "rapl_interface.h" + +#ifdef DEBUG +#define RAPL_DBG_PRINT printf +#define RAPL_ERROR_PRINT printf +#else +#define RAPL_DBG_PRINT(...) ((void) 0) +#define RAPL_ERROR_PRINT(...) ((void) 0) +#endif +#define RAPL_INFO_PRINT(format, m) fprintf(stderr, format, m) + +#define MAX_TEMP_STR_SIZE 20 + +// RAPL interface +#define MSR_RAPL_POWER_UNIT 0x606 +#define MSR_PKG_POWER_LIMIT 0x610 + +#define MSR_PKG_ENERY_STATUS 0x611 +#define MSR_PKG_POWER_INFO 0x614 +#define MSR_PKG_PERF_STATUS 0x613 + +#define MSR_DRAM_POWER_LIMIT 0x618 +#define MSR_DRAM_ENERY_STATUS 0x619 +#define MSR_DRAM_PERF_STATUS 0x61B +#define MSR_DRAM_POWER_INFO 0x61c + +#define MSR_PP0_POWER_LIMIT 0x638 +#define MSR_PP0_ENERY_STATUS 0x639 +#define MSR_PP0_POLICY 0x63A +#define MSR_PP0_PERF_STATUS 0x63B + +#define MSR_PP1_POWER_LIMIT 0x640 +#define MSR_PP1_ENERY_STATUS 0x641 +#define MSR_PP1_POLICY 0x642 + +#define PKG_DOMAIN_PRESENT 0x01 +#define DRAM_DOMAIN_PRESENT 0x02 +#define PP0_DOMAIN_PRESENT 0x04 +#define PP1_DOMAIN_PRESENT 0x08 + +c_rapl_interface::c_rapl_interface(const char *dev_name, int cpu) : + powercap_sysfs_present(false), + powercap_core_path(), + powercap_uncore_path(), + powercap_dram_path(), + first_cpu(cpu), + measurment_interval(def_sampling_interval), + last_pkg_energy_status(0.0), + last_dram_energy_status(0.0), + last_pp0_energy_status(0.0), + last_pp1_energy_status(0.0) +{ + uint64_t value; + int ret; + string package_path; + DIR *dir; + struct dirent *entry; + + RAPL_INFO_PRINT("RAPL device for cpu %d\n", cpu); + + rapl_domains = 0; + + if (dev_name) { + string base_path = "/sys/class/powercap/intel-rapl/"; + if ((dir = opendir(base_path.c_str())) != NULL) { + while ((entry = readdir(dir)) != NULL) { + string path = base_path + entry->d_name + "/name"; + string str = read_sysfs_string(path); + if (str.length() > 0) { + if (str == dev_name) { + package_path = base_path + entry->d_name + "/"; + powercap_sysfs_present = true; + rapl_domains |= PKG_DOMAIN_PRESENT; + break; + } + } + } + closedir(dir); + } + } + + if (powercap_sysfs_present) { + if ((dir = opendir(package_path.c_str())) != NULL) { + while ((entry = readdir(dir)) != NULL) { + string path = package_path + entry->d_name; + string str = read_sysfs_string(path + "/name"); + if (str.length() > 0) { + if (str == "core") { + rapl_domains |= PP0_DOMAIN_PRESENT; + powercap_core_path = path + "/"; + } + else if (str == "dram") { + rapl_domains |= DRAM_DOMAIN_PRESENT; + powercap_dram_path = path + "/"; + } + else if (str == "uncore") { + rapl_domains |= PP1_DOMAIN_PRESENT; + powercap_uncore_path = path + "/"; + } + } + } + closedir(dir); + } + + RAPL_INFO_PRINT("RAPL Using PowerCap Sysfs : Domain Mask %x\n", rapl_domains); + return; + } + + // Fallback to using MSRs + + // presence of each domain + // Check presence of PKG domain + ret = read_msr(first_cpu, MSR_PKG_ENERY_STATUS, &value); + if (ret > 0) { + rapl_domains |= PKG_DOMAIN_PRESENT; + RAPL_DBG_PRINT("Domain : PKG present\n"); + } else { + RAPL_DBG_PRINT("Domain : PKG Not present\n"); + } + + // Check presence of DRAM domain + ret = read_msr(first_cpu, MSR_DRAM_ENERY_STATUS, &value); + if (ret > 0) { + rapl_domains |= DRAM_DOMAIN_PRESENT; + RAPL_DBG_PRINT("Domain : DRAM present\n"); + } else { + RAPL_DBG_PRINT("Domain : DRAM Not present\n"); + } + + // Check presence of PP0 domain + ret = read_msr(first_cpu, MSR_PP0_ENERY_STATUS, &value); + if (ret > 0) { + rapl_domains |= PP0_DOMAIN_PRESENT; + RAPL_DBG_PRINT("Domain : PP0 present\n"); + } else { + RAPL_DBG_PRINT("Domain : PP0 Not present\n"); + } + + // Check presence of PP1 domain + ret = read_msr(first_cpu, MSR_PP1_ENERY_STATUS, &value); + if (ret > 0) { + rapl_domains |= PP1_DOMAIN_PRESENT; + RAPL_DBG_PRINT("Domain : PP1 present\n"); + } else { + RAPL_DBG_PRINT("Domain : PP1 Not present\n"); + } + + power_units = get_power_unit(); + energy_status_units = get_energy_status_unit(); + time_units = get_time_unit(); + + RAPL_DBG_PRINT("RAPL Domain mask: %x\n", rapl_domains); +} + +bool c_rapl_interface::pkg_domain_present() +{ + if ((rapl_domains & PKG_DOMAIN_PRESENT)) { + return true; + } + + return false; +} + +bool c_rapl_interface::dram_domain_present() +{ + if ((rapl_domains & DRAM_DOMAIN_PRESENT)) { + return true; + } + + return false; +} + +bool c_rapl_interface::pp0_domain_present() +{ + if ((rapl_domains & PP0_DOMAIN_PRESENT)) { + return true; + } + + return false; +} + +bool c_rapl_interface::pp1_domain_present() +{ + if ((rapl_domains & PP1_DOMAIN_PRESENT)) { + return true; + } + + return false; +} + +int c_rapl_interface::read_msr(int cpu, unsigned int idx, uint64_t *val) +{ + return ::read_msr(cpu, idx, val); +} + +int c_rapl_interface::write_msr(int cpu, unsigned int idx, uint64_t val) +{ + return ::write_msr(cpu, idx, val); +} + +int c_rapl_interface::get_rapl_power_unit(uint64_t *value) +{ + int ret; + + ret = read_msr(first_cpu, MSR_RAPL_POWER_UNIT, value); + + return ret; +} + +double c_rapl_interface::get_power_unit() +{ + int ret; + uint64_t value; + + ret = get_rapl_power_unit(&value); + if(ret < 0) + { + return ret; + } + + return (double) 1/pow((double)2, (double)(value & 0xf)); +} + +double c_rapl_interface::get_energy_status_unit() +{ + int ret; + uint64_t value; + + ret = get_rapl_power_unit(&value); + if(ret < 0) + { + return ret; + } + + return (double)1/ pow((double)2, (double)((value & 0x1f00) >> 8)); +} + +double c_rapl_interface::get_time_unit() +{ + int ret; + uint64_t value; + + ret = get_rapl_power_unit(&value); + if(ret < 0) + { + return ret; + } + + return (double)1 / pow((double)2, (double)((value & 0xf0000) >> 16)); +} + +int c_rapl_interface::get_pkg_energy_status(double *status) +{ + int ret; + uint64_t value; + + if (!pkg_domain_present()) { + return -1; + } + + ret = read_msr(first_cpu, MSR_PKG_ENERY_STATUS, &value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_pkg_energy_status failed\n"); + return ret; + } + + *status = (double) (value & 0xffffffff) * get_energy_status_unit(); + + return ret; +} + +int c_rapl_interface::get_pkg_power_info(double *thermal_spec_power, + double *max_power, double *min_power, double *max_time_window) +{ + int ret; + uint64_t value; + + if (!pkg_domain_present()) { + return -1; + } + ret = read_msr(first_cpu, MSR_PKG_POWER_INFO, &value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_pkg_power_info failed\n"); + return ret; + } + *thermal_spec_power = (value & 0x7FFF) * power_units; + *min_power = ((value & 0x7FFF0000) >> 16) * power_units; + *max_power = ((value & 0x7FFF00000000) >> 32) * power_units; + *max_time_window = ((value & 0x3f000000000000)>>48) * time_units; + + return ret; +} + +int c_rapl_interface::get_pkg_power_limit(uint64_t *value) +{ + int ret; + + if (!pkg_domain_present()) { + return -1; + } + + ret = read_msr(first_cpu, MSR_PKG_POWER_LIMIT, value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_pkg_power_limit failed\n"); + return ret; + } + + return ret; +} + +int c_rapl_interface::set_pkg_power_limit(uint64_t value) +{ + int ret; + + if (!pkg_domain_present()) { + return -1; + } + + ret = write_msr(first_cpu, MSR_PKG_POWER_LIMIT, value); + if(ret < 0) + { + RAPL_ERROR_PRINT("set_pkg_power_limit failed\n"); + return ret; + } + + return ret; +} + +int c_rapl_interface::get_dram_energy_status(double *status) +{ + int ret; + uint64_t value; + + if (!dram_domain_present()) { + return -1; + } + + if (powercap_sysfs_present) { + string str = read_sysfs_string(powercap_dram_path + "energy_uj"); + if (str.length() > 0) { + *status = atof(str.c_str()) / 1000000; // uj to Js + return 0; + } + + return -EINVAL; + } + + ret = read_msr(first_cpu, MSR_DRAM_ENERY_STATUS, &value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_dram_energy_status failed\n"); + return ret; + } + + *status = (double) (value & 0xffffffff) * get_energy_status_unit(); + + return ret; +} + +int c_rapl_interface::get_dram_power_info(double *thermal_spec_power, + double *max_power, double *min_power, double *max_time_window) +{ + int ret; + uint64_t value; + + if (!dram_domain_present()) { + return -1; + } + ret = read_msr(first_cpu, MSR_DRAM_POWER_INFO, &value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_dram_power_info failed\n"); + return ret; + } + + *thermal_spec_power = (value & 0x7FFF) * power_units; + *min_power = ((value & 0x7FFF0000) >> 16) * power_units; + *max_power = ((value & 0x7FFF00000000) >> 32) * power_units; + *max_time_window = ((value & 0x3f000000000000)>>48) * time_units; + + return ret; +} + +int c_rapl_interface::get_dram_power_limit(uint64_t *value) +{ + int ret; + + if (!dram_domain_present()) { + return -1; + } + + ret = read_msr(first_cpu, MSR_DRAM_POWER_LIMIT, value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_dram_power_limit failed\n"); + return ret; + } + + return ret; +} + +int c_rapl_interface::set_dram_power_limit(uint64_t value) +{ + int ret; + + if (!dram_domain_present()) { + return -1; + } + + ret = write_msr(first_cpu, MSR_DRAM_POWER_LIMIT, value); + if(ret < 0) + { + RAPL_ERROR_PRINT("set_dram_power_limit failed\n"); + return ret; + } + + return ret; +} + +int c_rapl_interface::get_pp0_energy_status(double *status) +{ + int ret; + uint64_t value; + + if (!pp0_domain_present()) { + return -1; + } + + if (powercap_sysfs_present) { + string str = read_sysfs_string(powercap_core_path + "energy_uj"); + if (str.length() > 0) { + *status = atof(str.c_str()) / 1000000; // uj to Js + return 0; + } + + return -EINVAL; + } + + ret = read_msr(first_cpu, MSR_PP0_ENERY_STATUS, &value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_pp0_energy_status failed\n"); + return ret; + } + + *status = (double) (value & 0xffffffff) * get_energy_status_unit(); + + return ret; +} + +int c_rapl_interface::get_pp0_power_limit(uint64_t *value) +{ + int ret; + + if (!pp0_domain_present()) { + return -1; + } + + ret = read_msr(first_cpu, MSR_PP0_POWER_LIMIT, value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_pp0_power_limit failed\n"); + return ret; + } + + return ret; +} + +int c_rapl_interface::set_pp0_power_limit(uint64_t value) +{ + int ret; + + if (!pp0_domain_present()) { + return -1; + } + + ret = write_msr(first_cpu, MSR_PP0_POWER_LIMIT, value); + if(ret < 0) + { + RAPL_ERROR_PRINT("set_pp0_power_limit failed\n"); + return ret; + } + + return ret; +} + +int c_rapl_interface::get_pp0_power_policy(unsigned int *pp0_power_policy) +{ + int ret; + uint64_t value; + + if (!pp0_domain_present()) { + return -1; + } + + ret = read_msr(first_cpu, MSR_PP0_POLICY, &value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_pp0_power_policy failed\n"); + return ret; + } + + *pp0_power_policy = value & 0x0f; + + return ret; +} + +int c_rapl_interface::get_pp1_energy_status(double *status) +{ + int ret; + uint64_t value; + + if (!pp1_domain_present()) { + return -1; + } + + if (powercap_sysfs_present) { + string str = read_sysfs_string(powercap_uncore_path + "energy_uj"); + if (str.length() > 0) { + *status = atof(str.c_str()) / 1000000; // uj to Js + return 0; + } + + return -EINVAL; + } + + ret = read_msr(first_cpu, MSR_PP1_ENERY_STATUS, &value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_pp1_energy_status failed\n"); + return ret; + } + + *status = (double) (value & 0xffffffff) * get_energy_status_unit(); + + return ret; +} + +int c_rapl_interface::get_pp1_power_limit(uint64_t *value) +{ + int ret; + + if (!pp1_domain_present()) { + return -1; + } + + ret = read_msr(first_cpu, MSR_PP1_POWER_LIMIT, value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_pp1_power_info failed\n"); + return ret; + } + + return ret; +} + +int c_rapl_interface::set_pp1_power_limit(uint64_t value) +{ + int ret; + + if (!pp1_domain_present()) { + return -1; + } + + ret = write_msr(first_cpu, MSR_PP1_POWER_LIMIT, value); + if(ret < 0) + { + RAPL_ERROR_PRINT("set_pp1_power_limit failed\n"); + return ret; + } + + return ret; +} + +int c_rapl_interface::get_pp1_power_policy(unsigned int *pp1_power_policy) +{ + int ret; + uint64_t value; + + if (!pp1_domain_present()) { + return -1; + } + + ret = read_msr(first_cpu, MSR_PP1_POLICY, &value); + if(ret < 0) + { + RAPL_ERROR_PRINT("get_pp1_power_policy failed\n"); + return ret; + } + + *pp1_power_policy = value & 0x0f; + + return ret; +} + +void c_rapl_interface::rapl_measure_energy() +{ +#ifdef RAPL_TEST_MODE + int ret; + double energy_status; + double thermal_spec_power; + double max_power; + double min_power; + double max_time_window; + double pkg_watts = 0; + double dram_watts = 0; + double pp0_watts = 0; + double pp1_watts = 0; + double pkg_joules = 0; + double dram_joules = 0; + double pp0_joules = 0; + double pp1_joules = 0; + + get_pkg_power_info(&thermal_spec_power, &max_power, &min_power, &max_time_window); + RAPL_DBG_PRINT("Pkg Power Info: Thermal spec %f watts, max %f watts, min %f watts, max time window %f seconds\n", thermal_spec_power, max_power, min_power, max_time_window); + get_dram_power_info(&thermal_spec_power, &max_power, &min_power, &max_time_window); + RAPL_DBG_PRINT("DRAM Power Info: Thermal spec %f watts, max %f watts, min %f watts, max time window %f seconds\n", thermal_spec_power, max_power, min_power, max_time_window); + + for (;;) { + if (pkg_domain_present()) { + ret = get_pkg_energy_status(&energy_status); + if (last_pkg_energy_status == 0) + last_pkg_energy_status = energy_status; + if (ret > 0) { + pkg_joules = energy_status; + pkg_watts = (energy_status-last_pkg_energy_status)/measurment_interval; + } + last_pkg_energy_status = energy_status; + } + if (dram_domain_present()) { + ret = get_dram_energy_status(&energy_status); + if (last_dram_energy_status == 0) + last_dram_energy_status = energy_status; + if (ret > 0){ + dram_joules = energy_status; + dram_watts = (energy_status-last_dram_energy_status)/measurment_interval; + } + last_dram_energy_status = energy_status; + } + if (pp0_domain_present()) { + ret = get_pp0_energy_status(&energy_status); + if (last_pp0_energy_status == 0) + last_pp0_energy_status = energy_status; + if (ret > 0){ + pp0_joules = energy_status; + pp0_watts = (energy_status-last_pp0_energy_status)/measurment_interval; + } + last_pp0_energy_status = energy_status; + } + if (pp1_domain_present()) { + ret = get_pp1_energy_status(&energy_status); + if (last_pp1_energy_status == 0) + last_pp1_energy_status = energy_status; + if (ret > 0){ + pp1_joules = energy_status; + pp1_watts = (energy_status-last_pp1_energy_status)/measurment_interval; + } + last_pp1_energy_status = energy_status; + } + RAPL_DBG_PRINT("%f, %f, %f, %f\n", pkg_watts, dram_watts, pp0_watts, pp1_watts); + sleep(measurment_interval); + } +#endif +} |