]>
Commit | Line | Data |
---|---|---|
74bd75c2 LR |
1 | /* |
2 | * utils.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" | |
6416d1a0 | 13 | #include <ctype.h> |
74bd75c2 LR |
14 | |
15 | static int rd_argc(struct rd *rd) | |
16 | { | |
17 | return rd->argc; | |
18 | } | |
19 | ||
40df8263 | 20 | char *rd_argv(struct rd *rd) |
74bd75c2 LR |
21 | { |
22 | if (!rd_argc(rd)) | |
23 | return NULL; | |
24 | return *rd->argv; | |
25 | } | |
26 | ||
27 | static int strcmpx(const char *str1, const char *str2) | |
28 | { | |
29 | if (strlen(str1) > strlen(str2)) | |
30 | return -1; | |
31 | return strncmp(str1, str2, strlen(str1)); | |
32 | } | |
33 | ||
34 | static bool rd_argv_match(struct rd *rd, const char *pattern) | |
35 | { | |
36 | if (!rd_argc(rd)) | |
37 | return false; | |
38 | return strcmpx(rd_argv(rd), pattern) == 0; | |
39 | } | |
40 | ||
41 | void rd_arg_inc(struct rd *rd) | |
42 | { | |
43 | if (!rd_argc(rd)) | |
44 | return; | |
45 | rd->argc--; | |
46 | rd->argv++; | |
47 | } | |
48 | ||
49 | bool rd_no_arg(struct rd *rd) | |
50 | { | |
51 | return rd_argc(rd) == 0; | |
52 | } | |
53 | ||
6416d1a0 LR |
54 | /* |
55 | * Possible input:output | |
56 | * dev/port | first port | is_dump_all | |
57 | * mlx5_1 | 0 | true | |
58 | * mlx5_1/ | 0 | true | |
59 | * mlx5_1/0 | 0 | false | |
60 | * mlx5_1/1 | 1 | false | |
61 | * mlx5_1/- | 0 | false | |
62 | * | |
63 | * In strict mode, /- will return error. | |
64 | */ | |
65 | static int get_port_from_argv(struct rd *rd, uint32_t *port, | |
66 | bool *is_dump_all, bool strict_port) | |
40df8263 LR |
67 | { |
68 | char *slash; | |
69 | ||
6416d1a0 LR |
70 | *port = 0; |
71 | *is_dump_all = true; | |
72 | ||
40df8263 LR |
73 | slash = strchr(rd_argv(rd), '/'); |
74 | /* if no port found, return 0 */ | |
6416d1a0 LR |
75 | if (slash++) { |
76 | if (*slash == '-') { | |
77 | if (strict_port) | |
78 | return -EINVAL; | |
79 | *is_dump_all = false; | |
80 | return 0; | |
81 | } | |
82 | ||
83 | if (isdigit(*slash)) { | |
84 | *is_dump_all = false; | |
85 | *port = atoi(slash); | |
86 | } | |
87 | if (!*port && strlen(slash)) | |
88 | return -EINVAL; | |
89 | } | |
90 | return 0; | |
40df8263 LR |
91 | } |
92 | ||
74bd75c2 LR |
93 | static struct dev_map *dev_map_alloc(const char *dev_name) |
94 | { | |
95 | struct dev_map *dev_map; | |
96 | ||
97 | dev_map = calloc(1, sizeof(*dev_map)); | |
98 | if (!dev_map) | |
99 | return NULL; | |
100 | dev_map->dev_name = strdup(dev_name); | |
101 | ||
102 | return dev_map; | |
103 | } | |
104 | ||
74bd75c2 LR |
105 | static void dev_map_cleanup(struct rd *rd) |
106 | { | |
107 | struct dev_map *dev_map, *tmp; | |
108 | ||
109 | list_for_each_entry_safe(dev_map, tmp, | |
110 | &rd->dev_map_list, list) { | |
111 | list_del(&dev_map->list); | |
b1e6bc43 LR |
112 | free(dev_map->dev_name); |
113 | free(dev_map); | |
74bd75c2 LR |
114 | } |
115 | } | |
116 | ||
117 | static const enum mnl_attr_data_type nldev_policy[RDMA_NLDEV_ATTR_MAX] = { | |
40df8263 | 118 | [RDMA_NLDEV_ATTR_DEV_INDEX] = MNL_TYPE_U32, |
74bd75c2 LR |
119 | [RDMA_NLDEV_ATTR_DEV_NAME] = MNL_TYPE_NUL_STRING, |
120 | [RDMA_NLDEV_ATTR_PORT_INDEX] = MNL_TYPE_U32, | |
40df8263 LR |
121 | [RDMA_NLDEV_ATTR_CAP_FLAGS] = MNL_TYPE_U64, |
122 | [RDMA_NLDEV_ATTR_FW_VERSION] = MNL_TYPE_NUL_STRING, | |
123 | [RDMA_NLDEV_ATTR_NODE_GUID] = MNL_TYPE_U64, | |
124 | [RDMA_NLDEV_ATTR_SYS_IMAGE_GUID] = MNL_TYPE_U64, | |
da990ab4 LR |
125 | [RDMA_NLDEV_ATTR_LID] = MNL_TYPE_U32, |
126 | [RDMA_NLDEV_ATTR_SM_LID] = MNL_TYPE_U32, | |
127 | [RDMA_NLDEV_ATTR_LMC] = MNL_TYPE_U8, | |
128 | [RDMA_NLDEV_ATTR_PORT_STATE] = MNL_TYPE_U8, | |
129 | [RDMA_NLDEV_ATTR_PORT_PHYS_STATE] = MNL_TYPE_U8, | |
40df8263 | 130 | [RDMA_NLDEV_ATTR_DEV_NODE_TYPE] = MNL_TYPE_U8, |
74bd75c2 LR |
131 | }; |
132 | ||
133 | int rd_attr_cb(const struct nlattr *attr, void *data) | |
134 | { | |
135 | const struct nlattr **tb = data; | |
136 | int type; | |
137 | ||
138 | if (mnl_attr_type_valid(attr, RDMA_NLDEV_ATTR_MAX) < 0) | |
139 | return MNL_CB_ERROR; | |
140 | ||
141 | type = mnl_attr_get_type(attr); | |
142 | ||
143 | if (mnl_attr_validate(attr, nldev_policy[type]) < 0) | |
144 | return MNL_CB_ERROR; | |
145 | ||
146 | tb[type] = attr; | |
147 | return MNL_CB_OK; | |
148 | } | |
149 | ||
150 | int rd_dev_init_cb(const struct nlmsghdr *nlh, void *data) | |
151 | { | |
152 | struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; | |
153 | struct dev_map *dev_map; | |
154 | struct rd *rd = data; | |
155 | const char *dev_name; | |
156 | ||
157 | mnl_attr_parse(nlh, 0, rd_attr_cb, tb); | |
158 | if (!tb[RDMA_NLDEV_ATTR_DEV_NAME] || !tb[RDMA_NLDEV_ATTR_DEV_INDEX]) | |
159 | return MNL_CB_ERROR; | |
160 | if (!tb[RDMA_NLDEV_ATTR_PORT_INDEX]) { | |
161 | pr_err("This tool doesn't support switches yet\n"); | |
162 | return MNL_CB_ERROR; | |
163 | } | |
164 | ||
165 | dev_name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); | |
166 | ||
167 | dev_map = dev_map_alloc(dev_name); | |
168 | if (!dev_map) | |
169 | /* The main function will cleanup the allocations */ | |
170 | return MNL_CB_ERROR; | |
171 | list_add_tail(&dev_map->list, &rd->dev_map_list); | |
172 | ||
173 | dev_map->num_ports = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]); | |
174 | dev_map->idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); | |
175 | return MNL_CB_OK; | |
176 | } | |
177 | ||
5fc17280 | 178 | void rd_free(struct rd *rd) |
74bd75c2 LR |
179 | { |
180 | if (!rd) | |
181 | return; | |
5fc17280 | 182 | free(rd->buff); |
74bd75c2 LR |
183 | dev_map_cleanup(rd); |
184 | } | |
185 | ||
6416d1a0 | 186 | int rd_exec_link(struct rd *rd, int (*cb)(struct rd *rd), bool strict_port) |
874c734c LR |
187 | { |
188 | struct dev_map *dev_map; | |
189 | uint32_t port; | |
190 | int ret = 0; | |
191 | ||
192 | if (rd->json_output) | |
193 | jsonw_start_array(rd->jw); | |
194 | if (rd_no_arg(rd)) { | |
195 | list_for_each_entry(dev_map, &rd->dev_map_list, list) { | |
196 | rd->dev_idx = dev_map->idx; | |
6416d1a0 LR |
197 | port = (strict_port) ? 1 : 0; |
198 | for (; port < dev_map->num_ports + 1; port++) { | |
874c734c LR |
199 | rd->port_idx = port; |
200 | ret = cb(rd); | |
201 | if (ret) | |
202 | goto out; | |
203 | } | |
204 | } | |
205 | ||
206 | } else { | |
6416d1a0 LR |
207 | bool is_dump_all; |
208 | ||
874c734c | 209 | dev_map = dev_map_lookup(rd, true); |
6416d1a0 LR |
210 | ret = get_port_from_argv(rd, &port, &is_dump_all, strict_port); |
211 | if (!dev_map || port > dev_map->num_ports || (!port && ret)) { | |
874c734c LR |
212 | pr_err("Wrong device name\n"); |
213 | ret = -ENOENT; | |
214 | goto out; | |
215 | } | |
216 | rd_arg_inc(rd); | |
217 | rd->dev_idx = dev_map->idx; | |
6416d1a0 | 218 | rd->port_idx = port; |
874c734c LR |
219 | for (; rd->port_idx < dev_map->num_ports + 1; rd->port_idx++) { |
220 | ret = cb(rd); | |
221 | if (ret) | |
222 | goto out; | |
6416d1a0 | 223 | if (!is_dump_all) |
874c734c LR |
224 | /* |
225 | * We got request to show link for devname | |
226 | * with port index. | |
227 | */ | |
228 | break; | |
229 | } | |
230 | } | |
231 | ||
232 | out: | |
233 | if (rd->json_output) | |
234 | jsonw_end_array(rd->jw); | |
235 | return ret; | |
236 | } | |
237 | ||
8b92a2c9 LR |
238 | int rd_exec_dev(struct rd *rd, int (*cb)(struct rd *rd)) |
239 | { | |
240 | struct dev_map *dev_map; | |
241 | int ret = 0; | |
242 | ||
243 | if (rd->json_output) | |
244 | jsonw_start_array(rd->jw); | |
245 | if (rd_no_arg(rd)) { | |
246 | list_for_each_entry(dev_map, &rd->dev_map_list, list) { | |
247 | rd->dev_idx = dev_map->idx; | |
248 | ret = cb(rd); | |
249 | if (ret) | |
250 | goto out; | |
251 | } | |
252 | } else { | |
253 | dev_map = dev_map_lookup(rd, false); | |
254 | if (!dev_map) { | |
b5564441 | 255 | pr_err("Wrong device name - %s\n", rd_argv(rd)); |
8b92a2c9 LR |
256 | ret = -ENOENT; |
257 | goto out; | |
258 | } | |
259 | rd_arg_inc(rd); | |
260 | rd->dev_idx = dev_map->idx; | |
261 | ret = cb(rd); | |
262 | } | |
263 | out: | |
264 | if (rd->json_output) | |
265 | jsonw_end_array(rd->jw); | |
266 | return ret; | |
267 | } | |
268 | ||
74bd75c2 LR |
269 | int rd_exec_cmd(struct rd *rd, const struct rd_cmd *cmds, const char *str) |
270 | { | |
271 | const struct rd_cmd *c; | |
272 | ||
273 | /* First argument in objs table is default variant */ | |
274 | if (rd_no_arg(rd)) | |
275 | return cmds->func(rd); | |
276 | ||
277 | for (c = cmds + 1; c->cmd; ++c) { | |
278 | if (rd_argv_match(rd, c->cmd)) { | |
279 | /* Move to next argument */ | |
280 | rd_arg_inc(rd); | |
281 | return c->func(rd); | |
282 | } | |
283 | } | |
284 | ||
285 | pr_err("Unknown %s '%s'.\n", str, rd_argv(rd)); | |
286 | return 0; | |
287 | } | |
288 | ||
289 | void rd_prepare_msg(struct rd *rd, uint32_t cmd, uint32_t *seq, uint16_t flags) | |
290 | { | |
291 | *seq = time(NULL); | |
292 | ||
293 | rd->nlh = mnl_nlmsg_put_header(rd->buff); | |
294 | rd->nlh->nlmsg_type = RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, cmd); | |
295 | rd->nlh->nlmsg_seq = *seq; | |
296 | rd->nlh->nlmsg_flags = flags; | |
297 | } | |
298 | ||
299 | int rd_send_msg(struct rd *rd) | |
300 | { | |
301 | int ret; | |
302 | ||
303 | rd->nl = mnl_socket_open(NETLINK_RDMA); | |
304 | if (!rd->nl) { | |
305 | pr_err("Failed to open NETLINK_RDMA socket\n"); | |
306 | return -ENODEV; | |
307 | } | |
308 | ||
309 | ret = mnl_socket_bind(rd->nl, 0, MNL_SOCKET_AUTOPID); | |
310 | if (ret < 0) { | |
311 | pr_err("Failed to bind socket with err %d\n", ret); | |
312 | goto err; | |
313 | } | |
314 | ||
315 | ret = mnl_socket_sendto(rd->nl, rd->nlh, rd->nlh->nlmsg_len); | |
316 | if (ret < 0) { | |
317 | pr_err("Failed to send to socket with err %d\n", ret); | |
318 | goto err; | |
319 | } | |
320 | return 0; | |
321 | ||
322 | err: | |
323 | mnl_socket_close(rd->nl); | |
324 | return ret; | |
325 | } | |
326 | ||
327 | int rd_recv_msg(struct rd *rd, mnl_cb_t callback, void *data, unsigned int seq) | |
328 | { | |
329 | int ret; | |
330 | unsigned int portid; | |
331 | char buf[MNL_SOCKET_BUFFER_SIZE]; | |
332 | ||
333 | portid = mnl_socket_get_portid(rd->nl); | |
334 | do { | |
335 | ret = mnl_socket_recvfrom(rd->nl, buf, sizeof(buf)); | |
336 | if (ret <= 0) | |
337 | break; | |
338 | ||
339 | ret = mnl_cb_run(buf, ret, seq, portid, callback, data); | |
340 | } while (ret > 0); | |
341 | ||
342 | mnl_socket_close(rd->nl); | |
343 | return ret; | |
344 | } | |
40df8263 | 345 | |
0fc8c30b | 346 | static struct dev_map *_dev_map_lookup(struct rd *rd, const char *dev_name) |
40df8263 LR |
347 | { |
348 | struct dev_map *dev_map; | |
349 | ||
350 | list_for_each_entry(dev_map, &rd->dev_map_list, list) | |
351 | if (strcmp(dev_name, dev_map->dev_name) == 0) | |
352 | return dev_map; | |
353 | ||
354 | return NULL; | |
355 | } | |
356 | ||
357 | struct dev_map *dev_map_lookup(struct rd *rd, bool allow_port_index) | |
358 | { | |
359 | struct dev_map *dev_map; | |
360 | char *dev_name; | |
361 | char *slash; | |
362 | ||
99da9032 LR |
363 | if (rd_no_arg(rd)) |
364 | return NULL; | |
365 | ||
40df8263 LR |
366 | dev_name = strdup(rd_argv(rd)); |
367 | if (allow_port_index) { | |
368 | slash = strrchr(dev_name, '/'); | |
369 | if (slash) | |
370 | *slash = '\0'; | |
371 | } | |
372 | ||
373 | dev_map = _dev_map_lookup(rd, dev_name); | |
374 | free(dev_name); | |
375 | return dev_map; | |
376 | } |