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 of
8 * this software and associated documentation files (the "Software"), to deal in
9 * the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
11 * of the Software, and to permit persons to whom the Software is furnished to do
12 * so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in all
15 * 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,
40 g_cfg_rpt_mm,
41 };
42 static size_t g_config_keys_num = STATIC_ARRAY_SIZE(g_config_keys);
43 static int g_flag_rpt_numa = 1;
44 static int g_flag_rpt_mm = 1;
46 struct entry_info {
47 char *d_name;
48 const char *node;
49 };
51 static int huge_config_callback(const char *key, const char *val)
52 {
53 DEBUG("%s: HugePages config key='%s', val='%s'", g_plugin_name, key, val);
55 if (strcasecmp(key, g_cfg_rpt_numa) == 0) {
56 g_flag_rpt_numa = IS_TRUE(val);
57 return 0;
58 }
59 if (strcasecmp(key, g_cfg_rpt_mm) == 0) {
60 g_flag_rpt_mm = IS_TRUE(val);
61 return 0;
62 }
64 return -1;
65 }
67 static void submit_hp(const char *plug_inst, const char *type,
68 const char *type_instance, gauge_t free_value, gauge_t used_value)
69 {
70 value_t values[2];
71 value_list_t vl = VALUE_LIST_INIT;
73 values[0].gauge = free_value;
74 values[1].gauge = used_value;
76 vl.values = values;
77 vl.values_len = 2;
78 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
79 sstrncpy (vl.plugin, g_plugin_name, sizeof (vl.plugin));
80 sstrncpy (vl.plugin_instance, plug_inst, sizeof (vl.plugin_instance));
81 sstrncpy (vl.type, type, sizeof (vl.type));
83 if (type_instance != NULL) {
84 sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
85 }
87 DEBUG("submit_hp pl_inst:%s, inst_type %s, type %s, free=%lf, used=%lf",
88 plug_inst, type_instance, type, free_value, used_value);
90 plugin_dispatch_values (&vl);
91 }
93 static int read_hugepage_entry(const char *path, const char *entry,
94 void *e_info)
95 {
96 char path2[PATH_MAX];
97 static const char type[] = "hugepages";
98 static const char partial_type_inst[] = "free_used";
99 char type_instance[PATH_MAX];
100 char *strin;
101 struct entry_info *hpsize_plinst = e_info;
102 static int flag = 0;
103 static double used_hp = 0;
104 static double free_hp = 0;
105 double value;
107 ssnprintf(path2, sizeof(path2), "%s/%s", path, entry);
109 FILE *fh = fopen(path2, "rt");
110 if (fh == NULL) {
111 ERROR("%s: cannot open %s", g_plugin_name, path2);
112 return -1;
113 }
115 if (fscanf(fh, "%lf", &value) !=1) {
116 ERROR("%s: cannot parse file %s", g_plugin_name, path2);
117 fclose(fh);
118 return -1;
119 }
121 if (strcmp(entry, "nr_hugepages") == 0) {
122 used_hp += value;
123 flag++;
124 } else if (strcmp(entry, "surplus_hugepages") == 0) {
125 used_hp += value;
126 flag++;
127 } else if (strcmp(entry, "free_hugepages") == 0) {
128 used_hp -= value;
129 free_hp = value;
130 flag++;
131 }
133 if (flag == 3) {
134 /* Can now submit "used" and "free" values.
135 * 0x2D is the ASCII "-" character, after which the string
136 * contains "<size>kB"
137 * The string passed as param 3 to submit_hp is of the format:
138 * <type>-<partial_type_inst>-<size>kB
139 */
140 strin = strchr(hpsize_plinst->d_name, 0x2D);
141 if (strin != NULL) {
142 ssnprintf(type_instance, sizeof(type_instance), "%s%s", partial_type_inst, strin);
143 } else {
144 ssnprintf(type_instance, sizeof(type_instance), "%s%s", partial_type_inst,
145 hpsize_plinst->d_name);
146 }
147 submit_hp(hpsize_plinst->node, type, type_instance, free_hp, used_hp);
149 /* Reset for next time */
150 flag = 0;
151 used_hp = 0;
152 free_hp = 0;
153 }
155 fclose(fh);
156 return 0;
157 }
159 static int read_syshugepages(const char* path, const char* node)
160 {
161 static const char hugepages_dir[] = "hugepages";
162 DIR *dir;
163 struct dirent *result;
164 char path2[PATH_MAX];
165 struct entry_info e_info;
166 long lim;
168 dir = opendir(path);
169 if (dir == NULL) {
170 ERROR("%s: cannot open directory %s", g_plugin_name, path);
171 return -1;
172 }
174 errno = 0;
175 if ((lim = pathconf(path, _PC_NAME_MAX)) == -1) {
176 /* Limit not defined if errno == 0, otherwise error */
177 if (errno != 0) {
178 ERROR("%s: pathconf failed", g_plugin_name);
179 closedir(dir);
180 return -1;
181 } else {
182 lim = PATH_MAX;
183 }
184 }
186 /* read "hugepages-XXXXXkB" entries */
187 while ((result = readdir(dir)) != NULL) {
188 if (strncmp(result->d_name, hugepages_dir, sizeof(hugepages_dir)-1)) {
189 /* not node dir */
190 errno = 0;
191 continue;
192 }
194 /* /sys/devices/system/node/node?/hugepages/ */
195 ssnprintf(path2, (size_t) lim, "%s/%s", path, result->d_name);
197 e_info.d_name = result->d_name;
198 e_info.node = node;
199 walk_directory(path2, read_hugepage_entry, &e_info, 0);
200 errno = 0;
201 }
203 /* Check if NULL return from readdir() was an error */
204 if (errno != 0) {
205 ERROR("%s: readdir failed", g_plugin_name);
206 closedir(dir);
207 return -1;
208 }
210 closedir(dir);
211 return 0;
212 }
214 static int read_nodes(void)
215 {
216 static const char sys_node[] = "/sys/devices/system/node";
217 static const char node_string[] = "node";
218 static const char sys_node_hugepages[] = "/sys/devices/system/node/%s/hugepages";
219 DIR *dir;
220 struct dirent *result;
221 char path[PATH_MAX];
222 long lim;
224 dir = opendir(sys_node);
225 if (dir == NULL) {
226 ERROR("%s: cannot open directory %s", g_plugin_name, sys_node);
227 return -1;
228 }
230 errno = 0;
231 if ((lim = pathconf(sys_node, _PC_NAME_MAX)) == -1) {
232 /* Limit not defined if errno == 0, otherwise error */
233 if (errno != 0) {
234 ERROR("%s: pathconf failed", g_plugin_name);
235 closedir(dir);
236 return -1;
237 } else {
238 lim = PATH_MAX;
239 }
240 }
242 while ((result = readdir(dir)) != NULL) {
243 if (strncmp(result->d_name, node_string, sizeof(node_string)-1)) {
244 /* not node dir */
245 errno = 0;
246 continue;
247 }
249 ssnprintf(path, (size_t) lim, sys_node_hugepages, result->d_name);
250 read_syshugepages(path, result->d_name);
251 errno = 0;
252 }
254 /* Check if NULL return from readdir() was an error */
255 if (errno != 0) {
256 ERROR("%s: readdir failed", g_plugin_name);
257 closedir(dir);
258 return -1;
259 }
261 closedir(dir);
262 return 0;
263 }
266 static int huge_read(void)
267 {
268 static const char sys_mm_hugepages[] = "/sys/kernel/mm/hugepages";
270 if (g_flag_rpt_mm) {
271 if (read_syshugepages(sys_mm_hugepages, "mm") != 0) {
272 return -1;
273 }
274 }
275 if (g_flag_rpt_numa) {
276 if (read_nodes() != 0) {
277 return -1;
278 }
279 }
281 return 0;
282 }
284 void module_register(void)
285 {
286 plugin_register_config(g_plugin_name, huge_config_callback, g_config_keys,
287 g_config_keys_num);
288 plugin_register_read(g_plugin_name, huge_read);
289 }