master
c 710 lines 32.7 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 // Heavily inspired from proc_net_dev.c
4 #include "plugin_proc.h"
5
6 #define PLUGIN_PROC_MODULE_INFINIBAND_NAME "/sys/class/infiniband"
7 #define CONFIG_SECTION_PLUGIN_SYS_CLASS_INFINIBAND \
8 "plugin:" PLUGIN_PROC_CONFIG_NAME ":" PLUGIN_PROC_MODULE_INFINIBAND_NAME
9
10 // ib_device::name[IB_DEVICE_NAME_MAX(64)] + "-" + ib_device::phys_port_cnt[u8 = 3 chars]
11 #define IBNAME_MAX 68
12
13 // ----------------------------------------------------------------------------
14 // infiniband & omnipath standard counters
15
16 // I use macro as there's no single file acting as summary, but a lot of different files, so can't use helpers like
17 // procfile(). Also, omnipath generates other counters, that are not provided by infiniband
18 #define FOREACH_COUNTER(GEN, ...) \
19 FOREACH_COUNTER_BYTES(GEN, __VA_ARGS__) \
20 FOREACH_COUNTER_PACKETS(GEN, __VA_ARGS__) \
21 FOREACH_COUNTER_ERRORS(GEN, __VA_ARGS__)
22
23 #define FOREACH_COUNTER_BYTES(GEN, ...) \
24 GEN(port_rcv_data, bytes, "Received", 1, __VA_ARGS__) \
25 GEN(port_xmit_data, bytes, "Sent", -1, __VA_ARGS__)
26
27 #define FOREACH_COUNTER_PACKETS(GEN, ...) \
28 GEN(port_rcv_packets, packets, "Received", 1, __VA_ARGS__) \
29 GEN(port_xmit_packets, packets, "Sent", -1, __VA_ARGS__) \
30 GEN(multicast_rcv_packets, packets, "Mcast rcvd", 1, __VA_ARGS__) \
31 GEN(multicast_xmit_packets, packets, "Mcast sent", -1, __VA_ARGS__) \
32 GEN(unicast_rcv_packets, packets, "Ucast rcvd", 1, __VA_ARGS__) \
33 GEN(unicast_xmit_packets, packets, "Ucast sent", -1, __VA_ARGS__)
34
35 #define FOREACH_COUNTER_ERRORS(GEN, ...) \
36 GEN(port_rcv_errors, errors, "Pkts malformated", 1, __VA_ARGS__) \
37 GEN(port_rcv_constraint_errors, errors, "Pkts rcvd discarded ", 1, __VA_ARGS__) \
38 GEN(port_xmit_discards, errors, "Pkts sent discarded", 1, __VA_ARGS__) \
39 GEN(port_xmit_wait, errors, "Tick Wait to send", 1, __VA_ARGS__) \
40 GEN(VL15_dropped, errors, "Pkts missed resource", 1, __VA_ARGS__) \
41 GEN(excessive_buffer_overrun_errors, errors, "Buffer overrun", 1, __VA_ARGS__) \
42 GEN(link_downed, errors, "Link Downed", 1, __VA_ARGS__) \
43 GEN(link_error_recovery, errors, "Link recovered", 1, __VA_ARGS__) \
44 GEN(local_link_integrity_errors, errors, "Link integrity err", 1, __VA_ARGS__) \
45 GEN(symbol_error, errors, "Link minor errors", 1, __VA_ARGS__) \
46 GEN(port_rcv_remote_physical_errors, errors, "Pkts rcvd with EBP", 1, __VA_ARGS__) \
47 GEN(port_rcv_switch_relay_errors, errors, "Pkts rcvd discarded by switch", 1, __VA_ARGS__) \
48 GEN(port_xmit_constraint_errors, errors, "Pkts sent discarded by switch", 1, __VA_ARGS__)
49
50 //
51 // Hardware Counters
52 //
53
54 // IMPORTANT: These are vendor-specific fields.
55 // If you want to add a new vendor, search this for for 'VENDORS:' keyword and
56 // add your definition as 'VENDOR-<key>:' where <key> if the string part that
57 // is shown in /sys/class/infiniband/<key>X_Y
58 // EG: for Mellanox, shown as mlx0_1, it's 'mlx'
59 // for Intel, shown as hfi1_1, it's 'hfi'
60
61 // VENDORS: List of implemented hardware vendors
62 #define FOREACH_HWCOUNTER_NAME(GEN, ...) GEN(mlx, __VA_ARGS__)
63
64 // VENDOR-MLX: HW Counters for Mellanox ConnectX Devices
65 #define FOREACH_HWCOUNTER_MLX(GEN, ...) \
66 FOREACH_HWCOUNTER_MLX_PACKETS(GEN, __VA_ARGS__) \
67 FOREACH_HWCOUNTER_MLX_ERRORS(GEN, __VA_ARGS__)
68
69 #define FOREACH_HWCOUNTER_MLX_PACKETS(GEN, ...) \
70 GEN(np_cnp_sent, hwpackets, "RoCEv2 Congestion sent", 1, __VA_ARGS__) \
71 GEN(np_ecn_marked_roce_packets, hwpackets, "RoCEv2 Congestion rcvd", -1, __VA_ARGS__) \
72 GEN(rp_cnp_handled, hwpackets, "IB Congestion handled", 1, __VA_ARGS__) \
73 GEN(rx_atomic_requests, hwpackets, "ATOMIC req. rcvd", 1, __VA_ARGS__) \
74 GEN(rx_dct_connect, hwpackets, "Connection req. rcvd", 1, __VA_ARGS__) \
75 GEN(rx_read_requests, hwpackets, "Read req. rcvd", 1, __VA_ARGS__) \
76 GEN(rx_write_requests, hwpackets, "Write req. rcvd", 1, __VA_ARGS__) \
77 GEN(roce_adp_retrans, hwpackets, "RoCE retrans adaptive", 1, __VA_ARGS__) \
78 GEN(roce_adp_retrans_to, hwpackets, "RoCE retrans timeout", 1, __VA_ARGS__) \
79 GEN(roce_slow_restart, hwpackets, "RoCE slow restart", 1, __VA_ARGS__) \
80 GEN(roce_slow_restart_cnps, hwpackets, "RoCE slow restart congestion", 1, __VA_ARGS__) \
81 GEN(roce_slow_restart_trans, hwpackets, "RoCE slow restart count", 1, __VA_ARGS__)
82
83 #define FOREACH_HWCOUNTER_MLX_ERRORS(GEN, ...) \
84 GEN(duplicate_request, hwerrors, "Duplicated packets", -1, __VA_ARGS__) \
85 GEN(implied_nak_seq_err, hwerrors, "Pkt Seq Num gap", 1, __VA_ARGS__) \
86 GEN(local_ack_timeout_err, hwerrors, "Ack timer expired", 1, __VA_ARGS__) \
87 GEN(out_of_buffer, hwerrors, "Drop missing buffer", 1, __VA_ARGS__) \
88 GEN(out_of_sequence, hwerrors, "Drop out of sequence", 1, __VA_ARGS__) \
89 GEN(packet_seq_err, hwerrors, "NAK sequence rcvd", 1, __VA_ARGS__) \
90 GEN(req_cqe_error, hwerrors, "CQE err Req", 1, __VA_ARGS__) \
91 GEN(resp_cqe_error, hwerrors, "CQE err Resp", 1, __VA_ARGS__) \
92 GEN(req_cqe_flush_error, hwerrors, "CQE Flushed err Req", 1, __VA_ARGS__) \
93 GEN(resp_cqe_flush_error, hwerrors, "CQE Flushed err Resp", 1, __VA_ARGS__) \
94 GEN(req_remote_access_errors, hwerrors, "Remote access err Req", 1, __VA_ARGS__) \
95 GEN(resp_remote_access_errors, hwerrors, "Remote access err Resp", 1, __VA_ARGS__) \
96 GEN(req_remote_invalid_request, hwerrors, "Remote invalid req", 1, __VA_ARGS__) \
97 GEN(resp_local_length_error, hwerrors, "Local length err Resp", 1, __VA_ARGS__) \
98 GEN(rnr_nak_retry_err, hwerrors, "RNR NAK Packets", 1, __VA_ARGS__) \
99 GEN(rp_cnp_ignored, hwerrors, "CNP Pkts ignored", 1, __VA_ARGS__) \
100 GEN(rx_icrc_encapsulated, hwerrors, "RoCE ICRC Errors", 1, __VA_ARGS__)
101
102 // Common definitions used more than once
103 #define GEN_RRD_DIM_ADD(NAME, GRP, DESC, DIR, PORT) \
104 GEN_RRD_DIM_ADD_CUSTOM(NAME, GRP, DESC, DIR, PORT, 1, 1, RRD_ALGORITHM_INCREMENTAL)
105
106 #define GEN_RRD_DIM_ADD_CUSTOM(NAME, GRP, DESC, DIR, PORT, MULT, DIV, ALGO) \
107 PORT->rd_##NAME = rrddim_add(PORT->st_##GRP, DESC, NULL, DIR * MULT, DIV, ALGO);
108
109 #define GEN_RRD_DIM_ADD_HW(NAME, GRP, DESC, DIR, PORT, HW) \
110 HW->rd_##NAME = rrddim_add(PORT->st_##GRP, DESC, NULL, DIR, 1, RRD_ALGORITHM_INCREMENTAL);
111
112 #define GEN_RRD_DIM_SETP(NAME, GRP, DESC, DIR, PORT) \
113 rrddim_set_by_pointer(PORT->st_##GRP, PORT->rd_##NAME, (collected_number)PORT->NAME);
114
115 #define GEN_RRD_DIM_SETP_HW(NAME, GRP, DESC, DIR, PORT, HW) \
116 rrddim_set_by_pointer(PORT->st_##GRP, HW->rd_##NAME, (collected_number)HW->NAME);
117
118 // https://community.mellanox.com/s/article/understanding-mlx5-linux-counters-and-status-parameters
119 // https://community.mellanox.com/s/article/infiniband-port-counters
120 static struct ibport {
121 char *name;
122 char *counters_path;
123 char *hwcounters_path;
124 int len;
125
126 // flags
127 int configured;
128 int enabled;
129 int discovered;
130
131 int do_bytes;
132 int do_packets;
133 int do_errors;
134 int do_hwpackets;
135 int do_hwerrors;
136
137 const char *chart_type_bytes;
138 const char *chart_type_packets;
139 const char *chart_type_errors;
140 const char *chart_type_hwpackets;
141 const char *chart_type_hwerrors;
142
143 const char *chart_id_bytes;
144 const char *chart_id_packets;
145 const char *chart_id_errors;
146 const char *chart_id_hwpackets;
147 const char *chart_id_hwerrors;
148
149 const char *chart_family;
150
151 unsigned long priority;
152
153 // Port details using drivers/infiniband/core/sysfs.c :: rate_show()
154 RRDDIM *rd_speed;
155 uint64_t speed;
156 uint64_t width;
157
158 // Stats from /$device/ports/$portid/counters
159 // as drivers/infiniband/hw/qib/qib_verbs.h
160 // All uint64 except vl15_dropped, local_link_integrity_errors, excessive_buffer_overrun_errors uint32
161 // Will generate 2 elements for each counter:
162 // - uint64_t to store the value
163 // - char* to store the filename path
164 // - RRDDIM* to store the RRD Dimension
165 #define GEN_DEF_COUNTER(NAME, ...) \
166 uint64_t NAME; \
167 char *file_##NAME; \
168 RRDDIM *rd_##NAME;
169 FOREACH_COUNTER(GEN_DEF_COUNTER)
170
171 // Vendor specific hwcounters from /$device/ports/$portid/hw_counters
172 // We will generate one struct pointer per vendor to avoid future casting
173 #define GEN_DEF_HWCOUNTER_PTR(VENDOR, ...) struct ibporthw_##VENDOR *hwcounters_##VENDOR;
174 FOREACH_HWCOUNTER_NAME(GEN_DEF_HWCOUNTER_PTR)
175
176 // Function pointer to the "infiniband_hwcounters_parse_<vendor>" function
177 void (*hwcounters_parse)(struct ibport *);
178 void (*hwcounters_dorrd)(struct ibport *);
179
180 // charts and dim
181 RRDSET *st_bytes;
182 RRDSET *st_packets;
183 RRDSET *st_errors;
184 RRDSET *st_hwpackets;
185 RRDSET *st_hwerrors;
186
187 const RRDVAR_ACQUIRED *stv_speed;
188
189
190 usec_t speed_last_collected_usec;
191
192 struct ibport *next;
193 } *ibport_root = NULL, *ibport_last_used = NULL;
194
195 // VENDORS: reading / calculation functions
196 #define GEN_DEF_HWCOUNTER(NAME, ...) \
197 uint64_t NAME; \
198 char *file_##NAME; \
199 RRDDIM *rd_##NAME;
200
201 #define GEN_DO_HWCOUNTER_READ(NAME, GRP, DESC, DIR, PORT, HW, ...) \
202 if (HW->file_##NAME) { \
203 if (read_single_number_file(HW->file_##NAME, (unsigned long long *)&HW->NAME)) { \
204 collector_error("cannot read iface '%s' hwcounter '" #HW "'", PORT->name); \
205 HW->file_##NAME = NULL; \
206 } \
207 }
208
209 // VENDOR-MLX: Mellanox
210 struct ibporthw_mlx {
211 FOREACH_HWCOUNTER_MLX(GEN_DEF_HWCOUNTER)
212 };
213 void infiniband_hwcounters_parse_mlx(struct ibport *port)
214 {
215 if (port->do_hwerrors != CONFIG_BOOLEAN_NO)
216 FOREACH_HWCOUNTER_MLX_ERRORS(GEN_DO_HWCOUNTER_READ, port, port->hwcounters_mlx)
217 if (port->do_hwpackets != CONFIG_BOOLEAN_NO)
218 FOREACH_HWCOUNTER_MLX_PACKETS(GEN_DO_HWCOUNTER_READ, port, port->hwcounters_mlx)
219 }
220 void sys_class_infiniband_plugin_cleanup(void) {
221 // Cleanup any acquired RRDVARs
222 struct ibport *port;
223 for (port = ibport_root; port; port = port->next) {
224 if (port->stv_speed) {
225 rrdvar_chart_variable_release(port->st_bytes, port->stv_speed);
226 port->stv_speed = NULL;
227 }
228 }
229 }
230
231 void infiniband_hwcounters_dorrd_mlx(struct ibport *port)
232 {
233 if (port->do_hwerrors != CONFIG_BOOLEAN_NO) {
234 FOREACH_HWCOUNTER_MLX_ERRORS(GEN_RRD_DIM_SETP_HW, port, port->hwcounters_mlx)
235 rrdset_done(port->st_hwerrors);
236 }
237 if (port->do_hwpackets != CONFIG_BOOLEAN_NO) {
238 FOREACH_HWCOUNTER_MLX_PACKETS(GEN_RRD_DIM_SETP_HW, port, port->hwcounters_mlx)
239 rrdset_done(port->st_hwpackets);
240 }
241 }
242
243 // ----------------------------------------------------------------------------
244
245 static struct ibport *get_ibport(const char *dev, const char *port)
246 {
247 struct ibport *p;
248
249 char name[IBNAME_MAX + 1];
250 snprintfz(name, IBNAME_MAX, "%s-%s", dev, port);
251
252 // search it, resuming from the last position in sequence
253 for (p = ibport_last_used; p; p = p->next) {
254 if (unlikely(!strcmp(name, p->name))) {
255 ibport_last_used = p->next;
256 return p;
257 }
258 }
259
260 // new round, from the beginning to the last position used this time
261 for (p = ibport_root; p != ibport_last_used; p = p->next) {
262 if (unlikely(!strcmp(name, p->name))) {
263 ibport_last_used = p->next;
264 return p;
265 }
266 }
267
268 // create a new one
269 p = callocz(1, sizeof(struct ibport));
270 p->name = strdupz(name);
271 p->len = strlen(p->name);
272
273 p->chart_type_bytes = strdupz("infiniband_cnt_bytes");
274 p->chart_type_packets = strdupz("infiniband_cnt_packets");
275 p->chart_type_errors = strdupz("infiniband_cnt_errors");
276 p->chart_type_hwpackets = strdupz("infiniband_hwc_packets");
277 p->chart_type_hwerrors = strdupz("infiniband_hwc_errors");
278
279 char buffer[RRD_ID_LENGTH_MAX + 1];
280 snprintfz(buffer, RRD_ID_LENGTH_MAX, "ib_cntbytes_%s", p->name);
281 p->chart_id_bytes = strdupz(buffer);
282
283 snprintfz(buffer, RRD_ID_LENGTH_MAX, "ib_cntpackets_%s", p->name);
284 p->chart_id_packets = strdupz(buffer);
285
286 snprintfz(buffer, RRD_ID_LENGTH_MAX, "ib_cnterrors_%s", p->name);
287 p->chart_id_errors = strdupz(buffer);
288
289 snprintfz(buffer, RRD_ID_LENGTH_MAX, "ib_hwcntpackets_%s", p->name);
290 p->chart_id_hwpackets = strdupz(buffer);
291
292 snprintfz(buffer, RRD_ID_LENGTH_MAX, "ib_hwcnterrors_%s", p->name);
293 p->chart_id_hwerrors = strdupz(buffer);
294
295 p->chart_family = strdupz(p->name);
296 p->priority = NETDATA_CHART_PRIO_INFINIBAND;
297
298 // Link current ibport to last one in the list
299 if (ibport_root) {
300 struct ibport *t;
301 for (t = ibport_root; t->next; t = t->next)
302 ;
303 t->next = p;
304 } else
305 ibport_root = p;
306
307 return p;
308 }
309
310 int do_sys_class_infiniband(int update_every, usec_t dt)
311 {
312 (void)dt;
313 static SIMPLE_PATTERN *disabled_list = NULL;
314 static int initialized = 0;
315 static int enable_new_ports = -1, enable_only_active = CONFIG_BOOLEAN_YES;
316 static int do_bytes = -1, do_packets = -1, do_errors = -1, do_hwpackets = -1, do_hwerrors = -1;
317 static const char *sys_class_infiniband_dirname = NULL;
318
319 static long long int dt_to_refresh_ports = 0, last_refresh_ports_usec = 0;
320
321 if (unlikely(enable_new_ports == -1)) {
322 char dirname[FILENAME_MAX + 1];
323
324 snprintfz(dirname, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/class/infiniband");
325 sys_class_infiniband_dirname =
326 inicfg_get(&netdata_config, CONFIG_SECTION_PLUGIN_SYS_CLASS_INFINIBAND, "dirname to monitor", dirname);
327
328 do_bytes = inicfg_get_boolean_ondemand(
329 &netdata_config, CONFIG_SECTION_PLUGIN_SYS_CLASS_INFINIBAND, "bandwidth counters", CONFIG_BOOLEAN_YES);
330 do_packets = inicfg_get_boolean_ondemand(
331 &netdata_config, CONFIG_SECTION_PLUGIN_SYS_CLASS_INFINIBAND, "packets counters", CONFIG_BOOLEAN_YES);
332 do_errors = inicfg_get_boolean_ondemand(
333 &netdata_config, CONFIG_SECTION_PLUGIN_SYS_CLASS_INFINIBAND, "errors counters", CONFIG_BOOLEAN_YES);
334 do_hwpackets = inicfg_get_boolean_ondemand(
335 &netdata_config, CONFIG_SECTION_PLUGIN_SYS_CLASS_INFINIBAND, "hardware packets counters", CONFIG_BOOLEAN_AUTO);
336 do_hwerrors = inicfg_get_boolean_ondemand(
337 &netdata_config, CONFIG_SECTION_PLUGIN_SYS_CLASS_INFINIBAND, "hardware errors counters", CONFIG_BOOLEAN_AUTO);
338
339 enable_only_active = inicfg_get_boolean_ondemand(
340 &netdata_config, CONFIG_SECTION_PLUGIN_SYS_CLASS_INFINIBAND, "monitor only active ports", CONFIG_BOOLEAN_AUTO);
341 disabled_list = simple_pattern_create(
342 inicfg_get(&netdata_config, CONFIG_SECTION_PLUGIN_SYS_CLASS_INFINIBAND, "disable by default interfaces matching", ""),
343 NULL,
344 SIMPLE_PATTERN_EXACT, true);
345
346 dt_to_refresh_ports =
347 inicfg_get_duration_seconds(&netdata_config, CONFIG_SECTION_PLUGIN_SYS_CLASS_INFINIBAND, "refresh ports state every", 30) *
348 USEC_PER_SEC;
349 if (dt_to_refresh_ports < 0)
350 dt_to_refresh_ports = 0;
351 }
352
353 // init listing of /sys/class/infiniband/ (or rediscovery)
354 if (unlikely(!initialized) || unlikely(last_refresh_ports_usec >= dt_to_refresh_ports)) {
355 // If folder does not exists, return 1 to disable
356 DIR *devices_dir = opendir(sys_class_infiniband_dirname);
357 if (unlikely(!devices_dir))
358 return 1;
359
360 // Work on all device available
361 struct dirent *dev_dent;
362 while ((dev_dent = readdir(devices_dir))) {
363 // Skip special folders
364 if (!strcmp(dev_dent->d_name, "..") || !strcmp(dev_dent->d_name, "."))
365 continue;
366
367 // /sys/class/infiniband/<dev>/ports
368 char ports_dirname[FILENAME_MAX + 1];
369 snprintfz(ports_dirname, FILENAME_MAX, "%s/%s/%s", sys_class_infiniband_dirname, dev_dent->d_name, "ports");
370
371 DIR *ports_dir = opendir(ports_dirname);
372 if (unlikely(!ports_dir))
373 continue;
374
375 struct dirent *port_dent;
376 while ((port_dent = readdir(ports_dir))) {
377 // Skip special folders
378 if (!strcmp(port_dent->d_name, "..") || !strcmp(port_dent->d_name, "."))
379 continue;
380
381 char buffer[FILENAME_MAX + 1];
382
383 // Check if counters are available (mandatory)
384 // /sys/class/infiniband/<device>/ports/<port>/counters
385 char counters_dirname[FILENAME_MAX + 1];
386 snprintfz(counters_dirname, FILENAME_MAX, "%s/%s/%s", ports_dirname, port_dent->d_name, "counters");
387 DIR *counters_dir = opendir(counters_dirname);
388 // Standard counters are mandatory
389 if (!counters_dir)
390 continue;
391 closedir(counters_dir);
392
393 // Hardware Counters are optional, used later
394 char hwcounters_dirname[FILENAME_MAX + 1];
395 snprintfz(
396 hwcounters_dirname, FILENAME_MAX, "%s/%s/%s", ports_dirname, port_dent->d_name, "hw_counters");
397
398 // Get new or existing ibport
399 struct ibport *p = get_ibport(dev_dent->d_name, port_dent->d_name);
400 if (!p)
401 continue;
402
403 // Prepare configuration
404 if (!p->configured) {
405 p->configured = 1;
406
407 // Enable by default, will be filtered out later
408 p->enabled = 1;
409
410 p->counters_path = strdupz(counters_dirname);
411 p->hwcounters_path = strdupz(hwcounters_dirname);
412
413 snprintfz(buffer, FILENAME_MAX, "plugin:proc:/sys/class/infiniband:%s", p->name);
414
415 // Standard counters
416 p->do_bytes = inicfg_get_boolean_ondemand(&netdata_config, buffer, "bytes", do_bytes);
417 p->do_packets = inicfg_get_boolean_ondemand(&netdata_config, buffer, "packets", do_packets);
418 p->do_errors = inicfg_get_boolean_ondemand(&netdata_config, buffer, "errors", do_errors);
419
420 // Gen filename allocation and concatenation
421 #define GEN_DO_COUNTER_NAME(NAME, GRP, DESC, DIR, PORT, ...) \
422 PORT->file_##NAME = callocz(1, strlen(PORT->counters_path) + sizeof(#NAME) + 3); \
423 strcat(PORT->file_##NAME, PORT->counters_path); \
424 strcat(PORT->file_##NAME, "/" #NAME);
425 FOREACH_COUNTER(GEN_DO_COUNTER_NAME, p)
426
427 // Check HW Counters vendor dependent
428 DIR *hwcounters_dir = opendir(hwcounters_dirname);
429 if (hwcounters_dir) {
430 // By default set standard
431 p->do_hwpackets = inicfg_get_boolean_ondemand(&netdata_config, buffer, "hwpackets", do_hwpackets);
432 p->do_hwerrors = inicfg_get_boolean_ondemand(&netdata_config, buffer, "hwerrors", do_hwerrors);
433
434 // VENDORS: Set your own
435
436 // Allocate the chars to the filenames
437 #define GEN_DO_HWCOUNTER_NAME(NAME, GRP, DESC, DIR, PORT, HW, ...) \
438 HW->file_##NAME = callocz(1, strlen(PORT->hwcounters_path) + sizeof(#NAME) + 3); \
439 strcat(HW->file_##NAME, PORT->hwcounters_path); \
440 strcat(HW->file_##NAME, "/" #NAME);
441
442 // VENDOR-MLX: Mellanox
443 if (strncmp(dev_dent->d_name, "mlx", 3) == 0) {
444 // Allocate the vendor specific struct
445 p->hwcounters_mlx = callocz(1, sizeof(struct ibporthw_mlx));
446
447 FOREACH_HWCOUNTER_MLX(GEN_DO_HWCOUNTER_NAME, p, p->hwcounters_mlx)
448
449 // Set the function pointer for hwcounter parsing
450 p->hwcounters_parse = &infiniband_hwcounters_parse_mlx;
451 p->hwcounters_dorrd = &infiniband_hwcounters_dorrd_mlx;
452 }
453
454 // VENDOR: Unknown
455 else {
456 p->do_hwpackets = CONFIG_BOOLEAN_NO;
457 p->do_hwerrors = CONFIG_BOOLEAN_NO;
458 }
459 closedir(hwcounters_dir);
460 }
461 }
462
463 // Check port state to keep activation
464 if (enable_only_active) {
465 snprintfz(buffer, FILENAME_MAX, "%s/%s/%s", ports_dirname, port_dent->d_name, "state");
466 unsigned long long active;
467 // File is "1: DOWN" or "4: ACTIVE", but str2ull will stop on first non-decimal char
468 read_single_number_file(buffer, &active);
469
470 // Want "IB_PORT_ACTIVE" == "4", as defined by drivers/infiniband/core/sysfs.c::state_show()
471 if (active == 4)
472 p->enabled = 1;
473 else
474 p->enabled = 0;
475 }
476
477 if (p->enabled)
478 p->enabled = !simple_pattern_matches(disabled_list, p->name);
479
480 // Check / Update the link speed & width frm "rate" file
481 // Sample output: "100 Gb/sec (4X EDR)"
482 snprintfz(buffer, FILENAME_MAX, "%s/%s/%s", ports_dirname, port_dent->d_name, "rate");
483 char buffer_rate[65];
484 p->width = 4;
485 if (read_txt_file(buffer, buffer_rate, sizeof(buffer_rate))) {
486 collector_error("Unable to read '%s'", buffer);
487 } else {
488 char *buffer_width = strstr(buffer_rate, "(");
489 if (buffer_width) {
490 buffer_width++;
491 // str2ull will stop on first non-decimal value
492 p->speed = str2ull(buffer_rate, NULL);
493 p->width = str2ull(buffer_width, NULL);
494 }
495 }
496
497 if (!p->discovered)
498 collector_info("Infiniband card %s port %s at speed %" PRIu64 " width %" PRIu64 "",
499 dev_dent->d_name,
500 port_dent->d_name,
501 p->speed,
502 p->width);
503
504 p->discovered = 1;
505 }
506 closedir(ports_dir);
507 }
508 closedir(devices_dir);
509
510 initialized = 1;
511 last_refresh_ports_usec = 0;
512 }
513 last_refresh_ports_usec += dt;
514
515 // Update all ports values
516 struct ibport *port;
517 for (port = ibport_root; port; port = port->next) {
518 if (!port->enabled)
519 continue;
520 //
521 // Read values from system to struct
522 //
523
524 // counter from file and place it in ibport struct
525 #define GEN_DO_COUNTER_READ(NAME, GRP, DESC, DIR, PORT, ...) \
526 if (PORT->file_##NAME) { \
527 if (read_single_number_file(PORT->file_##NAME, (unsigned long long *)&PORT->NAME)) { \
528 collector_error("cannot read iface '%s' counter '" #NAME "'", PORT->name); \
529 PORT->file_##NAME = NULL; \
530 } \
531 }
532
533 // Update charts
534 if (port->do_bytes != CONFIG_BOOLEAN_NO) {
535 // Read values from sysfs
536 FOREACH_COUNTER_BYTES(GEN_DO_COUNTER_READ, port)
537
538 // First creation of RRD Set (charts)
539 if (unlikely(!port->st_bytes)) {
540 port->st_bytes = rrdset_create_localhost(
541 "Infiniband",
542 port->chart_id_bytes,
543 NULL,
544 port->chart_family,
545 "ib.bytes",
546 "Bandwidth usage",
547 "kilobits/s",
548 PLUGIN_PROC_NAME,
549 PLUGIN_PROC_MODULE_INFINIBAND_NAME,
550 port->priority + 1,
551 update_every,
552 RRDSET_TYPE_AREA);
553
554 // On this chart, we want to have a KB/s so the dashboard will autoscale it
555 // The reported values are also per-lane, so we must multiply it by the width
556 // x4 lanes multiplier as per Documentation/ABI/stable/sysfs-class-infiniband
557 FOREACH_COUNTER_BYTES(GEN_RRD_DIM_ADD_CUSTOM, port, port->width * 8, 1000, RRD_ALGORITHM_INCREMENTAL)
558
559 port->stv_speed = rrdvar_chart_variable_add_and_acquire(port->st_bytes, "link_speed");
560 }
561
562 // Link read values to dimensions
563 FOREACH_COUNTER_BYTES(GEN_RRD_DIM_SETP, port)
564
565 // For link speed set only variable
566 rrdvar_chart_variable_set(port->st_bytes, port->stv_speed, port->speed);
567
568 rrdset_done(port->st_bytes);
569 }
570
571 if (port->do_packets != CONFIG_BOOLEAN_NO) {
572 // Read values from sysfs
573 FOREACH_COUNTER_PACKETS(GEN_DO_COUNTER_READ, port)
574
575 // First creation of RRD Set (charts)
576 if (unlikely(!port->st_packets)) {
577 port->st_packets = rrdset_create_localhost(
578 "Infiniband",
579 port->chart_id_packets,
580 NULL,
581 port->chart_family,
582 "ib.packets",
583 "Packets Statistics",
584 "packets/s",
585 PLUGIN_PROC_NAME,
586 PLUGIN_PROC_MODULE_INFINIBAND_NAME,
587 port->priority + 2,
588 update_every,
589 RRDSET_TYPE_AREA);
590
591 FOREACH_COUNTER_PACKETS(GEN_RRD_DIM_ADD, port)
592 }
593
594 // Link read values to dimensions
595 FOREACH_COUNTER_PACKETS(GEN_RRD_DIM_SETP, port)
596 rrdset_done(port->st_packets);
597 }
598
599 if (port->do_errors != CONFIG_BOOLEAN_NO) {
600 // Read values from sysfs
601 FOREACH_COUNTER_ERRORS(GEN_DO_COUNTER_READ, port)
602
603 // First creation of RRD Set (charts)
604 if (unlikely(!port->st_errors)) {
605 port->st_errors = rrdset_create_localhost(
606 "Infiniband",
607 port->chart_id_errors,
608 NULL,
609 port->chart_family,
610 "ib.errors",
611 "Error Counters",
612 "errors/s",
613 PLUGIN_PROC_NAME,
614 PLUGIN_PROC_MODULE_INFINIBAND_NAME,
615 port->priority + 3,
616 update_every,
617 RRDSET_TYPE_LINE);
618
619 FOREACH_COUNTER_ERRORS(GEN_RRD_DIM_ADD, port)
620 }
621
622 // Link read values to dimensions
623 FOREACH_COUNTER_ERRORS(GEN_RRD_DIM_SETP, port)
624 rrdset_done(port->st_errors);
625 }
626
627 //
628 // HW Counters
629 //
630
631 // Call the function for parsing and setting hwcounters
632 if (port->hwcounters_parse && port->hwcounters_dorrd) {
633 // Read all values (done by vendor-specific function)
634 (*port->hwcounters_parse)(port);
635
636 if (port->do_hwerrors != CONFIG_BOOLEAN_NO) {
637 // First creation of RRD Set (charts)
638 if (unlikely(!port->st_hwerrors)) {
639 port->st_hwerrors = rrdset_create_localhost(
640 "Infiniband",
641 port->chart_id_hwerrors,
642 NULL,
643 port->chart_family,
644 "ib.hwerrors",
645 "Hardware Errors",
646 "errors/s",
647 PLUGIN_PROC_NAME,
648 PLUGIN_PROC_MODULE_INFINIBAND_NAME,
649 port->priority + 4,
650 update_every,
651 RRDSET_TYPE_LINE);
652
653 // VENDORS: Set your selection
654
655 // VENDOR: Mellanox
656 if (strncmp(port->name, "mlx", 3) == 0) {
657 FOREACH_HWCOUNTER_MLX_ERRORS(GEN_RRD_DIM_ADD_HW, port, port->hwcounters_mlx)
658 }
659
660 // Unknown vendor, should not happen
661 else {
662 collector_error(
663 "Unmanaged vendor for '%s', do_hwerrors should have been set to no. Please report this bug",
664 port->name);
665 port->do_hwerrors = CONFIG_BOOLEAN_NO;
666 }
667 }
668 }
669
670 if (port->do_hwpackets != CONFIG_BOOLEAN_NO) {
671 // First creation of RRD Set (charts)
672 if (unlikely(!port->st_hwpackets)) {
673 port->st_hwpackets = rrdset_create_localhost(
674 "Infiniband",
675 port->chart_id_hwpackets,
676 NULL,
677 port->chart_family,
678 "ib.hwpackets",
679 "Hardware Packets Statistics",
680 "packets/s",
681 PLUGIN_PROC_NAME,
682 PLUGIN_PROC_MODULE_INFINIBAND_NAME,
683 port->priority + 5,
684 update_every,
685 RRDSET_TYPE_LINE);
686
687 // VENDORS: Set your selection
688
689 // VENDOR: Mellanox
690 if (strncmp(port->name, "mlx", 3) == 0) {
691 FOREACH_HWCOUNTER_MLX_PACKETS(GEN_RRD_DIM_ADD_HW, port, port->hwcounters_mlx)
692 }
693
694 // Unknown vendor, should not happen
695 else {
696 collector_error(
697 "Unmanaged vendor for '%s', do_hwpackets should have been set to no. Please report this bug",
698 port->name);
699 port->do_hwpackets = CONFIG_BOOLEAN_NO;
700 }
701 }
702 }
703
704 // Update values to rrd (done by vendor-specific function)
705 (*port->hwcounters_dorrd)(port);
706 }
707 }
708
709 return 0;
710 }