*
* Copyright(c) 2016 Intel Corporation. All rights reserved.
*
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is furnished to do
- * so, subject to the following conditions:
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
*
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
*
* Authors:
* Jaroslav Safka <jaroslavx.safka@intel.com>
+ * Kim-Marie Jones <kim-marie.jones@intel.com>
+ * Florian Forster <octo at collectd.org>
*/
#include "collectd.h"
+
#include "common.h" /* auxiliary functions */
#include "plugin.h" /* plugin_register_*, plugin_dispatch_values */
-#include <stdio.h>
-#include <string.h>
-#include <dirent.h>
-
-static int huge_read (void);
-static int huge_config_callback (const char *key, const char *val);
-
-static const char PLUGIN_NAME[] = "hugepages";
-static const char SYS_NODE[] = "/sys/devices/system/node";
-static const char NODE[] = "node";
-static const char HUGEPAGES_DIR[] = "hugepages";
-static const char SYS_NODE_HUGEPAGES[] = "/sys/devices/system/node/%s/hugepages";
-static const char SYS_MM_HUGEPAGES[] = "/sys/kernel/mm/hugepages";
-static const char CONFIG_NAME[] = "hugepages";
-static const char CFG_ENA_NUMA[] = "EnableNuma";
-static const char CFG_ENA_MM[] = "EnableMM";
-
-static const char *CONFIG_KEYS[] = {
- CFG_ENA_NUMA,
- CFG_ENA_MM,
-};
-static const size_t CONFIG_KEYS_NUM = sizeof(CONFIG_KEYS)/sizeof(*CONFIG_NAME);
-static int g_config_ena_numa = 1;
-static int g_config_ena_mm = 1;
+static const char g_plugin_name[] = "hugepages";
-static int huge_config_callback (const char *key, const char *val)
-{
- INFO("HugePages config key='%s', val='%s'", key, val);
+static _Bool g_flag_rpt_numa = 1;
+static _Bool g_flag_rpt_mm = 1;
- if (0 == strcasecmp(key, CFG_ENA_NUMA)) {
- g_config_ena_numa = IS_TRUE(val);
- return 0;
- }
- if (0 == strcasecmp(key, CFG_ENA_MM)) {
- g_config_ena_mm = IS_TRUE(val);
- return 0;
+static _Bool g_values_pages = 1;
+static _Bool g_values_bytes = 0;
+static _Bool g_values_percent = 0;
+
+#define HP_HAVE_NR 0x01
+#define HP_HAVE_SURPLUS 0x02
+#define HP_HAVE_FREE 0x04
+#define HP_HAVE_ALL 0x07
+
+struct entry_info {
+ char *d_name;
+ const char *node;
+ size_t page_size_kb;
+
+ gauge_t nr;
+ gauge_t surplus;
+ gauge_t free;
+ uint8_t flags;
+};
+
+static int hp_config(oconfig_item_t *ci) {
+ for (int i = 0; i < ci->children_num; i++) {
+ oconfig_item_t *child = ci->children + i;
+ if (strcasecmp("ReportPerNodeHP", child->key) == 0)
+ cf_util_get_boolean(child, &g_flag_rpt_numa);
+ else if (strcasecmp("ReportRootHP", child->key) == 0)
+ cf_util_get_boolean(child, &g_flag_rpt_mm);
+ else if (strcasecmp("ValuesPages", child->key) == 0)
+ cf_util_get_boolean(child, &g_values_pages);
+ else if (strcasecmp("ValuesBytes", child->key) == 0)
+ cf_util_get_boolean(child, &g_values_bytes);
+ else if (strcasecmp("ValuesPercentage", child->key) == 0)
+ cf_util_get_boolean(child, &g_values_percent);
+ else
+ ERROR("%s: Invalid configuration option: \"%s\".", g_plugin_name,
+ child->key);
}
- return -1;
+ return (0);
}
-static void submit_one (const char *plug_inst, const char *type,
- const char *type_instance, derive_t value)
-{
- value_t values[1];
+static void submit_hp(const struct entry_info *info) {
value_list_t vl = VALUE_LIST_INIT;
- values[0].derive = value;
-
- vl.values = values;
+ vl.values = &(value_t){.gauge = NAN};
vl.values_len = 1;
- sstrncpy (vl.host, hostname_g, sizeof (vl.host));
- sstrncpy (vl.plugin, PLUGIN_NAME, sizeof (vl.plugin));
- sstrncpy (vl.plugin_instance, plug_inst, sizeof (vl.plugin_instance));
- sstrncpy (vl.type, type, sizeof (vl.type));
- if (type_instance != NULL) {
- sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+ sstrncpy(vl.plugin, g_plugin_name, sizeof(vl.plugin));
+ if (info->node) {
+ ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s-%zuKb",
+ info->node, info->page_size_kb);
+ } else {
+ ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%zuKb",
+ info->page_size_kb);
}
- DEBUG("submit_one pl_inst:%s, inst_type %s, type %s, val=%lu",
- plug_inst, type_instance, type, value);
+ /* ensure all metrics have the same timestamp */
+ vl.time = cdtime();
+
+ gauge_t free = info->free;
+ gauge_t used = (info->nr + info->surplus) - info->free;
- plugin_dispatch_values (&vl);
+ if (g_values_pages) {
+ sstrncpy(vl.type, "vmpage_number", sizeof(vl.type));
+ plugin_dispatch_multivalue(&vl, /* store_percentage = */ 0, DS_TYPE_GAUGE,
+ "free", free, "used", used, NULL);
+ }
+ if (g_values_bytes) {
+ gauge_t page_size = (gauge_t)(1024 * info->page_size_kb);
+ sstrncpy(vl.type, "memory", sizeof(vl.type));
+ plugin_dispatch_multivalue(&vl, /* store_percentage = */ 0, DS_TYPE_GAUGE,
+ "free", free * page_size, "used",
+ used * page_size, NULL);
+ }
+ if (g_values_percent) {
+ sstrncpy(vl.type, "percent", sizeof(vl.type));
+ plugin_dispatch_multivalue(&vl, /* store_percentage = */ 1, DS_TYPE_GAUGE,
+ "free", free, "used", used, NULL);
+ }
}
-static int read_hugepage_entry(const char *path, const char* entry,
- const char* plinst, const char* hpsize)
-{
- char path2[512];
- long value = 0;
- snprintf(path2, sizeof(path2), "%s/%s", path, entry);
+static int read_hugepage_entry(const char *path, const char *entry,
+ void *e_info) {
+ char path2[PATH_MAX];
+ struct entry_info *info = e_info;
+ double value;
+
+ ssnprintf(path2, sizeof(path2), "%s/%s", path, entry);
FILE *fh = fopen(path2, "rt");
- if (NULL == fh) {
- ERROR("Cannot open %s", path2);
+ if (fh == NULL) {
+ ERROR("%s: cannot open %s", g_plugin_name, path2);
return -1;
}
- if (fscanf(fh, "%ld", &value) !=1) {
- ERROR("Cannot parse file %s", path2);
+ if (fscanf(fh, "%lf", &value) != 1) {
+ ERROR("%s: cannot parse file %s", g_plugin_name, path2);
fclose(fh);
return -1;
}
-
- submit_one (plinst, entry, hpsize, value);
-
fclose(fh);
- return 0;
-}
-
-static int read_syshugepage_dir(const char* path, const char* dirhpsize,
- const char* node)
-{
- DIR *dir = NULL;
- struct dirent *entry = NULL;
- struct dirent *result = NULL;
- size_t name_max = 0;
- size_t len = 0;
- dir = opendir(path);
- if (NULL == dir) {
- ERROR("Cannot open directory %s", path);
- return -1;
+ if (strcmp(entry, "nr_hugepages") == 0) {
+ info->nr = value;
+ info->flags |= HP_HAVE_NR;
+ } else if (strcmp(entry, "surplus_hugepages") == 0) {
+ info->surplus = value;
+ info->flags |= HP_HAVE_SURPLUS;
+ } else if (strcmp(entry, "free_hugepages") == 0) {
+ info->free = value;
+ info->flags |= HP_HAVE_FREE;
}
- name_max = pathconf(path, _PC_NAME_MAX);
- if (name_max == -1) { /* Limit not defined, or error */
- name_max = 255; /* Take a guess */
- }
-
- len = offsetof(struct dirent, d_name) + name_max + 1;
- entry = malloc(len);
- if (entry == NULL) {
- ERROR("Malloc returned NULL");
- return -1;
- }
-
- while (0 == readdir_r(dir, entry, &result)) {
- if (NULL == result) {
- /* end of dir */
- break;
- }
- if (result->d_name[0] == '.') {
- /* not interesting "." and ".." */
- continue;
- }
-
- read_hugepage_entry(path, result->d_name, node, dirhpsize);
+ if (info->flags != HP_HAVE_ALL) {
+ return 0;
}
- free(entry);
- closedir(dir);
-
+ submit_hp(info);
+ /* Reset flags so subsequent calls don't submit again. */
+ info->flags = 0;
return 0;
}
-static int read_syshugepages(const char* path, const char* node)
-{
- DIR *dir = NULL;
- struct dirent *entry = NULL;
- struct dirent *result = NULL;
- size_t name_max = 0;
- size_t len = 0;
- char path2[255];
+static int read_syshugepages(const char *path, const char *node) {
+ static const char hugepages_dir[] = "hugepages-";
+ DIR *dir;
+ struct dirent *result;
+ char path2[PATH_MAX];
dir = opendir(path);
- if (NULL == dir) {
- ERROR("Cannot open directory %s", path);
+ if (dir == NULL) {
+ ERROR("%s: cannot open directory %s", g_plugin_name, path);
return -1;
}
- name_max = pathconf(path, _PC_NAME_MAX);
- if (name_max == -1) { /* Limit not defined, or error */
- name_max = 255; /* Take a guess */
- }
- len = offsetof(struct dirent, d_name) + name_max + 1;
- entry = malloc(len);
- if (entry == NULL) {
- ERROR("Malloc returned NULL");
- return -1;
- }
-
- while (0 == readdir_r(dir, entry, &result)) {
- /* read "hugepages-XXXXXkB" entries */
- if (NULL == result) {
- /* end of dir */
- break;
+ /* read "hugepages-XXXXXkB" entries */
+ while ((result = readdir(dir)) != NULL) {
+ if (strncmp(result->d_name, hugepages_dir, sizeof(hugepages_dir) - 1)) {
+ /* not node dir */
+ errno = 0;
+ continue;
}
- if (strncmp(result->d_name, HUGEPAGES_DIR, sizeof(HUGEPAGES_DIR)-1)) {
- /* not node dir */
+ long page_size = strtol(result->d_name + strlen(hugepages_dir),
+ /* endptr = */ NULL, /* base = */ 10);
+ if (errno != 0) {
+ char errbuf[1024];
+ ERROR("%s: failed to determine page size from directory name \"%s\": %s",
+ g_plugin_name, result->d_name,
+ sstrerror(errno, errbuf, sizeof(errbuf)));
continue;
}
/* /sys/devices/system/node/node?/hugepages/ */
- snprintf(path2, sizeof(path2), "%s/%s", path, result->d_name);
- read_syshugepage_dir(path2, result->d_name, node);
+ ssnprintf(path2, sizeof(path2), "%s/%s", path, result->d_name);
+
+ walk_directory(path2, read_hugepage_entry,
+ &(struct entry_info){
+ .d_name = result->d_name,
+ .node = node,
+ .page_size_kb = (size_t)page_size,
+ },
+ /* hidden = */ 0);
+ errno = 0;
}
- free(entry);
- closedir(dir);
+ /* Check if NULL return from readdir() was an error */
+ if (errno != 0) {
+ ERROR("%s: readdir failed", g_plugin_name);
+ closedir(dir);
+ return -1;
+ }
+ closedir(dir);
return 0;
}
-static int read_nodes(void)
-{
- DIR *dir = NULL;
- struct dirent *entry = NULL;
- struct dirent *result = NULL;
- size_t name_max = 0;
- size_t len = 0;
- char path[255];
-
- dir = opendir(SYS_NODE);
- if (NULL == dir) {
- ERROR("Cannot open directory %s", SYS_NODE);
+static int read_nodes(void) {
+ static const char sys_node[] = "/sys/devices/system/node";
+ static const char node_string[] = "node";
+ static const char sys_node_hugepages[] =
+ "/sys/devices/system/node/%s/hugepages";
+ DIR *dir;
+ struct dirent *result;
+ char path[PATH_MAX];
+
+ dir = opendir(sys_node);
+ if (dir == NULL) {
+ ERROR("%s: cannot open directory %s", g_plugin_name, sys_node);
return -1;
}
- name_max = pathconf(SYS_NODE, _PC_NAME_MAX);
- if (name_max == -1) { /* Limit not defined, or error */
- name_max = 255; /* Take a guess */
- }
- len = offsetof(struct dirent, d_name) + name_max + 1;
- entry = malloc(len);
- if (entry == NULL) {
- ERROR("Malloc returned NULL");
- return -1;
- }
-
- while (0 == readdir_r(dir, entry, &result)) {
- if (NULL == result) {
- /* end of dir */
- break;
- }
-
- if (strncmp(result->d_name, NODE, sizeof(NODE)-1)) {
+ while ((result = readdir(dir)) != NULL) {
+ if (strncmp(result->d_name, node_string, sizeof(node_string) - 1)) {
/* not node dir */
+ errno = 0;
continue;
}
- snprintf(path, sizeof(path), SYS_NODE_HUGEPAGES, result->d_name);
+ ssnprintf(path, sizeof(path), sys_node_hugepages, result->d_name);
read_syshugepages(path, result->d_name);
+ errno = 0;
}
- free(entry);
- closedir(dir);
+ /* Check if NULL return from readdir() was an error */
+ if (errno != 0) {
+ ERROR("%s: readdir failed", g_plugin_name);
+ closedir(dir);
+ return -1;
+ }
+ closedir(dir);
return 0;
}
+static int huge_read(void) {
+ static const char sys_mm_hugepages[] = "/sys/kernel/mm/hugepages";
-static int huge_read (void)
-{
- if (g_config_ena_mm) {
- read_syshugepages(SYS_MM_HUGEPAGES, "mm");
+ if (g_flag_rpt_mm) {
+ if (read_syshugepages(sys_mm_hugepages, "mm") != 0) {
+ return -1;
+ }
}
- if (g_config_ena_numa) {
- read_nodes();
+ if (g_flag_rpt_numa) {
+ if (read_nodes() != 0) {
+ return -1;
+ }
}
return 0;
}
-void module_register (void)
-{
- plugin_register_config(CONFIG_NAME, huge_config_callback, CONFIG_KEYS,
- CONFIG_KEYS_NUM);
- plugin_register_read (PLUGIN_NAME, huge_read);
+void module_register(void) {
+ plugin_register_complex_config(g_plugin_name, hp_config);
+ plugin_register_read(g_plugin_name, huge_read);
}
-