hugepages: rework
[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   char *node;
49 };
50
51 static int huge_config_callback(const char *key, const char *val)
52 {
53   INFO("%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_t values[2];
71   value_list_t vl = VALUE_LIST_INIT;
72
73   values[0].gauge = free_value;
74   values[1].gauge = used_value;
75
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));
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   char *type = "hugepages";
98   char *partial_type_inst = "free_used";
99   char type_instance[PATH_MAX];
100   char *strin = NULL;
101   struct entry_info *hpsize_plinst = (struct entry_info *) 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   const char hugepages_dir[] = "hugepages";
162   DIR *dir = NULL;
163   struct dirent *result = NULL;
164   char path2[PATH_MAX];
165   struct entry_info e_info;
166
167   dir = opendir(path);
168   if (dir == NULL) {
169     ERROR("%s: cannot open directory %s", g_plugin_name, path);
170     return -1;
171   }
172
173   if (pathconf(path, _PC_NAME_MAX) == -1) {
174     /* Limit not defined, or error */
175     ERROR("%s: pathconf failed", g_plugin_name);
176     closedir(dir);
177     return -1;
178   }
179
180   /* read "hugepages-XXXXXkB" entries */
181   while ((result = readdir(dir)) != NULL) {
182     if (strncmp(result->d_name, hugepages_dir, sizeof(hugepages_dir)-1)) {
183       /* not node dir */
184       continue;
185     }
186
187     /* /sys/devices/system/node/node?/hugepages/ */
188     ssnprintf(path2, sizeof(path2), "%s/%s", path, result->d_name);
189
190     e_info.d_name = result->d_name;
191     e_info.node = (char *) node;
192     walk_directory(path2, read_hugepage_entry, (void *) &e_info, 0);
193   }
194
195   closedir(dir);
196
197   return 0;
198 }
199
200 static int read_nodes(void)
201 {
202   const char sys_node[] = "/sys/devices/system/node";
203   const char node_string[] = "node";
204   const char sys_node_hugepages[] = "/sys/devices/system/node/%s/hugepages";
205   DIR *dir = NULL;
206   struct dirent *result = NULL;
207   char path[PATH_MAX];
208
209   dir = opendir(sys_node);
210   if (dir == NULL) {
211     ERROR("%s: cannot open directory %s", g_plugin_name, sys_node);
212     return -1;
213   }
214
215   if (pathconf(sys_node, _PC_NAME_MAX) == -1) {
216     /* Limit not defined, or error */
217     ERROR("%s: pathconf failed", g_plugin_name);
218     closedir(dir);
219     return -1;
220   }
221
222   while ((result = readdir(dir)) != NULL) {
223     if (strncmp(result->d_name, node_string, sizeof(node_string)-1)) {
224       /* not node dir */
225       continue;
226     }
227
228     ssnprintf(path, sizeof(path), sys_node_hugepages, result->d_name);
229     read_syshugepages(path, result->d_name);
230   }
231
232   closedir(dir);
233
234   return 0;
235 }
236
237
238 static int huge_read(void)
239 {
240   const char sys_mm_hugepages[] = "/sys/kernel/mm/hugepages";
241
242   if (g_flag_rpt_mm) {
243     read_syshugepages(sys_mm_hugepages, "mm");
244   }
245   if (g_flag_rpt_numa) {
246     read_nodes();
247   }
248
249   return 0;
250 }
251
252 void module_register(void)
253 {
254   plugin_register_config(g_plugin_name, huge_config_callback, g_config_keys,
255       g_config_keys_num);
256   plugin_register_read(g_plugin_name, huge_read);
257 }
258