master
c 298 lines 10.6 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "plugin_proc.h"
4
5 struct edac_count {
6 bool updated;
7 char *filename;
8 procfile *ff;
9 kernel_uint_t count;
10 RRDDIM *rd;
11 };
12
13 struct edac_dimm {
14 char *name;
15
16 struct edac_count ce;
17 struct edac_count ue;
18
19 RRDSET *st;
20
21 struct edac_dimm *prev, *next;
22 };
23
24 struct mc {
25 char *name;
26
27 struct edac_count ce;
28 struct edac_count ue;
29 struct edac_count ce_noinfo;
30 struct edac_count ue_noinfo;
31
32 RRDSET *st;
33
34 struct edac_dimm *dimms;
35
36 struct mc *prev, *next;
37 };
38
39 static struct mc *mc_root = NULL;
40 static const char *mc_dirname = NULL;
41
42 static void find_all_mc() {
43 char name[FILENAME_MAX + 1];
44 snprintfz(name, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/devices/system/edac/mc");
45 mc_dirname = inicfg_get(&netdata_config, "plugin:proc:/sys/devices/system/edac/mc", "directory to monitor", name);
46
47 DIR *dir = opendir(mc_dirname);
48 if(unlikely(!dir)) {
49 collector_error("Cannot read EDAC memory errors directory '%s'", mc_dirname);
50 return;
51 }
52
53 struct dirent *de = NULL;
54 while((de = readdir(dir))) {
55 if(de->d_type == DT_DIR && de->d_name[0] == 'm' && de->d_name[1] == 'c' && isdigit(de->d_name[2])) {
56 struct mc *m = callocz(1, sizeof(struct mc));
57 m->name = strdupz(de->d_name);
58
59 struct stat st;
60
61 snprintfz(name, FILENAME_MAX, "%s/%s/ce_count", mc_dirname, de->d_name);
62 if(stat(name, &st) != -1)
63 m->ce.filename = strdupz(name);
64
65 snprintfz(name, FILENAME_MAX, "%s/%s/ue_count", mc_dirname, de->d_name);
66 if(stat(name, &st) != -1)
67 m->ue.filename = strdupz(name);
68
69 snprintfz(name, FILENAME_MAX, "%s/%s/ce_noinfo_count", mc_dirname, de->d_name);
70 if(stat(name, &st) != -1)
71 m->ce_noinfo.filename = strdupz(name);
72
73 snprintfz(name, FILENAME_MAX, "%s/%s/ue_noinfo_count", mc_dirname, de->d_name);
74 if(stat(name, &st) != -1)
75 m->ue_noinfo.filename = strdupz(name);
76
77 if(!m->ce.filename && !m->ue.filename && !m->ce_noinfo.filename && !m->ue_noinfo.filename) {
78 freez(m->name);
79 freez(m);
80 }
81 else
82 DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(mc_root, m, prev, next);
83 }
84 }
85 closedir(dir);
86
87 for(struct mc *m = mc_root; m ;m = m->next) {
88 snprintfz(name, FILENAME_MAX, "%s/%s", mc_dirname, m->name);
89 dir = opendir(name);
90 if(!dir) {
91 collector_error("Cannot read EDAC memory errors directory '%s'", name);
92 continue;
93 }
94
95 while((de = readdir(dir))) {
96 // it can be dimmX or rankX directory
97 // https://www.kernel.org/doc/html/v5.0/admin-guide/ras.html#f5
98
99 if (de->d_type == DT_DIR &&
100 ((strncmp(de->d_name, "rank", 4) == 0 || strncmp(de->d_name, "dimm", 4) == 0)) &&
101 isdigit(de->d_name[4])) {
102
103 struct edac_dimm *d = callocz(1, sizeof(struct edac_dimm));
104 d->name = strdupz(de->d_name);
105
106 struct stat st;
107
108 snprintfz(name, FILENAME_MAX, "%s/%s/%s/dimm_ce_count", mc_dirname, m->name, de->d_name);
109 if(stat(name, &st) != -1)
110 d->ce.filename = strdupz(name);
111
112 snprintfz(name, FILENAME_MAX, "%s/%s/%s/dimm_ue_count", mc_dirname, m->name, de->d_name);
113 if(stat(name, &st) != -1)
114 d->ue.filename = strdupz(name);
115
116 if(!d->ce.filename && !d->ue.filename) {
117 freez(d->name);
118 freez(d);
119 }
120 else
121 DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(m->dimms, d, prev, next);
122 }
123 }
124 closedir(dir);
125 }
126 }
127
128 static kernel_uint_t read_edac_count(struct edac_count *t) {
129 t->updated = false;
130 t->count = 0;
131
132 if(t->filename) {
133 if(unlikely(!t->ff)) {
134 t->ff = procfile_open(t->filename, " \t", PROCFILE_FLAG_DEFAULT);
135 if(unlikely(!t->ff))
136 return 0;
137 }
138
139 t->ff = procfile_readall(t->ff);
140 if(unlikely(!t->ff || procfile_lines(t->ff) < 1 || procfile_linewords(t->ff, 0) < 1))
141 return 0;
142
143 t->count = str2ull(procfile_lineword(t->ff, 0, 0), NULL);
144 t->updated = true;
145 }
146
147 return t->count;
148 }
149
150 static bool read_edac_mc_file(const char *mc, const char *filename, char *out, size_t out_size) {
151 char f[FILENAME_MAX + 1];
152 snprintfz(f, FILENAME_MAX, "%s/%s/%s", mc_dirname, mc, filename);
153 if(read_txt_file(f, out, out_size) != 0) {
154 collector_error("EDAC: cannot read file '%s'", f);
155 return false;
156 }
157 return true;
158 }
159
160 static bool read_edac_mc_rank_file(const char *mc, const char *rank, const char *filename, char *out, size_t out_size) {
161 char f[FILENAME_MAX + 1];
162 snprintfz(f, FILENAME_MAX, "%s/%s/%s/%s", mc_dirname, mc, rank, filename);
163 if(read_txt_file(f, out, out_size) != 0) {
164 collector_error("EDAC: cannot read file '%s'", f);
165 return false;
166 }
167 return true;
168 }
169
170 int do_proc_sys_devices_system_edac_mc(int update_every, usec_t dt __maybe_unused) {
171 if(unlikely(!mc_root)) {
172 find_all_mc();
173
174 if(!mc_root)
175 // don't call this again
176 return 1;
177 }
178
179 for(struct mc *m = mc_root; m; m = m->next) {
180 read_edac_count(&m->ce);
181 read_edac_count(&m->ce_noinfo);
182 read_edac_count(&m->ue);
183 read_edac_count(&m->ue_noinfo);
184
185 for(struct edac_dimm *d = m->dimms; d ;d = d->next) {
186 read_edac_count(&d->ce);
187 read_edac_count(&d->ue);
188 }
189 }
190
191 // --------------------------------------------------------------------
192
193 for(struct mc *m = mc_root; m ; m = m->next) {
194 if(unlikely(!m->ce.updated && !m->ue.updated && !m->ce_noinfo.updated && !m->ue_noinfo.updated))
195 continue;
196
197 if(unlikely(!m->st)) {
198 char id[RRD_ID_LENGTH_MAX + 1];
199 snprintfz(id, RRD_ID_LENGTH_MAX, "edac_%s", m->name);
200 m->st = rrdset_create_localhost(
201 "mem"
202 , id
203 , NULL
204 , "edac"
205 , "mem.edac_mc_errors"
206 , "Memory Controller (MC) Error Detection And Correction (EDAC) Errors"
207 , "errors"
208 , PLUGIN_PROC_NAME
209 , "/sys/devices/system/edac/mc"
210 , NETDATA_CHART_PRIO_MEM_HW_ECC_CE
211 , update_every
212 , RRDSET_TYPE_LINE
213 );
214
215 rrdlabels_add(m->st->rrdlabels, "controller", m->name, RRDLABEL_SRC_AUTO);
216
217 char buffer[1024 + 1];
218
219 if(read_edac_mc_file(m->name, "mc_name", buffer, 1024))
220 rrdlabels_add(m->st->rrdlabels, "mc_name", buffer, RRDLABEL_SRC_AUTO);
221
222 if(read_edac_mc_file(m->name, "size_mb", buffer, 1024))
223 rrdlabels_add(m->st->rrdlabels, "size_mb", buffer, RRDLABEL_SRC_AUTO);
224
225 if(read_edac_mc_file(m->name, "max_location", buffer, 1024))
226 rrdlabels_add(m->st->rrdlabels, "max_location", buffer, RRDLABEL_SRC_AUTO);
227
228 m->ce.rd = rrddim_add(m->st, "correctable", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
229 m->ue.rd = rrddim_add(m->st, "uncorrectable", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
230 m->ce_noinfo.rd = rrddim_add(m->st, "correctable_noinfo", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
231 m->ue_noinfo.rd = rrddim_add(m->st, "uncorrectable_noinfo", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
232 }
233
234 rrddim_set_by_pointer(m->st, m->ce.rd, (collected_number)m->ce.count);
235 rrddim_set_by_pointer(m->st, m->ue.rd, (collected_number)m->ue.count);
236 rrddim_set_by_pointer(m->st, m->ce_noinfo.rd, (collected_number)m->ce_noinfo.count);
237 rrddim_set_by_pointer(m->st, m->ue_noinfo.rd, (collected_number)m->ue_noinfo.count);
238
239 rrdset_done(m->st);
240
241 for(struct edac_dimm *d = m->dimms; d ;d = d->next) {
242 if(unlikely(!d->ce.updated && !d->ue.updated))
243 continue;
244
245 if(unlikely(!d->st)) {
246 char id[RRD_ID_LENGTH_MAX + 1];
247 snprintfz(id, RRD_ID_LENGTH_MAX, "edac_%s_%s", m->name, d->name);
248 d->st = rrdset_create_localhost(
249 "mem"
250 , id
251 , NULL
252 , "edac"
253 , "mem.edac_mc_dimm_errors"
254 , "DIMM Error Detection And Correction (EDAC) Errors"
255 , "errors"
256 , PLUGIN_PROC_NAME
257 , "/sys/devices/system/edac/mc"
258 , NETDATA_CHART_PRIO_MEM_HW_ECC_CE + 1
259 , update_every
260 , RRDSET_TYPE_LINE
261 );
262
263 rrdlabels_add(d->st->rrdlabels, "controller", m->name, RRDLABEL_SRC_AUTO);
264 rrdlabels_add(d->st->rrdlabels, "dimm", d->name, RRDLABEL_SRC_AUTO);
265
266 char buffer[1024 + 1];
267
268 if (read_edac_mc_rank_file(m->name, d->name, "dimm_dev_type", buffer, 1024))
269 rrdlabels_add(d->st->rrdlabels, "dimm_dev_type", buffer, RRDLABEL_SRC_AUTO);
270
271 if (read_edac_mc_rank_file(m->name, d->name, "dimm_edac_mode", buffer, 1024))
272 rrdlabels_add(d->st->rrdlabels, "dimm_edac_mode", buffer, RRDLABEL_SRC_AUTO);
273
274 if (read_edac_mc_rank_file(m->name, d->name, "dimm_label", buffer, 1024))
275 rrdlabels_add(d->st->rrdlabels, "dimm_label", buffer, RRDLABEL_SRC_AUTO);
276
277 if (read_edac_mc_rank_file(m->name, d->name, "dimm_location", buffer, 1024))
278 rrdlabels_add(d->st->rrdlabels, "dimm_location", buffer, RRDLABEL_SRC_AUTO);
279
280 if (read_edac_mc_rank_file(m->name, d->name, "dimm_mem_type", buffer, 1024))
281 rrdlabels_add(d->st->rrdlabels, "dimm_mem_type", buffer, RRDLABEL_SRC_AUTO);
282
283 if (read_edac_mc_rank_file(m->name, d->name, "size", buffer, 1024))
284 rrdlabels_add(d->st->rrdlabels, "size", buffer, RRDLABEL_SRC_AUTO);
285
286 d->ce.rd = rrddim_add(d->st, "correctable", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
287 d->ue.rd = rrddim_add(d->st, "uncorrectable", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
288 }
289
290 rrddim_set_by_pointer(d->st, d->ce.rd, (collected_number)d->ce.count);
291 rrddim_set_by_pointer(d->st, d->ue.rd, (collected_number)d->ue.count);
292
293 rrdset_done(d->st);
294 }
295 }
296
297 return 0;
298 }