hugepages plugin: Use the "complex" config callback.
[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
36 static _Bool g_flag_rpt_numa = 1;
37 static _Bool g_flag_rpt_mm = 1;
38
39 #define HP_HAVE_NR 0x01
40 #define HP_HAVE_SURPLUS 0x02
41 #define HP_HAVE_FREE 0x04
42 #define HP_HAVE_ALL 0x07
43
44 struct entry_info {
45   char *d_name;
46   const char *node;
47   size_t page_size_kb;
48
49   gauge_t nr;
50   gauge_t surplus;
51   gauge_t free;
52   uint8_t flags;
53 };
54
55 static int hp_config(oconfig_item_t *ci) {
56   for (int i = 0; i < ci->children_num; i++) {
57     oconfig_item_t *child = ci->children + i;
58     if (strcasecmp("ReportPerNodeHP", child->key) == 0)
59       cf_util_get_boolean(child, &g_flag_rpt_numa);
60     else if (strcasecmp("ReportRootHP", child->key) == 0)
61       cf_util_get_boolean(child, &g_flag_rpt_mm);
62     else
63       ERROR("%s: Invalid configuration option: \"%s\".", g_plugin_name,
64             child->key);
65   }
66
67   return (0);
68 }
69
70 static void submit_hp(const char *plug_inst, const char *type_instance,
71                       gauge_t free_value, gauge_t used_value) {
72   value_list_t vl = VALUE_LIST_INIT;
73   value_t values[] = {
74     { .gauge = free_value },
75     { .gauge = used_value },
76   };
77
78   vl.values = values;
79   vl.values_len = STATIC_ARRAY_SIZE (values);
80   sstrncpy(vl.host, hostname_g, sizeof(vl.host));
81   sstrncpy(vl.plugin, g_plugin_name, sizeof(vl.plugin));
82   sstrncpy(vl.plugin_instance, plug_inst, sizeof(vl.plugin_instance));
83   sstrncpy(vl.type, "hugepages", sizeof(vl.type));
84
85   if (type_instance != NULL) {
86     sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
87   }
88
89   DEBUG("submit_hp pl_inst:%s, inst_type %s, free=%lf, used=%lf", plug_inst,
90         type_instance, free_value, used_value);
91
92   plugin_dispatch_values(&vl);
93 }
94
95 static int read_hugepage_entry(const char *path, const char *entry,
96                                void *e_info) {
97   char path2[PATH_MAX];
98   char type_instance[PATH_MAX];
99   struct entry_info *info = e_info;
100   double value;
101
102   ssnprintf(path2, sizeof(path2), "%s/%s", path, entry);
103
104   FILE *fh = fopen(path2, "rt");
105   if (fh == NULL) {
106     ERROR("%s: cannot open %s", g_plugin_name, path2);
107     return -1;
108   }
109
110   if (fscanf(fh, "%lf", &value) != 1) {
111     ERROR("%s: cannot parse file %s", g_plugin_name, path2);
112     fclose(fh);
113     return -1;
114   }
115   fclose(fh);
116
117   if (strcmp(entry, "nr_hugepages") == 0) {
118     info->nr = value;
119     info->flags |= HP_HAVE_NR;
120   } else if (strcmp(entry, "surplus_hugepages") == 0) {
121     info->surplus = value;
122     info->flags |= HP_HAVE_SURPLUS;
123   } else if (strcmp(entry, "free_hugepages") == 0) {
124     info->free = value;
125     info->flags |= HP_HAVE_FREE;
126   }
127
128   if (info->flags != HP_HAVE_ALL) {
129     return 0;
130   }
131
132   ssnprintf(type_instance, sizeof(type_instance), "free_used-%zukB",
133             info->page_size_kb);
134   submit_hp(info->node, type_instance, info->free,
135             (info->nr + info->surplus) - info->free);
136
137   /* Reset flags so subsequent calls don't submit again. */
138   info->flags = 0;
139   return 0;
140 }
141
142 static int read_syshugepages(const char *path, const char *node) {
143   static const char hugepages_dir[] = "hugepages-";
144   DIR *dir;
145   struct dirent *result;
146   char path2[PATH_MAX];
147
148   dir = opendir(path);
149   if (dir == NULL) {
150     ERROR("%s: cannot open directory %s", g_plugin_name, path);
151     return -1;
152   }
153
154   /* read "hugepages-XXXXXkB" entries */
155   while ((result = readdir(dir)) != NULL) {
156     if (strncmp(result->d_name, hugepages_dir, sizeof(hugepages_dir) - 1)) {
157       /* not node dir */
158       errno = 0;
159       continue;
160     }
161
162     long page_size = strtol(result->d_name + strlen(hugepages_dir),
163                             /* endptr = */ NULL, /* base = */ 10);
164     if (errno != 0) {
165       char errbuf[1024];
166       ERROR("%s: failed to determine page size from directory name \"%s\": %s",
167             g_plugin_name, result->d_name,
168             sstrerror(errno, errbuf, sizeof(errbuf)));
169       continue;
170     }
171
172     /* /sys/devices/system/node/node?/hugepages/ */
173     ssnprintf(path2, sizeof(path2), "%s/%s", path, result->d_name);
174
175     walk_directory(path2, read_hugepage_entry,
176                    &(struct entry_info){
177                        .d_name = result->d_name,
178                        .node = node,
179                        .page_size_kb = (size_t)page_size,
180                    },
181                    /* hidden = */ 0);
182     errno = 0;
183   }
184
185   /* Check if NULL return from readdir() was an error */
186   if (errno != 0) {
187     ERROR("%s: readdir failed", g_plugin_name);
188     closedir(dir);
189     return -1;
190   }
191
192   closedir(dir);
193   return 0;
194 }
195
196 static int read_nodes(void) {
197   static const char sys_node[] = "/sys/devices/system/node";
198   static const char node_string[] = "node";
199   static const char sys_node_hugepages[] =
200       "/sys/devices/system/node/%s/hugepages";
201   DIR *dir;
202   struct dirent *result;
203   char path[PATH_MAX];
204
205   dir = opendir(sys_node);
206   if (dir == NULL) {
207     ERROR("%s: cannot open directory %s", g_plugin_name, sys_node);
208     return -1;
209   }
210
211   while ((result = readdir(dir)) != NULL) {
212     if (strncmp(result->d_name, node_string, sizeof(node_string) - 1)) {
213       /* not node dir */
214       errno = 0;
215       continue;
216     }
217
218     ssnprintf(path, sizeof(path), sys_node_hugepages, result->d_name);
219     read_syshugepages(path, result->d_name);
220     errno = 0;
221   }
222
223   /* Check if NULL return from readdir() was an error */
224   if (errno != 0) {
225     ERROR("%s: readdir failed", g_plugin_name);
226     closedir(dir);
227     return -1;
228   }
229
230   closedir(dir);
231   return 0;
232 }
233
234 static int huge_read(void) {
235   static const char sys_mm_hugepages[] = "/sys/kernel/mm/hugepages";
236
237   if (g_flag_rpt_mm) {
238     if (read_syshugepages(sys_mm_hugepages, "mm") != 0) {
239       return -1;
240     }
241   }
242   if (g_flag_rpt_numa) {
243     if (read_nodes() != 0) {
244       return -1;
245     }
246   }
247
248   return 0;
249 }
250
251 void module_register(void) {
252   plugin_register_complex_config(g_plugin_name, hp_config);
253   plugin_register_read(g_plugin_name, huge_read);
254 }