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 * Florian Forster <octo at collectd.org>
29 */
30
31 #include "collectd.h"
32
33 #include "plugin.h" /* plugin_register_*, plugin_dispatch_values */
34 #include "utils/common/common.h" /* auxiliary functions */
35
36 static const char g_plugin_name[] = "hugepages";
37
38 static bool g_flag_rpt_numa = true;
39 static bool g_flag_rpt_mm = true;
40
41 static bool g_values_pages = true;
42 static bool g_values_bytes;
43 static bool g_values_percent;
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
hp_config(oconfig_item_t * ci)61 static int hp_config(oconfig_item_t *ci) {
62 for (int i = 0; i < ci->children_num; i++) {
63 oconfig_item_t *child = ci->children + i;
64 if (strcasecmp("ReportPerNodeHP", child->key) == 0)
65 cf_util_get_boolean(child, &g_flag_rpt_numa);
66 else if (strcasecmp("ReportRootHP", child->key) == 0)
67 cf_util_get_boolean(child, &g_flag_rpt_mm);
68 else if (strcasecmp("ValuesPages", child->key) == 0)
69 cf_util_get_boolean(child, &g_values_pages);
70 else if (strcasecmp("ValuesBytes", child->key) == 0)
71 cf_util_get_boolean(child, &g_values_bytes);
72 else if (strcasecmp("ValuesPercentage", child->key) == 0)
73 cf_util_get_boolean(child, &g_values_percent);
74 else
75 ERROR("%s: Invalid configuration option: \"%s\".", g_plugin_name,
76 child->key);
77 }
78
79 return 0;
80 }
81
submit_hp(const struct entry_info * info)82 static void submit_hp(const struct entry_info *info) {
83 value_list_t vl = VALUE_LIST_INIT;
84
85 vl.values = &(value_t){.gauge = NAN};
86 vl.values_len = 1;
87
88 sstrncpy(vl.plugin, g_plugin_name, sizeof(vl.plugin));
89 if (info->node) {
90 snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s-%zuKb",
91 info->node, info->page_size_kb);
92 } else {
93 snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%zuKb",
94 info->page_size_kb);
95 }
96
97 /* ensure all metrics have the same timestamp */
98 vl.time = cdtime();
99
100 gauge_t free = info->free;
101 gauge_t used = (info->nr + info->surplus) - info->free;
102
103 if (g_values_pages) {
104 sstrncpy(vl.type, "vmpage_number", sizeof(vl.type));
105 plugin_dispatch_multivalue(&vl, /* store_percentage = */ false,
106 DS_TYPE_GAUGE, "free", free, "used", used, NULL);
107 }
108 if (g_values_bytes) {
109 gauge_t page_size = (gauge_t)(1024 * info->page_size_kb);
110 sstrncpy(vl.type, "memory", sizeof(vl.type));
111 plugin_dispatch_multivalue(&vl, /* store_percentage = */ false,
112 DS_TYPE_GAUGE, "free", free * page_size, "used",
113 used * page_size, NULL);
114 }
115 if (g_values_percent) {
116 sstrncpy(vl.type, "percent", sizeof(vl.type));
117 plugin_dispatch_multivalue(&vl, /* store_percentage = */ true,
118 DS_TYPE_GAUGE, "free", free, "used", used, NULL);
119 }
120 }
121
read_hugepage_entry(const char * path,const char * entry,void * e_info)122 static int read_hugepage_entry(const char *path, const char *entry,
123 void *e_info) {
124 char path2[PATH_MAX];
125 struct entry_info *info = e_info;
126 double value;
127
128 snprintf(path2, sizeof(path2), "%s/%s", path, entry);
129
130 FILE *fh = fopen(path2, "rt");
131 if (fh == NULL) {
132 ERROR("%s: cannot open %s", g_plugin_name, path2);
133 return -1;
134 }
135
136 if (fscanf(fh, "%lf", &value) != 1) {
137 ERROR("%s: cannot parse file %s", g_plugin_name, path2);
138 fclose(fh);
139 return -1;
140 }
141 fclose(fh);
142
143 if (strcmp(entry, "nr_hugepages") == 0) {
144 info->nr = value;
145 info->flags |= HP_HAVE_NR;
146 } else if (strcmp(entry, "surplus_hugepages") == 0) {
147 info->surplus = value;
148 info->flags |= HP_HAVE_SURPLUS;
149 } else if (strcmp(entry, "free_hugepages") == 0) {
150 info->free = value;
151 info->flags |= HP_HAVE_FREE;
152 }
153
154 if (info->flags != HP_HAVE_ALL) {
155 return 0;
156 }
157
158 submit_hp(info);
159
160 /* Reset flags so subsequent calls don't submit again. */
161 info->flags = 0;
162 return 0;
163 }
164
read_syshugepages(const char * path,const char * node)165 static int read_syshugepages(const char *path, const char *node) {
166 static const char hugepages_dir[] = "hugepages-";
167 DIR *dir;
168 struct dirent *result;
169 char path2[PATH_MAX];
170
171 dir = opendir(path);
172 if (dir == NULL) {
173 ERROR("%s: cannot open directory %s", g_plugin_name, path);
174 return -1;
175 }
176
177 /* read "hugepages-XXXXXkB" entries */
178 while ((result = readdir(dir)) != NULL) {
179 if (strncmp(result->d_name, hugepages_dir, sizeof(hugepages_dir) - 1)) {
180 /* not node dir */
181 errno = 0;
182 continue;
183 }
184
185 long page_size = strtol(result->d_name + strlen(hugepages_dir),
186 /* endptr = */ NULL, /* base = */ 10);
187 if (errno != 0) {
188 ERROR("%s: failed to determine page size from directory name \"%s\": %s",
189 g_plugin_name, result->d_name, STRERRNO);
190 continue;
191 }
192
193 /* /sys/devices/system/node/node?/hugepages/ */
194 snprintf(path2, sizeof(path2), "%s/%s", path, result->d_name);
195
196 walk_directory(path2, read_hugepage_entry,
197 &(struct entry_info){
198 .d_name = result->d_name,
199 .node = node,
200 .page_size_kb = (size_t)page_size,
201 },
202 /* hidden = */ 0);
203 errno = 0;
204 }
205
206 /* Check if NULL return from readdir() was an error */
207 if (errno != 0) {
208 ERROR("%s: readdir failed", g_plugin_name);
209 closedir(dir);
210 return -1;
211 }
212
213 closedir(dir);
214 return 0;
215 }
216
read_nodes(void)217 static int read_nodes(void) {
218 static const char sys_node[] = "/sys/devices/system/node";
219 static const char node_string[] = "node";
220 static const char sys_node_hugepages[] =
221 "/sys/devices/system/node/%s/hugepages";
222 DIR *dir;
223 struct dirent *result;
224 char path[PATH_MAX];
225
226 dir = opendir(sys_node);
227 if (dir == NULL) {
228 ERROR("%s: cannot open directory %s", g_plugin_name, sys_node);
229 return -1;
230 }
231
232 while ((result = readdir(dir)) != NULL) {
233 if (strncmp(result->d_name, node_string, sizeof(node_string) - 1)) {
234 /* not node dir */
235 errno = 0;
236 continue;
237 }
238
239 snprintf(path, sizeof(path), sys_node_hugepages, result->d_name);
240 read_syshugepages(path, result->d_name);
241 errno = 0;
242 }
243
244 /* Check if NULL return from readdir() was an error */
245 if (errno != 0) {
246 ERROR("%s: readdir failed", g_plugin_name);
247 closedir(dir);
248 return -1;
249 }
250
251 closedir(dir);
252 return 0;
253 }
254
huge_read(void)255 static int huge_read(void) {
256 static const char sys_mm_hugepages[] = "/sys/kernel/mm/hugepages";
257
258 if (g_flag_rpt_mm) {
259 if (read_syshugepages(sys_mm_hugepages, "mm") != 0) {
260 return -1;
261 }
262 }
263 if (g_flag_rpt_numa) {
264 if (read_nodes() != 0) {
265 return -1;
266 }
267 }
268
269 return 0;
270 }
271
module_register(void)272 void module_register(void) {
273 plugin_register_complex_config(g_plugin_name, hp_config);
274 plugin_register_read(g_plugin_name, huge_read);
275 }
276