static _Bool initialized = 0;
#define GET_THREAD(thread_base, thread_no, core_no, pkg_no) \
- (thread_base + (pkg_no) * topo.num_cores_per_pkg * \
- topo.num_threads_per_core + \
- (core_no) * topo.num_threads_per_core + (thread_no))
+ (thread_base + \
+ (pkg_no) * topology.num_cores * topology.num_threads + \
+ (core_no) * topology.num_threads + \
+ (thread_no))
#define GET_CORE(core_base, core_no, pkg_no) \
- (core_base + (pkg_no) * topo.num_cores_per_pkg + (core_no))
+ (core_base + \
+ (pkg_no) * topology.num_cores + \
+ (core_no))
#define GET_PKG(pkg_base, pkg_no) (pkg_base + pkg_no)
-struct topo_params {
+struct cpu_topology {
+ int package_id;
+ int core_id;
+ _Bool first_core_in_package;
+ _Bool first_thread_in_core;
+};
+
+struct topology {
+ int max_cpu_id;
int num_packages;
- int num_cpus;
int num_cores;
- int max_cpu_num;
- int num_cores_per_pkg;
- int num_threads_per_core;
-} topo;
+ int num_threads;
+ struct cpu_topology *cpus;
+} topology;
struct timeval tv_even, tv_odd, tv_delta;
return 0;
}
+
/**********************************
* Looping function over all CPUs *
**********************************/
/*
* Check if a given cpu id is in our compiled list of existing CPUs
- *
- * CHECKME: Why do we need this?
*/
static int
cpu_is_not_present(int cpu)
/*
* Loop on all CPUs in topological order
*
- * Skip 'non-present' cpus (CHECKME: Why do we need this?)
+ * Skip non-present cpus
* Return the error code at the first error or 0
*/
static int __attribute__((warn_unused_result))
{
int retval, pkg_no, core_no, thread_no;
- for (pkg_no = 0; pkg_no < topo.num_packages; ++pkg_no) {
- for (core_no = 0; core_no < topo.num_cores_per_pkg; ++core_no) {
- for (thread_no = 0; thread_no <
- topo.num_threads_per_core; ++thread_no) {
+ for (pkg_no = 0; pkg_no < topology.num_packages; ++pkg_no) {
+ for (core_no = 0; core_no < topology.num_cores; ++core_no) {
+ for (thread_no = 0; thread_no < topology.num_threads; ++thread_no) {
struct thread_data *t;
struct core_data *c;
struct pkg_data *p;
/*
* Dedicated loop: Extract every data evolution for all CPU
*
+ * Skip non-present cpus
+ * Return the error code at the first error or 0
+ *
* Core data is shared for all threads in one core: extracted only for the first thread
* Package data is shared for all core in one package: extracted only for the first thread of the first core
*/
{
int retval, pkg_no, core_no, thread_no;
- for (pkg_no = 0; pkg_no < topo.num_packages; ++pkg_no) {
- for (core_no = 0; core_no < topo.num_cores_per_pkg; ++core_no) {
- for (thread_no = 0; thread_no <
- topo.num_threads_per_core; ++thread_no) {
+ for (pkg_no = 0; pkg_no < topology.num_packages; ++pkg_no) {
+ for (core_no = 0; core_no < topology.num_cores; ++core_no) {
+ for (thread_no = 0; thread_no < topology.num_threads; ++thread_no) {
struct thread_data *t_delta;
const struct thread_data *t_old, *t_new;
struct core_data *c_delta;
}
-static void
-free_all_buffers(void)
-{
- allocated = 0;
- initialized = 0;
-
- CPU_FREE(cpu_present_set);
- cpu_present_set = NULL;
- cpu_present_set = 0;
-
- CPU_FREE(cpu_affinity_set);
- cpu_affinity_set = NULL;
- cpu_affinity_setsize = 0;
-
- CPU_FREE(cpu_saved_affinity_set);
- cpu_saved_affinity_set = NULL;
- cpu_saved_affinity_setsize = 0;
-
- free(thread_even);
- free(core_even);
- free(package_even);
-
- thread_even = NULL;
- core_even = NULL;
- package_even = NULL;
-
- free(thread_odd);
- free(core_odd);
- free(package_odd);
-
- thread_odd = NULL;
- core_odd = NULL;
- package_odd = NULL;
-
- free(thread_delta);
- free(core_delta);
- free(package_delta);
-
- thread_delta = NULL;
- core_delta = NULL;
- package_delta = NULL;
-}
+/***************
+ * CPU Probing *
+ ***************/
/*
- * Parse a file containing a single int.
+ * MSR_IA32_TEMPERATURE_TARGET indicates the temperature where
+ * the Thermal Control Circuit (TCC) activates.
+ * This is usually equal to tjMax.
+ *
+ * Older processors do not have this MSR, so there we guess,
+ * but also allow conficuration over-ride with "TCCActivationTemp".
+ *
+ * Several MSR temperature values are in units of degrees-C
+ * below this value, including the Digital Thermal Sensor (DTS),
+ * Package Thermal Management Sensor (PTM), and thermal event thresholds.
*/
-static int __attribute__ ((format(printf,1,2)))
-parse_int_file(const char *fmt, ...)
+static int __attribute__((warn_unused_result))
+set_temperature_target(struct thread_data *t, struct core_data *c, struct pkg_data *p)
{
- va_list args;
- char path[PATH_MAX];
- FILE *filep;
- int value;
+ unsigned long long msr;
+ unsigned int target_c_local;
- va_start(args, fmt);
- vsnprintf(path, sizeof(path), fmt, args);
- va_end(args);
- filep = fopen(path, "r");
- if (!filep) {
- ERROR("%s: open failed", path);
- return -ERR_CANT_OPEN_FILE;
- }
- if (fscanf(filep, "%d", &value) != 1) {
- ERROR("%s: failed to parse number from file", path);
- return -ERR_CANT_READ_NUMBER;
- }
- fclose(filep);
- return value;
-}
+ /* tcc_activation_temp is used only for dts or ptm */
+ if (!(do_dts || do_ptm))
+ return 0;
-/*
- * cpu_is_first_sibling_in_core(cpu)
- * return 1 if given CPU is 1st HT sibling in the core
- */
-static int
-cpu_is_first_sibling_in_core(int cpu)
-{
- return cpu == parse_int_file("/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list", cpu);
-}
+ /* this is a per-package concept */
+ if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE) || !(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE))
+ return 0;
-/*
- * cpu_is_first_core_in_package(cpu)
- * return 1 if given CPU is 1st core in package
- */
-static int
-cpu_is_first_core_in_package(int cpu)
-{
- return cpu == parse_int_file("/sys/devices/system/cpu/cpu%d/topology/core_siblings_list", cpu);
-}
+ if (tcc_activation_temp != 0) {
+ p->tcc_activation_temp = tcc_activation_temp;
+ return 0;
+ }
-static int
-get_physical_package_id(int cpu)
-{
- return parse_int_file("/sys/devices/system/cpu/cpu%d/topology/physical_package_id", cpu);
-}
+ if (get_msr(t->cpu_id, MSR_IA32_TEMPERATURE_TARGET, &msr))
+ goto guess;
-static int
-get_core_id(int cpu)
-{
- return parse_int_file("/sys/devices/system/cpu/cpu%d/topology/core_id", cpu);
-}
+ target_c_local = (msr >> 16) & 0xFF;
-static int
-get_num_ht_siblings(int cpu)
-{
- char path[80];
- FILE *filep;
- int sib1, sib2;
- int matches;
- char character;
+ if (!target_c_local)
+ goto guess;
- ssnprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list", cpu);
- filep = fopen(path, "r");
- if (!filep) {
- ERROR("%s: open failed", path);
- return -ERR_CANT_OPEN_FILE;
- }
- /*
- * file format:
- * if a pair of number with a character between: 2 siblings (eg. 1-2, or 1,4)
- * otherwinse 1 sibling (self).
- */
- matches = fscanf(filep, "%d%c%d\n", &sib1, &character, &sib2);
+ p->tcc_activation_temp = target_c_local;
- fclose(filep);
+ return 0;
- if (matches == 3)
- return 2;
- else
- return 1;
+guess:
+ p->tcc_activation_temp = TJMAX_DEFAULT;
+ WARNING("cpu%d: Guessing tjMax %d C, Please use TCCActivationTemp to specify",
+ t->cpu_id, p->tcc_activation_temp);
+
+ return 0;
}
/*
- * run func(cpu) on every cpu in /proc/stat
- * return max_cpu number
+ * Identify the functionality of the CPU
*/
static int __attribute__((warn_unused_result))
-for_all_proc_cpus(int (func)(int))
+probe_cpu()
{
- FILE *fp;
- int cpu_num;
- int retval;
-
- fp = fopen("/proc/stat", "r");
- if (!fp) {
- ERROR("Failed to open /proc/stat");
- return -ERR_CANT_OPEN_FILE;
- }
+ unsigned int eax, ebx, ecx, edx, max_level;
+ unsigned int fms, family, model;
- retval = fscanf(fp, "cpu %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n");
- if (retval != 0) {
- ERROR("Failed to parse /proc/stat");
- fclose(fp);
- return -ERR_CANT_READ_PROC_STAT;
+ /* CPUID(0):
+ * - EAX: Maximum Input Value for Basic CPUID Information
+ * - EBX: "Genu" (0x756e6547)
+ * - EDX: "ineI" (0x49656e69)
+ * - ECX: "ntel" (0x6c65746e)
+ */
+ max_level = ebx = ecx = edx = 0;
+ __get_cpuid(0, &max_level, &ebx, &ecx, &edx);
+ if (ebx != 0x756e6547 && edx != 0x49656e69 && ecx != 0x6c65746e) {
+ ERROR("Unsupported CPU");
+ return -UNSUPPORTED_CPU;
}
- while (1) {
- retval = fscanf(fp, "cpu%u %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n", &cpu_num);
- if (retval != 1)
- break;
-
- retval = func(cpu_num);
- if (retval) {
- fclose(fp);
- return(retval);
- }
+ /* CPUID(1):
+ * - EAX: Version Information: Type, Family, Model, and Stepping ID
+ * + 4-7: Model ID
+ * + 8-11: Family ID
+ * + 12-13: Processor type
+ * + 16-19: Extended Model ID
+ * + 20-27: Extended Family ID
+ * - EDX: Feature Information:
+ * + 5: Support for MSR read/write operations
+ */
+ fms = ebx = ecx = edx = 0;
+ __get_cpuid(1, &fms, &ebx, &ecx, &edx);
+ family = (fms >> 8) & 0xf;
+ model = (fms >> 4) & 0xf;
+ if (family == 0xf)
+ family += (fms >> 20) & 0xf;
+ if (family == 6 || family == 0xf)
+ model += ((fms >> 16) & 0xf) << 4;
+ if (!(edx & (1 << 5))) {
+ ERROR("CPUID: no MSR");
+ return -ERR_NO_MSR;
}
- fclose(fp);
- return 0;
-}
-
-/*
- * count_cpus()
- * remember the last one seen, it will be the max
- */
-static int
-count_cpus(int cpu)
-{
- if (topo.max_cpu_num < cpu)
- topo.max_cpu_num = cpu;
-
- topo.num_cpus += 1;
- return 0;
-}
-static int
-mark_cpu_present(int cpu)
-{
- CPU_SET_S(cpu, cpu_present_setsize, cpu_present_set);
- return 0;
-}
-
-
-static int setup_all_buffers(void);
-
-static int
-turbostat_read(user_data_t * not_used)
-{
- int ret;
- if (!allocated) {
- if ((ret = setup_all_buffers()) < 0)
- return ret;
- }
-
- if (for_all_proc_cpus(cpu_is_not_present)) {
- free_all_buffers();
- if ((ret = setup_all_buffers()) < 0)
- return ret;
- if (for_all_proc_cpus(cpu_is_not_present))
- return -ERR_CPU_NOT_PRESENT;
- }
-
- /* Saving the scheduling affinity, as it will be modified by get_counters */
- if (sched_getaffinity(0, cpu_saved_affinity_setsize, cpu_saved_affinity_set) != 0)
- return -ERR_CPU_SAVE_SCHED_AFFINITY;
-
- if (!initialized) {
- if ((ret = for_all_cpus(get_counters, EVEN_COUNTERS)) < 0)
- goto out;
- gettimeofday(&tv_even, (struct timezone *)NULL);
- is_even = 1;
- initialized = 1;
- ret = 0;
- goto out;
- }
-
- if (is_even) {
- if ((ret = for_all_cpus(get_counters, ODD_COUNTERS)) < 0)
- goto out;
- gettimeofday(&tv_odd, (struct timezone *)NULL);
- is_even = 0;
- timersub(&tv_odd, &tv_even, &tv_delta);
- if ((ret = for_all_cpus_delta(ODD_COUNTERS, EVEN_COUNTERS)) < 0)
- goto out;
- if ((ret = for_all_cpus(submit_counters, DELTA_COUNTERS)) < 0)
- goto out;
- } else {
- if ((ret = for_all_cpus(get_counters, EVEN_COUNTERS)) < 0)
- goto out;
- gettimeofday(&tv_even, (struct timezone *)NULL);
- is_even = 1;
- timersub(&tv_even, &tv_odd, &tv_delta);
- if ((ret = for_all_cpus_delta(EVEN_COUNTERS, ODD_COUNTERS)) < 0)
- goto out;
- if ((ret = for_all_cpus(submit_counters, DELTA_COUNTERS)) < 0)
- goto out;
- }
- ret = 0;
-out:
- /*
- * Let's restore the affinity
- * This might fail if the number of CPU changed, but we can't do anything in that case..
- */
- (void)sched_setaffinity(0, cpu_saved_affinity_setsize, cpu_saved_affinity_set);
- return ret;
-}
-
-static int __attribute__((warn_unused_result))
-check_dev_msr()
-{
- struct stat sb;
-
- if (stat("/dev/cpu/0/msr", &sb)) {
- ERROR("no /dev/cpu/0/msr, try \"# modprobe msr\"");
- return -ERR_NO_MSR;
- }
- return 0;
-}
-
-static int __attribute__((warn_unused_result))
-check_super_user()
-{
- if (getuid() != 0) {
- ERROR("must be root");
- return -ERR_NOT_ROOT;
- }
- return 0;
-}
-
-/*
- * MSR_IA32_TEMPERATURE_TARGET indicates the temperature where
- * the Thermal Control Circuit (TCC) activates.
- * This is usually equal to tjMax.
- *
- * Older processors do not have this MSR, so there we guess,
- * but also allow conficuration over-ride with "TCCActivationTemp".
- *
- * Several MSR temperature values are in units of degrees-C
- * below this value, including the Digital Thermal Sensor (DTS),
- * Package Thermal Management Sensor (PTM), and thermal event thresholds.
- */
-static int __attribute__((warn_unused_result))
-set_temperature_target(struct thread_data *t, struct core_data *c, struct pkg_data *p)
-{
- unsigned long long msr;
- unsigned int target_c_local;
-
- /* tcc_activation_temp is used only for dts or ptm */
- if (!(do_dts || do_ptm))
- return 0;
-
- /* this is a per-package concept */
- if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE) || !(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE))
- return 0;
-
- if (tcc_activation_temp != 0) {
- p->tcc_activation_temp = tcc_activation_temp;
- return 0;
- }
-
- if (get_msr(t->cpu_id, MSR_IA32_TEMPERATURE_TARGET, &msr))
- goto guess;
-
- target_c_local = (msr >> 16) & 0xFF;
-
- if (!target_c_local)
- goto guess;
-
- p->tcc_activation_temp = target_c_local;
-
- return 0;
-
-guess:
- p->tcc_activation_temp = TJMAX_DEFAULT;
- WARNING("cpu%d: Guessing tjMax %d C, Please use TCCActivationTemp to specify",
- t->cpu_id, p->tcc_activation_temp);
-
- return 0;
-}
-
-/*
- * Identify the functionality of the CPU
- */
-static int __attribute__((warn_unused_result))
-probe_cpu()
-{
- unsigned int eax, ebx, ecx, edx, max_level;
- unsigned int fms, family, model;
-
- /* CPUID(0):
- * - EAX: Maximum Input Value for Basic CPUID Information
- * - EBX: "Genu" (0x756e6547)
- * - EDX: "ineI" (0x49656e69)
- * - ECX: "ntel" (0x6c65746e)
- */
- max_level = ebx = ecx = edx = 0;
- __get_cpuid(0, &max_level, &ebx, &ecx, &edx);
- if (ebx != 0x756e6547 && edx != 0x49656e69 && ecx != 0x6c65746e) {
- ERROR("Unsupported CPU");
- return -UNSUPPORTED_CPU;
- }
-
- /* CPUID(1):
- * - EAX: Version Information: Type, Family, Model, and Stepping ID
- * + 4-7: Model ID
- * + 8-11: Family ID
- * + 12-13: Processor type
- * + 16-19: Extended Model ID
- * + 20-27: Extended Family ID
- * - EDX: Feature Information:
- * + 5: Support for MSR read/write operations
- */
- fms = ebx = ecx = edx = 0;
- __get_cpuid(1, &fms, &ebx, &ecx, &edx);
- family = (fms >> 8) & 0xf;
- model = (fms >> 4) & 0xf;
- if (family == 0xf)
- family += (fms >> 20) & 0xf;
- if (family == 6 || family == 0xf)
- model += ((fms >> 16) & 0xf) << 4;
- if (!(edx & (1 << 5))) {
- ERROR("CPUID: no MSR");
- return -ERR_NO_MSR;
- }
-
- /*
- * CPUID(0x80000000):
- * - EAX: Maximum Input Value for Extended Function CPUID Information
- *
- * This allows us to verify if the CPUID(0x80000007) can be called
- *
- * This check is valid for both Intel and AMD.
- */
- max_level = ebx = ecx = edx = 0;
- __get_cpuid(0x80000000, &max_level, &ebx, &ecx, &edx);
- if (max_level < 0x80000007) {
- ERROR("CPUID: no invariant TSC (max_level 0x%x)", max_level);
- return -ERR_NO_INVARIANT_TSC;
+ /*
+ * CPUID(0x80000000):
+ * - EAX: Maximum Input Value for Extended Function CPUID Information
+ *
+ * This allows us to verify if the CPUID(0x80000007) can be called
+ *
+ * This check is valid for both Intel and AMD.
+ */
+ max_level = ebx = ecx = edx = 0;
+ __get_cpuid(0x80000000, &max_level, &ebx, &ecx, &edx);
+ if (max_level < 0x80000007) {
+ ERROR("CPUID: no invariant TSC (max_level 0x%x)", max_level);
+ return -ERR_NO_INVARIANT_TSC;
}
/*
}
+/********************
+ * Topology Probing *
+ ********************/
-static int __attribute__((warn_unused_result))
-topology_probe()
+/*
+ * Read a single int from a file.
+ */
+static int __attribute__ ((format(printf,1,2)))
+parse_int_file(const char *fmt, ...)
{
- int i;
- int ret;
- int max_core_id = 0;
- int max_package_id = 0;
- int max_siblings = 0;
- struct cpu_topology {
- int core_id;
- int physical_package_id;
- } *cpus;
-
- /* Initialize num_cpus, max_cpu_num */
- topo.num_cpus = 0;
- topo.max_cpu_num = 0;
- ret = for_all_proc_cpus(count_cpus);
- if (ret < 0)
- return ret;
-
- DEBUG("num_cpus %d max_cpu_num %d\n", topo.num_cpus, topo.max_cpu_num);
-
- cpus = calloc(1, (topo.max_cpu_num + 1) * sizeof(struct cpu_topology));
- if (cpus == NULL) {
- ERROR("calloc cpus");
- return -ERR_CALLOC;
+ va_list args;
+ char path[PATH_MAX];
+ FILE *filep;
+ int value;
+
+ va_start(args, fmt);
+ vsnprintf(path, sizeof(path), fmt, args);
+ va_end(args);
+ filep = fopen(path, "r");
+ if (!filep) {
+ ERROR("%s: open failed", path);
+ return -ERR_CANT_OPEN_FILE;
}
+ if (fscanf(filep, "%d", &value) != 1) {
+ ERROR("%s: failed to parse number from file", path);
+ return -ERR_CANT_READ_NUMBER;
+ }
+ fclose(filep);
+ return value;
+}
+
+static int
+get_threads_on_core(int cpu)
+{
+ char path[80];
+ FILE *filep;
+ int sib1, sib2;
+ int matches;
+ char character;
+ ssnprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list", cpu);
+ filep = fopen(path, "r");
+ if (!filep) {
+ ERROR("%s: open failed", path);
+ return -ERR_CANT_OPEN_FILE;
+ }
/*
- * Allocate and initialize cpu_present_set
+ * file format:
+ * if a pair of number with a character between: 2 siblings (eg. 1-2, or 1,4)
+ * otherwinse 1 sibling (self).
*/
- cpu_present_set = CPU_ALLOC((topo.max_cpu_num + 1));
- if (cpu_present_set == NULL) {
- free(cpus);
- ERROR("CPU_ALLOC");
- return -ERR_CPU_ALLOC;
+ matches = fscanf(filep, "%d%c%d\n", &sib1, &character, &sib2);
+
+ fclose(filep);
+
+ if (matches == 3)
+ return 2;
+ else
+ return 1;
+}
+
+/*
+ * run func(cpu) on every cpu in /proc/stat
+ * return max_cpu number
+ */
+static int __attribute__((warn_unused_result))
+for_all_proc_cpus(int (func)(int))
+{
+ FILE *fp;
+ int cpu_num;
+ int retval;
+
+ fp = fopen("/proc/stat", "r");
+ if (!fp) {
+ ERROR("Failed to open /proc/stat");
+ return -ERR_CANT_OPEN_FILE;
+ }
+
+ retval = fscanf(fp, "cpu %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n");
+ if (retval != 0) {
+ ERROR("Failed to parse /proc/stat");
+ fclose(fp);
+ return -ERR_CANT_READ_PROC_STAT;
}
- cpu_present_setsize = CPU_ALLOC_SIZE((topo.max_cpu_num + 1));
- CPU_ZERO_S(cpu_present_setsize, cpu_present_set);
- ret = for_all_proc_cpus(mark_cpu_present);
- if (ret < 0) {
- free(cpus);
- return ret;
+
+ while (1) {
+ retval = fscanf(fp, "cpu%u %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n", &cpu_num);
+ if (retval != 1)
+ break;
+
+ retval = func(cpu_num);
+ if (retval) {
+ fclose(fp);
+ return(retval);
+ }
}
+ fclose(fp);
+ return 0;
+}
- /*
- * Allocate and initialize cpu_affinity_set
- */
- cpu_affinity_set = CPU_ALLOC((topo.max_cpu_num + 1));
- if (cpu_affinity_set == NULL) {
- free(cpus);
- ERROR("CPU_ALLOC");
+/*
+ * Update the stored topology.max_cpu_id
+ */
+static int
+update_max_cpu_id(int cpu)
+{
+ if (topology.max_cpu_id < cpu)
+ topology.max_cpu_id = cpu;
+ return 0;
+}
+
+static int
+mark_cpu_present(int cpu)
+{
+ CPU_SET_S(cpu, cpu_present_setsize, cpu_present_set);
+ return 0;
+}
+
+static int __attribute__((warn_unused_result))
+allocate_cpu_set(cpu_set_t ** set, size_t * size) {
+ *set = CPU_ALLOC(topology.max_cpu_id + 1);
+ if (*set == NULL) {
+ ERROR("Unable to allocate CPU state");
return -ERR_CPU_ALLOC;
}
- cpu_affinity_setsize = CPU_ALLOC_SIZE((topo.max_cpu_num + 1));
- CPU_ZERO_S(cpu_affinity_setsize, cpu_affinity_set);
+ *size = CPU_ALLOC_SIZE(topology.max_cpu_id + 1);
+ CPU_ZERO_S(*size, *set);
+ return 0;
+}
+/*
+ * Build a local representation of the cpu distribution
+ */
+static int __attribute__((warn_unused_result))
+topology_probe()
+{
+ int i;
+ int ret;
+ int max_package_id, max_core_id, max_thread_id;
+ max_package_id = max_core_id = max_thread_id = 0;
- /*
- * Allocate and initialize cpu_saved_affinity_set
- */
- cpu_saved_affinity_set = CPU_ALLOC((topo.max_cpu_num + 1));
- if (cpu_saved_affinity_set == NULL) {
- free(cpus);
- ERROR("CPU_ALLOC");
- return -ERR_CPU_ALLOC;
+ /* Clean topology */
+ free(topology.cpus);
+ memset(&topology, 0, sizeof(topology));
+
+ ret = for_all_proc_cpus(update_max_cpu_id);
+ if (ret != 0)
+ goto err;
+
+ topology.cpus = calloc(1, (topology.max_cpu_id + 1) * sizeof(struct cpu_topology));
+ if (topology.cpus == NULL) {
+ ERROR("Unable to allocate memory for cpu topology");
+ return -ERR_CALLOC;
}
- cpu_saved_affinity_setsize = CPU_ALLOC_SIZE((topo.max_cpu_num + 1));
- CPU_ZERO_S(cpu_saved_affinity_setsize, cpu_saved_affinity_set);
+ ret = allocate_cpu_set(&cpu_present_set, &cpu_present_setsize);
+ if (ret != 0)
+ goto err;
+ ret = allocate_cpu_set(&cpu_affinity_set, &cpu_affinity_setsize);
+ if (ret != 0)
+ goto err;
+ ret = allocate_cpu_set(&cpu_saved_affinity_set, &cpu_saved_affinity_setsize);
+ if (ret != 0)
+ goto err;
+
+ ret = for_all_proc_cpus(mark_cpu_present);
+ if (ret != 0)
+ goto err;
/*
* For online cpus
* find max_core_id, max_package_id
*/
- for (i = 0; i <= topo.max_cpu_num; ++i) {
- int siblings;
+ for (i = 0; i <= topology.max_cpu_id; ++i) {
+ int num_threads;
+ struct cpu_topology *cpu = &topology.cpus[i];
if (cpu_is_not_present(i)) {
WARNING("cpu%d NOT PRESENT", i);
continue;
}
- cpus[i].core_id = get_core_id(i);
- if (cpus[i].core_id < 0)
- return cpus[i].core_id;
- if (cpus[i].core_id > max_core_id)
- max_core_id = cpus[i].core_id;
-
- cpus[i].physical_package_id = get_physical_package_id(i);
- if (cpus[i].physical_package_id < 0)
- return cpus[i].physical_package_id;
- if (cpus[i].physical_package_id > max_package_id)
- max_package_id = cpus[i].physical_package_id;
-
- siblings = get_num_ht_siblings(i);
- if (siblings < 0)
- return siblings;
- if (siblings > max_siblings)
- max_siblings = siblings;
- DEBUG("cpu %d pkg %d core %d\n",
- i, cpus[i].physical_package_id, cpus[i].core_id);
- }
- topo.num_cores_per_pkg = max_core_id + 1;
- DEBUG("max_core_id %d, sizing for %d cores per package\n",
- max_core_id, topo.num_cores_per_pkg);
- topo.num_packages = max_package_id + 1;
- DEBUG("max_package_id %d, sizing for %d packages\n",
- max_package_id, topo.num_packages);
+ ret = parse_int_file("/sys/devices/system/cpu/cpu%d/topology/physical_package_id", i);
+ if (ret < 0)
+ goto err;
+ else
+ cpu->package_id = ret;
+ if (cpu->package_id > max_package_id)
+ max_package_id = cpu->package_id;
+
+ ret = parse_int_file("/sys/devices/system/cpu/cpu%d/topology/core_id", i);
+ if (ret < 0)
+ goto err;
+ else
+ cpu->core_id = ret;
+ if (cpu->core_id > max_core_id)
+ max_core_id = cpu->core_id;
+ ret = parse_int_file("/sys/devices/system/cpu/cpu%d/topology/core_siblings_list", i);
+ if (ret < 0)
+ goto err;
+ else if (ret == i)
+ cpu->first_core_in_package = 1;
+
+ ret = get_threads_on_core(i);
+ if (ret < 0)
+ goto err;
+ else
+ num_threads = ret;
+ if (num_threads > max_thread_id)
+ max_thread_id = num_threads;
+ ret = parse_int_file("/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list", i);
+ if (ret < 0)
+ goto err;
+ else if (ret == i)
+ cpu->first_thread_in_core = 1;
- topo.num_threads_per_core = max_siblings;
- DEBUG("max_siblings %d\n", max_siblings);
+ DEBUG("cpu %d pkg %d core %d\n",
+ i, cpu->package_id, cpu->core_id);
+ }
+ /* Num is max + 1 (need to count 0) */
+ topology.num_packages = max_package_id + 1;
+ topology.num_cores = max_core_id + 1;
+ topology.num_threads = max_thread_id + 1;
- free(cpus);
return 0;
+err:
+ free(topology.cpus);
+ return ret;
}
+
+/************************
+ * Main alloc/init/free *
+ ************************/
+
static int
-allocate_counters(struct thread_data **t, struct core_data **c, struct pkg_data **p)
+allocate_counters(struct thread_data **threads, struct core_data **cores, struct pkg_data **packages)
{
int i;
+ int total_threads, total_cores;
- *t = calloc(topo.num_threads_per_core * topo.num_cores_per_pkg *
- topo.num_packages, sizeof(struct thread_data));
- if (*t == NULL)
- goto error;
+ total_threads = topology.num_threads * topology.num_cores * topology.num_packages;
+ *threads = calloc(total_threads, sizeof(struct thread_data));
+ if (*threads == NULL)
+ goto err;
- for (i = 0; i < topo.num_threads_per_core *
- topo.num_cores_per_pkg * topo.num_packages; i++)
- (*t)[i].cpu_id = -1;
+ for (i = 0; i < total_threads; ++i)
+ (*threads)[i].cpu_id = -1;
- *c = calloc(topo.num_cores_per_pkg * topo.num_packages,
- sizeof(struct core_data));
- if (*c == NULL)
- goto error;
+ total_cores = topology.num_cores * topology.num_packages;
+ *cores = calloc(total_cores, sizeof(struct core_data));
+ if (*cores == NULL)
+ goto err_clean_threads;
- for (i = 0; i < topo.num_cores_per_pkg * topo.num_packages; i++)
- (*c)[i].core_id = -1;
+ for (i = 0; i < total_cores; ++i)
+ (*cores)[i].core_id = -1;
- *p = calloc(topo.num_packages, sizeof(struct pkg_data));
- if (*p == NULL)
- goto error;
+ *packages = calloc(topology.num_packages, sizeof(struct pkg_data));
+ if (*packages == NULL)
+ goto err_clean_cores;
- for (i = 0; i < topo.num_packages; i++)
- (*p)[i].package_id = i;
+ for (i = 0; i < topology.num_packages; i++)
+ (*packages)[i].package_id = i;
return 0;
-error:
+
+err_clean_cores:
+ free(*cores);
+err_clean_threads:
+ free(*threads);
+err:
ERROR("calloc counters");
return -ERR_CALLOC;
}
-/*
- * init_counter()
- *
- * set cpu_id, core_num, pkg_num
- * set FIRST_THREAD_IN_CORE and FIRST_CORE_IN_PACKAGE
- *
- * increment topo.num_cores when 1st core in pkg seen
- */
+
static int
init_counter(struct thread_data *thread_base, struct core_data *core_base,
- struct pkg_data *pkg_base, int thread_num, int core_num,
- int pkg_num, int cpu_id)
+ struct pkg_data *pkg_base, int cpu_id)
{
- int ret;
struct thread_data *t;
struct core_data *c;
struct pkg_data *p;
+ struct cpu_topology *cpu = &topology.cpus[cpu_id];
- t = GET_THREAD(thread_base, thread_num, core_num, pkg_num);
- c = GET_CORE(core_base, core_num, pkg_num);
- p = GET_PKG(pkg_base, pkg_num);
+ t = GET_THREAD(thread_base, !(cpu->first_thread_in_core), cpu->core_id, cpu->package_id);
+ c = GET_CORE(core_base, cpu->core_id, cpu->package_id);
+ p = GET_PKG(pkg_base, cpu->package_id);
t->cpu_id = cpu_id;
- if (thread_num == 0) {
+ if (cpu->first_thread_in_core)
t->flags |= CPU_IS_FIRST_THREAD_IN_CORE;
- if ((ret = cpu_is_first_core_in_package(cpu_id)) < 0) {
- return ret;
- } else if (ret != 0) {
- t->flags |= CPU_IS_FIRST_CORE_IN_PACKAGE;
- }
- }
+ if (cpu->first_core_in_package)
+ t->flags |= CPU_IS_FIRST_CORE_IN_PACKAGE;
- c->core_id = core_num;
- p->package_id = pkg_num;
+ c->core_id = cpu->core_id;
+ p->package_id = cpu->package_id;
return 0;
}
-
static int
-initialize_counters(int cpu_id)
+initialize_counters(void)
{
- int my_thread_id, my_core_id, my_package_id;
int ret;
+ int cpu_id;
- my_package_id = get_physical_package_id(cpu_id);
- if (my_package_id < 0)
- return my_package_id;
- my_core_id = get_core_id(cpu_id);
- if (my_core_id < 0)
- return my_core_id;
-
- if ((ret = cpu_is_first_sibling_in_core(cpu_id)) < 0) {
- return ret;
- } else if (ret != 0) {
- my_thread_id = 0;
- topo.num_cores++;
- } else {
- my_thread_id = 1;
- }
+ for (cpu_id = 0; cpu_id <= topology.max_cpu_id; ++cpu_id) {
+ if (cpu_is_not_present(cpu_id)) {
+ continue;
+ }
- ret = init_counter(EVEN_COUNTERS, my_thread_id, my_core_id, my_package_id, cpu_id);
- if (ret < 0)
- return ret;
- ret = init_counter(ODD_COUNTERS, my_thread_id, my_core_id, my_package_id, cpu_id);
- if (ret < 0)
- return ret;
- ret = init_counter(DELTA_COUNTERS, my_thread_id, my_core_id, my_package_id, cpu_id);
- if (ret < 0)
- return ret;
+ ret = init_counter(EVEN_COUNTERS, cpu_id);
+ if (ret < 0)
+ return ret;
+ ret = init_counter(ODD_COUNTERS, cpu_id);
+ if (ret < 0)
+ return ret;
+ ret = init_counter(DELTA_COUNTERS, cpu_id);
+ if (ret < 0)
+ return ret;
+ }
return 0;
}
+
+
+static void
+free_all_buffers(void)
+{
+ allocated = 0;
+ initialized = 0;
+
+ CPU_FREE(cpu_present_set);
+ cpu_present_set = NULL;
+ cpu_present_set = 0;
+
+ CPU_FREE(cpu_affinity_set);
+ cpu_affinity_set = NULL;
+ cpu_affinity_setsize = 0;
+
+ CPU_FREE(cpu_saved_affinity_set);
+ cpu_saved_affinity_set = NULL;
+ cpu_saved_affinity_setsize = 0;
+
+ free(thread_even);
+ free(core_even);
+ free(package_even);
+
+ thread_even = NULL;
+ core_even = NULL;
+ package_even = NULL;
+
+ free(thread_odd);
+ free(core_odd);
+ free(package_odd);
+
+ thread_odd = NULL;
+ core_odd = NULL;
+ package_odd = NULL;
+
+ free(thread_delta);
+ free(core_delta);
+ free(package_delta);
+
+ thread_delta = NULL;
+ core_delta = NULL;
+ package_delta = NULL;
+}
+
+
+/**********************
+ * Collectd functions *
+ **********************/
+
#define DO_OR_GOTO_ERR(something) \
do { \
ret = (something); \
DO_OR_GOTO_ERR(allocate_counters(&thread_even, &core_even, &package_even));
DO_OR_GOTO_ERR(allocate_counters(&thread_odd, &core_odd, &package_odd));
DO_OR_GOTO_ERR(allocate_counters(&thread_delta, &core_delta, &package_delta));
- DO_OR_GOTO_ERR(for_all_proc_cpus(initialize_counters));
+ DO_OR_GOTO_ERR(initialize_counters());
DO_OR_GOTO_ERR(for_all_cpus(set_temperature_target, EVEN_COUNTERS));
DO_OR_GOTO_ERR(for_all_cpus(set_temperature_target, ODD_COUNTERS));
}
static int
+turbostat_read(user_data_t * not_used)
+{
+ int ret;
+
+ if (!allocated) {
+ if ((ret = setup_all_buffers()) < 0)
+ return ret;
+ }
+
+ if (for_all_proc_cpus(cpu_is_not_present)) {
+ free_all_buffers();
+ if ((ret = setup_all_buffers()) < 0)
+ return ret;
+ if (for_all_proc_cpus(cpu_is_not_present))
+ return -ERR_CPU_NOT_PRESENT;
+ }
+
+ /* Saving the scheduling affinity, as it will be modified by get_counters */
+ if (sched_getaffinity(0, cpu_saved_affinity_setsize, cpu_saved_affinity_set) != 0)
+ return -ERR_CPU_SAVE_SCHED_AFFINITY;
+
+ if (!initialized) {
+ if ((ret = for_all_cpus(get_counters, EVEN_COUNTERS)) < 0)
+ goto out;
+ gettimeofday(&tv_even, (struct timezone *)NULL);
+ is_even = 1;
+ initialized = 1;
+ ret = 0;
+ goto out;
+ }
+
+ if (is_even) {
+ if ((ret = for_all_cpus(get_counters, ODD_COUNTERS)) < 0)
+ goto out;
+ gettimeofday(&tv_odd, (struct timezone *)NULL);
+ is_even = 0;
+ timersub(&tv_odd, &tv_even, &tv_delta);
+ if ((ret = for_all_cpus_delta(ODD_COUNTERS, EVEN_COUNTERS)) < 0)
+ goto out;
+ if ((ret = for_all_cpus(submit_counters, DELTA_COUNTERS)) < 0)
+ goto out;
+ } else {
+ if ((ret = for_all_cpus(get_counters, EVEN_COUNTERS)) < 0)
+ goto out;
+ gettimeofday(&tv_even, (struct timezone *)NULL);
+ is_even = 1;
+ timersub(&tv_even, &tv_odd, &tv_delta);
+ if ((ret = for_all_cpus_delta(EVEN_COUNTERS, ODD_COUNTERS)) < 0)
+ goto out;
+ if ((ret = for_all_cpus(submit_counters, DELTA_COUNTERS)) < 0)
+ goto out;
+ }
+ ret = 0;
+out:
+ /*
+ * Let's restore the affinity
+ * This might fail if the number of CPU changed, but we can't do anything in that case..
+ */
+ (void)sched_setaffinity(0, cpu_saved_affinity_setsize, cpu_saved_affinity_set);
+ return ret;
+}
+
+static int
turbostat_init(void)
{
+ struct stat sb;
int ret;
- DO_OR_GOTO_ERR(check_super_user());
+ if (getuid() != 0) {
+ ERROR("must be root");
+ return -ERR_NOT_ROOT;
+ }
+
DO_OR_GOTO_ERR(probe_cpu());
- DO_OR_GOTO_ERR(check_dev_msr());
+
+ if (stat("/dev/cpu/0/msr", &sb)) {
+ ERROR("no /dev/cpu/0/msr, try \"# modprobe msr\"");
+ return -ERR_NO_MSR;
+ }
+
DO_OR_GOTO_ERR(setup_all_buffers());
plugin_register_complex_read(NULL, PLUGIN_NAME, turbostat_read, NULL, NULL);
return 0;
}
-void module_register(void);
void module_register(void)
{
plugin_register_init(PLUGIN_NAME, turbostat_init);