Merge remote-tracking branch 'github/pr/1931'
[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 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  */
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 static const char g_cfg_rpt_numa[] = "ReportPerNodeHP";
36 static const char g_cfg_rpt_mm[] = "ReportRootHP";
37
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;
45
46 struct entry_info {
47   char *d_name;
48   const char *node;
49 };
50
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);
54
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   }
63
64   return -1;
65 }
66
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_list_t vl = VALUE_LIST_INIT;
71   value_t values[] = {
72     { .gauge = free_value },
73     { .gauge = used_value },
74   };
75
76   vl.values = values;
77   vl.values_len = STATIC_ARRAY_SIZE (values);
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));
82
83   if (type_instance != NULL) {
84     sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
85   }
86
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);
89
90   plugin_dispatch_values (&vl);
91 }
92
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;
106
107   ssnprintf(path2, sizeof(path2), "%s/%s", path, entry);
108
109   FILE *fh = fopen(path2, "rt");
110   if (fh == NULL) {
111     ERROR("%s: cannot open %s", g_plugin_name, path2);
112     return -1;
113   }
114
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   }
120
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   }
132
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);
148
149     /* Reset for next time */
150     flag = 0;
151     used_hp = 0;
152     free_hp = 0;
153   }
154
155   fclose(fh);
156   return 0;
157 }
158
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;
167
168   dir = opendir(path);
169   if (dir == NULL) {
170     ERROR("%s: cannot open directory %s", g_plugin_name, path);
171     return -1;
172   }
173
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   }
185
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     }
193
194     /* /sys/devices/system/node/node?/hugepages/ */
195     ssnprintf(path2, (size_t) lim, "%s/%s", path, result->d_name);
196
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   }
202
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   }
209
210   closedir(dir);
211   return 0;
212 }
213
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;
223
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   }
229
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   }
241
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     }
248
249     ssnprintf(path, (size_t) lim, sys_node_hugepages, result->d_name);
250     read_syshugepages(path, result->d_name);
251     errno = 0;
252   }
253
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   }
260
261   closedir(dir);
262   return 0;
263 }
264
265
266 static int huge_read(void)
267 {
268   static const char sys_mm_hugepages[] = "/sys/kernel/mm/hugepages";
269
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   }
280
281   return 0;
282 }
283
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 }
290