From 178402a00aec25297de1fdfd640d519401e4df18 Mon Sep 17 00:00:00 2001 From: Jaroslav Safka Date: Thu, 23 Jun 2016 05:53:12 +0100 Subject: [PATCH] hugepages plugin: a stats plugin for hugepages Plugin that allows stats for hugepages to be collected. Change-Id: Ide5ff31c54206cf5ee3d6155899bd53e8edae7bd Signed-off-by: Jaroslav Safka Signed-off-by: Kim Jones Signed-off-by: Maryam Tahhan --- README | 6 ++ configure.ac | 5 + src/Makefile.am | 7 ++ src/collectd.conf.in | 6 ++ src/collectd.conf.pod | 20 ++++ src/hugepages.c | 286 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/types.db | 6 ++ 7 files changed, 336 insertions(+) create mode 100644 src/hugepages.c diff --git a/README b/README index 1c3af7a4..1693c75e 100644 --- a/README +++ b/README @@ -128,6 +128,12 @@ Features - hddtemp Hard disk temperatures using hddtempd. + - hugepages + Report the total number of hugpages, the number of free/reserved + hugepages, and the size of the available hugepages. More info on + hugepages can be found here: + https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt. + - interface Interface traffic: Number of octets, packets and errors for each interface. diff --git a/configure.ac b/configure.ac index c69842cc..a69f16bb 100644 --- a/configure.ac +++ b/configure.ac @@ -482,6 +482,9 @@ fi # For hddtemp module AC_CHECK_HEADERS(linux/major.h) +# For hugepages module +AC_CHECK_HEADERS(dirent.h) + # For md module (Linux only) if test "x$ac_system" = "xLinux" then @@ -5971,6 +5974,7 @@ AC_PLUGIN([fscache], [$plugin_fscache], [fscache statistics] AC_PLUGIN([gmond], [$with_libganglia], [Ganglia plugin]) AC_PLUGIN([grpc], [$with_grpc], [gRPC plugin]) AC_PLUGIN([hddtemp], [yes], [Query hddtempd]) +AC_PLUGIN([hugepages], [yes], [Hugepages statistics]) AC_PLUGIN([interface], [$plugin_interface], [Interface traffic statistics]) AC_PLUGIN([ipc], [$plugin_ipc], [IPC statistics]) AC_PLUGIN([ipmi], [$plugin_ipmi], [IPMI sensor statistics]) @@ -6382,6 +6386,7 @@ AC_MSG_RESULT([ fscache . . . . . . . $enable_fscache]) AC_MSG_RESULT([ gmond . . . . . . . . $enable_gmond]) AC_MSG_RESULT([ grpc . . . . . . . . $enable_grpc]) AC_MSG_RESULT([ hddtemp . . . . . . . $enable_hddtemp]) +AC_MSG_RESULT([ hugepages . . . . . . $enable_hugepages]) AC_MSG_RESULT([ interface . . . . . . $enable_interface]) AC_MSG_RESULT([ ipc . . . . . . . . . $enable_ipc]) AC_MSG_RESULT([ ipmi . . . . . . . . $enable_ipmi]) diff --git a/src/Makefile.am b/src/Makefile.am index 641e2fae..0cc7e1cb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -442,6 +442,13 @@ hddtemp_la_LIBADD += -lsocket endif endif +if BUILD_PLUGIN_HUGEPAGES +pkglib_LTLIBRARIES += hugepages.la +hugepages_la_SOURCES = hugepages.c +hugepages_la_LDFLAGS = $(PLUGIN_LDFLAGS) +hugepages_la_LIBADD = +endif + if BUILD_PLUGIN_INTERFACE pkglib_LTLIBRARIES += interface.la interface_la_SOURCES = interface.c diff --git a/src/collectd.conf.in b/src/collectd.conf.in index 54072d0f..2b57a687 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -124,6 +124,7 @@ #@BUILD_PLUGIN_GMOND_TRUE@LoadPlugin gmond #@BUILD_PLUGIN_GRPC_TRUE@LoadPlugin grpc #@BUILD_PLUGIN_HDDTEMP_TRUE@LoadPlugin hddtemp +#@BUILD_PLUGIN_HUGEPAGES_TRUE@LoadPlugin hugepages @BUILD_PLUGIN_INTERFACE_TRUE@@BUILD_PLUGIN_INTERFACE_TRUE@LoadPlugin interface #@BUILD_PLUGIN_IPC_TRUE@LoadPlugin ipc #@BUILD_PLUGIN_IPMI_TRUE@LoadPlugin ipmi @@ -579,6 +580,11 @@ # Port "7634" # +# +# EnableNuma "true" +# EnableMM "true" +# + # # Interface "eth0" # IgnoreSelected false diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index 37a4d6b6..0a7bd014 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -2699,6 +2699,26 @@ TCP-Port to connect to. Defaults to B<7634>. =back +=head2 Plugin C + +To get values from B collectd read directories +"/sys/devices/system/node/*/hugepages" and +"/sys/kernel/mm/hugepages" +Reading of these directories can be disabled by following +options. Default is enabled. + +=over 4 + +=item B I|I + +Enable/Disable reading stats in "/sys/devices/system/node/*/hugepages" + +=item B I|I + +Enable/Disable reading stats in "/sys/kernel/mm/hugepages" + +=back + =head2 Plugin C =over 4 diff --git a/src/hugepages.c b/src/hugepages.c new file mode 100644 index 00000000..db18de05 --- /dev/null +++ b/src/hugepages.c @@ -0,0 +1,286 @@ +/*- + * collectd - src/hugepages.c + * MIT License + * + * 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: + * + * 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, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Jaroslav Safka + */ + +#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 int huge_config_callback (const char *key, const char *val) +{ + INFO("HugePages config key='%s', val='%s'", key, val); + + 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; + } + + return -1; +} + +static void submit_one (const char *plug_inst, const char *type, + const char *type_instance, derive_t value) +{ + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; + + values[0].derive = value; + + vl.values = values; + 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)); + } + + DEBUG("submit_one pl_inst:%s, inst_type %s, type %s, val=%lu", + plug_inst, type_instance, type, value); + + plugin_dispatch_values (&vl); +} + +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); + + FILE *fh = fopen(path2, "rt"); + if (NULL == fh) { + ERROR("Cannot open %s", path2); + return -1; + } + + if (fscanf(fh, "%ld", &value) !=1) { + ERROR("Cannot parse file %s", 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; + } + + 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); + } + + free(entry); + closedir(dir); + + + 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]; + + dir = opendir(path); + if (NULL == dir) { + ERROR("Cannot open directory %s", 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; + } + + if (strncmp(result->d_name, HUGEPAGES_DIR, sizeof(HUGEPAGES_DIR)-1)) { + /* not node dir */ + 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); + } + + free(entry); + 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"); + 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)) { + /* not node dir */ + continue; + } + + snprintf(path, sizeof(path), SYS_NODE_HUGEPAGES, result->d_name); + read_syshugepages(path, result->d_name); + } + + free(entry); + closedir(dir); + + return 0; +} + + +static int huge_read (void) +{ + if (g_config_ena_mm) { + read_syshugepages(SYS_MM_HUGEPAGES, "mm"); + } + if (g_config_ena_numa) { + read_nodes(); + } + + 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); +} + diff --git a/src/types.db b/src/types.db index 77a9d06f..8a1581a4 100644 --- a/src/types.db +++ b/src/types.db @@ -82,6 +82,7 @@ file_size value:GAUGE:0:U files value:GAUGE:0:U flow value:GAUGE:0:U fork_rate value:DERIVE:0:U +free_hugepages value:DERIVE:0:U frequency value:GAUGE:0:U frequency_error value:GAUGE:-2:2 frequency_offset value:GAUGE:-1000000:1000000 @@ -148,6 +149,9 @@ node_octets rx:DERIVE:0:U, tx:DERIVE:0:U node_rssi value:GAUGE:0:255 node_stat value:DERIVE:0:U node_tx_rate value:GAUGE:0:127 +nr_overcommit_hugepages value:DERIVE:0:U +nr_hugepages value:DERIVE:0:U +nr_hugepages_mempolicy value:DERIVE:0:U objects value:GAUGE:0:U operations value:DERIVE:0:U packets value:DERIVE:0:U @@ -191,6 +195,7 @@ records value:GAUGE:0:U requests value:GAUGE:0:U response_code value:GAUGE:0:U response_time value:GAUGE:0:U +resv_hugepages value:DERIVE:0:U root_delay value:GAUGE:U:U root_dispersion value:GAUGE:U:U route_etx value:GAUGE:0:U @@ -210,6 +215,7 @@ snr value:GAUGE:0:U spam_check value:GAUGE:0:U spam_score value:GAUGE:U:U spl value:GAUGE:U:U +surplus_hugepages value:DERIVE:0:U swap value:GAUGE:0:1099511627776 swap_io value:DERIVE:0:U tcp_connections value:GAUGE:0:4294967295 -- 2.11.0