Merge pull request #2806 from rorysexton/dev_turbostat_plugin
[collectd.git] / src / turbostat.c
1 /*
2  * turbostat -- Log CPU frequency and C-state residency
3  * on modern Intel turbo-capable processors for collectd.
4  *
5  * Based on the 'turbostat' tool of the Linux kernel, found at
6  * linux/tools/power/x86/turbostat/turbostat.c:
7  * ----
8  * Copyright (c) 2013 Intel Corporation.
9  * Len Brown <len.brown@intel.com>
10  *
11  * This program is free software; you can redistribute it and/or modify it
12  * under the terms and conditions of the GNU General Public License,
13  * version 2, as published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
18  * more details.
19  *
20  * You should have received a copy of the GNU General Public License along with
21  * this program; if not, write to the Free Software Foundation, Inc.,
22  * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
23  * ----
24  * Ported to collectd by Vincent Brillault <git@lerya.net>
25  */
26
27 /*
28  * _GNU_SOURCE is required because of the following functions:
29  * - CPU_ISSET_S
30  * - CPU_ZERO_S
31  * - CPU_SET_S
32  * - CPU_FREE
33  * - CPU_ALLOC
34  * - CPU_ALLOC_SIZE
35  */
36 #define _GNU_SOURCE
37
38 #include "collectd.h"
39
40 #include "common.h"
41 #include "plugin.h"
42 #include "utils_time.h"
43
44 #include "msr-index.h"
45 #include <cpuid.h>
46 #ifdef HAVE_SYS_CAPABILITY_H
47 #include <sys/capability.h>
48 #endif /* HAVE_SYS_CAPABILITY_H */
49
50 #define PLUGIN_NAME "turbostat"
51
52 /*
53  * This tool uses the Model-Specific Registers (MSRs) present on Intel
54  * processors.
55  * The general description each of these registers, depending on the
56  * architecture,
57  * can be found in the IntelĀ® 64 and IA-32 Architectures Software Developer
58  * Manual,
59  * Volume 3 Chapter 35.
60  */
61
62 /*
63  * If set, aperf_mperf_unstable disables a/mperf based stats.
64  * This includes: C0 & C1 states, frequency
65  *
66  * This value is automatically set if mperf or aperf go backward
67  */
68 static bool aperf_mperf_unstable;
69
70 /*
71  * If set, use kernel logical core numbering for all "per core" metrics.
72  */
73 static bool config_lcn;
74
75 /*
76  * Bitmask of the list of core C states supported by the processor.
77  * Currently supported C-states (by this plugin): 3, 6, 7
78  */
79 static unsigned int do_core_cstate;
80 static unsigned int config_core_cstate;
81 static bool apply_config_core_cstate;
82
83 /*
84  * Bitmask of the list of pacages C states supported by the processor.
85  * Currently supported C-states (by this plugin): 2, 3, 6, 7, 8, 9, 10
86  */
87 static unsigned int do_pkg_cstate;
88 static unsigned int config_pkg_cstate;
89 static bool apply_config_pkg_cstate;
90
91 /*
92  * Boolean indicating if the processor supports 'I/O System-Management Interrupt
93  * counter'
94  */
95 static bool do_smi;
96 static bool config_smi;
97 static bool apply_config_smi;
98
99 /*
100  * Boolean indicating if the processor supports 'Digital temperature sensor'
101  * This feature enables the monitoring of the temperature of each core
102  *
103  * This feature has two limitations:
104  *  - if MSR_IA32_TEMPERATURE_TARGET is not supported, the absolute temperature
105  * might be wrong
106  *  - Temperatures above the tcc_activation_temp are not recorded
107  */
108 static bool do_dts;
109 static bool config_dts;
110 static bool apply_config_dts;
111
112 /*
113  * Boolean indicating if the processor supports 'Package thermal management'
114  * This feature allows the monitoring of the temperature of each package
115  *
116  * This feature has two limitations:
117  *  - if MSR_IA32_TEMPERATURE_TARGET is not supported, the absolute temperature
118  * might be wrong
119  *  - Temperatures above the tcc_activation_temp are not recorded
120  */
121 static bool do_ptm;
122 static bool config_ptm;
123 static bool apply_config_ptm;
124
125 /*
126  * Thermal Control Circuit Activation Temperature as configured by the user.
127  * This override the automated detection via MSR_IA32_TEMPERATURE_TARGET
128  * and should only be used if the automated detection fails.
129  */
130 static unsigned int tcc_activation_temp;
131
132 static unsigned int do_power_fields;
133 #define UFS_PLATFORM (1 << 0)
134 #define TURBO_PLATFORM (1 << 1)
135 #define PSTATES_PLATFORM (1 << 2)
136
137 static unsigned int do_rapl;
138 static unsigned int config_rapl;
139 static bool apply_config_rapl;
140 static double rapl_energy_units;
141 static double rapl_power_units;
142
143 #define RAPL_PKG (1 << 0)
144 /* 0x610 MSR_PKG_POWER_LIMIT */
145 /* 0x611 MSR_PKG_ENERGY_STATUS */
146 /* 0x614 MSR_PKG_POWER_INFO */
147 #define RAPL_DRAM (1 << 1)
148 /* 0x618 MSR_DRAM_POWER_LIMIT */
149 /* 0x619 MSR_DRAM_ENERGY_STATUS */
150 /* 0x61c MSR_DRAM_POWER_INFO */
151 #define RAPL_CORES (1 << 2)
152 /* 0x638 MSR_PP0_POWER_LIMIT */
153 /* 0x639 MSR_PP0_ENERGY_STATUS */
154
155 #define RAPL_GFX (1 << 3)
156 /* 0x640 MSR_PP1_POWER_LIMIT */
157 /* 0x641 MSR_PP1_ENERGY_STATUS */
158 /* 0x642 MSR_PP1_POLICY */
159 #define TJMAX_DEFAULT 100
160
161 static cpu_set_t *cpu_present_set, *cpu_affinity_set, *cpu_saved_affinity_set;
162 static size_t cpu_present_setsize, cpu_affinity_setsize,
163     cpu_saved_affinity_setsize;
164
165 static struct thread_data {
166   unsigned long long tsc;
167   unsigned long long aperf;
168   unsigned long long mperf;
169   unsigned long long c1;
170   unsigned int smi_count;
171   unsigned int cpu_id;
172   unsigned int flags;
173 #define CPU_IS_FIRST_THREAD_IN_CORE 0x2
174 #define CPU_IS_FIRST_CORE_IN_PACKAGE 0x4
175 } * thread_delta, *thread_even, *thread_odd;
176
177 static struct core_data {
178   unsigned long long c3;
179   unsigned long long c6;
180   unsigned long long c7;
181   unsigned int core_temp_c;
182   unsigned int core_id;
183 } * core_delta, *core_even, *core_odd;
184
185 static struct pkg_data {
186   unsigned long long pc2;
187   unsigned long long pc3;
188   unsigned long long pc6;
189   unsigned long long pc7;
190   unsigned long long pc8;
191   unsigned long long pc9;
192   unsigned long long pc10;
193   unsigned int package_id;
194   uint32_t energy_pkg;   /* MSR_PKG_ENERGY_STATUS */
195   uint32_t energy_dram;  /* MSR_DRAM_ENERGY_STATUS */
196   uint32_t energy_cores; /* MSR_PP0_ENERGY_STATUS */
197   uint32_t energy_gfx;   /* MSR_PP1_ENERGY_STATUS */
198   uint32_t tdp;
199   uint8_t turbo_enabled;
200   uint8_t pstates_enabled;
201   uint32_t uncore;
202   unsigned int tcc_activation_temp;
203   unsigned int pkg_temp_c;
204 } * package_delta, *package_even, *package_odd;
205
206 #define DELTA_COUNTERS thread_delta, core_delta, package_delta
207 #define ODD_COUNTERS thread_odd, core_odd, package_odd
208 #define EVEN_COUNTERS thread_even, core_even, package_even
209 static bool is_even = true;
210
211 static bool allocated;
212 static bool initialized;
213
214 #define GET_THREAD(thread_base, thread_no, core_no, pkg_no)                    \
215   (thread_base + (pkg_no)*topology.num_cores * topology.num_threads +          \
216    (core_no)*topology.num_threads + (thread_no))
217 #define GET_CORE(core_base, core_no, pkg_no)                                   \
218   (core_base + (pkg_no)*topology.num_cores + (core_no))
219 #define GET_PKG(pkg_base, pkg_no) (pkg_base + pkg_no)
220
221 struct cpu_topology {
222   unsigned int package_id;
223   unsigned int core_id;
224   bool first_core_in_package;
225   bool first_thread_in_core;
226 };
227
228 static struct topology {
229   unsigned int max_cpu_id;
230   unsigned int num_packages;
231   unsigned int num_cores;
232   unsigned int num_threads;
233   struct cpu_topology *cpus;
234 } topology;
235
236 static cdtime_t time_even, time_odd, time_delta;
237
238 static const char *config_keys[] = {
239     "CoreCstates",
240     "PackageCstates",
241     "SystemManagementInterrupt",
242     "DigitalTemperatureSensor",
243     "PackageThermalManagement",
244     "TCCActivationTemp",
245     "RunningAveragePowerLimit",
246     "LogicalCoreNames",
247 };
248 static const int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
249
250 /*****************************
251  *  MSR Manipulation helpers *
252  *****************************/
253
254 /*
255  * Open a MSR device for reading
256  * Can change the scheduling affinity of the current process if multiple_read is
257  * true
258  */
259 static int __attribute__((warn_unused_result))
260 open_msr(unsigned int cpu, bool multiple_read) {
261   char pathname[32];
262   int fd;
263
264   /*
265    * If we need to do multiple read, let's migrate to the CPU
266    * Otherwise, we would lose time calling functions on another CPU
267    *
268    * If we are not yet initialized (cpu_affinity_setsize = 0),
269    * we need to skip this optimisation.
270    */
271   if (multiple_read && cpu_affinity_setsize) {
272     CPU_ZERO_S(cpu_affinity_setsize, cpu_affinity_set);
273     CPU_SET_S(cpu, cpu_affinity_setsize, cpu_affinity_set);
274     if (sched_setaffinity(0, cpu_affinity_setsize, cpu_affinity_set) == -1) {
275       ERROR("turbostat plugin: Could not migrate to CPU %d", cpu);
276       return -1;
277     }
278   }
279
280   snprintf(pathname, sizeof(pathname), "/dev/cpu/%d/msr", cpu);
281   fd = open(pathname, O_RDONLY);
282   if (fd < 0) {
283     ERROR("turbostat plugin: failed to open %s", pathname);
284     return -1;
285   }
286   return fd;
287 }
288
289 /*
290  * Read a single MSR from an open file descriptor
291  */
292 static int __attribute__((warn_unused_result))
293 read_msr(int fd, off_t offset, unsigned long long *msr) {
294   ssize_t retval;
295
296   retval = pread(fd, msr, sizeof *msr, offset);
297
298   if (retval != sizeof *msr) {
299     ERROR("turbostat plugin: MSR offset 0x%llx read failed",
300           (unsigned long long)offset);
301     return -1;
302   }
303   return 0;
304 }
305
306 /*
307  * Open a MSR device for reading, read the value asked for and close it.
308  * This call will not affect the scheduling affinity of this thread.
309  */
310 static ssize_t __attribute__((warn_unused_result))
311 get_msr(unsigned int cpu, off_t offset, unsigned long long *msr) {
312   ssize_t retval;
313   int fd;
314
315   fd = open_msr(cpu, 0);
316   if (fd < 0)
317     return fd;
318   retval = read_msr(fd, offset, msr);
319   close(fd);
320   return retval;
321 }
322
323 /********************************
324  * Raw data acquisition (1 CPU) *
325  ********************************/
326
327 /*
328  * Read every data avalaible for a single CPU
329  *
330  * Core data is shared for all threads in one core: extracted only for the first
331  * thread
332  * Package data is shared for all core in one package: extracted only for the
333  * first thread of the first core
334  *
335  * Side effect: migrates to the targeted CPU
336  */
337 static int __attribute__((warn_unused_result))
338 get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) {
339   unsigned int cpu = t->cpu_id;
340   unsigned long long msr;
341   int msr_fd;
342   int retval = 0;
343
344   msr_fd = open_msr(cpu, 1);
345   if (msr_fd < 0)
346     return msr_fd;
347
348 #define READ_MSR(msr, dst)                                                     \
349   do {                                                                         \
350     if (read_msr(msr_fd, msr, dst)) {                                          \
351       ERROR("turbostat plugin: Unable to read " #msr);                         \
352       retval = -1;                                                             \
353       goto out;                                                                \
354     }                                                                          \
355   } while (0)
356
357   READ_MSR(MSR_IA32_TSC, &t->tsc);
358
359   READ_MSR(MSR_IA32_APERF, &t->aperf);
360   READ_MSR(MSR_IA32_MPERF, &t->mperf);
361
362   if (do_smi) {
363     READ_MSR(MSR_SMI_COUNT, &msr);
364     t->smi_count = msr & 0xFFFFFFFF;
365   }
366
367   /* collect core counters only for 1st thread in core */
368   if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) {
369     retval = 0;
370     goto out;
371   }
372
373   if (do_core_cstate & (1 << 3))
374     READ_MSR(MSR_CORE_C3_RESIDENCY, &c->c3);
375   if (do_core_cstate & (1 << 6))
376     READ_MSR(MSR_CORE_C6_RESIDENCY, &c->c6);
377   if (do_core_cstate & (1 << 7))
378     READ_MSR(MSR_CORE_C7_RESIDENCY, &c->c7);
379
380   if (do_dts) {
381     READ_MSR(MSR_IA32_THERM_STATUS, &msr);
382     c->core_temp_c = p->tcc_activation_temp - ((msr >> 16) & 0x7F);
383   }
384
385   /* collect package counters only for 1st core in package */
386   if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) {
387     retval = 0;
388     goto out;
389   }
390
391   if (do_pkg_cstate & (1 << 2))
392     READ_MSR(MSR_PKG_C2_RESIDENCY, &p->pc2);
393   if (do_pkg_cstate & (1 << 3))
394     READ_MSR(MSR_PKG_C3_RESIDENCY, &p->pc3);
395   if (do_pkg_cstate & (1 << 6))
396     READ_MSR(MSR_PKG_C6_RESIDENCY, &p->pc6);
397   if (do_pkg_cstate & (1 << 7))
398     READ_MSR(MSR_PKG_C7_RESIDENCY, &p->pc7);
399   if (do_pkg_cstate & (1 << 8))
400     READ_MSR(MSR_PKG_C8_RESIDENCY, &p->pc8);
401   if (do_pkg_cstate & (1 << 9))
402     READ_MSR(MSR_PKG_C9_RESIDENCY, &p->pc9);
403   if (do_pkg_cstate & (1 << 10))
404     READ_MSR(MSR_PKG_C10_RESIDENCY, &p->pc10);
405
406   if (do_rapl & RAPL_PKG) {
407     READ_MSR(MSR_PKG_ENERGY_STATUS, &msr);
408     p->energy_pkg = msr & 0xFFFFFFFF;
409     READ_MSR(MSR_PKG_POWER_INFO, &msr);
410     p->tdp = msr & 0x7FFF;
411   }
412   if (do_rapl & RAPL_CORES) {
413     READ_MSR(MSR_PP0_ENERGY_STATUS, &msr);
414     p->energy_cores = msr & 0xFFFFFFFF;
415   }
416   if (do_rapl & RAPL_DRAM) {
417     READ_MSR(MSR_DRAM_ENERGY_STATUS, &msr);
418     p->energy_dram = msr & 0xFFFFFFFF;
419   }
420   if (do_rapl & RAPL_GFX) {
421     READ_MSR(MSR_PP1_ENERGY_STATUS, &msr);
422     p->energy_gfx = msr & 0xFFFFFFFF;
423   }
424   if (do_ptm) {
425     READ_MSR(MSR_IA32_PACKAGE_THERM_STATUS, &msr);
426     p->pkg_temp_c = p->tcc_activation_temp - ((msr >> 16) & 0x7F);
427   }
428   if (do_power_fields & TURBO_PLATFORM) {
429     READ_MSR(MSR_IA32_MISC_ENABLE, &msr);
430     p->turbo_enabled = !((msr >> 38) & 0x1);
431   }
432   if (do_power_fields & PSTATES_PLATFORM) {
433     READ_MSR(MSR_IA32_MISC_ENABLE, &msr);
434     p->pstates_enabled = (msr >> 16) & 0x1;
435   }
436   if (do_power_fields & UFS_PLATFORM) {
437     READ_MSR(MSR_UNCORE_FREQ_SCALING, &msr);
438     p->uncore = msr & 0x1F;
439   }
440
441 out:
442   close(msr_fd);
443   return retval;
444 }
445
446 /**********************************
447  * Evaluating the changes (1 CPU) *
448  **********************************/
449
450 /*
451  * Extract the evolution old->new in delta at a package level
452  * (some are not new-delta, e.g. temperature)
453  */
454 static inline void delta_package(struct pkg_data *delta,
455                                  const struct pkg_data *new,
456                                  const struct pkg_data *old) {
457   delta->pc2 = new->pc2 - old->pc2;
458   delta->pc3 = new->pc3 - old->pc3;
459   delta->pc6 = new->pc6 - old->pc6;
460   delta->pc7 = new->pc7 - old->pc7;
461   delta->pc8 = new->pc8 - old->pc8;
462   delta->pc9 = new->pc9 - old->pc9;
463   delta->pc10 = new->pc10 - old->pc10;
464   delta->pkg_temp_c = new->pkg_temp_c;
465
466   delta->energy_pkg = new->energy_pkg - old->energy_pkg;
467   delta->energy_cores = new->energy_cores - old->energy_cores;
468   delta->energy_gfx = new->energy_gfx - old->energy_gfx;
469   delta->energy_dram = new->energy_dram - old->energy_dram;
470   delta->tdp = new->tdp;
471   delta->turbo_enabled = new->turbo_enabled;
472   delta->pstates_enabled = new->pstates_enabled;
473   delta->tcc_activation_temp = new->tcc_activation_temp;
474   delta->uncore = new->uncore;
475 }
476
477 /*
478  * Extract the evolution old->new in delta at a core level
479  * (some are not new-delta, e.g. temperature)
480  */
481 static inline void delta_core(struct core_data *delta,
482                               const struct core_data *new,
483                               const struct core_data *old) {
484   delta->c3 = new->c3 - old->c3;
485   delta->c6 = new->c6 - old->c6;
486   delta->c7 = new->c7 - old->c7;
487   delta->core_temp_c = new->core_temp_c;
488 }
489
490 /*
491  * Extract the evolution old->new in delta at a package level
492  * core_delta is required for c1 estimation (tsc - c0 - all core cstates)
493  */
494 static inline int __attribute__((warn_unused_result))
495 delta_thread(struct thread_data *delta, const struct thread_data *new,
496              const struct thread_data *old, const struct core_data *cdelta) {
497   delta->tsc = new->tsc - old->tsc;
498
499   /* check for TSC < 1 Mcycles over interval */
500   if (delta->tsc < (1000 * 1000)) {
501     WARNING("turbostat plugin: Insanely slow TSC rate, TSC stops "
502             "in idle? You can disable all c-states by booting with"
503             " 'idle=poll' or just the deep ones with"
504             " 'processor.max_cstate=1'");
505     return -1;
506   }
507
508   delta->c1 = new->c1 - old->c1;
509
510   if ((new->aperf > old->aperf) && (new->mperf > old->mperf)) {
511     delta->aperf = new->aperf - old->aperf;
512     delta->mperf = new->mperf - old->mperf;
513   } else {
514     if (!aperf_mperf_unstable) {
515       WARNING("turbostat plugin: APERF or MPERF went "
516               "backwards. Frequency results do not cover "
517               "the entire interval. Fix this by running "
518               "Linux-2.6.30 or later.");
519
520       aperf_mperf_unstable = true;
521     }
522   }
523
524   /*
525    * As counter collection is not atomic,
526    * it is possible for mperf's non-halted cycles + idle states
527    * to exceed TSC's all cycles: show c1 = 0% in that case.
528    */
529   if ((delta->mperf + cdelta->c3 + cdelta->c6 + cdelta->c7) > delta->tsc)
530     delta->c1 = 0;
531   else {
532     /* normal case, derive c1 */
533     delta->c1 =
534         delta->tsc - delta->mperf - cdelta->c3 - cdelta->c6 - cdelta->c7;
535   }
536
537   if (delta->mperf == 0) {
538     WARNING("turbostat plugin: cpu%d MPERF 0!", old->cpu_id);
539     delta->mperf = 1; /* divide by 0 protection */
540   }
541
542   if (do_smi)
543     delta->smi_count = new->smi_count - old->smi_count;
544
545   return 0;
546 }
547
548 /**********************************
549  * Submitting the results (1 CPU) *
550  **********************************/
551
552 /*
553  * Submit one gauge value
554  */
555 static void turbostat_submit(const char *plugin_instance, const char *type,
556                              const char *type_instance, gauge_t value) {
557   value_list_t vl = VALUE_LIST_INIT;
558
559   vl.values = &(value_t){.gauge = value};
560   vl.values_len = 1;
561   sstrncpy(vl.plugin, PLUGIN_NAME, sizeof(vl.plugin));
562   if (plugin_instance != NULL)
563     sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
564   sstrncpy(vl.type, type, sizeof(vl.type));
565   if (type_instance != NULL)
566     sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
567
568   plugin_dispatch_values(&vl);
569 }
570
571 /*
572  * Submit every data for a single CPU
573  *
574  * Core data is shared for all threads in one core: submitted only for the first
575  * thread
576  * Package data is shared for all core in one package: submitted only for the
577  * first thread of the first core
578  */
579 static int submit_counters(struct thread_data *t, struct core_data *c,
580                            struct pkg_data *p) {
581   char name[DATA_MAX_NAME_LEN];
582   double interval_float;
583
584   interval_float = CDTIME_T_TO_DOUBLE(time_delta);
585
586   DEBUG("turbostat plugin: submit stats for cpu: %d, core: %d, pkg: %d",
587         t->cpu_id, c->core_id, p->package_id);
588
589   snprintf(name, sizeof(name), "cpu%02d", t->cpu_id);
590
591   if (!aperf_mperf_unstable)
592     turbostat_submit(name, "percent", "c0", 100.0 * t->mperf / t->tsc);
593   if (!aperf_mperf_unstable)
594     turbostat_submit(name, "percent", "c1", 100.0 * t->c1 / t->tsc);
595
596   turbostat_submit(name, "frequency", "average",
597                    1.0 / 1000000 * t->aperf / interval_float);
598
599   if ((!aperf_mperf_unstable) || (!(t->aperf > t->tsc || t->mperf > t->tsc)))
600     turbostat_submit(name, "frequency", "busy",
601                      1.0 * t->tsc / 1000000 * t->aperf / t->mperf /
602                          interval_float);
603
604   /* Sanity check (should stay stable) */
605   turbostat_submit(name, "gauge", "TSC",
606                    1.0 * t->tsc / 1000000 / interval_float);
607
608   /* SMI */
609   if (do_smi)
610     turbostat_submit(name, "count", NULL, t->smi_count);
611
612   /* submit per-core data only for 1st thread in core */
613   if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE))
614     goto done;
615
616   /* If not using logical core numbering, set core id */
617   if (!config_lcn) {
618     if (topology.num_packages > 1)
619       snprintf(name, sizeof(name), "pkg%02d-core%02d", p->package_id,
620                c->core_id);
621     else
622       snprintf(name, sizeof(name), "core%02d", c->core_id);
623   }
624
625   if (do_core_cstate & (1 << 3))
626     turbostat_submit(name, "percent", "c3", 100.0 * c->c3 / t->tsc);
627   if (do_core_cstate & (1 << 6))
628     turbostat_submit(name, "percent", "c6", 100.0 * c->c6 / t->tsc);
629   if (do_core_cstate & (1 << 7))
630     turbostat_submit(name, "percent", "c7", 100.0 * c->c7 / t->tsc);
631
632   if (do_dts)
633     turbostat_submit(name, "temperature", NULL, c->core_temp_c);
634
635   /* submit per-package data only for 1st core in package */
636   if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE))
637     goto done;
638
639   snprintf(name, sizeof(name), "pkg%02d", p->package_id);
640
641   if (do_ptm)
642     turbostat_submit(name, "temperature", NULL, p->pkg_temp_c);
643
644   if (do_pkg_cstate & (1 << 2))
645     turbostat_submit(name, "percent", "pc2", 100.0 * p->pc2 / t->tsc);
646   if (do_pkg_cstate & (1 << 3))
647     turbostat_submit(name, "percent", "pc3", 100.0 * p->pc3 / t->tsc);
648   if (do_pkg_cstate & (1 << 6))
649     turbostat_submit(name, "percent", "pc6", 100.0 * p->pc6 / t->tsc);
650   if (do_pkg_cstate & (1 << 7))
651     turbostat_submit(name, "percent", "pc7", 100.0 * p->pc7 / t->tsc);
652   if (do_pkg_cstate & (1 << 8))
653     turbostat_submit(name, "percent", "pc8", 100.0 * p->pc8 / t->tsc);
654   if (do_pkg_cstate & (1 << 9))
655     turbostat_submit(name, "percent", "pc9", 100.0 * p->pc9 / t->tsc);
656   if (do_pkg_cstate & (1 << 10))
657     turbostat_submit(name, "percent", "pc10", 100.0 * p->pc10 / t->tsc);
658
659   if (do_rapl) {
660     if (do_rapl & RAPL_PKG) {
661       turbostat_submit(name, "power", "pkg",
662                        p->energy_pkg * rapl_energy_units / interval_float);
663       turbostat_submit(name, "tdp", "pkg", p->tdp * rapl_power_units);
664     }
665     if (do_rapl & RAPL_CORES)
666       turbostat_submit(name, "power", "cores",
667                        p->energy_cores * rapl_energy_units / interval_float);
668     if (do_rapl & RAPL_GFX)
669       turbostat_submit(name, "power", "GFX",
670                        p->energy_gfx * rapl_energy_units / interval_float);
671     if (do_rapl & RAPL_DRAM)
672       turbostat_submit(name, "power", "DRAM",
673                        p->energy_dram * rapl_energy_units / interval_float);
674   }
675
676   if (do_power_fields & TURBO_PLATFORM) {
677     turbostat_submit(name, "turbo_enabled", NULL, p->turbo_enabled);
678   }
679   if (do_power_fields & PSTATES_PLATFORM) {
680     turbostat_submit(name, "pstates_enabled", NULL, p->pstates_enabled);
681   }
682   if (do_power_fields & UFS_PLATFORM) {
683     turbostat_submit(name, "uncore_ratio", NULL, p->uncore);
684   }
685   turbostat_submit(name, "temperature", "tcc_activation",
686                    p->tcc_activation_temp);
687 done:
688   return 0;
689 }
690
691 /**********************************
692  * Looping function over all CPUs *
693  **********************************/
694
695 /*
696  * Check if a given cpu id is in our compiled list of existing CPUs
697  */
698 static int cpu_is_not_present(unsigned int cpu) {
699   return !CPU_ISSET_S(cpu, cpu_present_setsize, cpu_present_set);
700 }
701
702 /*
703  * Loop on all CPUs in topological order
704  *
705  * Skip non-present cpus
706  * Return the error code at the first error or 0
707  */
708 static int __attribute__((warn_unused_result))
709 for_all_cpus(int(func)(struct thread_data *, struct core_data *,
710                        struct pkg_data *),
711              struct thread_data *thread_base, struct core_data *core_base,
712              struct pkg_data *pkg_base) {
713   int retval;
714
715   for (unsigned int pkg_no = 0; pkg_no < topology.num_packages; ++pkg_no) {
716     for (unsigned int core_no = 0; core_no < topology.num_cores; ++core_no) {
717       for (unsigned int thread_no = 0; thread_no < topology.num_threads;
718            ++thread_no) {
719         struct thread_data *t;
720         struct core_data *c;
721         struct pkg_data *p;
722
723         t = GET_THREAD(thread_base, thread_no, core_no, pkg_no);
724
725         if (cpu_is_not_present(t->cpu_id))
726           continue;
727
728         c = GET_CORE(core_base, core_no, pkg_no);
729         p = GET_PKG(pkg_base, pkg_no);
730
731         retval = func(t, c, p);
732         if (retval)
733           return retval;
734       }
735     }
736   }
737   return 0;
738 }
739
740 /*
741  * Dedicated loop: Extract every data evolution for all CPU
742  *
743  * Skip non-present cpus
744  * Return the error code at the first error or 0
745  *
746  * Core data is shared for all threads in one core: extracted only for the first
747  * thread
748  * Package data is shared for all core in one package: extracted only for the
749  * first thread of the first core
750  */
751 static int __attribute__((warn_unused_result))
752 for_all_cpus_delta(const struct thread_data *thread_new_base,
753                    const struct core_data *core_new_base,
754                    const struct pkg_data *pkg_new_base,
755                    const struct thread_data *thread_old_base,
756                    const struct core_data *core_old_base,
757                    const struct pkg_data *pkg_old_base) {
758   int retval;
759
760   for (unsigned int pkg_no = 0; pkg_no < topology.num_packages; ++pkg_no) {
761     for (unsigned int core_no = 0; core_no < topology.num_cores; ++core_no) {
762       for (unsigned int thread_no = 0; thread_no < topology.num_threads;
763            ++thread_no) {
764         struct thread_data *t_delta;
765         const struct thread_data *t_old, *t_new;
766         struct core_data *c_delta;
767
768         /* Get correct pointers for threads */
769         t_delta = GET_THREAD(thread_delta, thread_no, core_no, pkg_no);
770         t_new = GET_THREAD(thread_new_base, thread_no, core_no, pkg_no);
771         t_old = GET_THREAD(thread_old_base, thread_no, core_no, pkg_no);
772
773         /* Skip threads that disappeared */
774         if (cpu_is_not_present(t_delta->cpu_id))
775           continue;
776
777         /* c_delta is always required for delta_thread */
778         c_delta = GET_CORE(core_delta, core_no, pkg_no);
779
780         /* calculate core delta only for 1st thread in core */
781         if (t_new->flags & CPU_IS_FIRST_THREAD_IN_CORE) {
782           const struct core_data *c_old, *c_new;
783
784           c_new = GET_CORE(core_new_base, core_no, pkg_no);
785           c_old = GET_CORE(core_old_base, core_no, pkg_no);
786
787           delta_core(c_delta, c_new, c_old);
788         }
789
790         /* Always calculate thread delta */
791         retval = delta_thread(t_delta, t_new, t_old, c_delta);
792         if (retval)
793           return retval;
794
795         /* calculate package delta only for 1st core in package */
796         if (t_new->flags & CPU_IS_FIRST_CORE_IN_PACKAGE) {
797           struct pkg_data *p_delta;
798           const struct pkg_data *p_old, *p_new;
799
800           p_delta = GET_PKG(package_delta, pkg_no);
801           p_new = GET_PKG(pkg_new_base, pkg_no);
802           p_old = GET_PKG(pkg_old_base, pkg_no);
803
804           delta_package(p_delta, p_new, p_old);
805         }
806       }
807     }
808   }
809   return 0;
810 }
811
812 /***************
813  * CPU Probing *
814  ***************/
815
816 /*
817  * MSR_IA32_TEMPERATURE_TARGET indicates the temperature where
818  * the Thermal Control Circuit (TCC) activates.
819  * This is usually equal to tjMax.
820  *
821  * Older processors do not have this MSR, so there we guess,
822  * but also allow conficuration over-ride with "TCCActivationTemp".
823  *
824  * Several MSR temperature values are in units of degrees-C
825  * below this value, including the Digital Thermal Sensor (DTS),
826  * Package Thermal Management Sensor (PTM), and thermal event thresholds.
827  */
828 static int __attribute__((warn_unused_result))
829 set_temperature_target(struct thread_data *t, struct core_data *c,
830                        struct pkg_data *p) {
831   unsigned long long msr;
832   unsigned int target_c_local;
833
834   /* tcc_activation_temp is used only for dts or ptm */
835   if (!(do_dts || do_ptm))
836     return 0;
837
838   /* this is a per-package concept */
839   if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE) ||
840       !(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE))
841     return 0;
842
843   if (tcc_activation_temp != 0) {
844     p->tcc_activation_temp = tcc_activation_temp;
845     return 0;
846   }
847
848   if (get_msr(t->cpu_id, MSR_IA32_TEMPERATURE_TARGET, &msr))
849     goto guess;
850
851   target_c_local = (msr >> 16) & 0xFF;
852
853   if (!target_c_local)
854     goto guess;
855
856   p->tcc_activation_temp = target_c_local;
857
858   return 0;
859
860 guess:
861   p->tcc_activation_temp = TJMAX_DEFAULT;
862   WARNING("turbostat plugin: cpu%d: Guessing tjMax %d C,"
863           " Please use TCCActivationTemp to specify it.",
864           t->cpu_id, p->tcc_activation_temp);
865
866   return 0;
867 }
868
869 /*
870  * Identify the functionality of the CPU
871  */
872 static int __attribute__((warn_unused_result)) probe_cpu(void) {
873   unsigned int eax, ebx, ecx, edx, max_level;
874   unsigned int fms, family, model;
875
876   /* CPUID(0):
877    * - EAX: Maximum Input Value for Basic CPUID Information
878    * - EBX: "Genu" (0x756e6547)
879    * - EDX: "ineI" (0x49656e69)
880    * - ECX: "ntel" (0x6c65746e)
881    */
882   max_level = ebx = ecx = edx = 0;
883   __get_cpuid(0, &max_level, &ebx, &ecx, &edx);
884   if (ebx != 0x756e6547 && edx != 0x49656e69 && ecx != 0x6c65746e) {
885     ERROR("turbostat plugin: Unsupported CPU (not Intel)");
886     return -1;
887   }
888
889   /* CPUID(1):
890    * - EAX: Version Information: Type, Family, Model, and Stepping ID
891    *  + 4-7:   Model ID
892    *  + 8-11:  Family ID
893    *  + 12-13: Processor type
894    *  + 16-19: Extended Model ID
895    *  + 20-27: Extended Family ID
896    * - EDX: Feature Information:
897    *  + 5: Support for MSR read/write operations
898    */
899   fms = ebx = ecx = edx = 0;
900   __get_cpuid(1, &fms, &ebx, &ecx, &edx);
901   family = (fms >> 8) & 0xf;
902   model = (fms >> 4) & 0xf;
903   if (family == 0xf)
904     family += (fms >> 20) & 0xf;
905   if (family == 6 || family == 0xf)
906     model += ((fms >> 16) & 0xf) << 4;
907   if (!(edx & (1 << 5))) {
908     ERROR("turbostat plugin: Unsupported CPU (no MSR support)");
909     return -1;
910   }
911
912   /*
913    * CPUID(6):
914    * - EAX:
915    *  + 0: Digital temperature sensor is supported if set
916    *  + 6: Package thermal management is supported if set
917    * - ECX:
918    *  + 0: Hardware Coordination Feedback Capability (Presence of IA32_MPERF and
919    * IA32_APERF).
920    *  + 3: The processor supports performance-energy bias preference if set.
921    *       It also implies the presence of a new architectural MSR called
922    * IA32_ENERGY_PERF_BIAS
923    *
924    * This check is valid for both Intel and AMD
925    */
926   eax = ebx = ecx = edx = 0;
927   __get_cpuid(0x6, &eax, &ebx, &ecx, &edx);
928   do_dts = eax & (1 << 0);
929   do_ptm = eax & (1 << 6);
930   if (!(ecx & (1 << 0))) {
931     ERROR("turbostat plugin: Unsupported CPU (No APERF)");
932     return -1;
933   }
934
935   /*
936    * Enable or disable C states depending on the model and family
937    */
938   if (family == 6) {
939     switch (model) {
940     /* Atom (partial) */
941     case 0x27:
942       do_smi = false;
943       do_core_cstate = 0;
944       do_pkg_cstate = (1 << 2) | (1 << 4) | (1 << 6);
945       break;
946     /* Silvermont */
947     case 0x37: /* BYT */
948     case 0x4D: /* AVN */
949       do_smi = true;
950       do_core_cstate = (1 << 1) | (1 << 6);
951       do_pkg_cstate = (1 << 6);
952       break;
953     /* Nehalem */
954     case 0x1A: /* Core i7, Xeon 5500 series - Bloomfield, Gainstown NHM-EP */
955     case 0x1E: /* Core i7 and i5 Processor - Clarksfield, Lynnfield, Jasper
956                   Forest */
957     case 0x1F: /* Core i7 and i5 Processor - Nehalem */
958     case 0x2E: /* Nehalem-EX Xeon - Beckton */
959       do_smi = true;
960       do_core_cstate = (1 << 3) | (1 << 6);
961       do_pkg_cstate = (1 << 3) | (1 << 6) | (1 << 7);
962       break;
963     /* Westmere */
964     case 0x25: /* Westmere Client - Clarkdale, Arrandale */
965     case 0x2C: /* Westmere EP - Gulftown */
966     case 0x2F: /* Westmere-EX Xeon - Eagleton */
967       do_smi = true;
968       do_core_cstate = (1 << 3) | (1 << 6);
969       do_pkg_cstate = (1 << 3) | (1 << 6) | (1 << 7);
970       break;
971     /* Sandy Bridge */
972     case 0x2A: /* SNB */
973     case 0x2D: /* SNB Xeon */
974       do_smi = true;
975       do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7);
976       do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7);
977       break;
978     /* Ivy Bridge */
979     case 0x3A: /* IVB */
980     case 0x3E: /* IVB Xeon */
981       do_smi = true;
982       do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7);
983       do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7);
984       break;
985     /* Haswell Bridge */
986     case 0x3C: /* HSW */
987     case 0x3F: /* HSW */
988     case 0x46: /* HSW */
989       do_smi = true;
990       do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7);
991       do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7);
992       break;
993     case 0x45: /* HSW */
994       do_smi = true;
995       do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7);
996       do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7) | (1 << 8) |
997                       (1 << 9) | (1 << 10);
998       break;
999     /* Broadwell */
1000     case 0x4F: /* BDW */
1001     case 0x56: /* BDX-DE */
1002       do_smi = true;
1003       do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7);
1004       do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7);
1005       break;
1006     case 0x3D: /* BDW */
1007       do_smi = true;
1008       do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7);
1009       do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7) | (1 << 8) |
1010                       (1 << 9) | (1 << 10);
1011       break;
1012     default:
1013       do_smi = false;
1014       do_core_cstate = 0;
1015       do_pkg_cstate = 0;
1016       break;
1017     }
1018     switch (model) {
1019     case 0x2A: /* SNB */
1020     case 0x3A: /* IVB */
1021     case 0x3C: /* HSW */
1022     case 0x45: /* HSW */
1023     case 0x46: /* HSW */
1024     case 0x3D: /* BDW */
1025     case 0x5E: /* SKL */
1026       do_rapl = RAPL_PKG | RAPL_CORES | RAPL_GFX;
1027       break;
1028     case 0x3F: /* HSX */
1029     case 0x4F: /* BDX */
1030     case 0x56: /* BDX-DE */
1031       do_rapl = RAPL_PKG | RAPL_DRAM;
1032       do_power_fields = TURBO_PLATFORM | UFS_PLATFORM | PSTATES_PLATFORM;
1033       break;
1034     case 0x2D: /* SNB Xeon */
1035     case 0x3E: /* IVB Xeon */
1036       do_rapl = RAPL_PKG | RAPL_CORES | RAPL_DRAM;
1037       do_power_fields = TURBO_PLATFORM | PSTATES_PLATFORM;
1038       break;
1039     case 0x37: /* BYT */
1040     case 0x4D: /* AVN */
1041       do_rapl = RAPL_PKG | RAPL_CORES;
1042       break;
1043     default:
1044       do_rapl = 0;
1045     }
1046   } else {
1047     ERROR("turbostat plugin: Unsupported CPU (family: %#x, "
1048           "model: %#x)",
1049           family, model);
1050     return -1;
1051   }
1052
1053   /* Override detected values with configuration */
1054   if (apply_config_core_cstate)
1055     do_core_cstate = config_core_cstate;
1056   if (apply_config_pkg_cstate)
1057     do_pkg_cstate = config_pkg_cstate;
1058   if (apply_config_smi)
1059     do_smi = config_smi;
1060   if (apply_config_dts)
1061     do_dts = config_dts;
1062   if (apply_config_ptm)
1063     do_ptm = config_ptm;
1064   if (apply_config_rapl)
1065     do_rapl = config_rapl;
1066
1067   if (do_rapl) {
1068     unsigned long long msr;
1069     if (get_msr(0, MSR_RAPL_POWER_UNIT, &msr))
1070       return 0;
1071
1072     rapl_power_units = 1.0 / (1 << (msr & 0xF));
1073     if (model == 0x37)
1074       rapl_energy_units = 1.0 * (1 << (msr >> 8 & 0x1F)) / 1000000;
1075     else
1076       rapl_energy_units = 1.0 / (1 << (msr >> 8 & 0x1F));
1077   }
1078
1079   return 0;
1080 }
1081
1082 /********************
1083  * Topology Probing *
1084  ********************/
1085
1086 /*
1087  * Read a single int from a file.
1088  */
1089 static int __attribute__((format(printf, 1, 2)))
1090 parse_int_file(const char *fmt, ...) {
1091   va_list args;
1092   char path[PATH_MAX];
1093   int len;
1094
1095   va_start(args, fmt);
1096   len = vsnprintf(path, sizeof(path), fmt, args);
1097   va_end(args);
1098   if (len < 0 || len >= PATH_MAX) {
1099     ERROR("turbostat plugin: path truncated: '%s'", path);
1100     return -1;
1101   }
1102
1103   value_t v;
1104   if (parse_value_file(path, &v, DS_TYPE_DERIVE) != 0) {
1105     ERROR("turbostat plugin: Parsing \"%s\" failed.", path);
1106     return -1;
1107   }
1108
1109   return (int)v.derive;
1110 }
1111
1112 static int get_threads_on_core(unsigned int cpu) {
1113   char path[80];
1114   FILE *filep;
1115   int sib1, sib2;
1116   int matches;
1117   char character;
1118
1119   snprintf(path, sizeof(path),
1120            "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list", cpu);
1121   filep = fopen(path, "r");
1122   if (!filep) {
1123     ERROR("turbostat plugin: Failed to open '%s'", path);
1124     return -1;
1125   }
1126   /*
1127    * file format:
1128    * if a pair of number with a character between: 2 siblings (eg. 1-2, or 1,4)
1129    * otherwinse 1 sibling (self).
1130    */
1131   matches = fscanf(filep, "%d%c%d\n", &sib1, &character, &sib2);
1132
1133   fclose(filep);
1134
1135   if (matches == 3)
1136     return 2;
1137   else
1138     return 1;
1139 }
1140
1141 /*
1142  * run func(cpu) on every cpu in /proc/stat
1143  * return max_cpu number
1144  */
1145 static int __attribute__((warn_unused_result))
1146 for_all_proc_cpus(int(func)(unsigned int)) {
1147   FILE *fp;
1148   unsigned int cpu_num;
1149   int retval;
1150
1151   fp = fopen("/proc/stat", "r");
1152   if (!fp) {
1153     ERROR("turbostat plugin: Failed to open /proc/stat");
1154     return -1;
1155   }
1156
1157   retval = fscanf(fp, "cpu %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n");
1158   if (retval != 0) {
1159     ERROR("turbostat plugin: Failed to parse /proc/stat");
1160     fclose(fp);
1161     return -1;
1162   }
1163
1164   while (1) {
1165     retval =
1166         fscanf(fp, "cpu%u %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n", &cpu_num);
1167     if (retval != 1)
1168       break;
1169
1170     retval = func(cpu_num);
1171     if (retval) {
1172       fclose(fp);
1173       return retval;
1174     }
1175   }
1176   fclose(fp);
1177   return 0;
1178 }
1179
1180 /*
1181  * Update the stored topology.max_cpu_id
1182  */
1183 static int update_max_cpu_id(unsigned int cpu) {
1184   if (topology.max_cpu_id < cpu)
1185     topology.max_cpu_id = cpu;
1186   return 0;
1187 }
1188
1189 static int mark_cpu_present(unsigned int cpu) {
1190   CPU_SET_S(cpu, cpu_present_setsize, cpu_present_set);
1191   return 0;
1192 }
1193
1194 static int __attribute__((warn_unused_result))
1195 allocate_cpu_set(cpu_set_t **set, size_t *size) {
1196   *set = CPU_ALLOC(topology.max_cpu_id + 1);
1197   if (*set == NULL) {
1198     ERROR("turbostat plugin: Unable to allocate CPU state");
1199     return -1;
1200   }
1201   *size = CPU_ALLOC_SIZE(topology.max_cpu_id + 1);
1202   CPU_ZERO_S(*size, *set);
1203   return 0;
1204 }
1205
1206 /*
1207  * Build a local representation of the cpu distribution
1208  */
1209 static int __attribute__((warn_unused_result)) topology_probe(void) {
1210   int ret;
1211   unsigned int max_package_id, max_core_id, max_threads;
1212   max_package_id = max_core_id = max_threads = 0;
1213
1214   /* Clean topology */
1215   free(topology.cpus);
1216   memset(&topology, 0, sizeof(topology));
1217
1218   ret = for_all_proc_cpus(update_max_cpu_id);
1219   if (ret != 0)
1220     goto err;
1221
1222   topology.cpus =
1223       calloc(1, (topology.max_cpu_id + 1) * sizeof(struct cpu_topology));
1224   if (topology.cpus == NULL) {
1225     ERROR("turbostat plugin: Unable to allocate memory for CPU topology");
1226     return -1;
1227   }
1228
1229   ret = allocate_cpu_set(&cpu_present_set, &cpu_present_setsize);
1230   if (ret != 0)
1231     goto err;
1232   ret = allocate_cpu_set(&cpu_affinity_set, &cpu_affinity_setsize);
1233   if (ret != 0)
1234     goto err;
1235   ret = allocate_cpu_set(&cpu_saved_affinity_set, &cpu_saved_affinity_setsize);
1236   if (ret != 0)
1237     goto err;
1238
1239   ret = for_all_proc_cpus(mark_cpu_present);
1240   if (ret != 0)
1241     goto err;
1242
1243   /*
1244    * For online cpus
1245    * find max_core_id, max_package_id
1246    */
1247   for (unsigned int i = 0; i <= topology.max_cpu_id; ++i) {
1248     unsigned int num_threads;
1249     struct cpu_topology *cpu = &topology.cpus[i];
1250
1251     if (cpu_is_not_present(i)) {
1252       WARNING("turbostat plugin: cpu%d NOT PRESENT", i);
1253       continue;
1254     }
1255
1256     ret = parse_int_file(
1257         "/sys/devices/system/cpu/cpu%d/topology/physical_package_id", i);
1258     if (ret < 0)
1259       goto err;
1260     else
1261       cpu->package_id = (unsigned int)ret;
1262     if (cpu->package_id > max_package_id)
1263       max_package_id = cpu->package_id;
1264
1265     ret = parse_int_file("/sys/devices/system/cpu/cpu%d/topology/core_id", i);
1266     if (ret < 0)
1267       goto err;
1268     else
1269       cpu->core_id = (unsigned int)ret;
1270     if (cpu->core_id > max_core_id)
1271       max_core_id = cpu->core_id;
1272     ret = parse_int_file(
1273         "/sys/devices/system/cpu/cpu%d/topology/core_siblings_list", i);
1274     if (ret < 0)
1275       goto err;
1276     else if ((unsigned int)ret == i)
1277       cpu->first_core_in_package = true;
1278
1279     ret = get_threads_on_core(i);
1280     if (ret < 0)
1281       goto err;
1282     else
1283       num_threads = (unsigned int)ret;
1284     if (num_threads > max_threads)
1285       max_threads = num_threads;
1286     ret = parse_int_file(
1287         "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list", i);
1288     if (ret < 0)
1289       goto err;
1290     else if ((unsigned int)ret == i)
1291       cpu->first_thread_in_core = true;
1292
1293     DEBUG("turbostat plugin: cpu %d pkg %d core %d\n", i, cpu->package_id,
1294           cpu->core_id);
1295   }
1296   /* Num is max + 1 (need to count 0) */
1297   topology.num_packages = max_package_id + 1;
1298   topology.num_cores = max_core_id + 1;
1299   topology.num_threads = max_threads;
1300
1301   return 0;
1302 err:
1303   free(topology.cpus);
1304   return ret;
1305 }
1306
1307 /************************
1308  * Main alloc/init/free *
1309  ************************/
1310
1311 static int allocate_counters(struct thread_data **threads,
1312                              struct core_data **cores,
1313                              struct pkg_data **packages) {
1314   unsigned int total_threads, total_cores;
1315
1316   if ((topology.num_threads == 0) || (topology.num_cores == 0) ||
1317       (topology.num_packages == 0)) {
1318     ERROR(
1319         "turbostat plugin: Invalid topology: %u threads, %u cores, %u packages",
1320         topology.num_threads, topology.num_cores, topology.num_packages);
1321     return -1;
1322   }
1323
1324   total_threads =
1325       topology.num_threads * topology.num_cores * topology.num_packages;
1326   *threads = calloc(total_threads, sizeof(struct thread_data));
1327   if (*threads == NULL) {
1328     ERROR("turbostat plugin: calloc failed");
1329     return -1;
1330   }
1331
1332   for (unsigned int i = 0; i < total_threads; ++i)
1333     (*threads)[i].cpu_id = topology.max_cpu_id + 1;
1334
1335   total_cores = topology.num_cores * topology.num_packages;
1336   *cores = calloc(total_cores, sizeof(struct core_data));
1337   if (*cores == NULL) {
1338     ERROR("turbostat plugin: calloc failed");
1339     sfree(*threads);
1340     return -1;
1341   }
1342
1343   *packages = calloc(topology.num_packages, sizeof(struct pkg_data));
1344   if (*packages == NULL) {
1345     ERROR("turbostat plugin: calloc failed");
1346     sfree(*cores);
1347     sfree(*threads);
1348     return -1;
1349   }
1350
1351   return 0;
1352 }
1353
1354 static void init_counter(struct thread_data *thread_base,
1355                          struct core_data *core_base, struct pkg_data *pkg_base,
1356                          unsigned int cpu_id) {
1357   struct thread_data *t;
1358   struct core_data *c;
1359   struct pkg_data *p;
1360   struct cpu_topology *cpu = &topology.cpus[cpu_id];
1361
1362   t = GET_THREAD(thread_base, !(cpu->first_thread_in_core), cpu->core_id,
1363                  cpu->package_id);
1364   c = GET_CORE(core_base, cpu->core_id, cpu->package_id);
1365   p = GET_PKG(pkg_base, cpu->package_id);
1366
1367   t->cpu_id = cpu_id;
1368   if (cpu->first_thread_in_core)
1369     t->flags |= CPU_IS_FIRST_THREAD_IN_CORE;
1370   if (cpu->first_core_in_package)
1371     t->flags |= CPU_IS_FIRST_CORE_IN_PACKAGE;
1372
1373   c->core_id = cpu->core_id;
1374   p->package_id = cpu->package_id;
1375 }
1376
1377 static void initialize_counters(void) {
1378   for (unsigned int cpu_id = 0; cpu_id <= topology.max_cpu_id; ++cpu_id) {
1379     if (cpu_is_not_present(cpu_id))
1380       continue;
1381     init_counter(EVEN_COUNTERS, cpu_id);
1382     init_counter(ODD_COUNTERS, cpu_id);
1383     init_counter(DELTA_COUNTERS, cpu_id);
1384   }
1385 }
1386
1387 static void free_all_buffers(void) {
1388   allocated = false;
1389   initialized = false;
1390
1391   CPU_FREE(cpu_present_set);
1392   cpu_present_set = NULL;
1393   cpu_present_setsize = 0;
1394
1395   CPU_FREE(cpu_affinity_set);
1396   cpu_affinity_set = NULL;
1397   cpu_affinity_setsize = 0;
1398
1399   CPU_FREE(cpu_saved_affinity_set);
1400   cpu_saved_affinity_set = NULL;
1401   cpu_saved_affinity_setsize = 0;
1402
1403   free(thread_even);
1404   free(core_even);
1405   free(package_even);
1406
1407   thread_even = NULL;
1408   core_even = NULL;
1409   package_even = NULL;
1410
1411   free(thread_odd);
1412   free(core_odd);
1413   free(package_odd);
1414
1415   thread_odd = NULL;
1416   core_odd = NULL;
1417   package_odd = NULL;
1418
1419   free(thread_delta);
1420   free(core_delta);
1421   free(package_delta);
1422
1423   thread_delta = NULL;
1424   core_delta = NULL;
1425   package_delta = NULL;
1426 }
1427
1428 /**********************
1429  * Collectd functions *
1430  **********************/
1431
1432 #define DO_OR_GOTO_ERR(something)                                              \
1433   do {                                                                         \
1434     ret = (something);                                                         \
1435     if (ret < 0)                                                               \
1436       goto err;                                                                \
1437   } while (0)
1438
1439 static int setup_all_buffers(void) {
1440   int ret;
1441
1442   DO_OR_GOTO_ERR(topology_probe());
1443   DO_OR_GOTO_ERR(allocate_counters(&thread_even, &core_even, &package_even));
1444   DO_OR_GOTO_ERR(allocate_counters(&thread_odd, &core_odd, &package_odd));
1445   DO_OR_GOTO_ERR(allocate_counters(&thread_delta, &core_delta, &package_delta));
1446   initialize_counters();
1447   DO_OR_GOTO_ERR(for_all_cpus(set_temperature_target, EVEN_COUNTERS));
1448   DO_OR_GOTO_ERR(for_all_cpus(set_temperature_target, ODD_COUNTERS));
1449
1450   allocated = true;
1451   return 0;
1452 err:
1453   free_all_buffers();
1454   return ret;
1455 }
1456
1457 static int turbostat_read(void) {
1458   int ret;
1459
1460   if (!allocated) {
1461     if ((ret = setup_all_buffers()) < 0)
1462       return ret;
1463   }
1464
1465   if (for_all_proc_cpus(cpu_is_not_present)) {
1466     free_all_buffers();
1467     if ((ret = setup_all_buffers()) < 0)
1468       return ret;
1469     if (for_all_proc_cpus(cpu_is_not_present)) {
1470       ERROR("turbostat plugin: CPU appeared just after "
1471             "initialization");
1472       return -1;
1473     }
1474   }
1475
1476   /* Saving the scheduling affinity, as it will be modified by get_counters */
1477   if (sched_getaffinity(0, cpu_saved_affinity_setsize,
1478                         cpu_saved_affinity_set) != 0) {
1479     ERROR("turbostat plugin: Unable to save the CPU affinity: %s", STRERRNO);
1480     return -1;
1481   }
1482
1483   if (!initialized) {
1484     if ((ret = for_all_cpus(get_counters, EVEN_COUNTERS)) < 0)
1485       goto out;
1486     time_even = cdtime();
1487     is_even = true;
1488     initialized = true;
1489     ret = 0;
1490     goto out;
1491   }
1492
1493   if (is_even) {
1494     if ((ret = for_all_cpus(get_counters, ODD_COUNTERS)) < 0)
1495       goto out;
1496     time_odd = cdtime();
1497     is_even = false;
1498     time_delta = time_odd - time_even;
1499     if ((ret = for_all_cpus_delta(ODD_COUNTERS, EVEN_COUNTERS)) < 0)
1500       goto out;
1501     if ((ret = for_all_cpus(submit_counters, DELTA_COUNTERS)) < 0)
1502       goto out;
1503   } else {
1504     if ((ret = for_all_cpus(get_counters, EVEN_COUNTERS)) < 0)
1505       goto out;
1506     time_even = cdtime();
1507     is_even = true;
1508     time_delta = time_even - time_odd;
1509     if ((ret = for_all_cpus_delta(EVEN_COUNTERS, ODD_COUNTERS)) < 0)
1510       goto out;
1511     if ((ret = for_all_cpus(submit_counters, DELTA_COUNTERS)) < 0)
1512       goto out;
1513   }
1514   ret = 0;
1515 out:
1516   /*
1517    * Let's restore the affinity
1518    * This might fail if the number of CPU changed, but we can't do anything in
1519    * that case..
1520    */
1521   (void)sched_setaffinity(0, cpu_saved_affinity_setsize,
1522                           cpu_saved_affinity_set);
1523   return ret;
1524 }
1525
1526 static int check_permissions(void) {
1527
1528   if (getuid() == 0) {
1529     /* We have everything we need */
1530     return 0;
1531 #if !defined(HAVE_SYS_CAPABILITY_H) && !defined(CAP_SYS_RAWIO)
1532   } else {
1533     ERROR("turbostat plugin: Initialization failed: this plugin "
1534           "requires collectd to run as root");
1535     return -1;
1536   }
1537 #else  /* HAVE_SYS_CAPABILITY_H && CAP_SYS_RAWIO */
1538   }
1539
1540   int ret = 0;
1541
1542   if (check_capability(CAP_SYS_RAWIO) != 0) {
1543     WARNING("turbostat plugin: Collectd doesn't have the "
1544             "CAP_SYS_RAWIO capability. If you don't want to run "
1545             "collectd as root, try running \"setcap "
1546             "cap_sys_rawio=ep\" on collectd binary");
1547     ret = -1;
1548   }
1549
1550   if (euidaccess("/dev/cpu/0/msr", R_OK)) {
1551     WARNING("turbostat plugin: Collectd cannot open "
1552             "/dev/cpu/0/msr. If you don't want to run collectd as "
1553             "root, you need to change the ownership (chown) and "
1554             "permissions on /dev/cpu/*/msr to allow such access");
1555     ret = -1;
1556   }
1557
1558   if (ret != 0)
1559     ERROR("turbostat plugin: Initialization failed: this plugin "
1560           "requires collectd to either to run as root or give "
1561           "collectd a special capability (CAP_SYS_RAWIO) and read "
1562           "access to /dev/cpu/*/msr (see previous warnings)");
1563   return ret;
1564 #endif /* HAVE_SYS_CAPABILITY_H && CAP_SYS_RAWIO */
1565 }
1566
1567 static int turbostat_init(void) {
1568   struct stat sb;
1569   int ret;
1570
1571   if (stat("/dev/cpu/0/msr", &sb)) {
1572     ERROR("turbostat plugin: Initialization failed: /dev/cpu/0/msr "
1573           "does not exist while the CPU supports MSR. You may be "
1574           "missing the corresponding kernel module, please try '# "
1575           "modprobe msr'");
1576     return -1;
1577   }
1578
1579   DO_OR_GOTO_ERR(check_permissions());
1580
1581   DO_OR_GOTO_ERR(probe_cpu());
1582
1583   DO_OR_GOTO_ERR(setup_all_buffers());
1584
1585   plugin_register_read(PLUGIN_NAME, turbostat_read);
1586
1587   return 0;
1588 err:
1589   free_all_buffers();
1590   return ret;
1591 }
1592
1593 static int turbostat_config(const char *key, const char *value) {
1594   long unsigned int tmp_val;
1595   char *end;
1596
1597   if (strcasecmp("CoreCstates", key) == 0) {
1598     tmp_val = strtoul(value, &end, 0);
1599     if (*end != '\0' || tmp_val > UINT_MAX) {
1600       ERROR("turbostat plugin: Invalid CoreCstates '%s'", value);
1601       return -1;
1602     }
1603     config_core_cstate = (unsigned int)tmp_val;
1604     apply_config_core_cstate = true;
1605   } else if (strcasecmp("PackageCstates", key) == 0) {
1606     tmp_val = strtoul(value, &end, 0);
1607     if (*end != '\0' || tmp_val > UINT_MAX) {
1608       ERROR("turbostat plugin: Invalid PackageCstates '%s'", value);
1609       return -1;
1610     }
1611     config_pkg_cstate = (unsigned int)tmp_val;
1612     apply_config_pkg_cstate = true;
1613   } else if (strcasecmp("SystemManagementInterrupt", key) == 0) {
1614     config_smi = IS_TRUE(value);
1615     apply_config_smi = true;
1616   } else if (strcasecmp("DigitalTemperatureSensor", key) == 0) {
1617     config_dts = IS_TRUE(value);
1618     apply_config_dts = true;
1619   } else if (strcasecmp("PackageThermalManagement", key) == 0) {
1620     config_ptm = IS_TRUE(value);
1621     apply_config_ptm = true;
1622   } else if (strcasecmp("LogicalCoreNames", key) == 0) {
1623     config_lcn = IS_TRUE(value);
1624   } else if (strcasecmp("RunningAveragePowerLimit", key) == 0) {
1625     tmp_val = strtoul(value, &end, 0);
1626     if (*end != '\0' || tmp_val > UINT_MAX) {
1627       ERROR("turbostat plugin: Invalid RunningAveragePowerLimit '%s'", value);
1628       return -1;
1629     }
1630     config_rapl = (unsigned int)tmp_val;
1631     apply_config_rapl = true;
1632   } else if (strcasecmp("TCCActivationTemp", key) == 0) {
1633     tmp_val = strtoul(value, &end, 0);
1634     if (*end != '\0' || tmp_val > UINT_MAX) {
1635       ERROR("turbostat plugin: Invalid TCCActivationTemp '%s'", value);
1636       return -1;
1637     }
1638     tcc_activation_temp = (unsigned int)tmp_val;
1639   } else {
1640     ERROR("turbostat plugin: Invalid configuration option '%s'", key);
1641     return -1;
1642   }
1643   return 0;
1644 }
1645
1646 void module_register(void) {
1647   plugin_register_init(PLUGIN_NAME, turbostat_init);
1648   plugin_register_config(PLUGIN_NAME, turbostat_config, config_keys,
1649                          config_keys_num);
1650 }