8573fc34d636bbb29cfb1dfd2eaa86e209079431
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";
35 static const char g_cfg_rpt_numa[] = "ReportPerNodeHP";
36 static const char g_cfg_rpt_mm[] = "ReportRootHP";
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;
45 struct entry_info {
46 char *d_name;
47 const char *node;
48 };
50 static int huge_config_callback(const char *key, const char *val) {
51 DEBUG("%s: HugePages config key='%s', val='%s'", g_plugin_name, key, val);
53 if (strcasecmp(key, g_cfg_rpt_numa) == 0) {
54 g_flag_rpt_numa = IS_TRUE(val);
55 return 0;
56 }
57 if (strcasecmp(key, g_cfg_rpt_mm) == 0) {
58 g_flag_rpt_mm = IS_TRUE(val);
59 return 0;
60 }
62 return -1;
63 }
65 static void submit_hp(const char *plug_inst, const char *type,
66 const char *type_instance, gauge_t free_value,
67 gauge_t used_value) {
68 value_list_t vl = VALUE_LIST_INIT;
69 value_t values[] = {
70 { .gauge = free_value },
71 { .gauge = used_value },
72 };
74 vl.values = values;
75 vl.values_len = STATIC_ARRAY_SIZE (values);
76 sstrncpy(vl.host, hostname_g, sizeof(vl.host));
77 sstrncpy(vl.plugin, g_plugin_name, sizeof(vl.plugin));
78 sstrncpy(vl.plugin_instance, plug_inst, sizeof(vl.plugin_instance));
79 sstrncpy(vl.type, type, sizeof(vl.type));
81 if (type_instance != NULL) {
82 sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
83 }
85 DEBUG("submit_hp pl_inst:%s, inst_type %s, type %s, free=%lf, used=%lf",
86 plug_inst, type_instance, type, free_value, used_value);
88 plugin_dispatch_values(&vl);
89 }
91 static int read_hugepage_entry(const char *path, const char *entry,
92 void *e_info) {
93 char path2[PATH_MAX];
94 static const char type[] = "hugepages";
95 static const char partial_type_inst[] = "free_used";
96 char type_instance[PATH_MAX];
97 char *strin;
98 struct entry_info *hpsize_plinst = e_info;
99 static int flag = 0;
100 static double used_hp = 0;
101 static double free_hp = 0;
102 double value;
104 ssnprintf(path2, sizeof(path2), "%s/%s", path, entry);
106 FILE *fh = fopen(path2, "rt");
107 if (fh == NULL) {
108 ERROR("%s: cannot open %s", g_plugin_name, path2);
109 return -1;
110 }
112 if (fscanf(fh, "%lf", &value) != 1) {
113 ERROR("%s: cannot parse file %s", g_plugin_name, path2);
114 fclose(fh);
115 return -1;
116 }
118 if (strcmp(entry, "nr_hugepages") == 0) {
119 used_hp += value;
120 flag++;
121 } else if (strcmp(entry, "surplus_hugepages") == 0) {
122 used_hp += value;
123 flag++;
124 } else if (strcmp(entry, "free_hugepages") == 0) {
125 used_hp -= value;
126 free_hp = value;
127 flag++;
128 }
130 if (flag == 3) {
131 /* Can now submit "used" and "free" values.
132 * 0x2D is the ASCII "-" character, after which the string
133 * contains "<size>kB"
134 * The string passed as param 3 to submit_hp is of the format:
135 * <type>-<partial_type_inst>-<size>kB
136 */
137 strin = strchr(hpsize_plinst->d_name, 0x2D);
138 if (strin != NULL) {
139 ssnprintf(type_instance, sizeof(type_instance), "%s%s", partial_type_inst,
140 strin);
141 } else {
142 ssnprintf(type_instance, sizeof(type_instance), "%s%s", partial_type_inst,
143 hpsize_plinst->d_name);
144 }
145 submit_hp(hpsize_plinst->node, type, type_instance, free_hp, used_hp);
147 /* Reset for next time */
148 flag = 0;
149 used_hp = 0;
150 free_hp = 0;
151 }
153 fclose(fh);
154 return 0;
155 }
157 static int read_syshugepages(const char *path, const char *node) {
158 static const char hugepages_dir[] = "hugepages";
159 DIR *dir;
160 struct dirent *result;
161 char path2[PATH_MAX];
162 struct entry_info e_info;
163 long lim;
165 dir = opendir(path);
166 if (dir == NULL) {
167 ERROR("%s: cannot open directory %s", g_plugin_name, path);
168 return -1;
169 }
171 errno = 0;
172 if ((lim = pathconf(path, _PC_NAME_MAX)) == -1) {
173 /* Limit not defined if errno == 0, otherwise error */
174 if (errno != 0) {
175 ERROR("%s: pathconf failed", g_plugin_name);
176 closedir(dir);
177 return -1;
178 } else {
179 lim = PATH_MAX;
180 }
181 }
183 /* read "hugepages-XXXXXkB" entries */
184 while ((result = readdir(dir)) != NULL) {
185 if (strncmp(result->d_name, hugepages_dir, sizeof(hugepages_dir) - 1)) {
186 /* not node dir */
187 errno = 0;
188 continue;
189 }
191 /* /sys/devices/system/node/node?/hugepages/ */
192 ssnprintf(path2, (size_t)lim, "%s/%s", path, result->d_name);
194 e_info.d_name = result->d_name;
195 e_info.node = node;
196 walk_directory(path2, read_hugepage_entry, &e_info, 0);
197 errno = 0;
198 }
200 /* Check if NULL return from readdir() was an error */
201 if (errno != 0) {
202 ERROR("%s: readdir failed", g_plugin_name);
203 closedir(dir);
204 return -1;
205 }
207 closedir(dir);
208 return 0;
209 }
211 static int read_nodes(void) {
212 static const char sys_node[] = "/sys/devices/system/node";
213 static const char node_string[] = "node";
214 static const char sys_node_hugepages[] =
215 "/sys/devices/system/node/%s/hugepages";
216 DIR *dir;
217 struct dirent *result;
218 char path[PATH_MAX];
219 long lim;
221 dir = opendir(sys_node);
222 if (dir == NULL) {
223 ERROR("%s: cannot open directory %s", g_plugin_name, sys_node);
224 return -1;
225 }
227 errno = 0;
228 if ((lim = pathconf(sys_node, _PC_NAME_MAX)) == -1) {
229 /* Limit not defined if errno == 0, otherwise error */
230 if (errno != 0) {
231 ERROR("%s: pathconf failed", g_plugin_name);
232 closedir(dir);
233 return -1;
234 } else {
235 lim = PATH_MAX;
236 }
237 }
239 while ((result = readdir(dir)) != NULL) {
240 if (strncmp(result->d_name, node_string, sizeof(node_string) - 1)) {
241 /* not node dir */
242 errno = 0;
243 continue;
244 }
246 ssnprintf(path, (size_t)lim, sys_node_hugepages, result->d_name);
247 read_syshugepages(path, result->d_name);
248 errno = 0;
249 }
251 /* Check if NULL return from readdir() was an error */
252 if (errno != 0) {
253 ERROR("%s: readdir failed", g_plugin_name);
254 closedir(dir);
255 return -1;
256 }
258 closedir(dir);
259 return 0;
260 }
262 static int huge_read(void) {
263 static const char sys_mm_hugepages[] = "/sys/kernel/mm/hugepages";
265 if (g_flag_rpt_mm) {
266 if (read_syshugepages(sys_mm_hugepages, "mm") != 0) {
267 return -1;
268 }
269 }
270 if (g_flag_rpt_numa) {
271 if (read_nodes() != 0) {
272 return -1;
273 }
274 }
276 return 0;
277 }
279 void module_register(void) {
280 plugin_register_config(g_plugin_name, huge_config_callback, g_config_keys,
281 g_config_keys_num);
282 plugin_register_read(g_plugin_name, huge_read);
283 }