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