X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fhugepages.c;h=c5b0ecb996328032fe1384469238412240a28bb5;hb=e746ad785774de37a30302fef65f1c4aaf8698ab;hp=fb736a6a991758554af96e211d70b55059e83804;hpb=fa49565fa7ef682ce61bc446fb0d2469eedddc66;p=collectd.git diff --git a/src/hugepages.c b/src/hugepages.c index fb736a6a..c5b0ecb9 100644 --- a/src/hugepages.c +++ b/src/hugepages.c @@ -4,15 +4,15 @@ * * 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, @@ -24,263 +24,254 @@ * * Authors: * Jaroslav Safka + * Kim-Marie Jones + * Florian Forster */ #include "collectd.h" + #include "common.h" /* auxiliary functions */ #include "plugin.h" /* plugin_register_*, plugin_dispatch_values */ -#include -#include -#include - -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) { + snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s-%zuKb", + info->node, info->page_size_kb); + } else { + snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%zuKb", + info->page_size_kb); } - DEBUG("submit_one pl_inst:%s, inst_type %s, type %s, val=%"PRIu64"", - plug_inst, type_instance, type, value); + /* ensure all metrics have the same timestamp */ + vl.time = cdtime(); - plugin_dispatch_values (&vl); + gauge_t free = info->free; + gauge_t used = (info->nr + info->surplus) - info->free; + + 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; +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; + snprintf(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); + + 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); - 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"); +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; } - 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); + snprintf(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); } -