hugepages plugin: Parse page size from the directory name.
[collectd.git] / src / hugepages.c
1 /*-
2  * collectd - src/hugepages.c
3  * MIT License
4  *
5  * Copyright(c) 2016 Intel Corporation. All rights reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  *
25  * Authors:
26  *   Jaroslav Safka <jaroslavx.safka@intel.com>
27  *   Kim-Marie Jones <kim-marie.jones@intel.com>
28  */
29
30 #include "collectd.h"
31 #include "common.h" /* auxiliary functions */
32 #include "plugin.h" /* plugin_register_*, plugin_dispatch_values */
33
34 static const char g_plugin_name[] = "hugepages";
35 static const char g_cfg_rpt_numa[] = "ReportPerNodeHP";
36 static const char g_cfg_rpt_mm[] = "ReportRootHP";
37
38 static const char *g_config_keys[] = {
39     g_cfg_rpt_numa, g_cfg_rpt_mm,
40 };
41 static size_t g_config_keys_num = STATIC_ARRAY_SIZE(g_config_keys);
42 static int g_flag_rpt_numa = 1;
43 static int g_flag_rpt_mm = 1;
44
45 #define HP_HAVE_NR 0x01
46 #define HP_HAVE_SURPLUS 0x02
47 #define HP_HAVE_FREE 0x04
48 #define HP_HAVE_ALL 0x07
49
50 struct entry_info {
51   char *d_name;
52   const char *node;
53   size_t page_size_kb;
54
55   gauge_t nr;
56   gauge_t surplus;
57   gauge_t free;
58   uint8_t flags;
59 };
60
61 static int huge_config_callback(const char *key, const char *val) {
62   DEBUG("%s: HugePages config key='%s', val='%s'", g_plugin_name, key, val);
63
64   if (strcasecmp(key, g_cfg_rpt_numa) == 0) {
65     g_flag_rpt_numa = IS_TRUE(val);
66     return 0;
67   }
68   if (strcasecmp(key, g_cfg_rpt_mm) == 0) {
69     g_flag_rpt_mm = IS_TRUE(val);
70     return 0;
71   }
72
73   return -1;
74 }
75
76 static void submit_hp(const char *plug_inst, const char *type_instance,
77                       gauge_t free_value, gauge_t used_value) {
78   value_list_t vl = VALUE_LIST_INIT;
79   value_t values[] = {
80     { .gauge = free_value },
81     { .gauge = used_value },
82   };
83
84   vl.values = values;
85   vl.values_len = STATIC_ARRAY_SIZE (values);
86   sstrncpy(vl.host, hostname_g, sizeof(vl.host));
87   sstrncpy(vl.plugin, g_plugin_name, sizeof(vl.plugin));
88   sstrncpy(vl.plugin_instance, plug_inst, sizeof(vl.plugin_instance));
89   sstrncpy(vl.type, "hugepages", sizeof(vl.type));
90
91   if (type_instance != NULL) {
92     sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
93   }
94
95   DEBUG("submit_hp pl_inst:%s, inst_type %s, free=%lf, used=%lf", plug_inst,
96         type_instance, free_value, used_value);
97
98   plugin_dispatch_values(&vl);
99 }
100
101 static int read_hugepage_entry(const char *path, const char *entry,
102                                void *e_info) {
103   char path2[PATH_MAX];
104   char type_instance[PATH_MAX];
105   struct entry_info *info = e_info;
106   double value;
107
108   ssnprintf(path2, sizeof(path2), "%s/%s", path, entry);
109
110   FILE *fh = fopen(path2, "rt");
111   if (fh == NULL) {
112     ERROR("%s: cannot open %s", g_plugin_name, path2);
113     return -1;
114   }
115
116   if (fscanf(fh, "%lf", &value) != 1) {
117     ERROR("%s: cannot parse file %s", g_plugin_name, path2);
118     fclose(fh);
119     return -1;
120   }
121   fclose(fh);
122
123   if (strcmp(entry, "nr_hugepages") == 0) {
124     info->nr = value;
125     info->flags |= HP_HAVE_NR;
126   } else if (strcmp(entry, "surplus_hugepages") == 0) {
127     info->surplus = value;
128     info->flags |= HP_HAVE_SURPLUS;
129   } else if (strcmp(entry, "free_hugepages") == 0) {
130     info->free = value;
131     info->flags |= HP_HAVE_FREE;
132   }
133
134   if (info->flags != HP_HAVE_ALL) {
135     return 0;
136   }
137
138   ssnprintf(type_instance, sizeof(type_instance), "free_used-%zukB",
139             info->page_size_kb);
140   submit_hp(info->node, type_instance, info->free,
141             (info->nr + info->surplus) - info->free);
142
143   /* Reset flags so subsequent calls don't submit again. */
144   info->flags = 0;
145   return 0;
146 }
147
148 static int read_syshugepages(const char *path, const char *node) {
149   static const char hugepages_dir[] = "hugepages-";
150   DIR *dir;
151   struct dirent *result;
152   char path2[PATH_MAX];
153
154   dir = opendir(path);
155   if (dir == NULL) {
156     ERROR("%s: cannot open directory %s", g_plugin_name, path);
157     return -1;
158   }
159
160   /* read "hugepages-XXXXXkB" entries */
161   while ((result = readdir(dir)) != NULL) {
162     if (strncmp(result->d_name, hugepages_dir, sizeof(hugepages_dir) - 1)) {
163       /* not node dir */
164       errno = 0;
165       continue;
166     }
167
168     long page_size = strtol(result->d_name + strlen(hugepages_dir),
169                             /* endptr = */ NULL, /* base = */ 10);
170     if (errno != 0) {
171       char errbuf[1024];
172       ERROR("%s: failed to determine page size from directory name \"%s\": %s",
173             g_plugin_name, result->d_name,
174             sstrerror(errno, errbuf, sizeof(errbuf)));
175       continue;
176     }
177
178     /* /sys/devices/system/node/node?/hugepages/ */
179     ssnprintf(path2, sizeof(path2), "%s/%s", path, result->d_name);
180
181     walk_directory(path2, read_hugepage_entry,
182                    &(struct entry_info){
183                        .d_name = result->d_name,
184                        .node = node,
185                        .page_size_kb = (size_t)page_size,
186                    },
187                    /* hidden = */ 0);
188     errno = 0;
189   }
190
191   /* Check if NULL return from readdir() was an error */
192   if (errno != 0) {
193     ERROR("%s: readdir failed", g_plugin_name);
194     closedir(dir);
195     return -1;
196   }
197
198   closedir(dir);
199   return 0;
200 }
201
202 static int read_nodes(void) {
203   static const char sys_node[] = "/sys/devices/system/node";
204   static const char node_string[] = "node";
205   static const char sys_node_hugepages[] =
206       "/sys/devices/system/node/%s/hugepages";
207   DIR *dir;
208   struct dirent *result;
209   char path[PATH_MAX];
210
211   dir = opendir(sys_node);
212   if (dir == NULL) {
213     ERROR("%s: cannot open directory %s", g_plugin_name, sys_node);
214     return -1;
215   }
216
217   while ((result = readdir(dir)) != NULL) {
218     if (strncmp(result->d_name, node_string, sizeof(node_string) - 1)) {
219       /* not node dir */
220       errno = 0;
221       continue;
222     }
223
224     ssnprintf(path, sizeof(path), sys_node_hugepages, result->d_name);
225     read_syshugepages(path, result->d_name);
226     errno = 0;
227   }
228
229   /* Check if NULL return from readdir() was an error */
230   if (errno != 0) {
231     ERROR("%s: readdir failed", g_plugin_name);
232     closedir(dir);
233     return -1;
234   }
235
236   closedir(dir);
237   return 0;
238 }
239
240 static int huge_read(void) {
241   static const char sys_mm_hugepages[] = "/sys/kernel/mm/hugepages";
242
243   if (g_flag_rpt_mm) {
244     if (read_syshugepages(sys_mm_hugepages, "mm") != 0) {
245       return -1;
246     }
247   }
248   if (g_flag_rpt_numa) {
249     if (read_nodes() != 0) {
250       return -1;
251     }
252   }
253
254   return 0;
255 }
256
257 void module_register(void) {
258   plugin_register_config(g_plugin_name, huge_config_callback, g_config_keys,
259                          g_config_keys_num);
260   plugin_register_read(g_plugin_name, huge_read);
261 }