]>
Commit | Line | Data |
---|---|---|
835d8321 | 1 | // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
da990ab4 LR |
2 | /* |
3 | * link.c RDMA tool | |
da990ab4 LR |
4 | * Authors: Leon Romanovsky <leonro@mellanox.com> |
5 | */ | |
6 | ||
7 | #include "rdma.h" | |
8 | ||
9 | static int link_help(struct rd *rd) | |
10 | { | |
11 | pr_out("Usage: %s link show [DEV/PORT_INDEX]\n", rd->filename); | |
4336c582 SW |
12 | pr_out("Usage: %s link add NAME type TYPE netdev NETDEV\n", |
13 | rd->filename); | |
14 | pr_out("Usage: %s link delete NAME\n", rd->filename); | |
da990ab4 LR |
15 | return 0; |
16 | } | |
17 | ||
18 | static const char *caps_to_str(uint32_t idx) | |
19 | { | |
40fc8c2c | 20 | #define RDMA_PORT_FLAGS_LOW(x) \ |
d090fbf3 | 21 | x(RESERVED, 0) \ |
da990ab4 LR |
22 | x(SM, 1) \ |
23 | x(NOTICE, 2) \ | |
24 | x(TRAP, 3) \ | |
25 | x(OPT_IPD, 4) \ | |
26 | x(AUTO_MIGR, 5) \ | |
27 | x(SL_MAP, 6) \ | |
28 | x(MKEY_NVRAM, 7) \ | |
29 | x(PKEY_NVRAM, 8) \ | |
30 | x(LED_INFO, 9) \ | |
31 | x(SM_DISABLED, 10) \ | |
4e2eb9fd | 32 | x(SYS_IMAGE_GUID, 11) \ |
da990ab4 | 33 | x(PKEY_SW_EXT_PORT_TRAP, 12) \ |
d090fbf3 | 34 | x(CABLE_INFO, 13) \ |
da990ab4 | 35 | x(EXTENDED_SPEEDS, 14) \ |
d090fbf3 | 36 | x(CAP_MASK2, 15) \ |
da990ab4 LR |
37 | x(CM, 16) \ |
38 | x(SNMP_TUNNEL, 17) \ | |
39 | x(REINIT, 18) \ | |
40 | x(DEVICE_MGMT, 19) \ | |
41 | x(VENDOR_CLASS, 20) \ | |
42 | x(DR_NOTICE, 21) \ | |
43 | x(CAP_MASK_NOTICE, 22) \ | |
44 | x(BOOT_MGMT, 23) \ | |
45 | x(LINK_LATENCY, 24) \ | |
8f478ec2 | 46 | x(CLIENT_REG, 25) \ |
d090fbf3 LR |
47 | x(OTHER_LOCAL_CHANGES, 26) \ |
48 | x(LINK_SPPED_WIDTH, 27) \ | |
49 | x(VENDOR_SPECIFIC_MADS, 28) \ | |
50 | x(MULT_PKER_TRAP, 29) \ | |
51 | x(MULT_FDB, 30) \ | |
52 | x(HIERARCHY_INFO, 31) | |
da990ab4 | 53 | |
40fc8c2c MG |
54 | #define RDMA_PORT_FLAGS_HIGH(x) \ |
55 | x(SET_NODE_DESC, 0) \ | |
56 | x(EXT_INFO, 1) \ | |
57 | x(VIRT, 2) \ | |
58 | x(SWITCH_POR_STATE_TABLE, 3) \ | |
59 | x(LINK_WIDTH_2X, 4) \ | |
60 | x(LINK_SPEED_HDR, 5) | |
da990ab4 | 61 | |
40fc8c2c MG |
62 | /* |
63 | * Separation below is needed to allow compilation of rdmatool | |
64 | * on 32bits systems. On such systems, C-enum is limited to be | |
65 | * int and can't hold more than 32 bits. | |
66 | */ | |
67 | enum { RDMA_PORT_FLAGS_LOW(RDMA_BITMAP_ENUM) }; | |
68 | enum { RDMA_PORT_FLAGS_HIGH(RDMA_BITMAP_ENUM) }; | |
69 | ||
70 | static const char * const | |
71 | rdma_port_names_low[] = { RDMA_PORT_FLAGS_LOW(RDMA_BITMAP_NAMES) }; | |
da990ab4 | 72 | static const char * const |
40fc8c2c MG |
73 | rdma_port_names_high[] = { RDMA_PORT_FLAGS_HIGH(RDMA_BITMAP_NAMES) }; |
74 | uint32_t high_idx; | |
75 | #undef RDMA_PORT_FLAGS_LOW | |
76 | #undef RDMA_PORT_FLAGS_HIGH | |
77 | ||
78 | if (idx < ARRAY_SIZE(rdma_port_names_low) && rdma_port_names_low[idx]) | |
79 | return rdma_port_names_low[idx]; | |
80 | ||
81 | high_idx = idx - ARRAY_SIZE(rdma_port_names_low); | |
82 | if (high_idx < ARRAY_SIZE(rdma_port_names_high) && | |
83 | rdma_port_names_high[high_idx]) | |
84 | return rdma_port_names_high[high_idx]; | |
da990ab4 | 85 | |
40fc8c2c | 86 | return "UNKNOWN"; |
da990ab4 LR |
87 | } |
88 | ||
7fc75744 | 89 | static void link_print_caps(struct rd *rd, struct nlattr **tb) |
da990ab4 LR |
90 | { |
91 | uint64_t caps; | |
92 | uint32_t idx; | |
93 | ||
94 | if (!tb[RDMA_NLDEV_ATTR_CAP_FLAGS]) | |
95 | return; | |
96 | ||
97 | caps = mnl_attr_get_u64(tb[RDMA_NLDEV_ATTR_CAP_FLAGS]); | |
98 | ||
7fc75744 LR |
99 | if (rd->json_output) { |
100 | jsonw_name(rd->jw, "caps"); | |
101 | jsonw_start_array(rd->jw); | |
102 | } else { | |
103 | pr_out("\n caps: <"); | |
104 | } | |
da990ab4 LR |
105 | for (idx = 0; caps; idx++) { |
106 | if (caps & 0x1) { | |
7fc75744 LR |
107 | if (rd->json_output) { |
108 | jsonw_string(rd->jw, caps_to_str(idx)); | |
109 | } else { | |
110 | pr_out("%s", caps_to_str(idx)); | |
111 | if (caps >> 0x1) | |
112 | pr_out(", "); | |
113 | } | |
da990ab4 LR |
114 | } |
115 | caps >>= 0x1; | |
116 | } | |
117 | ||
7fc75744 LR |
118 | if (rd->json_output) |
119 | jsonw_end_array(rd->jw); | |
120 | else | |
121 | pr_out(">"); | |
da990ab4 LR |
122 | } |
123 | ||
7fc75744 | 124 | static void link_print_subnet_prefix(struct rd *rd, struct nlattr **tb) |
da990ab4 LR |
125 | { |
126 | uint64_t subnet_prefix; | |
7fc75744 LR |
127 | uint16_t vp[4]; |
128 | char str[32]; | |
da990ab4 LR |
129 | |
130 | if (!tb[RDMA_NLDEV_ATTR_SUBNET_PREFIX]) | |
131 | return; | |
132 | ||
133 | subnet_prefix = mnl_attr_get_u64(tb[RDMA_NLDEV_ATTR_SUBNET_PREFIX]); | |
7fc75744 LR |
134 | memcpy(vp, &subnet_prefix, sizeof(uint64_t)); |
135 | snprintf(str, 32, "%04x:%04x:%04x:%04x", vp[3], vp[2], vp[1], vp[0]); | |
136 | if (rd->json_output) | |
137 | jsonw_string_field(rd->jw, "subnet_prefix", str); | |
138 | else | |
139 | pr_out("subnet_prefix %s ", str); | |
da990ab4 LR |
140 | } |
141 | ||
7fc75744 | 142 | static void link_print_lid(struct rd *rd, struct nlattr **tb) |
da990ab4 | 143 | { |
7fc75744 LR |
144 | uint32_t lid; |
145 | ||
da990ab4 LR |
146 | if (!tb[RDMA_NLDEV_ATTR_LID]) |
147 | return; | |
148 | ||
7fc75744 LR |
149 | lid = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_LID]); |
150 | if (rd->json_output) | |
151 | jsonw_uint_field(rd->jw, "lid", lid); | |
152 | else | |
153 | pr_out("lid %u ", lid); | |
da990ab4 LR |
154 | } |
155 | ||
7fc75744 | 156 | static void link_print_sm_lid(struct rd *rd, struct nlattr **tb) |
da990ab4 | 157 | { |
7fc75744 LR |
158 | uint32_t sm_lid; |
159 | ||
da990ab4 LR |
160 | if (!tb[RDMA_NLDEV_ATTR_SM_LID]) |
161 | return; | |
162 | ||
7fc75744 LR |
163 | sm_lid = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_SM_LID]); |
164 | if (rd->json_output) | |
165 | jsonw_uint_field(rd->jw, "sm_lid", sm_lid); | |
166 | else | |
167 | pr_out("sm_lid %u ", sm_lid); | |
da990ab4 LR |
168 | } |
169 | ||
7fc75744 | 170 | static void link_print_lmc(struct rd *rd, struct nlattr **tb) |
da990ab4 | 171 | { |
7fc75744 LR |
172 | uint8_t lmc; |
173 | ||
da990ab4 LR |
174 | if (!tb[RDMA_NLDEV_ATTR_LMC]) |
175 | return; | |
176 | ||
7fc75744 LR |
177 | lmc = mnl_attr_get_u8(tb[RDMA_NLDEV_ATTR_LMC]); |
178 | if (rd->json_output) | |
179 | jsonw_uint_field(rd->jw, "lmc", lmc); | |
180 | else | |
181 | pr_out("lmc %u ", lmc); | |
da990ab4 LR |
182 | } |
183 | ||
184 | static const char *link_state_to_str(uint8_t link_state) | |
185 | { | |
186 | static const char * const link_state_str[] = { "NOP", "DOWN", | |
187 | "INIT", "ARMED", | |
188 | "ACTIVE", | |
189 | "ACTIVE_DEFER" }; | |
190 | if (link_state < ARRAY_SIZE(link_state_str)) | |
191 | return link_state_str[link_state]; | |
192 | return "UNKNOWN"; | |
193 | } | |
194 | ||
7fc75744 | 195 | static void link_print_state(struct rd *rd, struct nlattr **tb) |
da990ab4 LR |
196 | { |
197 | uint8_t state; | |
198 | ||
199 | if (!tb[RDMA_NLDEV_ATTR_PORT_STATE]) | |
200 | return; | |
201 | ||
202 | state = mnl_attr_get_u8(tb[RDMA_NLDEV_ATTR_PORT_STATE]); | |
7fc75744 LR |
203 | if (rd->json_output) |
204 | jsonw_string_field(rd->jw, "state", link_state_to_str(state)); | |
205 | else | |
206 | pr_out("state %s ", link_state_to_str(state)); | |
da990ab4 LR |
207 | } |
208 | ||
209 | static const char *phys_state_to_str(uint8_t phys_state) | |
210 | { | |
211 | static const char * const phys_state_str[] = { "NOP", "SLEEP", | |
212 | "POLLING", "DISABLED", | |
213 | "ARMED", "LINK_UP", | |
214 | "LINK_ERROR_RECOVER", | |
215 | "PHY_TEST", "UNKNOWN", | |
216 | "OPA_OFFLINE", | |
217 | "UNKNOWN", "OPA_TEST" }; | |
218 | if (phys_state < ARRAY_SIZE(phys_state_str)) | |
219 | return phys_state_str[phys_state]; | |
220 | return "UNKNOWN"; | |
221 | }; | |
222 | ||
7fc75744 | 223 | static void link_print_phys_state(struct rd *rd, struct nlattr **tb) |
da990ab4 LR |
224 | { |
225 | uint8_t phys_state; | |
226 | ||
227 | if (!tb[RDMA_NLDEV_ATTR_PORT_PHYS_STATE]) | |
228 | return; | |
229 | ||
230 | phys_state = mnl_attr_get_u8(tb[RDMA_NLDEV_ATTR_PORT_PHYS_STATE]); | |
7fc75744 LR |
231 | if (rd->json_output) |
232 | jsonw_string_field(rd->jw, "physical_state", | |
233 | phys_state_to_str(phys_state)); | |
234 | else | |
235 | pr_out("physical_state %s ", phys_state_to_str(phys_state)); | |
da990ab4 LR |
236 | } |
237 | ||
15259427 LR |
238 | static void link_print_netdev(struct rd *rd, struct nlattr **tb) |
239 | { | |
240 | const char *netdev_name; | |
241 | uint32_t idx; | |
242 | ||
243 | if (!tb[RDMA_NLDEV_ATTR_NDEV_NAME] || !tb[RDMA_NLDEV_ATTR_NDEV_INDEX]) | |
244 | return; | |
245 | ||
246 | netdev_name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_NDEV_NAME]); | |
247 | idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_NDEV_INDEX]); | |
248 | if (rd->json_output) { | |
249 | jsonw_string_field(rd->jw, "netdev", netdev_name); | |
250 | jsonw_uint_field(rd->jw, "netdev_index", idx); | |
251 | } else { | |
252 | pr_out("netdev %s ", netdev_name); | |
253 | if (rd->show_details) | |
254 | pr_out("netdev_index %u ", idx); | |
255 | } | |
256 | } | |
257 | ||
da990ab4 LR |
258 | static int link_parse_cb(const struct nlmsghdr *nlh, void *data) |
259 | { | |
260 | struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; | |
261 | struct rd *rd = data; | |
7fc75744 LR |
262 | uint32_t port, idx; |
263 | char name[32]; | |
da990ab4 LR |
264 | |
265 | mnl_attr_parse(nlh, 0, rd_attr_cb, tb); | |
266 | if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME]) | |
267 | return MNL_CB_ERROR; | |
268 | ||
269 | if (!tb[RDMA_NLDEV_ATTR_PORT_INDEX]) { | |
270 | pr_err("This tool doesn't support switches yet\n"); | |
271 | return MNL_CB_ERROR; | |
272 | } | |
273 | ||
7fc75744 LR |
274 | idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); |
275 | port = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]); | |
276 | snprintf(name, 32, "%s/%u", | |
277 | mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]), port); | |
278 | ||
279 | if (rd->json_output) { | |
280 | jsonw_uint_field(rd->jw, "ifindex", idx); | |
281 | jsonw_uint_field(rd->jw, "port", port); | |
282 | jsonw_string_field(rd->jw, "ifname", name); | |
283 | ||
284 | } else { | |
285 | pr_out("%u/%u: %s: ", idx, port, name); | |
286 | } | |
287 | ||
288 | link_print_subnet_prefix(rd, tb); | |
289 | link_print_lid(rd, tb); | |
290 | link_print_sm_lid(rd, tb); | |
291 | link_print_lmc(rd, tb); | |
292 | link_print_state(rd, tb); | |
293 | link_print_phys_state(rd, tb); | |
15259427 | 294 | link_print_netdev(rd, tb); |
da990ab4 | 295 | if (rd->show_details) |
7fc75744 | 296 | link_print_caps(rd, tb); |
da990ab4 | 297 | |
7fc75744 LR |
298 | if (!rd->json_output) |
299 | pr_out("\n"); | |
da990ab4 LR |
300 | return MNL_CB_OK; |
301 | } | |
302 | ||
303 | static int link_no_args(struct rd *rd) | |
304 | { | |
305 | uint32_t seq; | |
306 | int ret; | |
307 | ||
308 | rd_prepare_msg(rd, RDMA_NLDEV_CMD_PORT_GET, &seq, | |
309 | (NLM_F_REQUEST | NLM_F_ACK)); | |
310 | mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx); | |
311 | mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx); | |
312 | ret = rd_send_msg(rd); | |
313 | if (ret) | |
314 | return ret; | |
315 | ||
7fc75744 LR |
316 | if (rd->json_output) |
317 | jsonw_start_object(rd->jw); | |
318 | ret = rd_recv_msg(rd, link_parse_cb, rd, seq); | |
319 | if (rd->json_output) | |
320 | jsonw_end_object(rd->jw); | |
321 | return ret; | |
da990ab4 LR |
322 | } |
323 | ||
324 | static int link_one_show(struct rd *rd) | |
325 | { | |
326 | const struct rd_cmd cmds[] = { | |
327 | { NULL, link_no_args}, | |
328 | { 0 } | |
329 | }; | |
330 | ||
e3dee3c8 LR |
331 | if (!rd->port_idx) |
332 | return 0; | |
333 | ||
da990ab4 LR |
334 | return rd_exec_cmd(rd, cmds, "parameter"); |
335 | } | |
336 | ||
337 | static int link_show(struct rd *rd) | |
338 | { | |
6416d1a0 | 339 | return rd_exec_link(rd, link_one_show, true); |
da990ab4 LR |
340 | } |
341 | ||
4336c582 SW |
342 | static int link_add_netdev(struct rd *rd) |
343 | { | |
344 | char *link_netdev; | |
345 | uint32_t seq; | |
346 | ||
347 | if (rd_no_arg(rd)) { | |
348 | pr_err("Please provide a net device name.\n"); | |
349 | return -EINVAL; | |
350 | } | |
351 | ||
352 | link_netdev = rd_argv(rd); | |
353 | rd_prepare_msg(rd, RDMA_NLDEV_CMD_NEWLINK, &seq, | |
354 | (NLM_F_REQUEST | NLM_F_ACK)); | |
355 | mnl_attr_put_strz(rd->nlh, RDMA_NLDEV_ATTR_DEV_NAME, rd->link_name); | |
356 | mnl_attr_put_strz(rd->nlh, RDMA_NLDEV_ATTR_LINK_TYPE, rd->link_type); | |
357 | mnl_attr_put_strz(rd->nlh, RDMA_NLDEV_ATTR_NDEV_NAME, link_netdev); | |
358 | return rd_sendrecv_msg(rd, seq); | |
359 | } | |
360 | ||
361 | static int link_add_type(struct rd *rd) | |
362 | { | |
363 | const struct rd_cmd cmds[] = { | |
364 | { NULL, link_help}, | |
365 | { "netdev", link_add_netdev}, | |
366 | { 0 } | |
367 | }; | |
368 | ||
369 | if (rd_no_arg(rd)) { | |
370 | pr_err("Please provide a link type name.\n"); | |
371 | return -EINVAL; | |
372 | } | |
373 | rd->link_type = rd_argv(rd); | |
374 | rd_arg_inc(rd); | |
375 | return rd_exec_cmd(rd, cmds, "parameter"); | |
376 | } | |
377 | ||
378 | static int link_add(struct rd *rd) | |
379 | { | |
380 | const struct rd_cmd cmds[] = { | |
381 | { NULL, link_help}, | |
382 | { "type", link_add_type}, | |
383 | { 0 } | |
384 | }; | |
385 | ||
386 | if (rd_no_arg(rd)) { | |
387 | pr_err("Please provide a link name to add.\n"); | |
388 | return -EINVAL; | |
389 | } | |
390 | rd->link_name = rd_argv(rd); | |
391 | rd_arg_inc(rd); | |
392 | ||
393 | return rd_exec_cmd(rd, cmds, "parameter"); | |
394 | } | |
395 | ||
396 | static int _link_del(struct rd *rd) | |
397 | { | |
398 | uint32_t seq; | |
399 | ||
400 | if (!rd_no_arg(rd)) { | |
401 | pr_err("Unknown parameter %s\n", rd_argv(rd)); | |
402 | return -EINVAL; | |
403 | } | |
404 | rd_prepare_msg(rd, RDMA_NLDEV_CMD_DELLINK, &seq, | |
405 | (NLM_F_REQUEST | NLM_F_ACK)); | |
406 | mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx); | |
407 | return rd_sendrecv_msg(rd, seq); | |
408 | } | |
409 | ||
410 | static int link_del(struct rd *rd) | |
411 | { | |
412 | return rd_exec_require_dev(rd, _link_del); | |
413 | } | |
414 | ||
da990ab4 LR |
415 | int cmd_link(struct rd *rd) |
416 | { | |
417 | const struct rd_cmd cmds[] = { | |
418 | { NULL, link_show }, | |
4336c582 SW |
419 | { "add", link_add }, |
420 | { "delete", link_del }, | |
da990ab4 LR |
421 | { "show", link_show }, |
422 | { "list", link_show }, | |
423 | { "help", link_help }, | |
424 | { 0 } | |
425 | }; | |
426 | ||
427 | return rd_exec_cmd(rd, cmds, "link command"); | |
428 | } |