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