1
2
3
4
5
6
7#include "rdma.h"
8#include "res.h"
9#include "stat.h"
10#include <inttypes.h>
11
12static int stat_help(struct rd *rd)
13{
14 pr_out("Usage: %s [ OPTIONS ] statistic { COMMAND | help }\n", rd->filename);
15 pr_out(" %s statistic OBJECT show\n", rd->filename);
16 pr_out(" %s statistic OBJECT show link [ DEV/PORT_INDEX ] [ FILTER-NAME FILTER-VALUE ]\n", rd->filename);
17 pr_out(" %s statistic OBJECT mode\n", rd->filename);
18 pr_out(" %s statistic OBJECT set COUNTER_SCOPE [DEV/PORT_INDEX] auto {CRITERIA | off}\n", rd->filename);
19 pr_out(" %s statistic OBJECT bind COUNTER_SCOPE [DEV/PORT_INDEX] [OBJECT-ID] [COUNTER-ID]\n", rd->filename);
20 pr_out(" %s statistic OBJECT unbind COUNTER_SCOPE [DEV/PORT_INDEX] [COUNTER-ID]\n", rd->filename);
21 pr_out(" %s statistic show\n", rd->filename);
22 pr_out(" %s statistic show link [ DEV/PORT_INDEX ]\n", rd->filename);
23 pr_out(" %s statistic mode [ supported ]\n", rd->filename);
24 pr_out(" %s statistic mode [ supported ] link [ DEV/PORT_INDEX ]\n", rd->filename);
25 pr_out(" %s statistic set link [ DEV/PORT_INDEX ] optional-counters [ OPTIONAL-COUNTERS ]\n", rd->filename);
26 pr_out(" %s statistic unset link [ DEV/PORT_INDEX ] optional-counters\n", rd->filename);
27 pr_out("where OBJECT: = { qp }\n");
28 pr_out(" CRITERIA : = { type }\n");
29 pr_out(" COUNTER_SCOPE: = { link | dev }\n");
30 pr_out(" FILTER_NAME: = { cntn | lqpn | pid }\n");
31 pr_out("Examples:\n");
32 pr_out(" %s statistic qp show\n", rd->filename);
33 pr_out(" %s statistic qp show link mlx5_2/1\n", rd->filename);
34 pr_out(" %s statistic qp mode\n", rd->filename);
35 pr_out(" %s statistic qp mode link mlx5_0\n", rd->filename);
36 pr_out(" %s statistic qp set link mlx5_2/1 auto type on\n", rd->filename);
37 pr_out(" %s statistic qp set link mlx5_2/1 auto off\n", rd->filename);
38 pr_out(" %s statistic qp bind link mlx5_2/1 lqpn 178\n", rd->filename);
39 pr_out(" %s statistic qp bind link mlx5_2/1 lqpn 178 cntn 4\n", rd->filename);
40 pr_out(" %s statistic qp unbind link mlx5_2/1 cntn 4\n", rd->filename);
41 pr_out(" %s statistic qp unbind link mlx5_2/1 cntn 4 lqpn 178\n", rd->filename);
42 pr_out(" %s statistic show\n", rd->filename);
43 pr_out(" %s statistic show link mlx5_2/1\n", rd->filename);
44 pr_out(" %s statistic mode\n", rd->filename);
45 pr_out(" %s statistic mode link mlx5_2/1\n", rd->filename);
46 pr_out(" %s statistic mode supported\n", rd->filename);
47 pr_out(" %s statistic mode supported link mlx5_2/1\n", rd->filename);
48 pr_out(" %s statistic set link mlx5_2/1 optional-counters cc_rx_ce_pkts,cc_rx_cnp_pkts\n", rd->filename);
49 pr_out(" %s statistic unset link mlx5_2/1 optional-counters\n", rd->filename);
50
51 return 0;
52}
53
54struct counter_param {
55 char *name;
56 uint32_t attr;
57};
58
59static struct counter_param auto_params[] = {
60 { "type", RDMA_COUNTER_MASK_QP_TYPE, },
61 { "pid", RDMA_COUNTER_MASK_PID, },
62 { NULL },
63};
64
65static int prepare_auto_mode_str(struct nlattr **tb, uint32_t mask,
66 char *output, int len)
67{
68 char s[] = "qp auto";
69 int i, outlen = strlen(s);
70 bool first = true;
71
72 memset(output, 0, len);
73 snprintf(output, len, "%s", s);
74
75 if (mask) {
76 for (i = 0; auto_params[i].name != NULL; i++) {
77 if (mask & auto_params[i].attr) {
78 outlen += strlen(auto_params[i].name) + 1;
79 if (outlen >= len)
80 return -EINVAL;
81 if (first) {
82 strcat(output, " ");
83 first = false;
84 } else
85 strcat(output, ",");
86
87 strcat(output, auto_params[i].name);
88 }
89 }
90
91 if (outlen + strlen(" on") >= len)
92 return -EINVAL;
93 strcat(output, " on");
94 } else {
95 if (outlen + strlen(" off") >= len)
96 return -EINVAL;
97 strcat(output, " off");
98 }
99
100 return 0;
101}
102
103static int qp_link_get_mode_parse_cb(const struct nlmsghdr *nlh, void *data)
104{
105 struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
106 uint32_t mode = 0, mask = 0;
107 char output[128] = {};
108 struct rd *rd = data;
109 uint32_t idx, port;
110 const char *name;
111
112 mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
113 if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME])
114 return MNL_CB_ERROR;
115
116 if (!tb[RDMA_NLDEV_ATTR_PORT_INDEX]) {
117 pr_err("This tool doesn't support switches yet\n");
118 return MNL_CB_ERROR;
119 }
120
121 idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
122 port = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
123 name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]);
124 if (tb[RDMA_NLDEV_ATTR_STAT_MODE])
125 mode = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_STAT_MODE]);
126
127 if (mode == RDMA_COUNTER_MODE_AUTO) {
128 if (!tb[RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK])
129 return MNL_CB_ERROR;
130 mask = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK]);
131 prepare_auto_mode_str(tb, mask, output, sizeof(output));
132 } else {
133 snprintf(output, sizeof(output), "qp auto off");
134 }
135
136 open_json_object(NULL);
137 print_link(rd, idx, name, port, tb);
138 print_color_string(PRINT_ANY, COLOR_NONE, "mode", "mode %s ", output);
139 newline(rd);
140 return MNL_CB_OK;
141}
142
143static int stat_one_qp_link_get_mode(struct rd *rd)
144{
145 uint32_t seq;
146 int ret;
147
148 if (!rd->port_idx)
149 return 0;
150
151 rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET,
152 &seq, (NLM_F_REQUEST | NLM_F_ACK));
153
154 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
155 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
156
157
158
159 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
160 RDMA_COUNTER_MODE_MANUAL);
161 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
162 ret = rd_send_msg(rd);
163 if (ret)
164 return ret;
165
166 ret = rd_recv_msg(rd, qp_link_get_mode_parse_cb, rd, seq);
167 return ret;
168}
169
170static int stat_qp_link_get_mode(struct rd *rd)
171{
172 return rd_exec_link(rd, stat_one_qp_link_get_mode, false);
173}
174
175static int stat_qp_get_mode(struct rd *rd)
176{
177 const struct rd_cmd cmds[] = {
178 { NULL, stat_qp_link_get_mode },
179 { "link", stat_qp_link_get_mode },
180 { "help", stat_help },
181 { 0 }
182 };
183
184 return rd_exec_cmd(rd, cmds, "parameter");
185}
186
187int res_get_hwcounters(struct rd *rd, struct nlattr *hwc_table, bool print)
188{
189 struct nlattr *nla_entry;
190 const char *nm;
191 uint64_t v;
192 int err;
193
194 mnl_attr_for_each_nested(nla_entry, hwc_table) {
195 struct nlattr *hw_line[RDMA_NLDEV_ATTR_MAX] = {};
196
197 err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, hw_line);
198 if (err != MNL_CB_OK)
199 return -EINVAL;
200
201 if (!hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME] ||
202 !hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE]) {
203 return -EINVAL;
204 }
205
206 if (!print)
207 continue;
208
209 nm = mnl_attr_get_str(hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]);
210 v = mnl_attr_get_u64(hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE]);
211 if (rd->pretty_output && !rd->json_output)
212 newline_indent(rd);
213 res_print_uint(rd, nm, v, hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]);
214 }
215
216 return MNL_CB_OK;
217}
218
219static int res_counter_line(struct rd *rd, const char *name, int index,
220 struct nlattr **nla_line)
221{
222 uint32_t cntn, port = 0, pid = 0, qpn, qp_type = 0;
223 struct nlattr *hwc_table, *qp_table;
224 struct nlattr *nla_entry;
225 const char *comm = NULL;
226 bool isfirst;
227 int err;
228
229 if (nla_line[RDMA_NLDEV_ATTR_PORT_INDEX])
230 port = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_PORT_INDEX]);
231
232 hwc_table = nla_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS];
233 qp_table = nla_line[RDMA_NLDEV_ATTR_RES_QP];
234 if (!hwc_table || !qp_table ||
235 !nla_line[RDMA_NLDEV_ATTR_STAT_COUNTER_ID])
236 return MNL_CB_ERROR;
237
238 cntn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]);
239 if (rd_is_filtered_attr(rd, "cntn", cntn,
240 nla_line[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]))
241 return MNL_CB_OK;
242
243 if (nla_line[RDMA_NLDEV_ATTR_RES_TYPE])
244 qp_type = mnl_attr_get_u8(nla_line[RDMA_NLDEV_ATTR_RES_TYPE]);
245
246 if (rd_is_string_filtered_attr(rd, "qp-type", qp_types_to_str(qp_type),
247 nla_line[RDMA_NLDEV_ATTR_RES_TYPE]))
248 return MNL_CB_OK;
249
250 if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) {
251 pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]);
252 comm = get_task_name(pid);
253 }
254 if (rd_is_filtered_attr(rd, "pid", pid,
255 nla_line[RDMA_NLDEV_ATTR_RES_PID]))
256 return MNL_CB_OK;
257
258 if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME])
259 comm = (char *)mnl_attr_get_str(
260 nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]);
261
262 mnl_attr_for_each_nested(nla_entry, qp_table) {
263 struct nlattr *qp_line[RDMA_NLDEV_ATTR_MAX] = {};
264
265 err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, qp_line);
266 if (err != MNL_CB_OK)
267 return -EINVAL;
268
269 if (!qp_line[RDMA_NLDEV_ATTR_RES_LQPN])
270 return -EINVAL;
271
272 qpn = mnl_attr_get_u32(qp_line[RDMA_NLDEV_ATTR_RES_LQPN]);
273 if (rd_is_filtered_attr(rd, "lqpn", qpn,
274 qp_line[RDMA_NLDEV_ATTR_RES_LQPN]))
275 return MNL_CB_OK;
276 }
277
278 err = res_get_hwcounters(rd, hwc_table, false);
279 if (err != MNL_CB_OK)
280 return err;
281 open_json_object(NULL);
282 print_link(rd, index, name, port, nla_line);
283 print_color_uint(PRINT_ANY, COLOR_NONE, "cntn", "cntn %u ", cntn);
284 if (nla_line[RDMA_NLDEV_ATTR_RES_TYPE])
285 print_qp_type(rd, qp_type);
286 res_print_uint(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]);
287 print_comm(rd, comm, nla_line);
288 res_get_hwcounters(rd, hwc_table, true);
289 isfirst = true;
290 open_json_array(PRINT_JSON, "lqpn");
291 print_color_string(PRINT_FP, COLOR_NONE, NULL, "\n LQPN: <", NULL);
292 mnl_attr_for_each_nested(nla_entry, qp_table) {
293 struct nlattr *qp_line[RDMA_NLDEV_ATTR_MAX] = {};
294 err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, qp_line);
295 if (err != MNL_CB_OK)
296 return -EINVAL;
297
298 if (!qp_line[RDMA_NLDEV_ATTR_RES_LQPN])
299 return -EINVAL;
300
301 qpn = mnl_attr_get_u32(qp_line[RDMA_NLDEV_ATTR_RES_LQPN]);
302 if (!isfirst)
303 print_color_string(PRINT_FP, COLOR_NONE, NULL, ",",
304 NULL);
305 print_color_uint(PRINT_ANY, COLOR_NONE, NULL, "%d", qpn);
306 isfirst = false;
307 }
308 close_json_array(PRINT_ANY, ">");
309 newline(rd);
310 return MNL_CB_OK;
311}
312
313static int stat_qp_show_parse_cb(const struct nlmsghdr *nlh, void *data)
314{
315 struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
316 struct nlattr *nla_table, *nla_entry;
317 struct rd *rd = data;
318 const char *name;
319 uint32_t idx;
320 int ret = MNL_CB_OK;
321
322 mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
323 if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME] ||
324 !tb[RDMA_NLDEV_ATTR_STAT_COUNTER])
325 return MNL_CB_ERROR;
326
327 name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]);
328 idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
329 nla_table = tb[RDMA_NLDEV_ATTR_STAT_COUNTER];
330
331 mnl_attr_for_each_nested(nla_entry, nla_table) {
332 struct nlattr *nla_line[RDMA_NLDEV_ATTR_MAX] = {};
333
334 ret = mnl_attr_parse_nested(nla_entry, rd_attr_cb, nla_line);
335 if (ret != MNL_CB_OK)
336 break;
337
338 ret = res_counter_line(rd, name, idx, nla_line);
339 if (ret != MNL_CB_OK)
340 break;
341 }
342
343 return ret;
344}
345
346static const struct filters stat_valid_filters[MAX_NUMBER_OF_FILTERS] = {
347 { .name = "cntn", .is_number = true },
348 { .name = "lqpn", .is_number = true },
349 { .name = "pid", .is_number = true },
350 { .name = "qp-type", .is_number = false },
351};
352
353static int stat_qp_show_one_link(struct rd *rd)
354{
355 int flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP;
356 uint32_t seq;
357 int ret;
358
359 if (!rd->port_idx)
360 return 0;
361
362 ret = rd_build_filter(rd, stat_valid_filters);
363 if (ret)
364 return ret;
365
366 rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq, flags);
367 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
368 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
369 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
370 ret = rd_send_msg(rd);
371 if (ret)
372 return ret;
373
374 ret = rd_recv_msg(rd, stat_qp_show_parse_cb, rd, seq);
375 return ret;
376}
377
378static int stat_qp_show_link(struct rd *rd)
379{
380 return rd_exec_link(rd, stat_qp_show_one_link, false);
381}
382
383static int stat_qp_show(struct rd *rd)
384{
385 const struct rd_cmd cmds[] = {
386 { NULL, stat_qp_show_link },
387 { "link", stat_qp_show_link },
388 { "help", stat_help },
389 { 0 }
390 };
391
392 return rd_exec_cmd(rd, cmds, "parameter");
393}
394
395static int stat_qp_set_link_auto_sendmsg(struct rd *rd, uint32_t mask)
396{
397 uint32_t seq;
398
399 rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET,
400 &seq, (NLM_F_REQUEST | NLM_F_ACK));
401
402 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
403 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
404 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
405 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
406 RDMA_COUNTER_MODE_AUTO);
407 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK, mask);
408
409 return rd_sendrecv_msg(rd, seq);
410}
411
412static int stat_get_auto_mode_mask(struct rd *rd)
413{
414 char *modes = rd_argv(rd), *mode, *saved_ptr;
415 const char *delim = ",";
416 int mask = 0, found, i;
417
418 if (!modes)
419 return mask;
420
421 mode = strtok_r(modes, delim, &saved_ptr);
422 do {
423 if (!mode)
424 break;
425
426 found = false;
427 for (i = 0; auto_params[i].name != NULL; i++) {
428 if (!strcmp(mode, auto_params[i].name)) {
429 mask |= auto_params[i].attr;
430 found = true;
431 break;
432 }
433 }
434
435 if (!found) {
436 pr_err("Unknown auto mode '%s'.\n", mode);
437 mask = 0;
438 break;
439 }
440
441 mode = strtok_r(NULL, delim, &saved_ptr);
442 } while(1);
443
444 if (mask)
445 rd_arg_inc(rd);
446
447 return mask;
448}
449
450static int stat_one_qp_set_link_auto(struct rd *rd)
451{
452 int auto_mask = 0;
453
454 if (!rd_argc(rd))
455 return -EINVAL;
456
457 if (!strcmpx(rd_argv(rd), "off")) {
458 rd_arg_inc(rd);
459 return stat_qp_set_link_auto_sendmsg(rd, 0);
460 }
461
462 auto_mask = stat_get_auto_mode_mask(rd);
463 if (!auto_mask || !rd_argc(rd))
464 return -EINVAL;
465
466 if (!strcmpx(rd_argv(rd), "on")) {
467 rd_arg_inc(rd);
468 return stat_qp_set_link_auto_sendmsg(rd, auto_mask);
469 } else {
470 pr_err("Unknown parameter '%s'.\n", rd_argv(rd));
471 return -EINVAL;
472 }
473}
474
475static int stat_one_qp_set_link(struct rd *rd)
476{
477 const struct rd_cmd cmds[] = {
478 { NULL, stat_one_qp_link_get_mode },
479 { "auto", stat_one_qp_set_link_auto },
480 { 0 }
481 };
482
483 if (!rd->port_idx)
484 return 0;
485
486 return rd_exec_cmd(rd, cmds, "parameter");
487}
488
489static int stat_qp_set_link(struct rd *rd)
490{
491 return rd_exec_link(rd, stat_one_qp_set_link, false);
492}
493
494static int stat_qp_set(struct rd *rd)
495{
496 const struct rd_cmd cmds[] = {
497 { NULL, stat_help },
498 { "link", stat_qp_set_link },
499 { "help", stat_help },
500 { 0 }
501 };
502
503 return rd_exec_cmd(rd, cmds, "parameter");
504}
505
506static int stat_get_arg_str(struct rd *rd, const char *arg, char **value, bool allow_empty)
507{
508 int len = 0;
509
510 if (strcmpx(rd_argv(rd), arg) != 0) {
511 pr_err("Unknown parameter '%s'.\n", rd_argv(rd));
512 return -EINVAL;
513 }
514
515 rd_arg_inc(rd);
516 if (!rd_no_arg(rd)) {
517 *value = strdup(rd_argv(rd));
518 len = strlen(*value);
519 rd_arg_inc(rd);
520 }
521
522 if ((allow_empty && len) || (!allow_empty && !len)) {
523 stat_help(rd);
524 return -EINVAL;
525 }
526
527 return 0;
528}
529
530static int stat_get_arg(struct rd *rd, const char *arg)
531{
532 int value = 0;
533 char *endp;
534
535 if (strcmpx(rd_argv(rd), arg) != 0)
536 return -EINVAL;
537
538 rd_arg_inc(rd);
539
540 if (rd_is_multiarg(rd)) {
541 pr_err("The parameter %s shouldn't include range\n", arg);
542 return -EINVAL;
543 }
544
545 value = strtol(rd_argv(rd), &endp, 10);
546 rd_arg_inc(rd);
547
548 return value;
549}
550
551static int stat_one_qp_bind(struct rd *rd)
552{
553 int lqpn = 0, cntn = 0, ret;
554 uint32_t seq;
555
556 if (rd_no_arg(rd)) {
557 stat_help(rd);
558 return -EINVAL;
559 }
560
561 ret = rd_build_filter(rd, stat_valid_filters);
562 if (ret)
563 return ret;
564
565 lqpn = stat_get_arg(rd, "lqpn");
566 if (lqpn < 0)
567 return lqpn;
568
569 rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET,
570 &seq, (NLM_F_REQUEST | NLM_F_ACK));
571
572 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
573 RDMA_COUNTER_MODE_MANUAL);
574
575 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
576 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
577 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
578 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_RES_LQPN, lqpn);
579
580 if (rd_argc(rd)) {
581 cntn = stat_get_arg(rd, "cntn");
582 if (cntn < 0)
583 return cntn;
584
585 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID,
586 cntn);
587 }
588
589 return rd_sendrecv_msg(rd, seq);
590}
591
592static int do_stat_qp_unbind_lqpn(struct rd *rd, uint32_t cntn, uint32_t lqpn)
593{
594 uint32_t seq;
595
596 rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_DEL,
597 &seq, (NLM_F_REQUEST | NLM_F_ACK));
598
599 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
600 RDMA_COUNTER_MODE_MANUAL);
601 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
602 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
603 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
604 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID, cntn);
605 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_RES_LQPN, lqpn);
606
607 return rd_sendrecv_msg(rd, seq);
608}
609
610static int stat_get_counter_parse_cb(const struct nlmsghdr *nlh, void *data)
611{
612 struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
613 struct nlattr *nla_table, *nla_entry;
614 struct rd *rd = data;
615 uint32_t lqpn, cntn;
616 int err;
617
618 mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
619
620 if (!tb[RDMA_NLDEV_ATTR_STAT_COUNTER_ID])
621 return MNL_CB_ERROR;
622 cntn = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]);
623
624 nla_table = tb[RDMA_NLDEV_ATTR_RES_QP];
625 if (!nla_table)
626 return MNL_CB_ERROR;
627
628 mnl_attr_for_each_nested(nla_entry, nla_table) {
629 struct nlattr *nla_line[RDMA_NLDEV_ATTR_MAX] = {};
630
631 err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, nla_line);
632 if (err != MNL_CB_OK)
633 return -EINVAL;
634
635 if (!nla_line[RDMA_NLDEV_ATTR_RES_LQPN])
636 return -EINVAL;
637
638 lqpn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_LQPN]);
639 err = do_stat_qp_unbind_lqpn(rd, cntn, lqpn);
640 if (err)
641 return MNL_CB_ERROR;
642 }
643
644 return MNL_CB_OK;
645}
646
647static int stat_one_qp_unbind(struct rd *rd)
648{
649 int flags = NLM_F_REQUEST | NLM_F_ACK, ret;
650 char buf[MNL_SOCKET_BUFFER_SIZE];
651 int lqpn = 0, cntn = 0;
652 unsigned int portid;
653 uint32_t seq;
654
655 if (rd_no_arg(rd)) {
656 stat_help(rd);
657 return -EINVAL;
658 }
659
660 ret = rd_build_filter(rd, stat_valid_filters);
661 if (ret)
662 return ret;
663
664 cntn = stat_get_arg(rd, "cntn");
665 if (cntn < 0)
666 return cntn;
667
668 if (rd_argc(rd)) {
669 lqpn = stat_get_arg(rd, "lqpn");
670 if (lqpn < 0)
671 return lqpn;
672 return do_stat_qp_unbind_lqpn(rd, cntn, lqpn);
673 }
674
675 rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq, flags);
676 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
677 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
678 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
679 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID, cntn);
680 ret = rd_send_msg(rd);
681 if (ret)
682 return ret;
683
684
685
686
687
688 portid = mnl_socket_get_portid(rd->nl);
689 ret = mnl_socket_recvfrom(rd->nl, buf, sizeof(buf));
690 if (ret <= 0)
691 return ret;
692
693 ret = mnl_cb_run(buf, ret, seq, portid, stat_get_counter_parse_cb, rd);
694 mnl_socket_close(rd->nl);
695 if (ret != MNL_CB_OK)
696 return ret;
697
698 return 0;
699}
700
701static int stat_qp_bind_link(struct rd *rd)
702{
703 return rd_exec_link(rd, stat_one_qp_bind, true);
704}
705
706static int stat_qp_bind(struct rd *rd)
707{
708 const struct rd_cmd cmds[] = {
709 { NULL, stat_help },
710 { "link", stat_qp_bind_link },
711 { "help", stat_help },
712 { 0 },
713 };
714
715 return rd_exec_cmd(rd, cmds, "parameter");
716}
717
718static int stat_qp_unbind_link(struct rd *rd)
719{
720 return rd_exec_link(rd, stat_one_qp_unbind, true);
721}
722
723static int stat_qp_unbind(struct rd *rd)
724{
725 const struct rd_cmd cmds[] = {
726 { NULL, stat_help },
727 { "link", stat_qp_unbind_link },
728 { "help", stat_help },
729 { 0 },
730 };
731
732 return rd_exec_cmd(rd, cmds, "parameter");
733}
734
735static int stat_qp(struct rd *rd)
736{
737 const struct rd_cmd cmds[] = {
738 { NULL, stat_qp_show },
739 { "show", stat_qp_show },
740 { "list", stat_qp_show },
741 { "mode", stat_qp_get_mode },
742 { "set", stat_qp_set },
743 { "bind", stat_qp_bind },
744 { "unbind", stat_qp_unbind },
745 { "help", stat_help },
746 { 0 }
747 };
748
749 return rd_exec_cmd(rd, cmds, "parameter");
750}
751
752static int do_stat_mode_parse_cb(const struct nlmsghdr *nlh, void *data,
753 bool supported)
754{
755 struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
756 struct nlattr *nla_entry;
757 const char *dev, *name;
758 struct rd *rd = data;
759 int enabled, err = 0;
760 bool isfirst = true;
761 uint32_t port;
762
763 mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
764 if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME] ||
765 !tb[RDMA_NLDEV_ATTR_PORT_INDEX] ||
766 !tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS])
767 return MNL_CB_ERROR;
768
769 dev = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]);
770 port = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
771
772 mnl_attr_for_each_nested(nla_entry,
773 tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS]) {
774 struct nlattr *cnt[RDMA_NLDEV_ATTR_MAX] = {};
775
776 err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, cnt);
777 if ((err != MNL_CB_OK) ||
778 (!cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]))
779 return -EINVAL;
780
781 if (!cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC])
782 continue;
783
784 enabled = mnl_attr_get_u8(cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC]);
785 name = mnl_attr_get_str(cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]);
786 if (supported || enabled) {
787 if (isfirst) {
788 open_json_object(NULL);
789 print_color_string(PRINT_ANY, COLOR_NONE,
790 "ifname", "link %s/", dev);
791 print_color_uint(PRINT_ANY, COLOR_NONE, "port",
792 "%u ", port);
793 if (supported)
794 open_json_array(PRINT_ANY,
795 "supported optional-counters");
796 else
797 open_json_array(PRINT_ANY,
798 "optional-counters");
799 print_color_string(PRINT_FP, COLOR_NONE, NULL,
800 " ", NULL);
801 isfirst = false;
802 } else {
803 print_color_string(PRINT_FP, COLOR_NONE, NULL,
804 ",", NULL);
805 }
806 if (rd->pretty_output && !rd->json_output)
807 newline_indent(rd);
808
809 print_color_string(PRINT_ANY, COLOR_NONE, NULL, "%s",
810 name);
811 }
812 }
813
814 if (!isfirst) {
815 close_json_array(PRINT_JSON, NULL);
816 newline(rd);
817 }
818
819 return 0;
820}
821
822static int stat_mode_parse_cb(const struct nlmsghdr *nlh, void *data)
823{
824 return do_stat_mode_parse_cb(nlh, data, false);
825}
826
827static int stat_mode_parse_cb_supported(const struct nlmsghdr *nlh, void *data)
828{
829 return do_stat_mode_parse_cb(nlh, data, true);
830}
831
832static int stat_one_link_get_status_req(struct rd *rd, uint32_t *seq)
833{
834 int flags = NLM_F_REQUEST | NLM_F_ACK;
835
836 rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET_STATUS, seq, flags);
837 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
838 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
839
840 return rd_send_msg(rd);
841}
842
843static int stat_one_link_get_mode(struct rd *rd)
844{
845 uint32_t seq;
846 int err;
847
848 if (!rd->port_idx)
849 return 0;
850
851 err = stat_one_link_get_status_req(rd, &seq);
852 if (err)
853 return err;
854
855 return rd_recv_msg(rd, stat_mode_parse_cb, rd, seq);
856}
857
858static int stat_one_link_get_mode_supported(struct rd *rd)
859{
860 uint32_t seq;
861 int err;
862
863 if (!rd->port_idx)
864 return 0;
865
866 err = stat_one_link_get_status_req(rd, &seq);
867 if (err)
868 return err;
869
870 return rd_recv_msg(rd, stat_mode_parse_cb_supported, rd, seq);
871}
872
873static int stat_link_get_mode(struct rd *rd)
874{
875 return rd_exec_link(rd, stat_one_link_get_mode, false);
876}
877
878static int stat_link_get_mode_supported(struct rd *rd)
879{
880 return rd_exec_link(rd, stat_one_link_get_mode_supported, false);
881}
882
883static int stat_mode_supported(struct rd *rd)
884{
885 const struct rd_cmd cmds[] = {
886 { NULL, stat_link_get_mode_supported },
887 { "link", stat_link_get_mode_supported },
888 { "help", stat_help },
889 { 0 },
890 };
891 return rd_exec_cmd(rd, cmds, "parameter");
892}
893
894static int stat_mode(struct rd *rd)
895{
896 const struct rd_cmd cmds[] = {
897 { NULL, stat_link_get_mode },
898 { "link", stat_link_get_mode },
899 { "show", stat_link_get_mode },
900 { "supported", stat_mode_supported },
901 { "help", stat_help },
902 { 0 },
903 };
904
905 return rd_exec_cmd(rd, cmds, "parameter");
906}
907
908static int stat_one_set_link_opcounters(const struct nlmsghdr *nlh, void *data)
909{
910 struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
911 struct nlattr *nla_entry, *tb_set;
912 int ret, flags = NLM_F_REQUEST | NLM_F_ACK;
913 char *opcnt, *opcnts;
914 struct rd *rd = data;
915 uint32_t seq;
916 bool found;
917
918 mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
919 if (!tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS])
920 return MNL_CB_ERROR;
921
922 if (rd_no_arg(rd)) {
923 stat_help(rd);
924 return -EINVAL;
925 }
926
927 ret = stat_get_arg_str(rd, "optional-counters", &opcnts, false);
928 if (ret)
929 return ret;
930
931 rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET, &seq, flags);
932 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX,
933 rd->dev_idx);
934 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX,
935 rd->port_idx);
936
937 tb_set = mnl_attr_nest_start(rd->nlh, RDMA_NLDEV_ATTR_STAT_HWCOUNTERS);
938
939 opcnt = strtok(opcnts, ",");
940 while (opcnt) {
941 found = false;
942 mnl_attr_for_each_nested(nla_entry,
943 tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS]) {
944 struct nlattr *cnt[RDMA_NLDEV_ATTR_MAX] = {}, *nm, *id;
945
946 if (mnl_attr_parse_nested(nla_entry, rd_attr_cb,
947 cnt) != MNL_CB_OK)
948 return -EINVAL;
949
950 nm = cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME];
951 id = cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_INDEX];
952 if (!nm || ! id)
953 return -EINVAL;
954
955 if (!cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC])
956 continue;
957
958 if (strcmp(opcnt, mnl_attr_get_str(nm)) == 0) {
959 mnl_attr_put_u32(rd->nlh,
960 RDMA_NLDEV_ATTR_STAT_HWCOUNTER_INDEX,
961 mnl_attr_get_u32(id));
962 found = true;
963 }
964 }
965
966 if (!found)
967 return -EINVAL;
968
969 opcnt = strtok(NULL, ",");
970 }
971 mnl_attr_nest_end(rd->nlh, tb_set);
972
973 return rd_sendrecv_msg(rd, seq);
974}
975
976static int stat_one_set_link(struct rd *rd)
977{
978 uint32_t seq;
979 int err;
980
981 if (!rd->port_idx)
982 return 0;
983
984 err = stat_one_link_get_status_req(rd, &seq);
985 if (err)
986 return err;
987
988 return rd_recv_msg(rd, stat_one_set_link_opcounters, rd, seq);
989}
990
991static int stat_set_link(struct rd *rd)
992{
993 return rd_exec_link(rd, stat_one_set_link, true);
994}
995
996static int stat_set(struct rd *rd)
997{
998 const struct rd_cmd cmds[] = {
999 { NULL, stat_help },
1000 { "link", stat_set_link },
1001 { "help", stat_help },
1002 { 0 },
1003 };
1004 return rd_exec_cmd(rd, cmds, "parameter");
1005}
1006
1007static int stat_one_unset_link_opcounters(struct rd *rd)
1008{
1009 int ret, flags = NLM_F_REQUEST | NLM_F_ACK;
1010 struct nlattr *tbl;
1011 uint32_t seq;
1012 char *opcnts;
1013
1014 if (rd_no_arg(rd)) {
1015 stat_help(rd);
1016 return -EINVAL;
1017 }
1018
1019 ret = stat_get_arg_str(rd, "optional-counters", &opcnts, true);
1020 if (ret)
1021 return ret;
1022
1023 rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET, &seq, flags);
1024 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX,
1025 rd->dev_idx);
1026 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX,
1027 rd->port_idx);
1028
1029 tbl = mnl_attr_nest_start(rd->nlh, RDMA_NLDEV_ATTR_STAT_HWCOUNTERS);
1030 mnl_attr_nest_end(rd->nlh, tbl);
1031
1032 return rd_sendrecv_msg(rd, seq);
1033}
1034
1035static int stat_one_unset_link(struct rd *rd)
1036{
1037 return stat_one_unset_link_opcounters(rd);
1038}
1039
1040static int stat_unset_link(struct rd *rd)
1041{
1042 return rd_exec_link(rd, stat_one_unset_link, true);
1043}
1044
1045static int stat_unset(struct rd *rd)
1046{
1047 const struct rd_cmd cmds[] = {
1048 { NULL, stat_help },
1049 { "link", stat_unset_link },
1050 { "help", stat_help },
1051 { 0 },
1052 };
1053 return rd_exec_cmd(rd, cmds, "parameter");
1054}
1055
1056static int stat_show_parse_cb(const struct nlmsghdr *nlh, void *data)
1057{
1058 struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
1059 struct rd *rd = data;
1060 const char *name;
1061 uint32_t port;
1062 int ret;
1063
1064 mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
1065 if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME] ||
1066 !tb[RDMA_NLDEV_ATTR_PORT_INDEX] ||
1067 !tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS])
1068 return MNL_CB_ERROR;
1069
1070 name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]);
1071 port = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
1072 open_json_object(NULL);
1073 print_color_string(PRINT_ANY, COLOR_NONE, "ifname", "link %s/", name);
1074 print_color_uint(PRINT_ANY, COLOR_NONE, "port", "%u ", port);
1075 ret = res_get_hwcounters(rd, tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS], true);
1076
1077 newline(rd);
1078 return ret;
1079}
1080
1081static int stat_show_one_link(struct rd *rd)
1082{
1083 int flags = NLM_F_REQUEST | NLM_F_ACK;
1084 uint32_t seq;
1085 int ret;
1086
1087 if (!rd->port_idx)
1088 return 0;
1089
1090 rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq, flags);
1091 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
1092 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
1093 ret = rd_send_msg(rd);
1094 if (ret)
1095 return ret;
1096
1097 return rd_recv_msg(rd, stat_show_parse_cb, rd, seq);
1098}
1099
1100static int stat_show_link(struct rd *rd)
1101{
1102 return rd_exec_link(rd, stat_show_one_link, false);
1103}
1104
1105static int stat_show(struct rd *rd)
1106{
1107 const struct rd_cmd cmds[] = {
1108 { NULL, stat_show_link },
1109 { "link", stat_show_link },
1110 { "mr", stat_mr },
1111 { "help", stat_help },
1112 { 0 }
1113 };
1114
1115 return rd_exec_cmd(rd, cmds, "parameter");
1116}
1117
1118int cmd_stat(struct rd *rd)
1119{
1120 const struct rd_cmd cmds[] = {
1121 { NULL, stat_show },
1122 { "show", stat_show },
1123 { "list", stat_show },
1124 { "help", stat_help },
1125 { "qp", stat_qp },
1126 { "mr", stat_mr },
1127 { "mode", stat_mode },
1128 { "set", stat_set },
1129 { "unset", stat_unset },
1130 { 0 }
1131 };
1132
1133 return rd_exec_cmd(rd, cmds, "statistic command");
1134}
1135