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_u64(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 SPRINT_BUF(b);
252
253 pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]);
254 if (!get_task_name(pid, b, sizeof(b)))
255 comm = b;
256 } else if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) {
257
258 comm = (char *)mnl_attr_get_str(
259 nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]);
260 }
261
262 if (rd_is_filtered_attr(rd, "pid", pid,
263 nla_line[RDMA_NLDEV_ATTR_RES_PID]))
264 return MNL_CB_OK;
265
266 mnl_attr_for_each_nested(nla_entry, qp_table) {
267 struct nlattr *qp_line[RDMA_NLDEV_ATTR_MAX] = {};
268
269 err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, qp_line);
270 if (err != MNL_CB_OK)
271 return -EINVAL;
272
273 if (!qp_line[RDMA_NLDEV_ATTR_RES_LQPN])
274 return -EINVAL;
275
276 qpn = mnl_attr_get_u32(qp_line[RDMA_NLDEV_ATTR_RES_LQPN]);
277 if (rd_is_filtered_attr(rd, "lqpn", qpn,
278 qp_line[RDMA_NLDEV_ATTR_RES_LQPN]))
279 return MNL_CB_OK;
280 }
281
282 err = res_get_hwcounters(rd, hwc_table, false);
283 if (err != MNL_CB_OK)
284 return err;
285 open_json_object(NULL);
286 print_link(rd, index, name, port, nla_line);
287 print_color_uint(PRINT_ANY, COLOR_NONE, "cntn", "cntn %u ", cntn);
288 if (nla_line[RDMA_NLDEV_ATTR_RES_TYPE])
289 print_qp_type(rd, qp_type);
290 res_print_u64(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]);
291 print_comm(rd, comm, nla_line);
292 res_get_hwcounters(rd, hwc_table, true);
293 isfirst = true;
294 open_json_array(PRINT_JSON, "lqpn");
295 print_color_string(PRINT_FP, COLOR_NONE, NULL, "\n LQPN: <", NULL);
296 mnl_attr_for_each_nested(nla_entry, qp_table) {
297 struct nlattr *qp_line[RDMA_NLDEV_ATTR_MAX] = {};
298 err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, qp_line);
299 if (err != MNL_CB_OK)
300 return -EINVAL;
301
302 if (!qp_line[RDMA_NLDEV_ATTR_RES_LQPN])
303 return -EINVAL;
304
305 qpn = mnl_attr_get_u32(qp_line[RDMA_NLDEV_ATTR_RES_LQPN]);
306 if (!isfirst)
307 print_color_string(PRINT_FP, COLOR_NONE, NULL, ",",
308 NULL);
309 print_color_uint(PRINT_ANY, COLOR_NONE, NULL, "%d", qpn);
310 isfirst = false;
311 }
312 close_json_array(PRINT_ANY, ">");
313 newline(rd);
314 return MNL_CB_OK;
315}
316
317static int stat_qp_show_parse_cb(const struct nlmsghdr *nlh, void *data)
318{
319 struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
320 struct nlattr *nla_table, *nla_entry;
321 struct rd *rd = data;
322 const char *name;
323 uint32_t idx;
324 int ret = MNL_CB_OK;
325
326 mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
327 if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME] ||
328 !tb[RDMA_NLDEV_ATTR_STAT_COUNTER])
329 return MNL_CB_ERROR;
330
331 name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]);
332 idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
333 nla_table = tb[RDMA_NLDEV_ATTR_STAT_COUNTER];
334
335 mnl_attr_for_each_nested(nla_entry, nla_table) {
336 struct nlattr *nla_line[RDMA_NLDEV_ATTR_MAX] = {};
337
338 ret = mnl_attr_parse_nested(nla_entry, rd_attr_cb, nla_line);
339 if (ret != MNL_CB_OK)
340 break;
341
342 ret = res_counter_line(rd, name, idx, nla_line);
343 if (ret != MNL_CB_OK)
344 break;
345 }
346
347 return ret;
348}
349
350static const struct filters stat_valid_filters[MAX_NUMBER_OF_FILTERS] = {
351 { .name = "cntn", .is_number = true },
352 { .name = "lqpn", .is_number = true },
353 { .name = "pid", .is_number = true },
354 { .name = "qp-type", .is_number = false },
355};
356
357static int stat_qp_show_one_link(struct rd *rd)
358{
359 int flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP;
360 uint32_t seq;
361 int ret;
362
363 if (!rd->port_idx)
364 return 0;
365
366 ret = rd_build_filter(rd, stat_valid_filters);
367 if (ret)
368 return ret;
369
370 rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq, flags);
371 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
372 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
373 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
374 ret = rd_send_msg(rd);
375 if (ret)
376 return ret;
377
378 ret = rd_recv_msg(rd, stat_qp_show_parse_cb, rd, seq);
379 return ret;
380}
381
382static int stat_qp_show_link(struct rd *rd)
383{
384 return rd_exec_link(rd, stat_qp_show_one_link, false);
385}
386
387static int stat_qp_show(struct rd *rd)
388{
389 const struct rd_cmd cmds[] = {
390 { NULL, stat_qp_show_link },
391 { "link", stat_qp_show_link },
392 { "help", stat_help },
393 { 0 }
394 };
395
396 return rd_exec_cmd(rd, cmds, "parameter");
397}
398
399static int stat_qp_set_link_auto_sendmsg(struct rd *rd, uint32_t mask)
400{
401 uint32_t seq;
402
403 rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET,
404 &seq, (NLM_F_REQUEST | NLM_F_ACK));
405
406 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
407 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
408 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
409 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
410 RDMA_COUNTER_MODE_AUTO);
411 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK, mask);
412
413 return rd_sendrecv_msg(rd, seq);
414}
415
416static int stat_get_auto_mode_mask(struct rd *rd)
417{
418 char *modes = rd_argv(rd), *mode, *saved_ptr;
419 const char *delim = ",";
420 int mask = 0, found, i;
421
422 if (!modes)
423 return mask;
424
425 mode = strtok_r(modes, delim, &saved_ptr);
426 do {
427 if (!mode)
428 break;
429
430 found = false;
431 for (i = 0; auto_params[i].name != NULL; i++) {
432 if (!strcmp(mode, auto_params[i].name)) {
433 mask |= auto_params[i].attr;
434 found = true;
435 break;
436 }
437 }
438
439 if (!found) {
440 pr_err("Unknown auto mode '%s'.\n", mode);
441 mask = 0;
442 break;
443 }
444
445 mode = strtok_r(NULL, delim, &saved_ptr);
446 } while(1);
447
448 if (mask)
449 rd_arg_inc(rd);
450
451 return mask;
452}
453
454static int stat_one_qp_set_link_auto(struct rd *rd)
455{
456 int auto_mask = 0;
457
458 if (!rd_argc(rd))
459 return -EINVAL;
460
461 if (!strcmpx(rd_argv(rd), "off")) {
462 rd_arg_inc(rd);
463 return stat_qp_set_link_auto_sendmsg(rd, 0);
464 }
465
466 auto_mask = stat_get_auto_mode_mask(rd);
467 if (!auto_mask || !rd_argc(rd))
468 return -EINVAL;
469
470 if (!strcmpx(rd_argv(rd), "on")) {
471 rd_arg_inc(rd);
472 return stat_qp_set_link_auto_sendmsg(rd, auto_mask);
473 } else {
474 pr_err("Unknown parameter '%s'.\n", rd_argv(rd));
475 return -EINVAL;
476 }
477}
478
479static int stat_one_qp_set_link(struct rd *rd)
480{
481 const struct rd_cmd cmds[] = {
482 { NULL, stat_one_qp_link_get_mode },
483 { "auto", stat_one_qp_set_link_auto },
484 { 0 }
485 };
486
487 if (!rd->port_idx)
488 return 0;
489
490 return rd_exec_cmd(rd, cmds, "parameter");
491}
492
493static int stat_qp_set_link(struct rd *rd)
494{
495 return rd_exec_link(rd, stat_one_qp_set_link, false);
496}
497
498static int stat_qp_set(struct rd *rd)
499{
500 const struct rd_cmd cmds[] = {
501 { NULL, stat_help },
502 { "link", stat_qp_set_link },
503 { "help", stat_help },
504 { 0 }
505 };
506
507 return rd_exec_cmd(rd, cmds, "parameter");
508}
509
510static int stat_get_arg_str(struct rd *rd, const char *arg, char **value, bool allow_empty)
511{
512 int len = 0;
513
514 if (strcmpx(rd_argv(rd), arg) != 0) {
515 pr_err("Unknown parameter '%s'.\n", rd_argv(rd));
516 return -EINVAL;
517 }
518
519 rd_arg_inc(rd);
520 if (!rd_no_arg(rd)) {
521 *value = strdup(rd_argv(rd));
522 len = strlen(*value);
523 rd_arg_inc(rd);
524 }
525
526 if ((allow_empty && len) || (!allow_empty && !len)) {
527 stat_help(rd);
528 return -EINVAL;
529 }
530
531 return 0;
532}
533
534static int stat_get_arg(struct rd *rd, const char *arg)
535{
536 int value = 0;
537 char *endp;
538
539 if (strcmpx(rd_argv(rd), arg) != 0)
540 return -EINVAL;
541
542 rd_arg_inc(rd);
543
544 if (rd_is_multiarg(rd)) {
545 pr_err("The parameter %s shouldn't include range\n", arg);
546 return -EINVAL;
547 }
548
549 value = strtol(rd_argv(rd), &endp, 10);
550 rd_arg_inc(rd);
551
552 return value;
553}
554
555static int stat_one_qp_bind(struct rd *rd)
556{
557 int lqpn = 0, cntn = 0, ret;
558 uint32_t seq;
559
560 if (rd_no_arg(rd)) {
561 stat_help(rd);
562 return -EINVAL;
563 }
564
565 ret = rd_build_filter(rd, stat_valid_filters);
566 if (ret)
567 return ret;
568
569 lqpn = stat_get_arg(rd, "lqpn");
570 if (lqpn < 0)
571 return lqpn;
572
573 rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET,
574 &seq, (NLM_F_REQUEST | NLM_F_ACK));
575
576 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
577 RDMA_COUNTER_MODE_MANUAL);
578
579 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
580 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
581 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
582 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_RES_LQPN, lqpn);
583
584 if (rd_argc(rd)) {
585 cntn = stat_get_arg(rd, "cntn");
586 if (cntn < 0)
587 return cntn;
588
589 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID,
590 cntn);
591 }
592
593 return rd_sendrecv_msg(rd, seq);
594}
595
596static int do_stat_qp_unbind_lqpn(struct rd *rd, uint32_t cntn, uint32_t lqpn)
597{
598 uint32_t seq;
599
600 rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_DEL,
601 &seq, (NLM_F_REQUEST | NLM_F_ACK));
602
603 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
604 RDMA_COUNTER_MODE_MANUAL);
605 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
606 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
607 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
608 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID, cntn);
609 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_RES_LQPN, lqpn);
610
611 return rd_sendrecv_msg(rd, seq);
612}
613
614static int stat_get_counter_parse_cb(const struct nlmsghdr *nlh, void *data)
615{
616 struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
617 struct nlattr *nla_table, *nla_entry;
618 struct rd *rd = data;
619 uint32_t lqpn, cntn;
620 int err;
621
622 mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
623
624 if (!tb[RDMA_NLDEV_ATTR_STAT_COUNTER_ID])
625 return MNL_CB_ERROR;
626 cntn = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]);
627
628 nla_table = tb[RDMA_NLDEV_ATTR_RES_QP];
629 if (!nla_table)
630 return MNL_CB_ERROR;
631
632 mnl_attr_for_each_nested(nla_entry, nla_table) {
633 struct nlattr *nla_line[RDMA_NLDEV_ATTR_MAX] = {};
634
635 err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, nla_line);
636 if (err != MNL_CB_OK)
637 return -EINVAL;
638
639 if (!nla_line[RDMA_NLDEV_ATTR_RES_LQPN])
640 return -EINVAL;
641
642 lqpn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_LQPN]);
643 err = do_stat_qp_unbind_lqpn(rd, cntn, lqpn);
644 if (err)
645 return MNL_CB_ERROR;
646 }
647
648 return MNL_CB_OK;
649}
650
651static int stat_one_qp_unbind(struct rd *rd)
652{
653 int flags = NLM_F_REQUEST | NLM_F_ACK, ret;
654 char buf[MNL_SOCKET_BUFFER_SIZE];
655 int lqpn = 0, cntn = 0;
656 unsigned int portid;
657 uint32_t seq;
658
659 if (rd_no_arg(rd)) {
660 stat_help(rd);
661 return -EINVAL;
662 }
663
664 ret = rd_build_filter(rd, stat_valid_filters);
665 if (ret)
666 return ret;
667
668 cntn = stat_get_arg(rd, "cntn");
669 if (cntn < 0)
670 return cntn;
671
672 if (rd_argc(rd)) {
673 lqpn = stat_get_arg(rd, "lqpn");
674 if (lqpn < 0)
675 return lqpn;
676 return do_stat_qp_unbind_lqpn(rd, cntn, lqpn);
677 }
678
679 rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq, flags);
680 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
681 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
682 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
683 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID, cntn);
684 ret = rd_send_msg(rd);
685 if (ret)
686 return ret;
687
688
689
690
691
692 portid = mnl_socket_get_portid(rd->nl);
693 ret = mnl_socket_recvfrom(rd->nl, buf, sizeof(buf));
694 if (ret <= 0)
695 return ret;
696
697 ret = mnl_cb_run(buf, ret, seq, portid, stat_get_counter_parse_cb, rd);
698 mnl_socket_close(rd->nl);
699 if (ret != MNL_CB_OK)
700 return ret;
701
702 return 0;
703}
704
705static int stat_qp_bind_link(struct rd *rd)
706{
707 return rd_exec_link(rd, stat_one_qp_bind, true);
708}
709
710static int stat_qp_bind(struct rd *rd)
711{
712 const struct rd_cmd cmds[] = {
713 { NULL, stat_help },
714 { "link", stat_qp_bind_link },
715 { "help", stat_help },
716 { 0 },
717 };
718
719 return rd_exec_cmd(rd, cmds, "parameter");
720}
721
722static int stat_qp_unbind_link(struct rd *rd)
723{
724 return rd_exec_link(rd, stat_one_qp_unbind, true);
725}
726
727static int stat_qp_unbind(struct rd *rd)
728{
729 const struct rd_cmd cmds[] = {
730 { NULL, stat_help },
731 { "link", stat_qp_unbind_link },
732 { "help", stat_help },
733 { 0 },
734 };
735
736 return rd_exec_cmd(rd, cmds, "parameter");
737}
738
739static int stat_qp(struct rd *rd)
740{
741 const struct rd_cmd cmds[] = {
742 { NULL, stat_qp_show },
743 { "show", stat_qp_show },
744 { "list", stat_qp_show },
745 { "mode", stat_qp_get_mode },
746 { "set", stat_qp_set },
747 { "bind", stat_qp_bind },
748 { "unbind", stat_qp_unbind },
749 { "help", stat_help },
750 { 0 }
751 };
752
753 return rd_exec_cmd(rd, cmds, "parameter");
754}
755
756static int do_stat_mode_parse_cb(const struct nlmsghdr *nlh, void *data,
757 bool supported)
758{
759 struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
760 struct nlattr *nla_entry;
761 const char *dev, *name;
762 struct rd *rd = data;
763 int enabled, err = 0;
764 bool isfirst = true;
765 uint32_t port;
766
767 mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
768 if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME] ||
769 !tb[RDMA_NLDEV_ATTR_PORT_INDEX] ||
770 !tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS])
771 return MNL_CB_ERROR;
772
773 dev = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]);
774 port = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
775
776 mnl_attr_for_each_nested(nla_entry,
777 tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS]) {
778 struct nlattr *cnt[RDMA_NLDEV_ATTR_MAX] = {};
779
780 err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, cnt);
781 if ((err != MNL_CB_OK) ||
782 (!cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]))
783 return -EINVAL;
784
785 if (!cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC])
786 continue;
787
788 enabled = mnl_attr_get_u8(cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC]);
789 name = mnl_attr_get_str(cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]);
790 if (supported || enabled) {
791 if (isfirst) {
792 open_json_object(NULL);
793 print_color_string(PRINT_ANY, COLOR_NONE,
794 "ifname", "link %s/", dev);
795 print_color_uint(PRINT_ANY, COLOR_NONE, "port",
796 "%u ", port);
797 if (supported)
798 open_json_array(PRINT_ANY,
799 "supported optional-counters");
800 else
801 open_json_array(PRINT_ANY,
802 "optional-counters");
803 print_color_string(PRINT_FP, COLOR_NONE, NULL,
804 " ", NULL);
805 isfirst = false;
806 } else {
807 print_color_string(PRINT_FP, COLOR_NONE, NULL,
808 ",", NULL);
809 }
810 if (rd->pretty_output && !rd->json_output)
811 newline_indent(rd);
812
813 print_color_string(PRINT_ANY, COLOR_NONE, NULL, "%s",
814 name);
815 }
816 }
817
818 if (!isfirst) {
819 close_json_array(PRINT_JSON, NULL);
820 newline(rd);
821 }
822
823 return 0;
824}
825
826static int stat_mode_parse_cb(const struct nlmsghdr *nlh, void *data)
827{
828 return do_stat_mode_parse_cb(nlh, data, false);
829}
830
831static int stat_mode_parse_cb_supported(const struct nlmsghdr *nlh, void *data)
832{
833 return do_stat_mode_parse_cb(nlh, data, true);
834}
835
836static int stat_one_link_get_status_req(struct rd *rd, uint32_t *seq)
837{
838 int flags = NLM_F_REQUEST | NLM_F_ACK;
839
840 rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET_STATUS, seq, flags);
841 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
842 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
843
844 return rd_send_msg(rd);
845}
846
847static int stat_one_link_get_mode(struct rd *rd)
848{
849 uint32_t seq;
850 int err;
851
852 if (!rd->port_idx)
853 return 0;
854
855 err = stat_one_link_get_status_req(rd, &seq);
856 if (err)
857 return err;
858
859 return rd_recv_msg(rd, stat_mode_parse_cb, rd, seq);
860}
861
862static int stat_one_link_get_mode_supported(struct rd *rd)
863{
864 uint32_t seq;
865 int err;
866
867 if (!rd->port_idx)
868 return 0;
869
870 err = stat_one_link_get_status_req(rd, &seq);
871 if (err)
872 return err;
873
874 return rd_recv_msg(rd, stat_mode_parse_cb_supported, rd, seq);
875}
876
877static int stat_link_get_mode(struct rd *rd)
878{
879 return rd_exec_link(rd, stat_one_link_get_mode, false);
880}
881
882static int stat_link_get_mode_supported(struct rd *rd)
883{
884 return rd_exec_link(rd, stat_one_link_get_mode_supported, false);
885}
886
887static int stat_mode_supported(struct rd *rd)
888{
889 const struct rd_cmd cmds[] = {
890 { NULL, stat_link_get_mode_supported },
891 { "link", stat_link_get_mode_supported },
892 { "help", stat_help },
893 { 0 },
894 };
895 return rd_exec_cmd(rd, cmds, "parameter");
896}
897
898static int stat_mode(struct rd *rd)
899{
900 const struct rd_cmd cmds[] = {
901 { NULL, stat_link_get_mode },
902 { "link", stat_link_get_mode },
903 { "show", stat_link_get_mode },
904 { "supported", stat_mode_supported },
905 { "help", stat_help },
906 { 0 },
907 };
908
909 return rd_exec_cmd(rd, cmds, "parameter");
910}
911
912static int stat_one_set_link_opcounters(const struct nlmsghdr *nlh, void *data)
913{
914 struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
915 struct nlattr *nla_entry, *tb_set;
916 int ret, flags = NLM_F_REQUEST | NLM_F_ACK;
917 char *opcnt, *opcnts;
918 struct rd *rd = data;
919 uint32_t seq;
920 bool found;
921
922 mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
923 if (!tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS])
924 return MNL_CB_ERROR;
925
926 if (rd_no_arg(rd)) {
927 stat_help(rd);
928 return -EINVAL;
929 }
930
931 ret = stat_get_arg_str(rd, "optional-counters", &opcnts, false);
932 if (ret)
933 return ret;
934
935 rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET, &seq, flags);
936 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX,
937 rd->dev_idx);
938 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX,
939 rd->port_idx);
940
941 tb_set = mnl_attr_nest_start(rd->nlh, RDMA_NLDEV_ATTR_STAT_HWCOUNTERS);
942
943 opcnt = strtok(opcnts, ",");
944 while (opcnt) {
945 found = false;
946 mnl_attr_for_each_nested(nla_entry,
947 tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS]) {
948 struct nlattr *cnt[RDMA_NLDEV_ATTR_MAX] = {}, *nm, *id;
949
950 if (mnl_attr_parse_nested(nla_entry, rd_attr_cb,
951 cnt) != MNL_CB_OK)
952 return -EINVAL;
953
954 nm = cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME];
955 id = cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_INDEX];
956 if (!nm || ! id)
957 return -EINVAL;
958
959 if (!cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC])
960 continue;
961
962 if (strcmp(opcnt, mnl_attr_get_str(nm)) == 0) {
963 mnl_attr_put_u32(rd->nlh,
964 RDMA_NLDEV_ATTR_STAT_HWCOUNTER_INDEX,
965 mnl_attr_get_u32(id));
966 found = true;
967 }
968 }
969
970 if (!found)
971 return -EINVAL;
972
973 opcnt = strtok(NULL, ",");
974 }
975 mnl_attr_nest_end(rd->nlh, tb_set);
976
977 return rd_sendrecv_msg(rd, seq);
978}
979
980static int stat_one_set_link(struct rd *rd)
981{
982 uint32_t seq;
983 int err;
984
985 if (!rd->port_idx)
986 return 0;
987
988 err = stat_one_link_get_status_req(rd, &seq);
989 if (err)
990 return err;
991
992 return rd_recv_msg(rd, stat_one_set_link_opcounters, rd, seq);
993}
994
995static int stat_set_link(struct rd *rd)
996{
997 return rd_exec_link(rd, stat_one_set_link, true);
998}
999
1000static int stat_set(struct rd *rd)
1001{
1002 const struct rd_cmd cmds[] = {
1003 { NULL, stat_help },
1004 { "link", stat_set_link },
1005 { "help", stat_help },
1006 { 0 },
1007 };
1008 return rd_exec_cmd(rd, cmds, "parameter");
1009}
1010
1011static int stat_one_unset_link_opcounters(struct rd *rd)
1012{
1013 int ret, flags = NLM_F_REQUEST | NLM_F_ACK;
1014 struct nlattr *tbl;
1015 uint32_t seq;
1016 char *opcnts;
1017
1018 if (rd_no_arg(rd)) {
1019 stat_help(rd);
1020 return -EINVAL;
1021 }
1022
1023 ret = stat_get_arg_str(rd, "optional-counters", &opcnts, true);
1024 if (ret)
1025 return ret;
1026
1027 rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET, &seq, flags);
1028 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX,
1029 rd->dev_idx);
1030 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX,
1031 rd->port_idx);
1032
1033 tbl = mnl_attr_nest_start(rd->nlh, RDMA_NLDEV_ATTR_STAT_HWCOUNTERS);
1034 mnl_attr_nest_end(rd->nlh, tbl);
1035
1036 return rd_sendrecv_msg(rd, seq);
1037}
1038
1039static int stat_one_unset_link(struct rd *rd)
1040{
1041 return stat_one_unset_link_opcounters(rd);
1042}
1043
1044static int stat_unset_link(struct rd *rd)
1045{
1046 return rd_exec_link(rd, stat_one_unset_link, true);
1047}
1048
1049static int stat_unset(struct rd *rd)
1050{
1051 const struct rd_cmd cmds[] = {
1052 { NULL, stat_help },
1053 { "link", stat_unset_link },
1054 { "help", stat_help },
1055 { 0 },
1056 };
1057 return rd_exec_cmd(rd, cmds, "parameter");
1058}
1059
1060static int stat_show_parse_cb(const struct nlmsghdr *nlh, void *data)
1061{
1062 struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
1063 struct rd *rd = data;
1064 const char *name;
1065 uint32_t port;
1066 int ret;
1067
1068 mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
1069 if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME] ||
1070 !tb[RDMA_NLDEV_ATTR_PORT_INDEX] ||
1071 !tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS])
1072 return MNL_CB_ERROR;
1073
1074 name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]);
1075 port = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
1076 open_json_object(NULL);
1077 print_color_string(PRINT_ANY, COLOR_NONE, "ifname", "link %s/", name);
1078 print_color_uint(PRINT_ANY, COLOR_NONE, "port", "%u ", port);
1079 ret = res_get_hwcounters(rd, tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS], true);
1080
1081 newline(rd);
1082 return ret;
1083}
1084
1085static int stat_show_one_link(struct rd *rd)
1086{
1087 int flags = NLM_F_REQUEST | NLM_F_ACK;
1088 uint32_t seq;
1089 int ret;
1090
1091 if (!rd->port_idx)
1092 return 0;
1093
1094 rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq, flags);
1095 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
1096 mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
1097 ret = rd_send_msg(rd);
1098 if (ret)
1099 return ret;
1100
1101 return rd_recv_msg(rd, stat_show_parse_cb, rd, seq);
1102}
1103
1104static int stat_show_link(struct rd *rd)
1105{
1106 return rd_exec_link(rd, stat_show_one_link, false);
1107}
1108
1109static int stat_show(struct rd *rd)
1110{
1111 const struct rd_cmd cmds[] = {
1112 { NULL, stat_show_link },
1113 { "link", stat_show_link },
1114 { "mr", stat_mr },
1115 { "help", stat_help },
1116 { 0 }
1117 };
1118
1119 return rd_exec_cmd(rd, cmds, "parameter");
1120}
1121
1122int cmd_stat(struct rd *rd)
1123{
1124 const struct rd_cmd cmds[] = {
1125 { NULL, stat_show },
1126 { "show", stat_show },
1127 { "list", stat_show },
1128 { "help", stat_help },
1129 { "qp", stat_qp },
1130 { "mr", stat_mr },
1131 { "mode", stat_mode },
1132 { "set", stat_set },
1133 { "unset", stat_unset },
1134 { 0 }
1135 };
1136
1137 return rd_exec_cmd(rd, cmds, "statistic command");
1138}
1139