]> git.proxmox.com Git - mirror_iproute2.git/blob - devlink/devlink.c
devlink: Fix help and man of "devlink health set" command
[mirror_iproute2.git] / devlink / devlink.c
1 /*
2 * devlink.c Devlink 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: Jiri Pirko <jiri@mellanox.com>
10 */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stdarg.h>
15 #include <string.h>
16 #include <stdbool.h>
17 #include <unistd.h>
18 #include <getopt.h>
19 #include <limits.h>
20 #include <errno.h>
21 #include <inttypes.h>
22 #include <sys/sysinfo.h>
23 #define _LINUX_SYSINFO_H /* avoid collision with musl header */
24 #include <linux/genetlink.h>
25 #include <linux/devlink.h>
26 #include <libmnl/libmnl.h>
27 #include <netinet/ether.h>
28 #include <sys/types.h>
29
30 #include "SNAPSHOT.h"
31 #include "list.h"
32 #include "mnlg.h"
33 #include "json_writer.h"
34 #include "utils.h"
35 #include "namespace.h"
36
37 #define ESWITCH_MODE_LEGACY "legacy"
38 #define ESWITCH_MODE_SWITCHDEV "switchdev"
39 #define ESWITCH_INLINE_MODE_NONE "none"
40 #define ESWITCH_INLINE_MODE_LINK "link"
41 #define ESWITCH_INLINE_MODE_NETWORK "network"
42 #define ESWITCH_INLINE_MODE_TRANSPORT "transport"
43
44 #define PARAM_CMODE_RUNTIME_STR "runtime"
45 #define PARAM_CMODE_DRIVERINIT_STR "driverinit"
46 #define PARAM_CMODE_PERMANENT_STR "permanent"
47 #define DL_ARGS_REQUIRED_MAX_ERR_LEN 80
48
49 #define HEALTH_REPORTER_STATE_HEALTHY_STR "healthy"
50 #define HEALTH_REPORTER_STATE_ERROR_STR "error"
51 #define HEALTH_REPORTER_TIMESTAMP_FMT_LEN 80
52
53 static int g_new_line_count;
54 static int g_indent_level;
55 static bool g_indent_newline;
56
57 #define INDENT_STR_STEP 2
58 #define INDENT_STR_MAXLEN 32
59 static char g_indent_str[INDENT_STR_MAXLEN + 1] = "";
60
61 static void __attribute__((format(printf, 1, 2)))
62 pr_err(const char *fmt, ...)
63 {
64 va_list ap;
65
66 va_start(ap, fmt);
67 vfprintf(stderr, fmt, ap);
68 va_end(ap);
69 }
70
71 static void __attribute__((format(printf, 1, 2)))
72 pr_out(const char *fmt, ...)
73 {
74 va_list ap;
75
76 if (g_indent_newline) {
77 printf("%s", g_indent_str);
78 g_indent_newline = false;
79 }
80 va_start(ap, fmt);
81 vprintf(fmt, ap);
82 va_end(ap);
83 g_new_line_count = 0;
84 }
85
86 static void __attribute__((format(printf, 2, 3)))
87 pr_out_sp(unsigned int num, const char *fmt, ...)
88 {
89 va_list ap;
90 int ret;
91
92 va_start(ap, fmt);
93 ret = vprintf(fmt, ap);
94 va_end(ap);
95
96 if (ret < num)
97 printf("%*s", num - ret, "");
98 g_new_line_count = 0; \
99 }
100
101 static void __attribute__((format(printf, 1, 2)))
102 pr_out_tty(const char *fmt, ...)
103 {
104 va_list ap;
105
106 if (!isatty(STDOUT_FILENO))
107 return;
108 va_start(ap, fmt);
109 vprintf(fmt, ap);
110 va_end(ap);
111 }
112
113 static void __pr_out_indent_inc(void)
114 {
115 if (g_indent_level + INDENT_STR_STEP > INDENT_STR_MAXLEN)
116 return;
117 g_indent_level += INDENT_STR_STEP;
118 memset(g_indent_str, ' ', sizeof(g_indent_str));
119 g_indent_str[g_indent_level] = '\0';
120 }
121
122 static void __pr_out_indent_dec(void)
123 {
124 if (g_indent_level - INDENT_STR_STEP < 0)
125 return;
126 g_indent_level -= INDENT_STR_STEP;
127 g_indent_str[g_indent_level] = '\0';
128 }
129
130 static void __pr_out_newline(void)
131 {
132 if (g_new_line_count < 1) {
133 pr_out("\n");
134 g_indent_newline = true;
135 }
136 g_new_line_count++;
137 }
138
139 static int _mnlg_socket_recv_run(struct mnlg_socket *nlg,
140 mnl_cb_t data_cb, void *data)
141 {
142 int err;
143
144 err = mnlg_socket_recv_run(nlg, data_cb, data);
145 if (err < 0) {
146 pr_err("devlink answers: %s\n", strerror(errno));
147 return -errno;
148 }
149 return 0;
150 }
151
152 static int _mnlg_socket_send(struct mnlg_socket *nlg,
153 const struct nlmsghdr *nlh)
154 {
155 int err;
156
157 err = mnlg_socket_send(nlg, nlh);
158 if (err < 0) {
159 pr_err("Failed to call mnlg_socket_send\n");
160 return -errno;
161 }
162 return 0;
163 }
164
165 static int _mnlg_socket_sndrcv(struct mnlg_socket *nlg,
166 const struct nlmsghdr *nlh,
167 mnl_cb_t data_cb, void *data)
168 {
169 int err;
170
171 err = _mnlg_socket_send(nlg, nlh);
172 if (err)
173 return err;
174 return _mnlg_socket_recv_run(nlg, data_cb, data);
175 }
176
177 static int _mnlg_socket_group_add(struct mnlg_socket *nlg,
178 const char *group_name)
179 {
180 int err;
181
182 err = mnlg_socket_group_add(nlg, group_name);
183 if (err < 0) {
184 pr_err("Failed to call mnlg_socket_group_add\n");
185 return -errno;
186 }
187 return 0;
188 }
189
190 struct ifname_map {
191 struct list_head list;
192 char *bus_name;
193 char *dev_name;
194 uint32_t port_index;
195 char *ifname;
196 };
197
198 static struct ifname_map *ifname_map_alloc(const char *bus_name,
199 const char *dev_name,
200 uint32_t port_index,
201 const char *ifname)
202 {
203 struct ifname_map *ifname_map;
204
205 ifname_map = calloc(1, sizeof(*ifname_map));
206 if (!ifname_map)
207 return NULL;
208 ifname_map->bus_name = strdup(bus_name);
209 ifname_map->dev_name = strdup(dev_name);
210 ifname_map->port_index = port_index;
211 ifname_map->ifname = strdup(ifname);
212 if (!ifname_map->bus_name || !ifname_map->dev_name ||
213 !ifname_map->ifname) {
214 free(ifname_map->ifname);
215 free(ifname_map->dev_name);
216 free(ifname_map->bus_name);
217 free(ifname_map);
218 return NULL;
219 }
220 return ifname_map;
221 }
222
223 static void ifname_map_free(struct ifname_map *ifname_map)
224 {
225 free(ifname_map->ifname);
226 free(ifname_map->dev_name);
227 free(ifname_map->bus_name);
228 free(ifname_map);
229 }
230
231 #define DL_OPT_HANDLE BIT(0)
232 #define DL_OPT_HANDLEP BIT(1)
233 #define DL_OPT_PORT_TYPE BIT(2)
234 #define DL_OPT_PORT_COUNT BIT(3)
235 #define DL_OPT_SB BIT(4)
236 #define DL_OPT_SB_POOL BIT(5)
237 #define DL_OPT_SB_SIZE BIT(6)
238 #define DL_OPT_SB_TYPE BIT(7)
239 #define DL_OPT_SB_THTYPE BIT(8)
240 #define DL_OPT_SB_TH BIT(9)
241 #define DL_OPT_SB_TC BIT(10)
242 #define DL_OPT_ESWITCH_MODE BIT(11)
243 #define DL_OPT_ESWITCH_INLINE_MODE BIT(12)
244 #define DL_OPT_DPIPE_TABLE_NAME BIT(13)
245 #define DL_OPT_DPIPE_TABLE_COUNTERS BIT(14)
246 #define DL_OPT_ESWITCH_ENCAP_MODE BIT(15)
247 #define DL_OPT_RESOURCE_PATH BIT(16)
248 #define DL_OPT_RESOURCE_SIZE BIT(17)
249 #define DL_OPT_PARAM_NAME BIT(18)
250 #define DL_OPT_PARAM_VALUE BIT(19)
251 #define DL_OPT_PARAM_CMODE BIT(20)
252 #define DL_OPT_HANDLE_REGION BIT(21)
253 #define DL_OPT_REGION_SNAPSHOT_ID BIT(22)
254 #define DL_OPT_REGION_ADDRESS BIT(23)
255 #define DL_OPT_REGION_LENGTH BIT(24)
256 #define DL_OPT_FLASH_FILE_NAME BIT(25)
257 #define DL_OPT_FLASH_COMPONENT BIT(26)
258 #define DL_OPT_HEALTH_REPORTER_NAME BIT(27)
259 #define DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD BIT(28)
260 #define DL_OPT_HEALTH_REPORTER_AUTO_RECOVER BIT(29)
261 #define DL_OPT_TRAP_NAME BIT(30)
262 #define DL_OPT_TRAP_ACTION BIT(31)
263 #define DL_OPT_TRAP_GROUP_NAME BIT(32)
264 #define DL_OPT_NETNS BIT(33)
265 #define DL_OPT_TRAP_POLICER_ID BIT(34)
266 #define DL_OPT_TRAP_POLICER_RATE BIT(35)
267 #define DL_OPT_TRAP_POLICER_BURST BIT(36)
268
269 struct dl_opts {
270 uint64_t present; /* flags of present items */
271 char *bus_name;
272 char *dev_name;
273 uint32_t port_index;
274 enum devlink_port_type port_type;
275 uint32_t port_count;
276 uint32_t sb_index;
277 uint16_t sb_pool_index;
278 uint32_t sb_pool_size;
279 enum devlink_sb_pool_type sb_pool_type;
280 enum devlink_sb_threshold_type sb_pool_thtype;
281 uint32_t sb_threshold;
282 uint16_t sb_tc_index;
283 enum devlink_eswitch_mode eswitch_mode;
284 enum devlink_eswitch_inline_mode eswitch_inline_mode;
285 const char *dpipe_table_name;
286 bool dpipe_counters_enable;
287 bool eswitch_encap_mode;
288 const char *resource_path;
289 uint64_t resource_size;
290 uint32_t resource_id;
291 bool resource_id_valid;
292 const char *param_name;
293 const char *param_value;
294 enum devlink_param_cmode cmode;
295 char *region_name;
296 uint32_t region_snapshot_id;
297 uint64_t region_address;
298 uint64_t region_length;
299 const char *flash_file_name;
300 const char *flash_component;
301 const char *reporter_name;
302 uint64_t reporter_graceful_period;
303 bool reporter_auto_recover;
304 const char *trap_name;
305 const char *trap_group_name;
306 enum devlink_trap_action trap_action;
307 bool netns_is_pid;
308 uint32_t netns;
309 uint32_t trap_policer_id;
310 uint64_t trap_policer_rate;
311 uint64_t trap_policer_burst;
312 };
313
314 struct dl {
315 struct mnlg_socket *nlg;
316 struct list_head ifname_map_list;
317 int argc;
318 char **argv;
319 bool no_nice_names;
320 struct dl_opts opts;
321 json_writer_t *jw;
322 bool json_output;
323 bool pretty_output;
324 bool verbose;
325 bool stats;
326 struct {
327 bool present;
328 char *bus_name;
329 char *dev_name;
330 uint32_t port_index;
331 } arr_last;
332 };
333
334 static int dl_argc(struct dl *dl)
335 {
336 return dl->argc;
337 }
338
339 static char *dl_argv(struct dl *dl)
340 {
341 if (dl_argc(dl) == 0)
342 return NULL;
343 return *dl->argv;
344 }
345
346 static void dl_arg_inc(struct dl *dl)
347 {
348 if (dl_argc(dl) == 0)
349 return;
350 dl->argc--;
351 dl->argv++;
352 }
353
354 static void dl_arg_dec(struct dl *dl)
355 {
356 dl->argc++;
357 dl->argv--;
358 }
359
360 static char *dl_argv_next(struct dl *dl)
361 {
362 char *ret;
363
364 if (dl_argc(dl) == 0)
365 return NULL;
366
367 ret = *dl->argv;
368 dl_arg_inc(dl);
369 return ret;
370 }
371
372 static char *dl_argv_index(struct dl *dl, unsigned int index)
373 {
374 if (index >= dl_argc(dl))
375 return NULL;
376 return dl->argv[index];
377 }
378
379 static int strcmpx(const char *str1, const char *str2)
380 {
381 if (strlen(str1) > strlen(str2))
382 return -1;
383 return strncmp(str1, str2, strlen(str1));
384 }
385
386 static bool dl_argv_match(struct dl *dl, const char *pattern)
387 {
388 if (dl_argc(dl) == 0)
389 return false;
390 return strcmpx(dl_argv(dl), pattern) == 0;
391 }
392
393 static bool dl_no_arg(struct dl *dl)
394 {
395 return dl_argc(dl) == 0;
396 }
397
398 static void __pr_out_indent_newline(struct dl *dl)
399 {
400 if (!g_indent_newline && !dl->json_output)
401 pr_out(" ");
402 }
403
404 static void check_indent_newline(struct dl *dl)
405 {
406 __pr_out_indent_newline(dl);
407
408 if (g_indent_newline && !is_json_context()) {
409 printf("%s", g_indent_str);
410 g_indent_newline = false;
411 }
412 g_new_line_count = 0;
413 }
414
415 static const enum mnl_attr_data_type devlink_policy[DEVLINK_ATTR_MAX + 1] = {
416 [DEVLINK_ATTR_BUS_NAME] = MNL_TYPE_NUL_STRING,
417 [DEVLINK_ATTR_DEV_NAME] = MNL_TYPE_NUL_STRING,
418 [DEVLINK_ATTR_PORT_INDEX] = MNL_TYPE_U32,
419 [DEVLINK_ATTR_PORT_TYPE] = MNL_TYPE_U16,
420 [DEVLINK_ATTR_PORT_DESIRED_TYPE] = MNL_TYPE_U16,
421 [DEVLINK_ATTR_PORT_NETDEV_IFINDEX] = MNL_TYPE_U32,
422 [DEVLINK_ATTR_PORT_NETDEV_NAME] = MNL_TYPE_NUL_STRING,
423 [DEVLINK_ATTR_PORT_IBDEV_NAME] = MNL_TYPE_NUL_STRING,
424 [DEVLINK_ATTR_SB_INDEX] = MNL_TYPE_U32,
425 [DEVLINK_ATTR_SB_SIZE] = MNL_TYPE_U32,
426 [DEVLINK_ATTR_SB_INGRESS_POOL_COUNT] = MNL_TYPE_U16,
427 [DEVLINK_ATTR_SB_EGRESS_POOL_COUNT] = MNL_TYPE_U16,
428 [DEVLINK_ATTR_SB_INGRESS_TC_COUNT] = MNL_TYPE_U16,
429 [DEVLINK_ATTR_SB_EGRESS_TC_COUNT] = MNL_TYPE_U16,
430 [DEVLINK_ATTR_SB_POOL_INDEX] = MNL_TYPE_U16,
431 [DEVLINK_ATTR_SB_POOL_TYPE] = MNL_TYPE_U8,
432 [DEVLINK_ATTR_SB_POOL_SIZE] = MNL_TYPE_U32,
433 [DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE] = MNL_TYPE_U8,
434 [DEVLINK_ATTR_SB_THRESHOLD] = MNL_TYPE_U32,
435 [DEVLINK_ATTR_SB_TC_INDEX] = MNL_TYPE_U16,
436 [DEVLINK_ATTR_SB_OCC_CUR] = MNL_TYPE_U32,
437 [DEVLINK_ATTR_SB_OCC_MAX] = MNL_TYPE_U32,
438 [DEVLINK_ATTR_ESWITCH_MODE] = MNL_TYPE_U16,
439 [DEVLINK_ATTR_ESWITCH_INLINE_MODE] = MNL_TYPE_U8,
440 [DEVLINK_ATTR_ESWITCH_ENCAP_MODE] = MNL_TYPE_U8,
441 [DEVLINK_ATTR_DPIPE_TABLES] = MNL_TYPE_NESTED,
442 [DEVLINK_ATTR_DPIPE_TABLE] = MNL_TYPE_NESTED,
443 [DEVLINK_ATTR_DPIPE_TABLE_NAME] = MNL_TYPE_STRING,
444 [DEVLINK_ATTR_DPIPE_TABLE_SIZE] = MNL_TYPE_U64,
445 [DEVLINK_ATTR_DPIPE_TABLE_MATCHES] = MNL_TYPE_NESTED,
446 [DEVLINK_ATTR_DPIPE_TABLE_ACTIONS] = MNL_TYPE_NESTED,
447 [DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED] = MNL_TYPE_U8,
448 [DEVLINK_ATTR_DPIPE_ENTRIES] = MNL_TYPE_NESTED,
449 [DEVLINK_ATTR_DPIPE_ENTRY] = MNL_TYPE_NESTED,
450 [DEVLINK_ATTR_DPIPE_ENTRY_INDEX] = MNL_TYPE_U64,
451 [DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES] = MNL_TYPE_NESTED,
452 [DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES] = MNL_TYPE_NESTED,
453 [DEVLINK_ATTR_DPIPE_ENTRY_COUNTER] = MNL_TYPE_U64,
454 [DEVLINK_ATTR_DPIPE_MATCH] = MNL_TYPE_NESTED,
455 [DEVLINK_ATTR_DPIPE_MATCH_VALUE] = MNL_TYPE_NESTED,
456 [DEVLINK_ATTR_DPIPE_MATCH_TYPE] = MNL_TYPE_U32,
457 [DEVLINK_ATTR_DPIPE_ACTION] = MNL_TYPE_NESTED,
458 [DEVLINK_ATTR_DPIPE_ACTION_VALUE] = MNL_TYPE_NESTED,
459 [DEVLINK_ATTR_DPIPE_ACTION_TYPE] = MNL_TYPE_U32,
460 [DEVLINK_ATTR_DPIPE_VALUE_MAPPING] = MNL_TYPE_U32,
461 [DEVLINK_ATTR_DPIPE_HEADERS] = MNL_TYPE_NESTED,
462 [DEVLINK_ATTR_DPIPE_HEADER] = MNL_TYPE_NESTED,
463 [DEVLINK_ATTR_DPIPE_HEADER_NAME] = MNL_TYPE_STRING,
464 [DEVLINK_ATTR_DPIPE_HEADER_ID] = MNL_TYPE_U32,
465 [DEVLINK_ATTR_DPIPE_HEADER_FIELDS] = MNL_TYPE_NESTED,
466 [DEVLINK_ATTR_DPIPE_HEADER_GLOBAL] = MNL_TYPE_U8,
467 [DEVLINK_ATTR_DPIPE_HEADER_INDEX] = MNL_TYPE_U32,
468 [DEVLINK_ATTR_DPIPE_FIELD] = MNL_TYPE_NESTED,
469 [DEVLINK_ATTR_DPIPE_FIELD_NAME] = MNL_TYPE_STRING,
470 [DEVLINK_ATTR_DPIPE_FIELD_ID] = MNL_TYPE_U32,
471 [DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH] = MNL_TYPE_U32,
472 [DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE] = MNL_TYPE_U32,
473 [DEVLINK_ATTR_PARAM] = MNL_TYPE_NESTED,
474 [DEVLINK_ATTR_PARAM_NAME] = MNL_TYPE_STRING,
475 [DEVLINK_ATTR_PARAM_TYPE] = MNL_TYPE_U8,
476 [DEVLINK_ATTR_PARAM_VALUES_LIST] = MNL_TYPE_NESTED,
477 [DEVLINK_ATTR_PARAM_VALUE] = MNL_TYPE_NESTED,
478 [DEVLINK_ATTR_PARAM_VALUE_CMODE] = MNL_TYPE_U8,
479 [DEVLINK_ATTR_REGION_NAME] = MNL_TYPE_STRING,
480 [DEVLINK_ATTR_REGION_SIZE] = MNL_TYPE_U64,
481 [DEVLINK_ATTR_REGION_SNAPSHOTS] = MNL_TYPE_NESTED,
482 [DEVLINK_ATTR_REGION_SNAPSHOT] = MNL_TYPE_NESTED,
483 [DEVLINK_ATTR_REGION_SNAPSHOT_ID] = MNL_TYPE_U32,
484 [DEVLINK_ATTR_REGION_CHUNKS] = MNL_TYPE_NESTED,
485 [DEVLINK_ATTR_REGION_CHUNK] = MNL_TYPE_NESTED,
486 [DEVLINK_ATTR_REGION_CHUNK_DATA] = MNL_TYPE_BINARY,
487 [DEVLINK_ATTR_REGION_CHUNK_ADDR] = MNL_TYPE_U64,
488 [DEVLINK_ATTR_REGION_CHUNK_LEN] = MNL_TYPE_U64,
489 [DEVLINK_ATTR_INFO_DRIVER_NAME] = MNL_TYPE_STRING,
490 [DEVLINK_ATTR_INFO_SERIAL_NUMBER] = MNL_TYPE_STRING,
491 [DEVLINK_ATTR_INFO_VERSION_FIXED] = MNL_TYPE_NESTED,
492 [DEVLINK_ATTR_INFO_VERSION_RUNNING] = MNL_TYPE_NESTED,
493 [DEVLINK_ATTR_INFO_VERSION_STORED] = MNL_TYPE_NESTED,
494 [DEVLINK_ATTR_INFO_VERSION_NAME] = MNL_TYPE_STRING,
495 [DEVLINK_ATTR_INFO_VERSION_VALUE] = MNL_TYPE_STRING,
496 [DEVLINK_ATTR_HEALTH_REPORTER] = MNL_TYPE_NESTED,
497 [DEVLINK_ATTR_HEALTH_REPORTER_NAME] = MNL_TYPE_STRING,
498 [DEVLINK_ATTR_HEALTH_REPORTER_STATE] = MNL_TYPE_U8,
499 [DEVLINK_ATTR_HEALTH_REPORTER_ERR_COUNT] = MNL_TYPE_U64,
500 [DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT] = MNL_TYPE_U64,
501 [DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS] = MNL_TYPE_U64,
502 [DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] = MNL_TYPE_U64,
503 [DEVLINK_ATTR_FLASH_UPDATE_COMPONENT] = MNL_TYPE_STRING,
504 [DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG] = MNL_TYPE_STRING,
505 [DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE] = MNL_TYPE_U64,
506 [DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL] = MNL_TYPE_U64,
507 [DEVLINK_ATTR_STATS] = MNL_TYPE_NESTED,
508 [DEVLINK_ATTR_TRAP_NAME] = MNL_TYPE_STRING,
509 [DEVLINK_ATTR_TRAP_ACTION] = MNL_TYPE_U8,
510 [DEVLINK_ATTR_TRAP_TYPE] = MNL_TYPE_U8,
511 [DEVLINK_ATTR_TRAP_GENERIC] = MNL_TYPE_FLAG,
512 [DEVLINK_ATTR_TRAP_METADATA] = MNL_TYPE_NESTED,
513 [DEVLINK_ATTR_TRAP_GROUP_NAME] = MNL_TYPE_STRING,
514 [DEVLINK_ATTR_RELOAD_FAILED] = MNL_TYPE_U8,
515 [DEVLINK_ATTR_TRAP_POLICER_ID] = MNL_TYPE_U32,
516 [DEVLINK_ATTR_TRAP_POLICER_RATE] = MNL_TYPE_U64,
517 [DEVLINK_ATTR_TRAP_POLICER_BURST] = MNL_TYPE_U64,
518 };
519
520 static const enum mnl_attr_data_type
521 devlink_stats_policy[DEVLINK_ATTR_STATS_MAX + 1] = {
522 [DEVLINK_ATTR_STATS_RX_PACKETS] = MNL_TYPE_U64,
523 [DEVLINK_ATTR_STATS_RX_BYTES] = MNL_TYPE_U64,
524 [DEVLINK_ATTR_STATS_RX_DROPPED] = MNL_TYPE_U64,
525 };
526
527 static int attr_cb(const struct nlattr *attr, void *data)
528 {
529 const struct nlattr **tb = data;
530 int type;
531
532 if (mnl_attr_type_valid(attr, DEVLINK_ATTR_MAX) < 0)
533 return MNL_CB_OK;
534
535 type = mnl_attr_get_type(attr);
536 if (mnl_attr_validate(attr, devlink_policy[type]) < 0)
537 return MNL_CB_ERROR;
538
539 tb[type] = attr;
540 return MNL_CB_OK;
541 }
542
543 static int attr_stats_cb(const struct nlattr *attr, void *data)
544 {
545 const struct nlattr **tb = data;
546 int type;
547
548 /* Allow the tool to work on top of newer kernels that might contain
549 * more attributes.
550 */
551 if (mnl_attr_type_valid(attr, DEVLINK_ATTR_STATS_MAX) < 0)
552 return MNL_CB_OK;
553
554 type = mnl_attr_get_type(attr);
555 if (mnl_attr_validate(attr, devlink_stats_policy[type]) < 0)
556 return MNL_CB_ERROR;
557
558 tb[type] = attr;
559 return MNL_CB_OK;
560 }
561
562 static int ifname_map_cb(const struct nlmsghdr *nlh, void *data)
563 {
564 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
565 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
566 struct dl *dl = data;
567 struct ifname_map *ifname_map;
568 const char *bus_name;
569 const char *dev_name;
570 uint32_t port_ifindex;
571 const char *port_ifname;
572
573 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
574 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
575 !tb[DEVLINK_ATTR_PORT_INDEX])
576 return MNL_CB_ERROR;
577
578 if (!tb[DEVLINK_ATTR_PORT_NETDEV_NAME])
579 return MNL_CB_OK;
580
581 bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]);
582 dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]);
583 port_ifindex = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX]);
584 port_ifname = mnl_attr_get_str(tb[DEVLINK_ATTR_PORT_NETDEV_NAME]);
585 ifname_map = ifname_map_alloc(bus_name, dev_name,
586 port_ifindex, port_ifname);
587 if (!ifname_map)
588 return MNL_CB_ERROR;
589 list_add(&ifname_map->list, &dl->ifname_map_list);
590
591 return MNL_CB_OK;
592 }
593
594 static void ifname_map_fini(struct dl *dl)
595 {
596 struct ifname_map *ifname_map, *tmp;
597
598 list_for_each_entry_safe(ifname_map, tmp,
599 &dl->ifname_map_list, list) {
600 list_del(&ifname_map->list);
601 ifname_map_free(ifname_map);
602 }
603 }
604
605 static int ifname_map_init(struct dl *dl)
606 {
607 struct nlmsghdr *nlh;
608 int err;
609
610 INIT_LIST_HEAD(&dl->ifname_map_list);
611
612 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_GET,
613 NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
614
615 err = _mnlg_socket_sndrcv(dl->nlg, nlh, ifname_map_cb, dl);
616 if (err) {
617 ifname_map_fini(dl);
618 return err;
619 }
620 return 0;
621 }
622
623 static int ifname_map_lookup(struct dl *dl, const char *ifname,
624 char **p_bus_name, char **p_dev_name,
625 uint32_t *p_port_index)
626 {
627 struct ifname_map *ifname_map;
628
629 list_for_each_entry(ifname_map, &dl->ifname_map_list, list) {
630 if (strcmp(ifname, ifname_map->ifname) == 0) {
631 *p_bus_name = ifname_map->bus_name;
632 *p_dev_name = ifname_map->dev_name;
633 *p_port_index = ifname_map->port_index;
634 return 0;
635 }
636 }
637 return -ENOENT;
638 }
639
640 static int ifname_map_rev_lookup(struct dl *dl, const char *bus_name,
641 const char *dev_name, uint32_t port_index,
642 char **p_ifname)
643 {
644 struct ifname_map *ifname_map;
645
646 list_for_each_entry(ifname_map, &dl->ifname_map_list, list) {
647 if (strcmp(bus_name, ifname_map->bus_name) == 0 &&
648 strcmp(dev_name, ifname_map->dev_name) == 0 &&
649 port_index == ifname_map->port_index) {
650 *p_ifname = ifname_map->ifname;
651 return 0;
652 }
653 }
654 return -ENOENT;
655 }
656
657 static unsigned int strslashcount(char *str)
658 {
659 unsigned int count = 0;
660 char *pos = str;
661
662 while ((pos = strchr(pos, '/'))) {
663 count++;
664 pos++;
665 }
666 return count;
667 }
668
669 static int strslashrsplit(char *str, char **before, char **after)
670 {
671 char *slash;
672
673 slash = strrchr(str, '/');
674 if (!slash)
675 return -EINVAL;
676 *slash = '\0';
677 *before = str;
678 *after = slash + 1;
679 return 0;
680 }
681
682 static int strtouint64_t(const char *str, uint64_t *p_val)
683 {
684 char *endptr;
685 unsigned long long int val;
686
687 val = strtoull(str, &endptr, 10);
688 if (endptr == str || *endptr != '\0')
689 return -EINVAL;
690 if (val > ULONG_MAX)
691 return -ERANGE;
692 *p_val = val;
693 return 0;
694 }
695
696 static int strtouint32_t(const char *str, uint32_t *p_val)
697 {
698 char *endptr;
699 unsigned long int val;
700
701 val = strtoul(str, &endptr, 10);
702 if (endptr == str || *endptr != '\0')
703 return -EINVAL;
704 if (val > UINT_MAX)
705 return -ERANGE;
706 *p_val = val;
707 return 0;
708 }
709
710 static int strtouint16_t(const char *str, uint16_t *p_val)
711 {
712 char *endptr;
713 unsigned long int val;
714
715 val = strtoul(str, &endptr, 10);
716 if (endptr == str || *endptr != '\0')
717 return -EINVAL;
718 if (val > USHRT_MAX)
719 return -ERANGE;
720 *p_val = val;
721 return 0;
722 }
723
724 static int strtouint8_t(const char *str, uint8_t *p_val)
725 {
726 char *endptr;
727 unsigned long int val;
728
729 val = strtoul(str, &endptr, 10);
730 if (endptr == str || *endptr != '\0')
731 return -EINVAL;
732 if (val > UCHAR_MAX)
733 return -ERANGE;
734 *p_val = val;
735 return 0;
736 }
737
738 static int strtobool(const char *str, bool *p_val)
739 {
740 bool val;
741
742 if (!strcmp(str, "true") || !strcmp(str, "1") ||
743 !strcmp(str, "enable"))
744 val = true;
745 else if (!strcmp(str, "false") || !strcmp(str, "0") ||
746 !strcmp(str, "disable"))
747 val = false;
748 else
749 return -EINVAL;
750 *p_val = val;
751 return 0;
752 }
753
754 static int __dl_argv_handle(char *str, char **p_bus_name, char **p_dev_name)
755 {
756 strslashrsplit(str, p_bus_name, p_dev_name);
757 return 0;
758 }
759
760 static int dl_argv_handle(struct dl *dl, char **p_bus_name, char **p_dev_name)
761 {
762 char *str = dl_argv_next(dl);
763
764 if (!str) {
765 pr_err("Devlink identification (\"bus_name/dev_name\") expected\n");
766 return -EINVAL;
767 }
768 if (strslashcount(str) != 1) {
769 pr_err("Wrong devlink identification string format.\n");
770 pr_err("Expected \"bus_name/dev_name\".\n");
771 return -EINVAL;
772 }
773 return __dl_argv_handle(str, p_bus_name, p_dev_name);
774 }
775
776 static int __dl_argv_handle_port(char *str,
777 char **p_bus_name, char **p_dev_name,
778 uint32_t *p_port_index)
779 {
780 char *handlestr;
781 char *portstr;
782 int err;
783
784 err = strslashrsplit(str, &handlestr, &portstr);
785 if (err) {
786 pr_err("Port identification \"%s\" is invalid\n", str);
787 return err;
788 }
789 err = strtouint32_t(portstr, p_port_index);
790 if (err) {
791 pr_err("Port index \"%s\" is not a number or not within range\n",
792 portstr);
793 return err;
794 }
795 err = strslashrsplit(handlestr, p_bus_name, p_dev_name);
796 if (err) {
797 pr_err("Port identification \"%s\" is invalid\n", str);
798 return err;
799 }
800 return 0;
801 }
802
803 static int __dl_argv_handle_port_ifname(struct dl *dl, char *str,
804 char **p_bus_name, char **p_dev_name,
805 uint32_t *p_port_index)
806 {
807 int err;
808
809 err = ifname_map_lookup(dl, str, p_bus_name, p_dev_name,
810 p_port_index);
811 if (err) {
812 pr_err("Netdevice \"%s\" not found\n", str);
813 return err;
814 }
815 return 0;
816 }
817
818 static int dl_argv_handle_port(struct dl *dl, char **p_bus_name,
819 char **p_dev_name, uint32_t *p_port_index)
820 {
821 char *str = dl_argv_next(dl);
822 unsigned int slash_count;
823
824 if (!str) {
825 pr_err("Port identification (\"bus_name/dev_name/port_index\" or \"netdev ifname\") expected.\n");
826 return -EINVAL;
827 }
828 slash_count = strslashcount(str);
829 switch (slash_count) {
830 case 0:
831 return __dl_argv_handle_port_ifname(dl, str, p_bus_name,
832 p_dev_name, p_port_index);
833 case 2:
834 return __dl_argv_handle_port(str, p_bus_name,
835 p_dev_name, p_port_index);
836 default:
837 pr_err("Wrong port identification string format.\n");
838 pr_err("Expected \"bus_name/dev_name/port_index\" or \"netdev_ifname\".\n");
839 return -EINVAL;
840 }
841 }
842
843 static int dl_argv_handle_both(struct dl *dl, char **p_bus_name,
844 char **p_dev_name, uint32_t *p_port_index,
845 uint64_t *p_handle_bit)
846 {
847 char *str = dl_argv_next(dl);
848 unsigned int slash_count;
849 int err;
850
851 if (!str) {
852 pr_err("One of following identifications expected:\n"
853 "Devlink identification (\"bus_name/dev_name\")\n"
854 "Port identification (\"bus_name/dev_name/port_index\" or \"netdev ifname\")\n");
855 return -EINVAL;
856 }
857 slash_count = strslashcount(str);
858 if (slash_count == 1) {
859 err = __dl_argv_handle(str, p_bus_name, p_dev_name);
860 if (err)
861 return err;
862 *p_handle_bit = DL_OPT_HANDLE;
863 } else if (slash_count == 2) {
864 err = __dl_argv_handle_port(str, p_bus_name,
865 p_dev_name, p_port_index);
866 if (err)
867 return err;
868 *p_handle_bit = DL_OPT_HANDLEP;
869 } else if (slash_count == 0) {
870 err = __dl_argv_handle_port_ifname(dl, str, p_bus_name,
871 p_dev_name, p_port_index);
872 if (err)
873 return err;
874 *p_handle_bit = DL_OPT_HANDLEP;
875 } else {
876 pr_err("Wrong port identification string format.\n");
877 pr_err("Expected \"bus_name/dev_name\" or \"bus_name/dev_name/port_index\" or \"netdev_ifname\".\n");
878 return -EINVAL;
879 }
880 return 0;
881 }
882
883 static int __dl_argv_handle_region(char *str, char **p_bus_name,
884 char **p_dev_name, char **p_region)
885 {
886 char *handlestr;
887 int err;
888
889 err = strslashrsplit(str, &handlestr, p_region);
890 if (err) {
891 pr_err("Region identification \"%s\" is invalid\n", str);
892 return err;
893 }
894 err = strslashrsplit(handlestr, p_bus_name, p_dev_name);
895 if (err) {
896 pr_err("Region identification \"%s\" is invalid\n", str);
897 return err;
898 }
899 return 0;
900 }
901
902 static int dl_argv_handle_region(struct dl *dl, char **p_bus_name,
903 char **p_dev_name, char **p_region)
904 {
905 char *str = dl_argv_next(dl);
906 unsigned int slash_count;
907
908 if (!str) {
909 pr_err("Expected \"bus_name/dev_name/region\" identification.\n");
910 return -EINVAL;
911 }
912
913 slash_count = strslashcount(str);
914 if (slash_count != 2) {
915 pr_err("Wrong region identification string format.\n");
916 pr_err("Expected \"bus_name/dev_name/region\" identification.\n"".\n");
917 return -EINVAL;
918 }
919
920 return __dl_argv_handle_region(str, p_bus_name, p_dev_name, p_region);
921 }
922
923 static int dl_argv_uint64_t(struct dl *dl, uint64_t *p_val)
924 {
925 char *str = dl_argv_next(dl);
926 int err;
927
928 if (!str) {
929 pr_err("Unsigned number argument expected\n");
930 return -EINVAL;
931 }
932
933 err = strtouint64_t(str, p_val);
934 if (err) {
935 pr_err("\"%s\" is not a number or not within range\n", str);
936 return err;
937 }
938 return 0;
939 }
940
941 static int dl_argv_uint32_t(struct dl *dl, uint32_t *p_val)
942 {
943 char *str = dl_argv_next(dl);
944 int err;
945
946 if (!str) {
947 pr_err("Unsigned number argument expected\n");
948 return -EINVAL;
949 }
950
951 err = strtouint32_t(str, p_val);
952 if (err) {
953 pr_err("\"%s\" is not a number or not within range\n", str);
954 return err;
955 }
956 return 0;
957 }
958
959 static int dl_argv_uint16_t(struct dl *dl, uint16_t *p_val)
960 {
961 char *str = dl_argv_next(dl);
962 int err;
963
964 if (!str) {
965 pr_err("Unsigned number argument expected\n");
966 return -EINVAL;
967 }
968
969 err = strtouint16_t(str, p_val);
970 if (err) {
971 pr_err("\"%s\" is not a number or not within range\n", str);
972 return err;
973 }
974 return 0;
975 }
976
977 static int dl_argv_bool(struct dl *dl, bool *p_val)
978 {
979 char *str = dl_argv_next(dl);
980 int err;
981
982 if (!str) {
983 pr_err("Boolean argument expected\n");
984 return -EINVAL;
985 }
986
987 err = strtobool(str, p_val);
988 if (err) {
989 pr_err("\"%s\" is not a valid boolean value\n", str);
990 return err;
991 }
992 return 0;
993 }
994
995 static int dl_argv_str(struct dl *dl, const char **p_str)
996 {
997 const char *str = dl_argv_next(dl);
998
999 if (!str) {
1000 pr_err("String parameter expected\n");
1001 return -EINVAL;
1002 }
1003 *p_str = str;
1004 return 0;
1005 }
1006
1007 static int port_type_get(const char *typestr, enum devlink_port_type *p_type)
1008 {
1009 if (strcmp(typestr, "auto") == 0) {
1010 *p_type = DEVLINK_PORT_TYPE_AUTO;
1011 } else if (strcmp(typestr, "eth") == 0) {
1012 *p_type = DEVLINK_PORT_TYPE_ETH;
1013 } else if (strcmp(typestr, "ib") == 0) {
1014 *p_type = DEVLINK_PORT_TYPE_IB;
1015 } else {
1016 pr_err("Unknown port type \"%s\"\n", typestr);
1017 return -EINVAL;
1018 }
1019 return 0;
1020 }
1021
1022 static int pool_type_get(const char *typestr, enum devlink_sb_pool_type *p_type)
1023 {
1024 if (strcmp(typestr, "ingress") == 0) {
1025 *p_type = DEVLINK_SB_POOL_TYPE_INGRESS;
1026 } else if (strcmp(typestr, "egress") == 0) {
1027 *p_type = DEVLINK_SB_POOL_TYPE_EGRESS;
1028 } else {
1029 pr_err("Unknown pool type \"%s\"\n", typestr);
1030 return -EINVAL;
1031 }
1032 return 0;
1033 }
1034
1035 static int threshold_type_get(const char *typestr,
1036 enum devlink_sb_threshold_type *p_type)
1037 {
1038 if (strcmp(typestr, "static") == 0) {
1039 *p_type = DEVLINK_SB_THRESHOLD_TYPE_STATIC;
1040 } else if (strcmp(typestr, "dynamic") == 0) {
1041 *p_type = DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC;
1042 } else {
1043 pr_err("Unknown threshold type \"%s\"\n", typestr);
1044 return -EINVAL;
1045 }
1046 return 0;
1047 }
1048
1049 static int eswitch_mode_get(const char *typestr,
1050 enum devlink_eswitch_mode *p_mode)
1051 {
1052 if (strcmp(typestr, ESWITCH_MODE_LEGACY) == 0) {
1053 *p_mode = DEVLINK_ESWITCH_MODE_LEGACY;
1054 } else if (strcmp(typestr, ESWITCH_MODE_SWITCHDEV) == 0) {
1055 *p_mode = DEVLINK_ESWITCH_MODE_SWITCHDEV;
1056 } else {
1057 pr_err("Unknown eswitch mode \"%s\"\n", typestr);
1058 return -EINVAL;
1059 }
1060 return 0;
1061 }
1062
1063 static int eswitch_inline_mode_get(const char *typestr,
1064 enum devlink_eswitch_inline_mode *p_mode)
1065 {
1066 if (strcmp(typestr, ESWITCH_INLINE_MODE_NONE) == 0) {
1067 *p_mode = DEVLINK_ESWITCH_INLINE_MODE_NONE;
1068 } else if (strcmp(typestr, ESWITCH_INLINE_MODE_LINK) == 0) {
1069 *p_mode = DEVLINK_ESWITCH_INLINE_MODE_LINK;
1070 } else if (strcmp(typestr, ESWITCH_INLINE_MODE_NETWORK) == 0) {
1071 *p_mode = DEVLINK_ESWITCH_INLINE_MODE_NETWORK;
1072 } else if (strcmp(typestr, ESWITCH_INLINE_MODE_TRANSPORT) == 0) {
1073 *p_mode = DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT;
1074 } else {
1075 pr_err("Unknown eswitch inline mode \"%s\"\n", typestr);
1076 return -EINVAL;
1077 }
1078 return 0;
1079 }
1080
1081 static int eswitch_encap_mode_get(const char *typestr, bool *p_mode)
1082 {
1083 if (strcmp(typestr, "enable") == 0) {
1084 *p_mode = true;
1085 } else if (strcmp(typestr, "disable") == 0) {
1086 *p_mode = false;
1087 } else {
1088 pr_err("Unknown eswitch encap mode \"%s\"\n", typestr);
1089 return -EINVAL;
1090 }
1091 return 0;
1092 }
1093
1094 static int param_cmode_get(const char *cmodestr,
1095 enum devlink_param_cmode *cmode)
1096 {
1097 if (strcmp(cmodestr, PARAM_CMODE_RUNTIME_STR) == 0) {
1098 *cmode = DEVLINK_PARAM_CMODE_RUNTIME;
1099 } else if (strcmp(cmodestr, PARAM_CMODE_DRIVERINIT_STR) == 0) {
1100 *cmode = DEVLINK_PARAM_CMODE_DRIVERINIT;
1101 } else if (strcmp(cmodestr, PARAM_CMODE_PERMANENT_STR) == 0) {
1102 *cmode = DEVLINK_PARAM_CMODE_PERMANENT;
1103 } else {
1104 pr_err("Unknown configuration mode \"%s\"\n", cmodestr);
1105 return -EINVAL;
1106 }
1107 return 0;
1108 }
1109
1110 static int trap_action_get(const char *actionstr,
1111 enum devlink_trap_action *p_action)
1112 {
1113 if (strcmp(actionstr, "drop") == 0) {
1114 *p_action = DEVLINK_TRAP_ACTION_DROP;
1115 } else if (strcmp(actionstr, "trap") == 0) {
1116 *p_action = DEVLINK_TRAP_ACTION_TRAP;
1117 } else {
1118 pr_err("Unknown trap action \"%s\"\n", actionstr);
1119 return -EINVAL;
1120 }
1121 return 0;
1122 }
1123
1124 struct dl_args_metadata {
1125 uint64_t o_flag;
1126 char err_msg[DL_ARGS_REQUIRED_MAX_ERR_LEN];
1127 };
1128
1129 static const struct dl_args_metadata dl_args_required[] = {
1130 {DL_OPT_PORT_TYPE, "Port type not set."},
1131 {DL_OPT_PORT_COUNT, "Port split count option expected."},
1132 {DL_OPT_SB_POOL, "Pool index option expected."},
1133 {DL_OPT_SB_SIZE, "Pool size option expected."},
1134 {DL_OPT_SB_TYPE, "Pool type option expected."},
1135 {DL_OPT_SB_THTYPE, "Pool threshold type option expected."},
1136 {DL_OPT_SB_TH, "Threshold option expected."},
1137 {DL_OPT_SB_TC, "TC index option expected."},
1138 {DL_OPT_ESWITCH_MODE, "E-Switch mode option expected."},
1139 {DL_OPT_ESWITCH_INLINE_MODE, "E-Switch inline-mode option expected."},
1140 {DL_OPT_DPIPE_TABLE_NAME, "Dpipe table name expected."},
1141 {DL_OPT_DPIPE_TABLE_COUNTERS, "Dpipe table counter state expected."},
1142 {DL_OPT_ESWITCH_ENCAP_MODE, "E-Switch encapsulation option expected."},
1143 {DL_OPT_RESOURCE_PATH, "Resource path expected."},
1144 {DL_OPT_RESOURCE_SIZE, "Resource size expected."},
1145 {DL_OPT_PARAM_NAME, "Parameter name expected."},
1146 {DL_OPT_PARAM_VALUE, "Value to set expected."},
1147 {DL_OPT_PARAM_CMODE, "Configuration mode expected."},
1148 {DL_OPT_REGION_SNAPSHOT_ID, "Region snapshot id expected."},
1149 {DL_OPT_REGION_ADDRESS, "Region address value expected."},
1150 {DL_OPT_REGION_LENGTH, "Region length value expected."},
1151 {DL_OPT_HEALTH_REPORTER_NAME, "Reporter's name is expected."},
1152 {DL_OPT_TRAP_NAME, "Trap's name is expected."},
1153 {DL_OPT_TRAP_GROUP_NAME, "Trap group's name is expected."},
1154 };
1155
1156 static int dl_args_finding_required_validate(uint64_t o_required,
1157 uint64_t o_found)
1158 {
1159 uint64_t o_flag;
1160 int i;
1161
1162 for (i = 0; i < ARRAY_SIZE(dl_args_required); i++) {
1163 o_flag = dl_args_required[i].o_flag;
1164 if ((o_required & o_flag) && !(o_found & o_flag)) {
1165 pr_err("%s\n", dl_args_required[i].err_msg);
1166 return -EINVAL;
1167 }
1168 }
1169 if (o_required & ~o_found) {
1170 pr_err("BUG: unknown argument required but not found\n");
1171 return -EINVAL;
1172 }
1173 return 0;
1174 }
1175
1176 static int dl_argv_parse(struct dl *dl, uint64_t o_required,
1177 uint64_t o_optional)
1178 {
1179 struct dl_opts *opts = &dl->opts;
1180 uint64_t o_all = o_required | o_optional;
1181 uint64_t o_found = 0;
1182 int err;
1183
1184 if (o_required & DL_OPT_HANDLE && o_required & DL_OPT_HANDLEP) {
1185 uint64_t handle_bit;
1186
1187 err = dl_argv_handle_both(dl, &opts->bus_name, &opts->dev_name,
1188 &opts->port_index, &handle_bit);
1189 if (err)
1190 return err;
1191 o_required &= ~(DL_OPT_HANDLE | DL_OPT_HANDLEP) | handle_bit;
1192 o_found |= handle_bit;
1193 } else if (o_required & DL_OPT_HANDLE) {
1194 err = dl_argv_handle(dl, &opts->bus_name, &opts->dev_name);
1195 if (err)
1196 return err;
1197 o_found |= DL_OPT_HANDLE;
1198 } else if (o_required & DL_OPT_HANDLEP) {
1199 err = dl_argv_handle_port(dl, &opts->bus_name, &opts->dev_name,
1200 &opts->port_index);
1201 if (err)
1202 return err;
1203 o_found |= DL_OPT_HANDLEP;
1204 } else if (o_required & DL_OPT_HANDLE_REGION) {
1205 err = dl_argv_handle_region(dl, &opts->bus_name,
1206 &opts->dev_name,
1207 &opts->region_name);
1208 if (err)
1209 return err;
1210 o_found |= DL_OPT_HANDLE_REGION;
1211 }
1212
1213 while (dl_argc(dl)) {
1214 if (dl_argv_match(dl, "type") &&
1215 (o_all & DL_OPT_PORT_TYPE)) {
1216 const char *typestr;
1217
1218 dl_arg_inc(dl);
1219 err = dl_argv_str(dl, &typestr);
1220 if (err)
1221 return err;
1222 err = port_type_get(typestr, &opts->port_type);
1223 if (err)
1224 return err;
1225 o_found |= DL_OPT_PORT_TYPE;
1226 } else if (dl_argv_match(dl, "count") &&
1227 (o_all & DL_OPT_PORT_COUNT)) {
1228 dl_arg_inc(dl);
1229 err = dl_argv_uint32_t(dl, &opts->port_count);
1230 if (err)
1231 return err;
1232 o_found |= DL_OPT_PORT_COUNT;
1233 } else if (dl_argv_match(dl, "sb") &&
1234 (o_all & DL_OPT_SB)) {
1235 dl_arg_inc(dl);
1236 err = dl_argv_uint32_t(dl, &opts->sb_index);
1237 if (err)
1238 return err;
1239 o_found |= DL_OPT_SB;
1240 } else if (dl_argv_match(dl, "pool") &&
1241 (o_all & DL_OPT_SB_POOL)) {
1242 dl_arg_inc(dl);
1243 err = dl_argv_uint16_t(dl, &opts->sb_pool_index);
1244 if (err)
1245 return err;
1246 o_found |= DL_OPT_SB_POOL;
1247 } else if (dl_argv_match(dl, "size") &&
1248 (o_all & DL_OPT_SB_SIZE)) {
1249 dl_arg_inc(dl);
1250 err = dl_argv_uint32_t(dl, &opts->sb_pool_size);
1251 if (err)
1252 return err;
1253 o_found |= DL_OPT_SB_SIZE;
1254 } else if (dl_argv_match(dl, "type") &&
1255 (o_all & DL_OPT_SB_TYPE)) {
1256 const char *typestr;
1257
1258 dl_arg_inc(dl);
1259 err = dl_argv_str(dl, &typestr);
1260 if (err)
1261 return err;
1262 err = pool_type_get(typestr, &opts->sb_pool_type);
1263 if (err)
1264 return err;
1265 o_found |= DL_OPT_SB_TYPE;
1266 } else if (dl_argv_match(dl, "thtype") &&
1267 (o_all & DL_OPT_SB_THTYPE)) {
1268 const char *typestr;
1269
1270 dl_arg_inc(dl);
1271 err = dl_argv_str(dl, &typestr);
1272 if (err)
1273 return err;
1274 err = threshold_type_get(typestr,
1275 &opts->sb_pool_thtype);
1276 if (err)
1277 return err;
1278 o_found |= DL_OPT_SB_THTYPE;
1279 } else if (dl_argv_match(dl, "th") &&
1280 (o_all & DL_OPT_SB_TH)) {
1281 dl_arg_inc(dl);
1282 err = dl_argv_uint32_t(dl, &opts->sb_threshold);
1283 if (err)
1284 return err;
1285 o_found |= DL_OPT_SB_TH;
1286 } else if (dl_argv_match(dl, "tc") &&
1287 (o_all & DL_OPT_SB_TC)) {
1288 dl_arg_inc(dl);
1289 err = dl_argv_uint16_t(dl, &opts->sb_tc_index);
1290 if (err)
1291 return err;
1292 o_found |= DL_OPT_SB_TC;
1293 } else if (dl_argv_match(dl, "mode") &&
1294 (o_all & DL_OPT_ESWITCH_MODE)) {
1295 const char *typestr;
1296
1297 dl_arg_inc(dl);
1298 err = dl_argv_str(dl, &typestr);
1299 if (err)
1300 return err;
1301 err = eswitch_mode_get(typestr, &opts->eswitch_mode);
1302 if (err)
1303 return err;
1304 o_found |= DL_OPT_ESWITCH_MODE;
1305 } else if (dl_argv_match(dl, "inline-mode") &&
1306 (o_all & DL_OPT_ESWITCH_INLINE_MODE)) {
1307 const char *typestr;
1308
1309 dl_arg_inc(dl);
1310 err = dl_argv_str(dl, &typestr);
1311 if (err)
1312 return err;
1313 err = eswitch_inline_mode_get(
1314 typestr, &opts->eswitch_inline_mode);
1315 if (err)
1316 return err;
1317 o_found |= DL_OPT_ESWITCH_INLINE_MODE;
1318 } else if (dl_argv_match(dl, "name") &&
1319 (o_all & DL_OPT_DPIPE_TABLE_NAME)) {
1320 dl_arg_inc(dl);
1321 err = dl_argv_str(dl, &opts->dpipe_table_name);
1322 if (err)
1323 return err;
1324 o_found |= DL_OPT_DPIPE_TABLE_NAME;
1325 } else if (dl_argv_match(dl, "counters") &&
1326 (o_all & DL_OPT_DPIPE_TABLE_COUNTERS)) {
1327 dl_arg_inc(dl);
1328 err = dl_argv_bool(dl, &opts->dpipe_counters_enable);
1329 if (err)
1330 return err;
1331 o_found |= DL_OPT_DPIPE_TABLE_COUNTERS;
1332 } else if (dl_argv_match(dl, "encap") &&
1333 (o_all & DL_OPT_ESWITCH_ENCAP_MODE)) {
1334 const char *typestr;
1335
1336 dl_arg_inc(dl);
1337 err = dl_argv_str(dl, &typestr);
1338 if (err)
1339 return err;
1340 err = eswitch_encap_mode_get(typestr,
1341 &opts->eswitch_encap_mode);
1342 if (err)
1343 return err;
1344 o_found |= DL_OPT_ESWITCH_ENCAP_MODE;
1345 } else if (dl_argv_match(dl, "path") &&
1346 (o_all & DL_OPT_RESOURCE_PATH)) {
1347 dl_arg_inc(dl);
1348 err = dl_argv_str(dl, &opts->resource_path);
1349 if (err)
1350 return err;
1351 o_found |= DL_OPT_RESOURCE_PATH;
1352 } else if (dl_argv_match(dl, "size") &&
1353 (o_all & DL_OPT_RESOURCE_SIZE)) {
1354 dl_arg_inc(dl);
1355 err = dl_argv_uint64_t(dl, &opts->resource_size);
1356 if (err)
1357 return err;
1358 o_found |= DL_OPT_RESOURCE_SIZE;
1359 } else if (dl_argv_match(dl, "name") &&
1360 (o_all & DL_OPT_PARAM_NAME)) {
1361 dl_arg_inc(dl);
1362 err = dl_argv_str(dl, &opts->param_name);
1363 if (err)
1364 return err;
1365 o_found |= DL_OPT_PARAM_NAME;
1366 } else if (dl_argv_match(dl, "value") &&
1367 (o_all & DL_OPT_PARAM_VALUE)) {
1368 dl_arg_inc(dl);
1369 err = dl_argv_str(dl, &opts->param_value);
1370 if (err)
1371 return err;
1372 o_found |= DL_OPT_PARAM_VALUE;
1373 } else if (dl_argv_match(dl, "cmode") &&
1374 (o_all & DL_OPT_PARAM_CMODE)) {
1375 const char *cmodestr;
1376
1377 dl_arg_inc(dl);
1378 err = dl_argv_str(dl, &cmodestr);
1379 if (err)
1380 return err;
1381 err = param_cmode_get(cmodestr, &opts->cmode);
1382 if (err)
1383 return err;
1384 o_found |= DL_OPT_PARAM_CMODE;
1385 } else if (dl_argv_match(dl, "snapshot") &&
1386 (o_all & DL_OPT_REGION_SNAPSHOT_ID)) {
1387 dl_arg_inc(dl);
1388 err = dl_argv_uint32_t(dl, &opts->region_snapshot_id);
1389 if (err)
1390 return err;
1391 o_found |= DL_OPT_REGION_SNAPSHOT_ID;
1392 } else if (dl_argv_match(dl, "address") &&
1393 (o_all & DL_OPT_REGION_ADDRESS)) {
1394 dl_arg_inc(dl);
1395 err = dl_argv_uint64_t(dl, &opts->region_address);
1396 if (err)
1397 return err;
1398 o_found |= DL_OPT_REGION_ADDRESS;
1399 } else if (dl_argv_match(dl, "length") &&
1400 (o_all & DL_OPT_REGION_LENGTH)) {
1401 dl_arg_inc(dl);
1402 err = dl_argv_uint64_t(dl, &opts->region_length);
1403 if (err)
1404 return err;
1405 o_found |= DL_OPT_REGION_LENGTH;
1406 } else if (dl_argv_match(dl, "file") &&
1407 (o_all & DL_OPT_FLASH_FILE_NAME)) {
1408 dl_arg_inc(dl);
1409 err = dl_argv_str(dl, &opts->flash_file_name);
1410 if (err)
1411 return err;
1412 o_found |= DL_OPT_FLASH_FILE_NAME;
1413 } else if (dl_argv_match(dl, "component") &&
1414 (o_all & DL_OPT_FLASH_COMPONENT)) {
1415 dl_arg_inc(dl);
1416 err = dl_argv_str(dl, &opts->flash_component);
1417 if (err)
1418 return err;
1419 o_found |= DL_OPT_FLASH_COMPONENT;
1420 } else if (dl_argv_match(dl, "reporter") &&
1421 (o_all & DL_OPT_HEALTH_REPORTER_NAME)) {
1422 dl_arg_inc(dl);
1423 err = dl_argv_str(dl, &opts->reporter_name);
1424 if (err)
1425 return err;
1426 o_found |= DL_OPT_HEALTH_REPORTER_NAME;
1427 } else if (dl_argv_match(dl, "grace_period") &&
1428 (o_all & DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD)) {
1429 dl_arg_inc(dl);
1430 err = dl_argv_uint64_t(dl,
1431 &opts->reporter_graceful_period);
1432 if (err)
1433 return err;
1434 o_found |= DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD;
1435 } else if (dl_argv_match(dl, "auto_recover") &&
1436 (o_all & DL_OPT_HEALTH_REPORTER_AUTO_RECOVER)) {
1437 dl_arg_inc(dl);
1438 err = dl_argv_bool(dl, &opts->reporter_auto_recover);
1439 if (err)
1440 return err;
1441 o_found |= DL_OPT_HEALTH_REPORTER_AUTO_RECOVER;
1442 } else if (dl_argv_match(dl, "trap") &&
1443 (o_all & DL_OPT_TRAP_NAME)) {
1444 dl_arg_inc(dl);
1445 err = dl_argv_str(dl, &opts->trap_name);
1446 if (err)
1447 return err;
1448 o_found |= DL_OPT_TRAP_NAME;
1449 } else if (dl_argv_match(dl, "group") &&
1450 (o_all & DL_OPT_TRAP_GROUP_NAME)) {
1451 dl_arg_inc(dl);
1452 err = dl_argv_str(dl, &opts->trap_group_name);
1453 if (err)
1454 return err;
1455 o_found |= DL_OPT_TRAP_GROUP_NAME;
1456 } else if (dl_argv_match(dl, "action") &&
1457 (o_all & DL_OPT_TRAP_ACTION)) {
1458 const char *actionstr;
1459
1460 dl_arg_inc(dl);
1461 err = dl_argv_str(dl, &actionstr);
1462 if (err)
1463 return err;
1464 err = trap_action_get(actionstr, &opts->trap_action);
1465 if (err)
1466 return err;
1467 o_found |= DL_OPT_TRAP_ACTION;
1468 } else if (dl_argv_match(dl, "netns") &&
1469 (o_all & DL_OPT_NETNS)) {
1470 const char *netns_str;
1471
1472 dl_arg_inc(dl);
1473 err = dl_argv_str(dl, &netns_str);
1474 if (err)
1475 return err;
1476 opts->netns = netns_get_fd(netns_str);
1477 if ((int)opts->netns < 0) {
1478 dl_arg_dec(dl);
1479 err = dl_argv_uint32_t(dl, &opts->netns);
1480 if (err)
1481 return err;
1482 opts->netns_is_pid = true;
1483 }
1484 o_found |= DL_OPT_NETNS;
1485 } else if (dl_argv_match(dl, "policer") &&
1486 (o_all & DL_OPT_TRAP_POLICER_ID)) {
1487 dl_arg_inc(dl);
1488 err = dl_argv_uint32_t(dl, &opts->trap_policer_id);
1489 if (err)
1490 return err;
1491 o_found |= DL_OPT_TRAP_POLICER_ID;
1492 } else if (dl_argv_match(dl, "nopolicer") &&
1493 (o_all & DL_OPT_TRAP_POLICER_ID)) {
1494 dl_arg_inc(dl);
1495 opts->trap_policer_id = 0;
1496 o_found |= DL_OPT_TRAP_POLICER_ID;
1497 } else if (dl_argv_match(dl, "rate") &&
1498 (o_all & DL_OPT_TRAP_POLICER_RATE)) {
1499 dl_arg_inc(dl);
1500 err = dl_argv_uint64_t(dl, &opts->trap_policer_rate);
1501 if (err)
1502 return err;
1503 o_found |= DL_OPT_TRAP_POLICER_RATE;
1504 } else if (dl_argv_match(dl, "burst") &&
1505 (o_all & DL_OPT_TRAP_POLICER_BURST)) {
1506 dl_arg_inc(dl);
1507 err = dl_argv_uint64_t(dl, &opts->trap_policer_burst);
1508 if (err)
1509 return err;
1510 o_found |= DL_OPT_TRAP_POLICER_BURST;
1511 } else {
1512 pr_err("Unknown option \"%s\"\n", dl_argv(dl));
1513 return -EINVAL;
1514 }
1515 }
1516
1517 opts->present = o_found;
1518
1519 if ((o_optional & DL_OPT_SB) && !(o_found & DL_OPT_SB)) {
1520 opts->sb_index = 0;
1521 opts->present |= DL_OPT_SB;
1522 }
1523
1524 return dl_args_finding_required_validate(o_required, o_found);
1525 }
1526
1527 static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl)
1528 {
1529 struct dl_opts *opts = &dl->opts;
1530
1531 if (opts->present & DL_OPT_HANDLE) {
1532 mnl_attr_put_strz(nlh, DEVLINK_ATTR_BUS_NAME, opts->bus_name);
1533 mnl_attr_put_strz(nlh, DEVLINK_ATTR_DEV_NAME, opts->dev_name);
1534 } else if (opts->present & DL_OPT_HANDLEP) {
1535 mnl_attr_put_strz(nlh, DEVLINK_ATTR_BUS_NAME, opts->bus_name);
1536 mnl_attr_put_strz(nlh, DEVLINK_ATTR_DEV_NAME, opts->dev_name);
1537 mnl_attr_put_u32(nlh, DEVLINK_ATTR_PORT_INDEX,
1538 opts->port_index);
1539 } else if (opts->present & DL_OPT_HANDLE_REGION) {
1540 mnl_attr_put_strz(nlh, DEVLINK_ATTR_BUS_NAME, opts->bus_name);
1541 mnl_attr_put_strz(nlh, DEVLINK_ATTR_DEV_NAME, opts->dev_name);
1542 mnl_attr_put_strz(nlh, DEVLINK_ATTR_REGION_NAME,
1543 opts->region_name);
1544 }
1545 if (opts->present & DL_OPT_PORT_TYPE)
1546 mnl_attr_put_u16(nlh, DEVLINK_ATTR_PORT_TYPE,
1547 opts->port_type);
1548 if (opts->present & DL_OPT_PORT_COUNT)
1549 mnl_attr_put_u32(nlh, DEVLINK_ATTR_PORT_SPLIT_COUNT,
1550 opts->port_count);
1551 if (opts->present & DL_OPT_SB)
1552 mnl_attr_put_u32(nlh, DEVLINK_ATTR_SB_INDEX,
1553 opts->sb_index);
1554 if (opts->present & DL_OPT_SB_POOL)
1555 mnl_attr_put_u16(nlh, DEVLINK_ATTR_SB_POOL_INDEX,
1556 opts->sb_pool_index);
1557 if (opts->present & DL_OPT_SB_SIZE)
1558 mnl_attr_put_u32(nlh, DEVLINK_ATTR_SB_POOL_SIZE,
1559 opts->sb_pool_size);
1560 if (opts->present & DL_OPT_SB_TYPE)
1561 mnl_attr_put_u8(nlh, DEVLINK_ATTR_SB_POOL_TYPE,
1562 opts->sb_pool_type);
1563 if (opts->present & DL_OPT_SB_THTYPE)
1564 mnl_attr_put_u8(nlh, DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE,
1565 opts->sb_pool_thtype);
1566 if (opts->present & DL_OPT_SB_TH)
1567 mnl_attr_put_u32(nlh, DEVLINK_ATTR_SB_THRESHOLD,
1568 opts->sb_threshold);
1569 if (opts->present & DL_OPT_SB_TC)
1570 mnl_attr_put_u16(nlh, DEVLINK_ATTR_SB_TC_INDEX,
1571 opts->sb_tc_index);
1572 if (opts->present & DL_OPT_ESWITCH_MODE)
1573 mnl_attr_put_u16(nlh, DEVLINK_ATTR_ESWITCH_MODE,
1574 opts->eswitch_mode);
1575 if (opts->present & DL_OPT_ESWITCH_INLINE_MODE)
1576 mnl_attr_put_u8(nlh, DEVLINK_ATTR_ESWITCH_INLINE_MODE,
1577 opts->eswitch_inline_mode);
1578 if (opts->present & DL_OPT_DPIPE_TABLE_NAME)
1579 mnl_attr_put_strz(nlh, DEVLINK_ATTR_DPIPE_TABLE_NAME,
1580 opts->dpipe_table_name);
1581 if (opts->present & DL_OPT_DPIPE_TABLE_COUNTERS)
1582 mnl_attr_put_u8(nlh, DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED,
1583 opts->dpipe_counters_enable);
1584 if (opts->present & DL_OPT_ESWITCH_ENCAP_MODE)
1585 mnl_attr_put_u8(nlh, DEVLINK_ATTR_ESWITCH_ENCAP_MODE,
1586 opts->eswitch_encap_mode);
1587 if ((opts->present & DL_OPT_RESOURCE_PATH) && opts->resource_id_valid)
1588 mnl_attr_put_u64(nlh, DEVLINK_ATTR_RESOURCE_ID,
1589 opts->resource_id);
1590 if (opts->present & DL_OPT_RESOURCE_SIZE)
1591 mnl_attr_put_u64(nlh, DEVLINK_ATTR_RESOURCE_SIZE,
1592 opts->resource_size);
1593 if (opts->present & DL_OPT_PARAM_NAME)
1594 mnl_attr_put_strz(nlh, DEVLINK_ATTR_PARAM_NAME,
1595 opts->param_name);
1596 if (opts->present & DL_OPT_PARAM_CMODE)
1597 mnl_attr_put_u8(nlh, DEVLINK_ATTR_PARAM_VALUE_CMODE,
1598 opts->cmode);
1599 if (opts->present & DL_OPT_REGION_SNAPSHOT_ID)
1600 mnl_attr_put_u32(nlh, DEVLINK_ATTR_REGION_SNAPSHOT_ID,
1601 opts->region_snapshot_id);
1602 if (opts->present & DL_OPT_REGION_ADDRESS)
1603 mnl_attr_put_u64(nlh, DEVLINK_ATTR_REGION_CHUNK_ADDR,
1604 opts->region_address);
1605 if (opts->present & DL_OPT_REGION_LENGTH)
1606 mnl_attr_put_u64(nlh, DEVLINK_ATTR_REGION_CHUNK_LEN,
1607 opts->region_length);
1608 if (opts->present & DL_OPT_FLASH_FILE_NAME)
1609 mnl_attr_put_strz(nlh, DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME,
1610 opts->flash_file_name);
1611 if (opts->present & DL_OPT_FLASH_COMPONENT)
1612 mnl_attr_put_strz(nlh, DEVLINK_ATTR_FLASH_UPDATE_COMPONENT,
1613 opts->flash_component);
1614 if (opts->present & DL_OPT_HEALTH_REPORTER_NAME)
1615 mnl_attr_put_strz(nlh, DEVLINK_ATTR_HEALTH_REPORTER_NAME,
1616 opts->reporter_name);
1617 if (opts->present & DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD)
1618 mnl_attr_put_u64(nlh,
1619 DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD,
1620 opts->reporter_graceful_period);
1621 if (opts->present & DL_OPT_HEALTH_REPORTER_AUTO_RECOVER)
1622 mnl_attr_put_u8(nlh, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER,
1623 opts->reporter_auto_recover);
1624 if (opts->present & DL_OPT_TRAP_NAME)
1625 mnl_attr_put_strz(nlh, DEVLINK_ATTR_TRAP_NAME,
1626 opts->trap_name);
1627 if (opts->present & DL_OPT_TRAP_GROUP_NAME)
1628 mnl_attr_put_strz(nlh, DEVLINK_ATTR_TRAP_GROUP_NAME,
1629 opts->trap_group_name);
1630 if (opts->present & DL_OPT_TRAP_ACTION)
1631 mnl_attr_put_u8(nlh, DEVLINK_ATTR_TRAP_ACTION,
1632 opts->trap_action);
1633 if (opts->present & DL_OPT_NETNS)
1634 mnl_attr_put_u32(nlh,
1635 opts->netns_is_pid ? DEVLINK_ATTR_NETNS_PID :
1636 DEVLINK_ATTR_NETNS_FD,
1637 opts->netns);
1638 if (opts->present & DL_OPT_TRAP_POLICER_ID)
1639 mnl_attr_put_u32(nlh, DEVLINK_ATTR_TRAP_POLICER_ID,
1640 opts->trap_policer_id);
1641 if (opts->present & DL_OPT_TRAP_POLICER_RATE)
1642 mnl_attr_put_u64(nlh, DEVLINK_ATTR_TRAP_POLICER_RATE,
1643 opts->trap_policer_rate);
1644 if (opts->present & DL_OPT_TRAP_POLICER_BURST)
1645 mnl_attr_put_u64(nlh, DEVLINK_ATTR_TRAP_POLICER_BURST,
1646 opts->trap_policer_burst);
1647 }
1648
1649 static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl,
1650 uint64_t o_required, uint64_t o_optional)
1651 {
1652 int err;
1653
1654 err = dl_argv_parse(dl, o_required, o_optional);
1655 if (err)
1656 return err;
1657 dl_opts_put(nlh, dl);
1658 return 0;
1659 }
1660
1661 static bool dl_dump_filter(struct dl *dl, struct nlattr **tb)
1662 {
1663 struct dl_opts *opts = &dl->opts;
1664 struct nlattr *attr_bus_name = tb[DEVLINK_ATTR_BUS_NAME];
1665 struct nlattr *attr_dev_name = tb[DEVLINK_ATTR_DEV_NAME];
1666 struct nlattr *attr_port_index = tb[DEVLINK_ATTR_PORT_INDEX];
1667 struct nlattr *attr_sb_index = tb[DEVLINK_ATTR_SB_INDEX];
1668
1669 if (opts->present & DL_OPT_HANDLE &&
1670 attr_bus_name && attr_dev_name) {
1671 const char *bus_name = mnl_attr_get_str(attr_bus_name);
1672 const char *dev_name = mnl_attr_get_str(attr_dev_name);
1673
1674 if (strcmp(bus_name, opts->bus_name) != 0 ||
1675 strcmp(dev_name, opts->dev_name) != 0)
1676 return false;
1677 }
1678 if (opts->present & DL_OPT_HANDLEP &&
1679 attr_bus_name && attr_dev_name && attr_port_index) {
1680 const char *bus_name = mnl_attr_get_str(attr_bus_name);
1681 const char *dev_name = mnl_attr_get_str(attr_dev_name);
1682 uint32_t port_index = mnl_attr_get_u32(attr_port_index);
1683
1684 if (strcmp(bus_name, opts->bus_name) != 0 ||
1685 strcmp(dev_name, opts->dev_name) != 0 ||
1686 port_index != opts->port_index)
1687 return false;
1688 }
1689 if (opts->present & DL_OPT_SB && attr_sb_index) {
1690 uint32_t sb_index = mnl_attr_get_u32(attr_sb_index);
1691
1692 if (sb_index != opts->sb_index)
1693 return false;
1694 }
1695 return true;
1696 }
1697
1698 static void cmd_dev_help(void)
1699 {
1700 pr_err("Usage: devlink dev show [ DEV ]\n");
1701 pr_err(" devlink dev eswitch set DEV [ mode { legacy | switchdev } ]\n");
1702 pr_err(" [ inline-mode { none | link | network | transport } ]\n");
1703 pr_err(" [ encap { disable | enable } ]\n");
1704 pr_err(" devlink dev eswitch show DEV\n");
1705 pr_err(" devlink dev param set DEV name PARAMETER value VALUE cmode { permanent | driverinit | runtime }\n");
1706 pr_err(" devlink dev param show [DEV name PARAMETER]\n");
1707 pr_err(" devlink dev reload DEV [ netns { PID | NAME | ID } ]\n");
1708 pr_err(" devlink dev info [ DEV ]\n");
1709 pr_err(" devlink dev flash DEV file PATH [ component NAME ]\n");
1710 }
1711
1712 static bool cmp_arr_last_handle(struct dl *dl, const char *bus_name,
1713 const char *dev_name)
1714 {
1715 if (!dl->arr_last.present)
1716 return false;
1717 return strcmp(dl->arr_last.bus_name, bus_name) == 0 &&
1718 strcmp(dl->arr_last.dev_name, dev_name) == 0;
1719 }
1720
1721 static void arr_last_handle_set(struct dl *dl, const char *bus_name,
1722 const char *dev_name)
1723 {
1724 dl->arr_last.present = true;
1725 free(dl->arr_last.dev_name);
1726 free(dl->arr_last.bus_name);
1727 dl->arr_last.bus_name = strdup(bus_name);
1728 dl->arr_last.dev_name = strdup(dev_name);
1729 }
1730
1731 static bool should_arr_last_handle_start(struct dl *dl, const char *bus_name,
1732 const char *dev_name)
1733 {
1734 return !cmp_arr_last_handle(dl, bus_name, dev_name);
1735 }
1736
1737 static bool should_arr_last_handle_end(struct dl *dl, const char *bus_name,
1738 const char *dev_name)
1739 {
1740 return dl->arr_last.present &&
1741 !cmp_arr_last_handle(dl, bus_name, dev_name);
1742 }
1743
1744 static void __pr_out_handle_start(struct dl *dl, struct nlattr **tb,
1745 bool content, bool array)
1746 {
1747 const char *bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]);
1748 const char *dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]);
1749 char buf[64];
1750
1751 sprintf(buf, "%s/%s", bus_name, dev_name);
1752
1753 if (dl->json_output) {
1754 if (array) {
1755 if (should_arr_last_handle_end(dl, bus_name, dev_name))
1756 close_json_array(PRINT_JSON, NULL);
1757 if (should_arr_last_handle_start(dl, bus_name,
1758 dev_name)) {
1759 open_json_array(PRINT_JSON, buf);
1760 open_json_object(NULL);
1761 arr_last_handle_set(dl, bus_name, dev_name);
1762 } else {
1763 open_json_object(NULL);
1764 }
1765 } else {
1766 open_json_object(buf);
1767 }
1768 } else {
1769 if (array) {
1770 if (should_arr_last_handle_end(dl, bus_name, dev_name))
1771 __pr_out_indent_dec();
1772 if (should_arr_last_handle_start(dl, bus_name,
1773 dev_name)) {
1774 pr_out("%s%s", buf, content ? ":" : "");
1775 __pr_out_newline();
1776 __pr_out_indent_inc();
1777 arr_last_handle_set(dl, bus_name, dev_name);
1778 }
1779 } else {
1780 pr_out("%s%s", buf, content ? ":" : "");
1781 }
1782 }
1783 }
1784
1785 static void pr_out_handle_start_arr(struct dl *dl, struct nlattr **tb)
1786 {
1787 __pr_out_handle_start(dl, tb, true, true);
1788 }
1789
1790 static void pr_out_handle_end(struct dl *dl)
1791 {
1792 if (dl->json_output)
1793 close_json_object();
1794 else
1795 __pr_out_newline();
1796 }
1797
1798 static void pr_out_handle(struct dl *dl, struct nlattr **tb)
1799 {
1800 __pr_out_handle_start(dl, tb, false, false);
1801 pr_out_handle_end(dl);
1802 }
1803
1804 static bool cmp_arr_last_port_handle(struct dl *dl, const char *bus_name,
1805 const char *dev_name, uint32_t port_index)
1806 {
1807 return cmp_arr_last_handle(dl, bus_name, dev_name) &&
1808 dl->arr_last.port_index == port_index;
1809 }
1810
1811 static void arr_last_port_handle_set(struct dl *dl, const char *bus_name,
1812 const char *dev_name, uint32_t port_index)
1813 {
1814 arr_last_handle_set(dl, bus_name, dev_name);
1815 dl->arr_last.port_index = port_index;
1816 }
1817
1818 static bool should_arr_last_port_handle_start(struct dl *dl,
1819 const char *bus_name,
1820 const char *dev_name,
1821 uint32_t port_index)
1822 {
1823 return !cmp_arr_last_port_handle(dl, bus_name, dev_name, port_index);
1824 }
1825
1826 static bool should_arr_last_port_handle_end(struct dl *dl,
1827 const char *bus_name,
1828 const char *dev_name,
1829 uint32_t port_index)
1830 {
1831 return dl->arr_last.present &&
1832 !cmp_arr_last_port_handle(dl, bus_name, dev_name, port_index);
1833 }
1834
1835 static void __pr_out_port_handle_start(struct dl *dl, const char *bus_name,
1836 const char *dev_name,
1837 uint32_t port_index, bool try_nice,
1838 bool array)
1839 {
1840 static char buf[64];
1841 char *ifname = NULL;
1842
1843 if (dl->no_nice_names || !try_nice ||
1844 ifname_map_rev_lookup(dl, bus_name, dev_name,
1845 port_index, &ifname) != 0)
1846 sprintf(buf, "%s/%s/%d", bus_name, dev_name, port_index);
1847 else
1848 sprintf(buf, "%s", ifname);
1849
1850 if (dl->json_output) {
1851 if (array) {
1852 if (should_arr_last_port_handle_end(dl, bus_name,
1853 dev_name,
1854 port_index))
1855 close_json_array(PRINT_JSON, NULL);
1856 if (should_arr_last_port_handle_start(dl, bus_name,
1857 dev_name,
1858 port_index)) {
1859 open_json_array(PRINT_JSON, buf);
1860 open_json_object(NULL);
1861 arr_last_port_handle_set(dl, bus_name, dev_name,
1862 port_index);
1863 } else {
1864 open_json_object(NULL);
1865 }
1866 } else {
1867 open_json_object(buf);
1868 }
1869 } else {
1870 pr_out("%s:", buf);
1871 }
1872 }
1873
1874 static void pr_out_port_handle_start(struct dl *dl, struct nlattr **tb, bool try_nice)
1875 {
1876 const char *bus_name;
1877 const char *dev_name;
1878 uint32_t port_index;
1879
1880 bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]);
1881 dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]);
1882 port_index = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX]);
1883 __pr_out_port_handle_start(dl, bus_name, dev_name, port_index, try_nice, false);
1884 }
1885
1886 static void pr_out_port_handle_start_arr(struct dl *dl, struct nlattr **tb, bool try_nice)
1887 {
1888 const char *bus_name;
1889 const char *dev_name;
1890 uint32_t port_index;
1891
1892 bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]);
1893 dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]);
1894 port_index = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX]);
1895 __pr_out_port_handle_start(dl, bus_name, dev_name, port_index, try_nice, true);
1896 }
1897
1898 static void pr_out_port_handle_end(struct dl *dl)
1899 {
1900 if (dl->json_output)
1901 close_json_object();
1902 else
1903 pr_out("\n");
1904 }
1905
1906 static void pr_out_u64(struct dl *dl, const char *name, uint64_t val)
1907 {
1908 __pr_out_indent_newline(dl);
1909 if (val == (uint64_t) -1)
1910 return print_string_name_value(name, "unlimited");
1911
1912 if (dl->json_output)
1913 print_u64(PRINT_JSON, name, NULL, val);
1914 else
1915 pr_out("%s %"PRIu64, name, val);
1916 }
1917
1918 static bool is_binary_eol(int i)
1919 {
1920 return !(i%16);
1921 }
1922
1923 static void pr_out_binary_value(struct dl *dl, uint8_t *data, uint32_t len)
1924 {
1925 int i = 0;
1926
1927 while (i < len) {
1928 if (dl->json_output)
1929 print_int(PRINT_JSON, NULL, NULL, data[i]);
1930 else
1931 pr_out("%02x ", data[i]);
1932 i++;
1933 if (!dl->json_output && is_binary_eol(i))
1934 __pr_out_newline();
1935 }
1936 if (!dl->json_output && !is_binary_eol(i))
1937 __pr_out_newline();
1938 }
1939
1940 static void pr_out_name(struct dl *dl, const char *name)
1941 {
1942 __pr_out_indent_newline(dl);
1943 if (dl->json_output)
1944 print_string(PRINT_JSON, name, NULL, NULL);
1945 else
1946 pr_out("%s:", name);
1947 }
1948
1949 static void pr_out_region_chunk_start(struct dl *dl, uint64_t addr)
1950 {
1951 if (dl->json_output) {
1952 print_uint(PRINT_JSON, "address", NULL, addr);
1953 open_json_array(PRINT_JSON, "data");
1954 }
1955 }
1956
1957 static void pr_out_region_chunk_end(struct dl *dl)
1958 {
1959 if (dl->json_output)
1960 close_json_array(PRINT_JSON, NULL);
1961 }
1962
1963 static void pr_out_region_chunk(struct dl *dl, uint8_t *data, uint32_t len,
1964 uint64_t addr)
1965 {
1966 static uint64_t align_val;
1967 uint32_t i = 0;
1968
1969 pr_out_region_chunk_start(dl, addr);
1970 while (i < len) {
1971 if (!dl->json_output)
1972 if (!(align_val % 16))
1973 pr_out("%s%016"PRIx64" ",
1974 align_val ? "\n" : "",
1975 addr);
1976
1977 align_val++;
1978
1979 if (dl->json_output)
1980 print_int(PRINT_JSON, NULL, NULL, data[i]);
1981 else
1982 pr_out("%02x ", data[i]);
1983
1984 addr++;
1985 i++;
1986 }
1987 pr_out_region_chunk_end(dl);
1988 }
1989
1990 static void pr_out_section_start(struct dl *dl, const char *name)
1991 {
1992 if (dl->json_output) {
1993 open_json_object(NULL);
1994 open_json_object(name);
1995 }
1996 }
1997
1998 static void pr_out_section_end(struct dl *dl)
1999 {
2000 if (dl->json_output) {
2001 if (dl->arr_last.present)
2002 close_json_array(PRINT_JSON, NULL);
2003 close_json_object();
2004 close_json_object();
2005 }
2006 }
2007
2008 static void pr_out_array_start(struct dl *dl, const char *name)
2009 {
2010 if (dl->json_output) {
2011 open_json_array(PRINT_JSON, name);
2012 } else {
2013 __pr_out_indent_inc();
2014 __pr_out_newline();
2015 pr_out("%s:", name);
2016 __pr_out_indent_inc();
2017 __pr_out_newline();
2018 }
2019 }
2020
2021 static void pr_out_array_end(struct dl *dl)
2022 {
2023 if (dl->json_output) {
2024 close_json_array(PRINT_JSON, NULL);
2025 } else {
2026 __pr_out_indent_dec();
2027 __pr_out_indent_dec();
2028 }
2029 }
2030
2031 static void pr_out_object_start(struct dl *dl, const char *name)
2032 {
2033 if (dl->json_output) {
2034 open_json_object(name);
2035 } else {
2036 __pr_out_indent_inc();
2037 __pr_out_newline();
2038 pr_out("%s:", name);
2039 __pr_out_indent_inc();
2040 __pr_out_newline();
2041 }
2042 }
2043
2044 static void pr_out_object_end(struct dl *dl)
2045 {
2046 if (dl->json_output) {
2047 close_json_object();
2048 } else {
2049 __pr_out_indent_dec();
2050 __pr_out_indent_dec();
2051 }
2052 }
2053
2054 static void pr_out_entry_start(struct dl *dl)
2055 {
2056 if (dl->json_output)
2057 open_json_object(NULL);
2058 }
2059
2060 static void pr_out_entry_end(struct dl *dl)
2061 {
2062 if (dl->json_output)
2063 close_json_object();
2064 else
2065 __pr_out_newline();
2066 }
2067
2068 static void pr_out_stats(struct dl *dl, struct nlattr *nla_stats)
2069 {
2070 struct nlattr *tb[DEVLINK_ATTR_STATS_MAX + 1] = {};
2071 int err;
2072
2073 if (!dl->stats)
2074 return;
2075
2076 err = mnl_attr_parse_nested(nla_stats, attr_stats_cb, tb);
2077 if (err != MNL_CB_OK)
2078 return;
2079
2080 pr_out_object_start(dl, "stats");
2081 pr_out_object_start(dl, "rx");
2082 if (tb[DEVLINK_ATTR_STATS_RX_BYTES])
2083 pr_out_u64(dl, "bytes",
2084 mnl_attr_get_u64(tb[DEVLINK_ATTR_STATS_RX_BYTES]));
2085 if (tb[DEVLINK_ATTR_STATS_RX_PACKETS])
2086 pr_out_u64(dl, "packets",
2087 mnl_attr_get_u64(tb[DEVLINK_ATTR_STATS_RX_PACKETS]));
2088 if (tb[DEVLINK_ATTR_STATS_RX_DROPPED])
2089 pr_out_u64(dl, "dropped",
2090 mnl_attr_get_u64(tb[DEVLINK_ATTR_STATS_RX_DROPPED]));
2091 pr_out_object_end(dl);
2092 pr_out_object_end(dl);
2093 }
2094
2095 static const char *param_cmode_name(uint8_t cmode)
2096 {
2097 switch (cmode) {
2098 case DEVLINK_PARAM_CMODE_RUNTIME:
2099 return PARAM_CMODE_RUNTIME_STR;
2100 case DEVLINK_PARAM_CMODE_DRIVERINIT:
2101 return PARAM_CMODE_DRIVERINIT_STR;
2102 case DEVLINK_PARAM_CMODE_PERMANENT:
2103 return PARAM_CMODE_PERMANENT_STR;
2104 default: return "<unknown type>";
2105 }
2106 }
2107
2108 static const char *eswitch_mode_name(uint32_t mode)
2109 {
2110 switch (mode) {
2111 case DEVLINK_ESWITCH_MODE_LEGACY: return ESWITCH_MODE_LEGACY;
2112 case DEVLINK_ESWITCH_MODE_SWITCHDEV: return ESWITCH_MODE_SWITCHDEV;
2113 default: return "<unknown mode>";
2114 }
2115 }
2116
2117 static const char *eswitch_inline_mode_name(uint32_t mode)
2118 {
2119 switch (mode) {
2120 case DEVLINK_ESWITCH_INLINE_MODE_NONE:
2121 return ESWITCH_INLINE_MODE_NONE;
2122 case DEVLINK_ESWITCH_INLINE_MODE_LINK:
2123 return ESWITCH_INLINE_MODE_LINK;
2124 case DEVLINK_ESWITCH_INLINE_MODE_NETWORK:
2125 return ESWITCH_INLINE_MODE_NETWORK;
2126 case DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT:
2127 return ESWITCH_INLINE_MODE_TRANSPORT;
2128 default:
2129 return "<unknown mode>";
2130 }
2131 }
2132
2133 static void pr_out_eswitch(struct dl *dl, struct nlattr **tb)
2134 {
2135 __pr_out_handle_start(dl, tb, true, false);
2136
2137 if (tb[DEVLINK_ATTR_ESWITCH_MODE]) {
2138 check_indent_newline(dl);
2139 print_string(PRINT_ANY, "mode", "mode %s",
2140 eswitch_mode_name(mnl_attr_get_u16(
2141 tb[DEVLINK_ATTR_ESWITCH_MODE])));
2142 }
2143 if (tb[DEVLINK_ATTR_ESWITCH_INLINE_MODE]) {
2144 check_indent_newline(dl);
2145 print_string(PRINT_ANY, "inline-mode", "inline-mode %s",
2146 eswitch_inline_mode_name(mnl_attr_get_u8(
2147 tb[DEVLINK_ATTR_ESWITCH_INLINE_MODE])));
2148 }
2149 if (tb[DEVLINK_ATTR_ESWITCH_ENCAP_MODE]) {
2150 bool encap_mode = !!mnl_attr_get_u8(tb[DEVLINK_ATTR_ESWITCH_ENCAP_MODE]);
2151
2152 check_indent_newline(dl);
2153 print_string(PRINT_ANY, "encap", "encap %s",
2154 encap_mode ? "enable" : "disable");
2155 }
2156
2157 pr_out_handle_end(dl);
2158 }
2159
2160 static int cmd_dev_eswitch_show_cb(const struct nlmsghdr *nlh, void *data)
2161 {
2162 struct dl *dl = data;
2163 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
2164 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
2165
2166 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
2167 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME])
2168 return MNL_CB_ERROR;
2169 pr_out_eswitch(dl, tb);
2170 return MNL_CB_OK;
2171 }
2172
2173 static int cmd_dev_eswitch_show(struct dl *dl)
2174 {
2175 struct nlmsghdr *nlh;
2176 int err;
2177
2178 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_ESWITCH_GET,
2179 NLM_F_REQUEST | NLM_F_ACK);
2180
2181 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, 0);
2182 if (err)
2183 return err;
2184
2185 pr_out_section_start(dl, "dev");
2186 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dev_eswitch_show_cb, dl);
2187 pr_out_section_end(dl);
2188 return err;
2189 }
2190
2191 static int cmd_dev_eswitch_set(struct dl *dl)
2192 {
2193 struct nlmsghdr *nlh;
2194 int err;
2195
2196 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_ESWITCH_SET,
2197 NLM_F_REQUEST | NLM_F_ACK);
2198
2199 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE,
2200 DL_OPT_ESWITCH_MODE |
2201 DL_OPT_ESWITCH_INLINE_MODE |
2202 DL_OPT_ESWITCH_ENCAP_MODE);
2203
2204 if (err)
2205 return err;
2206
2207 if (dl->opts.present == 1) {
2208 pr_err("Need to set at least one option\n");
2209 return -ENOENT;
2210 }
2211
2212 return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
2213 }
2214
2215 static int cmd_dev_eswitch(struct dl *dl)
2216 {
2217 if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
2218 cmd_dev_help();
2219 return 0;
2220 } else if (dl_argv_match(dl, "set")) {
2221 dl_arg_inc(dl);
2222 return cmd_dev_eswitch_set(dl);
2223 } else if (dl_argv_match(dl, "show")) {
2224 dl_arg_inc(dl);
2225 return cmd_dev_eswitch_show(dl);
2226 }
2227 pr_err("Command \"%s\" not found\n", dl_argv(dl));
2228 return -ENOENT;
2229 }
2230
2231 struct param_val_conv {
2232 const char *name;
2233 const char *vstr;
2234 uint32_t vuint;
2235 };
2236
2237 static bool param_val_conv_exists(const struct param_val_conv *param_val_conv,
2238 uint32_t len, const char *name)
2239 {
2240 uint32_t i;
2241
2242 for (i = 0; i < len; i++)
2243 if (!strcmp(param_val_conv[i].name, name))
2244 return true;
2245
2246 return false;
2247 }
2248
2249 static int
2250 param_val_conv_uint_get(const struct param_val_conv *param_val_conv,
2251 uint32_t len, const char *name, const char *vstr,
2252 uint32_t *vuint)
2253 {
2254 uint32_t i;
2255
2256 for (i = 0; i < len; i++)
2257 if (!strcmp(param_val_conv[i].name, name) &&
2258 !strcmp(param_val_conv[i].vstr, vstr)) {
2259 *vuint = param_val_conv[i].vuint;
2260 return 0;
2261 }
2262
2263 return -ENOENT;
2264 }
2265
2266 static int
2267 param_val_conv_str_get(const struct param_val_conv *param_val_conv,
2268 uint32_t len, const char *name, uint32_t vuint,
2269 const char **vstr)
2270 {
2271 uint32_t i;
2272
2273 for (i = 0; i < len; i++)
2274 if (!strcmp(param_val_conv[i].name, name) &&
2275 param_val_conv[i].vuint == vuint) {
2276 *vstr = param_val_conv[i].vstr;
2277 return 0;
2278 }
2279
2280 return -ENOENT;
2281 }
2282
2283 static const struct param_val_conv param_val_conv[] = {
2284 {
2285 .name = "fw_load_policy",
2286 .vstr = "driver",
2287 .vuint = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER,
2288 },
2289 {
2290 .name = "fw_load_policy",
2291 .vstr = "flash",
2292 .vuint = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH,
2293 },
2294 {
2295 .name = "reset_dev_on_drv_probe",
2296 .vstr = "unknown",
2297 .vuint = DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_UNKNOWN,
2298 },
2299 {
2300 .name = "fw_load_policy",
2301 .vstr = "unknown",
2302 .vuint = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_UNKNOWN,
2303 },
2304 {
2305 .name = "reset_dev_on_drv_probe",
2306 .vstr = "always",
2307 .vuint = DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_ALWAYS,
2308 },
2309 {
2310 .name = "reset_dev_on_drv_probe",
2311 .vstr = "never",
2312 .vuint = DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_NEVER,
2313 },
2314 {
2315 .name = "reset_dev_on_drv_probe",
2316 .vstr = "disk",
2317 .vuint = DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_DISK,
2318 },
2319 };
2320
2321 #define PARAM_VAL_CONV_LEN ARRAY_SIZE(param_val_conv)
2322
2323 static void pr_out_param_value(struct dl *dl, const char *nla_name,
2324 int nla_type, struct nlattr *nl)
2325 {
2326 struct nlattr *nla_value[DEVLINK_ATTR_MAX + 1] = {};
2327 struct nlattr *val_attr;
2328 const char *vstr;
2329 bool conv_exists;
2330 int err;
2331
2332 err = mnl_attr_parse_nested(nl, attr_cb, nla_value);
2333 if (err != MNL_CB_OK)
2334 return;
2335
2336 if (!nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE] ||
2337 (nla_type != MNL_TYPE_FLAG &&
2338 !nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA]))
2339 return;
2340
2341 check_indent_newline(dl);
2342 print_string(PRINT_ANY, "cmode", "cmode %s",
2343 param_cmode_name(mnl_attr_get_u8(nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE])));
2344
2345 val_attr = nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA];
2346
2347 conv_exists = param_val_conv_exists(param_val_conv, PARAM_VAL_CONV_LEN,
2348 nla_name);
2349
2350 switch (nla_type) {
2351 case MNL_TYPE_U8:
2352 if (conv_exists) {
2353 err = param_val_conv_str_get(param_val_conv,
2354 PARAM_VAL_CONV_LEN,
2355 nla_name,
2356 mnl_attr_get_u8(val_attr),
2357 &vstr);
2358 if (err)
2359 return;
2360 print_string(PRINT_ANY, "value", " value %s", vstr);
2361 } else {
2362 print_uint(PRINT_ANY, "value", " value %u",
2363 mnl_attr_get_u8(val_attr));
2364 }
2365 break;
2366 case MNL_TYPE_U16:
2367 if (conv_exists) {
2368 err = param_val_conv_str_get(param_val_conv,
2369 PARAM_VAL_CONV_LEN,
2370 nla_name,
2371 mnl_attr_get_u16(val_attr),
2372 &vstr);
2373 if (err)
2374 return;
2375 print_string(PRINT_ANY, "value", " value %s", vstr);
2376 } else {
2377 print_uint(PRINT_ANY, "value", " value %u",
2378 mnl_attr_get_u16(val_attr));
2379 }
2380 break;
2381 case MNL_TYPE_U32:
2382 if (conv_exists) {
2383 err = param_val_conv_str_get(param_val_conv,
2384 PARAM_VAL_CONV_LEN,
2385 nla_name,
2386 mnl_attr_get_u32(val_attr),
2387 &vstr);
2388 if (err)
2389 return;
2390 print_string(PRINT_ANY, "value", " value %s", vstr);
2391 } else {
2392 print_uint(PRINT_ANY, "value", " value %u",
2393 mnl_attr_get_u32(val_attr));
2394 }
2395 break;
2396 case MNL_TYPE_STRING:
2397 print_string(PRINT_ANY, "value", " value %s",
2398 mnl_attr_get_str(val_attr));
2399 break;
2400 case MNL_TYPE_FLAG:
2401 print_bool(PRINT_ANY, "value", " value %s", val_attr);
2402 break;
2403 }
2404 }
2405
2406 static void pr_out_param(struct dl *dl, struct nlattr **tb, bool array)
2407 {
2408 struct nlattr *nla_param[DEVLINK_ATTR_MAX + 1] = {};
2409 struct nlattr *param_value_attr;
2410 const char *nla_name;
2411 int nla_type;
2412 int err;
2413
2414 err = mnl_attr_parse_nested(tb[DEVLINK_ATTR_PARAM], attr_cb, nla_param);
2415 if (err != MNL_CB_OK)
2416 return;
2417 if (!nla_param[DEVLINK_ATTR_PARAM_NAME] ||
2418 !nla_param[DEVLINK_ATTR_PARAM_TYPE] ||
2419 !nla_param[DEVLINK_ATTR_PARAM_VALUES_LIST])
2420 return;
2421
2422 if (array)
2423 pr_out_handle_start_arr(dl, tb);
2424 else
2425 __pr_out_handle_start(dl, tb, true, false);
2426
2427 nla_type = mnl_attr_get_u8(nla_param[DEVLINK_ATTR_PARAM_TYPE]);
2428
2429 nla_name = mnl_attr_get_str(nla_param[DEVLINK_ATTR_PARAM_NAME]);
2430 check_indent_newline(dl);
2431 print_string(PRINT_ANY, "name", "name %s ", nla_name);
2432 if (!nla_param[DEVLINK_ATTR_PARAM_GENERIC])
2433 print_string(PRINT_ANY, "type", "type %s", "driver-specific");
2434 else
2435 print_string(PRINT_ANY, "type", "type %s", "generic");
2436
2437 pr_out_array_start(dl, "values");
2438 mnl_attr_for_each_nested(param_value_attr,
2439 nla_param[DEVLINK_ATTR_PARAM_VALUES_LIST]) {
2440 pr_out_entry_start(dl);
2441 pr_out_param_value(dl, nla_name, nla_type, param_value_attr);
2442 pr_out_entry_end(dl);
2443 }
2444 pr_out_array_end(dl);
2445 pr_out_handle_end(dl);
2446 }
2447
2448 static int cmd_dev_param_show_cb(const struct nlmsghdr *nlh, void *data)
2449 {
2450 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
2451 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
2452 struct dl *dl = data;
2453
2454 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
2455 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
2456 !tb[DEVLINK_ATTR_PARAM])
2457 return MNL_CB_ERROR;
2458 pr_out_param(dl, tb, true);
2459 return MNL_CB_OK;
2460 }
2461
2462 struct param_ctx {
2463 struct dl *dl;
2464 int nla_type;
2465 union {
2466 uint8_t vu8;
2467 uint16_t vu16;
2468 uint32_t vu32;
2469 const char *vstr;
2470 bool vbool;
2471 } value;
2472 };
2473
2474 static int cmd_dev_param_set_cb(const struct nlmsghdr *nlh, void *data)
2475 {
2476 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
2477 struct nlattr *nla_param[DEVLINK_ATTR_MAX + 1] = {};
2478 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
2479 struct nlattr *param_value_attr;
2480 enum devlink_param_cmode cmode;
2481 struct param_ctx *ctx = data;
2482 struct dl *dl = ctx->dl;
2483 int nla_type;
2484 int err;
2485
2486 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
2487 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
2488 !tb[DEVLINK_ATTR_PARAM])
2489 return MNL_CB_ERROR;
2490
2491 err = mnl_attr_parse_nested(tb[DEVLINK_ATTR_PARAM], attr_cb, nla_param);
2492 if (err != MNL_CB_OK)
2493 return MNL_CB_ERROR;
2494
2495 if (!nla_param[DEVLINK_ATTR_PARAM_TYPE] ||
2496 !nla_param[DEVLINK_ATTR_PARAM_VALUES_LIST])
2497 return MNL_CB_ERROR;
2498
2499 nla_type = mnl_attr_get_u8(nla_param[DEVLINK_ATTR_PARAM_TYPE]);
2500 mnl_attr_for_each_nested(param_value_attr,
2501 nla_param[DEVLINK_ATTR_PARAM_VALUES_LIST]) {
2502 struct nlattr *nla_value[DEVLINK_ATTR_MAX + 1] = {};
2503 struct nlattr *val_attr;
2504
2505 err = mnl_attr_parse_nested(param_value_attr,
2506 attr_cb, nla_value);
2507 if (err != MNL_CB_OK)
2508 return MNL_CB_ERROR;
2509
2510 if (!nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE] ||
2511 (nla_type != MNL_TYPE_FLAG &&
2512 !nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA]))
2513 return MNL_CB_ERROR;
2514
2515 cmode = mnl_attr_get_u8(nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE]);
2516 if (cmode == dl->opts.cmode) {
2517 val_attr = nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA];
2518 switch (nla_type) {
2519 case MNL_TYPE_U8:
2520 ctx->value.vu8 = mnl_attr_get_u8(val_attr);
2521 break;
2522 case MNL_TYPE_U16:
2523 ctx->value.vu16 = mnl_attr_get_u16(val_attr);
2524 break;
2525 case MNL_TYPE_U32:
2526 ctx->value.vu32 = mnl_attr_get_u32(val_attr);
2527 break;
2528 case MNL_TYPE_STRING:
2529 ctx->value.vstr = mnl_attr_get_str(val_attr);
2530 break;
2531 case MNL_TYPE_FLAG:
2532 ctx->value.vbool = val_attr ? true : false;
2533 break;
2534 }
2535 break;
2536 }
2537 }
2538 ctx->nla_type = nla_type;
2539 return MNL_CB_OK;
2540 }
2541
2542 static int cmd_dev_param_set(struct dl *dl)
2543 {
2544 struct param_ctx ctx = {};
2545 struct nlmsghdr *nlh;
2546 bool conv_exists;
2547 uint32_t val_u32;
2548 uint16_t val_u16;
2549 uint8_t val_u8;
2550 bool val_bool;
2551 int err;
2552
2553 err = dl_argv_parse(dl, DL_OPT_HANDLE |
2554 DL_OPT_PARAM_NAME |
2555 DL_OPT_PARAM_VALUE |
2556 DL_OPT_PARAM_CMODE, 0);
2557 if (err)
2558 return err;
2559
2560 /* Get value type */
2561 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PARAM_GET,
2562 NLM_F_REQUEST | NLM_F_ACK);
2563 dl_opts_put(nlh, dl);
2564
2565 ctx.dl = dl;
2566 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dev_param_set_cb, &ctx);
2567 if (err)
2568 return err;
2569
2570 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PARAM_SET,
2571 NLM_F_REQUEST | NLM_F_ACK);
2572 dl_opts_put(nlh, dl);
2573
2574 conv_exists = param_val_conv_exists(param_val_conv, PARAM_VAL_CONV_LEN,
2575 dl->opts.param_name);
2576
2577 mnl_attr_put_u8(nlh, DEVLINK_ATTR_PARAM_TYPE, ctx.nla_type);
2578 switch (ctx.nla_type) {
2579 case MNL_TYPE_U8:
2580 if (conv_exists) {
2581 err = param_val_conv_uint_get(param_val_conv,
2582 PARAM_VAL_CONV_LEN,
2583 dl->opts.param_name,
2584 dl->opts.param_value,
2585 &val_u32);
2586 val_u8 = val_u32;
2587 } else {
2588 err = strtouint8_t(dl->opts.param_value, &val_u8);
2589 }
2590 if (err)
2591 goto err_param_value_parse;
2592 if (val_u8 == ctx.value.vu8)
2593 return 0;
2594 mnl_attr_put_u8(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u8);
2595 break;
2596 case MNL_TYPE_U16:
2597 if (conv_exists) {
2598 err = param_val_conv_uint_get(param_val_conv,
2599 PARAM_VAL_CONV_LEN,
2600 dl->opts.param_name,
2601 dl->opts.param_value,
2602 &val_u32);
2603 val_u16 = val_u32;
2604 } else {
2605 err = strtouint16_t(dl->opts.param_value, &val_u16);
2606 }
2607 if (err)
2608 goto err_param_value_parse;
2609 if (val_u16 == ctx.value.vu16)
2610 return 0;
2611 mnl_attr_put_u16(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u16);
2612 break;
2613 case MNL_TYPE_U32:
2614 if (conv_exists)
2615 err = param_val_conv_uint_get(param_val_conv,
2616 PARAM_VAL_CONV_LEN,
2617 dl->opts.param_name,
2618 dl->opts.param_value,
2619 &val_u32);
2620 else
2621 err = strtouint32_t(dl->opts.param_value, &val_u32);
2622 if (err)
2623 goto err_param_value_parse;
2624 if (val_u32 == ctx.value.vu32)
2625 return 0;
2626 mnl_attr_put_u32(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u32);
2627 break;
2628 case MNL_TYPE_FLAG:
2629 err = strtobool(dl->opts.param_value, &val_bool);
2630 if (err)
2631 goto err_param_value_parse;
2632 if (val_bool == ctx.value.vbool)
2633 return 0;
2634 if (val_bool)
2635 mnl_attr_put(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA,
2636 0, NULL);
2637 break;
2638 case MNL_TYPE_STRING:
2639 mnl_attr_put_strz(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA,
2640 dl->opts.param_value);
2641 if (!strcmp(dl->opts.param_value, ctx.value.vstr))
2642 return 0;
2643 break;
2644 default:
2645 printf("Value type not supported\n");
2646 return -ENOTSUP;
2647 }
2648 return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
2649
2650 err_param_value_parse:
2651 pr_err("Value \"%s\" is not a number or not within range\n",
2652 dl->opts.param_value);
2653 return err;
2654 }
2655
2656 static int cmd_dev_param_show(struct dl *dl)
2657 {
2658 uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
2659 struct nlmsghdr *nlh;
2660 int err;
2661
2662 if (dl_argc(dl) == 0)
2663 flags |= NLM_F_DUMP;
2664
2665 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PARAM_GET, flags);
2666
2667 if (dl_argc(dl) > 0) {
2668 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE |
2669 DL_OPT_PARAM_NAME, 0);
2670 if (err)
2671 return err;
2672 }
2673
2674 pr_out_section_start(dl, "param");
2675 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dev_param_show_cb, dl);
2676 pr_out_section_end(dl);
2677 return err;
2678 }
2679
2680 static int cmd_dev_param(struct dl *dl)
2681 {
2682 if (dl_argv_match(dl, "help")) {
2683 cmd_dev_help();
2684 return 0;
2685 } else if (dl_argv_match(dl, "show") ||
2686 dl_argv_match(dl, "list") || dl_no_arg(dl)) {
2687 dl_arg_inc(dl);
2688 return cmd_dev_param_show(dl);
2689 } else if (dl_argv_match(dl, "set")) {
2690 dl_arg_inc(dl);
2691 return cmd_dev_param_set(dl);
2692 }
2693 pr_err("Command \"%s\" not found\n", dl_argv(dl));
2694 return -ENOENT;
2695 }
2696 static int cmd_dev_show_cb(const struct nlmsghdr *nlh, void *data)
2697 {
2698 struct dl *dl = data;
2699 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
2700 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
2701 uint8_t reload_failed = 0;
2702
2703 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
2704 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME])
2705 return MNL_CB_ERROR;
2706
2707 if (tb[DEVLINK_ATTR_RELOAD_FAILED])
2708 reload_failed = mnl_attr_get_u8(tb[DEVLINK_ATTR_RELOAD_FAILED]);
2709
2710 if (reload_failed) {
2711 __pr_out_handle_start(dl, tb, true, false);
2712 check_indent_newline(dl);
2713 print_bool(PRINT_ANY, "reload_failed", "reload_failed %s", true);
2714 pr_out_handle_end(dl);
2715 } else {
2716 pr_out_handle(dl, tb);
2717 }
2718
2719 return MNL_CB_OK;
2720 }
2721
2722 static int cmd_dev_show(struct dl *dl)
2723 {
2724 struct nlmsghdr *nlh;
2725 uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
2726 int err;
2727
2728 if (dl_argc(dl) == 0)
2729 flags |= NLM_F_DUMP;
2730
2731 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_GET, flags);
2732
2733 if (dl_argc(dl) > 0) {
2734 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, 0);
2735 if (err)
2736 return err;
2737 }
2738
2739 pr_out_section_start(dl, "dev");
2740 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dev_show_cb, dl);
2741 pr_out_section_end(dl);
2742 return err;
2743 }
2744
2745 static void cmd_dev_reload_help(void)
2746 {
2747 pr_err("Usage: devlink dev reload DEV [ netns { PID | NAME | ID } ]\n");
2748 }
2749
2750 static int cmd_dev_reload(struct dl *dl)
2751 {
2752 struct nlmsghdr *nlh;
2753 int err;
2754
2755 if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
2756 cmd_dev_reload_help();
2757 return 0;
2758 }
2759
2760 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RELOAD,
2761 NLM_F_REQUEST | NLM_F_ACK);
2762
2763 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, DL_OPT_NETNS);
2764 if (err)
2765 return err;
2766
2767 return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
2768 }
2769
2770 static void pr_out_versions_single(struct dl *dl, const struct nlmsghdr *nlh,
2771 const char *name, int type)
2772 {
2773 struct nlattr *version;
2774
2775 mnl_attr_for_each(version, nlh, sizeof(struct genlmsghdr)) {
2776 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
2777 const char *ver_value;
2778 const char *ver_name;
2779 int err;
2780
2781 if (mnl_attr_get_type(version) != type)
2782 continue;
2783
2784 err = mnl_attr_parse_nested(version, attr_cb, tb);
2785 if (err != MNL_CB_OK)
2786 continue;
2787
2788 if (!tb[DEVLINK_ATTR_INFO_VERSION_NAME] ||
2789 !tb[DEVLINK_ATTR_INFO_VERSION_VALUE])
2790 continue;
2791
2792 if (name) {
2793 pr_out_object_start(dl, name);
2794 name = NULL;
2795 }
2796
2797 ver_name = mnl_attr_get_str(tb[DEVLINK_ATTR_INFO_VERSION_NAME]);
2798 ver_value = mnl_attr_get_str(tb[DEVLINK_ATTR_INFO_VERSION_VALUE]);
2799
2800 check_indent_newline(dl);
2801 print_string_name_value(ver_name, ver_value);
2802 if (!dl->json_output)
2803 __pr_out_newline();
2804 }
2805
2806 if (!name)
2807 pr_out_object_end(dl);
2808 }
2809
2810 static void pr_out_info(struct dl *dl, const struct nlmsghdr *nlh,
2811 struct nlattr **tb, bool has_versions)
2812 {
2813 __pr_out_handle_start(dl, tb, true, false);
2814
2815 __pr_out_indent_inc();
2816 if (tb[DEVLINK_ATTR_INFO_DRIVER_NAME]) {
2817 struct nlattr *nla_drv = tb[DEVLINK_ATTR_INFO_DRIVER_NAME];
2818
2819 if (!dl->json_output)
2820 __pr_out_newline();
2821 check_indent_newline(dl);
2822 print_string(PRINT_ANY, "driver", "driver %s",
2823 mnl_attr_get_str(nla_drv));
2824 }
2825
2826 if (tb[DEVLINK_ATTR_INFO_SERIAL_NUMBER]) {
2827 struct nlattr *nla_sn = tb[DEVLINK_ATTR_INFO_SERIAL_NUMBER];
2828
2829 if (!dl->json_output)
2830 __pr_out_newline();
2831 check_indent_newline(dl);
2832 print_string(PRINT_ANY, "serial_number", "serial_number %s",
2833 mnl_attr_get_str(nla_sn));
2834 }
2835 __pr_out_indent_dec();
2836
2837 if (has_versions) {
2838 pr_out_object_start(dl, "versions");
2839
2840 pr_out_versions_single(dl, nlh, "fixed",
2841 DEVLINK_ATTR_INFO_VERSION_FIXED);
2842 pr_out_versions_single(dl, nlh, "running",
2843 DEVLINK_ATTR_INFO_VERSION_RUNNING);
2844 pr_out_versions_single(dl, nlh, "stored",
2845 DEVLINK_ATTR_INFO_VERSION_STORED);
2846
2847 pr_out_object_end(dl);
2848 }
2849
2850 pr_out_handle_end(dl);
2851 }
2852
2853 static int cmd_versions_show_cb(const struct nlmsghdr *nlh, void *data)
2854 {
2855 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
2856 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
2857 bool has_versions, has_info;
2858 struct dl *dl = data;
2859
2860 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
2861
2862 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME])
2863 return MNL_CB_ERROR;
2864
2865 has_versions = tb[DEVLINK_ATTR_INFO_VERSION_FIXED] ||
2866 tb[DEVLINK_ATTR_INFO_VERSION_RUNNING] ||
2867 tb[DEVLINK_ATTR_INFO_VERSION_STORED];
2868 has_info = tb[DEVLINK_ATTR_INFO_DRIVER_NAME] ||
2869 tb[DEVLINK_ATTR_INFO_SERIAL_NUMBER] ||
2870 has_versions;
2871
2872 if (has_info)
2873 pr_out_info(dl, nlh, tb, has_versions);
2874
2875 return MNL_CB_OK;
2876 }
2877
2878 static void cmd_dev_info_help(void)
2879 {
2880 pr_err("Usage: devlink dev info [ DEV ]\n");
2881 }
2882
2883 static int cmd_dev_info(struct dl *dl)
2884 {
2885 struct nlmsghdr *nlh;
2886 uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
2887 int err;
2888
2889 if (dl_argv_match(dl, "help")) {
2890 cmd_dev_info_help();
2891 return 0;
2892 }
2893
2894 if (dl_argc(dl) == 0)
2895 flags |= NLM_F_DUMP;
2896
2897 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_INFO_GET, flags);
2898
2899 if (dl_argc(dl) > 0) {
2900 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, 0);
2901 if (err)
2902 return err;
2903 }
2904
2905 pr_out_section_start(dl, "info");
2906 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_versions_show_cb, dl);
2907 pr_out_section_end(dl);
2908 return err;
2909 }
2910
2911 static void cmd_dev_flash_help(void)
2912 {
2913 pr_err("Usage: devlink dev flash DEV file PATH [ component NAME ]\n");
2914 }
2915
2916
2917 struct cmd_dev_flash_status_ctx {
2918 struct dl *dl;
2919 char *last_msg;
2920 char *last_component;
2921 uint8_t not_first:1,
2922 last_pc:1,
2923 received_end:1,
2924 flash_done:1;
2925 };
2926
2927 static int nullstrcmp(const char *str1, const char *str2)
2928 {
2929 if (str1 && str2)
2930 return strcmp(str1, str2);
2931 if (!str1 && !str2)
2932 return 0;
2933 return str1 ? 1 : -1;
2934 }
2935
2936 static int cmd_dev_flash_status_cb(const struct nlmsghdr *nlh, void *data)
2937 {
2938 struct cmd_dev_flash_status_ctx *ctx = data;
2939 struct dl_opts *opts = &ctx->dl->opts;
2940 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
2941 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
2942 const char *component = NULL;
2943 uint64_t done = 0, total = 0;
2944 const char *msg = NULL;
2945 const char *bus_name;
2946 const char *dev_name;
2947
2948 if (genl->cmd != DEVLINK_CMD_FLASH_UPDATE_STATUS &&
2949 genl->cmd != DEVLINK_CMD_FLASH_UPDATE_END)
2950 return MNL_CB_STOP;
2951
2952 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
2953 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME])
2954 return MNL_CB_ERROR;
2955 bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]);
2956 dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]);
2957 if (strcmp(bus_name, opts->bus_name) ||
2958 strcmp(dev_name, opts->dev_name))
2959 return MNL_CB_ERROR;
2960
2961 if (genl->cmd == DEVLINK_CMD_FLASH_UPDATE_END && ctx->not_first) {
2962 pr_out("\n");
2963 free(ctx->last_msg);
2964 free(ctx->last_component);
2965 ctx->received_end = 1;
2966 return MNL_CB_STOP;
2967 }
2968
2969 if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG])
2970 msg = mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG]);
2971 if (tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT])
2972 component = mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT]);
2973 if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE])
2974 done = mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE]);
2975 if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL])
2976 total = mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL]);
2977
2978 if (!nullstrcmp(msg, ctx->last_msg) &&
2979 !nullstrcmp(component, ctx->last_component) &&
2980 ctx->last_pc && ctx->not_first) {
2981 pr_out_tty("\b\b\b\b\b"); /* clean percentage */
2982 } else {
2983 if (ctx->not_first)
2984 pr_out("\n");
2985 if (component) {
2986 pr_out("[%s] ", component);
2987 free(ctx->last_component);
2988 ctx->last_component = strdup(component);
2989 }
2990 if (msg) {
2991 pr_out("%s", msg);
2992 free(ctx->last_msg);
2993 ctx->last_msg = strdup(msg);
2994 }
2995 }
2996 if (total) {
2997 pr_out_tty(" %3lu%%", (done * 100) / total);
2998 ctx->last_pc = 1;
2999 } else {
3000 ctx->last_pc = 0;
3001 }
3002 fflush(stdout);
3003 ctx->not_first = 1;
3004
3005 return MNL_CB_STOP;
3006 }
3007
3008 static int cmd_dev_flash_fds_process(struct cmd_dev_flash_status_ctx *ctx,
3009 struct mnlg_socket *nlg_ntf,
3010 int pipe_r)
3011 {
3012 int nlfd = mnlg_socket_get_fd(nlg_ntf);
3013 fd_set fds[3];
3014 int fdmax;
3015 int i;
3016 int err;
3017 int err2;
3018
3019 for (i = 0; i < 3; i++)
3020 FD_ZERO(&fds[i]);
3021 FD_SET(pipe_r, &fds[0]);
3022 fdmax = pipe_r + 1;
3023 FD_SET(nlfd, &fds[0]);
3024 if (nlfd >= fdmax)
3025 fdmax = nlfd + 1;
3026
3027 while (select(fdmax, &fds[0], &fds[1], &fds[2], NULL) < 0) {
3028 if (errno == EINTR)
3029 continue;
3030 pr_err("select() failed\n");
3031 return -errno;
3032 }
3033 if (FD_ISSET(nlfd, &fds[0])) {
3034 err = _mnlg_socket_recv_run(nlg_ntf,
3035 cmd_dev_flash_status_cb, ctx);
3036 if (err)
3037 return err;
3038 }
3039 if (FD_ISSET(pipe_r, &fds[0])) {
3040 err = read(pipe_r, &err2, sizeof(err2));
3041 if (err == -1) {
3042 pr_err("Failed to read pipe\n");
3043 return -errno;
3044 }
3045 if (err2)
3046 return err2;
3047 ctx->flash_done = 1;
3048 }
3049 return 0;
3050 }
3051
3052
3053 static int cmd_dev_flash(struct dl *dl)
3054 {
3055 struct cmd_dev_flash_status_ctx ctx = {.dl = dl,};
3056 struct mnlg_socket *nlg_ntf;
3057 struct nlmsghdr *nlh;
3058 int pipe_r, pipe_w;
3059 int pipe_fds[2];
3060 pid_t pid;
3061 int err;
3062
3063 if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
3064 cmd_dev_flash_help();
3065 return 0;
3066 }
3067
3068 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_FLASH_UPDATE,
3069 NLM_F_REQUEST | NLM_F_ACK);
3070
3071 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_FLASH_FILE_NAME,
3072 DL_OPT_FLASH_COMPONENT);
3073 if (err)
3074 return err;
3075
3076 nlg_ntf = mnlg_socket_open(DEVLINK_GENL_NAME, DEVLINK_GENL_VERSION);
3077 if (!nlg_ntf)
3078 return err;
3079
3080 err = _mnlg_socket_group_add(nlg_ntf, DEVLINK_GENL_MCGRP_CONFIG_NAME);
3081 if (err)
3082 return err;
3083
3084 err = pipe(pipe_fds);
3085 if (err == -1)
3086 return -errno;
3087 pipe_r = pipe_fds[0];
3088 pipe_w = pipe_fds[1];
3089
3090 pid = fork();
3091 if (pid == -1) {
3092 close(pipe_r);
3093 close(pipe_w);
3094 return -errno;
3095 } else if (!pid) {
3096 /* In child, just execute the flash and pass returned
3097 * value through pipe once it is done.
3098 */
3099 int cc;
3100
3101 close(pipe_r);
3102 err = _mnlg_socket_send(dl->nlg, nlh);
3103 cc = write(pipe_w, &err, sizeof(err));
3104 close(pipe_w);
3105 exit(cc != sizeof(err));
3106 }
3107 close(pipe_w);
3108
3109 do {
3110 err = cmd_dev_flash_fds_process(&ctx, nlg_ntf, pipe_r);
3111 if (err)
3112 goto out;
3113 } while (!ctx.flash_done || (ctx.not_first && !ctx.received_end));
3114
3115 err = _mnlg_socket_recv_run(dl->nlg, NULL, NULL);
3116 out:
3117 close(pipe_r);
3118 mnlg_socket_close(nlg_ntf);
3119 return err;
3120 }
3121
3122 static int cmd_dev(struct dl *dl)
3123 {
3124 if (dl_argv_match(dl, "help")) {
3125 cmd_dev_help();
3126 return 0;
3127 } else if (dl_argv_match(dl, "show") ||
3128 dl_argv_match(dl, "list") || dl_no_arg(dl)) {
3129 dl_arg_inc(dl);
3130 return cmd_dev_show(dl);
3131 } else if (dl_argv_match(dl, "eswitch")) {
3132 dl_arg_inc(dl);
3133 return cmd_dev_eswitch(dl);
3134 } else if (dl_argv_match(dl, "reload")) {
3135 dl_arg_inc(dl);
3136 return cmd_dev_reload(dl);
3137 } else if (dl_argv_match(dl, "param")) {
3138 dl_arg_inc(dl);
3139 return cmd_dev_param(dl);
3140 } else if (dl_argv_match(dl, "info")) {
3141 dl_arg_inc(dl);
3142 return cmd_dev_info(dl);
3143 } else if (dl_argv_match(dl, "flash")) {
3144 dl_arg_inc(dl);
3145 return cmd_dev_flash(dl);
3146 }
3147 pr_err("Command \"%s\" not found\n", dl_argv(dl));
3148 return -ENOENT;
3149 }
3150
3151 static void cmd_port_help(void)
3152 {
3153 pr_err("Usage: devlink port show [ DEV/PORT_INDEX ]\n");
3154 pr_err(" devlink port set DEV/PORT_INDEX [ type { eth | ib | auto} ]\n");
3155 pr_err(" devlink port split DEV/PORT_INDEX count COUNT\n");
3156 pr_err(" devlink port unsplit DEV/PORT_INDEX\n");
3157 }
3158
3159 static const char *port_type_name(uint32_t type)
3160 {
3161 switch (type) {
3162 case DEVLINK_PORT_TYPE_NOTSET: return "notset";
3163 case DEVLINK_PORT_TYPE_AUTO: return "auto";
3164 case DEVLINK_PORT_TYPE_ETH: return "eth";
3165 case DEVLINK_PORT_TYPE_IB: return "ib";
3166 default: return "<unknown type>";
3167 }
3168 }
3169
3170 static const char *port_flavour_name(uint16_t flavour)
3171 {
3172 switch (flavour) {
3173 case DEVLINK_PORT_FLAVOUR_PHYSICAL:
3174 return "physical";
3175 case DEVLINK_PORT_FLAVOUR_CPU:
3176 return "cpu";
3177 case DEVLINK_PORT_FLAVOUR_DSA:
3178 return "dsa";
3179 case DEVLINK_PORT_FLAVOUR_PCI_PF:
3180 return "pcipf";
3181 case DEVLINK_PORT_FLAVOUR_PCI_VF:
3182 return "pcivf";
3183 case DEVLINK_PORT_FLAVOUR_VIRTUAL:
3184 return "virtual";
3185 default:
3186 return "<unknown flavour>";
3187 }
3188 }
3189
3190 static void pr_out_port_pfvf_num(struct dl *dl, struct nlattr **tb)
3191 {
3192 uint16_t fn_num;
3193
3194 if (tb[DEVLINK_ATTR_PORT_PCI_PF_NUMBER]) {
3195 fn_num = mnl_attr_get_u16(tb[DEVLINK_ATTR_PORT_PCI_PF_NUMBER]);
3196 print_uint(PRINT_ANY, "pfnum", " pfnum %u", fn_num);
3197 }
3198 if (tb[DEVLINK_ATTR_PORT_PCI_VF_NUMBER]) {
3199 fn_num = mnl_attr_get_u16(tb[DEVLINK_ATTR_PORT_PCI_VF_NUMBER]);
3200 print_uint(PRINT_ANY, "vfnum", " vfnum %u", fn_num);
3201 }
3202 }
3203
3204 static void pr_out_port(struct dl *dl, struct nlattr **tb)
3205 {
3206 struct nlattr *pt_attr = tb[DEVLINK_ATTR_PORT_TYPE];
3207 struct nlattr *dpt_attr = tb[DEVLINK_ATTR_PORT_DESIRED_TYPE];
3208
3209 pr_out_port_handle_start(dl, tb, false);
3210 check_indent_newline(dl);
3211 if (pt_attr) {
3212 uint16_t port_type = mnl_attr_get_u16(pt_attr);
3213
3214 print_string(PRINT_ANY, "type", "type %s",
3215 port_type_name(port_type));
3216 if (dpt_attr) {
3217 uint16_t des_port_type = mnl_attr_get_u16(dpt_attr);
3218
3219 if (port_type != des_port_type)
3220 print_string(PRINT_ANY, "des_type", " des_type %s",
3221 port_type_name(des_port_type));
3222 }
3223 }
3224 if (tb[DEVLINK_ATTR_PORT_NETDEV_NAME]) {
3225 print_string(PRINT_ANY, "netdev", " netdev %s",
3226 mnl_attr_get_str(tb[DEVLINK_ATTR_PORT_NETDEV_NAME]));
3227 }
3228 if (tb[DEVLINK_ATTR_PORT_IBDEV_NAME]) {
3229 print_string(PRINT_ANY, "ibdev", " ibdev %s",
3230 mnl_attr_get_str(tb[DEVLINK_ATTR_PORT_IBDEV_NAME]));
3231 }
3232 if (tb[DEVLINK_ATTR_PORT_FLAVOUR]) {
3233 uint16_t port_flavour =
3234 mnl_attr_get_u16(tb[DEVLINK_ATTR_PORT_FLAVOUR]);
3235
3236 print_string(PRINT_ANY, "flavour", " flavour %s",
3237 port_flavour_name(port_flavour));
3238
3239 switch (port_flavour) {
3240 case DEVLINK_PORT_FLAVOUR_PCI_PF:
3241 case DEVLINK_PORT_FLAVOUR_PCI_VF:
3242 pr_out_port_pfvf_num(dl, tb);
3243 break;
3244 default:
3245 break;
3246 }
3247 }
3248 if (tb[DEVLINK_ATTR_PORT_NUMBER]) {
3249 uint32_t port_number;
3250
3251 port_number = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_NUMBER]);
3252 print_uint(PRINT_ANY, "port", " port %u", port_number);
3253 }
3254 if (tb[DEVLINK_ATTR_PORT_SPLIT_GROUP])
3255 print_uint(PRINT_ANY, "split_group", " split_group %u",
3256 mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_SPLIT_GROUP]));
3257 pr_out_port_handle_end(dl);
3258 }
3259
3260 static int cmd_port_show_cb(const struct nlmsghdr *nlh, void *data)
3261 {
3262 struct dl *dl = data;
3263 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
3264 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
3265
3266 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
3267 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
3268 !tb[DEVLINK_ATTR_PORT_INDEX])
3269 return MNL_CB_ERROR;
3270 pr_out_port(dl, tb);
3271 return MNL_CB_OK;
3272 }
3273
3274 static int cmd_port_show(struct dl *dl)
3275 {
3276 struct nlmsghdr *nlh;
3277 uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
3278 int err;
3279
3280 if (dl_argc(dl) == 0)
3281 flags |= NLM_F_DUMP;
3282
3283 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_GET, flags);
3284
3285 if (dl_argc(dl) > 0) {
3286 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP, 0);
3287 if (err)
3288 return err;
3289 }
3290
3291 pr_out_section_start(dl, "port");
3292 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_port_show_cb, dl);
3293 pr_out_section_end(dl);
3294 return err;
3295 }
3296
3297 static int cmd_port_set(struct dl *dl)
3298 {
3299 struct nlmsghdr *nlh;
3300 int err;
3301
3302 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_SET,
3303 NLM_F_REQUEST | NLM_F_ACK);
3304
3305 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_PORT_TYPE, 0);
3306 if (err)
3307 return err;
3308
3309 return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
3310 }
3311
3312 static int cmd_port_split(struct dl *dl)
3313 {
3314 struct nlmsghdr *nlh;
3315 int err;
3316
3317 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_SPLIT,
3318 NLM_F_REQUEST | NLM_F_ACK);
3319
3320 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_PORT_COUNT, 0);
3321 if (err)
3322 return err;
3323
3324 return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
3325 }
3326
3327 static int cmd_port_unsplit(struct dl *dl)
3328 {
3329 struct nlmsghdr *nlh;
3330 int err;
3331
3332 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_UNSPLIT,
3333 NLM_F_REQUEST | NLM_F_ACK);
3334
3335 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP, 0);
3336 if (err)
3337 return err;
3338
3339 return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
3340 }
3341
3342 static int cmd_port(struct dl *dl)
3343 {
3344 if (dl_argv_match(dl, "help")) {
3345 cmd_port_help();
3346 return 0;
3347 } else if (dl_argv_match(dl, "show") ||
3348 dl_argv_match(dl, "list") || dl_no_arg(dl)) {
3349 dl_arg_inc(dl);
3350 return cmd_port_show(dl);
3351 } else if (dl_argv_match(dl, "set")) {
3352 dl_arg_inc(dl);
3353 return cmd_port_set(dl);
3354 } else if (dl_argv_match(dl, "split")) {
3355 dl_arg_inc(dl);
3356 return cmd_port_split(dl);
3357 } else if (dl_argv_match(dl, "unsplit")) {
3358 dl_arg_inc(dl);
3359 return cmd_port_unsplit(dl);
3360 }
3361 pr_err("Command \"%s\" not found\n", dl_argv(dl));
3362 return -ENOENT;
3363 }
3364
3365 static void cmd_sb_help(void)
3366 {
3367 pr_err("Usage: devlink sb show [ DEV [ sb SB_INDEX ] ]\n");
3368 pr_err(" devlink sb pool show [ DEV [ sb SB_INDEX ] pool POOL_INDEX ]\n");
3369 pr_err(" devlink sb pool set DEV [ sb SB_INDEX ] pool POOL_INDEX\n");
3370 pr_err(" size POOL_SIZE thtype { static | dynamic }\n");
3371 pr_err(" devlink sb port pool show [ DEV/PORT_INDEX [ sb SB_INDEX ]\n");
3372 pr_err(" pool POOL_INDEX ]\n");
3373 pr_err(" devlink sb port pool set DEV/PORT_INDEX [ sb SB_INDEX ]\n");
3374 pr_err(" pool POOL_INDEX th THRESHOLD\n");
3375 pr_err(" devlink sb tc bind show [ DEV/PORT_INDEX [ sb SB_INDEX ] tc TC_INDEX\n");
3376 pr_err(" type { ingress | egress } ]\n");
3377 pr_err(" devlink sb tc bind set DEV/PORT_INDEX [ sb SB_INDEX ] tc TC_INDEX\n");
3378 pr_err(" type { ingress | egress } pool POOL_INDEX\n");
3379 pr_err(" th THRESHOLD\n");
3380 pr_err(" devlink sb occupancy show { DEV | DEV/PORT_INDEX } [ sb SB_INDEX ]\n");
3381 pr_err(" devlink sb occupancy snapshot DEV [ sb SB_INDEX ]\n");
3382 pr_err(" devlink sb occupancy clearmax DEV [ sb SB_INDEX ]\n");
3383 }
3384
3385 static void pr_out_sb(struct dl *dl, struct nlattr **tb)
3386 {
3387 pr_out_handle_start_arr(dl, tb);
3388 check_indent_newline(dl);
3389 print_uint(PRINT_ANY, "sb", "sb %u",
3390 mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]));
3391 print_uint(PRINT_ANY, "size", " size %u",
3392 mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_SIZE]));
3393 print_uint(PRINT_ANY, "ing_pools", " ing_pools %u",
3394 mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_INGRESS_POOL_COUNT]));
3395 print_uint(PRINT_ANY, "eg_pools", " eg_pools %u",
3396 mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_EGRESS_POOL_COUNT]));
3397 print_uint(PRINT_ANY, "ing_tcs", " ing_tcs %u",
3398 mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_INGRESS_TC_COUNT]));
3399 print_uint(PRINT_ANY, "eg_tcs", " eg_tcs %u",
3400 mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_EGRESS_TC_COUNT]));
3401 pr_out_handle_end(dl);
3402 }
3403
3404 static int cmd_sb_show_cb(const struct nlmsghdr *nlh, void *data)
3405 {
3406 struct dl *dl = data;
3407 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
3408 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
3409
3410 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
3411 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
3412 !tb[DEVLINK_ATTR_SB_INDEX] || !tb[DEVLINK_ATTR_SB_SIZE] ||
3413 !tb[DEVLINK_ATTR_SB_INGRESS_POOL_COUNT] ||
3414 !tb[DEVLINK_ATTR_SB_EGRESS_POOL_COUNT] ||
3415 !tb[DEVLINK_ATTR_SB_INGRESS_TC_COUNT] ||
3416 !tb[DEVLINK_ATTR_SB_EGRESS_TC_COUNT])
3417 return MNL_CB_ERROR;
3418 pr_out_sb(dl, tb);
3419 return MNL_CB_OK;
3420 }
3421
3422 static int cmd_sb_show(struct dl *dl)
3423 {
3424 struct nlmsghdr *nlh;
3425 uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
3426 int err;
3427
3428 if (dl_argc(dl) == 0)
3429 flags |= NLM_F_DUMP;
3430
3431 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_GET, flags);
3432
3433 if (dl_argc(dl) > 0) {
3434 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, DL_OPT_SB);
3435 if (err)
3436 return err;
3437 }
3438
3439 pr_out_section_start(dl, "sb");
3440 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_show_cb, dl);
3441 pr_out_section_end(dl);
3442 return err;
3443 }
3444
3445 static const char *pool_type_name(uint8_t type)
3446 {
3447 switch (type) {
3448 case DEVLINK_SB_POOL_TYPE_INGRESS: return "ingress";
3449 case DEVLINK_SB_POOL_TYPE_EGRESS: return "egress";
3450 default: return "<unknown type>";
3451 }
3452 }
3453
3454 static const char *threshold_type_name(uint8_t type)
3455 {
3456 switch (type) {
3457 case DEVLINK_SB_THRESHOLD_TYPE_STATIC: return "static";
3458 case DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC: return "dynamic";
3459 default: return "<unknown type>";
3460 }
3461 }
3462
3463 static void pr_out_sb_pool(struct dl *dl, struct nlattr **tb)
3464 {
3465 pr_out_handle_start_arr(dl, tb);
3466 check_indent_newline(dl);
3467 print_uint(PRINT_ANY, "sb", "sb %u",
3468 mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]));
3469 print_uint(PRINT_ANY, "pool", " pool %u",
3470 mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]));
3471 print_string(PRINT_ANY, "type", " type %s",
3472 pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE])));
3473 print_uint(PRINT_ANY, "size", " size %u",
3474 mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_POOL_SIZE]));
3475 print_string(PRINT_ANY, "thtype", " thtype %s",
3476 threshold_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE])));
3477 if (tb[DEVLINK_ATTR_SB_POOL_CELL_SIZE])
3478 print_uint(PRINT_ANY, "cell_size", " cell size %u",
3479 mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_POOL_CELL_SIZE]));
3480 pr_out_handle_end(dl);
3481 }
3482
3483 static int cmd_sb_pool_show_cb(const struct nlmsghdr *nlh, void *data)
3484 {
3485 struct dl *dl = data;
3486 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
3487 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
3488
3489 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
3490 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
3491 !tb[DEVLINK_ATTR_SB_INDEX] || !tb[DEVLINK_ATTR_SB_POOL_INDEX] ||
3492 !tb[DEVLINK_ATTR_SB_POOL_TYPE] || !tb[DEVLINK_ATTR_SB_POOL_SIZE] ||
3493 !tb[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE])
3494 return MNL_CB_ERROR;
3495 pr_out_sb_pool(dl, tb);
3496 return MNL_CB_OK;
3497 }
3498
3499 static int cmd_sb_pool_show(struct dl *dl)
3500 {
3501 struct nlmsghdr *nlh;
3502 uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
3503 int err;
3504
3505 if (dl_argc(dl) == 0)
3506 flags |= NLM_F_DUMP;
3507
3508 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_POOL_GET, flags);
3509
3510 if (dl_argc(dl) > 0) {
3511 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_SB_POOL,
3512 DL_OPT_SB);
3513 if (err)
3514 return err;
3515 }
3516
3517 pr_out_section_start(dl, "pool");
3518 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_pool_show_cb, dl);
3519 pr_out_section_end(dl);
3520 return err;
3521 }
3522
3523 static int cmd_sb_pool_set(struct dl *dl)
3524 {
3525 struct nlmsghdr *nlh;
3526 int err;
3527
3528 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_POOL_SET,
3529 NLM_F_REQUEST | NLM_F_ACK);
3530
3531 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_SB_POOL |
3532 DL_OPT_SB_SIZE | DL_OPT_SB_THTYPE, DL_OPT_SB);
3533 if (err)
3534 return err;
3535
3536 return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
3537 }
3538
3539 static int cmd_sb_pool(struct dl *dl)
3540 {
3541 if (dl_argv_match(dl, "help")) {
3542 cmd_sb_help();
3543 return 0;
3544 } else if (dl_argv_match(dl, "show") ||
3545 dl_argv_match(dl, "list") || dl_no_arg(dl)) {
3546 dl_arg_inc(dl);
3547 return cmd_sb_pool_show(dl);
3548 } else if (dl_argv_match(dl, "set")) {
3549 dl_arg_inc(dl);
3550 return cmd_sb_pool_set(dl);
3551 }
3552 pr_err("Command \"%s\" not found\n", dl_argv(dl));
3553 return -ENOENT;
3554 }
3555
3556 static void pr_out_sb_port_pool(struct dl *dl, struct nlattr **tb)
3557 {
3558 pr_out_port_handle_start_arr(dl, tb, true);
3559 check_indent_newline(dl);
3560 print_uint(PRINT_ANY, "sb", "sb %u",
3561 mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]));
3562 print_uint(PRINT_ANY, "pool", " pool %u",
3563 mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]));
3564 print_uint(PRINT_ANY, "threshold", " threshold %u",
3565 mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_THRESHOLD]));
3566 pr_out_port_handle_end(dl);
3567 }
3568
3569 static int cmd_sb_port_pool_show_cb(const struct nlmsghdr *nlh, void *data)
3570 {
3571 struct dl *dl = data;
3572 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
3573 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
3574
3575 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
3576 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
3577 !tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_SB_INDEX] ||
3578 !tb[DEVLINK_ATTR_SB_POOL_INDEX] || !tb[DEVLINK_ATTR_SB_THRESHOLD])
3579 return MNL_CB_ERROR;
3580 pr_out_sb_port_pool(dl, tb);
3581 return MNL_CB_OK;
3582 }
3583
3584 static int cmd_sb_port_pool_show(struct dl *dl)
3585 {
3586 struct nlmsghdr *nlh;
3587 uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
3588 int err;
3589
3590 if (dl_argc(dl) == 0)
3591 flags |= NLM_F_DUMP;
3592
3593 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_PORT_POOL_GET, flags);
3594
3595 if (dl_argc(dl) > 0) {
3596 err = dl_argv_parse_put(nlh, dl,
3597 DL_OPT_HANDLEP | DL_OPT_SB_POOL,
3598 DL_OPT_SB);
3599 if (err)
3600 return err;
3601 }
3602
3603 pr_out_section_start(dl, "port_pool");
3604 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_port_pool_show_cb, dl);
3605 pr_out_section_end(dl);
3606 return 0;
3607 }
3608
3609 static int cmd_sb_port_pool_set(struct dl *dl)
3610 {
3611 struct nlmsghdr *nlh;
3612 int err;
3613
3614 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_PORT_POOL_SET,
3615 NLM_F_REQUEST | NLM_F_ACK);
3616
3617 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_POOL |
3618 DL_OPT_SB_TH, DL_OPT_SB);
3619 if (err)
3620 return err;
3621
3622 return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
3623 }
3624
3625 static int cmd_sb_port_pool(struct dl *dl)
3626 {
3627 if (dl_argv_match(dl, "help")) {
3628 cmd_sb_help();
3629 return 0;
3630 } else if (dl_argv_match(dl, "show") ||
3631 dl_argv_match(dl, "list") || dl_no_arg(dl)) {
3632 dl_arg_inc(dl);
3633 return cmd_sb_port_pool_show(dl);
3634 } else if (dl_argv_match(dl, "set")) {
3635 dl_arg_inc(dl);
3636 return cmd_sb_port_pool_set(dl);
3637 }
3638 pr_err("Command \"%s\" not found\n", dl_argv(dl));
3639 return -ENOENT;
3640 }
3641
3642 static int cmd_sb_port(struct dl *dl)
3643 {
3644 if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
3645 cmd_sb_help();
3646 return 0;
3647 } else if (dl_argv_match(dl, "pool")) {
3648 dl_arg_inc(dl);
3649 return cmd_sb_port_pool(dl);
3650 }
3651 pr_err("Command \"%s\" not found\n", dl_argv(dl));
3652 return -ENOENT;
3653 }
3654
3655 static void pr_out_sb_tc_bind(struct dl *dl, struct nlattr **tb)
3656 {
3657 pr_out_port_handle_start_arr(dl, tb, true);
3658 check_indent_newline(dl);
3659 print_uint(PRINT_ANY, "sb", "sb %u",
3660 mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]));
3661 print_uint(PRINT_ANY, "tc", " tc %u",
3662 mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_TC_INDEX]));
3663 print_string(PRINT_ANY, "type", " type %s",
3664 pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE])));
3665 print_uint(PRINT_ANY, "pool", " pool %u",
3666 mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]));
3667 print_uint(PRINT_ANY, "threshold", " threshold %u",
3668 mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_THRESHOLD]));
3669 pr_out_port_handle_end(dl);
3670 }
3671
3672 static int cmd_sb_tc_bind_show_cb(const struct nlmsghdr *nlh, void *data)
3673 {
3674 struct dl *dl = data;
3675 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
3676 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
3677
3678 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
3679 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
3680 !tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_SB_INDEX] ||
3681 !tb[DEVLINK_ATTR_SB_TC_INDEX] || !tb[DEVLINK_ATTR_SB_POOL_TYPE] ||
3682 !tb[DEVLINK_ATTR_SB_POOL_INDEX] || !tb[DEVLINK_ATTR_SB_THRESHOLD])
3683 return MNL_CB_ERROR;
3684 pr_out_sb_tc_bind(dl, tb);
3685 return MNL_CB_OK;
3686 }
3687
3688 static int cmd_sb_tc_bind_show(struct dl *dl)
3689 {
3690 struct nlmsghdr *nlh;
3691 uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
3692 int err;
3693
3694 if (dl_argc(dl) == 0)
3695 flags |= NLM_F_DUMP;
3696
3697 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_TC_POOL_BIND_GET, flags);
3698
3699 if (dl_argc(dl) > 0) {
3700 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_TC |
3701 DL_OPT_SB_TYPE, DL_OPT_SB);
3702 if (err)
3703 return err;
3704 }
3705
3706 pr_out_section_start(dl, "tc_bind");
3707 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_tc_bind_show_cb, dl);
3708 pr_out_section_end(dl);
3709 return err;
3710 }
3711
3712 static int cmd_sb_tc_bind_set(struct dl *dl)
3713 {
3714 struct nlmsghdr *nlh;
3715 int err;
3716
3717 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_TC_POOL_BIND_SET,
3718 NLM_F_REQUEST | NLM_F_ACK);
3719
3720 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_TC |
3721 DL_OPT_SB_TYPE | DL_OPT_SB_POOL | DL_OPT_SB_TH,
3722 DL_OPT_SB);
3723 if (err)
3724 return err;
3725
3726 return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
3727 }
3728
3729 static int cmd_sb_tc_bind(struct dl *dl)
3730 {
3731 if (dl_argv_match(dl, "help")) {
3732 cmd_sb_help();
3733 return 0;
3734 } else if (dl_argv_match(dl, "show") ||
3735 dl_argv_match(dl, "list") || dl_no_arg(dl)) {
3736 dl_arg_inc(dl);
3737 return cmd_sb_tc_bind_show(dl);
3738 } else if (dl_argv_match(dl, "set")) {
3739 dl_arg_inc(dl);
3740 return cmd_sb_tc_bind_set(dl);
3741 }
3742 pr_err("Command \"%s\" not found\n", dl_argv(dl));
3743 return -ENOENT;
3744 }
3745
3746 static int cmd_sb_tc(struct dl *dl)
3747 {
3748 if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
3749 cmd_sb_help();
3750 return 0;
3751 } else if (dl_argv_match(dl, "bind")) {
3752 dl_arg_inc(dl);
3753 return cmd_sb_tc_bind(dl);
3754 }
3755 pr_err("Command \"%s\" not found\n", dl_argv(dl));
3756 return -ENOENT;
3757 }
3758
3759 struct occ_item {
3760 struct list_head list;
3761 uint32_t index;
3762 uint32_t cur;
3763 uint32_t max;
3764 uint32_t bound_pool_index;
3765 };
3766
3767 struct occ_port {
3768 struct list_head list;
3769 char *bus_name;
3770 char *dev_name;
3771 uint32_t port_index;
3772 uint32_t sb_index;
3773 struct list_head pool_list;
3774 struct list_head ing_tc_list;
3775 struct list_head eg_tc_list;
3776 };
3777
3778 struct occ_show {
3779 struct dl *dl;
3780 int err;
3781 struct list_head port_list;
3782 };
3783
3784 static struct occ_item *occ_item_alloc(void)
3785 {
3786 return calloc(1, sizeof(struct occ_item));
3787 }
3788
3789 static void occ_item_free(struct occ_item *occ_item)
3790 {
3791 free(occ_item);
3792 }
3793
3794 static struct occ_port *occ_port_alloc(uint32_t port_index)
3795 {
3796 struct occ_port *occ_port;
3797
3798 occ_port = calloc(1, sizeof(*occ_port));
3799 if (!occ_port)
3800 return NULL;
3801 occ_port->port_index = port_index;
3802 INIT_LIST_HEAD(&occ_port->pool_list);
3803 INIT_LIST_HEAD(&occ_port->ing_tc_list);
3804 INIT_LIST_HEAD(&occ_port->eg_tc_list);
3805 return occ_port;
3806 }
3807
3808 static void occ_port_free(struct occ_port *occ_port)
3809 {
3810 struct occ_item *occ_item, *tmp;
3811
3812 list_for_each_entry_safe(occ_item, tmp, &occ_port->pool_list, list)
3813 occ_item_free(occ_item);
3814 list_for_each_entry_safe(occ_item, tmp, &occ_port->ing_tc_list, list)
3815 occ_item_free(occ_item);
3816 list_for_each_entry_safe(occ_item, tmp, &occ_port->eg_tc_list, list)
3817 occ_item_free(occ_item);
3818 }
3819
3820 static struct occ_show *occ_show_alloc(struct dl *dl)
3821 {
3822 struct occ_show *occ_show;
3823
3824 occ_show = calloc(1, sizeof(*occ_show));
3825 if (!occ_show)
3826 return NULL;
3827 occ_show->dl = dl;
3828 INIT_LIST_HEAD(&occ_show->port_list);
3829 return occ_show;
3830 }
3831
3832 static void occ_show_free(struct occ_show *occ_show)
3833 {
3834 struct occ_port *occ_port, *tmp;
3835
3836 list_for_each_entry_safe(occ_port, tmp, &occ_show->port_list, list)
3837 occ_port_free(occ_port);
3838 }
3839
3840 static struct occ_port *occ_port_get(struct occ_show *occ_show,
3841 struct nlattr **tb)
3842 {
3843 struct occ_port *occ_port;
3844 uint32_t port_index;
3845
3846 port_index = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX]);
3847
3848 list_for_each_entry_reverse(occ_port, &occ_show->port_list, list) {
3849 if (occ_port->port_index == port_index)
3850 return occ_port;
3851 }
3852 occ_port = occ_port_alloc(port_index);
3853 if (!occ_port)
3854 return NULL;
3855 list_add_tail(&occ_port->list, &occ_show->port_list);
3856 return occ_port;
3857 }
3858
3859 static void pr_out_occ_show_item_list(const char *label, struct list_head *list,
3860 bool bound_pool)
3861 {
3862 struct occ_item *occ_item;
3863 int i = 1;
3864
3865 pr_out_sp(7, " %s:", label);
3866 list_for_each_entry(occ_item, list, list) {
3867 if ((i - 1) % 4 == 0 && i != 1)
3868 pr_out_sp(7, " ");
3869 if (bound_pool)
3870 pr_out_sp(7, "%2u(%u):", occ_item->index,
3871 occ_item->bound_pool_index);
3872 else
3873 pr_out_sp(7, "%2u:", occ_item->index);
3874 pr_out_sp(21, "%10u/%u", occ_item->cur, occ_item->max);
3875 if (i++ % 4 == 0)
3876 pr_out("\n");
3877 }
3878 if ((i - 1) % 4 != 0)
3879 pr_out("\n");
3880 }
3881
3882 static void pr_out_json_occ_show_item_list(struct dl *dl, const char *label,
3883 struct list_head *list,
3884 bool bound_pool)
3885 {
3886 struct occ_item *occ_item;
3887 char buf[32];
3888
3889 open_json_object(label);
3890 list_for_each_entry(occ_item, list, list) {
3891 sprintf(buf, "%u", occ_item->index);
3892 open_json_object(buf);
3893 if (bound_pool)
3894 print_uint(PRINT_JSON, "bound_pool", NULL,
3895 occ_item->bound_pool_index);
3896 print_uint(PRINT_JSON, "current", NULL, occ_item->cur);
3897 print_uint(PRINT_JSON, "max", NULL, occ_item->max);
3898 close_json_object();
3899 }
3900 close_json_object();
3901 }
3902
3903 static void pr_out_occ_show_port(struct dl *dl, struct occ_port *occ_port)
3904 {
3905 if (dl->json_output) {
3906 pr_out_json_occ_show_item_list(dl, "pool",
3907 &occ_port->pool_list, false);
3908 pr_out_json_occ_show_item_list(dl, "itc",
3909 &occ_port->ing_tc_list, true);
3910 pr_out_json_occ_show_item_list(dl, "etc",
3911 &occ_port->eg_tc_list, true);
3912 } else {
3913 pr_out("\n");
3914 pr_out_occ_show_item_list("pool", &occ_port->pool_list, false);
3915 pr_out_occ_show_item_list("itc", &occ_port->ing_tc_list, true);
3916 pr_out_occ_show_item_list("etc", &occ_port->eg_tc_list, true);
3917 }
3918 }
3919
3920 static void pr_out_occ_show(struct occ_show *occ_show)
3921 {
3922 struct dl *dl = occ_show->dl;
3923 struct dl_opts *opts = &dl->opts;
3924 struct occ_port *occ_port;
3925
3926 list_for_each_entry(occ_port, &occ_show->port_list, list) {
3927 __pr_out_port_handle_start(dl, opts->bus_name, opts->dev_name,
3928 occ_port->port_index, true, false);
3929 pr_out_occ_show_port(dl, occ_port);
3930 pr_out_port_handle_end(dl);
3931 }
3932 }
3933
3934 static void cmd_sb_occ_port_pool_process(struct occ_show *occ_show,
3935 struct nlattr **tb)
3936 {
3937 struct occ_port *occ_port;
3938 struct occ_item *occ_item;
3939
3940 if (occ_show->err || !dl_dump_filter(occ_show->dl, tb))
3941 return;
3942
3943 occ_port = occ_port_get(occ_show, tb);
3944 if (!occ_port) {
3945 occ_show->err = -ENOMEM;
3946 return;
3947 }
3948
3949 occ_item = occ_item_alloc();
3950 if (!occ_item) {
3951 occ_show->err = -ENOMEM;
3952 return;
3953 }
3954 occ_item->index = mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]);
3955 occ_item->cur = mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_OCC_CUR]);
3956 occ_item->max = mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_OCC_MAX]);
3957 list_add_tail(&occ_item->list, &occ_port->pool_list);
3958 }
3959
3960 static int cmd_sb_occ_port_pool_process_cb(const struct nlmsghdr *nlh, void *data)
3961 {
3962 struct occ_show *occ_show = data;
3963 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
3964 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
3965
3966 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
3967 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
3968 !tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_SB_INDEX] ||
3969 !tb[DEVLINK_ATTR_SB_POOL_INDEX] ||
3970 !tb[DEVLINK_ATTR_SB_OCC_CUR] || !tb[DEVLINK_ATTR_SB_OCC_MAX])
3971 return MNL_CB_ERROR;
3972 cmd_sb_occ_port_pool_process(occ_show, tb);
3973 return MNL_CB_OK;
3974 }
3975
3976 static void cmd_sb_occ_tc_pool_process(struct occ_show *occ_show,
3977 struct nlattr **tb)
3978 {
3979 struct occ_port *occ_port;
3980 struct occ_item *occ_item;
3981 uint8_t pool_type;
3982
3983 if (occ_show->err || !dl_dump_filter(occ_show->dl, tb))
3984 return;
3985
3986 occ_port = occ_port_get(occ_show, tb);
3987 if (!occ_port) {
3988 occ_show->err = -ENOMEM;
3989 return;
3990 }
3991
3992 occ_item = occ_item_alloc();
3993 if (!occ_item) {
3994 occ_show->err = -ENOMEM;
3995 return;
3996 }
3997 occ_item->index = mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_TC_INDEX]);
3998 occ_item->cur = mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_OCC_CUR]);
3999 occ_item->max = mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_OCC_MAX]);
4000 occ_item->bound_pool_index =
4001 mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]);
4002 pool_type = mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE]);
4003 if (pool_type == DEVLINK_SB_POOL_TYPE_INGRESS)
4004 list_add_tail(&occ_item->list, &occ_port->ing_tc_list);
4005 else if (pool_type == DEVLINK_SB_POOL_TYPE_EGRESS)
4006 list_add_tail(&occ_item->list, &occ_port->eg_tc_list);
4007 else
4008 occ_item_free(occ_item);
4009 }
4010
4011 static int cmd_sb_occ_tc_pool_process_cb(const struct nlmsghdr *nlh, void *data)
4012 {
4013 struct occ_show *occ_show = data;
4014 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
4015 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
4016
4017 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
4018 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
4019 !tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_SB_INDEX] ||
4020 !tb[DEVLINK_ATTR_SB_TC_INDEX] || !tb[DEVLINK_ATTR_SB_POOL_TYPE] ||
4021 !tb[DEVLINK_ATTR_SB_POOL_INDEX] ||
4022 !tb[DEVLINK_ATTR_SB_OCC_CUR] || !tb[DEVLINK_ATTR_SB_OCC_MAX])
4023 return MNL_CB_ERROR;
4024 cmd_sb_occ_tc_pool_process(occ_show, tb);
4025 return MNL_CB_OK;
4026 }
4027
4028 static int cmd_sb_occ_show(struct dl *dl)
4029 {
4030 struct nlmsghdr *nlh;
4031 struct occ_show *occ_show;
4032 uint16_t flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP;
4033 int err;
4034
4035 err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_HANDLEP, DL_OPT_SB);
4036 if (err)
4037 return err;
4038
4039 occ_show = occ_show_alloc(dl);
4040 if (!occ_show)
4041 return -ENOMEM;
4042
4043 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_PORT_POOL_GET, flags);
4044
4045 err = _mnlg_socket_sndrcv(dl->nlg, nlh,
4046 cmd_sb_occ_port_pool_process_cb, occ_show);
4047 if (err)
4048 goto out;
4049
4050 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_TC_POOL_BIND_GET, flags);
4051
4052 err = _mnlg_socket_sndrcv(dl->nlg, nlh,
4053 cmd_sb_occ_tc_pool_process_cb, occ_show);
4054 if (err)
4055 goto out;
4056
4057 pr_out_section_start(dl, "occupancy");
4058 pr_out_occ_show(occ_show);
4059 pr_out_section_end(dl);
4060
4061 out:
4062 occ_show_free(occ_show);
4063 return err;
4064 }
4065
4066 static int cmd_sb_occ_snapshot(struct dl *dl)
4067 {
4068 struct nlmsghdr *nlh;
4069 int err;
4070
4071 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_OCC_SNAPSHOT,
4072 NLM_F_REQUEST | NLM_F_ACK);
4073
4074 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, DL_OPT_SB);
4075 if (err)
4076 return err;
4077
4078 return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
4079 }
4080
4081 static int cmd_sb_occ_clearmax(struct dl *dl)
4082 {
4083 struct nlmsghdr *nlh;
4084 int err;
4085
4086 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_OCC_MAX_CLEAR,
4087 NLM_F_REQUEST | NLM_F_ACK);
4088
4089 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, DL_OPT_SB);
4090 if (err)
4091 return err;
4092
4093 return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
4094 }
4095
4096 static int cmd_sb_occ(struct dl *dl)
4097 {
4098 if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
4099 cmd_sb_help();
4100 return 0;
4101 } else if (dl_argv_match(dl, "show") ||
4102 dl_argv_match(dl, "list")) {
4103 dl_arg_inc(dl);
4104 return cmd_sb_occ_show(dl);
4105 } else if (dl_argv_match(dl, "snapshot")) {
4106 dl_arg_inc(dl);
4107 return cmd_sb_occ_snapshot(dl);
4108 } else if (dl_argv_match(dl, "clearmax")) {
4109 dl_arg_inc(dl);
4110 return cmd_sb_occ_clearmax(dl);
4111 }
4112 pr_err("Command \"%s\" not found\n", dl_argv(dl));
4113 return -ENOENT;
4114 }
4115
4116 static int cmd_sb(struct dl *dl)
4117 {
4118 if (dl_argv_match(dl, "help")) {
4119 cmd_sb_help();
4120 return 0;
4121 } else if (dl_argv_match(dl, "show") ||
4122 dl_argv_match(dl, "list") || dl_no_arg(dl)) {
4123 dl_arg_inc(dl);
4124 return cmd_sb_show(dl);
4125 } else if (dl_argv_match(dl, "pool")) {
4126 dl_arg_inc(dl);
4127 return cmd_sb_pool(dl);
4128 } else if (dl_argv_match(dl, "port")) {
4129 dl_arg_inc(dl);
4130 return cmd_sb_port(dl);
4131 } else if (dl_argv_match(dl, "tc")) {
4132 dl_arg_inc(dl);
4133 return cmd_sb_tc(dl);
4134 } else if (dl_argv_match(dl, "occupancy")) {
4135 dl_arg_inc(dl);
4136 return cmd_sb_occ(dl);
4137 }
4138 pr_err("Command \"%s\" not found\n", dl_argv(dl));
4139 return -ENOENT;
4140 }
4141
4142 static const char *cmd_name(uint8_t cmd)
4143 {
4144 switch (cmd) {
4145 case DEVLINK_CMD_UNSPEC: return "unspec";
4146 case DEVLINK_CMD_GET: return "get";
4147 case DEVLINK_CMD_SET: return "set";
4148 case DEVLINK_CMD_NEW: return "new";
4149 case DEVLINK_CMD_DEL: return "del";
4150 case DEVLINK_CMD_PORT_GET: return "get";
4151 case DEVLINK_CMD_PORT_SET: return "set";
4152 case DEVLINK_CMD_PORT_NEW: return "new";
4153 case DEVLINK_CMD_PORT_DEL: return "del";
4154 case DEVLINK_CMD_PARAM_GET: return "get";
4155 case DEVLINK_CMD_PARAM_SET: return "set";
4156 case DEVLINK_CMD_PARAM_NEW: return "new";
4157 case DEVLINK_CMD_PARAM_DEL: return "del";
4158 case DEVLINK_CMD_REGION_GET: return "get";
4159 case DEVLINK_CMD_REGION_SET: return "set";
4160 case DEVLINK_CMD_REGION_NEW: return "new";
4161 case DEVLINK_CMD_REGION_DEL: return "del";
4162 case DEVLINK_CMD_FLASH_UPDATE: return "begin";
4163 case DEVLINK_CMD_FLASH_UPDATE_END: return "end";
4164 case DEVLINK_CMD_FLASH_UPDATE_STATUS: return "status";
4165 case DEVLINK_CMD_HEALTH_REPORTER_RECOVER: return "status";
4166 case DEVLINK_CMD_TRAP_GET: return "get";
4167 case DEVLINK_CMD_TRAP_SET: return "set";
4168 case DEVLINK_CMD_TRAP_NEW: return "new";
4169 case DEVLINK_CMD_TRAP_DEL: return "del";
4170 case DEVLINK_CMD_TRAP_GROUP_GET: return "get";
4171 case DEVLINK_CMD_TRAP_GROUP_SET: return "set";
4172 case DEVLINK_CMD_TRAP_GROUP_NEW: return "new";
4173 case DEVLINK_CMD_TRAP_GROUP_DEL: return "del";
4174 case DEVLINK_CMD_TRAP_POLICER_GET: return "get";
4175 case DEVLINK_CMD_TRAP_POLICER_SET: return "set";
4176 case DEVLINK_CMD_TRAP_POLICER_NEW: return "new";
4177 case DEVLINK_CMD_TRAP_POLICER_DEL: return "del";
4178 default: return "<unknown cmd>";
4179 }
4180 }
4181
4182 static const char *cmd_obj(uint8_t cmd)
4183 {
4184 switch (cmd) {
4185 case DEVLINK_CMD_UNSPEC: return "unspec";
4186 case DEVLINK_CMD_GET:
4187 case DEVLINK_CMD_SET:
4188 case DEVLINK_CMD_NEW:
4189 case DEVLINK_CMD_DEL:
4190 return "dev";
4191 case DEVLINK_CMD_PORT_GET:
4192 case DEVLINK_CMD_PORT_SET:
4193 case DEVLINK_CMD_PORT_NEW:
4194 case DEVLINK_CMD_PORT_DEL:
4195 return "port";
4196 case DEVLINK_CMD_PARAM_GET:
4197 case DEVLINK_CMD_PARAM_SET:
4198 case DEVLINK_CMD_PARAM_NEW:
4199 case DEVLINK_CMD_PARAM_DEL:
4200 return "param";
4201 case DEVLINK_CMD_REGION_GET:
4202 case DEVLINK_CMD_REGION_SET:
4203 case DEVLINK_CMD_REGION_NEW:
4204 case DEVLINK_CMD_REGION_DEL:
4205 return "region";
4206 case DEVLINK_CMD_FLASH_UPDATE:
4207 case DEVLINK_CMD_FLASH_UPDATE_END:
4208 case DEVLINK_CMD_FLASH_UPDATE_STATUS:
4209 return "flash";
4210 case DEVLINK_CMD_HEALTH_REPORTER_RECOVER:
4211 return "health";
4212 case DEVLINK_CMD_TRAP_GET:
4213 case DEVLINK_CMD_TRAP_SET:
4214 case DEVLINK_CMD_TRAP_NEW:
4215 case DEVLINK_CMD_TRAP_DEL:
4216 return "trap";
4217 case DEVLINK_CMD_TRAP_GROUP_GET:
4218 case DEVLINK_CMD_TRAP_GROUP_SET:
4219 case DEVLINK_CMD_TRAP_GROUP_NEW:
4220 case DEVLINK_CMD_TRAP_GROUP_DEL:
4221 return "trap-group";
4222 case DEVLINK_CMD_TRAP_POLICER_GET:
4223 case DEVLINK_CMD_TRAP_POLICER_SET:
4224 case DEVLINK_CMD_TRAP_POLICER_NEW:
4225 case DEVLINK_CMD_TRAP_POLICER_DEL:
4226 return "trap-policer";
4227 default: return "<unknown obj>";
4228 }
4229 }
4230
4231 static void pr_out_mon_header(uint8_t cmd)
4232 {
4233 pr_out("[%s,%s] ", cmd_obj(cmd), cmd_name(cmd));
4234 }
4235
4236 static bool cmd_filter_check(struct dl *dl, uint8_t cmd)
4237 {
4238 const char *obj = cmd_obj(cmd);
4239 unsigned int index = 0;
4240 const char *cur_obj;
4241
4242 if (dl_no_arg(dl))
4243 return true;
4244 while ((cur_obj = dl_argv_index(dl, index++))) {
4245 if (strcmp(cur_obj, obj) == 0 || strcmp(cur_obj, "all") == 0)
4246 return true;
4247 }
4248 return false;
4249 }
4250
4251 static void pr_out_flash_update(struct dl *dl, struct nlattr **tb)
4252 {
4253 __pr_out_handle_start(dl, tb, true, false);
4254
4255 if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG]) {
4256 check_indent_newline(dl);
4257 print_string(PRINT_ANY, "msg", "msg %s",
4258 mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG]));
4259 }
4260 if (tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT]) {
4261 check_indent_newline(dl);
4262 print_string(PRINT_ANY, "component", "component %s",
4263 mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT]));
4264 }
4265
4266 if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE])
4267 pr_out_u64(dl, "done",
4268 mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE]));
4269
4270 if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL])
4271 pr_out_u64(dl, "total",
4272 mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL]));
4273
4274 pr_out_handle_end(dl);
4275 }
4276
4277 static void pr_out_region(struct dl *dl, struct nlattr **tb);
4278 static void pr_out_health(struct dl *dl, struct nlattr **tb_health);
4279 static void pr_out_trap(struct dl *dl, struct nlattr **tb, bool array);
4280 static void pr_out_trap_group(struct dl *dl, struct nlattr **tb, bool array);
4281 static void pr_out_trap_policer(struct dl *dl, struct nlattr **tb, bool array);
4282
4283 static int cmd_mon_show_cb(const struct nlmsghdr *nlh, void *data)
4284 {
4285 struct dl *dl = data;
4286 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
4287 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
4288 uint8_t cmd = genl->cmd;
4289
4290 if (!cmd_filter_check(dl, cmd))
4291 return MNL_CB_OK;
4292
4293 switch (cmd) {
4294 case DEVLINK_CMD_GET: /* fall through */
4295 case DEVLINK_CMD_SET: /* fall through */
4296 case DEVLINK_CMD_NEW: /* fall through */
4297 case DEVLINK_CMD_DEL:
4298 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
4299 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME])
4300 return MNL_CB_ERROR;
4301 pr_out_mon_header(genl->cmd);
4302 pr_out_handle(dl, tb);
4303 break;
4304 case DEVLINK_CMD_PORT_GET: /* fall through */
4305 case DEVLINK_CMD_PORT_SET: /* fall through */
4306 case DEVLINK_CMD_PORT_NEW: /* fall through */
4307 case DEVLINK_CMD_PORT_DEL:
4308 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
4309 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
4310 !tb[DEVLINK_ATTR_PORT_INDEX])
4311 return MNL_CB_ERROR;
4312 pr_out_mon_header(genl->cmd);
4313 pr_out_port(dl, tb);
4314 break;
4315 case DEVLINK_CMD_PARAM_GET: /* fall through */
4316 case DEVLINK_CMD_PARAM_SET: /* fall through */
4317 case DEVLINK_CMD_PARAM_NEW: /* fall through */
4318 case DEVLINK_CMD_PARAM_DEL:
4319 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
4320 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
4321 !tb[DEVLINK_ATTR_PARAM])
4322 return MNL_CB_ERROR;
4323 pr_out_mon_header(genl->cmd);
4324 pr_out_param(dl, tb, false);
4325 break;
4326 case DEVLINK_CMD_REGION_GET: /* fall through */
4327 case DEVLINK_CMD_REGION_SET: /* fall through */
4328 case DEVLINK_CMD_REGION_NEW: /* fall through */
4329 case DEVLINK_CMD_REGION_DEL:
4330 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
4331 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
4332 !tb[DEVLINK_ATTR_REGION_NAME])
4333 return MNL_CB_ERROR;
4334 pr_out_mon_header(genl->cmd);
4335 pr_out_region(dl, tb);
4336 break;
4337 case DEVLINK_CMD_FLASH_UPDATE: /* fall through */
4338 case DEVLINK_CMD_FLASH_UPDATE_END: /* fall through */
4339 case DEVLINK_CMD_FLASH_UPDATE_STATUS:
4340 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
4341 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME])
4342 return MNL_CB_ERROR;
4343 pr_out_mon_header(genl->cmd);
4344 pr_out_flash_update(dl, tb);
4345 break;
4346 case DEVLINK_CMD_HEALTH_REPORTER_RECOVER:
4347 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
4348 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
4349 !tb[DEVLINK_ATTR_HEALTH_REPORTER])
4350 return MNL_CB_ERROR;
4351 pr_out_mon_header(genl->cmd);
4352 pr_out_health(dl, tb);
4353 break;
4354 case DEVLINK_CMD_TRAP_GET: /* fall through */
4355 case DEVLINK_CMD_TRAP_SET: /* fall through */
4356 case DEVLINK_CMD_TRAP_NEW: /* fall through */
4357 case DEVLINK_CMD_TRAP_DEL:
4358 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
4359 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
4360 !tb[DEVLINK_ATTR_TRAP_NAME] ||
4361 !tb[DEVLINK_ATTR_TRAP_TYPE] ||
4362 !tb[DEVLINK_ATTR_TRAP_ACTION] ||
4363 !tb[DEVLINK_ATTR_TRAP_GROUP_NAME] ||
4364 !tb[DEVLINK_ATTR_TRAP_METADATA] ||
4365 !tb[DEVLINK_ATTR_STATS])
4366 return MNL_CB_ERROR;
4367 pr_out_mon_header(genl->cmd);
4368 pr_out_trap(dl, tb, false);
4369 break;
4370 case DEVLINK_CMD_TRAP_GROUP_GET: /* fall through */
4371 case DEVLINK_CMD_TRAP_GROUP_SET: /* fall through */
4372 case DEVLINK_CMD_TRAP_GROUP_NEW: /* fall through */
4373 case DEVLINK_CMD_TRAP_GROUP_DEL:
4374 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
4375 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
4376 !tb[DEVLINK_ATTR_TRAP_GROUP_NAME] ||
4377 !tb[DEVLINK_ATTR_STATS])
4378 return MNL_CB_ERROR;
4379 pr_out_mon_header(genl->cmd);
4380 pr_out_trap_group(dl, tb, false);
4381 break;
4382 case DEVLINK_CMD_TRAP_POLICER_GET: /* fall through */
4383 case DEVLINK_CMD_TRAP_POLICER_SET: /* fall through */
4384 case DEVLINK_CMD_TRAP_POLICER_NEW: /* fall through */
4385 case DEVLINK_CMD_TRAP_POLICER_DEL: /* fall through */
4386 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
4387 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
4388 !tb[DEVLINK_ATTR_TRAP_POLICER_ID] ||
4389 !tb[DEVLINK_ATTR_TRAP_POLICER_RATE] ||
4390 !tb[DEVLINK_ATTR_TRAP_POLICER_BURST])
4391 return MNL_CB_ERROR;
4392 pr_out_mon_header(genl->cmd);
4393 pr_out_trap_policer(dl, tb, false);
4394 break;
4395 }
4396 return MNL_CB_OK;
4397 }
4398
4399 static int cmd_mon_show(struct dl *dl)
4400 {
4401 int err;
4402 unsigned int index = 0;
4403 const char *cur_obj;
4404
4405 while ((cur_obj = dl_argv_index(dl, index++))) {
4406 if (strcmp(cur_obj, "all") != 0 &&
4407 strcmp(cur_obj, "dev") != 0 &&
4408 strcmp(cur_obj, "port") != 0 &&
4409 strcmp(cur_obj, "health") != 0 &&
4410 strcmp(cur_obj, "trap") != 0 &&
4411 strcmp(cur_obj, "trap-group") != 0 &&
4412 strcmp(cur_obj, "trap-policer") != 0) {
4413 pr_err("Unknown object \"%s\"\n", cur_obj);
4414 return -EINVAL;
4415 }
4416 }
4417 err = _mnlg_socket_group_add(dl->nlg, DEVLINK_GENL_MCGRP_CONFIG_NAME);
4418 if (err)
4419 return err;
4420 err = _mnlg_socket_recv_run(dl->nlg, cmd_mon_show_cb, dl);
4421 if (err)
4422 return err;
4423 return 0;
4424 }
4425
4426 static void cmd_mon_help(void)
4427 {
4428 pr_err("Usage: devlink monitor [ all | OBJECT-LIST ]\n"
4429 "where OBJECT-LIST := { dev | port | health | trap | trap-group | trap-policer }\n");
4430 }
4431
4432 static int cmd_mon(struct dl *dl)
4433 {
4434 if (dl_argv_match(dl, "help")) {
4435 cmd_mon_help();
4436 return 0;
4437 }
4438 return cmd_mon_show(dl);
4439 }
4440
4441 struct dpipe_field {
4442 char *name;
4443 unsigned int id;
4444 unsigned int bitwidth;
4445 enum devlink_dpipe_field_mapping_type mapping_type;
4446 };
4447
4448 struct dpipe_header {
4449 struct list_head list;
4450 char *name;
4451 unsigned int id;
4452 struct dpipe_field *fields;
4453 unsigned int fields_count;
4454 };
4455
4456 struct dpipe_table {
4457 struct list_head list;
4458 char *name;
4459 unsigned int resource_id;
4460 bool resource_valid;
4461 };
4462
4463 struct dpipe_tables {
4464 struct list_head table_list;
4465 };
4466
4467 struct resource {
4468 char *name;
4469 uint64_t size;
4470 uint64_t size_new;
4471 uint64_t size_min;
4472 uint64_t size_max;
4473 uint64_t size_gran;
4474 enum devlink_resource_unit unit;
4475 bool size_valid;
4476 uint64_t size_occ;
4477 bool occ_valid;
4478 uint64_t id;
4479 struct list_head list;
4480 struct list_head resource_list;
4481 struct resource *parent;
4482 };
4483
4484 struct resources {
4485 struct list_head resource_list;
4486 };
4487
4488 struct resource_ctx {
4489 struct dl *dl;
4490 int err;
4491 struct resources *resources;
4492 struct dpipe_tables *tables;
4493 bool print_resources;
4494 bool pending_change;
4495 };
4496
4497 static struct resource *resource_alloc(void)
4498 {
4499 struct resource *resource;
4500
4501 resource = calloc(1, sizeof(struct resource));
4502 if (!resource)
4503 return NULL;
4504 INIT_LIST_HEAD(&resource->resource_list);
4505 return resource;
4506 }
4507
4508 static void resource_free(struct resource *resource)
4509 {
4510 struct resource *child_resource, *tmp;
4511
4512 list_for_each_entry_safe(child_resource, tmp, &resource->resource_list,
4513 list) {
4514 free(child_resource->name);
4515 resource_free(child_resource);
4516 }
4517 free(resource);
4518 }
4519
4520 static struct resources *resources_alloc(void)
4521 {
4522 struct resources *resources;
4523
4524 resources = calloc(1, sizeof(struct resources));
4525 if (!resources)
4526 return NULL;
4527 INIT_LIST_HEAD(&resources->resource_list);
4528 return resources;
4529 }
4530
4531 static void resources_free(struct resources *resources)
4532 {
4533 struct resource *resource, *tmp;
4534
4535 list_for_each_entry_safe(resource, tmp, &resources->resource_list, list)
4536 resource_free(resource);
4537 }
4538
4539 static int resource_ctx_init(struct resource_ctx *ctx, struct dl *dl)
4540 {
4541 ctx->resources = resources_alloc();
4542 if (!ctx->resources)
4543 return -ENOMEM;
4544 ctx->dl = dl;
4545 return 0;
4546 }
4547
4548 static void resource_ctx_fini(struct resource_ctx *ctx)
4549 {
4550 resources_free(ctx->resources);
4551 }
4552
4553 struct dpipe_ctx {
4554 struct dl *dl;
4555 int err;
4556 struct list_head global_headers;
4557 struct list_head local_headers;
4558 struct dpipe_tables *tables;
4559 struct resources *resources;
4560 bool print_headers;
4561 bool print_tables;
4562 };
4563
4564 static struct dpipe_header *dpipe_header_alloc(unsigned int fields_count)
4565 {
4566 struct dpipe_header *header;
4567
4568 header = calloc(1, sizeof(struct dpipe_header));
4569 if (!header)
4570 return NULL;
4571 header->fields = calloc(fields_count, sizeof(struct dpipe_field));
4572 if (!header->fields)
4573 goto err_fields_alloc;
4574 header->fields_count = fields_count;
4575 return header;
4576
4577 err_fields_alloc:
4578 free(header);
4579 return NULL;
4580 }
4581
4582 static void dpipe_header_free(struct dpipe_header *header)
4583 {
4584 free(header->fields);
4585 free(header);
4586 }
4587
4588 static void dpipe_header_clear(struct dpipe_header *header)
4589 {
4590 struct dpipe_field *field;
4591 int i;
4592
4593 for (i = 0; i < header->fields_count; i++) {
4594 field = &header->fields[i];
4595 free(field->name);
4596 }
4597 free(header->name);
4598 }
4599
4600 static void dpipe_header_add(struct dpipe_ctx *ctx,
4601 struct dpipe_header *header, bool global)
4602 {
4603 if (global)
4604 list_add(&header->list, &ctx->global_headers);
4605 else
4606 list_add(&header->list, &ctx->local_headers);
4607 }
4608
4609 static void dpipe_header_del(struct dpipe_header *header)
4610 {
4611 list_del(&header->list);
4612 }
4613
4614 static struct dpipe_table *dpipe_table_alloc(void)
4615 {
4616 return calloc(1, sizeof(struct dpipe_table));
4617 }
4618
4619 static void dpipe_table_free(struct dpipe_table *table)
4620 {
4621 free(table);
4622 }
4623
4624 static struct dpipe_tables *dpipe_tables_alloc(void)
4625 {
4626 struct dpipe_tables *tables;
4627
4628 tables = calloc(1, sizeof(struct dpipe_tables));
4629 if (!tables)
4630 return NULL;
4631 INIT_LIST_HEAD(&tables->table_list);
4632 return tables;
4633 }
4634
4635 static void dpipe_tables_free(struct dpipe_tables *tables)
4636 {
4637 struct dpipe_table *table, *tmp;
4638
4639 list_for_each_entry_safe(table, tmp, &tables->table_list, list)
4640 dpipe_table_free(table);
4641 free(tables);
4642 }
4643
4644 static int dpipe_ctx_init(struct dpipe_ctx *ctx, struct dl *dl)
4645 {
4646 ctx->tables = dpipe_tables_alloc();
4647 if (!ctx->tables)
4648 return -ENOMEM;
4649
4650 ctx->dl = dl;
4651 INIT_LIST_HEAD(&ctx->global_headers);
4652 INIT_LIST_HEAD(&ctx->local_headers);
4653 return 0;
4654 }
4655
4656 static void dpipe_ctx_fini(struct dpipe_ctx *ctx)
4657 {
4658 struct dpipe_header *header, *tmp;
4659
4660 list_for_each_entry_safe(header, tmp, &ctx->global_headers,
4661 list) {
4662 dpipe_header_del(header);
4663 dpipe_header_clear(header);
4664 dpipe_header_free(header);
4665 }
4666 list_for_each_entry_safe(header, tmp, &ctx->local_headers,
4667 list) {
4668 dpipe_header_del(header);
4669 dpipe_header_clear(header);
4670 dpipe_header_free(header);
4671 }
4672 dpipe_tables_free(ctx->tables);
4673 }
4674
4675 static const char *dpipe_header_id2s(struct dpipe_ctx *ctx,
4676 uint32_t header_id, bool global)
4677 {
4678 struct list_head *header_list;
4679 struct dpipe_header *header;
4680
4681 if (global)
4682 header_list = &ctx->global_headers;
4683 else
4684 header_list = &ctx->local_headers;
4685 list_for_each_entry(header, header_list, list) {
4686 if (header->id != header_id)
4687 continue;
4688 return header->name;
4689 }
4690 return NULL;
4691 }
4692
4693 static const char *dpipe_field_id2s(struct dpipe_ctx *ctx,
4694 uint32_t header_id,
4695 uint32_t field_id, bool global)
4696 {
4697 struct list_head *header_list;
4698 struct dpipe_header *header;
4699
4700 if (global)
4701 header_list = &ctx->global_headers;
4702 else
4703 header_list = &ctx->local_headers;
4704 list_for_each_entry(header, header_list, list) {
4705 if (header->id != header_id)
4706 continue;
4707 return header->fields[field_id].name;
4708 }
4709 return NULL;
4710 }
4711
4712 static const char *
4713 dpipe_field_mapping_e2s(enum devlink_dpipe_field_mapping_type mapping_type)
4714 {
4715 switch (mapping_type) {
4716 case DEVLINK_DPIPE_FIELD_MAPPING_TYPE_NONE:
4717 return NULL;
4718 case DEVLINK_DPIPE_FIELD_MAPPING_TYPE_IFINDEX:
4719 return "ifindex";
4720 default:
4721 return "<unknown>";
4722 }
4723 }
4724
4725 static const char *
4726 dpipe_mapping_get(struct dpipe_ctx *ctx, uint32_t header_id,
4727 uint32_t field_id, bool global)
4728 {
4729 enum devlink_dpipe_field_mapping_type mapping_type;
4730 struct list_head *header_list;
4731 struct dpipe_header *header;
4732
4733 if (global)
4734 header_list = &ctx->global_headers;
4735 else
4736 header_list = &ctx->local_headers;
4737 list_for_each_entry(header, header_list, list) {
4738 if (header->id != header_id)
4739 continue;
4740 mapping_type = header->fields[field_id].mapping_type;
4741 return dpipe_field_mapping_e2s(mapping_type);
4742 }
4743 return NULL;
4744 }
4745
4746 static void pr_out_dpipe_fields(struct dpipe_ctx *ctx,
4747 struct dpipe_field *fields,
4748 unsigned int field_count)
4749 {
4750 struct dpipe_field *field;
4751 int i;
4752
4753 for (i = 0; i < field_count; i++) {
4754 field = &fields[i];
4755 pr_out_entry_start(ctx->dl);
4756 check_indent_newline(ctx->dl);
4757 print_string(PRINT_ANY, "name", "name %s", field->name);
4758 if (ctx->dl->verbose)
4759 print_uint(PRINT_ANY, "id", " id %u", field->id);
4760 print_uint(PRINT_ANY, "bitwidth", " bitwidth %u", field->bitwidth);
4761 if (field->mapping_type) {
4762 print_string(PRINT_ANY, "mapping_type", " mapping_type %s",
4763 dpipe_field_mapping_e2s(field->mapping_type));
4764 }
4765 pr_out_entry_end(ctx->dl);
4766 }
4767 }
4768
4769 static void
4770 pr_out_dpipe_header(struct dpipe_ctx *ctx, struct nlattr **tb,
4771 struct dpipe_header *header, bool global)
4772 {
4773 pr_out_handle_start_arr(ctx->dl, tb);
4774 check_indent_newline(ctx->dl);
4775 print_string(PRINT_ANY, "name", "name %s", header->name);
4776 if (ctx->dl->verbose) {
4777 print_uint(PRINT_ANY, "id", " id %u", header->id);
4778 print_bool(PRINT_ANY, "global", " global %s", global);
4779 }
4780 pr_out_array_start(ctx->dl, "field");
4781 pr_out_dpipe_fields(ctx, header->fields,
4782 header->fields_count);
4783 pr_out_array_end(ctx->dl);
4784 pr_out_handle_end(ctx->dl);
4785 }
4786
4787 static void pr_out_dpipe_headers(struct dpipe_ctx *ctx,
4788 struct nlattr **tb)
4789 {
4790 struct dpipe_header *header;
4791
4792 list_for_each_entry(header, &ctx->local_headers, list)
4793 pr_out_dpipe_header(ctx, tb, header, false);
4794
4795 list_for_each_entry(header, &ctx->global_headers, list)
4796 pr_out_dpipe_header(ctx, tb, header, true);
4797 }
4798
4799 static int dpipe_header_field_get(struct nlattr *nl, struct dpipe_field *field)
4800 {
4801 struct nlattr *nla_field[DEVLINK_ATTR_MAX + 1] = {};
4802 const char *name;
4803 int err;
4804
4805 err = mnl_attr_parse_nested(nl, attr_cb, nla_field);
4806 if (err != MNL_CB_OK)
4807 return -EINVAL;
4808 if (!nla_field[DEVLINK_ATTR_DPIPE_FIELD_ID] ||
4809 !nla_field[DEVLINK_ATTR_DPIPE_FIELD_NAME] ||
4810 !nla_field[DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH] ||
4811 !nla_field[DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE])
4812 return -EINVAL;
4813
4814 name = mnl_attr_get_str(nla_field[DEVLINK_ATTR_DPIPE_FIELD_NAME]);
4815 field->id = mnl_attr_get_u32(nla_field[DEVLINK_ATTR_DPIPE_FIELD_ID]);
4816 field->bitwidth = mnl_attr_get_u32(nla_field[DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH]);
4817 field->name = strdup(name);
4818 if (!field->name)
4819 return -ENOMEM;
4820 field->mapping_type = mnl_attr_get_u32(nla_field[DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE]);
4821 return 0;
4822 }
4823
4824 static int dpipe_header_fields_get(struct nlattr *nla_fields,
4825 struct dpipe_field *fields)
4826 {
4827 struct nlattr *nla_field;
4828 int count = 0;
4829 int err;
4830
4831 mnl_attr_for_each_nested(nla_field, nla_fields) {
4832 err = dpipe_header_field_get(nla_field, &fields[count]);
4833 if (err)
4834 return err;
4835 count++;
4836 }
4837 return 0;
4838 }
4839
4840 static unsigned int dpipe_header_field_count_get(struct nlattr *nla_fields)
4841 {
4842 struct nlattr *nla_field;
4843 unsigned int count = 0;
4844
4845 mnl_attr_for_each_nested(nla_field, nla_fields)
4846 count++;
4847 return count;
4848 }
4849
4850 static int dpipe_header_get(struct dpipe_ctx *ctx, struct nlattr *nl)
4851 {
4852 struct nlattr *nla_header[DEVLINK_ATTR_MAX + 1] = {};
4853 struct dpipe_header *header;
4854 unsigned int fields_count;
4855 const char *header_name;
4856 bool global;
4857 int err;
4858
4859 err = mnl_attr_parse_nested(nl, attr_cb, nla_header);
4860 if (err != MNL_CB_OK)
4861 return -EINVAL;
4862
4863 if (!nla_header[DEVLINK_ATTR_DPIPE_HEADER_NAME] ||
4864 !nla_header[DEVLINK_ATTR_DPIPE_HEADER_ID] ||
4865 !nla_header[DEVLINK_ATTR_DPIPE_HEADER_FIELDS])
4866 return -EINVAL;
4867
4868 fields_count = dpipe_header_field_count_get(nla_header[DEVLINK_ATTR_DPIPE_HEADER_FIELDS]);
4869 header = dpipe_header_alloc(fields_count);
4870 if (!header)
4871 return -ENOMEM;
4872
4873 header_name = mnl_attr_get_str(nla_header[DEVLINK_ATTR_DPIPE_HEADER_NAME]);
4874 header->name = strdup(header_name);
4875 header->id = mnl_attr_get_u32(nla_header[DEVLINK_ATTR_DPIPE_HEADER_ID]);
4876 header->fields_count = fields_count;
4877 global = !!mnl_attr_get_u8(nla_header[DEVLINK_ATTR_DPIPE_HEADER_GLOBAL]);
4878
4879 err = dpipe_header_fields_get(nla_header[DEVLINK_ATTR_DPIPE_HEADER_FIELDS],
4880 header->fields);
4881 if (err)
4882 goto err_field_get;
4883 dpipe_header_add(ctx, header, global);
4884 return 0;
4885
4886 err_field_get:
4887 dpipe_header_free(header);
4888 return err;
4889 }
4890
4891 static int dpipe_headers_get(struct dpipe_ctx *ctx, struct nlattr **tb)
4892 {
4893 struct nlattr *nla_headers = tb[DEVLINK_ATTR_DPIPE_HEADERS];
4894 struct nlattr *nla_header;
4895 int err;
4896
4897 mnl_attr_for_each_nested(nla_header, nla_headers) {
4898 err = dpipe_header_get(ctx, nla_header);
4899 if (err)
4900 return err;
4901 }
4902 return 0;
4903 }
4904
4905 static int cmd_dpipe_header_cb(const struct nlmsghdr *nlh, void *data)
4906 {
4907 struct dpipe_ctx *ctx = data;
4908 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
4909 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
4910 int err;
4911
4912 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
4913 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
4914 !tb[DEVLINK_ATTR_DPIPE_HEADERS])
4915 return MNL_CB_ERROR;
4916 err = dpipe_headers_get(ctx, tb);
4917 if (err) {
4918 ctx->err = err;
4919 return MNL_CB_ERROR;
4920 }
4921
4922 if (ctx->print_headers)
4923 pr_out_dpipe_headers(ctx, tb);
4924 return MNL_CB_OK;
4925 }
4926
4927 static int cmd_dpipe_headers_show(struct dl *dl)
4928 {
4929 struct nlmsghdr *nlh;
4930 struct dpipe_ctx ctx = {};
4931 uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
4932 int err;
4933
4934 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_HEADERS_GET, flags);
4935
4936 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, 0);
4937 if (err)
4938 return err;
4939
4940 err = dpipe_ctx_init(&ctx, dl);
4941 if (err)
4942 return err;
4943
4944 ctx.print_headers = true;
4945
4946 pr_out_section_start(dl, "header");
4947 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, &ctx);
4948 if (err)
4949 pr_err("error get headers %s\n", strerror(ctx.err));
4950 pr_out_section_end(dl);
4951
4952 dpipe_ctx_fini(&ctx);
4953 return err;
4954 }
4955
4956 static void cmd_dpipe_header_help(void)
4957 {
4958 pr_err("Usage: devlink dpipe headers show DEV\n");
4959 }
4960
4961 static int cmd_dpipe_header(struct dl *dl)
4962 {
4963 if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
4964 cmd_dpipe_header_help();
4965 return 0;
4966 } else if (dl_argv_match(dl, "show")) {
4967 dl_arg_inc(dl);
4968 return cmd_dpipe_headers_show(dl);
4969 }
4970 pr_err("Command \"%s\" not found\n", dl_argv(dl));
4971 return -ENOENT;
4972 }
4973
4974 static const char
4975 *dpipe_action_type_e2s(enum devlink_dpipe_action_type action_type)
4976 {
4977 switch (action_type) {
4978 case DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY:
4979 return "field_modify";
4980 default:
4981 return "<unknown>";
4982 }
4983 }
4984
4985 struct dpipe_op_info {
4986 uint32_t header_id;
4987 uint32_t field_id;
4988 bool header_global;
4989 };
4990
4991 struct dpipe_action {
4992 struct dpipe_op_info info;
4993 uint32_t type;
4994 };
4995
4996 static void pr_out_dpipe_action(struct dpipe_action *action,
4997 struct dpipe_ctx *ctx)
4998 {
4999 struct dpipe_op_info *op_info = &action->info;
5000 const char *mapping;
5001
5002 check_indent_newline(ctx->dl);
5003 print_string(PRINT_ANY, "type", "type %s",
5004 dpipe_action_type_e2s(action->type));
5005 print_string(PRINT_ANY, "header", " header %s",
5006 dpipe_header_id2s(ctx, op_info->header_id,
5007 op_info->header_global));
5008 print_string(PRINT_ANY, "field", " field %s",
5009 dpipe_field_id2s(ctx, op_info->header_id,
5010 op_info->field_id,
5011 op_info->header_global));
5012 mapping = dpipe_mapping_get(ctx, op_info->header_id,
5013 op_info->field_id,
5014 op_info->header_global);
5015 if (mapping)
5016 print_string(PRINT_ANY, "mapping", " mapping %s", mapping);
5017 }
5018
5019 static int dpipe_action_parse(struct dpipe_action *action, struct nlattr *nl)
5020 {
5021 struct nlattr *nla_action[DEVLINK_ATTR_MAX + 1] = {};
5022 int err;
5023
5024 err = mnl_attr_parse_nested(nl, attr_cb, nla_action);
5025 if (err != MNL_CB_OK)
5026 return -EINVAL;
5027
5028 if (!nla_action[DEVLINK_ATTR_DPIPE_ACTION_TYPE] ||
5029 !nla_action[DEVLINK_ATTR_DPIPE_HEADER_INDEX] ||
5030 !nla_action[DEVLINK_ATTR_DPIPE_HEADER_ID] ||
5031 !nla_action[DEVLINK_ATTR_DPIPE_FIELD_ID]) {
5032 return -EINVAL;
5033 }
5034
5035 action->type = mnl_attr_get_u32(nla_action[DEVLINK_ATTR_DPIPE_ACTION_TYPE]);
5036 action->info.header_id = mnl_attr_get_u32(nla_action[DEVLINK_ATTR_DPIPE_HEADER_ID]);
5037 action->info.field_id = mnl_attr_get_u32(nla_action[DEVLINK_ATTR_DPIPE_FIELD_ID]);
5038 action->info.header_global = !!mnl_attr_get_u8(nla_action[DEVLINK_ATTR_DPIPE_HEADER_GLOBAL]);
5039
5040 return 0;
5041 }
5042
5043 static int dpipe_table_actions_show(struct dpipe_ctx *ctx,
5044 struct nlattr *nla_actions)
5045 {
5046 struct nlattr *nla_action;
5047 struct dpipe_action action;
5048
5049 mnl_attr_for_each_nested(nla_action, nla_actions) {
5050 pr_out_entry_start(ctx->dl);
5051 if (dpipe_action_parse(&action, nla_action))
5052 goto err_action_parse;
5053 pr_out_dpipe_action(&action, ctx);
5054 pr_out_entry_end(ctx->dl);
5055 }
5056 return 0;
5057
5058 err_action_parse:
5059 pr_out_entry_end(ctx->dl);
5060 return -EINVAL;
5061 }
5062
5063 static const char *
5064 dpipe_match_type_e2s(enum devlink_dpipe_match_type match_type)
5065 {
5066 switch (match_type) {
5067 case DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT:
5068 return "field_exact";
5069 default:
5070 return "<unknown>";
5071 }
5072 }
5073
5074 struct dpipe_match {
5075 struct dpipe_op_info info;
5076 uint32_t type;
5077 };
5078
5079 static void pr_out_dpipe_match(struct dpipe_match *match,
5080 struct dpipe_ctx *ctx)
5081 {
5082 struct dpipe_op_info *op_info = &match->info;
5083 const char *mapping;
5084
5085 check_indent_newline(ctx->dl);
5086 print_string(PRINT_ANY, "type", "type %s",
5087 dpipe_match_type_e2s(match->type));
5088 print_string(PRINT_ANY, "header", " header %s",
5089 dpipe_header_id2s(ctx, op_info->header_id,
5090 op_info->header_global));
5091 print_string(PRINT_ANY, "field", " field %s",
5092 dpipe_field_id2s(ctx, op_info->header_id,
5093 op_info->field_id,
5094 op_info->header_global));
5095 mapping = dpipe_mapping_get(ctx, op_info->header_id,
5096 op_info->field_id,
5097 op_info->header_global);
5098 if (mapping)
5099 print_string(PRINT_ANY, "mapping", " mapping %s", mapping);
5100 }
5101
5102 static int dpipe_match_parse(struct dpipe_match *match,
5103 struct nlattr *nl)
5104
5105 {
5106 struct nlattr *nla_match[DEVLINK_ATTR_MAX + 1] = {};
5107 int err;
5108
5109 err = mnl_attr_parse_nested(nl, attr_cb, nla_match);
5110 if (err != MNL_CB_OK)
5111 return -EINVAL;
5112
5113 if (!nla_match[DEVLINK_ATTR_DPIPE_MATCH_TYPE] ||
5114 !nla_match[DEVLINK_ATTR_DPIPE_HEADER_INDEX] ||
5115 !nla_match[DEVLINK_ATTR_DPIPE_HEADER_ID] ||
5116 !nla_match[DEVLINK_ATTR_DPIPE_FIELD_ID]) {
5117 return -EINVAL;
5118 }
5119
5120 match->type = mnl_attr_get_u32(nla_match[DEVLINK_ATTR_DPIPE_MATCH_TYPE]);
5121 match->info.header_id = mnl_attr_get_u32(nla_match[DEVLINK_ATTR_DPIPE_HEADER_ID]);
5122 match->info.field_id = mnl_attr_get_u32(nla_match[DEVLINK_ATTR_DPIPE_FIELD_ID]);
5123 match->info.header_global = !!mnl_attr_get_u8(nla_match[DEVLINK_ATTR_DPIPE_HEADER_GLOBAL]);
5124
5125 return 0;
5126 }
5127
5128 static int dpipe_table_matches_show(struct dpipe_ctx *ctx,
5129 struct nlattr *nla_matches)
5130 {
5131 struct nlattr *nla_match;
5132 struct dpipe_match match;
5133
5134 mnl_attr_for_each_nested(nla_match, nla_matches) {
5135 pr_out_entry_start(ctx->dl);
5136 if (dpipe_match_parse(&match, nla_match))
5137 goto err_match_parse;
5138 pr_out_dpipe_match(&match, ctx);
5139 pr_out_entry_end(ctx->dl);
5140 }
5141 return 0;
5142
5143 err_match_parse:
5144 pr_out_entry_end(ctx->dl);
5145 return -EINVAL;
5146 }
5147
5148 static struct resource *
5149 resource_find(struct resources *resources, struct resource *resource,
5150 uint64_t resource_id)
5151 {
5152 struct list_head *list_head;
5153
5154 if (!resource)
5155 list_head = &resources->resource_list;
5156 else
5157 list_head = &resource->resource_list;
5158
5159 list_for_each_entry(resource, list_head, list) {
5160 struct resource *child_resource;
5161
5162 if (resource->id == resource_id)
5163 return resource;
5164
5165 child_resource = resource_find(resources, resource,
5166 resource_id);
5167 if (child_resource)
5168 return child_resource;
5169 }
5170 return NULL;
5171 }
5172
5173 static void
5174 resource_path_print(struct dl *dl, struct resources *resources,
5175 uint64_t resource_id)
5176 {
5177 struct resource *resource, *parent_resource;
5178 const char del[] = "/";
5179 int path_len = 0;
5180 char *path;
5181
5182 resource = resource_find(resources, NULL, resource_id);
5183 if (!resource)
5184 return;
5185
5186 for (parent_resource = resource; parent_resource;
5187 parent_resource = parent_resource->parent)
5188 path_len += strlen(parent_resource->name) + 1;
5189
5190 path_len++;
5191 path = calloc(1, path_len);
5192 if (!path)
5193 return;
5194
5195 path += path_len - 1;
5196 for (parent_resource = resource; parent_resource;
5197 parent_resource = parent_resource->parent) {
5198 path -= strlen(parent_resource->name);
5199 memcpy(path, parent_resource->name,
5200 strlen(parent_resource->name));
5201 path -= strlen(del);
5202 memcpy(path, del, strlen(del));
5203 }
5204 check_indent_newline(dl);
5205 print_string(PRINT_ANY, "resource_path", "resource_path %s", path);
5206 free(path);
5207 }
5208
5209 static int dpipe_table_show(struct dpipe_ctx *ctx, struct nlattr *nl)
5210 {
5211 struct nlattr *nla_table[DEVLINK_ATTR_MAX + 1] = {};
5212 struct dpipe_table *table;
5213 uint32_t resource_units;
5214 bool counters_enabled;
5215 bool resource_valid;
5216 uint32_t size;
5217 int err;
5218
5219 err = mnl_attr_parse_nested(nl, attr_cb, nla_table);
5220 if (err != MNL_CB_OK)
5221 return -EINVAL;
5222
5223 if (!nla_table[DEVLINK_ATTR_DPIPE_TABLE_NAME] ||
5224 !nla_table[DEVLINK_ATTR_DPIPE_TABLE_SIZE] ||
5225 !nla_table[DEVLINK_ATTR_DPIPE_TABLE_ACTIONS] ||
5226 !nla_table[DEVLINK_ATTR_DPIPE_TABLE_MATCHES] ||
5227 !nla_table[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED]) {
5228 return -EINVAL;
5229 }
5230
5231 table = dpipe_table_alloc();
5232 if (!table)
5233 return -ENOMEM;
5234
5235 table->name = strdup(mnl_attr_get_str(nla_table[DEVLINK_ATTR_DPIPE_TABLE_NAME]));
5236 size = mnl_attr_get_u32(nla_table[DEVLINK_ATTR_DPIPE_TABLE_SIZE]);
5237 counters_enabled = !!mnl_attr_get_u8(nla_table[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED]);
5238
5239 resource_valid = nla_table[DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID] &&
5240 ctx->resources;
5241 if (resource_valid) {
5242 table->resource_id = mnl_attr_get_u64(nla_table[DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID]);
5243 table->resource_valid = true;
5244 }
5245
5246 list_add_tail(&table->list, &ctx->tables->table_list);
5247 if (!ctx->print_tables)
5248 return 0;
5249
5250 check_indent_newline(ctx->dl);
5251 print_string(PRINT_ANY, "name", "name %s", table->name);
5252 print_uint(PRINT_ANY, "size", " size %u", size);
5253 print_bool(PRINT_ANY, "counters_enabled", " counters_enabled %s", counters_enabled);
5254
5255 if (resource_valid) {
5256 resource_units = mnl_attr_get_u32(nla_table[DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_UNITS]);
5257 resource_path_print(ctx->dl, ctx->resources,
5258 table->resource_id);
5259 print_uint(PRINT_ANY, "resource_units", " resource_units %u",
5260 resource_units);
5261 }
5262
5263 pr_out_array_start(ctx->dl, "match");
5264 if (dpipe_table_matches_show(ctx, nla_table[DEVLINK_ATTR_DPIPE_TABLE_MATCHES]))
5265 goto err_matches_show;
5266 pr_out_array_end(ctx->dl);
5267
5268 pr_out_array_start(ctx->dl, "action");
5269 if (dpipe_table_actions_show(ctx, nla_table[DEVLINK_ATTR_DPIPE_TABLE_ACTIONS]))
5270 goto err_actions_show;
5271 pr_out_array_end(ctx->dl);
5272
5273 return 0;
5274
5275 err_actions_show:
5276 err_matches_show:
5277 pr_out_array_end(ctx->dl);
5278 return -EINVAL;
5279 }
5280
5281 static int dpipe_tables_show(struct dpipe_ctx *ctx, struct nlattr **tb)
5282 {
5283 struct nlattr *nla_tables = tb[DEVLINK_ATTR_DPIPE_TABLES];
5284 struct nlattr *nla_table;
5285
5286 mnl_attr_for_each_nested(nla_table, nla_tables) {
5287 if (ctx->print_tables)
5288 pr_out_handle_start_arr(ctx->dl, tb);
5289 if (dpipe_table_show(ctx, nla_table))
5290 goto err_table_show;
5291 if (ctx->print_tables)
5292 pr_out_handle_end(ctx->dl);
5293 }
5294 return 0;
5295
5296 err_table_show:
5297 if (ctx->print_tables)
5298 pr_out_handle_end(ctx->dl);
5299 return -EINVAL;
5300 }
5301
5302 static int cmd_dpipe_table_show_cb(const struct nlmsghdr *nlh, void *data)
5303 {
5304 struct dpipe_ctx *ctx = data;
5305 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
5306 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
5307
5308 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
5309 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
5310 !tb[DEVLINK_ATTR_DPIPE_TABLES])
5311 return MNL_CB_ERROR;
5312
5313 if (dpipe_tables_show(ctx, tb))
5314 return MNL_CB_ERROR;
5315 return MNL_CB_OK;
5316 }
5317
5318 static int cmd_resource_dump_cb(const struct nlmsghdr *nlh, void *data);
5319
5320 static int cmd_dpipe_table_show(struct dl *dl)
5321 {
5322 struct nlmsghdr *nlh;
5323 struct dpipe_ctx dpipe_ctx = {};
5324 struct resource_ctx resource_ctx = {};
5325 uint16_t flags = NLM_F_REQUEST;
5326 int err;
5327
5328 err = dl_argv_parse(dl, DL_OPT_HANDLE, DL_OPT_DPIPE_TABLE_NAME);
5329 if (err)
5330 return err;
5331
5332 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_HEADERS_GET, flags);
5333
5334 err = dpipe_ctx_init(&dpipe_ctx, dl);
5335 if (err)
5336 return err;
5337
5338 dpipe_ctx.print_tables = true;
5339
5340 dl_opts_put(nlh, dl);
5341 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb,
5342 &dpipe_ctx);
5343 if (err) {
5344 pr_err("error get headers %s\n", strerror(dpipe_ctx.err));
5345 goto err_headers_get;
5346 }
5347
5348 err = resource_ctx_init(&resource_ctx, dl);
5349 if (err)
5350 goto err_resource_ctx_init;
5351
5352 resource_ctx.print_resources = false;
5353 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RESOURCE_DUMP, flags);
5354 dl_opts_put(nlh, dl);
5355 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_resource_dump_cb,
5356 &resource_ctx);
5357 if (!err)
5358 dpipe_ctx.resources = resource_ctx.resources;
5359
5360 flags = NLM_F_REQUEST | NLM_F_ACK;
5361 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_TABLE_GET, flags);
5362 dl_opts_put(nlh, dl);
5363
5364 pr_out_section_start(dl, "table");
5365 _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_table_show_cb, &dpipe_ctx);
5366 pr_out_section_end(dl);
5367
5368 resource_ctx_fini(&resource_ctx);
5369 dpipe_ctx_fini(&dpipe_ctx);
5370 return 0;
5371
5372 err_resource_ctx_init:
5373 err_headers_get:
5374 dpipe_ctx_fini(&dpipe_ctx);
5375 return err;
5376 }
5377
5378 static int cmd_dpipe_table_set(struct dl *dl)
5379 {
5380 struct nlmsghdr *nlh;
5381 int err;
5382
5383 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_TABLE_COUNTERS_SET,
5384 NLM_F_REQUEST | NLM_F_ACK);
5385
5386 err = dl_argv_parse_put(nlh, dl,
5387 DL_OPT_HANDLE | DL_OPT_DPIPE_TABLE_NAME |
5388 DL_OPT_DPIPE_TABLE_COUNTERS, 0);
5389 if (err)
5390 return err;
5391
5392 return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
5393 }
5394
5395 enum dpipe_value_type {
5396 DPIPE_VALUE_TYPE_VALUE,
5397 DPIPE_VALUE_TYPE_MASK,
5398 };
5399
5400 static const char *
5401 dpipe_value_type_e2s(enum dpipe_value_type type)
5402 {
5403 switch (type) {
5404 case DPIPE_VALUE_TYPE_VALUE:
5405 return "value";
5406 case DPIPE_VALUE_TYPE_MASK:
5407 return "value_mask";
5408 default:
5409 return "<unknown>";
5410 }
5411 }
5412
5413 struct dpipe_field_printer {
5414 unsigned int field_id;
5415 void (*printer)(struct dpipe_ctx *, enum dpipe_value_type, void *);
5416 };
5417
5418 struct dpipe_header_printer {
5419 struct dpipe_field_printer *printers;
5420 unsigned int printers_count;
5421 unsigned int header_id;
5422 };
5423
5424 static void dpipe_field_printer_ipv4_addr(struct dpipe_ctx *ctx,
5425 enum dpipe_value_type type,
5426 void *value)
5427 {
5428 struct in_addr ip_addr;
5429
5430 ip_addr.s_addr = htonl(*(uint32_t *)value);
5431 check_indent_newline(ctx->dl);
5432 print_string_name_value(dpipe_value_type_e2s(type), inet_ntoa(ip_addr));
5433 }
5434
5435 static void
5436 dpipe_field_printer_ethernet_addr(struct dpipe_ctx *ctx,
5437 enum dpipe_value_type type,
5438 void *value)
5439 {
5440 check_indent_newline(ctx->dl);
5441 print_string_name_value(dpipe_value_type_e2s(type),
5442 ether_ntoa((struct ether_addr *)value));
5443 }
5444
5445 static void dpipe_field_printer_ipv6_addr(struct dpipe_ctx *ctx,
5446 enum dpipe_value_type type,
5447 void *value)
5448 {
5449 char str[INET6_ADDRSTRLEN];
5450
5451 inet_ntop(AF_INET6, value, str, INET6_ADDRSTRLEN);
5452 check_indent_newline(ctx->dl);
5453 print_string_name_value(dpipe_value_type_e2s(type), str);
5454 }
5455
5456 static struct dpipe_field_printer dpipe_field_printers_ipv4[] = {
5457 {
5458 .printer = dpipe_field_printer_ipv4_addr,
5459 .field_id = DEVLINK_DPIPE_FIELD_IPV4_DST_IP,
5460 }
5461 };
5462
5463 static struct dpipe_header_printer dpipe_header_printer_ipv4 = {
5464 .printers = dpipe_field_printers_ipv4,
5465 .printers_count = ARRAY_SIZE(dpipe_field_printers_ipv4),
5466 .header_id = DEVLINK_DPIPE_HEADER_IPV4,
5467 };
5468
5469 static struct dpipe_field_printer dpipe_field_printers_ethernet[] = {
5470 {
5471 .printer = dpipe_field_printer_ethernet_addr,
5472 .field_id = DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC,
5473 },
5474 };
5475
5476 static struct dpipe_header_printer dpipe_header_printer_ethernet = {
5477 .printers = dpipe_field_printers_ethernet,
5478 .printers_count = ARRAY_SIZE(dpipe_field_printers_ethernet),
5479 .header_id = DEVLINK_DPIPE_HEADER_ETHERNET,
5480 };
5481
5482 static struct dpipe_field_printer dpipe_field_printers_ipv6[] = {
5483 {
5484 .printer = dpipe_field_printer_ipv6_addr,
5485 .field_id = DEVLINK_DPIPE_FIELD_IPV6_DST_IP,
5486 }
5487 };
5488
5489 static struct dpipe_header_printer dpipe_header_printer_ipv6 = {
5490 .printers = dpipe_field_printers_ipv6,
5491 .printers_count = ARRAY_SIZE(dpipe_field_printers_ipv6),
5492 .header_id = DEVLINK_DPIPE_HEADER_IPV6,
5493 };
5494
5495 static struct dpipe_header_printer *dpipe_header_printers[] = {
5496 &dpipe_header_printer_ipv4,
5497 &dpipe_header_printer_ethernet,
5498 &dpipe_header_printer_ipv6,
5499 };
5500
5501 static int dpipe_print_prot_header(struct dpipe_ctx *ctx,
5502 struct dpipe_op_info *info,
5503 enum dpipe_value_type type,
5504 void *value)
5505 {
5506 unsigned int header_printers_count = ARRAY_SIZE(dpipe_header_printers);
5507 struct dpipe_header_printer *header_printer;
5508 struct dpipe_field_printer *field_printer;
5509 unsigned int field_printers_count;
5510 int j;
5511 int i;
5512
5513 for (i = 0; i < header_printers_count; i++) {
5514 header_printer = dpipe_header_printers[i];
5515 if (header_printer->header_id != info->header_id)
5516 continue;
5517 field_printers_count = header_printer->printers_count;
5518 for (j = 0; j < field_printers_count; j++) {
5519 field_printer = &header_printer->printers[j];
5520 if (field_printer->field_id != info->field_id)
5521 continue;
5522 field_printer->printer(ctx, type, value);
5523 return 0;
5524 }
5525 }
5526
5527 return -EINVAL;
5528 }
5529
5530 static void __pr_out_entry_value(struct dpipe_ctx *ctx,
5531 void *value,
5532 unsigned int value_len,
5533 struct dpipe_op_info *info,
5534 enum dpipe_value_type type)
5535 {
5536 if (info->header_global &&
5537 !dpipe_print_prot_header(ctx, info, type, value))
5538 return;
5539
5540 if (value_len == sizeof(uint32_t)) {
5541 uint32_t *value_32 = value;
5542
5543 check_indent_newline(ctx->dl);
5544 print_uint_name_value(dpipe_value_type_e2s(type), *value_32);
5545 }
5546 }
5547
5548 static void pr_out_dpipe_entry_value(struct dpipe_ctx *ctx,
5549 struct nlattr **nla_match_value,
5550 struct dpipe_op_info *info)
5551 {
5552 void *value, *value_mask;
5553 uint32_t value_mapping;
5554 uint16_t value_len;
5555 bool mask, mapping;
5556
5557 mask = !!nla_match_value[DEVLINK_ATTR_DPIPE_VALUE_MASK];
5558 mapping = !!nla_match_value[DEVLINK_ATTR_DPIPE_VALUE_MAPPING];
5559
5560 value_len = mnl_attr_get_payload_len(nla_match_value[DEVLINK_ATTR_DPIPE_VALUE]);
5561 value = mnl_attr_get_payload(nla_match_value[DEVLINK_ATTR_DPIPE_VALUE]);
5562
5563 if (mapping) {
5564 value_mapping = mnl_attr_get_u32(nla_match_value[DEVLINK_ATTR_DPIPE_VALUE_MAPPING]);
5565 check_indent_newline(ctx->dl);
5566 print_uint(PRINT_ANY, "mapping_value", "mapping_value %u", value_mapping);
5567 }
5568
5569 if (mask) {
5570 value_mask = mnl_attr_get_payload(nla_match_value[DEVLINK_ATTR_DPIPE_VALUE]);
5571 __pr_out_entry_value(ctx, value_mask, value_len, info,
5572 DPIPE_VALUE_TYPE_MASK);
5573 }
5574
5575 __pr_out_entry_value(ctx, value, value_len, info, DPIPE_VALUE_TYPE_VALUE);
5576 }
5577
5578 static int dpipe_entry_match_value_show(struct dpipe_ctx *ctx,
5579 struct nlattr *nl)
5580 {
5581 struct nlattr *nla_match_value[DEVLINK_ATTR_MAX + 1] = {};
5582 struct dpipe_match match;
5583 int err;
5584
5585 err = mnl_attr_parse_nested(nl, attr_cb, nla_match_value);
5586 if (err != MNL_CB_OK)
5587 return -EINVAL;
5588
5589 if (!nla_match_value[DEVLINK_ATTR_DPIPE_MATCH] ||
5590 !nla_match_value[DEVLINK_ATTR_DPIPE_VALUE]) {
5591 return -EINVAL;
5592 }
5593
5594 pr_out_entry_start(ctx->dl);
5595 if (dpipe_match_parse(&match,
5596 nla_match_value[DEVLINK_ATTR_DPIPE_MATCH]))
5597 goto err_match_parse;
5598 pr_out_dpipe_match(&match, ctx);
5599 pr_out_dpipe_entry_value(ctx, nla_match_value, &match.info);
5600 pr_out_entry_end(ctx->dl);
5601
5602 return 0;
5603
5604 err_match_parse:
5605 pr_out_entry_end(ctx->dl);
5606 return -EINVAL;
5607 }
5608
5609 static int dpipe_entry_action_value_show(struct dpipe_ctx *ctx,
5610 struct nlattr *nl)
5611 {
5612 struct nlattr *nla_action_value[DEVLINK_ATTR_MAX + 1] = {};
5613 struct dpipe_action action;
5614 int err;
5615
5616 err = mnl_attr_parse_nested(nl, attr_cb, nla_action_value);
5617 if (err != MNL_CB_OK)
5618 return -EINVAL;
5619
5620 if (!nla_action_value[DEVLINK_ATTR_DPIPE_ACTION] ||
5621 !nla_action_value[DEVLINK_ATTR_DPIPE_VALUE]) {
5622 return -EINVAL;
5623 }
5624
5625 pr_out_entry_start(ctx->dl);
5626 if (dpipe_action_parse(&action,
5627 nla_action_value[DEVLINK_ATTR_DPIPE_ACTION]))
5628 goto err_action_parse;
5629 pr_out_dpipe_action(&action, ctx);
5630 pr_out_dpipe_entry_value(ctx, nla_action_value, &action.info);
5631 pr_out_entry_end(ctx->dl);
5632
5633 return 0;
5634
5635 err_action_parse:
5636 pr_out_entry_end(ctx->dl);
5637 return -EINVAL;
5638 }
5639
5640 static int
5641 dpipe_tables_action_values_show(struct dpipe_ctx *ctx,
5642 struct nlattr *nla_action_values)
5643 {
5644 struct nlattr *nla_action_value;
5645
5646 mnl_attr_for_each_nested(nla_action_value, nla_action_values) {
5647 if (dpipe_entry_action_value_show(ctx, nla_action_value))
5648 return -EINVAL;
5649 }
5650 return 0;
5651 }
5652
5653 static int
5654 dpipe_tables_match_values_show(struct dpipe_ctx *ctx,
5655 struct nlattr *nla_match_values)
5656 {
5657 struct nlattr *nla_match_value;
5658
5659 mnl_attr_for_each_nested(nla_match_value, nla_match_values) {
5660 if (dpipe_entry_match_value_show(ctx, nla_match_value))
5661 return -EINVAL;
5662 }
5663 return 0;
5664 }
5665
5666 static int dpipe_entry_show(struct dpipe_ctx *ctx, struct nlattr *nl)
5667 {
5668 struct nlattr *nla_entry[DEVLINK_ATTR_MAX + 1] = {};
5669 uint32_t entry_index;
5670 uint64_t counter;
5671 int err;
5672
5673 err = mnl_attr_parse_nested(nl, attr_cb, nla_entry);
5674 if (err != MNL_CB_OK)
5675 return -EINVAL;
5676
5677 if (!nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_INDEX] ||
5678 !nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES] ||
5679 !nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES]) {
5680 return -EINVAL;
5681 }
5682
5683 check_indent_newline(ctx->dl);
5684 entry_index = mnl_attr_get_u32(nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_INDEX]);
5685 print_uint(PRINT_ANY, "index", "index %u", entry_index);
5686
5687 if (nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_COUNTER]) {
5688 counter = mnl_attr_get_u64(nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_COUNTER]);
5689 print_uint(PRINT_ANY, "counter", " counter %u", counter);
5690 }
5691
5692 pr_out_array_start(ctx->dl, "match_value");
5693 if (dpipe_tables_match_values_show(ctx,
5694 nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES]))
5695 goto err_match_values_show;
5696 pr_out_array_end(ctx->dl);
5697
5698 pr_out_array_start(ctx->dl, "action_value");
5699 if (dpipe_tables_action_values_show(ctx,
5700 nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES]))
5701 goto err_action_values_show;
5702 pr_out_array_end(ctx->dl);
5703 return 0;
5704
5705 err_action_values_show:
5706 err_match_values_show:
5707 pr_out_array_end(ctx->dl);
5708 return -EINVAL;
5709 }
5710
5711 static int dpipe_table_entries_show(struct dpipe_ctx *ctx, struct nlattr **tb)
5712 {
5713 struct nlattr *nla_entries = tb[DEVLINK_ATTR_DPIPE_ENTRIES];
5714 struct nlattr *nla_entry;
5715
5716 mnl_attr_for_each_nested(nla_entry, nla_entries) {
5717 pr_out_handle_start_arr(ctx->dl, tb);
5718 if (dpipe_entry_show(ctx, nla_entry))
5719 goto err_entry_show;
5720 pr_out_handle_end(ctx->dl);
5721 }
5722 return 0;
5723
5724 err_entry_show:
5725 pr_out_handle_end(ctx->dl);
5726 return -EINVAL;
5727 }
5728
5729 static int cmd_dpipe_table_entry_dump_cb(const struct nlmsghdr *nlh, void *data)
5730 {
5731 struct dpipe_ctx *ctx = data;
5732 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
5733 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
5734
5735 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
5736 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
5737 !tb[DEVLINK_ATTR_DPIPE_ENTRIES])
5738 return MNL_CB_ERROR;
5739
5740 if (dpipe_table_entries_show(ctx, tb))
5741 return MNL_CB_ERROR;
5742 return MNL_CB_OK;
5743 }
5744
5745 static int cmd_dpipe_table_dump(struct dl *dl)
5746 {
5747 struct nlmsghdr *nlh;
5748 struct dpipe_ctx ctx = {};
5749 uint16_t flags = NLM_F_REQUEST;
5750 int err;
5751
5752 err = dpipe_ctx_init(&ctx, dl);
5753 if (err)
5754 return err;
5755
5756 err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_DPIPE_TABLE_NAME, 0);
5757 if (err)
5758 goto out;
5759
5760 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_HEADERS_GET, flags);
5761 dl_opts_put(nlh, dl);
5762 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, &ctx);
5763 if (err) {
5764 pr_err("error get headers %s\n", strerror(ctx.err));
5765 goto out;
5766 }
5767
5768 flags = NLM_F_REQUEST | NLM_F_ACK;
5769 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_ENTRIES_GET, flags);
5770 dl_opts_put(nlh, dl);
5771
5772 pr_out_section_start(dl, "table_entry");
5773 _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_table_entry_dump_cb, &ctx);
5774 pr_out_section_end(dl);
5775 out:
5776 dpipe_ctx_fini(&ctx);
5777 return err;
5778 }
5779
5780 static void cmd_dpipe_table_help(void)
5781 {
5782 pr_err("Usage: devlink dpipe table [ OBJECT-LIST ]\n"
5783 "where OBJECT-LIST := { show | set | dump }\n");
5784 }
5785
5786 static int cmd_dpipe_table(struct dl *dl)
5787 {
5788 if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
5789 cmd_dpipe_table_help();
5790 return 0;
5791 } else if (dl_argv_match(dl, "show")) {
5792 dl_arg_inc(dl);
5793 return cmd_dpipe_table_show(dl);
5794 } else if (dl_argv_match(dl, "set")) {
5795 dl_arg_inc(dl);
5796 return cmd_dpipe_table_set(dl);
5797 } else if (dl_argv_match(dl, "dump")) {
5798 dl_arg_inc(dl);
5799 return cmd_dpipe_table_dump(dl);
5800 }
5801 pr_err("Command \"%s\" not found\n", dl_argv(dl));
5802 return -ENOENT;
5803 }
5804
5805 static void cmd_dpipe_help(void)
5806 {
5807 pr_err("Usage: devlink dpipe [ OBJECT-LIST ]\n"
5808 "where OBJECT-LIST := { header | table }\n");
5809 }
5810
5811 static int cmd_dpipe(struct dl *dl)
5812 {
5813 if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
5814 cmd_dpipe_help();
5815 return 0;
5816 } else if (dl_argv_match(dl, "header")) {
5817 dl_arg_inc(dl);
5818 return cmd_dpipe_header(dl);
5819 } else if (dl_argv_match(dl, "table")) {
5820 dl_arg_inc(dl);
5821 return cmd_dpipe_table(dl);
5822 }
5823 pr_err("Command \"%s\" not found\n", dl_argv(dl));
5824 return -ENOENT;
5825 }
5826
5827 static int
5828 resource_parse(struct resource_ctx *ctx, struct resource *resource,
5829 struct nlattr **nla_resource)
5830 {
5831 if (!nla_resource[DEVLINK_ATTR_RESOURCE_NAME] ||
5832 !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE] ||
5833 !nla_resource[DEVLINK_ATTR_RESOURCE_ID] ||
5834 !nla_resource[DEVLINK_ATTR_RESOURCE_UNIT] ||
5835 !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MIN] ||
5836 !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MAX] ||
5837 !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_GRAN]) {
5838 return -EINVAL;
5839 }
5840
5841 resource->name = strdup(mnl_attr_get_str(nla_resource[DEVLINK_ATTR_RESOURCE_NAME]));
5842 resource->size = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE]);
5843 resource->id = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_ID]);
5844 resource->unit = mnl_attr_get_u8(nla_resource[DEVLINK_ATTR_RESOURCE_UNIT]);
5845 resource->size_min = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MIN]);
5846 resource->size_max = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MAX]);
5847 resource->size_gran = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_GRAN]);
5848
5849 if (nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_NEW])
5850 resource->size_new = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_NEW]);
5851 else
5852 resource->size_new = resource->size;
5853
5854 if (nla_resource[DEVLINK_ATTR_RESOURCE_OCC]) {
5855 resource->size_occ = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_OCC]);
5856 resource->occ_valid = true;
5857 }
5858
5859 if (resource->size_new != resource->size)
5860 ctx->pending_change = true;
5861
5862 return 0;
5863 }
5864
5865 static int
5866 resource_get(struct resource_ctx *ctx, struct resource *resource,
5867 struct resource *parent_resource, struct nlattr *nl)
5868 {
5869 struct nlattr *nla_resource[DEVLINK_ATTR_MAX + 1] = {};
5870 struct nlattr *nla_child_resource;
5871 struct nlattr *nla_resources;
5872 bool top = false;
5873 int err;
5874
5875 if (!resource) {
5876 nla_resources = nl;
5877 top = true;
5878 goto out;
5879 }
5880
5881 err = mnl_attr_parse_nested(nl, attr_cb, nla_resource);
5882 if (err != MNL_CB_OK)
5883 return -EINVAL;
5884
5885 err = resource_parse(ctx, resource, nla_resource);
5886 if (err)
5887 return err;
5888
5889 resource->parent = parent_resource;
5890 if (!nla_resource[DEVLINK_ATTR_RESOURCE_LIST])
5891 return 0;
5892
5893 resource->size_valid = !!mnl_attr_get_u8(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_VALID]);
5894 nla_resources = nla_resource[DEVLINK_ATTR_RESOURCE_LIST];
5895 out:
5896 mnl_attr_for_each_nested(nla_child_resource, nla_resources) {
5897 struct resource *child_resource;
5898 struct list_head *list;
5899
5900 child_resource = resource_alloc();
5901 if (!child_resource)
5902 return -ENOMEM;
5903
5904 if (top)
5905 list = &ctx->resources->resource_list;
5906 else
5907 list = &resource->resource_list;
5908
5909 list_add_tail(&child_resource->list, list);
5910 err = resource_get(ctx, child_resource, resource,
5911 nla_child_resource);
5912 if (err)
5913 return err;
5914 }
5915
5916 return 0;
5917 }
5918
5919 static const char *resource_unit_str_get(enum devlink_resource_unit unit)
5920 {
5921 switch (unit) {
5922 case DEVLINK_RESOURCE_UNIT_ENTRY: return "entry";
5923 default: return "<unknown unit>";
5924 }
5925 }
5926
5927 static void resource_show(struct resource *resource,
5928 struct resource_ctx *ctx)
5929 {
5930 struct resource *child_resource;
5931 struct dpipe_table *table;
5932 struct dl *dl = ctx->dl;
5933 bool array = false;
5934
5935 check_indent_newline(dl);
5936 print_string(PRINT_ANY, "name", "name %s", resource->name);
5937 if (dl->verbose)
5938 resource_path_print(dl, ctx->resources, resource->id);
5939 pr_out_u64(dl, "size", resource->size);
5940 if (resource->size != resource->size_new)
5941 pr_out_u64(dl, "size_new", resource->size_new);
5942 if (resource->occ_valid)
5943 print_uint(PRINT_ANY, "occ", " occ %u", resource->size_occ);
5944 print_string(PRINT_ANY, "unit", " unit %s",
5945 resource_unit_str_get(resource->unit));
5946
5947 if (resource->size_min != resource->size_max) {
5948 print_uint(PRINT_ANY, "size_min", " size_min %u",
5949 resource->size_min);
5950 pr_out_u64(dl, "size_max", resource->size_max);
5951 print_uint(PRINT_ANY, "size_gran", " size_gran %u",
5952 resource->size_gran);
5953 }
5954
5955 list_for_each_entry(table, &ctx->tables->table_list, list)
5956 if (table->resource_id == resource->id &&
5957 table->resource_valid)
5958 array = true;
5959
5960 if (array)
5961 pr_out_array_start(dl, "dpipe_tables");
5962 else
5963 print_string(PRINT_ANY, "dpipe_tables", " dpipe_tables none",
5964 "none");
5965
5966 list_for_each_entry(table, &ctx->tables->table_list, list) {
5967 if (table->resource_id != resource->id ||
5968 !table->resource_valid)
5969 continue;
5970 pr_out_entry_start(dl);
5971 check_indent_newline(dl);
5972 print_string(PRINT_ANY, "table_name", "table_name %s",
5973 table->name);
5974 pr_out_entry_end(dl);
5975 }
5976 if (array)
5977 pr_out_array_end(dl);
5978
5979 if (list_empty(&resource->resource_list))
5980 return;
5981
5982 if (ctx->pending_change) {
5983 check_indent_newline(dl);
5984 print_string(PRINT_ANY, "size_valid", "size_valid %s",
5985 resource->size_valid ? "true" : "false");
5986 }
5987 pr_out_array_start(dl, "resources");
5988 list_for_each_entry(child_resource, &resource->resource_list, list) {
5989 pr_out_entry_start(dl);
5990 resource_show(child_resource, ctx);
5991 pr_out_entry_end(dl);
5992 }
5993 pr_out_array_end(dl);
5994 }
5995
5996 static void
5997 resources_show(struct resource_ctx *ctx, struct nlattr **tb)
5998 {
5999 struct resources *resources = ctx->resources;
6000 struct resource *resource;
6001
6002 list_for_each_entry(resource, &resources->resource_list, list) {
6003 pr_out_handle_start_arr(ctx->dl, tb);
6004 resource_show(resource, ctx);
6005 pr_out_handle_end(ctx->dl);
6006 }
6007 }
6008
6009 static int resources_get(struct resource_ctx *ctx, struct nlattr **tb)
6010 {
6011 return resource_get(ctx, NULL, NULL, tb[DEVLINK_ATTR_RESOURCE_LIST]);
6012 }
6013
6014 static int cmd_resource_dump_cb(const struct nlmsghdr *nlh, void *data)
6015 {
6016 struct resource_ctx *ctx = data;
6017 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
6018 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
6019 int err;
6020
6021 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
6022 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
6023 !tb[DEVLINK_ATTR_RESOURCE_LIST])
6024 return MNL_CB_ERROR;
6025
6026 err = resources_get(ctx, tb);
6027 if (err) {
6028 ctx->err = err;
6029 return MNL_CB_ERROR;
6030 }
6031
6032 if (ctx->print_resources)
6033 resources_show(ctx, tb);
6034
6035 return MNL_CB_OK;
6036 }
6037
6038 static int cmd_resource_show(struct dl *dl)
6039 {
6040 struct nlmsghdr *nlh;
6041 struct dpipe_ctx dpipe_ctx = {};
6042 struct resource_ctx resource_ctx = {};
6043 int err;
6044
6045 err = dl_argv_parse(dl, DL_OPT_HANDLE, 0);
6046 if (err)
6047 return err;
6048
6049 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_TABLE_GET,
6050 NLM_F_REQUEST);
6051 dl_opts_put(nlh, dl);
6052
6053 err = dpipe_ctx_init(&dpipe_ctx, dl);
6054 if (err)
6055 return err;
6056
6057 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_table_show_cb,
6058 &dpipe_ctx);
6059 if (err) {
6060 pr_err("error get tables %s\n", strerror(dpipe_ctx.err));
6061 goto out;
6062 }
6063
6064 err = resource_ctx_init(&resource_ctx, dl);
6065 if (err)
6066 goto out;
6067
6068 resource_ctx.print_resources = true;
6069 resource_ctx.tables = dpipe_ctx.tables;
6070 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RESOURCE_DUMP,
6071 NLM_F_REQUEST | NLM_F_ACK);
6072 dl_opts_put(nlh, dl);
6073 pr_out_section_start(dl, "resources");
6074 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_resource_dump_cb,
6075 &resource_ctx);
6076 pr_out_section_end(dl);
6077 resource_ctx_fini(&resource_ctx);
6078 out:
6079 dpipe_ctx_fini(&dpipe_ctx);
6080 return err;
6081 }
6082
6083 static void cmd_resource_help(void)
6084 {
6085 pr_err("Usage: devlink resource show DEV\n"
6086 " devlink resource set DEV path PATH size SIZE\n");
6087 }
6088
6089 static struct resource *
6090 resource_find_by_name(struct list_head *list, char *name)
6091 {
6092 struct resource *resource;
6093
6094 list_for_each_entry(resource, list, list) {
6095 if (!strcmp(resource->name, name))
6096 return resource;
6097 }
6098 return NULL;
6099 }
6100
6101 static int
6102 resource_path_parse(struct resource_ctx *ctx, const char *resource_path,
6103 uint32_t *p_resource_id, bool *p_resource_valid)
6104 {
6105 struct resource *resource;
6106 uint32_t resource_id = 0;
6107 char *resource_path_dup;
6108 struct list_head *list;
6109 const char del[] = "/";
6110 char *resource_name;
6111
6112 resource_path_dup = strdup(resource_path);
6113 list = &ctx->resources->resource_list;
6114 resource_name = strtok(resource_path_dup, del);
6115 while (resource_name != NULL) {
6116 resource = resource_find_by_name(list, resource_name);
6117 if (!resource)
6118 goto err_resource_lookup;
6119
6120 list = &resource->resource_list;
6121 resource_name = strtok(NULL, del);
6122 resource_id = resource->id;
6123 }
6124 free(resource_path_dup);
6125 *p_resource_valid = true;
6126 *p_resource_id = resource_id;
6127 return 0;
6128
6129 err_resource_lookup:
6130 free(resource_path_dup);
6131 return -EINVAL;
6132 }
6133
6134 static int cmd_resource_set(struct dl *dl)
6135 {
6136 struct nlmsghdr *nlh;
6137 struct resource_ctx ctx = {};
6138 int err;
6139
6140 err = resource_ctx_init(&ctx, dl);
6141 if (err)
6142 return err;
6143
6144 ctx.print_resources = false;
6145 err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_RESOURCE_PATH |
6146 DL_OPT_RESOURCE_SIZE, 0);
6147 if (err)
6148 goto out;
6149
6150 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RESOURCE_DUMP,
6151 NLM_F_REQUEST);
6152 dl_opts_put(nlh, dl);
6153 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_resource_dump_cb, &ctx);
6154 if (err) {
6155 pr_err("error getting resources %s\n", strerror(ctx.err));
6156 goto out;
6157 }
6158
6159 err = resource_path_parse(&ctx, dl->opts.resource_path,
6160 &dl->opts.resource_id,
6161 &dl->opts.resource_id_valid);
6162 if (err) {
6163 pr_err("error parsing resource path %s\n", strerror(-err));
6164 goto out;
6165 }
6166
6167 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RESOURCE_SET,
6168 NLM_F_REQUEST | NLM_F_ACK);
6169
6170 dl_opts_put(nlh, dl);
6171 err = _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
6172 out:
6173 resource_ctx_fini(&ctx);
6174 return err;
6175 }
6176
6177 static int cmd_resource(struct dl *dl)
6178 {
6179 if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
6180 cmd_resource_help();
6181 return 0;
6182 } else if (dl_argv_match(dl, "show")) {
6183 dl_arg_inc(dl);
6184 return cmd_resource_show(dl);
6185 } else if (dl_argv_match(dl, "set")) {
6186 dl_arg_inc(dl);
6187 return cmd_resource_set(dl);
6188 }
6189 pr_err("Command \"%s\" not found\n", dl_argv(dl));
6190 return -ENOENT;
6191 }
6192
6193 static void pr_out_region_handle_start(struct dl *dl, struct nlattr **tb)
6194 {
6195 const char *bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]);
6196 const char *dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]);
6197 const char *region_name = mnl_attr_get_str(tb[DEVLINK_ATTR_REGION_NAME]);
6198 char buf[256];
6199
6200 sprintf(buf, "%s/%s/%s", bus_name, dev_name, region_name);
6201 if (dl->json_output)
6202 open_json_object(buf);
6203 else
6204 pr_out("%s:", buf);
6205 }
6206
6207 static void pr_out_region_handle_end(struct dl *dl)
6208 {
6209 if (dl->json_output)
6210 close_json_object();
6211 else
6212 pr_out("\n");
6213 }
6214
6215 static void pr_out_region_snapshots_start(struct dl *dl, bool array)
6216 {
6217 __pr_out_indent_newline(dl);
6218 if (dl->json_output)
6219 open_json_array(PRINT_JSON, "snapshot");
6220 else
6221 pr_out("snapshot %s", array ? "[" : "");
6222 }
6223
6224 static void pr_out_region_snapshots_end(struct dl *dl, bool array)
6225 {
6226 if (dl->json_output)
6227 close_json_array(PRINT_JSON, NULL);
6228 else if (array)
6229 pr_out("]");
6230 }
6231
6232 static void pr_out_region_snapshots_id(struct dl *dl, struct nlattr **tb, int index)
6233 {
6234 uint32_t snapshot_id;
6235
6236 if (!tb[DEVLINK_ATTR_REGION_SNAPSHOT_ID])
6237 return;
6238
6239 snapshot_id = mnl_attr_get_u32(tb[DEVLINK_ATTR_REGION_SNAPSHOT_ID]);
6240
6241 if (dl->json_output)
6242 print_uint(PRINT_JSON, NULL, NULL, snapshot_id);
6243 else
6244 pr_out("%s%u", index ? " " : "", snapshot_id);
6245 }
6246
6247 static void pr_out_snapshots(struct dl *dl, struct nlattr **tb)
6248 {
6249 struct nlattr *tb_snapshot[DEVLINK_ATTR_MAX + 1] = {};
6250 struct nlattr *nla_sanpshot;
6251 int err, index = 0;
6252
6253 pr_out_region_snapshots_start(dl, true);
6254 mnl_attr_for_each_nested(nla_sanpshot, tb[DEVLINK_ATTR_REGION_SNAPSHOTS]) {
6255 err = mnl_attr_parse_nested(nla_sanpshot, attr_cb, tb_snapshot);
6256 if (err != MNL_CB_OK)
6257 return;
6258 pr_out_region_snapshots_id(dl, tb_snapshot, index++);
6259 }
6260 pr_out_region_snapshots_end(dl, true);
6261 }
6262
6263 static void pr_out_snapshot(struct dl *dl, struct nlattr **tb)
6264 {
6265 pr_out_region_snapshots_start(dl, false);
6266 pr_out_region_snapshots_id(dl, tb, 0);
6267 pr_out_region_snapshots_end(dl, false);
6268 }
6269
6270 static void pr_out_region(struct dl *dl, struct nlattr **tb)
6271 {
6272 pr_out_region_handle_start(dl, tb);
6273
6274 if (tb[DEVLINK_ATTR_REGION_SIZE])
6275 pr_out_u64(dl, "size",
6276 mnl_attr_get_u64(tb[DEVLINK_ATTR_REGION_SIZE]));
6277
6278 if (tb[DEVLINK_ATTR_REGION_SNAPSHOTS])
6279 pr_out_snapshots(dl, tb);
6280
6281 if (tb[DEVLINK_ATTR_REGION_SNAPSHOT_ID])
6282 pr_out_snapshot(dl, tb);
6283
6284 pr_out_region_handle_end(dl);
6285 }
6286
6287 static int cmd_region_show_cb(const struct nlmsghdr *nlh, void *data)
6288 {
6289 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
6290 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
6291 struct dl *dl = data;
6292
6293 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
6294 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
6295 !tb[DEVLINK_ATTR_REGION_NAME] || !tb[DEVLINK_ATTR_REGION_SIZE])
6296 return MNL_CB_ERROR;
6297
6298 pr_out_region(dl, tb);
6299
6300 return MNL_CB_OK;
6301 }
6302
6303 static int cmd_region_show(struct dl *dl)
6304 {
6305 struct nlmsghdr *nlh;
6306 uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
6307 int err;
6308
6309 if (dl_argc(dl) == 0)
6310 flags |= NLM_F_DUMP;
6311
6312 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_REGION_GET, flags);
6313
6314 if (dl_argc(dl) > 0) {
6315 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE_REGION, 0);
6316 if (err)
6317 return err;
6318 }
6319
6320 pr_out_section_start(dl, "regions");
6321 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_region_show_cb, dl);
6322 pr_out_section_end(dl);
6323 return err;
6324 }
6325
6326 static int cmd_region_snapshot_del(struct dl *dl)
6327 {
6328 struct nlmsghdr *nlh;
6329 int err;
6330
6331 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_REGION_DEL,
6332 NLM_F_REQUEST | NLM_F_ACK);
6333
6334 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE_REGION |
6335 DL_OPT_REGION_SNAPSHOT_ID, 0);
6336 if (err)
6337 return err;
6338
6339 return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
6340 }
6341
6342 static int cmd_region_read_cb(const struct nlmsghdr *nlh, void *data)
6343 {
6344 struct nlattr *nla_entry, *nla_chunk_data, *nla_chunk_addr;
6345 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
6346 struct nlattr *tb_field[DEVLINK_ATTR_MAX + 1] = {};
6347 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
6348 struct dl *dl = data;
6349 int err;
6350
6351 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
6352 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
6353 !tb[DEVLINK_ATTR_REGION_CHUNKS])
6354 return MNL_CB_ERROR;
6355
6356 mnl_attr_for_each_nested(nla_entry, tb[DEVLINK_ATTR_REGION_CHUNKS]) {
6357 err = mnl_attr_parse_nested(nla_entry, attr_cb, tb_field);
6358 if (err != MNL_CB_OK)
6359 return MNL_CB_ERROR;
6360
6361 nla_chunk_data = tb_field[DEVLINK_ATTR_REGION_CHUNK_DATA];
6362 if (!nla_chunk_data)
6363 continue;
6364
6365 nla_chunk_addr = tb_field[DEVLINK_ATTR_REGION_CHUNK_ADDR];
6366 if (!nla_chunk_addr)
6367 continue;
6368
6369 pr_out_region_chunk(dl, mnl_attr_get_payload(nla_chunk_data),
6370 mnl_attr_get_payload_len(nla_chunk_data),
6371 mnl_attr_get_u64(nla_chunk_addr));
6372 }
6373 return MNL_CB_OK;
6374 }
6375
6376 static int cmd_region_dump(struct dl *dl)
6377 {
6378 struct nlmsghdr *nlh;
6379 int err;
6380
6381 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_REGION_READ,
6382 NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
6383
6384 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE_REGION |
6385 DL_OPT_REGION_SNAPSHOT_ID, 0);
6386 if (err)
6387 return err;
6388
6389 pr_out_section_start(dl, "dump");
6390 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_region_read_cb, dl);
6391 pr_out_section_end(dl);
6392 if (!dl->json_output)
6393 pr_out("\n");
6394 return err;
6395 }
6396
6397 static int cmd_region_read(struct dl *dl)
6398 {
6399 struct nlmsghdr *nlh;
6400 int err;
6401
6402 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_REGION_READ,
6403 NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
6404
6405 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE_REGION |
6406 DL_OPT_REGION_ADDRESS | DL_OPT_REGION_LENGTH |
6407 DL_OPT_REGION_SNAPSHOT_ID, 0);
6408 if (err)
6409 return err;
6410
6411 pr_out_section_start(dl, "read");
6412 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_region_read_cb, dl);
6413 pr_out_section_end(dl);
6414 if (!dl->json_output)
6415 pr_out("\n");
6416 return err;
6417 }
6418
6419 static void cmd_region_help(void)
6420 {
6421 pr_err("Usage: devlink region show [ DEV/REGION ]\n");
6422 pr_err(" devlink region del DEV/REGION snapshot SNAPSHOT_ID\n");
6423 pr_err(" devlink region dump DEV/REGION [ snapshot SNAPSHOT_ID ]\n");
6424 pr_err(" devlink region read DEV/REGION [ snapshot SNAPSHOT_ID ] address ADDRESS length LENGTH\n");
6425 }
6426
6427 static int cmd_region(struct dl *dl)
6428 {
6429 if (dl_no_arg(dl)) {
6430 return cmd_region_show(dl);
6431 } else if (dl_argv_match(dl, "help")) {
6432 cmd_region_help();
6433 return 0;
6434 } else if (dl_argv_match(dl, "show")) {
6435 dl_arg_inc(dl);
6436 return cmd_region_show(dl);
6437 } else if (dl_argv_match(dl, "del")) {
6438 dl_arg_inc(dl);
6439 return cmd_region_snapshot_del(dl);
6440 } else if (dl_argv_match(dl, "dump")) {
6441 dl_arg_inc(dl);
6442 return cmd_region_dump(dl);
6443 } else if (dl_argv_match(dl, "read")) {
6444 dl_arg_inc(dl);
6445 return cmd_region_read(dl);
6446 }
6447 pr_err("Command \"%s\" not found\n", dl_argv(dl));
6448 return -ENOENT;
6449 }
6450
6451 static int cmd_health_set_params(struct dl *dl)
6452 {
6453 struct nlmsghdr *nlh;
6454 int err;
6455
6456 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_HEALTH_REPORTER_SET,
6457 NLM_F_REQUEST | NLM_F_ACK);
6458 err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_HEALTH_REPORTER_NAME,
6459 DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD |
6460 DL_OPT_HEALTH_REPORTER_AUTO_RECOVER);
6461 if (err)
6462 return err;
6463
6464 dl_opts_put(nlh, dl);
6465 return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
6466 }
6467
6468 static int cmd_health_dump_clear(struct dl *dl)
6469 {
6470 struct nlmsghdr *nlh;
6471 int err;
6472
6473 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_HEALTH_REPORTER_DUMP_CLEAR,
6474 NLM_F_REQUEST | NLM_F_ACK);
6475
6476 err = dl_argv_parse_put(nlh, dl,
6477 DL_OPT_HANDLE | DL_OPT_HEALTH_REPORTER_NAME, 0);
6478 if (err)
6479 return err;
6480
6481 dl_opts_put(nlh, dl);
6482 return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
6483 }
6484
6485 static int fmsg_value_show(struct dl *dl, int type, struct nlattr *nl_data)
6486 {
6487 uint8_t *data;
6488 uint32_t len;
6489
6490 check_indent_newline(dl);
6491 switch (type) {
6492 case MNL_TYPE_FLAG:
6493 print_bool(PRINT_ANY, NULL, "%s", mnl_attr_get_u8(nl_data));
6494 break;
6495 case MNL_TYPE_U8:
6496 print_uint(PRINT_ANY, NULL, "%u", mnl_attr_get_u8(nl_data));
6497 break;
6498 case MNL_TYPE_U16:
6499 print_uint(PRINT_ANY, NULL, "%u", mnl_attr_get_u16(nl_data));
6500 break;
6501 case MNL_TYPE_U32:
6502 print_uint(PRINT_ANY, NULL, "%u", mnl_attr_get_u32(nl_data));
6503 break;
6504 case MNL_TYPE_U64:
6505 print_u64(PRINT_ANY, NULL, "%"PRIu64, mnl_attr_get_u64(nl_data));
6506 break;
6507 case MNL_TYPE_NUL_STRING:
6508 print_string(PRINT_ANY, NULL, "%s", mnl_attr_get_str(nl_data));
6509 break;
6510 case MNL_TYPE_BINARY:
6511 len = mnl_attr_get_payload_len(nl_data);
6512 data = mnl_attr_get_payload(nl_data);
6513 pr_out_binary_value(dl, data, len);
6514 break;
6515 default:
6516 return -EINVAL;
6517 }
6518 return MNL_CB_OK;
6519 }
6520
6521 static void pr_out_fmsg_name(struct dl *dl, char **name)
6522 {
6523 if (!*name)
6524 return;
6525
6526 pr_out_name(dl, *name);
6527 free(*name);
6528 *name = NULL;
6529 }
6530
6531 struct nest_entry {
6532 int attr_type;
6533 struct list_head list;
6534 };
6535
6536 struct fmsg_cb_data {
6537 char *name;
6538 struct dl *dl;
6539 uint8_t value_type;
6540 struct list_head entry_list;
6541 };
6542
6543 static int cmd_fmsg_nest_queue(struct fmsg_cb_data *fmsg_data,
6544 uint8_t *attr_value, bool insert)
6545 {
6546 struct nest_entry *entry;
6547
6548 if (insert) {
6549 entry = malloc(sizeof(struct nest_entry));
6550 if (!entry)
6551 return -ENOMEM;
6552
6553 entry->attr_type = *attr_value;
6554 list_add(&entry->list, &fmsg_data->entry_list);
6555 } else {
6556 if (list_empty(&fmsg_data->entry_list))
6557 return MNL_CB_ERROR;
6558 entry = list_first_entry(&fmsg_data->entry_list,
6559 struct nest_entry, list);
6560 *attr_value = entry->attr_type;
6561 list_del(&entry->list);
6562 free(entry);
6563 }
6564 return MNL_CB_OK;
6565 }
6566
6567 static void pr_out_fmsg_group_start(struct dl *dl, char **name)
6568 {
6569 __pr_out_newline();
6570 pr_out_fmsg_name(dl, name);
6571 __pr_out_newline();
6572 __pr_out_indent_inc();
6573 }
6574
6575 static void pr_out_fmsg_group_end(struct dl *dl)
6576 {
6577 __pr_out_newline();
6578 __pr_out_indent_dec();
6579 }
6580
6581 static void pr_out_fmsg_start_object(struct dl *dl, char **name)
6582 {
6583 if (dl->json_output) {
6584 pr_out_fmsg_name(dl, name);
6585 open_json_object(NULL);
6586 } else {
6587 pr_out_fmsg_group_start(dl, name);
6588 }
6589 }
6590
6591 static void pr_out_fmsg_end_object(struct dl *dl)
6592 {
6593 if (dl->json_output)
6594 close_json_object();
6595 else
6596 pr_out_fmsg_group_end(dl);
6597 }
6598
6599 static void pr_out_fmsg_start_array(struct dl *dl, char **name)
6600 {
6601 if (dl->json_output) {
6602 pr_out_fmsg_name(dl, name);
6603 open_json_array(PRINT_JSON, NULL);
6604 } else {
6605 pr_out_fmsg_group_start(dl, name);
6606 }
6607 }
6608
6609 static void pr_out_fmsg_end_array(struct dl *dl)
6610 {
6611 if (dl->json_output)
6612 close_json_array(PRINT_JSON, NULL);
6613 else
6614 pr_out_fmsg_group_end(dl);
6615 }
6616
6617 static int cmd_fmsg_nest(struct fmsg_cb_data *fmsg_data, uint8_t nest_value,
6618 bool start)
6619 {
6620 struct dl *dl = fmsg_data->dl;
6621 uint8_t value = nest_value;
6622 int err;
6623
6624 err = cmd_fmsg_nest_queue(fmsg_data, &value, start);
6625 if (err != MNL_CB_OK)
6626 return err;
6627
6628 switch (value) {
6629 case DEVLINK_ATTR_FMSG_OBJ_NEST_START:
6630 if (start)
6631 pr_out_fmsg_start_object(dl, &fmsg_data->name);
6632 else
6633 pr_out_fmsg_end_object(dl);
6634 break;
6635 case DEVLINK_ATTR_FMSG_PAIR_NEST_START:
6636 break;
6637 case DEVLINK_ATTR_FMSG_ARR_NEST_START:
6638 if (start)
6639 pr_out_fmsg_start_array(dl, &fmsg_data->name);
6640 else
6641 pr_out_fmsg_end_array(dl);
6642 break;
6643 default:
6644 return -EINVAL;
6645 }
6646 return MNL_CB_OK;
6647 }
6648
6649 static int cmd_fmsg_object_cb(const struct nlmsghdr *nlh, void *data)
6650 {
6651 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
6652 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
6653 struct fmsg_cb_data *fmsg_data = data;
6654 struct dl *dl = fmsg_data->dl;
6655 struct nlattr *nla_object;
6656 int attr_type;
6657 int err;
6658
6659 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
6660 if (!tb[DEVLINK_ATTR_FMSG])
6661 return MNL_CB_ERROR;
6662
6663 mnl_attr_for_each_nested(nla_object, tb[DEVLINK_ATTR_FMSG]) {
6664 attr_type = mnl_attr_get_type(nla_object);
6665 switch (attr_type) {
6666 case DEVLINK_ATTR_FMSG_OBJ_NEST_START:
6667 case DEVLINK_ATTR_FMSG_PAIR_NEST_START:
6668 case DEVLINK_ATTR_FMSG_ARR_NEST_START:
6669 err = cmd_fmsg_nest(fmsg_data, attr_type, true);
6670 if (err != MNL_CB_OK)
6671 return err;
6672 break;
6673 case DEVLINK_ATTR_FMSG_NEST_END:
6674 err = cmd_fmsg_nest(fmsg_data, attr_type, false);
6675 if (err != MNL_CB_OK)
6676 return err;
6677 break;
6678 case DEVLINK_ATTR_FMSG_OBJ_NAME:
6679 free(fmsg_data->name);
6680 fmsg_data->name = strdup(mnl_attr_get_str(nla_object));
6681 if (!fmsg_data->name)
6682 return -ENOMEM;
6683 break;
6684 case DEVLINK_ATTR_FMSG_OBJ_VALUE_TYPE:
6685 fmsg_data->value_type = mnl_attr_get_u8(nla_object);
6686 break;
6687 case DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA:
6688 pr_out_fmsg_name(dl, &fmsg_data->name);
6689 err = fmsg_value_show(dl, fmsg_data->value_type,
6690 nla_object);
6691 if (err != MNL_CB_OK)
6692 return err;
6693 break;
6694 default:
6695 return -EINVAL;
6696 }
6697 }
6698 return MNL_CB_OK;
6699 }
6700
6701 static void cmd_fmsg_init(struct dl *dl, struct fmsg_cb_data *data)
6702 {
6703 /* FMSG is dynamic: opening of an object or array causes a
6704 * newline. JSON starts with an { or [, but plain text should
6705 * not start with a new line. Ensure this by setting
6706 * g_new_line_count to 1: avoiding newline before the first
6707 * print.
6708 */
6709 g_new_line_count = 1;
6710 data->name = NULL;
6711 data->dl = dl;
6712 INIT_LIST_HEAD(&data->entry_list);
6713 }
6714
6715 static int cmd_health_object_common(struct dl *dl, uint8_t cmd, uint16_t flags)
6716 {
6717 struct fmsg_cb_data data;
6718 struct nlmsghdr *nlh;
6719 int err;
6720
6721 nlh = mnlg_msg_prepare(dl->nlg, cmd, flags | NLM_F_REQUEST | NLM_F_ACK);
6722
6723 err = dl_argv_parse_put(nlh, dl,
6724 DL_OPT_HANDLE | DL_OPT_HEALTH_REPORTER_NAME, 0);
6725 if (err)
6726 return err;
6727
6728 cmd_fmsg_init(dl, &data);
6729 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_fmsg_object_cb, &data);
6730 free(data.name);
6731 return err;
6732 }
6733
6734 static int cmd_health_dump_show(struct dl *dl)
6735 {
6736 return cmd_health_object_common(dl,
6737 DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET,
6738 NLM_F_DUMP);
6739 }
6740
6741 static int cmd_health_diagnose(struct dl *dl)
6742 {
6743 return cmd_health_object_common(dl,
6744 DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE,
6745 0);
6746 }
6747
6748 static int cmd_health_recover(struct dl *dl)
6749 {
6750 struct nlmsghdr *nlh;
6751 int err;
6752
6753 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_HEALTH_REPORTER_RECOVER,
6754 NLM_F_REQUEST | NLM_F_ACK);
6755
6756 err = dl_argv_parse_put(nlh, dl,
6757 DL_OPT_HANDLE | DL_OPT_HEALTH_REPORTER_NAME, 0);
6758 if (err)
6759 return err;
6760
6761 dl_opts_put(nlh, dl);
6762 return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
6763 }
6764
6765 enum devlink_health_reporter_state {
6766 DEVLINK_HEALTH_REPORTER_STATE_HEALTHY,
6767 DEVLINK_HEALTH_REPORTER_STATE_ERROR,
6768 };
6769
6770 static const char *health_state_name(uint8_t state)
6771 {
6772 switch (state) {
6773 case DEVLINK_HEALTH_REPORTER_STATE_HEALTHY:
6774 return HEALTH_REPORTER_STATE_HEALTHY_STR;
6775 case DEVLINK_HEALTH_REPORTER_STATE_ERROR:
6776 return HEALTH_REPORTER_STATE_ERROR_STR;
6777 default:
6778 return "<unknown state>";
6779 }
6780 }
6781
6782 static void pr_out_dump_reporter_format_logtime(struct dl *dl, const struct nlattr *attr)
6783 {
6784 char dump_date[HEALTH_REPORTER_TIMESTAMP_FMT_LEN];
6785 char dump_time[HEALTH_REPORTER_TIMESTAMP_FMT_LEN];
6786 uint64_t time_ms = mnl_attr_get_u64(attr);
6787 struct sysinfo s_info;
6788 struct tm *info;
6789 time_t now, sec;
6790 int err;
6791
6792 time(&now);
6793 info = localtime(&now);
6794 err = sysinfo(&s_info);
6795 if (err)
6796 goto out;
6797 /* Subtract uptime in sec from now yields the time of system
6798 * uptime. To this, add time_ms which is the amount of
6799 * milliseconds elapsed between uptime and the dump taken.
6800 */
6801 sec = now - s_info.uptime + time_ms / 1000;
6802 info = localtime(&sec);
6803 out:
6804 strftime(dump_date, HEALTH_REPORTER_TIMESTAMP_FMT_LEN, "%Y-%m-%d", info);
6805 strftime(dump_time, HEALTH_REPORTER_TIMESTAMP_FMT_LEN, "%H:%M:%S", info);
6806 check_indent_newline(dl);
6807 print_string(PRINT_ANY, "last_dump_date", "last_dump_date %s", dump_date);
6808 print_string(PRINT_ANY, "last_dump_time", " last_dump_time %s", dump_time);
6809 }
6810
6811 static void pr_out_dump_report_timestamp(struct dl *dl, const struct nlattr *attr)
6812 {
6813 char dump_date[HEALTH_REPORTER_TIMESTAMP_FMT_LEN];
6814 char dump_time[HEALTH_REPORTER_TIMESTAMP_FMT_LEN];
6815 time_t tv_sec;
6816 struct tm *tm;
6817 uint64_t ts;
6818
6819 ts = mnl_attr_get_u64(attr);
6820 tv_sec = ts / 1000000000;
6821 tm = localtime(&tv_sec);
6822
6823 strftime(dump_date, HEALTH_REPORTER_TIMESTAMP_FMT_LEN, "%Y-%m-%d", tm);
6824 strftime(dump_time, HEALTH_REPORTER_TIMESTAMP_FMT_LEN, "%H:%M:%S", tm);
6825
6826 check_indent_newline(dl);
6827 print_string(PRINT_ANY, "last_dump_date", "last_dump_date %s", dump_date);
6828 print_string(PRINT_ANY, "last_dump_time", " last_dump_time %s", dump_time);
6829 }
6830
6831 static void pr_out_health(struct dl *dl, struct nlattr **tb_health)
6832 {
6833 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
6834 enum devlink_health_reporter_state state;
6835 int err;
6836
6837 err = mnl_attr_parse_nested(tb_health[DEVLINK_ATTR_HEALTH_REPORTER],
6838 attr_cb, tb);
6839 if (err != MNL_CB_OK)
6840 return;
6841
6842 if (!tb[DEVLINK_ATTR_HEALTH_REPORTER_NAME] ||
6843 !tb[DEVLINK_ATTR_HEALTH_REPORTER_ERR_COUNT] ||
6844 !tb[DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT] ||
6845 !tb[DEVLINK_ATTR_HEALTH_REPORTER_STATE])
6846 return;
6847
6848 pr_out_handle_start_arr(dl, tb_health);
6849
6850 check_indent_newline(dl);
6851 print_string(PRINT_ANY, "reporter", "reporter %s",
6852 mnl_attr_get_str(tb[DEVLINK_ATTR_HEALTH_REPORTER_NAME]));
6853 if (!dl->json_output) {
6854 __pr_out_newline();
6855 __pr_out_indent_inc();
6856 }
6857 state = mnl_attr_get_u8(tb[DEVLINK_ATTR_HEALTH_REPORTER_STATE]);
6858 check_indent_newline(dl);
6859 print_string(PRINT_ANY, "state", "state %s", health_state_name(state));
6860 pr_out_u64(dl, "error",
6861 mnl_attr_get_u64(tb[DEVLINK_ATTR_HEALTH_REPORTER_ERR_COUNT]));
6862 pr_out_u64(dl, "recover",
6863 mnl_attr_get_u64(tb[DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT]));
6864 if (tb[DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS_NS])
6865 pr_out_dump_report_timestamp(dl, tb[DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS_NS]);
6866 else if (tb[DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS])
6867 pr_out_dump_reporter_format_logtime(dl, tb[DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS]);
6868 if (tb[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD])
6869 pr_out_u64(dl, "grace_period",
6870 mnl_attr_get_u64(tb[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD]));
6871 if (tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER])
6872 print_bool(PRINT_ANY, "auto_recover", " auto_recover %s",
6873 mnl_attr_get_u8(tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]));
6874
6875 __pr_out_indent_dec();
6876 pr_out_handle_end(dl);
6877 }
6878
6879 static int cmd_health_show_cb(const struct nlmsghdr *nlh, void *data)
6880 {
6881 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
6882 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
6883 struct dl *dl = data;
6884
6885 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
6886 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
6887 !tb[DEVLINK_ATTR_HEALTH_REPORTER])
6888 return MNL_CB_ERROR;
6889
6890 pr_out_health(dl, tb);
6891
6892 return MNL_CB_OK;
6893 }
6894
6895 static int cmd_health_show(struct dl *dl)
6896 {
6897 struct nlmsghdr *nlh;
6898 uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
6899 int err;
6900
6901 if (dl_argc(dl) == 0)
6902 flags |= NLM_F_DUMP;
6903 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_HEALTH_REPORTER_GET,
6904 flags);
6905
6906 if (dl_argc(dl) > 0) {
6907 err = dl_argv_parse_put(nlh, dl,
6908 DL_OPT_HANDLE |
6909 DL_OPT_HEALTH_REPORTER_NAME, 0);
6910 if (err)
6911 return err;
6912 }
6913 pr_out_section_start(dl, "health");
6914
6915 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_health_show_cb, dl);
6916 pr_out_section_end(dl);
6917 return err;
6918 }
6919
6920 static void cmd_health_help(void)
6921 {
6922 pr_err("Usage: devlink health show [ dev DEV reporter REPORTER_NAME ]\n");
6923 pr_err(" devlink health recover DEV reporter REPORTER_NAME\n");
6924 pr_err(" devlink health diagnose DEV reporter REPORTER_NAME\n");
6925 pr_err(" devlink health dump show DEV reporter REPORTER_NAME\n");
6926 pr_err(" devlink health dump clear DEV reporter REPORTER_NAME\n");
6927 pr_err(" devlink health set DEV reporter REPORTER_NAME\n");
6928 pr_err(" [ grace_period MSEC ]\n");
6929 pr_err(" [ auto_recover { true | false } ]\n");
6930 }
6931
6932 static int cmd_health(struct dl *dl)
6933 {
6934 if (dl_argv_match(dl, "help")) {
6935 cmd_health_help();
6936 return 0;
6937 } else if (dl_argv_match(dl, "show") ||
6938 dl_argv_match(dl, "list") || dl_no_arg(dl)) {
6939 dl_arg_inc(dl);
6940 return cmd_health_show(dl);
6941 } else if (dl_argv_match(dl, "recover")) {
6942 dl_arg_inc(dl);
6943 return cmd_health_recover(dl);
6944 } else if (dl_argv_match(dl, "diagnose")) {
6945 dl_arg_inc(dl);
6946 return cmd_health_diagnose(dl);
6947 } else if (dl_argv_match(dl, "dump")) {
6948 dl_arg_inc(dl);
6949 if (dl_argv_match(dl, "show")) {
6950 dl_arg_inc(dl);
6951 return cmd_health_dump_show(dl);
6952 } else if (dl_argv_match(dl, "clear")) {
6953 dl_arg_inc(dl);
6954 return cmd_health_dump_clear(dl);
6955 }
6956 } else if (dl_argv_match(dl, "set")) {
6957 dl_arg_inc(dl);
6958 return cmd_health_set_params(dl);
6959 }
6960 pr_err("Command \"%s\" not found\n", dl_argv(dl));
6961 return -ENOENT;
6962 }
6963
6964 static const char *trap_type_name(uint8_t type)
6965 {
6966 switch (type) {
6967 case DEVLINK_TRAP_TYPE_DROP:
6968 return "drop";
6969 case DEVLINK_TRAP_TYPE_EXCEPTION:
6970 return "exception";
6971 default:
6972 return "<unknown type>";
6973 }
6974 }
6975
6976 static const char *trap_action_name(uint8_t action)
6977 {
6978 switch (action) {
6979 case DEVLINK_TRAP_ACTION_DROP:
6980 return "drop";
6981 case DEVLINK_TRAP_ACTION_TRAP:
6982 return "trap";
6983 default:
6984 return "<unknown action>";
6985 }
6986 }
6987
6988 static const char *trap_metadata_name(const struct nlattr *attr)
6989 {
6990 switch (attr->nla_type) {
6991 case DEVLINK_ATTR_TRAP_METADATA_TYPE_IN_PORT:
6992 return "input_port";
6993 case DEVLINK_ATTR_TRAP_METADATA_TYPE_FA_COOKIE:
6994 return "flow_action_cookie";
6995 default:
6996 return "<unknown metadata type>";
6997 }
6998 }
6999 static void pr_out_trap_metadata(struct dl *dl, struct nlattr *attr)
7000 {
7001 struct nlattr *attr_metadata;
7002
7003 pr_out_array_start(dl, "metadata");
7004 mnl_attr_for_each_nested(attr_metadata, attr) {
7005 check_indent_newline(dl);
7006 print_string(PRINT_ANY, NULL, "%s",
7007 trap_metadata_name(attr_metadata));
7008 }
7009 pr_out_array_end(dl);
7010 }
7011
7012 static void pr_out_trap(struct dl *dl, struct nlattr **tb, bool array)
7013 {
7014 uint8_t action = mnl_attr_get_u8(tb[DEVLINK_ATTR_TRAP_ACTION]);
7015 uint8_t type = mnl_attr_get_u8(tb[DEVLINK_ATTR_TRAP_TYPE]);
7016
7017 if (array)
7018 pr_out_handle_start_arr(dl, tb);
7019 else
7020 __pr_out_handle_start(dl, tb, true, false);
7021
7022 check_indent_newline(dl);
7023 print_string(PRINT_ANY, "name", "name %s",
7024 mnl_attr_get_str(tb[DEVLINK_ATTR_TRAP_NAME]));
7025 print_string(PRINT_ANY, "type", " type %s", trap_type_name(type));
7026 print_bool(PRINT_ANY, "generic", " generic %s", !!tb[DEVLINK_ATTR_TRAP_GENERIC]);
7027 print_string(PRINT_ANY, "action", " action %s", trap_action_name(action));
7028 print_string(PRINT_ANY, "group", " group %s",
7029 mnl_attr_get_str(tb[DEVLINK_ATTR_TRAP_GROUP_NAME]));
7030 if (dl->verbose)
7031 pr_out_trap_metadata(dl, tb[DEVLINK_ATTR_TRAP_METADATA]);
7032 pr_out_stats(dl, tb[DEVLINK_ATTR_STATS]);
7033 pr_out_handle_end(dl);
7034 }
7035
7036 static int cmd_trap_show_cb(const struct nlmsghdr *nlh, void *data)
7037 {
7038 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
7039 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
7040 struct dl *dl = data;
7041
7042 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
7043 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
7044 !tb[DEVLINK_ATTR_TRAP_NAME] || !tb[DEVLINK_ATTR_TRAP_TYPE] ||
7045 !tb[DEVLINK_ATTR_TRAP_ACTION] ||
7046 !tb[DEVLINK_ATTR_TRAP_GROUP_NAME] ||
7047 !tb[DEVLINK_ATTR_TRAP_METADATA] || !tb[DEVLINK_ATTR_STATS])
7048 return MNL_CB_ERROR;
7049
7050 pr_out_trap(dl, tb, true);
7051
7052 return MNL_CB_OK;
7053 }
7054
7055 static void cmd_trap_help(void)
7056 {
7057 pr_err("Usage: devlink trap set DEV trap TRAP [ action { trap | drop } ]\n");
7058 pr_err(" devlink trap show [ DEV trap TRAP ]\n");
7059 pr_err(" devlink trap group set DEV group GROUP [ action { trap | drop } ]\n");
7060 pr_err(" [ policer POLICER ] [ nopolicer ]\n");
7061 pr_err(" devlink trap group show [ DEV group GROUP ]\n");
7062 pr_err(" devlink trap policer set DEV policer POLICER [ rate RATE ] [ burst BURST ]\n");
7063 pr_err(" devlink trap policer show DEV policer POLICER\n");
7064 }
7065
7066 static int cmd_trap_show(struct dl *dl)
7067 {
7068 uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
7069 struct nlmsghdr *nlh;
7070 int err;
7071
7072 if (dl_argc(dl) == 0)
7073 flags |= NLM_F_DUMP;
7074
7075 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_GET, flags);
7076
7077 if (dl_argc(dl) > 0) {
7078 err = dl_argv_parse_put(nlh, dl,
7079 DL_OPT_HANDLE | DL_OPT_TRAP_NAME, 0);
7080 if (err)
7081 return err;
7082 }
7083
7084 pr_out_section_start(dl, "trap");
7085 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_trap_show_cb, dl);
7086 pr_out_section_end(dl);
7087
7088 return err;
7089 }
7090
7091 static int cmd_trap_set(struct dl *dl)
7092 {
7093 struct nlmsghdr *nlh;
7094 int err;
7095
7096 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_SET,
7097 NLM_F_REQUEST | NLM_F_ACK);
7098
7099 err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_TRAP_NAME,
7100 DL_OPT_TRAP_ACTION);
7101 if (err)
7102 return err;
7103
7104 return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
7105 }
7106
7107 static void pr_out_trap_group(struct dl *dl, struct nlattr **tb, bool array)
7108 {
7109 if (array)
7110 pr_out_handle_start_arr(dl, tb);
7111 else
7112 __pr_out_handle_start(dl, tb, true, false);
7113
7114 check_indent_newline(dl);
7115 print_string(PRINT_ANY, "name", "name %s",
7116 mnl_attr_get_str(tb[DEVLINK_ATTR_TRAP_GROUP_NAME]));
7117 print_bool(PRINT_ANY, "generic", " generic %s", !!tb[DEVLINK_ATTR_TRAP_GENERIC]);
7118 if (tb[DEVLINK_ATTR_TRAP_POLICER_ID])
7119 print_uint(PRINT_ANY, "policer", " policer %u",
7120 mnl_attr_get_u32(tb[DEVLINK_ATTR_TRAP_POLICER_ID]));
7121 pr_out_stats(dl, tb[DEVLINK_ATTR_STATS]);
7122 pr_out_handle_end(dl);
7123 }
7124
7125 static int cmd_trap_group_show_cb(const struct nlmsghdr *nlh, void *data)
7126 {
7127 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
7128 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
7129 struct dl *dl = data;
7130
7131 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
7132 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
7133 !tb[DEVLINK_ATTR_TRAP_GROUP_NAME] || !tb[DEVLINK_ATTR_STATS])
7134 return MNL_CB_ERROR;
7135
7136 pr_out_trap_group(dl, tb, true);
7137
7138 return MNL_CB_OK;
7139 }
7140
7141 static int cmd_trap_group_show(struct dl *dl)
7142 {
7143 uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
7144 struct nlmsghdr *nlh;
7145 int err;
7146
7147 if (dl_argc(dl) == 0)
7148 flags |= NLM_F_DUMP;
7149
7150 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_GROUP_GET, flags);
7151
7152 if (dl_argc(dl) > 0) {
7153 err = dl_argv_parse_put(nlh, dl,
7154 DL_OPT_HANDLE | DL_OPT_TRAP_GROUP_NAME,
7155 0);
7156 if (err)
7157 return err;
7158 }
7159
7160 pr_out_section_start(dl, "trap_group");
7161 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_trap_group_show_cb, dl);
7162 pr_out_section_end(dl);
7163
7164 return err;
7165 }
7166
7167 static int cmd_trap_group_set(struct dl *dl)
7168 {
7169 struct nlmsghdr *nlh;
7170 int err;
7171
7172 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_GROUP_SET,
7173 NLM_F_REQUEST | NLM_F_ACK);
7174
7175 err = dl_argv_parse_put(nlh, dl,
7176 DL_OPT_HANDLE | DL_OPT_TRAP_GROUP_NAME,
7177 DL_OPT_TRAP_ACTION | DL_OPT_TRAP_POLICER_ID);
7178 if (err)
7179 return err;
7180
7181 return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
7182 }
7183
7184 static int cmd_trap_group(struct dl *dl)
7185 {
7186 if (dl_argv_match(dl, "help")) {
7187 cmd_trap_help();
7188 return 0;
7189 } else if (dl_argv_match(dl, "show") ||
7190 dl_argv_match(dl, "list") || dl_no_arg(dl)) {
7191 dl_arg_inc(dl);
7192 return cmd_trap_group_show(dl);
7193 } else if (dl_argv_match(dl, "set")) {
7194 dl_arg_inc(dl);
7195 return cmd_trap_group_set(dl);
7196 }
7197 pr_err("Command \"%s\" not found\n", dl_argv(dl));
7198 return -ENOENT;
7199 }
7200
7201 static void pr_out_trap_policer(struct dl *dl, struct nlattr **tb, bool array)
7202 {
7203 if (array)
7204 pr_out_handle_start_arr(dl, tb);
7205 else
7206 __pr_out_handle_start(dl, tb, true, false);
7207
7208 check_indent_newline(dl);
7209 print_uint(PRINT_ANY, "policer", "policer %u",
7210 mnl_attr_get_u32(tb[DEVLINK_ATTR_TRAP_POLICER_ID]));
7211 print_u64(PRINT_ANY, "rate", " rate %llu",
7212 mnl_attr_get_u64(tb[DEVLINK_ATTR_TRAP_POLICER_RATE]));
7213 print_u64(PRINT_ANY, "burst", " burst %llu",
7214 mnl_attr_get_u64(tb[DEVLINK_ATTR_TRAP_POLICER_BURST]));
7215 if (tb[DEVLINK_ATTR_STATS])
7216 pr_out_stats(dl, tb[DEVLINK_ATTR_STATS]);
7217 pr_out_handle_end(dl);
7218 }
7219
7220 static int cmd_trap_policer_show_cb(const struct nlmsghdr *nlh, void *data)
7221 {
7222 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
7223 struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
7224 struct dl *dl = data;
7225
7226 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
7227 if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
7228 !tb[DEVLINK_ATTR_TRAP_POLICER_ID] ||
7229 !tb[DEVLINK_ATTR_TRAP_POLICER_RATE] ||
7230 !tb[DEVLINK_ATTR_TRAP_POLICER_BURST])
7231 return MNL_CB_ERROR;
7232
7233 pr_out_trap_policer(dl, tb, true);
7234
7235 return MNL_CB_OK;
7236 }
7237
7238 static int cmd_trap_policer_show(struct dl *dl)
7239 {
7240 uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
7241 struct nlmsghdr *nlh;
7242 int err;
7243
7244 if (dl_argc(dl) == 0)
7245 flags |= NLM_F_DUMP;
7246
7247 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_POLICER_GET, flags);
7248
7249 if (dl_argc(dl) > 0) {
7250 err = dl_argv_parse_put(nlh, dl,
7251 DL_OPT_HANDLE | DL_OPT_TRAP_POLICER_ID,
7252 0);
7253 if (err)
7254 return err;
7255 }
7256
7257 pr_out_section_start(dl, "trap_policer");
7258 err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_trap_policer_show_cb, dl);
7259 pr_out_section_end(dl);
7260
7261 return err;
7262 }
7263
7264 static int cmd_trap_policer_set(struct dl *dl)
7265 {
7266 struct nlmsghdr *nlh;
7267 int err;
7268
7269 nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_POLICER_SET,
7270 NLM_F_REQUEST | NLM_F_ACK);
7271
7272 err = dl_argv_parse_put(nlh, dl,
7273 DL_OPT_HANDLE | DL_OPT_TRAP_POLICER_ID,
7274 DL_OPT_TRAP_POLICER_RATE |
7275 DL_OPT_TRAP_POLICER_BURST);
7276 if (err)
7277 return err;
7278
7279 return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
7280 }
7281
7282 static int cmd_trap_policer(struct dl *dl)
7283 {
7284 if (dl_argv_match(dl, "help")) {
7285 cmd_trap_help();
7286 return 0;
7287 } else if (dl_argv_match(dl, "show") ||
7288 dl_argv_match(dl, "list") || dl_no_arg(dl)) {
7289 dl_arg_inc(dl);
7290 return cmd_trap_policer_show(dl);
7291 } else if (dl_argv_match(dl, "set")) {
7292 dl_arg_inc(dl);
7293 return cmd_trap_policer_set(dl);
7294 }
7295 pr_err("Command \"%s\" not found\n", dl_argv(dl));
7296 return -ENOENT;
7297 }
7298
7299 static int cmd_trap(struct dl *dl)
7300 {
7301 if (dl_argv_match(dl, "help")) {
7302 cmd_trap_help();
7303 return 0;
7304 } else if (dl_argv_match(dl, "show") ||
7305 dl_argv_match(dl, "list") || dl_no_arg(dl)) {
7306 dl_arg_inc(dl);
7307 return cmd_trap_show(dl);
7308 } else if (dl_argv_match(dl, "set")) {
7309 dl_arg_inc(dl);
7310 return cmd_trap_set(dl);
7311 } else if (dl_argv_match(dl, "group")) {
7312 dl_arg_inc(dl);
7313 return cmd_trap_group(dl);
7314 } else if (dl_argv_match(dl, "policer")) {
7315 dl_arg_inc(dl);
7316 return cmd_trap_policer(dl);
7317 }
7318 pr_err("Command \"%s\" not found\n", dl_argv(dl));
7319 return -ENOENT;
7320 }
7321
7322 static void help(void)
7323 {
7324 pr_err("Usage: devlink [ OPTIONS ] OBJECT { COMMAND | help }\n"
7325 " devlink [ -f[orce] ] -b[atch] filename -N[etns] netnsname\n"
7326 "where OBJECT := { dev | port | sb | monitor | dpipe | resource | region | health | trap }\n"
7327 " OPTIONS := { -V[ersion] | -n[o-nice-names] | -j[son] | -p[retty] | -v[erbose] -s[tatistics] }\n");
7328 }
7329
7330 static int dl_cmd(struct dl *dl, int argc, char **argv)
7331 {
7332 dl->argc = argc;
7333 dl->argv = argv;
7334
7335 if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
7336 help();
7337 return 0;
7338 } else if (dl_argv_match(dl, "dev")) {
7339 dl_arg_inc(dl);
7340 return cmd_dev(dl);
7341 } else if (dl_argv_match(dl, "port")) {
7342 dl_arg_inc(dl);
7343 return cmd_port(dl);
7344 } else if (dl_argv_match(dl, "sb")) {
7345 dl_arg_inc(dl);
7346 return cmd_sb(dl);
7347 } else if (dl_argv_match(dl, "monitor")) {
7348 dl_arg_inc(dl);
7349 return cmd_mon(dl);
7350 } else if (dl_argv_match(dl, "dpipe")) {
7351 dl_arg_inc(dl);
7352 return cmd_dpipe(dl);
7353 } else if (dl_argv_match(dl, "resource")) {
7354 dl_arg_inc(dl);
7355 return cmd_resource(dl);
7356 } else if (dl_argv_match(dl, "region")) {
7357 dl_arg_inc(dl);
7358 return cmd_region(dl);
7359 } else if (dl_argv_match(dl, "health")) {
7360 dl_arg_inc(dl);
7361 return cmd_health(dl);
7362 } else if (dl_argv_match(dl, "trap")) {
7363 dl_arg_inc(dl);
7364 return cmd_trap(dl);
7365 }
7366 pr_err("Object \"%s\" not found\n", dl_argv(dl));
7367 return -ENOENT;
7368 }
7369
7370 static int dl_init(struct dl *dl)
7371 {
7372 int err;
7373
7374 dl->nlg = mnlg_socket_open(DEVLINK_GENL_NAME, DEVLINK_GENL_VERSION);
7375 if (!dl->nlg) {
7376 pr_err("Failed to connect to devlink Netlink\n");
7377 return -errno;
7378 }
7379
7380 err = ifname_map_init(dl);
7381 if (err) {
7382 pr_err("Failed to create index map\n");
7383 goto err_ifname_map_create;
7384 }
7385 new_json_obj_plain(dl->json_output);
7386 return 0;
7387
7388 err_ifname_map_create:
7389 mnlg_socket_close(dl->nlg);
7390 return err;
7391 }
7392
7393 static void dl_fini(struct dl *dl)
7394 {
7395 delete_json_obj_plain();
7396 ifname_map_fini(dl);
7397 mnlg_socket_close(dl->nlg);
7398 }
7399
7400 static struct dl *dl_alloc(void)
7401 {
7402 struct dl *dl;
7403
7404 dl = calloc(1, sizeof(*dl));
7405 if (!dl)
7406 return NULL;
7407 return dl;
7408 }
7409
7410 static void dl_free(struct dl *dl)
7411 {
7412 free(dl);
7413 }
7414
7415 static int dl_batch(struct dl *dl, const char *name, bool force)
7416 {
7417 char *line = NULL;
7418 size_t len = 0;
7419 int ret = EXIT_SUCCESS;
7420
7421 if (name && strcmp(name, "-") != 0) {
7422 if (freopen(name, "r", stdin) == NULL) {
7423 fprintf(stderr,
7424 "Cannot open file \"%s\" for reading: %s\n",
7425 name, strerror(errno));
7426 return EXIT_FAILURE;
7427 }
7428 }
7429
7430 cmdlineno = 0;
7431 while (getcmdline(&line, &len, stdin) != -1) {
7432 char *largv[100];
7433 int largc;
7434
7435 largc = makeargs(line, largv, 100);
7436 if (!largc)
7437 continue; /* blank line */
7438
7439 if (dl_cmd(dl, largc, largv)) {
7440 fprintf(stderr, "Command failed %s:%d\n",
7441 name, cmdlineno);
7442 ret = EXIT_FAILURE;
7443 if (!force)
7444 break;
7445 }
7446 }
7447
7448 if (line)
7449 free(line);
7450
7451 return ret;
7452 }
7453
7454 int main(int argc, char **argv)
7455 {
7456 static const struct option long_options[] = {
7457 { "Version", no_argument, NULL, 'V' },
7458 { "force", no_argument, NULL, 'f' },
7459 { "batch", required_argument, NULL, 'b' },
7460 { "no-nice-names", no_argument, NULL, 'n' },
7461 { "json", no_argument, NULL, 'j' },
7462 { "pretty", no_argument, NULL, 'p' },
7463 { "verbose", no_argument, NULL, 'v' },
7464 { "statistics", no_argument, NULL, 's' },
7465 { "Netns", required_argument, NULL, 'N' },
7466 { NULL, 0, NULL, 0 }
7467 };
7468 const char *batch_file = NULL;
7469 bool force = false;
7470 struct dl *dl;
7471 int opt;
7472 int err;
7473 int ret;
7474
7475 dl = dl_alloc();
7476 if (!dl) {
7477 pr_err("Failed to allocate memory for devlink\n");
7478 return EXIT_FAILURE;
7479 }
7480
7481 while ((opt = getopt_long(argc, argv, "Vfb:njpvsN:",
7482 long_options, NULL)) >= 0) {
7483
7484 switch (opt) {
7485 case 'V':
7486 printf("devlink utility, iproute2-ss%s\n", SNAPSHOT);
7487 ret = EXIT_SUCCESS;
7488 goto dl_free;
7489 case 'f':
7490 force = true;
7491 break;
7492 case 'b':
7493 batch_file = optarg;
7494 break;
7495 case 'n':
7496 dl->no_nice_names = true;
7497 break;
7498 case 'j':
7499 dl->json_output = true;
7500 break;
7501 case 'p':
7502 pretty = true;
7503 break;
7504 case 'v':
7505 dl->verbose = true;
7506 break;
7507 case 's':
7508 dl->stats = true;
7509 break;
7510 case 'N':
7511 if (netns_switch(optarg)) {
7512 ret = EXIT_FAILURE;
7513 goto dl_free;
7514 }
7515 break;
7516 default:
7517 pr_err("Unknown option.\n");
7518 help();
7519 ret = EXIT_FAILURE;
7520 goto dl_free;
7521 }
7522 }
7523
7524 argc -= optind;
7525 argv += optind;
7526
7527 err = dl_init(dl);
7528 if (err) {
7529 ret = EXIT_FAILURE;
7530 goto dl_free;
7531 }
7532
7533 if (batch_file)
7534 err = dl_batch(dl, batch_file, force);
7535 else
7536 err = dl_cmd(dl, argc, argv);
7537
7538 if (err) {
7539 ret = EXIT_FAILURE;
7540 goto dl_fini;
7541 }
7542
7543 ret = EXIT_SUCCESS;
7544
7545 dl_fini:
7546 dl_fini(dl);
7547 dl_free:
7548 dl_free(dl);
7549
7550 return ret;
7551 }