b5acfba2a731d482be2d375261b1525c4ecc6dbc
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 */
30 #include "collectd.h"
31 #include "common.h" /* auxiliary functions */
32 #include "plugin.h" /* plugin_register_*, plugin_dispatch_values */
34 static const char g_plugin_name[] = "hugepages";
36 static _Bool g_flag_rpt_numa = 1;
37 static _Bool g_flag_rpt_mm = 1;
39 #define HP_HAVE_NR 0x01
40 #define HP_HAVE_SURPLUS 0x02
41 #define HP_HAVE_FREE 0x04
42 #define HP_HAVE_ALL 0x07
44 struct entry_info {
45 char *d_name;
46 const char *node;
47 size_t page_size_kb;
49 gauge_t nr;
50 gauge_t surplus;
51 gauge_t free;
52 uint8_t flags;
53 };
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 }
67 return (0);
68 }
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 };
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));
85 if (type_instance != NULL) {
86 sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
87 }
89 DEBUG("submit_hp pl_inst:%s, inst_type %s, free=%lf, used=%lf", plug_inst,
90 type_instance, free_value, used_value);
92 plugin_dispatch_values(&vl);
93 }
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;
102 ssnprintf(path2, sizeof(path2), "%s/%s", path, entry);
104 FILE *fh = fopen(path2, "rt");
105 if (fh == NULL) {
106 ERROR("%s: cannot open %s", g_plugin_name, path2);
107 return -1;
108 }
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);
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 }
128 if (info->flags != HP_HAVE_ALL) {
129 return 0;
130 }
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);
137 /* Reset flags so subsequent calls don't submit again. */
138 info->flags = 0;
139 return 0;
140 }
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];
148 dir = opendir(path);
149 if (dir == NULL) {
150 ERROR("%s: cannot open directory %s", g_plugin_name, path);
151 return -1;
152 }
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 }
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 }
172 /* /sys/devices/system/node/node?/hugepages/ */
173 ssnprintf(path2, sizeof(path2), "%s/%s", path, result->d_name);
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 }
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 }
192 closedir(dir);
193 return 0;
194 }
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];
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 }
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 }
218 ssnprintf(path, sizeof(path), sys_node_hugepages, result->d_name);
219 read_syshugepages(path, result->d_name);
220 errno = 0;
221 }
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 }
230 closedir(dir);
231 return 0;
232 }
234 static int huge_read(void) {
235 static const char sys_mm_hugepages[] = "/sys/kernel/mm/hugepages";
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 }
248 return 0;
249 }
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 }