sys_class_infiniband.c 33 KB

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