]>
Commit | Line | Data |
---|---|---|
5937552b MZ |
1 | // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
2 | /* | |
3 | * rdma.c RDMA tool | |
4 | * Authors: Mark Zhang <markz@mellanox.com> | |
5 | */ | |
6 | ||
7 | #include "rdma.h" | |
8 | #include "res.h" | |
9 | #include <inttypes.h> | |
10 | ||
11 | static int stat_help(struct rd *rd) | |
12 | { | |
13 | pr_out("Usage: %s [ OPTIONS ] statistic { COMMAND | help }\n", rd->filename); | |
14 | pr_out(" %s statistic OBJECT show\n", rd->filename); | |
15 | pr_out(" %s statistic OBJECT show link [ DEV/PORT_INDEX ] [ FILTER-NAME FILTER-VALUE ]\n", rd->filename); | |
1b2ca7ad | 16 | pr_out(" %s statistic OBJECT mode\n", rd->filename); |
887fc739 | 17 | pr_out(" %s statistic OBJECT set COUNTER_SCOPE [DEV/PORT_INDEX] auto {CRITERIA | off}\n", rd->filename); |
1b2ca7ad | 18 | pr_out("where OBJECT: = { qp }\n"); |
887fc739 MZ |
19 | pr_out(" CRITERIA : = { type }\n"); |
20 | pr_out(" COUNTER_SCOPE: = { link | dev }\n"); | |
5937552b MZ |
21 | pr_out("Examples:\n"); |
22 | pr_out(" %s statistic qp show\n", rd->filename); | |
23 | pr_out(" %s statistic qp show link mlx5_2/1\n", rd->filename); | |
1b2ca7ad MZ |
24 | pr_out(" %s statistic qp mode\n", rd->filename); |
25 | pr_out(" %s statistic qp mode link mlx5_0\n", rd->filename); | |
887fc739 MZ |
26 | pr_out(" %s statistic qp set link mlx5_2/1 auto type on\n", rd->filename); |
27 | pr_out(" %s statistic qp set link mlx5_2/1 auto off\n", rd->filename); | |
5937552b MZ |
28 | |
29 | return 0; | |
30 | } | |
31 | ||
1b2ca7ad MZ |
32 | struct counter_param { |
33 | char *name; | |
34 | uint32_t attr; | |
35 | }; | |
36 | ||
37 | static struct counter_param auto_params[] = { | |
38 | { "type", RDMA_COUNTER_MASK_QP_TYPE, }, | |
39 | { NULL }, | |
40 | }; | |
41 | ||
42 | static int prepare_auto_mode_str(struct nlattr **tb, uint32_t mask, | |
43 | char *output, int len) | |
44 | { | |
45 | char s[] = "qp auto"; | |
46 | int i, outlen = strlen(s); | |
47 | ||
48 | memset(output, 0, len); | |
49 | snprintf(output, len, "%s", s); | |
50 | ||
51 | if (mask) { | |
52 | for (i = 0; auto_params[i].name != NULL; i++) { | |
53 | if (mask & auto_params[i].attr) { | |
54 | outlen += strlen(auto_params[i].name) + 1; | |
55 | if (outlen >= len) | |
56 | return -EINVAL; | |
57 | strcat(output, " "); | |
58 | strcat(output, auto_params[i].name); | |
59 | } | |
60 | } | |
61 | ||
62 | if (outlen + strlen(" on") >= len) | |
63 | return -EINVAL; | |
64 | strcat(output, " on"); | |
65 | } else { | |
66 | if (outlen + strlen(" off") >= len) | |
67 | return -EINVAL; | |
68 | strcat(output, " off"); | |
69 | } | |
70 | ||
71 | return 0; | |
72 | } | |
73 | ||
74 | static int qp_link_get_mode_parse_cb(const struct nlmsghdr *nlh, void *data) | |
75 | { | |
76 | struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; | |
77 | uint32_t mode = 0, mask = 0; | |
78 | char output[128] = {}; | |
79 | struct rd *rd = data; | |
80 | uint32_t idx, port; | |
81 | const char *name; | |
82 | ||
83 | mnl_attr_parse(nlh, 0, rd_attr_cb, tb); | |
84 | if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME]) | |
85 | return MNL_CB_ERROR; | |
86 | ||
87 | if (!tb[RDMA_NLDEV_ATTR_PORT_INDEX]) { | |
88 | pr_err("This tool doesn't support switches yet\n"); | |
89 | return MNL_CB_ERROR; | |
90 | } | |
91 | ||
92 | idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); | |
93 | port = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]); | |
94 | name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); | |
95 | if (tb[RDMA_NLDEV_ATTR_STAT_MODE]) | |
96 | mode = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_STAT_MODE]); | |
97 | ||
98 | if (mode == RDMA_COUNTER_MODE_AUTO) { | |
99 | if (!tb[RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK]) | |
100 | return MNL_CB_ERROR; | |
101 | mask = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK]); | |
102 | prepare_auto_mode_str(tb, mask, output, sizeof(output)); | |
103 | } else { | |
104 | snprintf(output, sizeof(output), "qp auto off"); | |
105 | } | |
106 | ||
107 | if (rd->json_output) { | |
108 | jsonw_uint_field(rd->jw, "ifindex", idx); | |
109 | jsonw_uint_field(rd->jw, "port", port); | |
110 | jsonw_string_field(rd->jw, "mode", output); | |
111 | } else { | |
112 | pr_out("%u/%u: %s/%u: %s\n", idx, port, name, port, output); | |
113 | } | |
114 | ||
115 | return MNL_CB_OK; | |
116 | } | |
117 | ||
118 | static int stat_one_qp_link_get_mode(struct rd *rd) | |
119 | { | |
120 | uint32_t seq; | |
121 | int ret; | |
122 | ||
123 | if (!rd->port_idx) | |
124 | return 0; | |
125 | ||
126 | rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, | |
127 | &seq, (NLM_F_REQUEST | NLM_F_ACK)); | |
128 | ||
129 | mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx); | |
130 | mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx); | |
131 | /* Make RDMA_NLDEV_ATTR_STAT_MODE valid so that kernel knows | |
132 | * return only mode instead of all counters | |
133 | */ | |
134 | mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE, | |
135 | RDMA_COUNTER_MODE_MANUAL); | |
136 | mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP); | |
137 | ret = rd_send_msg(rd); | |
138 | if (ret) | |
139 | return ret; | |
140 | ||
141 | if (rd->json_output) | |
142 | jsonw_start_object(rd->jw); | |
143 | ret = rd_recv_msg(rd, qp_link_get_mode_parse_cb, rd, seq); | |
144 | if (rd->json_output) | |
145 | jsonw_end_object(rd->jw); | |
146 | ||
147 | return ret; | |
148 | } | |
149 | ||
150 | static int stat_qp_link_get_mode(struct rd *rd) | |
151 | { | |
152 | return rd_exec_link(rd, stat_one_qp_link_get_mode, false); | |
153 | } | |
154 | ||
155 | static int stat_qp_get_mode(struct rd *rd) | |
156 | { | |
157 | const struct rd_cmd cmds[] = { | |
158 | { NULL, stat_qp_link_get_mode }, | |
159 | { "link", stat_qp_link_get_mode }, | |
160 | { "help", stat_help }, | |
161 | { 0 } | |
162 | }; | |
163 | ||
164 | return rd_exec_cmd(rd, cmds, "parameter"); | |
165 | } | |
166 | ||
5937552b MZ |
167 | static int res_get_hwcounters(struct rd *rd, struct nlattr *hwc_table, bool print) |
168 | { | |
169 | struct nlattr *nla_entry; | |
170 | const char *nm; | |
171 | uint64_t v; | |
172 | int err; | |
173 | ||
174 | mnl_attr_for_each_nested(nla_entry, hwc_table) { | |
175 | struct nlattr *hw_line[RDMA_NLDEV_ATTR_MAX] = {}; | |
176 | ||
177 | err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, hw_line); | |
178 | if (err != MNL_CB_OK) | |
179 | return -EINVAL; | |
180 | ||
181 | if (!hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME] || | |
182 | !hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE]) { | |
183 | return -EINVAL; | |
184 | } | |
185 | ||
186 | if (!print) | |
187 | continue; | |
188 | ||
189 | nm = mnl_attr_get_str(hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]); | |
190 | v = mnl_attr_get_u64(hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE]); | |
191 | if (rd->pretty_output && !rd->json_output) | |
192 | newline_indent(rd); | |
193 | res_print_uint(rd, nm, v, hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]); | |
194 | } | |
195 | ||
196 | return MNL_CB_OK; | |
197 | } | |
198 | ||
199 | static int res_counter_line(struct rd *rd, const char *name, int index, | |
200 | struct nlattr **nla_line) | |
201 | { | |
202 | uint32_t cntn, port = 0, pid = 0, qpn; | |
203 | struct nlattr *hwc_table, *qp_table; | |
204 | struct nlattr *nla_entry; | |
205 | const char *comm = NULL; | |
206 | bool isfirst; | |
207 | int err; | |
208 | ||
209 | if (nla_line[RDMA_NLDEV_ATTR_PORT_INDEX]) | |
210 | port = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_PORT_INDEX]); | |
211 | ||
212 | hwc_table = nla_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS]; | |
213 | qp_table = nla_line[RDMA_NLDEV_ATTR_RES_QP]; | |
214 | if (!hwc_table || !qp_table || | |
215 | !nla_line[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]) | |
216 | return MNL_CB_ERROR; | |
217 | ||
218 | cntn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]); | |
219 | if (rd_is_filtered_attr(rd, "cntn", cntn, | |
220 | nla_line[RDMA_NLDEV_ATTR_STAT_COUNTER_ID])) | |
221 | return MNL_CB_OK; | |
222 | ||
223 | if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) { | |
224 | pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]); | |
225 | comm = get_task_name(pid); | |
226 | } | |
227 | if (rd_is_filtered_attr(rd, "pid", pid, | |
228 | nla_line[RDMA_NLDEV_ATTR_RES_PID])) | |
229 | return MNL_CB_OK; | |
230 | ||
231 | if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) | |
232 | comm = (char *)mnl_attr_get_str( | |
233 | nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]); | |
234 | ||
235 | mnl_attr_for_each_nested(nla_entry, qp_table) { | |
236 | struct nlattr *qp_line[RDMA_NLDEV_ATTR_MAX] = {}; | |
237 | ||
238 | err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, qp_line); | |
239 | if (err != MNL_CB_OK) | |
240 | return -EINVAL; | |
241 | ||
242 | if (!qp_line[RDMA_NLDEV_ATTR_RES_LQPN]) | |
243 | return -EINVAL; | |
244 | ||
245 | qpn = mnl_attr_get_u32(qp_line[RDMA_NLDEV_ATTR_RES_LQPN]); | |
246 | if (rd_is_filtered_attr(rd, "lqpn", qpn, | |
247 | qp_line[RDMA_NLDEV_ATTR_RES_LQPN])) | |
248 | return MNL_CB_OK; | |
249 | } | |
250 | ||
251 | err = res_get_hwcounters(rd, hwc_table, false); | |
252 | if (err != MNL_CB_OK) | |
253 | return err; | |
254 | ||
255 | if (rd->json_output) { | |
256 | jsonw_string_field(rd->jw, "ifname", name); | |
257 | if (port) | |
258 | jsonw_uint_field(rd->jw, "port", port); | |
259 | jsonw_uint_field(rd->jw, "cntn", cntn); | |
260 | } else { | |
261 | if (port) | |
262 | pr_out("link %s/%u cntn %u ", name, port, cntn); | |
263 | else | |
264 | pr_out("dev %s cntn %u ", name, cntn); | |
265 | } | |
266 | ||
267 | res_print_uint(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]); | |
268 | print_comm(rd, comm, nla_line); | |
269 | ||
270 | res_get_hwcounters(rd, hwc_table, true); | |
271 | ||
272 | isfirst = true; | |
273 | mnl_attr_for_each_nested(nla_entry, qp_table) { | |
274 | struct nlattr *qp_line[RDMA_NLDEV_ATTR_MAX] = {}; | |
275 | ||
276 | if (isfirst && !rd->json_output) | |
277 | pr_out("\n LQPN: <"); | |
278 | ||
279 | err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, qp_line); | |
280 | if (err != MNL_CB_OK) | |
281 | return -EINVAL; | |
282 | ||
283 | if (!qp_line[RDMA_NLDEV_ATTR_RES_LQPN]) | |
284 | return -EINVAL; | |
285 | ||
286 | qpn = mnl_attr_get_u32(qp_line[RDMA_NLDEV_ATTR_RES_LQPN]); | |
287 | if (rd->json_output) { | |
288 | jsonw_uint_field(rd->jw, "lqpn", qpn); | |
289 | } else { | |
290 | if (isfirst) | |
291 | pr_out("%d", qpn); | |
292 | else | |
293 | pr_out(", %d", qpn); | |
294 | } | |
295 | isfirst = false; | |
296 | } | |
297 | ||
298 | if (!rd->json_output) | |
299 | pr_out(">\n"); | |
300 | return MNL_CB_OK; | |
301 | } | |
302 | ||
303 | static int stat_qp_show_parse_cb(const struct nlmsghdr *nlh, void *data) | |
304 | { | |
305 | struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; | |
306 | struct nlattr *nla_table, *nla_entry; | |
307 | struct rd *rd = data; | |
308 | const char *name; | |
309 | uint32_t idx; | |
310 | int ret; | |
311 | ||
312 | mnl_attr_parse(nlh, 0, rd_attr_cb, tb); | |
313 | if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME] || | |
314 | !tb[RDMA_NLDEV_ATTR_STAT_COUNTER]) | |
315 | return MNL_CB_ERROR; | |
316 | ||
317 | name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); | |
318 | idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); | |
319 | nla_table = tb[RDMA_NLDEV_ATTR_STAT_COUNTER]; | |
320 | ||
321 | mnl_attr_for_each_nested(nla_entry, nla_table) { | |
322 | struct nlattr *nla_line[RDMA_NLDEV_ATTR_MAX] = {}; | |
323 | ||
324 | ret = mnl_attr_parse_nested(nla_entry, rd_attr_cb, nla_line); | |
325 | if (ret != MNL_CB_OK) | |
326 | break; | |
327 | ||
328 | ret = res_counter_line(rd, name, idx, nla_line); | |
329 | if (ret != MNL_CB_OK) | |
330 | break; | |
331 | } | |
332 | ||
333 | return ret; | |
334 | } | |
335 | ||
336 | static const struct filters stat_valid_filters[MAX_NUMBER_OF_FILTERS] = { | |
337 | { .name = "cntn", .is_number = true }, | |
338 | { .name = "lqpn", .is_number = true }, | |
339 | { .name = "pid", .is_number = true }, | |
340 | }; | |
341 | ||
342 | static int stat_qp_show_one_link(struct rd *rd) | |
343 | { | |
344 | int flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP; | |
345 | uint32_t seq; | |
346 | int ret; | |
347 | ||
348 | if (!rd->port_idx) | |
349 | return 0; | |
350 | ||
351 | ret = rd_build_filter(rd, stat_valid_filters); | |
352 | if (ret) | |
353 | return ret; | |
354 | ||
355 | rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq, flags); | |
356 | mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx); | |
357 | mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx); | |
358 | mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP); | |
359 | ret = rd_send_msg(rd); | |
360 | if (ret) | |
361 | return ret; | |
362 | ||
363 | if (rd->json_output) | |
364 | jsonw_start_object(rd->jw); | |
365 | ret = rd_recv_msg(rd, stat_qp_show_parse_cb, rd, seq); | |
366 | if (rd->json_output) | |
367 | jsonw_end_object(rd->jw); | |
368 | ||
369 | return ret; | |
370 | } | |
371 | ||
372 | static int stat_qp_show_link(struct rd *rd) | |
373 | { | |
374 | return rd_exec_link(rd, stat_qp_show_one_link, false); | |
375 | } | |
376 | ||
377 | static int stat_qp_show(struct rd *rd) | |
378 | { | |
379 | const struct rd_cmd cmds[] = { | |
380 | { NULL, stat_qp_show_link }, | |
381 | { "link", stat_qp_show_link }, | |
382 | { "help", stat_help }, | |
383 | { 0 } | |
384 | }; | |
385 | ||
386 | return rd_exec_cmd(rd, cmds, "parameter"); | |
387 | } | |
388 | ||
887fc739 MZ |
389 | static int stat_qp_set_link_auto_sendmsg(struct rd *rd, uint32_t mask) |
390 | { | |
391 | uint32_t seq; | |
392 | ||
393 | rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET, | |
394 | &seq, (NLM_F_REQUEST | NLM_F_ACK)); | |
395 | ||
396 | mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx); | |
397 | mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx); | |
398 | mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP); | |
399 | mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE, | |
400 | RDMA_COUNTER_MODE_AUTO); | |
401 | mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK, mask); | |
402 | ||
403 | return rd_sendrecv_msg(rd, seq); | |
404 | } | |
405 | ||
406 | static int stat_one_qp_set_link_auto_off(struct rd *rd) | |
407 | { | |
408 | return stat_qp_set_link_auto_sendmsg(rd, 0); | |
409 | } | |
410 | ||
411 | static int stat_one_qp_set_auto_type_on(struct rd *rd) | |
412 | { | |
413 | return stat_qp_set_link_auto_sendmsg(rd, RDMA_COUNTER_MASK_QP_TYPE); | |
414 | } | |
415 | ||
416 | static int stat_one_qp_set_link_auto_type(struct rd *rd) | |
417 | { | |
418 | const struct rd_cmd cmds[] = { | |
419 | { NULL, stat_help }, | |
420 | { "on", stat_one_qp_set_auto_type_on }, | |
421 | { 0 } | |
422 | }; | |
423 | ||
424 | return rd_exec_cmd(rd, cmds, "parameter"); | |
425 | } | |
426 | ||
427 | static int stat_one_qp_set_link_auto(struct rd *rd) | |
428 | { | |
429 | const struct rd_cmd cmds[] = { | |
430 | { NULL, stat_one_qp_link_get_mode }, | |
431 | { "off", stat_one_qp_set_link_auto_off }, | |
432 | { "type", stat_one_qp_set_link_auto_type }, | |
433 | { 0 } | |
434 | }; | |
435 | ||
436 | return rd_exec_cmd(rd, cmds, "parameter"); | |
437 | } | |
438 | ||
439 | static int stat_one_qp_set_link(struct rd *rd) | |
440 | { | |
441 | const struct rd_cmd cmds[] = { | |
442 | { NULL, stat_one_qp_link_get_mode }, | |
443 | { "auto", stat_one_qp_set_link_auto }, | |
444 | { 0 } | |
445 | }; | |
446 | ||
447 | if (!rd->port_idx) | |
448 | return 0; | |
449 | ||
450 | return rd_exec_cmd(rd, cmds, "parameter"); | |
451 | } | |
452 | ||
453 | static int stat_qp_set_link(struct rd *rd) | |
454 | { | |
455 | return rd_exec_link(rd, stat_one_qp_set_link, false); | |
456 | } | |
457 | ||
458 | static int stat_qp_set(struct rd *rd) | |
459 | { | |
460 | const struct rd_cmd cmds[] = { | |
461 | { NULL, stat_help }, | |
462 | { "link", stat_qp_set_link }, | |
463 | { "help", stat_help }, | |
464 | { 0 } | |
465 | }; | |
466 | ||
467 | return rd_exec_cmd(rd, cmds, "parameter"); | |
468 | } | |
469 | ||
5937552b MZ |
470 | static int stat_qp(struct rd *rd) |
471 | { | |
472 | const struct rd_cmd cmds[] = { | |
473 | { NULL, stat_qp_show }, | |
474 | { "show", stat_qp_show }, | |
475 | { "list", stat_qp_show }, | |
1b2ca7ad | 476 | { "mode", stat_qp_get_mode }, |
887fc739 | 477 | { "set", stat_qp_set }, |
5937552b MZ |
478 | { "help", stat_help }, |
479 | { 0 } | |
480 | }; | |
481 | ||
482 | return rd_exec_cmd(rd, cmds, "parameter"); | |
483 | } | |
484 | ||
485 | int cmd_stat(struct rd *rd) | |
486 | { | |
487 | const struct rd_cmd cmds[] = { | |
488 | { NULL, stat_help }, | |
489 | { "help", stat_help }, | |
490 | { "qp", stat_qp }, | |
491 | { 0 } | |
492 | }; | |
493 | ||
494 | return rd_exec_cmd(rd, cmds, "statistic command"); | |
495 | } |