]>
Commit | Line | Data |
---|---|---|
923aa825 LR |
1 | /* |
2 | * res.c RDMA tool | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU General Public License | |
6 | * as published by the Free Software Foundation; either version | |
7 | * 2 of the License, or (at your option) any later version. | |
8 | * | |
9 | * Authors: Leon Romanovsky <leonro@mellanox.com> | |
10 | */ | |
11 | ||
12 | #include "rdma.h" | |
13 | #include <inttypes.h> | |
14 | ||
15 | static int res_help(struct rd *rd) | |
16 | { | |
17 | pr_out("Usage: %s resource\n", rd->filename); | |
18 | pr_out(" resource show [DEV]\n"); | |
9a362cc7 | 19 | pr_out(" resource show [qp|cm_id]\n"); |
8ecac46a LR |
20 | pr_out(" resource show qp link [DEV/PORT]\n"); |
21 | pr_out(" resource show qp link [DEV/PORT] [FILTER-NAME FILTER-VALUE]\n"); | |
9a362cc7 SW |
22 | pr_out(" resource show cm_id link [DEV/PORT]\n"); |
23 | pr_out(" resource show cm_id link [DEV/PORT] [FILTER-NAME FILTER-VALUE]\n"); | |
b0b8e32c SW |
24 | pr_out(" resource show cq link [DEV/PORT]\n"); |
25 | pr_out(" resource show cq link [DEV/PORT] [FILTER-NAME FILTER-VALUE]\n"); | |
923aa825 LR |
26 | return 0; |
27 | } | |
28 | ||
29 | static int res_print_summary(struct rd *rd, struct nlattr **tb) | |
30 | { | |
31 | struct nlattr *nla_table = tb[RDMA_NLDEV_ATTR_RES_SUMMARY]; | |
32 | struct nlattr *nla_entry; | |
33 | const char *name; | |
34 | uint64_t curr; | |
35 | int err; | |
36 | ||
37 | mnl_attr_for_each_nested(nla_entry, nla_table) { | |
38 | struct nlattr *nla_line[RDMA_NLDEV_ATTR_MAX] = {}; | |
39 | char json_name[32]; | |
40 | ||
41 | err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, nla_line); | |
42 | if (err != MNL_CB_OK) | |
43 | return -EINVAL; | |
44 | ||
45 | if (!nla_line[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_NAME] || | |
46 | !nla_line[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR]) { | |
47 | return -EINVAL; | |
48 | } | |
49 | ||
50 | name = mnl_attr_get_str(nla_line[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_NAME]); | |
51 | curr = mnl_attr_get_u64(nla_line[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR]); | |
52 | if (rd->json_output) { | |
53 | snprintf(json_name, 32, "%s", name); | |
54 | jsonw_lluint_field(rd->jw, json_name, curr); | |
55 | } else { | |
56 | pr_out("%s %"PRId64 " ", name, curr); | |
57 | } | |
58 | } | |
59 | return 0; | |
60 | } | |
61 | ||
62 | static int res_no_args_parse_cb(const struct nlmsghdr *nlh, void *data) | |
63 | { | |
64 | struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; | |
65 | struct rd *rd = data; | |
66 | const char *name; | |
67 | uint32_t idx; | |
68 | ||
69 | mnl_attr_parse(nlh, 0, rd_attr_cb, tb); | |
70 | if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || | |
71 | !tb[RDMA_NLDEV_ATTR_DEV_NAME] || | |
72 | !tb[RDMA_NLDEV_ATTR_RES_SUMMARY]) | |
73 | return MNL_CB_ERROR; | |
74 | ||
75 | idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); | |
76 | name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); | |
77 | if (rd->json_output) { | |
78 | jsonw_uint_field(rd->jw, "ifindex", idx); | |
79 | jsonw_string_field(rd->jw, "ifname", name); | |
80 | } else { | |
81 | pr_out("%u: %s: ", idx, name); | |
82 | } | |
83 | ||
84 | res_print_summary(rd, tb); | |
85 | ||
86 | if (!rd->json_output) | |
87 | pr_out("\n"); | |
88 | return MNL_CB_OK; | |
89 | } | |
90 | ||
91 | static int _res_send_msg(struct rd *rd, uint32_t command, mnl_cb_t callback) | |
92 | { | |
93 | uint32_t flags = NLM_F_REQUEST | NLM_F_ACK; | |
94 | uint32_t seq; | |
95 | int ret; | |
96 | ||
97 | if (command != RDMA_NLDEV_CMD_RES_GET) | |
98 | flags |= NLM_F_DUMP; | |
99 | ||
100 | rd_prepare_msg(rd, command, &seq, flags); | |
101 | mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx); | |
102 | if (rd->port_idx) | |
103 | mnl_attr_put_u32(rd->nlh, | |
104 | RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx); | |
105 | ||
106 | ret = rd_send_msg(rd); | |
107 | if (ret) | |
108 | return ret; | |
109 | ||
110 | if (rd->json_output) | |
111 | jsonw_start_object(rd->jw); | |
112 | ret = rd_recv_msg(rd, callback, rd, seq); | |
113 | if (rd->json_output) | |
114 | jsonw_end_object(rd->jw); | |
115 | return ret; | |
116 | } | |
117 | ||
118 | #define RES_FUNC(name, command, valid_filters, strict_port) \ | |
119 | static int _##name(struct rd *rd)\ | |
120 | { \ | |
121 | return _res_send_msg(rd, command, name##_parse_cb); \ | |
122 | } \ | |
123 | static int name(struct rd *rd) \ | |
124 | {\ | |
125 | int ret = rd_build_filter(rd, valid_filters); \ | |
126 | if (ret) \ | |
127 | return ret; \ | |
128 | if ((uintptr_t)valid_filters != (uintptr_t)NULL) { \ | |
129 | ret = rd_set_arg_to_devname(rd); \ | |
130 | if (ret) \ | |
131 | return ret;\ | |
132 | } \ | |
133 | if (strict_port) \ | |
134 | return rd_exec_dev(rd, _##name); \ | |
135 | else \ | |
136 | return rd_exec_link(rd, _##name, strict_port); \ | |
137 | } | |
138 | ||
8ecac46a LR |
139 | static const char *path_mig_to_str(uint8_t idx) |
140 | { | |
141 | static const char * const path_mig_str[] = { "MIGRATED", | |
142 | "REARM", "ARMED" }; | |
143 | ||
144 | if (idx < ARRAY_SIZE(path_mig_str)) | |
145 | return path_mig_str[idx]; | |
146 | return "UNKNOWN"; | |
147 | } | |
148 | ||
149 | static const char *qp_states_to_str(uint8_t idx) | |
150 | { | |
151 | static const char * const qp_states_str[] = { "RESET", "INIT", | |
152 | "RTR", "RTS", "SQD", | |
153 | "SQE", "ERR" }; | |
154 | ||
155 | if (idx < ARRAY_SIZE(qp_states_str)) | |
156 | return qp_states_str[idx]; | |
157 | return "UNKNOWN"; | |
158 | } | |
159 | ||
160 | static const char *qp_types_to_str(uint8_t idx) | |
161 | { | |
162 | static const char * const qp_types_str[] = { "SMI", "GSI", "RC", | |
163 | "UC", "UD", "RAW_IPV6", | |
164 | "RAW_ETHERTYPE", | |
165 | "UNKNOWN", "RAW_PACKET", | |
166 | "XRC_INI", "XRC_TGT" }; | |
167 | ||
168 | if (idx < ARRAY_SIZE(qp_types_str)) | |
169 | return qp_types_str[idx]; | |
170 | return "UNKNOWN"; | |
171 | } | |
172 | ||
173 | static void print_lqpn(struct rd *rd, uint32_t val) | |
174 | { | |
175 | if (rd->json_output) | |
176 | jsonw_uint_field(rd->jw, "lqpn", val); | |
177 | else | |
178 | pr_out("lqpn %u ", val); | |
179 | } | |
180 | ||
181 | static void print_rqpn(struct rd *rd, uint32_t val, struct nlattr **nla_line) | |
182 | { | |
183 | if (!nla_line[RDMA_NLDEV_ATTR_RES_RQPN]) | |
184 | return; | |
185 | ||
186 | if (rd->json_output) | |
187 | jsonw_uint_field(rd->jw, "rqpn", val); | |
188 | else | |
189 | pr_out("rqpn %u ", val); | |
190 | } | |
191 | ||
192 | static void print_type(struct rd *rd, uint32_t val) | |
193 | { | |
194 | if (rd->json_output) | |
195 | jsonw_string_field(rd->jw, "type", | |
196 | qp_types_to_str(val)); | |
197 | else | |
198 | pr_out("type %s ", qp_types_to_str(val)); | |
199 | } | |
200 | ||
201 | static void print_state(struct rd *rd, uint32_t val) | |
202 | { | |
203 | if (rd->json_output) | |
204 | jsonw_string_field(rd->jw, "state", | |
205 | qp_states_to_str(val)); | |
206 | else | |
207 | pr_out("state %s ", qp_states_to_str(val)); | |
208 | } | |
209 | ||
210 | static void print_rqpsn(struct rd *rd, uint32_t val, struct nlattr **nla_line) | |
211 | { | |
212 | if (!nla_line[RDMA_NLDEV_ATTR_RES_RQ_PSN]) | |
213 | return; | |
214 | ||
215 | if (rd->json_output) | |
216 | jsonw_uint_field(rd->jw, "rq-psn", val); | |
217 | else | |
218 | pr_out("rq-psn %u ", val); | |
219 | } | |
220 | ||
221 | static void print_sqpsn(struct rd *rd, uint32_t val) | |
222 | { | |
223 | if (rd->json_output) | |
224 | jsonw_uint_field(rd->jw, "sq-psn", val); | |
225 | else | |
226 | pr_out("sq-psn %u ", val); | |
227 | } | |
228 | ||
229 | static void print_pathmig(struct rd *rd, uint32_t val, | |
230 | struct nlattr **nla_line) | |
231 | { | |
232 | if (!nla_line[RDMA_NLDEV_ATTR_RES_PATH_MIG_STATE]) | |
233 | return; | |
234 | ||
235 | if (rd->json_output) | |
236 | jsonw_string_field(rd->jw, | |
237 | "path-mig-state", | |
238 | path_mig_to_str(val)); | |
239 | else | |
240 | pr_out("path-mig-state %s ", path_mig_to_str(val)); | |
241 | } | |
242 | ||
243 | static void print_pid(struct rd *rd, uint32_t val) | |
244 | { | |
245 | if (rd->json_output) | |
246 | jsonw_uint_field(rd->jw, "pid", val); | |
247 | else | |
248 | pr_out("pid %u ", val); | |
249 | } | |
250 | ||
251 | static void print_comm(struct rd *rd, const char *str, | |
252 | struct nlattr **nla_line) | |
253 | { | |
254 | char tmp[18]; | |
255 | ||
256 | if (rd->json_output) { | |
257 | /* Don't beatify output in JSON format */ | |
258 | jsonw_string_field(rd->jw, "comm", str); | |
259 | return; | |
260 | } | |
261 | ||
262 | if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) | |
263 | snprintf(tmp, sizeof(tmp), "%s", str); | |
264 | else | |
265 | snprintf(tmp, sizeof(tmp), "[%s]", str); | |
266 | ||
267 | pr_out("comm %s ", tmp); | |
268 | } | |
269 | ||
b0b8e32c SW |
270 | static void print_dev(struct rd *rd, uint32_t idx, const char *name) |
271 | { | |
272 | if (rd->json_output) { | |
273 | jsonw_uint_field(rd->jw, "ifindex", idx); | |
274 | jsonw_string_field(rd->jw, "ifname", name); | |
275 | } else { | |
276 | pr_out("dev %s ", name); | |
277 | } | |
278 | } | |
279 | ||
8ecac46a LR |
280 | static void print_link(struct rd *rd, uint32_t idx, const char *name, |
281 | uint32_t port, struct nlattr **nla_line) | |
282 | { | |
283 | if (rd->json_output) { | |
284 | jsonw_uint_field(rd->jw, "ifindex", idx); | |
285 | ||
286 | if (nla_line[RDMA_NLDEV_ATTR_PORT_INDEX]) | |
287 | jsonw_uint_field(rd->jw, "port", port); | |
288 | ||
289 | jsonw_string_field(rd->jw, "ifname", name); | |
290 | } else { | |
291 | if (nla_line[RDMA_NLDEV_ATTR_PORT_INDEX]) | |
292 | pr_out("link %s/%u ", name, port); | |
293 | else | |
294 | pr_out("link %s/- ", name); | |
295 | } | |
296 | } | |
297 | ||
298 | static char *get_task_name(uint32_t pid) | |
299 | { | |
300 | char *comm; | |
301 | FILE *f; | |
302 | ||
303 | if (asprintf(&comm, "/proc/%d/comm", pid) < 0) | |
304 | return NULL; | |
305 | ||
306 | f = fopen(comm, "r"); | |
307 | free(comm); | |
308 | if (!f) | |
309 | return NULL; | |
310 | ||
311 | if (fscanf(f, "%ms\n", &comm) != 1) | |
312 | comm = NULL; | |
313 | ||
314 | fclose(f); | |
315 | ||
316 | return comm; | |
317 | } | |
318 | ||
319 | static int res_qp_parse_cb(const struct nlmsghdr *nlh, void *data) | |
320 | { | |
321 | struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; | |
322 | struct nlattr *nla_table, *nla_entry; | |
323 | struct rd *rd = data; | |
324 | const char *name; | |
325 | uint32_t idx; | |
326 | ||
327 | mnl_attr_parse(nlh, 0, rd_attr_cb, tb); | |
328 | if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || | |
329 | !tb[RDMA_NLDEV_ATTR_DEV_NAME] || | |
330 | !tb[RDMA_NLDEV_ATTR_RES_QP]) | |
331 | return MNL_CB_ERROR; | |
332 | ||
333 | name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); | |
334 | idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); | |
335 | nla_table = tb[RDMA_NLDEV_ATTR_RES_QP]; | |
336 | ||
337 | mnl_attr_for_each_nested(nla_entry, nla_table) { | |
338 | struct nlattr *nla_line[RDMA_NLDEV_ATTR_MAX] = {}; | |
339 | uint32_t lqpn, rqpn = 0, rq_psn = 0, sq_psn; | |
340 | uint8_t type, state, path_mig_state = 0; | |
341 | uint32_t port = 0, pid = 0; | |
342 | char *comm = NULL; | |
343 | int err; | |
344 | ||
345 | err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, nla_line); | |
346 | if (err != MNL_CB_OK) | |
347 | return MNL_CB_ERROR; | |
348 | ||
349 | if (!nla_line[RDMA_NLDEV_ATTR_RES_LQPN] || | |
350 | !nla_line[RDMA_NLDEV_ATTR_RES_SQ_PSN] || | |
351 | !nla_line[RDMA_NLDEV_ATTR_RES_TYPE] || | |
352 | !nla_line[RDMA_NLDEV_ATTR_RES_STATE] || | |
353 | (!nla_line[RDMA_NLDEV_ATTR_RES_PID] && | |
354 | !nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME])) { | |
355 | return MNL_CB_ERROR; | |
356 | } | |
357 | ||
358 | if (nla_line[RDMA_NLDEV_ATTR_PORT_INDEX]) | |
359 | port = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_PORT_INDEX]); | |
360 | ||
361 | if (port != rd->port_idx) | |
362 | continue; | |
363 | ||
364 | lqpn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_LQPN]); | |
365 | if (rd_check_is_filtered(rd, "lqpn", lqpn)) | |
366 | continue; | |
367 | ||
368 | if (nla_line[RDMA_NLDEV_ATTR_RES_RQPN]) { | |
369 | rqpn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_RQPN]); | |
370 | if (rd_check_is_filtered(rd, "rqpn", rqpn)) | |
371 | continue; | |
372 | } else { | |
373 | if (rd_check_is_key_exist(rd, "rqpn")) | |
374 | continue; | |
375 | } | |
376 | ||
377 | if (nla_line[RDMA_NLDEV_ATTR_RES_RQ_PSN]) { | |
378 | rq_psn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_RQ_PSN]); | |
379 | if (rd_check_is_filtered(rd, "rq-psn", rq_psn)) | |
380 | continue; | |
381 | } else { | |
382 | if (rd_check_is_key_exist(rd, "rq-psn")) | |
383 | continue; | |
384 | } | |
385 | ||
386 | sq_psn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_SQ_PSN]); | |
387 | if (rd_check_is_filtered(rd, "sq-psn", sq_psn)) | |
388 | continue; | |
389 | ||
390 | if (nla_line[RDMA_NLDEV_ATTR_RES_PATH_MIG_STATE]) { | |
391 | path_mig_state = mnl_attr_get_u8(nla_line[RDMA_NLDEV_ATTR_RES_PATH_MIG_STATE]); | |
392 | if (rd_check_is_string_filtered(rd, "path-mig-state", path_mig_to_str(path_mig_state))) | |
393 | continue; | |
394 | } else { | |
395 | if (rd_check_is_key_exist(rd, "path-mig-state")) | |
396 | continue; | |
397 | } | |
398 | ||
399 | type = mnl_attr_get_u8(nla_line[RDMA_NLDEV_ATTR_RES_TYPE]); | |
400 | if (rd_check_is_string_filtered(rd, "type", qp_types_to_str(type))) | |
401 | continue; | |
402 | ||
403 | state = mnl_attr_get_u8(nla_line[RDMA_NLDEV_ATTR_RES_STATE]); | |
404 | if (rd_check_is_string_filtered(rd, "state", qp_states_to_str(state))) | |
405 | continue; | |
406 | ||
407 | if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) { | |
408 | pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]); | |
409 | comm = get_task_name(pid); | |
410 | } | |
411 | ||
4ac152d0 LR |
412 | if (rd_check_is_filtered(rd, "pid", pid)) { |
413 | free(comm); | |
8ecac46a | 414 | continue; |
4ac152d0 | 415 | } |
8ecac46a LR |
416 | |
417 | if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) | |
418 | /* discard const from mnl_attr_get_str */ | |
419 | comm = (char *)mnl_attr_get_str(nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]); | |
420 | ||
421 | if (rd->json_output) | |
422 | jsonw_start_array(rd->jw); | |
423 | ||
424 | print_link(rd, idx, name, port, nla_line); | |
425 | ||
426 | print_lqpn(rd, lqpn); | |
427 | print_rqpn(rd, rqpn, nla_line); | |
428 | ||
429 | print_type(rd, type); | |
430 | print_state(rd, state); | |
431 | ||
432 | print_rqpsn(rd, rq_psn, nla_line); | |
433 | print_sqpsn(rd, sq_psn); | |
434 | ||
435 | print_pathmig(rd, path_mig_state, nla_line); | |
436 | print_pid(rd, pid); | |
437 | print_comm(rd, comm, nla_line); | |
438 | ||
439 | if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) | |
440 | free(comm); | |
441 | ||
442 | if (rd->json_output) | |
443 | jsonw_end_array(rd->jw); | |
444 | else | |
445 | pr_out("\n"); | |
446 | } | |
447 | return MNL_CB_OK; | |
448 | } | |
449 | ||
9a362cc7 SW |
450 | static void print_qp_type(struct rd *rd, uint32_t val) |
451 | { | |
452 | if (rd->json_output) | |
453 | jsonw_string_field(rd->jw, "qp-type", | |
454 | qp_types_to_str(val)); | |
455 | else | |
456 | pr_out("qp-type %s ", qp_types_to_str(val)); | |
457 | } | |
458 | ||
459 | static const char *cm_id_state_to_str(uint8_t idx) | |
460 | { | |
461 | static const char * const cm_id_states_str[] = { | |
462 | "IDLE", "ADDR_QUERY", "ADDR_RESOLVED", "ROUTE_QUERY", | |
463 | "ROUTE_RESOLVED", "CONNECT", "DISCONNECT", "ADDR_BOUND", | |
464 | "LISTEN", "DEVICE_REMOVAL", "DESTROYING" }; | |
465 | ||
466 | if (idx < ARRAY_SIZE(cm_id_states_str)) | |
467 | return cm_id_states_str[idx]; | |
468 | return "UNKNOWN"; | |
469 | } | |
470 | ||
471 | static const char *cm_id_ps_to_str(uint32_t ps) | |
472 | { | |
473 | switch (ps) { | |
474 | case RDMA_PS_IPOIB: | |
475 | return "IPoIB"; | |
476 | case RDMA_PS_IB: | |
477 | return "IPoIB"; | |
478 | case RDMA_PS_TCP: | |
479 | return "TCP"; | |
480 | case RDMA_PS_UDP: | |
481 | return "UDP"; | |
482 | default: | |
483 | return "---"; | |
484 | } | |
485 | } | |
486 | ||
487 | static void print_cm_id_state(struct rd *rd, uint8_t state) | |
488 | { | |
489 | if (rd->json_output) { | |
490 | jsonw_string_field(rd->jw, "state", cm_id_state_to_str(state)); | |
491 | return; | |
492 | } | |
493 | pr_out("state %s ", cm_id_state_to_str(state)); | |
494 | } | |
495 | ||
496 | static void print_ps(struct rd *rd, uint32_t ps) | |
497 | { | |
498 | if (rd->json_output) { | |
499 | jsonw_string_field(rd->jw, "ps", cm_id_ps_to_str(ps)); | |
500 | return; | |
501 | } | |
502 | pr_out("ps %s ", cm_id_ps_to_str(ps)); | |
503 | } | |
504 | ||
505 | static void print_ipaddr(struct rd *rd, const char *key, char *addrstr, | |
506 | uint16_t port) | |
507 | { | |
508 | if (rd->json_output) { | |
509 | int name_size = INET6_ADDRSTRLEN+strlen(":65535"); | |
510 | char json_name[name_size]; | |
511 | ||
512 | snprintf(json_name, name_size, "%s:%u", addrstr, port); | |
513 | jsonw_string_field(rd->jw, key, json_name); | |
514 | return; | |
515 | } | |
516 | pr_out("%s %s:%u ", key, addrstr, port); | |
517 | } | |
518 | ||
519 | static int ss_ntop(struct nlattr *nla_line, char *addr_str, uint16_t *port) | |
520 | { | |
521 | struct __kernel_sockaddr_storage *addr; | |
522 | ||
523 | addr = (struct __kernel_sockaddr_storage *) | |
524 | mnl_attr_get_payload(nla_line); | |
525 | switch (addr->ss_family) { | |
526 | case AF_INET: { | |
527 | struct sockaddr_in *sin = (struct sockaddr_in *)addr; | |
528 | ||
529 | if (!inet_ntop(AF_INET, (const void *)&sin->sin_addr, addr_str, | |
530 | INET6_ADDRSTRLEN)) | |
531 | return -EINVAL; | |
532 | *port = ntohs(sin->sin_port); | |
533 | break; | |
534 | } | |
535 | case AF_INET6: { | |
536 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr; | |
537 | ||
538 | if (!inet_ntop(AF_INET6, (const void *)&sin6->sin6_addr, | |
539 | addr_str, INET6_ADDRSTRLEN)) | |
540 | return -EINVAL; | |
541 | *port = ntohs(sin6->sin6_port); | |
542 | break; | |
543 | } | |
544 | default: | |
545 | return -EINVAL; | |
546 | } | |
547 | return 0; | |
548 | } | |
549 | ||
550 | static int res_cm_id_parse_cb(const struct nlmsghdr *nlh, void *data) | |
551 | { | |
552 | struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; | |
553 | struct nlattr *nla_table, *nla_entry; | |
554 | struct rd *rd = data; | |
555 | const char *name; | |
556 | int idx; | |
557 | ||
558 | mnl_attr_parse(nlh, 0, rd_attr_cb, tb); | |
559 | if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || | |
560 | !tb[RDMA_NLDEV_ATTR_DEV_NAME] || | |
561 | !tb[RDMA_NLDEV_ATTR_RES_CM_ID]) | |
562 | return MNL_CB_ERROR; | |
563 | ||
564 | name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); | |
565 | idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); | |
566 | nla_table = tb[RDMA_NLDEV_ATTR_RES_CM_ID]; | |
567 | mnl_attr_for_each_nested(nla_entry, nla_table) { | |
568 | struct nlattr *nla_line[RDMA_NLDEV_ATTR_MAX] = {}; | |
569 | char src_addr_str[INET6_ADDRSTRLEN]; | |
570 | char dst_addr_str[INET6_ADDRSTRLEN]; | |
571 | uint16_t src_port, dst_port; | |
572 | uint32_t port = 0, pid = 0; | |
573 | uint8_t type = 0, state; | |
574 | uint32_t lqpn = 0, ps; | |
575 | char *comm = NULL; | |
576 | int err; | |
577 | ||
578 | err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, nla_line); | |
579 | if (err != MNL_CB_OK) | |
580 | return -EINVAL; | |
581 | ||
582 | if (!nla_line[RDMA_NLDEV_ATTR_RES_STATE] || | |
583 | !nla_line[RDMA_NLDEV_ATTR_RES_PS] || | |
584 | (!nla_line[RDMA_NLDEV_ATTR_RES_PID] && | |
585 | !nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME])) { | |
586 | return MNL_CB_ERROR; | |
587 | } | |
588 | ||
589 | if (nla_line[RDMA_NLDEV_ATTR_PORT_INDEX]) | |
590 | port = mnl_attr_get_u32( | |
591 | nla_line[RDMA_NLDEV_ATTR_PORT_INDEX]); | |
592 | ||
593 | if (port && port != rd->port_idx) | |
594 | continue; | |
595 | ||
596 | if (nla_line[RDMA_NLDEV_ATTR_RES_LQPN]) { | |
597 | lqpn = mnl_attr_get_u32( | |
598 | nla_line[RDMA_NLDEV_ATTR_RES_LQPN]); | |
599 | if (rd_check_is_filtered(rd, "lqpn", lqpn)) | |
600 | continue; | |
601 | } | |
602 | if (nla_line[RDMA_NLDEV_ATTR_RES_TYPE]) { | |
603 | type = mnl_attr_get_u8( | |
604 | nla_line[RDMA_NLDEV_ATTR_RES_TYPE]); | |
605 | if (rd_check_is_string_filtered(rd, "qp-type", | |
606 | qp_types_to_str(type))) | |
607 | continue; | |
608 | } | |
609 | ||
610 | ps = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PS]); | |
611 | if (rd_check_is_string_filtered(rd, "ps", cm_id_ps_to_str(ps))) | |
612 | continue; | |
613 | ||
614 | state = mnl_attr_get_u8(nla_line[RDMA_NLDEV_ATTR_RES_STATE]); | |
615 | if (rd_check_is_string_filtered(rd, "state", | |
616 | cm_id_state_to_str(state))) | |
617 | continue; | |
618 | ||
619 | if (nla_line[RDMA_NLDEV_ATTR_RES_SRC_ADDR]) { | |
620 | if (ss_ntop(nla_line[RDMA_NLDEV_ATTR_RES_SRC_ADDR], | |
621 | src_addr_str, &src_port)) | |
622 | continue; | |
623 | if (rd_check_is_string_filtered(rd, "src-addr", | |
624 | src_addr_str)) | |
625 | continue; | |
626 | } | |
627 | ||
628 | if (nla_line[RDMA_NLDEV_ATTR_RES_DST_ADDR]) { | |
629 | if (ss_ntop(nla_line[RDMA_NLDEV_ATTR_RES_DST_ADDR], | |
630 | dst_addr_str, &dst_port)) | |
631 | continue; | |
632 | if (rd_check_is_string_filtered(rd, "dst-addr", | |
633 | dst_addr_str)) | |
634 | continue; | |
635 | } | |
636 | ||
637 | if (rd_check_is_filtered(rd, "src-port", src_port)) | |
638 | continue; | |
639 | ||
640 | if (rd_check_is_filtered(rd, "dst-port", dst_port)) | |
641 | continue; | |
642 | ||
643 | if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) { | |
644 | pid = mnl_attr_get_u32( | |
645 | nla_line[RDMA_NLDEV_ATTR_RES_PID]); | |
646 | comm = get_task_name(pid); | |
647 | } | |
648 | ||
649 | if (rd_check_is_filtered(rd, "pid", pid)) { | |
650 | free(comm); | |
651 | continue; | |
652 | } | |
653 | ||
654 | if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) { | |
655 | /* discard const from mnl_attr_get_str */ | |
656 | comm = (char *)mnl_attr_get_str( | |
657 | nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]); | |
658 | } | |
659 | ||
660 | if (rd->json_output) | |
661 | jsonw_start_array(rd->jw); | |
662 | ||
663 | print_link(rd, idx, name, port, nla_line); | |
664 | if (nla_line[RDMA_NLDEV_ATTR_RES_LQPN]) | |
665 | print_lqpn(rd, lqpn); | |
666 | if (nla_line[RDMA_NLDEV_ATTR_RES_TYPE]) | |
667 | print_qp_type(rd, type); | |
668 | print_cm_id_state(rd, state); | |
669 | print_ps(rd, ps); | |
670 | print_pid(rd, pid); | |
671 | print_comm(rd, comm, nla_line); | |
672 | ||
673 | if (nla_line[RDMA_NLDEV_ATTR_RES_SRC_ADDR]) | |
674 | print_ipaddr(rd, "src-addr", src_addr_str, src_port); | |
675 | if (nla_line[RDMA_NLDEV_ATTR_RES_DST_ADDR]) | |
676 | print_ipaddr(rd, "dst-addr", dst_addr_str, dst_port); | |
677 | ||
678 | if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) | |
679 | free(comm); | |
680 | ||
681 | if (rd->json_output) | |
682 | jsonw_end_array(rd->jw); | |
683 | else | |
684 | pr_out("\n"); | |
685 | } | |
686 | return MNL_CB_OK; | |
687 | } | |
688 | ||
b0b8e32c SW |
689 | static void print_cqe(struct rd *rd, uint32_t val) |
690 | { | |
691 | if (rd->json_output) | |
692 | jsonw_uint_field(rd->jw, "cqe", val); | |
693 | else | |
694 | pr_out("cqe %u ", val); | |
695 | } | |
696 | ||
697 | static void print_users(struct rd *rd, uint64_t val) | |
698 | { | |
699 | if (rd->json_output) | |
700 | jsonw_uint_field(rd->jw, "users", val); | |
701 | else | |
702 | pr_out("users %" PRIu64 " ", val); | |
703 | } | |
704 | ||
705 | static const char *poll_ctx_to_str(uint8_t idx) | |
706 | { | |
707 | static const char * const cm_id_states_str[] = { | |
708 | "DIRECT", "SOFTIRQ", "WORKQUEUE"}; | |
709 | ||
710 | if (idx < ARRAY_SIZE(cm_id_states_str)) | |
711 | return cm_id_states_str[idx]; | |
712 | return "UNKNOWN"; | |
713 | } | |
714 | ||
715 | static void print_poll_ctx(struct rd *rd, uint8_t poll_ctx) | |
716 | { | |
717 | if (rd->json_output) { | |
718 | jsonw_string_field(rd->jw, "poll-ctx", | |
719 | poll_ctx_to_str(poll_ctx)); | |
720 | return; | |
721 | } | |
722 | pr_out("poll-ctx %s ", poll_ctx_to_str(poll_ctx)); | |
723 | } | |
724 | ||
725 | static int res_cq_parse_cb(const struct nlmsghdr *nlh, void *data) | |
726 | { | |
727 | struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; | |
728 | struct nlattr *nla_table, *nla_entry; | |
729 | struct rd *rd = data; | |
730 | const char *name; | |
731 | uint32_t idx; | |
732 | ||
733 | mnl_attr_parse(nlh, 0, rd_attr_cb, tb); | |
734 | if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || | |
735 | !tb[RDMA_NLDEV_ATTR_DEV_NAME] || | |
736 | !tb[RDMA_NLDEV_ATTR_RES_CQ]) | |
737 | return MNL_CB_ERROR; | |
738 | ||
739 | name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); | |
740 | idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); | |
741 | nla_table = tb[RDMA_NLDEV_ATTR_RES_CQ]; | |
742 | ||
743 | mnl_attr_for_each_nested(nla_entry, nla_table) { | |
744 | struct nlattr *nla_line[RDMA_NLDEV_ATTR_MAX] = {}; | |
745 | char *comm = NULL; | |
746 | uint32_t pid = 0; | |
747 | uint8_t poll_ctx = 0; | |
748 | uint64_t users; | |
749 | uint32_t cqe; | |
750 | int err; | |
751 | ||
752 | err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, nla_line); | |
753 | if (err != MNL_CB_OK) | |
754 | return MNL_CB_ERROR; | |
755 | ||
756 | if (!nla_line[RDMA_NLDEV_ATTR_RES_CQE] || | |
757 | !nla_line[RDMA_NLDEV_ATTR_RES_USECNT] || | |
758 | (!nla_line[RDMA_NLDEV_ATTR_RES_PID] && | |
759 | !nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME])) { | |
760 | return MNL_CB_ERROR; | |
761 | } | |
762 | ||
763 | cqe = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_CQE]); | |
764 | ||
765 | users = mnl_attr_get_u64(nla_line[RDMA_NLDEV_ATTR_RES_USECNT]); | |
766 | if (rd_check_is_filtered(rd, "users", users)) | |
767 | continue; | |
768 | ||
769 | if (nla_line[RDMA_NLDEV_ATTR_RES_POLL_CTX]) { | |
770 | poll_ctx = mnl_attr_get_u8( | |
771 | nla_line[RDMA_NLDEV_ATTR_RES_POLL_CTX]); | |
772 | if (rd_check_is_string_filtered(rd, "poll-ctx", | |
773 | poll_ctx_to_str(poll_ctx))) | |
774 | continue; | |
775 | } | |
776 | ||
777 | if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) { | |
778 | pid = mnl_attr_get_u32( | |
779 | nla_line[RDMA_NLDEV_ATTR_RES_PID]); | |
780 | comm = get_task_name(pid); | |
781 | } | |
782 | ||
783 | if (rd_check_is_filtered(rd, "pid", pid)) { | |
784 | free(comm); | |
785 | continue; | |
786 | } | |
787 | ||
788 | if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) | |
789 | /* discard const from mnl_attr_get_str */ | |
790 | comm = (char *)mnl_attr_get_str( | |
791 | nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]); | |
792 | ||
793 | if (rd->json_output) | |
794 | jsonw_start_array(rd->jw); | |
795 | ||
796 | print_dev(rd, idx, name); | |
797 | print_cqe(rd, cqe); | |
798 | print_users(rd, users); | |
799 | if (nla_line[RDMA_NLDEV_ATTR_RES_POLL_CTX]) | |
800 | print_poll_ctx(rd, poll_ctx); | |
801 | print_pid(rd, pid); | |
802 | print_comm(rd, comm, nla_line); | |
803 | ||
804 | if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) | |
805 | free(comm); | |
806 | ||
807 | if (rd->json_output) | |
808 | jsonw_end_array(rd->jw); | |
809 | else | |
810 | pr_out("\n"); | |
811 | } | |
812 | return MNL_CB_OK; | |
813 | } | |
814 | ||
8958a15c SW |
815 | static void print_key(struct rd *rd, const char *name, uint32_t val) |
816 | { | |
817 | if (rd->json_output) | |
818 | jsonw_xint_field(rd->jw, name, val); | |
819 | else | |
820 | pr_out("%s 0x%x ", name, val); | |
821 | } | |
822 | ||
823 | static void print_iova(struct rd *rd, uint64_t val) | |
824 | { | |
825 | if (rd->json_output) | |
826 | jsonw_xint_field(rd->jw, "iova", val); | |
827 | else | |
828 | pr_out("iova 0x%" PRIx64 " ", val); | |
829 | } | |
830 | ||
831 | static void print_mrlen(struct rd *rd, uint64_t val) | |
832 | { | |
833 | if (rd->json_output) | |
834 | jsonw_uint_field(rd->jw, "mrlen", val); | |
835 | else | |
836 | pr_out("mrlen %" PRIu64 " ", val); | |
837 | } | |
838 | ||
839 | static int res_mr_parse_cb(const struct nlmsghdr *nlh, void *data) | |
840 | { | |
841 | struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; | |
842 | struct nlattr *nla_table, *nla_entry; | |
843 | struct rd *rd = data; | |
844 | const char *name; | |
845 | uint32_t idx; | |
846 | ||
847 | mnl_attr_parse(nlh, 0, rd_attr_cb, tb); | |
848 | if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || | |
849 | !tb[RDMA_NLDEV_ATTR_DEV_NAME] || | |
850 | !tb[RDMA_NLDEV_ATTR_RES_MR]) | |
851 | return MNL_CB_ERROR; | |
852 | ||
853 | name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); | |
854 | idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); | |
855 | nla_table = tb[RDMA_NLDEV_ATTR_RES_MR]; | |
856 | ||
857 | mnl_attr_for_each_nested(nla_entry, nla_table) { | |
858 | struct nlattr *nla_line[RDMA_NLDEV_ATTR_MAX] = {}; | |
859 | uint32_t rkey = 0, lkey = 0; | |
860 | uint64_t iova = 0, mrlen; | |
861 | char *comm = NULL; | |
862 | uint32_t pid = 0; | |
863 | int err; | |
864 | ||
865 | err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, nla_line); | |
866 | if (err != MNL_CB_OK) | |
867 | return MNL_CB_ERROR; | |
868 | ||
869 | if (!nla_line[RDMA_NLDEV_ATTR_RES_MRLEN] || | |
870 | (!nla_line[RDMA_NLDEV_ATTR_RES_PID] && | |
871 | !nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME])) { | |
872 | return MNL_CB_ERROR; | |
873 | } | |
874 | ||
875 | if (nla_line[RDMA_NLDEV_ATTR_RES_RKEY]) | |
876 | rkey = mnl_attr_get_u32( | |
877 | nla_line[RDMA_NLDEV_ATTR_RES_RKEY]); | |
878 | if (nla_line[RDMA_NLDEV_ATTR_RES_LKEY]) | |
879 | lkey = mnl_attr_get_u32( | |
880 | nla_line[RDMA_NLDEV_ATTR_RES_LKEY]); | |
881 | if (nla_line[RDMA_NLDEV_ATTR_RES_IOVA]) | |
882 | iova = mnl_attr_get_u64( | |
883 | nla_line[RDMA_NLDEV_ATTR_RES_IOVA]); | |
884 | ||
885 | mrlen = mnl_attr_get_u64(nla_line[RDMA_NLDEV_ATTR_RES_MRLEN]); | |
886 | if (rd_check_is_filtered(rd, "mrlen", mrlen)) | |
887 | continue; | |
888 | ||
889 | if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) { | |
890 | pid = mnl_attr_get_u32( | |
891 | nla_line[RDMA_NLDEV_ATTR_RES_PID]); | |
892 | comm = get_task_name(pid); | |
893 | } | |
894 | ||
895 | if (rd_check_is_filtered(rd, "pid", pid)) { | |
896 | free(comm); | |
897 | continue; | |
898 | } | |
899 | ||
900 | if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) | |
901 | /* discard const from mnl_attr_get_str */ | |
902 | comm = (char *)mnl_attr_get_str( | |
903 | nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]); | |
904 | ||
905 | if (rd->json_output) | |
906 | jsonw_start_array(rd->jw); | |
907 | ||
908 | print_dev(rd, idx, name); | |
909 | if (nla_line[RDMA_NLDEV_ATTR_RES_RKEY]) | |
910 | print_key(rd, "rkey", rkey); | |
911 | if (nla_line[RDMA_NLDEV_ATTR_RES_LKEY]) | |
912 | print_key(rd, "lkey", lkey); | |
913 | if (nla_line[RDMA_NLDEV_ATTR_RES_IOVA]) | |
914 | print_iova(rd, iova); | |
915 | print_mrlen(rd, mrlen); | |
916 | print_pid(rd, pid); | |
917 | print_comm(rd, comm, nla_line); | |
918 | ||
919 | if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) | |
920 | free(comm); | |
921 | ||
922 | if (rd->json_output) | |
923 | jsonw_end_array(rd->jw); | |
924 | else | |
925 | pr_out("\n"); | |
926 | } | |
927 | return MNL_CB_OK; | |
928 | } | |
929 | ||
923aa825 LR |
930 | RES_FUNC(res_no_args, RDMA_NLDEV_CMD_RES_GET, NULL, true); |
931 | ||
8ecac46a LR |
932 | static const struct |
933 | filters qp_valid_filters[MAX_NUMBER_OF_FILTERS] = {{ .name = "link", | |
934 | .is_number = false }, | |
935 | { .name = "lqpn", | |
936 | .is_number = true }, | |
937 | { .name = "rqpn", | |
938 | .is_number = true }, | |
939 | { .name = "pid", | |
940 | .is_number = true }, | |
941 | { .name = "sq-psn", | |
942 | .is_number = true }, | |
943 | { .name = "rq-psn", | |
944 | .is_number = true }, | |
945 | { .name = "type", | |
946 | .is_number = false }, | |
947 | { .name = "path-mig-state", | |
948 | .is_number = false }, | |
949 | { .name = "state", | |
950 | .is_number = false } }; | |
951 | ||
952 | RES_FUNC(res_qp, RDMA_NLDEV_CMD_RES_QP_GET, qp_valid_filters, false); | |
953 | ||
9a362cc7 SW |
954 | static const |
955 | struct filters cm_id_valid_filters[MAX_NUMBER_OF_FILTERS] = { | |
956 | { .name = "link", .is_number = false }, | |
957 | { .name = "lqpn", .is_number = true }, | |
958 | { .name = "qp-type", .is_number = false }, | |
959 | { .name = "state", .is_number = false }, | |
960 | { .name = "ps", .is_number = false }, | |
961 | { .name = "dev-type", .is_number = false }, | |
962 | { .name = "transport-type", .is_number = false }, | |
963 | { .name = "pid", .is_number = true }, | |
964 | { .name = "src-addr", .is_number = false }, | |
965 | { .name = "src-port", .is_number = true }, | |
966 | { .name = "dst-addr", .is_number = false }, | |
967 | { .name = "dst-port", .is_number = true } | |
968 | }; | |
969 | ||
970 | RES_FUNC(res_cm_id, RDMA_NLDEV_CMD_RES_CM_ID_GET, cm_id_valid_filters, false); | |
971 | ||
b0b8e32c SW |
972 | static const |
973 | struct filters cq_valid_filters[MAX_NUMBER_OF_FILTERS] = { | |
974 | { .name = "dev", .is_number = false }, | |
975 | { .name = "users", .is_number = true }, | |
976 | { .name = "poll-ctx", .is_number = false }, | |
977 | { .name = "pid", .is_number = true } | |
978 | }; | |
979 | ||
980 | RES_FUNC(res_cq, RDMA_NLDEV_CMD_RES_CQ_GET, cq_valid_filters, true); | |
981 | ||
8958a15c SW |
982 | static const |
983 | struct filters mr_valid_filters[MAX_NUMBER_OF_FILTERS] = { | |
984 | { .name = "dev", .is_number = false }, | |
985 | { .name = "rkey", .is_number = true }, | |
986 | { .name = "lkey", .is_number = true }, | |
987 | { .name = "mrlen", .is_number = true }, | |
988 | { .name = "pid", .is_number = true } | |
989 | }; | |
990 | ||
991 | RES_FUNC(res_mr, RDMA_NLDEV_CMD_RES_MR_GET, mr_valid_filters, true); | |
992 | ||
923aa825 LR |
993 | static int res_show(struct rd *rd) |
994 | { | |
995 | const struct rd_cmd cmds[] = { | |
996 | { NULL, res_no_args }, | |
8ecac46a | 997 | { "qp", res_qp }, |
9a362cc7 | 998 | { "cm_id", res_cm_id }, |
b0b8e32c | 999 | { "cq", res_cq }, |
8958a15c | 1000 | { "mr", res_mr }, |
923aa825 LR |
1001 | { 0 } |
1002 | }; | |
1003 | ||
1004 | /* | |
1005 | * Special case to support "rdma res show DEV_NAME" | |
1006 | */ | |
1007 | if (rd_argc(rd) == 1 && dev_map_lookup(rd, false)) | |
1008 | return rd_exec_dev(rd, _res_no_args); | |
1009 | ||
1010 | return rd_exec_cmd(rd, cmds, "parameter"); | |
1011 | } | |
1012 | ||
1013 | int cmd_res(struct rd *rd) | |
1014 | { | |
1015 | const struct rd_cmd cmds[] = { | |
1016 | { NULL, res_show }, | |
1017 | { "show", res_show }, | |
1018 | { "list", res_show }, | |
1019 | { "help", res_help }, | |
1020 | { 0 } | |
1021 | }; | |
1022 | ||
1023 | return rd_exec_cmd(rd, cmds, "resource command"); | |
1024 | } |