2 * collectd - src/intel_rdt.c
4 * Copyright(c) 2016 Intel Corporation. All rights reserved.
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * Serhiy Pshyk <serhiyx.pshyk@intel.com>
33 #define RDT_PLUGIN "intel_rdt"
35 #define RDT_MAX_SOCKETS 8
36 #define RDT_MAX_SOCKET_CORES 64
37 #define RDT_MAX_CORES (RDT_MAX_SOCKET_CORES * RDT_MAX_SOCKETS)
39 struct rdt_core_group_s {
43 enum pqos_mon_event events;
45 typedef struct rdt_core_group_s rdt_core_group_t;
48 rdt_core_group_t cgroups[RDT_MAX_CORES];
49 struct pqos_mon_data *pgroups[RDT_MAX_CORES];
51 const struct pqos_cpuinfo *pqos_cpu;
52 const struct pqos_cap *pqos_cap;
53 const struct pqos_capability *cap_mon;
55 typedef struct rdt_ctx_s rdt_ctx_t;
57 static rdt_ctx_t *g_rdt = NULL;
59 static int isdup(const uint64_t *nums, size_t size, uint64_t val) {
60 for (size_t i = 0; i < size; i++)
66 static int strtouint64(const char *s, uint64_t *n) {
72 *n = strtoull(s, &endptr, 0);
74 if (!(*s != '\0' && *endptr == '\0')) {
75 DEBUG(RDT_PLUGIN ": Error converting '%s' to unsigned number.", s);
87 * Converts string of characters representing list of numbers into array of
88 * numbers. Allowed formats are:
93 * Numbers can be in decimal or hexadecimal format.
96 * `s' String representing list of unsigned numbers.
97 * `nums' Array to put converted numeric values into.
98 * `max' Maximum number of elements that nums can accommodate.
101 * Number of elements placed into nums.
103 static size_t strlisttonums(char *s, uint64_t *nums, size_t max) {
106 char *saveptr = NULL;
108 if (s == NULL || nums == NULL || max == 0)
115 token = strtok_r(s, ",", &saveptr);
121 while (isspace(*token))
126 p = strchr(token, '-');
128 uint64_t n, start, end;
130 ret = strtouint64(token, &start);
133 ret = strtouint64(p + 1, &end);
139 for (n = start; n <= end; n++) {
140 if (!(isdup(nums, index, n))) {
150 ret = strtouint64(token, &val);
154 if (!(isdup(nums, index, val))) {
171 * Function to compare cores in 2 core groups.
174 * `cg_a' Pointer to core group a.
175 * `cg_b' Pointer to core group b.
178 * 1 if both groups contain the same cores
179 * 0 if none of their cores match
180 * -1 if some but not all cores match
182 static int cgroup_cmp(const rdt_core_group_t *cg_a,
183 const rdt_core_group_t *cg_b) {
186 assert(cg_a != NULL);
187 assert(cg_b != NULL);
189 const int sz_a = cg_a->num_cores;
190 const int sz_b = cg_b->num_cores;
191 const unsigned *tab_a = cg_a->cores;
192 const unsigned *tab_b = cg_b->cores;
194 for (int i = 0; i < sz_a; i++) {
195 for (int j = 0; j < sz_b; j++)
196 if (tab_a[i] == tab_b[j])
199 /* if no cores are the same */
202 /* if group contains same cores */
203 if (sz_a == sz_b && sz_b == found)
205 /* if not all cores are the same */
209 static int cgroup_set(rdt_core_group_t *cg, char *desc, uint64_t *cores,
212 assert(desc != NULL);
213 assert(cores != NULL);
214 assert(num_cores > 0);
216 cg->cores = calloc(num_cores, sizeof(unsigned));
217 if (cg->cores == NULL) {
218 ERROR(RDT_PLUGIN ": Error allocating core group table");
221 cg->num_cores = num_cores;
222 cg->desc = strdup(desc);
223 if (cg->desc == NULL) {
224 ERROR(RDT_PLUGIN ": Error allocating core group description");
229 for (size_t i = 0; i < num_cores; i++)
230 cg->cores[i] = (unsigned)cores[i];
240 * Function to set the descriptions and cores for each core group.
241 * Takes a config option containing list of strings that are used to set
245 * `item' Config option containing core groups.
246 * `groups' Table of core groups to set values in.
247 * `max_groups' Maximum number of core groups allowed.
248 * `max_core' Maximum allowed core value.
251 * On success, the number of core groups set up. On error, appropriate
252 * negative error value.
254 static int oconfig_to_cgroups(oconfig_item_t *item, rdt_core_group_t *groups,
255 size_t max_groups, uint64_t max_core) {
258 assert(groups != NULL);
259 assert(max_groups > 0);
260 assert(item != NULL);
262 for (int j = 0; j < item->values_num; j++) {
265 uint64_t cores[RDT_MAX_CORES] = {0};
266 char value[DATA_MAX_NAME_LEN];
268 if ((item->values[j].value.string == NULL) ||
269 (strlen(item->values[j].value.string) == 0))
272 sstrncpy(value, item->values[j].value.string, sizeof(value));
274 n = strlisttonums(value, cores, STATIC_ARRAY_SIZE(cores));
276 ERROR(RDT_PLUGIN ": Error parsing core group (%s)",
277 item->values[j].value.string);
281 for (int i = 0; i < n; i++) {
282 if (cores[i] > max_core) {
283 ERROR(RDT_PLUGIN ": Core group (%s) contains invalid core id (%d)",
284 item->values[j].value.string, (int)cores[i]);
289 /* set core group info */
290 ret = cgroup_set(&groups[index], item->values[j].value.string, cores, n);
296 if (index >= max_groups) {
297 WARNING(RDT_PLUGIN ": Too many core groups configured");
306 static void rdt_dump_cgroups(void) {
307 char cores[RDT_MAX_CORES * 4];
312 DEBUG(RDT_PLUGIN ": Core Groups Dump");
313 DEBUG(RDT_PLUGIN ": groups count: %zu", g_rdt->num_groups);
315 for (int i = 0; i < g_rdt->num_groups; i++) {
317 memset(cores, 0, sizeof(cores));
318 for (int j = 0; j < g_rdt->cgroups[i].num_cores; j++) {
319 snprintf(cores + strlen(cores), sizeof(cores) - strlen(cores) - 1, " %d",
320 g_rdt->cgroups[i].cores[j]);
323 DEBUG(RDT_PLUGIN ": group[%d]:", i);
324 DEBUG(RDT_PLUGIN ": description: %s", g_rdt->cgroups[i].desc);
325 DEBUG(RDT_PLUGIN ": cores: %s", cores);
326 DEBUG(RDT_PLUGIN ": events: 0x%X", g_rdt->cgroups[i].events);
332 static inline double bytes_to_kb(const double bytes) { return bytes / 1024.0; }
334 static inline double bytes_to_mb(const double bytes) {
335 return bytes / (1024.0 * 1024.0);
338 static void rdt_dump_data(void) {
340 * CORE - monitored group of cores
341 * RMID - Resource Monitoring ID associated with the monitored group
342 * LLC - last level cache occupancy
343 * MBL - local memory bandwidth
344 * MBR - remote memory bandwidth
346 DEBUG(" CORE RMID LLC[KB] MBL[MB] MBR[MB]");
347 for (int i = 0; i < g_rdt->num_groups; i++) {
349 const struct pqos_event_values *pv = &g_rdt->pgroups[i]->values;
351 double llc = bytes_to_kb(pv->llc);
352 double mbr = bytes_to_mb(pv->mbm_remote_delta);
353 double mbl = bytes_to_mb(pv->mbm_local_delta);
355 DEBUG(" [%s] %8u %10.1f %10.1f %10.1f", g_rdt->cgroups[i].desc,
356 g_rdt->pgroups[i]->poll_ctx[0].rmid, llc, mbl, mbr);
359 #endif /* COLLECT_DEBUG */
361 static void rdt_free_cgroups(void) {
362 for (int i = 0; i < RDT_MAX_CORES; i++) {
363 sfree(g_rdt->cgroups[i].desc);
365 sfree(g_rdt->cgroups[i].cores);
366 g_rdt->cgroups[i].num_cores = 0;
368 sfree(g_rdt->pgroups[i]);
372 static int rdt_default_cgroups(void) {
375 /* configure each core in separate group */
376 for (unsigned i = 0; i < g_rdt->pqos_cpu->num_cores; i++) {
377 char desc[DATA_MAX_NAME_LEN];
380 ssnprintf(desc, sizeof(desc), "%d", g_rdt->pqos_cpu->cores[i].lcore);
382 /* set core group info */
383 ret = cgroup_set(&g_rdt->cgroups[i], desc, &core, 1);
388 return g_rdt->pqos_cpu->num_cores;
391 static int rdt_config_cgroups(oconfig_item_t *item) {
393 enum pqos_mon_event events = 0;
396 DEBUG(RDT_PLUGIN ": cgroups_config: Invalid argument.");
400 DEBUG(RDT_PLUGIN ": Core groups [%d]:", item->values_num);
401 for (int j = 0; j < item->values_num; j++) {
402 if (item->values[j].type != OCONFIG_TYPE_STRING) {
403 ERROR(RDT_PLUGIN ": given core group value is not a string [idx=%d]", j);
406 DEBUG(RDT_PLUGIN ": [%d]: %s", j, item->values[j].value.string);
409 n = oconfig_to_cgroups(item, g_rdt->cgroups, RDT_MAX_CORES,
410 g_rdt->pqos_cpu->num_cores - 1);
413 ERROR(RDT_PLUGIN ": Error parsing core groups configuration.");
418 /* create default core groups if "Cores" config option is empty */
419 n = rdt_default_cgroups();
422 ERROR(RDT_PLUGIN ": Error creating default core groups configuration.");
426 ": No core groups configured. Default core groups created.");
429 /* Get all available events on this platform */
430 for (int i = 0; i < g_rdt->cap_mon->u.mon->num_events; i++)
431 events |= g_rdt->cap_mon->u.mon->events[i].type;
433 events &= ~(PQOS_PERF_EVENT_LLC_MISS);
435 DEBUG(RDT_PLUGIN ": Number of cores in the system: %u",
436 g_rdt->pqos_cpu->num_cores);
437 DEBUG(RDT_PLUGIN ": Available events to monitor: %#x", events);
439 g_rdt->num_groups = n;
440 for (int i = 0; i < n; i++) {
441 for (int j = 0; j < i; j++) {
443 found = cgroup_cmp(&g_rdt->cgroups[j], &g_rdt->cgroups[i]);
446 ERROR(RDT_PLUGIN ": Cannot monitor same cores in different groups.");
451 g_rdt->cgroups[i].events = events;
452 g_rdt->pgroups[i] = calloc(1, sizeof(*g_rdt->pgroups[i]));
453 if (g_rdt->pgroups[i] == NULL) {
455 ERROR(RDT_PLUGIN ": Failed to allocate memory for monitoring data.");
463 static void rdt_pqos_log(void *context, const size_t size, const char *msg) {
464 DEBUG(RDT_PLUGIN ": %s", msg);
467 static int rdt_preinit(void) {
471 /* already initialized if config callback was called before init callback */
475 g_rdt = calloc(1, sizeof(*g_rdt));
477 ERROR(RDT_PLUGIN ": Failed to allocate memory for rdt context.");
481 struct pqos_config pqos = {.fd_log = -1,
482 .callback_log = rdt_pqos_log,
486 ret = pqos_init(&pqos);
487 if (ret != PQOS_RETVAL_OK) {
488 ERROR(RDT_PLUGIN ": Error initializing PQoS library!");
489 goto rdt_preinit_error1;
492 ret = pqos_cap_get(&g_rdt->pqos_cap, &g_rdt->pqos_cpu);
493 if (ret != PQOS_RETVAL_OK) {
494 ERROR(RDT_PLUGIN ": Error retrieving PQoS capabilities.");
495 goto rdt_preinit_error2;
498 ret = pqos_cap_get_type(g_rdt->pqos_cap, PQOS_CAP_TYPE_MON, &g_rdt->cap_mon);
499 if (ret == PQOS_RETVAL_PARAM) {
500 ERROR(RDT_PLUGIN ": Error retrieving monitoring capabilities.");
501 goto rdt_preinit_error2;
504 if (g_rdt->cap_mon == NULL) {
507 ": Monitoring capability not detected. Nothing to do for the plugin.");
508 goto rdt_preinit_error2;
511 /* Reset pqos monitoring groups registers */
526 static int rdt_config(oconfig_item_t *ci) {
533 for (int i = 0; i < ci->children_num; i++) {
534 oconfig_item_t *child = ci->children + i;
536 if (strcasecmp("Cores", child->key) == 0) {
538 ret = rdt_config_cgroups(child);
544 #endif /* COLLECT_DEBUG */
547 ERROR(RDT_PLUGIN ": Unknown configuration parameter \"%s\".", child->key);
554 static void rdt_submit_derive(char *cgroup, char *type, char *type_instance,
556 value_list_t vl = VALUE_LIST_INIT;
558 vl.values = &(value_t){.derive = value};
561 sstrncpy(vl.plugin, RDT_PLUGIN, sizeof(vl.plugin));
562 snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s", cgroup);
563 sstrncpy(vl.type, type, sizeof(vl.type));
565 sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
567 plugin_dispatch_values(&vl);
570 static void rdt_submit_gauge(char *cgroup, char *type, char *type_instance,
572 value_list_t vl = VALUE_LIST_INIT;
574 vl.values = &(value_t){.gauge = value};
577 sstrncpy(vl.plugin, RDT_PLUGIN, sizeof(vl.plugin));
578 snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s", cgroup);
579 sstrncpy(vl.type, type, sizeof(vl.type));
581 sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
583 plugin_dispatch_values(&vl);
586 static int rdt_read(__attribute__((unused)) user_data_t *ud) {
590 ERROR(RDT_PLUGIN ": rdt_read: plugin not initialized.");
594 ret = pqos_mon_poll(&g_rdt->pgroups[0], (unsigned)g_rdt->num_groups);
595 if (ret != PQOS_RETVAL_OK) {
596 ERROR(RDT_PLUGIN ": Failed to poll monitoring data.");
602 #endif /* COLLECT_DEBUG */
604 for (int i = 0; i < g_rdt->num_groups; i++) {
605 enum pqos_mon_event mbm_events =
606 (PQOS_MON_EVENT_LMEM_BW | PQOS_MON_EVENT_TMEM_BW |
607 PQOS_MON_EVENT_RMEM_BW);
609 const struct pqos_event_values *pv = &g_rdt->pgroups[i]->values;
611 /* Submit only monitored events data */
613 if (g_rdt->cgroups[i].events & PQOS_MON_EVENT_L3_OCCUP)
614 rdt_submit_gauge(g_rdt->cgroups[i].desc, "bytes", "llc", pv->llc);
616 if (g_rdt->cgroups[i].events & PQOS_PERF_EVENT_IPC)
617 rdt_submit_gauge(g_rdt->cgroups[i].desc, "ipc", NULL, pv->ipc);
619 if (g_rdt->cgroups[i].events & mbm_events) {
620 rdt_submit_derive(g_rdt->cgroups[i].desc, "memory_bandwidth", "local",
621 pv->mbm_local_delta);
622 rdt_submit_derive(g_rdt->cgroups[i].desc, "memory_bandwidth", "remote",
623 pv->mbm_remote_delta);
630 static int rdt_init(void) {
637 /* Start monitoring */
638 for (int i = 0; i < g_rdt->num_groups; i++) {
639 rdt_core_group_t *cg = &g_rdt->cgroups[i];
641 ret = pqos_mon_start(cg->num_cores, cg->cores, cg->events, (void *)cg->desc,
644 if (ret != PQOS_RETVAL_OK)
645 ERROR(RDT_PLUGIN ": Error starting monitoring group %s (pqos status=%d)",
652 static int rdt_shutdown(void) {
655 DEBUG(RDT_PLUGIN ": rdt_shutdown.");
660 /* Stop monitoring */
661 for (int i = 0; i < g_rdt->num_groups; i++) {
662 pqos_mon_stop(g_rdt->pgroups[i]);
666 if (ret != PQOS_RETVAL_OK)
667 ERROR(RDT_PLUGIN ": Error shutting down PQoS library.");
675 void module_register(void) {
676 plugin_register_init(RDT_PLUGIN, rdt_init);
677 plugin_register_complex_config(RDT_PLUGIN, rdt_config);
678 plugin_register_complex_read(NULL, RDT_PLUGIN, rdt_read, 0, NULL);
679 plugin_register_shutdown(RDT_PLUGIN, rdt_shutdown);