X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=devlink%2Fdevlink.c;h=0982faef7d88eef07e1d2ff79035a492d62d7662;hb=fbef655568ee931a82ad463a6f46f01ce3fb27aa;hp=c77f80c87adc0a1a74525dfebf3aace614157b88;hpb=9083a1344d8cbdd4282c22eec6a1f629059221f1;p=mirror_iproute2.git diff --git a/devlink/devlink.c b/devlink/devlink.c index c77f80c8..0982faef 100644 --- a/devlink/devlink.c +++ b/devlink/devlink.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -18,17 +19,27 @@ #include #include #include +#include +#include +#include +#include +#include +#define _LINUX_SYSINFO_H /* avoid collision with musl header */ #include #include +#include #include #include -#include +#include +#include +#include -#include "SNAPSHOT.h" +#include "version.h" #include "list.h" #include "mnlg.h" -#include "json_writer.h" +#include "json_print.h" #include "utils.h" +#include "namespace.h" #define ESWITCH_MODE_LEGACY "legacy" #define ESWITCH_MODE_SWITCHDEV "switchdev" @@ -37,6 +48,9 @@ #define ESWITCH_INLINE_MODE_NETWORK "network" #define ESWITCH_INLINE_MODE_TRANSPORT "transport" +#define ESWITCH_ENCAP_MODE_NONE "none" +#define ESWITCH_ENCAP_MODE_BASIC "basic" + #define PARAM_CMODE_RUNTIME_STR "runtime" #define PARAM_CMODE_DRIVERINIT_STR "driverinit" #define PARAM_CMODE_PERMANENT_STR "permanent" @@ -47,32 +61,65 @@ #define HEALTH_REPORTER_TIMESTAMP_FMT_LEN 80 static int g_new_line_count; - -#define pr_err(args...) fprintf(stderr, ##args) -#define pr_out(args...) \ - do { \ - if (g_indent_newline) { \ - fprintf(stdout, "%s", g_indent_str); \ - g_indent_newline = false; \ - } \ - fprintf(stdout, ##args); \ - g_new_line_count = 0; \ - } while (0) - -#define pr_out_sp(num, args...) \ - do { \ - int ret = fprintf(stdout, ##args); \ - if (ret < num) \ - fprintf(stdout, "%*s", num - ret, ""); \ - g_new_line_count = 0; \ - } while (0) - static int g_indent_level; static bool g_indent_newline; + #define INDENT_STR_STEP 2 #define INDENT_STR_MAXLEN 32 static char g_indent_str[INDENT_STR_MAXLEN + 1] = ""; +static void __attribute__((format(printf, 1, 2))) +pr_err(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +static void __attribute__((format(printf, 1, 2))) +pr_out(const char *fmt, ...) +{ + va_list ap; + + if (g_indent_newline) { + printf("%s", g_indent_str); + g_indent_newline = false; + } + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + g_new_line_count = 0; +} + +static void __attribute__((format(printf, 2, 3))) +pr_out_sp(unsigned int num, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vprintf(fmt, ap); + va_end(ap); + + if (ret < num) + printf("%*s", num - ret, ""); + g_new_line_count = 0; \ +} + +static void __attribute__((format(printf, 1, 2))) +pr_out_tty(const char *fmt, ...) +{ + va_list ap; + + if (!isatty(STDOUT_FILENO)) + return; + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); +} + static void __pr_out_indent_inc(void) { if (g_indent_level + INDENT_STR_STEP > INDENT_STR_MAXLEN) @@ -112,9 +159,32 @@ static int _mnlg_socket_recv_run(struct mnlg_socket *nlg, return 0; } -static int _mnlg_socket_sndrcv(struct mnlg_socket *nlg, - const struct nlmsghdr *nlh, - mnl_cb_t data_cb, void *data) +static void dummy_signal_handler(int signum) +{ +} + +static int _mnlg_socket_recv_run_intr(struct mnlg_socket *nlg, + mnl_cb_t data_cb, void *data) +{ + struct sigaction act, oact; + int err; + + act.sa_handler = dummy_signal_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_NODEFER; + + sigaction(SIGINT, &act, &oact); + err = mnlg_socket_recv_run(nlg, data_cb, data); + sigaction(SIGINT, &oact, NULL); + if (err < 0 && errno != EINTR) { + pr_err("devlink answers: %s\n", strerror(errno)); + return -errno; + } + return 0; +} + +static int _mnlg_socket_send(struct mnlg_socket *nlg, + const struct nlmsghdr *nlh) { int err; @@ -123,6 +193,18 @@ static int _mnlg_socket_sndrcv(struct mnlg_socket *nlg, pr_err("Failed to call mnlg_socket_send\n"); return -errno; } + return 0; +} + +static int _mnlg_socket_sndrcv(struct mnlg_socket *nlg, + const struct nlmsghdr *nlh, + mnl_cb_t data_cb, void *data) +{ + int err; + + err = _mnlg_socket_send(nlg, nlh); + if (err) + return err; return _mnlg_socket_recv_run(nlg, data_cb, data); } @@ -208,9 +290,19 @@ static void ifname_map_free(struct ifname_map *ifname_map) #define DL_OPT_FLASH_FILE_NAME BIT(25) #define DL_OPT_FLASH_COMPONENT BIT(26) #define DL_OPT_HEALTH_REPORTER_NAME BIT(27) +#define DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD BIT(28) +#define DL_OPT_HEALTH_REPORTER_AUTO_RECOVER BIT(29) +#define DL_OPT_TRAP_NAME BIT(30) +#define DL_OPT_TRAP_ACTION BIT(31) +#define DL_OPT_TRAP_GROUP_NAME BIT(32) +#define DL_OPT_NETNS BIT(33) +#define DL_OPT_TRAP_POLICER_ID BIT(34) +#define DL_OPT_TRAP_POLICER_RATE BIT(35) +#define DL_OPT_TRAP_POLICER_BURST BIT(36) +#define DL_OPT_HEALTH_REPORTER_AUTO_DUMP BIT(37) struct dl_opts { - uint32_t present; /* flags of present items */ + uint64_t present; /* flags of present items */ char *bus_name; char *dev_name; uint32_t port_index; @@ -226,10 +318,10 @@ struct dl_opts { enum devlink_eswitch_mode eswitch_mode; enum devlink_eswitch_inline_mode eswitch_inline_mode; const char *dpipe_table_name; - bool dpipe_counters_enable; - bool eswitch_encap_mode; + bool dpipe_counters_enabled; + enum devlink_eswitch_encap_mode eswitch_encap_mode; const char *resource_path; - uint32_t resource_size; + uint64_t resource_size; uint32_t resource_id; bool resource_id_valid; const char *param_name; @@ -242,6 +334,17 @@ struct dl_opts { const char *flash_file_name; const char *flash_component; const char *reporter_name; + uint64_t reporter_graceful_period; + bool reporter_auto_recover; + bool reporter_auto_dump; + const char *trap_name; + const char *trap_group_name; + enum devlink_trap_action trap_action; + bool netns_is_pid; + uint32_t netns; + uint32_t trap_policer_id; + uint64_t trap_policer_rate; + uint64_t trap_policer_burst; }; struct dl { @@ -251,10 +354,10 @@ struct dl { char **argv; bool no_nice_names; struct dl_opts opts; - json_writer_t *jw; bool json_output; bool pretty_output; bool verbose; + bool stats; struct { bool present; char *bus_name; @@ -283,6 +386,12 @@ static void dl_arg_inc(struct dl *dl) dl->argv++; } +static void dl_arg_dec(struct dl *dl) +{ + dl->argc++; + dl->argv--; +} + static char *dl_argv_next(struct dl *dl) { char *ret; @@ -321,6 +430,23 @@ static bool dl_no_arg(struct dl *dl) return dl_argc(dl) == 0; } +static void __pr_out_indent_newline(struct dl *dl) +{ + if (!g_indent_newline && !dl->json_output) + pr_out(" "); +} + +static void check_indent_newline(struct dl *dl) +{ + __pr_out_indent_newline(dl); + + if (g_indent_newline && !is_json_context()) { + printf("%s", g_indent_str); + g_indent_newline = false; + } + g_new_line_count = 0; +} + static const enum mnl_attr_data_type devlink_policy[DEVLINK_ATTR_MAX + 1] = { [DEVLINK_ATTR_BUS_NAME] = MNL_TYPE_NUL_STRING, [DEVLINK_ATTR_DEV_NAME] = MNL_TYPE_NUL_STRING, @@ -409,6 +535,28 @@ static const enum mnl_attr_data_type devlink_policy[DEVLINK_ATTR_MAX + 1] = { [DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT] = MNL_TYPE_U64, [DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS] = MNL_TYPE_U64, [DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] = MNL_TYPE_U64, + [DEVLINK_ATTR_FLASH_UPDATE_COMPONENT] = MNL_TYPE_STRING, + [DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG] = MNL_TYPE_STRING, + [DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE] = MNL_TYPE_U64, + [DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL] = MNL_TYPE_U64, + [DEVLINK_ATTR_STATS] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_TRAP_NAME] = MNL_TYPE_STRING, + [DEVLINK_ATTR_TRAP_ACTION] = MNL_TYPE_U8, + [DEVLINK_ATTR_TRAP_TYPE] = MNL_TYPE_U8, + [DEVLINK_ATTR_TRAP_GENERIC] = MNL_TYPE_FLAG, + [DEVLINK_ATTR_TRAP_METADATA] = MNL_TYPE_NESTED, + [DEVLINK_ATTR_TRAP_GROUP_NAME] = MNL_TYPE_STRING, + [DEVLINK_ATTR_RELOAD_FAILED] = MNL_TYPE_U8, + [DEVLINK_ATTR_TRAP_POLICER_ID] = MNL_TYPE_U32, + [DEVLINK_ATTR_TRAP_POLICER_RATE] = MNL_TYPE_U64, + [DEVLINK_ATTR_TRAP_POLICER_BURST] = MNL_TYPE_U64, +}; + +static const enum mnl_attr_data_type +devlink_stats_policy[DEVLINK_ATTR_STATS_MAX + 1] = { + [DEVLINK_ATTR_STATS_RX_PACKETS] = MNL_TYPE_U64, + [DEVLINK_ATTR_STATS_RX_BYTES] = MNL_TYPE_U64, + [DEVLINK_ATTR_STATS_RX_DROPPED] = MNL_TYPE_U64, }; static int attr_cb(const struct nlattr *attr, void *data) @@ -427,6 +575,25 @@ static int attr_cb(const struct nlattr *attr, void *data) return MNL_CB_OK; } +static int attr_stats_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type; + + /* Allow the tool to work on top of newer kernels that might contain + * more attributes. + */ + if (mnl_attr_type_valid(attr, DEVLINK_ATTR_STATS_MAX) < 0) + return MNL_CB_OK; + + type = mnl_attr_get_type(attr); + if (mnl_attr_validate(attr, devlink_stats_policy[type]) < 0) + return MNL_CB_ERROR; + + tb[type] = attr; + return MNL_CB_OK; +} + static int ifname_map_cb(const struct nlmsghdr *nlh, void *data) { struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; @@ -607,9 +774,11 @@ static int strtobool(const char *str, bool *p_val) { bool val; - if (!strcmp(str, "true") || !strcmp(str, "1")) + if (!strcmp(str, "true") || !strcmp(str, "1") || + !strcmp(str, "enable")) val = true; - else if (!strcmp(str, "false") || !strcmp(str, "0")) + else if (!strcmp(str, "false") || !strcmp(str, "0") || + !strcmp(str, "disable")) val = false; else return -EINVAL; @@ -708,7 +877,7 @@ static int dl_argv_handle_port(struct dl *dl, char **p_bus_name, static int dl_argv_handle_both(struct dl *dl, char **p_bus_name, char **p_dev_name, uint32_t *p_port_index, - uint32_t *p_handle_bit) + uint64_t *p_handle_bit) { char *str = dl_argv_next(dl); unsigned int slash_count; @@ -840,6 +1009,24 @@ static int dl_argv_uint16_t(struct dl *dl, uint16_t *p_val) return 0; } +static int dl_argv_bool(struct dl *dl, bool *p_val) +{ + char *str = dl_argv_next(dl); + int err; + + if (!str) { + pr_err("Boolean argument expected\n"); + return -EINVAL; + } + + err = strtobool(str, p_val); + if (err) { + pr_err("\"%s\" is not a valid boolean value\n", str); + return err; + } + return 0; +} + static int dl_argv_str(struct dl *dl, const char **p_str) { const char *str = dl_argv_next(dl); @@ -926,26 +1113,19 @@ static int eswitch_inline_mode_get(const char *typestr, return 0; } -static int dpipe_counters_enable_get(const char *typestr, - bool *counters_enable) -{ - if (strcmp(typestr, "enable") == 0) { - *counters_enable = 1; - } else if (strcmp(typestr, "disable") == 0) { - *counters_enable = 0; - } else { - pr_err("Unknown counter_state \"%s\"\n", typestr); - return -EINVAL; - } - return 0; -} - -static int eswitch_encap_mode_get(const char *typestr, bool *p_mode) +static int +eswitch_encap_mode_get(const char *typestr, + enum devlink_eswitch_encap_mode *p_encap_mode) { - if (strcmp(typestr, "enable") == 0) { - *p_mode = true; - } else if (strcmp(typestr, "disable") == 0) { - *p_mode = false; + /* The initial implementation incorrectly accepted "enable"/"disable". + * Carry it to maintain backward compatibility. + */ + if (strcmp(typestr, "disable") == 0 || + strcmp(typestr, ESWITCH_ENCAP_MODE_NONE) == 0) { + *p_encap_mode = DEVLINK_ESWITCH_ENCAP_MODE_NONE; + } else if (strcmp(typestr, "enable") == 0 || + strcmp(typestr, ESWITCH_ENCAP_MODE_BASIC) == 0) { + *p_encap_mode = DEVLINK_ESWITCH_ENCAP_MODE_BASIC; } else { pr_err("Unknown eswitch encap mode \"%s\"\n", typestr); return -EINVAL; @@ -969,8 +1149,24 @@ static int param_cmode_get(const char *cmodestr, return 0; } +static int trap_action_get(const char *actionstr, + enum devlink_trap_action *p_action) +{ + if (strcmp(actionstr, "drop") == 0) { + *p_action = DEVLINK_TRAP_ACTION_DROP; + } else if (strcmp(actionstr, "trap") == 0) { + *p_action = DEVLINK_TRAP_ACTION_TRAP; + } else if (strcmp(actionstr, "mirror") == 0) { + *p_action = DEVLINK_TRAP_ACTION_MIRROR; + } else { + pr_err("Unknown trap action \"%s\"\n", actionstr); + return -EINVAL; + } + return 0; +} + struct dl_args_metadata { - uint32_t o_flag; + uint64_t o_flag; char err_msg[DL_ARGS_REQUIRED_MAX_ERR_LEN]; }; @@ -988,6 +1184,8 @@ static const struct dl_args_metadata dl_args_required[] = { {DL_OPT_DPIPE_TABLE_NAME, "Dpipe table name expected."}, {DL_OPT_DPIPE_TABLE_COUNTERS, "Dpipe table counter state expected."}, {DL_OPT_ESWITCH_ENCAP_MODE, "E-Switch encapsulation option expected."}, + {DL_OPT_RESOURCE_PATH, "Resource path expected."}, + {DL_OPT_RESOURCE_SIZE, "Resource size expected."}, {DL_OPT_PARAM_NAME, "Parameter name expected."}, {DL_OPT_PARAM_VALUE, "Value to set expected."}, {DL_OPT_PARAM_CMODE, "Configuration mode expected."}, @@ -995,12 +1193,14 @@ static const struct dl_args_metadata dl_args_required[] = { {DL_OPT_REGION_ADDRESS, "Region address value expected."}, {DL_OPT_REGION_LENGTH, "Region length value expected."}, {DL_OPT_HEALTH_REPORTER_NAME, "Reporter's name is expected."}, + {DL_OPT_TRAP_NAME, "Trap's name is expected."}, + {DL_OPT_TRAP_GROUP_NAME, "Trap group's name is expected."}, }; -static int dl_args_finding_required_validate(uint32_t o_required, - uint32_t o_found) +static int dl_args_finding_required_validate(uint64_t o_required, + uint64_t o_found) { - uint32_t o_flag; + uint64_t o_flag; int i; for (i = 0; i < ARRAY_SIZE(dl_args_required); i++) { @@ -1010,24 +1210,29 @@ static int dl_args_finding_required_validate(uint32_t o_required, return -EINVAL; } } + if (o_required & ~o_found) { + pr_err("BUG: unknown argument required but not found\n"); + return -EINVAL; + } return 0; } -static int dl_argv_parse(struct dl *dl, uint32_t o_required, - uint32_t o_optional) +static int dl_argv_parse(struct dl *dl, uint64_t o_required, + uint64_t o_optional) { struct dl_opts *opts = &dl->opts; - uint32_t o_all = o_required | o_optional; - uint32_t o_found = 0; + uint64_t o_all = o_required | o_optional; + uint64_t o_found = 0; int err; if (o_required & DL_OPT_HANDLE && o_required & DL_OPT_HANDLEP) { - uint32_t handle_bit; + uint64_t handle_bit; err = dl_argv_handle_both(dl, &opts->bus_name, &opts->dev_name, &opts->port_index, &handle_bit); if (err) return err; + o_required &= ~(DL_OPT_HANDLE | DL_OPT_HANDLEP) | handle_bit; o_found |= handle_bit; } else if (o_required & DL_OPT_HANDLE) { err = dl_argv_handle(dl, &opts->bus_name, &opts->dev_name); @@ -1161,20 +1366,16 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required, if (err) return err; o_found |= DL_OPT_DPIPE_TABLE_NAME; - } else if (dl_argv_match(dl, "counters") && + } else if ((dl_argv_match(dl, "counters") || + dl_argv_match(dl, "counters_enabled")) && (o_all & DL_OPT_DPIPE_TABLE_COUNTERS)) { - const char *typestr; - dl_arg_inc(dl); - err = dl_argv_str(dl, &typestr); - if (err) - return err; - err = dpipe_counters_enable_get(typestr, - &opts->dpipe_counters_enable); + err = dl_argv_bool(dl, &opts->dpipe_counters_enabled); if (err) return err; o_found |= DL_OPT_DPIPE_TABLE_COUNTERS; - } else if (dl_argv_match(dl, "encap") && + } else if ((dl_argv_match(dl, "encap") || /* Original incorrect implementation */ + dl_argv_match(dl, "encap-mode")) && (o_all & DL_OPT_ESWITCH_ENCAP_MODE)) { const char *typestr; @@ -1197,7 +1398,7 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required, } else if (dl_argv_match(dl, "size") && (o_all & DL_OPT_RESOURCE_SIZE)) { dl_arg_inc(dl); - err = dl_argv_uint32_t(dl, &opts->resource_size); + err = dl_argv_uint64_t(dl, &opts->resource_size); if (err) return err; o_found |= DL_OPT_RESOURCE_SIZE; @@ -1269,6 +1470,97 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required, if (err) return err; o_found |= DL_OPT_HEALTH_REPORTER_NAME; + } else if (dl_argv_match(dl, "grace_period") && + (o_all & DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD)) { + dl_arg_inc(dl); + err = dl_argv_uint64_t(dl, + &opts->reporter_graceful_period); + if (err) + return err; + o_found |= DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD; + } else if (dl_argv_match(dl, "auto_recover") && + (o_all & DL_OPT_HEALTH_REPORTER_AUTO_RECOVER)) { + dl_arg_inc(dl); + err = dl_argv_bool(dl, &opts->reporter_auto_recover); + if (err) + return err; + o_found |= DL_OPT_HEALTH_REPORTER_AUTO_RECOVER; + } else if (dl_argv_match(dl, "auto_dump") && + (o_all & DL_OPT_HEALTH_REPORTER_AUTO_DUMP)) { + dl_arg_inc(dl); + err = dl_argv_bool(dl, &opts->reporter_auto_dump); + if (err) + return err; + o_found |= DL_OPT_HEALTH_REPORTER_AUTO_DUMP; + } else if (dl_argv_match(dl, "trap") && + (o_all & DL_OPT_TRAP_NAME)) { + dl_arg_inc(dl); + err = dl_argv_str(dl, &opts->trap_name); + if (err) + return err; + o_found |= DL_OPT_TRAP_NAME; + } else if (dl_argv_match(dl, "group") && + (o_all & DL_OPT_TRAP_GROUP_NAME)) { + dl_arg_inc(dl); + err = dl_argv_str(dl, &opts->trap_group_name); + if (err) + return err; + o_found |= DL_OPT_TRAP_GROUP_NAME; + } else if (dl_argv_match(dl, "action") && + (o_all & DL_OPT_TRAP_ACTION)) { + const char *actionstr; + + dl_arg_inc(dl); + err = dl_argv_str(dl, &actionstr); + if (err) + return err; + err = trap_action_get(actionstr, &opts->trap_action); + if (err) + return err; + o_found |= DL_OPT_TRAP_ACTION; + } else if (dl_argv_match(dl, "netns") && + (o_all & DL_OPT_NETNS)) { + const char *netns_str; + + dl_arg_inc(dl); + err = dl_argv_str(dl, &netns_str); + if (err) + return err; + opts->netns = netns_get_fd(netns_str); + if ((int)opts->netns < 0) { + dl_arg_dec(dl); + err = dl_argv_uint32_t(dl, &opts->netns); + if (err) + return err; + opts->netns_is_pid = true; + } + o_found |= DL_OPT_NETNS; + } else if (dl_argv_match(dl, "policer") && + (o_all & DL_OPT_TRAP_POLICER_ID)) { + dl_arg_inc(dl); + err = dl_argv_uint32_t(dl, &opts->trap_policer_id); + if (err) + return err; + o_found |= DL_OPT_TRAP_POLICER_ID; + } else if (dl_argv_match(dl, "nopolicer") && + (o_all & DL_OPT_TRAP_POLICER_ID)) { + dl_arg_inc(dl); + opts->trap_policer_id = 0; + o_found |= DL_OPT_TRAP_POLICER_ID; + } else if (dl_argv_match(dl, "rate") && + (o_all & DL_OPT_TRAP_POLICER_RATE)) { + dl_arg_inc(dl); + err = dl_argv_uint64_t(dl, &opts->trap_policer_rate); + if (err) + return err; + o_found |= DL_OPT_TRAP_POLICER_RATE; + } else if (dl_argv_match(dl, "burst") && + (o_all & DL_OPT_TRAP_POLICER_BURST)) { + dl_arg_inc(dl); + err = dl_argv_uint64_t(dl, &opts->trap_policer_burst); + if (err) + return err; + o_found |= DL_OPT_TRAP_POLICER_BURST; } else { pr_err("Unknown option \"%s\"\n", dl_argv(dl)); return -EINVAL; @@ -1341,7 +1633,7 @@ static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl) opts->dpipe_table_name); if (opts->present & DL_OPT_DPIPE_TABLE_COUNTERS) mnl_attr_put_u8(nlh, DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED, - opts->dpipe_counters_enable); + opts->dpipe_counters_enabled); if (opts->present & DL_OPT_ESWITCH_ENCAP_MODE) mnl_attr_put_u8(nlh, DEVLINK_ATTR_ESWITCH_ENCAP_MODE, opts->eswitch_encap_mode); @@ -1375,10 +1667,43 @@ static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl) if (opts->present & DL_OPT_HEALTH_REPORTER_NAME) mnl_attr_put_strz(nlh, DEVLINK_ATTR_HEALTH_REPORTER_NAME, opts->reporter_name); + if (opts->present & DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD) + mnl_attr_put_u64(nlh, + DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD, + opts->reporter_graceful_period); + if (opts->present & DL_OPT_HEALTH_REPORTER_AUTO_RECOVER) + mnl_attr_put_u8(nlh, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER, + opts->reporter_auto_recover); + if (opts->present & DL_OPT_HEALTH_REPORTER_AUTO_DUMP) + mnl_attr_put_u8(nlh, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP, + opts->reporter_auto_dump); + if (opts->present & DL_OPT_TRAP_NAME) + mnl_attr_put_strz(nlh, DEVLINK_ATTR_TRAP_NAME, + opts->trap_name); + if (opts->present & DL_OPT_TRAP_GROUP_NAME) + mnl_attr_put_strz(nlh, DEVLINK_ATTR_TRAP_GROUP_NAME, + opts->trap_group_name); + if (opts->present & DL_OPT_TRAP_ACTION) + mnl_attr_put_u8(nlh, DEVLINK_ATTR_TRAP_ACTION, + opts->trap_action); + if (opts->present & DL_OPT_NETNS) + mnl_attr_put_u32(nlh, + opts->netns_is_pid ? DEVLINK_ATTR_NETNS_PID : + DEVLINK_ATTR_NETNS_FD, + opts->netns); + if (opts->present & DL_OPT_TRAP_POLICER_ID) + mnl_attr_put_u32(nlh, DEVLINK_ATTR_TRAP_POLICER_ID, + opts->trap_policer_id); + if (opts->present & DL_OPT_TRAP_POLICER_RATE) + mnl_attr_put_u64(nlh, DEVLINK_ATTR_TRAP_POLICER_RATE, + opts->trap_policer_rate); + if (opts->present & DL_OPT_TRAP_POLICER_BURST) + mnl_attr_put_u64(nlh, DEVLINK_ATTR_TRAP_POLICER_BURST, + opts->trap_policer_burst); } static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl, - uint32_t o_required, uint32_t o_optional) + uint64_t o_required, uint64_t o_optional) { int err; @@ -1431,11 +1756,11 @@ static void cmd_dev_help(void) pr_err("Usage: devlink dev show [ DEV ]\n"); pr_err(" devlink dev eswitch set DEV [ mode { legacy | switchdev } ]\n"); pr_err(" [ inline-mode { none | link | network | transport } ]\n"); - pr_err(" [ encap { disable | enable } ]\n"); + pr_err(" [ encap-mode { none | basic } ]\n"); pr_err(" devlink dev eswitch show DEV\n"); pr_err(" devlink dev param set DEV name PARAMETER value VALUE cmode { permanent | driverinit | runtime }\n"); pr_err(" devlink dev param show [DEV name PARAMETER]\n"); - pr_err(" devlink dev reload DEV\n"); + pr_err(" devlink dev reload DEV [ netns { PID | NAME | ID } ]\n"); pr_err(" devlink dev info [ DEV ]\n"); pr_err(" devlink dev flash DEV file PATH [ component NAME ]\n"); } @@ -1477,26 +1802,24 @@ static void __pr_out_handle_start(struct dl *dl, struct nlattr **tb, { const char *bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]); const char *dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]); - char buf[32]; + char buf[64]; sprintf(buf, "%s/%s", bus_name, dev_name); if (dl->json_output) { if (array) { if (should_arr_last_handle_end(dl, bus_name, dev_name)) - jsonw_end_array(dl->jw); + close_json_array(PRINT_JSON, NULL); if (should_arr_last_handle_start(dl, bus_name, dev_name)) { - jsonw_name(dl->jw, buf); - jsonw_start_array(dl->jw); - jsonw_start_object(dl->jw); + open_json_array(PRINT_JSON, buf); + open_json_object(NULL); arr_last_handle_set(dl, bus_name, dev_name); } else { - jsonw_start_object(dl->jw); + open_json_object(NULL); } } else { - jsonw_name(dl->jw, buf); - jsonw_start_object(dl->jw); + open_json_object(buf); } } else { if (array) { @@ -1523,7 +1846,7 @@ static void pr_out_handle_start_arr(struct dl *dl, struct nlattr **tb) static void pr_out_handle_end(struct dl *dl) { if (dl->json_output) - jsonw_end_object(dl->jw); + close_json_object(); else __pr_out_newline(); } @@ -1570,7 +1893,7 @@ static void __pr_out_port_handle_start(struct dl *dl, const char *bus_name, uint32_t port_index, bool try_nice, bool array) { - static char buf[32]; + static char buf[64]; char *ifname = NULL; if (dl->no_nice_names || !try_nice || @@ -1585,21 +1908,19 @@ static void __pr_out_port_handle_start(struct dl *dl, const char *bus_name, if (should_arr_last_port_handle_end(dl, bus_name, dev_name, port_index)) - jsonw_end_array(dl->jw); + close_json_array(PRINT_JSON, NULL); if (should_arr_last_port_handle_start(dl, bus_name, dev_name, port_index)) { - jsonw_name(dl->jw, buf); - jsonw_start_array(dl->jw); - jsonw_start_object(dl->jw); + open_json_array(PRINT_JSON, buf); + open_json_object(NULL); arr_last_port_handle_set(dl, bus_name, dev_name, port_index); } else { - jsonw_start_object(dl->jw); + open_json_object(NULL); } } else { - jsonw_name(dl->jw, buf); - jsonw_start_object(dl->jw); + open_json_object(buf); } } else { pr_out("%s:", buf); @@ -1633,138 +1954,66 @@ static void pr_out_port_handle_start_arr(struct dl *dl, struct nlattr **tb, bool static void pr_out_port_handle_end(struct dl *dl) { if (dl->json_output) - jsonw_end_object(dl->jw); + close_json_object(); else pr_out("\n"); } - -static void pr_out_str(struct dl *dl, const char *name, const char *val) -{ - if (dl->json_output) { - jsonw_string_field(dl->jw, name, val); - } else { - if (g_indent_newline) - pr_out("%s %s", name, val); - else - pr_out(" %s %s", name, val); - } -} - -static void pr_out_bool(struct dl *dl, const char *name, bool val) -{ - if (dl->json_output) - jsonw_bool_field(dl->jw, name, val); - else - pr_out_str(dl, name, val ? "true" : "false"); -} - -static void pr_out_uint(struct dl *dl, const char *name, unsigned int val) -{ - if (dl->json_output) { - jsonw_uint_field(dl->jw, name, val); - } else { - if (g_indent_newline) - pr_out("%s %u", name, val); - else - pr_out(" %s %u", name, val); - } -} - static void pr_out_u64(struct dl *dl, const char *name, uint64_t val) { + __pr_out_indent_newline(dl); if (val == (uint64_t) -1) - return pr_out_str(dl, name, "unlimited"); - - if (dl->json_output) { - jsonw_u64_field(dl->jw, name, val); - } else { - if (g_indent_newline) - pr_out("%s %lu", name, val); - else - pr_out(" %s %lu", name, val); - } -} - -static void pr_out_bool_value(struct dl *dl, bool value) -{ - if (dl->json_output) - jsonw_bool(dl->jw, value); - else - pr_out(" %s", value ? "true" : "false"); -} + return print_string_name_value(name, "unlimited"); -static void pr_out_uint_value(struct dl *dl, unsigned int value) -{ if (dl->json_output) - jsonw_uint(dl->jw, value); + print_u64(PRINT_JSON, name, NULL, val); else - pr_out(" %u", value); + pr_out("%s %"PRIu64, name, val); } -static void pr_out_uint64_value(struct dl *dl, uint64_t value) +static bool is_binary_eol(int i) { - if (dl->json_output) - jsonw_u64(dl->jw, value); - else - pr_out(" %lu", value); + return !(i%16); } static void pr_out_binary_value(struct dl *dl, uint8_t *data, uint32_t len) { - int i = 1; - - if (dl->json_output) - jsonw_start_array(dl->jw); - else - pr_out("\n"); + int i = 0; while (i < len) { - if (dl->json_output) { - jsonw_printf(dl->jw, "%d", data[i]); - } else { - pr_out(" %02x", data[i]); - if (!(i % 16)) - pr_out("\n"); - } + if (dl->json_output) + print_int(PRINT_JSON, NULL, NULL, data[i]); + else + pr_out("%02x ", data[i]); i++; + if (!dl->json_output && is_binary_eol(i)) + __pr_out_newline(); } - if (dl->json_output) - jsonw_end_array(dl->jw); - else if ((i - 1) % 16) - pr_out("\n"); -} - -static void pr_out_str_value(struct dl *dl, const char *value) -{ - if (dl->json_output) - jsonw_string(dl->jw, value); - else - pr_out(" %s", value); + if (!dl->json_output && !is_binary_eol(i)) + __pr_out_newline(); } static void pr_out_name(struct dl *dl, const char *name) { + __pr_out_indent_newline(dl); if (dl->json_output) - jsonw_name(dl->jw, name); + print_string(PRINT_JSON, name, NULL, NULL); else - pr_out(" %s:", name); + pr_out("%s:", name); } static void pr_out_region_chunk_start(struct dl *dl, uint64_t addr) { if (dl->json_output) { - jsonw_name(dl->jw, "address"); - jsonw_uint(dl->jw, addr); - jsonw_name(dl->jw, "data"); - jsonw_start_array(dl->jw); + print_uint(PRINT_JSON, "address", NULL, addr); + open_json_array(PRINT_JSON, "data"); } } static void pr_out_region_chunk_end(struct dl *dl) { if (dl->json_output) - jsonw_end_array(dl->jw); + close_json_array(PRINT_JSON, NULL); } static void pr_out_region_chunk(struct dl *dl, uint8_t *data, uint32_t len, @@ -1784,7 +2033,7 @@ static void pr_out_region_chunk(struct dl *dl, uint8_t *data, uint32_t len, align_val++; if (dl->json_output) - jsonw_printf(dl->jw, "%d", data[i]); + print_int(PRINT_JSON, NULL, NULL, data[i]); else pr_out("%02x ", data[i]); @@ -1794,17 +2043,11 @@ static void pr_out_region_chunk(struct dl *dl, uint8_t *data, uint32_t len, pr_out_region_chunk_end(dl); } -static void pr_out_dev(struct dl *dl, struct nlattr **tb) -{ - pr_out_handle(dl, tb); -} - static void pr_out_section_start(struct dl *dl, const char *name) { if (dl->json_output) { - jsonw_start_object(dl->jw); - jsonw_name(dl->jw, name); - jsonw_start_object(dl->jw); + open_json_object(NULL); + open_json_object(name); } } @@ -1812,17 +2055,16 @@ static void pr_out_section_end(struct dl *dl) { if (dl->json_output) { if (dl->arr_last.present) - jsonw_end_array(dl->jw); - jsonw_end_object(dl->jw); - jsonw_end_object(dl->jw); + close_json_array(PRINT_JSON, NULL); + close_json_object(); + close_json_object(); } } static void pr_out_array_start(struct dl *dl, const char *name) { if (dl->json_output) { - jsonw_name(dl->jw, name); - jsonw_start_array(dl->jw); + open_json_array(PRINT_JSON, name); } else { __pr_out_indent_inc(); __pr_out_newline(); @@ -1835,7 +2077,7 @@ static void pr_out_array_start(struct dl *dl, const char *name) static void pr_out_array_end(struct dl *dl) { if (dl->json_output) { - jsonw_end_array(dl->jw); + close_json_array(PRINT_JSON, NULL); } else { __pr_out_indent_dec(); __pr_out_indent_dec(); @@ -1845,8 +2087,7 @@ static void pr_out_array_end(struct dl *dl) static void pr_out_object_start(struct dl *dl, const char *name) { if (dl->json_output) { - jsonw_name(dl->jw, name); - jsonw_start_object(dl->jw); + open_json_object(name); } else { __pr_out_indent_inc(); __pr_out_newline(); @@ -1859,7 +2100,7 @@ static void pr_out_object_start(struct dl *dl, const char *name) static void pr_out_object_end(struct dl *dl) { if (dl->json_output) { - jsonw_end_object(dl->jw); + close_json_object(); } else { __pr_out_indent_dec(); __pr_out_indent_dec(); @@ -1869,17 +2110,44 @@ static void pr_out_object_end(struct dl *dl) static void pr_out_entry_start(struct dl *dl) { if (dl->json_output) - jsonw_start_object(dl->jw); + open_json_object(NULL); } static void pr_out_entry_end(struct dl *dl) { if (dl->json_output) - jsonw_end_object(dl->jw); + close_json_object(); else __pr_out_newline(); } +static void pr_out_stats(struct dl *dl, struct nlattr *nla_stats) +{ + struct nlattr *tb[DEVLINK_ATTR_STATS_MAX + 1] = {}; + int err; + + if (!dl->stats) + return; + + err = mnl_attr_parse_nested(nla_stats, attr_stats_cb, tb); + if (err != MNL_CB_OK) + return; + + pr_out_object_start(dl, "stats"); + pr_out_object_start(dl, "rx"); + if (tb[DEVLINK_ATTR_STATS_RX_BYTES]) + pr_out_u64(dl, "bytes", + mnl_attr_get_u64(tb[DEVLINK_ATTR_STATS_RX_BYTES])); + if (tb[DEVLINK_ATTR_STATS_RX_PACKETS]) + pr_out_u64(dl, "packets", + mnl_attr_get_u64(tb[DEVLINK_ATTR_STATS_RX_PACKETS])); + if (tb[DEVLINK_ATTR_STATS_RX_DROPPED]) + pr_out_u64(dl, "dropped", + mnl_attr_get_u64(tb[DEVLINK_ATTR_STATS_RX_DROPPED])); + pr_out_object_end(dl); + pr_out_object_end(dl); +} + static const char *param_cmode_name(uint8_t cmode) { switch (cmode) { @@ -1918,23 +2186,39 @@ static const char *eswitch_inline_mode_name(uint32_t mode) } } +static const char *eswitch_encap_mode_name(uint32_t mode) +{ + switch (mode) { + case DEVLINK_ESWITCH_ENCAP_MODE_NONE: + return ESWITCH_ENCAP_MODE_NONE; + case DEVLINK_ESWITCH_ENCAP_MODE_BASIC: + return ESWITCH_ENCAP_MODE_BASIC; + default: + return ""; + } +} + static void pr_out_eswitch(struct dl *dl, struct nlattr **tb) { __pr_out_handle_start(dl, tb, true, false); - if (tb[DEVLINK_ATTR_ESWITCH_MODE]) - pr_out_str(dl, "mode", - eswitch_mode_name(mnl_attr_get_u16(tb[DEVLINK_ATTR_ESWITCH_MODE]))); - - if (tb[DEVLINK_ATTR_ESWITCH_INLINE_MODE]) - pr_out_str(dl, "inline-mode", - eswitch_inline_mode_name(mnl_attr_get_u8( - tb[DEVLINK_ATTR_ESWITCH_INLINE_MODE]))); - + if (tb[DEVLINK_ATTR_ESWITCH_MODE]) { + check_indent_newline(dl); + print_string(PRINT_ANY, "mode", "mode %s", + eswitch_mode_name(mnl_attr_get_u16( + tb[DEVLINK_ATTR_ESWITCH_MODE]))); + } + if (tb[DEVLINK_ATTR_ESWITCH_INLINE_MODE]) { + check_indent_newline(dl); + print_string(PRINT_ANY, "inline-mode", "inline-mode %s", + eswitch_inline_mode_name(mnl_attr_get_u8( + tb[DEVLINK_ATTR_ESWITCH_INLINE_MODE]))); + } if (tb[DEVLINK_ATTR_ESWITCH_ENCAP_MODE]) { - bool encap_mode = !!mnl_attr_get_u8(tb[DEVLINK_ATTR_ESWITCH_ENCAP_MODE]); - - pr_out_str(dl, "encap", encap_mode ? "enable" : "disable"); + check_indent_newline(dl); + print_string(PRINT_ANY, "encap-mode", "encap-mode %s", + eswitch_encap_mode_name(mnl_attr_get_u8( + tb[DEVLINK_ATTR_ESWITCH_ENCAP_MODE]))); } pr_out_handle_end(dl); @@ -2074,6 +2358,36 @@ static const struct param_val_conv param_val_conv[] = { .vstr = "flash", .vuint = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH, }, + { + .name = "fw_load_policy", + .vstr = "disk", + .vuint = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DISK, + }, + { + .name = "reset_dev_on_drv_probe", + .vstr = "unknown", + .vuint = DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_UNKNOWN, + }, + { + .name = "fw_load_policy", + .vstr = "unknown", + .vuint = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_UNKNOWN, + }, + { + .name = "reset_dev_on_drv_probe", + .vstr = "always", + .vuint = DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_ALWAYS, + }, + { + .name = "reset_dev_on_drv_probe", + .vstr = "never", + .vuint = DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_NEVER, + }, + { + .name = "reset_dev_on_drv_probe", + .vstr = "disk", + .vuint = DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_DISK, + }, }; #define PARAM_VAL_CONV_LEN ARRAY_SIZE(param_val_conv) @@ -2096,8 +2410,10 @@ static void pr_out_param_value(struct dl *dl, const char *nla_name, !nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA])) return; - pr_out_str(dl, "cmode", - param_cmode_name(mnl_attr_get_u8(nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE]))); + check_indent_newline(dl); + print_string(PRINT_ANY, "cmode", "cmode %s", + param_cmode_name(mnl_attr_get_u8(nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE]))); + val_attr = nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA]; conv_exists = param_val_conv_exists(param_val_conv, PARAM_VAL_CONV_LEN, @@ -2113,9 +2429,10 @@ static void pr_out_param_value(struct dl *dl, const char *nla_name, &vstr); if (err) return; - pr_out_str(dl, "value", vstr); + print_string(PRINT_ANY, "value", " value %s", vstr); } else { - pr_out_uint(dl, "value", mnl_attr_get_u8(val_attr)); + print_uint(PRINT_ANY, "value", " value %u", + mnl_attr_get_u8(val_attr)); } break; case MNL_TYPE_U16: @@ -2127,9 +2444,10 @@ static void pr_out_param_value(struct dl *dl, const char *nla_name, &vstr); if (err) return; - pr_out_str(dl, "value", vstr); + print_string(PRINT_ANY, "value", " value %s", vstr); } else { - pr_out_uint(dl, "value", mnl_attr_get_u16(val_attr)); + print_uint(PRINT_ANY, "value", " value %u", + mnl_attr_get_u16(val_attr)); } break; case MNL_TYPE_U32: @@ -2141,16 +2459,18 @@ static void pr_out_param_value(struct dl *dl, const char *nla_name, &vstr); if (err) return; - pr_out_str(dl, "value", vstr); + print_string(PRINT_ANY, "value", " value %s", vstr); } else { - pr_out_uint(dl, "value", mnl_attr_get_u32(val_attr)); + print_uint(PRINT_ANY, "value", " value %u", + mnl_attr_get_u32(val_attr)); } break; case MNL_TYPE_STRING: - pr_out_str(dl, "value", mnl_attr_get_str(val_attr)); + print_string(PRINT_ANY, "value", " value %s", + mnl_attr_get_str(val_attr)); break; case MNL_TYPE_FLAG: - pr_out_bool(dl, "value", val_attr ? true : false); + print_bool(PRINT_ANY, "value", " value %s", val_attr); break; } } @@ -2179,12 +2499,12 @@ static void pr_out_param(struct dl *dl, struct nlattr **tb, bool array) nla_type = mnl_attr_get_u8(nla_param[DEVLINK_ATTR_PARAM_TYPE]); nla_name = mnl_attr_get_str(nla_param[DEVLINK_ATTR_PARAM_NAME]); - pr_out_str(dl, "name", nla_name); - + check_indent_newline(dl); + print_string(PRINT_ANY, "name", "name %s ", nla_name); if (!nla_param[DEVLINK_ATTR_PARAM_GENERIC]) - pr_out_str(dl, "type", "driver-specific"); + print_string(PRINT_ANY, "type", "type %s", "driver-specific"); else - pr_out_str(dl, "type", "generic"); + print_string(PRINT_ANY, "type", "type %s", "generic"); pr_out_array_start(dl, "values"); mnl_attr_for_each_nested(param_value_attr, @@ -2450,11 +2770,24 @@ static int cmd_dev_show_cb(const struct nlmsghdr *nlh, void *data) struct dl *dl = data; struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + uint8_t reload_failed = 0; mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME]) return MNL_CB_ERROR; - pr_out_dev(dl, tb); + + if (tb[DEVLINK_ATTR_RELOAD_FAILED]) + reload_failed = mnl_attr_get_u8(tb[DEVLINK_ATTR_RELOAD_FAILED]); + + if (reload_failed) { + __pr_out_handle_start(dl, tb, true, false); + check_indent_newline(dl); + print_bool(PRINT_ANY, "reload_failed", "reload_failed %s", true); + pr_out_handle_end(dl); + } else { + pr_out_handle(dl, tb); + } + return MNL_CB_OK; } @@ -2481,25 +2814,20 @@ static int cmd_dev_show(struct dl *dl) return err; } -static void cmd_dev_reload_help(void) -{ - pr_err("Usage: devlink dev reload [ DEV ]\n"); -} - static int cmd_dev_reload(struct dl *dl) { struct nlmsghdr *nlh; int err; if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { - cmd_dev_reload_help(); + cmd_dev_help(); return 0; } nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RELOAD, NLM_F_REQUEST | NLM_F_ACK); - err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, 0); + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, DL_OPT_NETNS); if (err) return err; @@ -2536,7 +2864,8 @@ static void pr_out_versions_single(struct dl *dl, const struct nlmsghdr *nlh, ver_name = mnl_attr_get_str(tb[DEVLINK_ATTR_INFO_VERSION_NAME]); ver_value = mnl_attr_get_str(tb[DEVLINK_ATTR_INFO_VERSION_VALUE]); - pr_out_str(dl, ver_name, ver_value); + check_indent_newline(dl); + print_string_name_value(ver_name, ver_value); if (!dl->json_output) __pr_out_newline(); } @@ -2556,7 +2885,9 @@ static void pr_out_info(struct dl *dl, const struct nlmsghdr *nlh, if (!dl->json_output) __pr_out_newline(); - pr_out_str(dl, "driver", mnl_attr_get_str(nla_drv)); + check_indent_newline(dl); + print_string(PRINT_ANY, "driver", "driver %s", + mnl_attr_get_str(nla_drv)); } if (tb[DEVLINK_ATTR_INFO_SERIAL_NUMBER]) { @@ -2564,7 +2895,9 @@ static void pr_out_info(struct dl *dl, const struct nlmsghdr *nlh, if (!dl->json_output) __pr_out_newline(); - pr_out_str(dl, "serial_number", mnl_attr_get_str(nla_sn)); + check_indent_newline(dl); + print_string(PRINT_ANY, "serial_number", "serial_number %s", + mnl_attr_get_str(nla_sn)); } __pr_out_indent_dec(); @@ -2609,11 +2942,6 @@ static int cmd_versions_show_cb(const struct nlmsghdr *nlh, void *data) return MNL_CB_OK; } -static void cmd_dev_info_help(void) -{ - pr_err("Usage: devlink dev info [ DEV ]\n"); -} - static int cmd_dev_info(struct dl *dl) { struct nlmsghdr *nlh; @@ -2621,7 +2949,7 @@ static int cmd_dev_info(struct dl *dl) int err; if (dl_argv_match(dl, "help")) { - cmd_dev_info_help(); + cmd_dev_help(); return 0; } @@ -2642,18 +2970,154 @@ static int cmd_dev_info(struct dl *dl) return err; } -static void cmd_dev_flash_help(void) +struct cmd_dev_flash_status_ctx { + struct dl *dl; + char *last_msg; + char *last_component; + uint8_t not_first:1, + last_pc:1, + received_end:1, + flash_done:1; +}; + +static int nullstrcmp(const char *str1, const char *str2) +{ + if (str1 && str2) + return strcmp(str1, str2); + if (!str1 && !str2) + return 0; + return str1 ? 1 : -1; +} + +static int cmd_dev_flash_status_cb(const struct nlmsghdr *nlh, void *data) +{ + struct cmd_dev_flash_status_ctx *ctx = data; + struct dl_opts *opts = &ctx->dl->opts; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + const char *component = NULL; + uint64_t done = 0, total = 0; + const char *msg = NULL; + const char *bus_name; + const char *dev_name; + + if (genl->cmd != DEVLINK_CMD_FLASH_UPDATE_STATUS && + genl->cmd != DEVLINK_CMD_FLASH_UPDATE_END) + return MNL_CB_STOP; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME]) + return MNL_CB_ERROR; + bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]); + dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]); + if (strcmp(bus_name, opts->bus_name) || + strcmp(dev_name, opts->dev_name)) + return MNL_CB_ERROR; + + if (genl->cmd == DEVLINK_CMD_FLASH_UPDATE_END && ctx->not_first) { + pr_out("\n"); + free(ctx->last_msg); + free(ctx->last_component); + ctx->received_end = 1; + return MNL_CB_STOP; + } + + if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG]) + msg = mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG]); + if (tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT]) + component = mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT]); + if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE]) + done = mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE]); + if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL]) + total = mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL]); + + if (!nullstrcmp(msg, ctx->last_msg) && + !nullstrcmp(component, ctx->last_component) && + ctx->last_pc && ctx->not_first) { + pr_out_tty("\b\b\b\b\b"); /* clean percentage */ + } else { + if (ctx->not_first) + pr_out("\n"); + if (component) { + pr_out("[%s] ", component); + free(ctx->last_component); + ctx->last_component = strdup(component); + } + if (msg) { + pr_out("%s", msg); + free(ctx->last_msg); + ctx->last_msg = strdup(msg); + } + } + if (total) { + pr_out_tty(" %3lu%%", (done * 100) / total); + ctx->last_pc = 1; + } else { + ctx->last_pc = 0; + } + fflush(stdout); + ctx->not_first = 1; + + return MNL_CB_STOP; +} + +static int cmd_dev_flash_fds_process(struct cmd_dev_flash_status_ctx *ctx, + struct mnlg_socket *nlg_ntf, + int pipe_r) { - pr_err("Usage: devlink dev flash DEV file PATH [ component NAME ]\n"); + int nlfd = mnlg_socket_get_fd(nlg_ntf); + fd_set fds[3]; + int fdmax; + int i; + int err; + int err2; + + for (i = 0; i < 3; i++) + FD_ZERO(&fds[i]); + FD_SET(pipe_r, &fds[0]); + fdmax = pipe_r + 1; + FD_SET(nlfd, &fds[0]); + if (nlfd >= fdmax) + fdmax = nlfd + 1; + + while (select(fdmax, &fds[0], &fds[1], &fds[2], NULL) < 0) { + if (errno == EINTR) + continue; + pr_err("select() failed\n"); + return -errno; + } + if (FD_ISSET(nlfd, &fds[0])) { + err = _mnlg_socket_recv_run(nlg_ntf, + cmd_dev_flash_status_cb, ctx); + if (err) + return err; + } + if (FD_ISSET(pipe_r, &fds[0])) { + err = read(pipe_r, &err2, sizeof(err2)); + if (err == -1) { + pr_err("Failed to read pipe\n"); + return -errno; + } + if (err2) + return err2; + ctx->flash_done = 1; + } + return 0; } + static int cmd_dev_flash(struct dl *dl) { + struct cmd_dev_flash_status_ctx ctx = {.dl = dl,}; + struct mnlg_socket *nlg_ntf; struct nlmsghdr *nlh; + int pipe_r, pipe_w; + int pipe_fds[2]; + pid_t pid; int err; if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { - cmd_dev_flash_help(); + cmd_dev_help(); return 0; } @@ -2665,7 +3129,50 @@ static int cmd_dev_flash(struct dl *dl) if (err) return err; - return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); + nlg_ntf = mnlg_socket_open(DEVLINK_GENL_NAME, DEVLINK_GENL_VERSION); + if (!nlg_ntf) + return err; + + err = _mnlg_socket_group_add(nlg_ntf, DEVLINK_GENL_MCGRP_CONFIG_NAME); + if (err) + return err; + + err = pipe(pipe_fds); + if (err == -1) + return -errno; + pipe_r = pipe_fds[0]; + pipe_w = pipe_fds[1]; + + pid = fork(); + if (pid == -1) { + close(pipe_r); + close(pipe_w); + return -errno; + } else if (!pid) { + /* In child, just execute the flash and pass returned + * value through pipe once it is done. + */ + int cc; + + close(pipe_r); + err = _mnlg_socket_send(dl->nlg, nlh); + cc = write(pipe_w, &err, sizeof(err)); + close(pipe_w); + exit(cc != sizeof(err)); + } + close(pipe_w); + + do { + err = cmd_dev_flash_fds_process(&ctx, nlg_ntf, pipe_r); + if (err) + goto out; + } while (!ctx.flash_done || (ctx.not_first && !ctx.received_end)); + + err = _mnlg_socket_recv_run(dl->nlg, NULL, NULL); +out: + close(pipe_r); + mnlg_socket_close(nlg_ntf); + return err; } static int cmd_dev(struct dl *dl) @@ -2725,44 +3232,84 @@ static const char *port_flavour_name(uint16_t flavour) return "cpu"; case DEVLINK_PORT_FLAVOUR_DSA: return "dsa"; + case DEVLINK_PORT_FLAVOUR_PCI_PF: + return "pcipf"; + case DEVLINK_PORT_FLAVOUR_PCI_VF: + return "pcivf"; + case DEVLINK_PORT_FLAVOUR_VIRTUAL: + return "virtual"; default: return ""; } } +static void pr_out_port_pfvf_num(struct dl *dl, struct nlattr **tb) +{ + uint16_t fn_num; + + if (tb[DEVLINK_ATTR_PORT_PCI_PF_NUMBER]) { + fn_num = mnl_attr_get_u16(tb[DEVLINK_ATTR_PORT_PCI_PF_NUMBER]); + print_uint(PRINT_ANY, "pfnum", " pfnum %u", fn_num); + } + if (tb[DEVLINK_ATTR_PORT_PCI_VF_NUMBER]) { + fn_num = mnl_attr_get_u16(tb[DEVLINK_ATTR_PORT_PCI_VF_NUMBER]); + print_uint(PRINT_ANY, "vfnum", " vfnum %u", fn_num); + } +} + static void pr_out_port(struct dl *dl, struct nlattr **tb) { struct nlattr *pt_attr = tb[DEVLINK_ATTR_PORT_TYPE]; struct nlattr *dpt_attr = tb[DEVLINK_ATTR_PORT_DESIRED_TYPE]; pr_out_port_handle_start(dl, tb, false); + check_indent_newline(dl); if (pt_attr) { uint16_t port_type = mnl_attr_get_u16(pt_attr); - pr_out_str(dl, "type", port_type_name(port_type)); + print_string(PRINT_ANY, "type", "type %s", + port_type_name(port_type)); if (dpt_attr) { uint16_t des_port_type = mnl_attr_get_u16(dpt_attr); if (port_type != des_port_type) - pr_out_str(dl, "des_type", - port_type_name(des_port_type)); + print_string(PRINT_ANY, "des_type", " des_type %s", + port_type_name(des_port_type)); } } - if (tb[DEVLINK_ATTR_PORT_NETDEV_NAME]) - pr_out_str(dl, "netdev", - mnl_attr_get_str(tb[DEVLINK_ATTR_PORT_NETDEV_NAME])); - if (tb[DEVLINK_ATTR_PORT_IBDEV_NAME]) - pr_out_str(dl, "ibdev", - mnl_attr_get_str(tb[DEVLINK_ATTR_PORT_IBDEV_NAME])); + if (tb[DEVLINK_ATTR_PORT_NETDEV_NAME]) { + print_string(PRINT_ANY, "netdev", " netdev %s", + mnl_attr_get_str(tb[DEVLINK_ATTR_PORT_NETDEV_NAME])); + } + if (tb[DEVLINK_ATTR_PORT_IBDEV_NAME]) { + print_string(PRINT_ANY, "ibdev", " ibdev %s", + mnl_attr_get_str(tb[DEVLINK_ATTR_PORT_IBDEV_NAME])); + } if (tb[DEVLINK_ATTR_PORT_FLAVOUR]) { uint16_t port_flavour = mnl_attr_get_u16(tb[DEVLINK_ATTR_PORT_FLAVOUR]); - pr_out_str(dl, "flavour", port_flavour_name(port_flavour)); + print_string(PRINT_ANY, "flavour", " flavour %s", + port_flavour_name(port_flavour)); + + switch (port_flavour) { + case DEVLINK_PORT_FLAVOUR_PCI_PF: + case DEVLINK_PORT_FLAVOUR_PCI_VF: + pr_out_port_pfvf_num(dl, tb); + break; + default: + break; + } + } + if (tb[DEVLINK_ATTR_PORT_NUMBER]) { + uint32_t port_number; + + port_number = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_NUMBER]); + print_uint(PRINT_ANY, "port", " port %u", port_number); } if (tb[DEVLINK_ATTR_PORT_SPLIT_GROUP]) - pr_out_uint(dl, "split_group", - mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_SPLIT_GROUP])); + print_uint(PRINT_ANY, "split_group", " split_group %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_SPLIT_GROUP])); pr_out_port_handle_end(dl); } @@ -2894,18 +3441,19 @@ static void cmd_sb_help(void) static void pr_out_sb(struct dl *dl, struct nlattr **tb) { pr_out_handle_start_arr(dl, tb); - pr_out_uint(dl, "sb", - mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); - pr_out_uint(dl, "size", - mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_SIZE])); - pr_out_uint(dl, "ing_pools", - mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_INGRESS_POOL_COUNT])); - pr_out_uint(dl, "eg_pools", - mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_EGRESS_POOL_COUNT])); - pr_out_uint(dl, "ing_tcs", - mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_INGRESS_TC_COUNT])); - pr_out_uint(dl, "eg_tcs", - mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_EGRESS_TC_COUNT])); + check_indent_newline(dl); + print_uint(PRINT_ANY, "sb", "sb %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); + print_uint(PRINT_ANY, "size", " size %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_SIZE])); + print_uint(PRINT_ANY, "ing_pools", " ing_pools %u", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_INGRESS_POOL_COUNT])); + print_uint(PRINT_ANY, "eg_pools", " eg_pools %u", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_EGRESS_POOL_COUNT])); + print_uint(PRINT_ANY, "ing_tcs", " ing_tcs %u", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_INGRESS_TC_COUNT])); + print_uint(PRINT_ANY, "eg_tcs", " eg_tcs %u", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_EGRESS_TC_COUNT])); pr_out_handle_end(dl); } @@ -2971,19 +3519,20 @@ static const char *threshold_type_name(uint8_t type) static void pr_out_sb_pool(struct dl *dl, struct nlattr **tb) { pr_out_handle_start_arr(dl, tb); - pr_out_uint(dl, "sb", - mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); - pr_out_uint(dl, "pool", - mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX])); - pr_out_str(dl, "type", - pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE]))); - pr_out_uint(dl, "size", - mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_POOL_SIZE])); - pr_out_str(dl, "thtype", - threshold_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE]))); + check_indent_newline(dl); + print_uint(PRINT_ANY, "sb", "sb %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); + print_uint(PRINT_ANY, "pool", " pool %u", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX])); + print_string(PRINT_ANY, "type", " type %s", + pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE]))); + print_uint(PRINT_ANY, "size", " size %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_POOL_SIZE])); + print_string(PRINT_ANY, "thtype", " thtype %s", + threshold_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE]))); if (tb[DEVLINK_ATTR_SB_POOL_CELL_SIZE]) - pr_out_uint(dl, "cell_size", - mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_POOL_CELL_SIZE])); + print_uint(PRINT_ANY, "cell_size", " cell size %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_POOL_CELL_SIZE])); pr_out_handle_end(dl); } @@ -3063,12 +3612,13 @@ static int cmd_sb_pool(struct dl *dl) static void pr_out_sb_port_pool(struct dl *dl, struct nlattr **tb) { pr_out_port_handle_start_arr(dl, tb, true); - pr_out_uint(dl, "sb", - mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); - pr_out_uint(dl, "pool", - mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX])); - pr_out_uint(dl, "threshold", - mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_THRESHOLD])); + check_indent_newline(dl); + print_uint(PRINT_ANY, "sb", "sb %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); + print_uint(PRINT_ANY, "pool", " pool %u", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX])); + print_uint(PRINT_ANY, "threshold", " threshold %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_THRESHOLD])); pr_out_port_handle_end(dl); } @@ -3161,16 +3711,17 @@ static int cmd_sb_port(struct dl *dl) static void pr_out_sb_tc_bind(struct dl *dl, struct nlattr **tb) { pr_out_port_handle_start_arr(dl, tb, true); - pr_out_uint(dl, "sb", - mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); - pr_out_uint(dl, "tc", - mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_TC_INDEX])); - pr_out_str(dl, "type", - pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE]))); - pr_out_uint(dl, "pool", - mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX])); - pr_out_uint(dl, "threshold", - mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_THRESHOLD])); + check_indent_newline(dl); + print_uint(PRINT_ANY, "sb", "sb %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); + print_uint(PRINT_ANY, "tc", " tc %u", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_TC_INDEX])); + print_string(PRINT_ANY, "type", " type %s", + pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE]))); + print_uint(PRINT_ANY, "pool", " pool %u", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX])); + print_uint(PRINT_ANY, "threshold", " threshold %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_THRESHOLD])); pr_out_port_handle_end(dl); } @@ -3376,7 +3927,7 @@ static void pr_out_occ_show_item_list(const char *label, struct list_head *list, occ_item->bound_pool_index); else pr_out_sp(7, "%2u:", occ_item->index); - pr_out_sp(15, "%7u/%u", occ_item->cur, occ_item->max); + pr_out_sp(21, "%10u/%u", occ_item->cur, occ_item->max); if (i++ % 4 == 0) pr_out("\n"); } @@ -3391,20 +3942,18 @@ static void pr_out_json_occ_show_item_list(struct dl *dl, const char *label, struct occ_item *occ_item; char buf[32]; - jsonw_name(dl->jw, label); - jsonw_start_object(dl->jw); + open_json_object(label); list_for_each_entry(occ_item, list, list) { sprintf(buf, "%u", occ_item->index); - jsonw_name(dl->jw, buf); - jsonw_start_object(dl->jw); + open_json_object(buf); if (bound_pool) - jsonw_uint_field(dl->jw, "bound_pool", - occ_item->bound_pool_index); - jsonw_uint_field(dl->jw, "current", occ_item->cur); - jsonw_uint_field(dl->jw, "max", occ_item->max); - jsonw_end_object(dl->jw); + print_uint(PRINT_JSON, "bound_pool", NULL, + occ_item->bound_pool_index); + print_uint(PRINT_JSON, "current", NULL, occ_item->cur); + print_uint(PRINT_JSON, "max", NULL, occ_item->max); + close_json_object(); } - jsonw_end_object(dl->jw); + close_json_object(); } static void pr_out_occ_show_port(struct dl *dl, struct occ_port *occ_port) @@ -3666,6 +4215,22 @@ static const char *cmd_name(uint8_t cmd) case DEVLINK_CMD_REGION_SET: return "set"; case DEVLINK_CMD_REGION_NEW: return "new"; case DEVLINK_CMD_REGION_DEL: return "del"; + case DEVLINK_CMD_FLASH_UPDATE: return "begin"; + case DEVLINK_CMD_FLASH_UPDATE_END: return "end"; + case DEVLINK_CMD_FLASH_UPDATE_STATUS: return "status"; + case DEVLINK_CMD_HEALTH_REPORTER_RECOVER: return "status"; + case DEVLINK_CMD_TRAP_GET: return "get"; + case DEVLINK_CMD_TRAP_SET: return "set"; + case DEVLINK_CMD_TRAP_NEW: return "new"; + case DEVLINK_CMD_TRAP_DEL: return "del"; + case DEVLINK_CMD_TRAP_GROUP_GET: return "get"; + case DEVLINK_CMD_TRAP_GROUP_SET: return "set"; + case DEVLINK_CMD_TRAP_GROUP_NEW: return "new"; + case DEVLINK_CMD_TRAP_GROUP_DEL: return "del"; + case DEVLINK_CMD_TRAP_POLICER_GET: return "get"; + case DEVLINK_CMD_TRAP_POLICER_SET: return "set"; + case DEVLINK_CMD_TRAP_POLICER_NEW: return "new"; + case DEVLINK_CMD_TRAP_POLICER_DEL: return "del"; default: return ""; } } @@ -3694,13 +4259,48 @@ static const char *cmd_obj(uint8_t cmd) case DEVLINK_CMD_REGION_NEW: case DEVLINK_CMD_REGION_DEL: return "region"; + case DEVLINK_CMD_FLASH_UPDATE: + case DEVLINK_CMD_FLASH_UPDATE_END: + case DEVLINK_CMD_FLASH_UPDATE_STATUS: + return "flash"; + case DEVLINK_CMD_HEALTH_REPORTER_RECOVER: + return "health"; + case DEVLINK_CMD_TRAP_GET: + case DEVLINK_CMD_TRAP_SET: + case DEVLINK_CMD_TRAP_NEW: + case DEVLINK_CMD_TRAP_DEL: + return "trap"; + case DEVLINK_CMD_TRAP_GROUP_GET: + case DEVLINK_CMD_TRAP_GROUP_SET: + case DEVLINK_CMD_TRAP_GROUP_NEW: + case DEVLINK_CMD_TRAP_GROUP_DEL: + return "trap-group"; + case DEVLINK_CMD_TRAP_POLICER_GET: + case DEVLINK_CMD_TRAP_POLICER_SET: + case DEVLINK_CMD_TRAP_POLICER_NEW: + case DEVLINK_CMD_TRAP_POLICER_DEL: + return "trap-policer"; default: return ""; } } static void pr_out_mon_header(uint8_t cmd) { - pr_out("[%s,%s] ", cmd_obj(cmd), cmd_name(cmd)); + if (!is_json_context()) { + pr_out("[%s,%s] ", cmd_obj(cmd), cmd_name(cmd)); + } else { + open_json_object(NULL); + print_string(PRINT_JSON, "command", NULL, cmd_name(cmd)); + open_json_object(cmd_obj(cmd)); + } +} + +static void pr_out_mon_footer(void) +{ + if (is_json_context()) { + close_json_object(); + close_json_object(); + } } static bool cmd_filter_check(struct dl *dl, uint8_t cmd) @@ -3718,7 +4318,37 @@ static bool cmd_filter_check(struct dl *dl, uint8_t cmd) return false; } +static void pr_out_flash_update(struct dl *dl, struct nlattr **tb) +{ + __pr_out_handle_start(dl, tb, true, false); + + if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG]) { + check_indent_newline(dl); + print_string(PRINT_ANY, "msg", "msg %s", + mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG])); + } + if (tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT]) { + check_indent_newline(dl); + print_string(PRINT_ANY, "component", "component %s", + mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT])); + } + + if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE]) + pr_out_u64(dl, "done", + mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE])); + + if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL]) + pr_out_u64(dl, "total", + mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL])); + + pr_out_handle_end(dl); +} + static void pr_out_region(struct dl *dl, struct nlattr **tb); +static void pr_out_health(struct dl *dl, struct nlattr **tb_health); +static void pr_out_trap(struct dl *dl, struct nlattr **tb, bool array); +static void pr_out_trap_group(struct dl *dl, struct nlattr **tb, bool array); +static void pr_out_trap_policer(struct dl *dl, struct nlattr **tb, bool array); static int cmd_mon_show_cb(const struct nlmsghdr *nlh, void *data) { @@ -3739,7 +4369,8 @@ static int cmd_mon_show_cb(const struct nlmsghdr *nlh, void *data) if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME]) return MNL_CB_ERROR; pr_out_mon_header(genl->cmd); - pr_out_dev(dl, tb); + pr_out_handle(dl, tb); + pr_out_mon_footer(); break; case DEVLINK_CMD_PORT_GET: /* fall through */ case DEVLINK_CMD_PORT_SET: /* fall through */ @@ -3751,6 +4382,7 @@ static int cmd_mon_show_cb(const struct nlmsghdr *nlh, void *data) return MNL_CB_ERROR; pr_out_mon_header(genl->cmd); pr_out_port(dl, tb); + pr_out_mon_footer(); break; case DEVLINK_CMD_PARAM_GET: /* fall through */ case DEVLINK_CMD_PARAM_SET: /* fall through */ @@ -3762,6 +4394,7 @@ static int cmd_mon_show_cb(const struct nlmsghdr *nlh, void *data) return MNL_CB_ERROR; pr_out_mon_header(genl->cmd); pr_out_param(dl, tb, false); + pr_out_mon_footer(); break; case DEVLINK_CMD_REGION_GET: /* fall through */ case DEVLINK_CMD_REGION_SET: /* fall through */ @@ -3773,6 +4406,69 @@ static int cmd_mon_show_cb(const struct nlmsghdr *nlh, void *data) return MNL_CB_ERROR; pr_out_mon_header(genl->cmd); pr_out_region(dl, tb); + pr_out_mon_footer(); + break; + case DEVLINK_CMD_FLASH_UPDATE: /* fall through */ + case DEVLINK_CMD_FLASH_UPDATE_END: /* fall through */ + case DEVLINK_CMD_FLASH_UPDATE_STATUS: + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME]) + return MNL_CB_ERROR; + pr_out_mon_header(genl->cmd); + pr_out_flash_update(dl, tb); + pr_out_mon_footer(); + break; + case DEVLINK_CMD_HEALTH_REPORTER_RECOVER: + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_HEALTH_REPORTER]) + return MNL_CB_ERROR; + pr_out_mon_header(genl->cmd); + pr_out_health(dl, tb); + pr_out_mon_footer(); + break; + case DEVLINK_CMD_TRAP_GET: /* fall through */ + case DEVLINK_CMD_TRAP_SET: /* fall through */ + case DEVLINK_CMD_TRAP_NEW: /* fall through */ + case DEVLINK_CMD_TRAP_DEL: + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_TRAP_NAME] || + !tb[DEVLINK_ATTR_TRAP_TYPE] || + !tb[DEVLINK_ATTR_TRAP_ACTION] || + !tb[DEVLINK_ATTR_TRAP_GROUP_NAME] || + !tb[DEVLINK_ATTR_TRAP_METADATA] || + !tb[DEVLINK_ATTR_STATS]) + return MNL_CB_ERROR; + pr_out_mon_header(genl->cmd); + pr_out_trap(dl, tb, false); + pr_out_mon_footer(); + break; + case DEVLINK_CMD_TRAP_GROUP_GET: /* fall through */ + case DEVLINK_CMD_TRAP_GROUP_SET: /* fall through */ + case DEVLINK_CMD_TRAP_GROUP_NEW: /* fall through */ + case DEVLINK_CMD_TRAP_GROUP_DEL: + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_TRAP_GROUP_NAME] || + !tb[DEVLINK_ATTR_STATS]) + return MNL_CB_ERROR; + pr_out_mon_header(genl->cmd); + pr_out_trap_group(dl, tb, false); + pr_out_mon_footer(); + break; + case DEVLINK_CMD_TRAP_POLICER_GET: /* fall through */ + case DEVLINK_CMD_TRAP_POLICER_SET: /* fall through */ + case DEVLINK_CMD_TRAP_POLICER_NEW: /* fall through */ + case DEVLINK_CMD_TRAP_POLICER_DEL: /* fall through */ + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_TRAP_POLICER_ID] || + !tb[DEVLINK_ATTR_TRAP_POLICER_RATE] || + !tb[DEVLINK_ATTR_TRAP_POLICER_BURST]) + return MNL_CB_ERROR; + pr_out_mon_header(genl->cmd); + pr_out_trap_policer(dl, tb, false); break; } return MNL_CB_OK; @@ -3787,7 +4483,11 @@ static int cmd_mon_show(struct dl *dl) while ((cur_obj = dl_argv_index(dl, index++))) { if (strcmp(cur_obj, "all") != 0 && strcmp(cur_obj, "dev") != 0 && - strcmp(cur_obj, "port") != 0) { + strcmp(cur_obj, "port") != 0 && + strcmp(cur_obj, "health") != 0 && + strcmp(cur_obj, "trap") != 0 && + strcmp(cur_obj, "trap-group") != 0 && + strcmp(cur_obj, "trap-policer") != 0) { pr_err("Unknown object \"%s\"\n", cur_obj); return -EINVAL; } @@ -3795,7 +4495,11 @@ static int cmd_mon_show(struct dl *dl) err = _mnlg_socket_group_add(dl->nlg, DEVLINK_GENL_MCGRP_CONFIG_NAME); if (err) return err; - err = _mnlg_socket_recv_run(dl->nlg, cmd_mon_show_cb, dl); + open_json_object(NULL); + open_json_array(PRINT_JSON, "mon"); + err = _mnlg_socket_recv_run_intr(dl->nlg, cmd_mon_show_cb, dl); + close_json_array(PRINT_JSON, NULL); + close_json_object(); if (err) return err; return 0; @@ -3804,7 +4508,7 @@ static int cmd_mon_show(struct dl *dl) static void cmd_mon_help(void) { pr_err("Usage: devlink monitor [ all | OBJECT-LIST ]\n" - "where OBJECT-LIST := { dev | port }\n"); + "where OBJECT-LIST := { dev | port | health | trap | trap-group | trap-policer }\n"); } static int cmd_mon(struct dl *dl) @@ -3812,12 +4516,8 @@ static int cmd_mon(struct dl *dl) if (dl_argv_match(dl, "help")) { cmd_mon_help(); return 0; - } else if (dl_no_arg(dl)) { - dl_arg_inc(dl); - return cmd_mon_show(dl); } - pr_err("Command \"%s\" not found\n", dl_argv(dl)); - return -ENOENT; + return cmd_mon_show(dl); } struct dpipe_field { @@ -4135,13 +4835,15 @@ static void pr_out_dpipe_fields(struct dpipe_ctx *ctx, for (i = 0; i < field_count; i++) { field = &fields[i]; pr_out_entry_start(ctx->dl); - pr_out_str(ctx->dl, "name", field->name); + check_indent_newline(ctx->dl); + print_string(PRINT_ANY, "name", "name %s", field->name); if (ctx->dl->verbose) - pr_out_uint(ctx->dl, "id", field->id); - pr_out_uint(ctx->dl, "bitwidth", field->bitwidth); - if (field->mapping_type) - pr_out_str(ctx->dl, "mapping_type", - dpipe_field_mapping_e2s(field->mapping_type)); + print_uint(PRINT_ANY, "id", " id %u", field->id); + print_uint(PRINT_ANY, "bitwidth", " bitwidth %u", field->bitwidth); + if (field->mapping_type) { + print_string(PRINT_ANY, "mapping_type", " mapping_type %s", + dpipe_field_mapping_e2s(field->mapping_type)); + } pr_out_entry_end(ctx->dl); } } @@ -4151,11 +4853,11 @@ pr_out_dpipe_header(struct dpipe_ctx *ctx, struct nlattr **tb, struct dpipe_header *header, bool global) { pr_out_handle_start_arr(ctx->dl, tb); - pr_out_str(ctx->dl, "name", header->name); + check_indent_newline(ctx->dl); + print_string(PRINT_ANY, "name", "name %s", header->name); if (ctx->dl->verbose) { - pr_out_uint(ctx->dl, "id", header->id); - pr_out_str(ctx->dl, "global", - global ? "true" : "false"); + print_uint(PRINT_ANY, "id", " id %u", header->id); + print_bool(PRINT_ANY, "global", " global %s", global); } pr_out_array_start(ctx->dl, "field"); pr_out_dpipe_fields(ctx, header->fields, @@ -4333,15 +5035,19 @@ static int cmd_dpipe_headers_show(struct dl *dl) return err; } -static void cmd_dpipe_header_help(void) +static void cmd_dpipe_help(void) { - pr_err("Usage: devlink dpipe headers show DEV\n"); + pr_err("Usage: devlink dpipe table show DEV [ name TABLE_NAME ]\n"); + pr_err(" devlink dpipe table set DEV name TABLE_NAME\n"); + pr_err(" [ counters_enabled { true | false } ]\n"); + pr_err(" devlink dpipe table dump DEV name TABLE_NAME\n"); + pr_err(" devlink dpipe header show DEV\n"); } static int cmd_dpipe_header(struct dl *dl) { if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { - cmd_dpipe_header_help(); + cmd_dpipe_help(); return 0; } else if (dl_argv_match(dl, "show")) { dl_arg_inc(dl); @@ -4379,20 +5085,21 @@ static void pr_out_dpipe_action(struct dpipe_action *action, struct dpipe_op_info *op_info = &action->info; const char *mapping; - pr_out_str(ctx->dl, "type", - dpipe_action_type_e2s(action->type)); - pr_out_str(ctx->dl, "header", - dpipe_header_id2s(ctx, op_info->header_id, - op_info->header_global)); - pr_out_str(ctx->dl, "field", - dpipe_field_id2s(ctx, op_info->header_id, - op_info->field_id, - op_info->header_global)); + check_indent_newline(ctx->dl); + print_string(PRINT_ANY, "type", "type %s", + dpipe_action_type_e2s(action->type)); + print_string(PRINT_ANY, "header", " header %s", + dpipe_header_id2s(ctx, op_info->header_id, + op_info->header_global)); + print_string(PRINT_ANY, "field", " field %s", + dpipe_field_id2s(ctx, op_info->header_id, + op_info->field_id, + op_info->header_global)); mapping = dpipe_mapping_get(ctx, op_info->header_id, op_info->field_id, op_info->header_global); if (mapping) - pr_out_str(ctx->dl, "mapping", mapping); + print_string(PRINT_ANY, "mapping", " mapping %s", mapping); } static int dpipe_action_parse(struct dpipe_action *action, struct nlattr *nl) @@ -4461,20 +5168,21 @@ static void pr_out_dpipe_match(struct dpipe_match *match, struct dpipe_op_info *op_info = &match->info; const char *mapping; - pr_out_str(ctx->dl, "type", - dpipe_match_type_e2s(match->type)); - pr_out_str(ctx->dl, "header", - dpipe_header_id2s(ctx, op_info->header_id, - op_info->header_global)); - pr_out_str(ctx->dl, "field", - dpipe_field_id2s(ctx, op_info->header_id, - op_info->field_id, - op_info->header_global)); + check_indent_newline(ctx->dl); + print_string(PRINT_ANY, "type", "type %s", + dpipe_match_type_e2s(match->type)); + print_string(PRINT_ANY, "header", " header %s", + dpipe_header_id2s(ctx, op_info->header_id, + op_info->header_global)); + print_string(PRINT_ANY, "field", " field %s", + dpipe_field_id2s(ctx, op_info->header_id, + op_info->field_id, + op_info->header_global)); mapping = dpipe_mapping_get(ctx, op_info->header_id, op_info->field_id, op_info->header_global); if (mapping) - pr_out_str(ctx->dl, "mapping", mapping); + print_string(PRINT_ANY, "mapping", " mapping %s", mapping); } static int dpipe_match_parse(struct dpipe_match *match, @@ -4579,7 +5287,8 @@ resource_path_print(struct dl *dl, struct resources *resources, path -= strlen(del); memcpy(path, del, strlen(del)); } - pr_out_str(dl, "resource_path", path); + check_indent_newline(dl); + print_string(PRINT_ANY, "resource_path", "resource_path %s", path); free(path); } @@ -4624,16 +5333,17 @@ static int dpipe_table_show(struct dpipe_ctx *ctx, struct nlattr *nl) if (!ctx->print_tables) return 0; - pr_out_str(ctx->dl, "name", table->name); - pr_out_uint(ctx->dl, "size", size); - pr_out_str(ctx->dl, "counters_enabled", - counters_enabled ? "true" : "false"); + check_indent_newline(ctx->dl); + print_string(PRINT_ANY, "name", "name %s", table->name); + print_uint(PRINT_ANY, "size", " size %u", size); + print_bool(PRINT_ANY, "counters_enabled", " counters_enabled %s", counters_enabled); if (resource_valid) { resource_units = mnl_attr_get_u32(nla_table[DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_UNITS]); resource_path_print(ctx->dl, ctx->resources, table->resource_id); - pr_out_uint(ctx->dl, "resource_units", resource_units); + print_uint(PRINT_ANY, "resource_units", " resource_units %u", + resource_units); } pr_out_array_start(ctx->dl, "match"); @@ -4804,7 +5514,8 @@ static void dpipe_field_printer_ipv4_addr(struct dpipe_ctx *ctx, struct in_addr ip_addr; ip_addr.s_addr = htonl(*(uint32_t *)value); - pr_out_str(ctx->dl, dpipe_value_type_e2s(type), inet_ntoa(ip_addr)); + check_indent_newline(ctx->dl); + print_string_name_value(dpipe_value_type_e2s(type), inet_ntoa(ip_addr)); } static void @@ -4812,8 +5523,9 @@ dpipe_field_printer_ethernet_addr(struct dpipe_ctx *ctx, enum dpipe_value_type type, void *value) { - pr_out_str(ctx->dl, dpipe_value_type_e2s(type), - ether_ntoa((struct ether_addr *)value)); + check_indent_newline(ctx->dl); + print_string_name_value(dpipe_value_type_e2s(type), + ether_ntoa((struct ether_addr *)value)); } static void dpipe_field_printer_ipv6_addr(struct dpipe_ctx *ctx, @@ -4823,7 +5535,8 @@ static void dpipe_field_printer_ipv6_addr(struct dpipe_ctx *ctx, char str[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, value, str, INET6_ADDRSTRLEN); - pr_out_str(ctx->dl, dpipe_value_type_e2s(type), str); + check_indent_newline(ctx->dl); + print_string_name_value(dpipe_value_type_e2s(type), str); } static struct dpipe_field_printer dpipe_field_printers_ipv4[] = { @@ -4913,7 +5626,8 @@ static void __pr_out_entry_value(struct dpipe_ctx *ctx, if (value_len == sizeof(uint32_t)) { uint32_t *value_32 = value; - pr_out_uint(ctx->dl, dpipe_value_type_e2s(type), *value_32); + check_indent_newline(ctx->dl); + print_uint_name_value(dpipe_value_type_e2s(type), *value_32); } } @@ -4934,7 +5648,8 @@ static void pr_out_dpipe_entry_value(struct dpipe_ctx *ctx, if (mapping) { value_mapping = mnl_attr_get_u32(nla_match_value[DEVLINK_ATTR_DPIPE_VALUE_MAPPING]); - pr_out_uint(ctx->dl, "mapping_value", value_mapping); + check_indent_newline(ctx->dl); + print_uint(PRINT_ANY, "mapping_value", "mapping_value %u", value_mapping); } if (mask) { @@ -5051,12 +5766,13 @@ static int dpipe_entry_show(struct dpipe_ctx *ctx, struct nlattr *nl) return -EINVAL; } + check_indent_newline(ctx->dl); entry_index = mnl_attr_get_u32(nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_INDEX]); - pr_out_uint(ctx->dl, "index", entry_index); + print_uint(PRINT_ANY, "index", "index %u", entry_index); if (nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_COUNTER]) { counter = mnl_attr_get_u64(nla_entry[DEVLINK_ATTR_DPIPE_ENTRY_COUNTER]); - pr_out_uint(ctx->dl, "counter", counter); + print_uint(PRINT_ANY, "counter", " counter %u", counter); } pr_out_array_start(ctx->dl, "match_value"); @@ -5147,16 +5863,10 @@ out: return err; } -static void cmd_dpipe_table_help(void) -{ - pr_err("Usage: devlink dpipe table [ OBJECT-LIST ]\n" - "where OBJECT-LIST := { show | set | dump }\n"); -} - static int cmd_dpipe_table(struct dl *dl) { if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { - cmd_dpipe_table_help(); + cmd_dpipe_help(); return 0; } else if (dl_argv_match(dl, "show")) { dl_arg_inc(dl); @@ -5172,12 +5882,6 @@ static int cmd_dpipe_table(struct dl *dl) return -ENOENT; } -static void cmd_dpipe_help(void) -{ - pr_err("Usage: devlink dpipe [ OBJECT-LIST ]\n" - "where OBJECT-LIST := { header | table }\n"); -} - static int cmd_dpipe(struct dl *dl) { if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { @@ -5302,20 +6006,24 @@ static void resource_show(struct resource *resource, struct dl *dl = ctx->dl; bool array = false; - pr_out_str(dl, "name", resource->name); + check_indent_newline(dl); + print_string(PRINT_ANY, "name", "name %s", resource->name); if (dl->verbose) resource_path_print(dl, ctx->resources, resource->id); pr_out_u64(dl, "size", resource->size); if (resource->size != resource->size_new) pr_out_u64(dl, "size_new", resource->size_new); if (resource->occ_valid) - pr_out_uint(dl, "occ", resource->size_occ); - pr_out_str(dl, "unit", resource_unit_str_get(resource->unit)); + print_uint(PRINT_ANY, "occ", " occ %u", resource->size_occ); + print_string(PRINT_ANY, "unit", " unit %s", + resource_unit_str_get(resource->unit)); if (resource->size_min != resource->size_max) { - pr_out_uint(dl, "size_min", resource->size_min); + print_uint(PRINT_ANY, "size_min", " size_min %u", + resource->size_min); pr_out_u64(dl, "size_max", resource->size_max); - pr_out_uint(dl, "size_gran", resource->size_gran); + print_uint(PRINT_ANY, "size_gran", " size_gran %u", + resource->size_gran); } list_for_each_entry(table, &ctx->tables->table_list, list) @@ -5326,14 +6034,17 @@ static void resource_show(struct resource *resource, if (array) pr_out_array_start(dl, "dpipe_tables"); else - pr_out_str(dl, "dpipe_tables", "none"); + print_string(PRINT_ANY, "dpipe_tables", " dpipe_tables none", + "none"); list_for_each_entry(table, &ctx->tables->table_list, list) { if (table->resource_id != resource->id || !table->resource_valid) continue; pr_out_entry_start(dl); - pr_out_str(dl, "table_name", table->name); + check_indent_newline(dl); + print_string(PRINT_ANY, "table_name", "table_name %s", + table->name); pr_out_entry_end(dl); } if (array) @@ -5342,9 +6053,11 @@ static void resource_show(struct resource *resource, if (list_empty(&resource->resource_list)) return; - if (ctx->pending_change) - pr_out_str(dl, "size_valid", resource->size_valid ? - "true" : "false"); + if (ctx->pending_change) { + check_indent_newline(dl); + print_string(PRINT_ANY, "size_valid", "size_valid %s", + resource->size_valid ? "true" : "false"); + } pr_out_array_start(dl, "resources"); list_for_each_entry(child_resource, &resource->resource_list, list) { pr_out_entry_start(dl); @@ -5559,39 +6272,33 @@ static void pr_out_region_handle_start(struct dl *dl, struct nlattr **tb) char buf[256]; sprintf(buf, "%s/%s/%s", bus_name, dev_name, region_name); - if (dl->json_output) { - jsonw_name(dl->jw, buf); - jsonw_start_object(dl->jw); - } else { + if (dl->json_output) + open_json_object(buf); + else pr_out("%s:", buf); - } } static void pr_out_region_handle_end(struct dl *dl) { if (dl->json_output) - jsonw_end_object(dl->jw); + close_json_object(); else pr_out("\n"); } static void pr_out_region_snapshots_start(struct dl *dl, bool array) { - if (dl->json_output) { - jsonw_name(dl->jw, "snapshot"); - jsonw_start_array(dl->jw); - } else { - if (g_indent_newline) - pr_out("snapshot %s", array ? "[" : ""); - else - pr_out(" snapshot %s", array ? "[" : ""); - } + __pr_out_indent_newline(dl); + if (dl->json_output) + open_json_array(PRINT_JSON, "snapshot"); + else + pr_out("snapshot %s", array ? "[" : ""); } static void pr_out_region_snapshots_end(struct dl *dl, bool array) { if (dl->json_output) - jsonw_end_array(dl->jw); + close_json_array(PRINT_JSON, NULL); else if (array) pr_out("]"); } @@ -5606,7 +6313,7 @@ static void pr_out_region_snapshots_id(struct dl *dl, struct nlattr **tb, int in snapshot_id = mnl_attr_get_u32(tb[DEVLINK_ATTR_REGION_SNAPSHOT_ID]); if (dl->json_output) - jsonw_uint(dl->jw, snapshot_id); + print_uint(PRINT_JSON, NULL, NULL, snapshot_id); else pr_out("%s%u", index ? " " : "", snapshot_id); } @@ -5783,10 +6490,47 @@ static int cmd_region_read(struct dl *dl) return err; } +static int cmd_region_snapshot_new_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct dl *dl = data; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_REGION_NAME] || + !tb[DEVLINK_ATTR_REGION_SNAPSHOT_ID]) + return MNL_CB_ERROR; + + pr_out_region(dl, tb); + + return MNL_CB_OK; +} + +static int cmd_region_snapshot_new(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_REGION_NEW, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE_REGION, + DL_OPT_REGION_SNAPSHOT_ID); + if (err) + return err; + + pr_out_section_start(dl, "regions"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_region_snapshot_new_cb, dl); + pr_out_section_end(dl); + return err; +} + static void cmd_region_help(void) { pr_err("Usage: devlink region show [ DEV/REGION ]\n"); pr_err(" devlink region del DEV/REGION snapshot SNAPSHOT_ID\n"); + pr_err(" devlink region new DEV/REGION snapshot SNAPSHOT_ID\n"); pr_err(" devlink region dump DEV/REGION [ snapshot SNAPSHOT_ID ]\n"); pr_err(" devlink region read DEV/REGION [ snapshot SNAPSHOT_ID ] address ADDRESS length LENGTH\n"); } @@ -5810,17 +6554,38 @@ static int cmd_region(struct dl *dl) } else if (dl_argv_match(dl, "read")) { dl_arg_inc(dl); return cmd_region_read(dl); + } else if (dl_argv_match(dl, "new")) { + dl_arg_inc(dl); + return cmd_region_snapshot_new(dl); } pr_err("Command \"%s\" not found\n", dl_argv(dl)); return -ENOENT; } -static int cmd_health_recover(struct dl *dl) +static int cmd_health_set_params(struct dl *dl) { struct nlmsghdr *nlh; int err; - nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_HEALTH_REPORTER_RECOVER, + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_HEALTH_REPORTER_SET, + NLM_F_REQUEST | NLM_F_ACK); + err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_HEALTH_REPORTER_NAME, + DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD | + DL_OPT_HEALTH_REPORTER_AUTO_RECOVER | + DL_OPT_HEALTH_REPORTER_AUTO_DUMP); + if (err) + return err; + + dl_opts_put(nlh, dl); + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_health_dump_clear(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_HEALTH_REPORTER_DUMP_CLEAR, NLM_F_REQUEST | NLM_F_ACK); err = dl_argv_parse_put(nlh, dl, @@ -5832,60 +6597,364 @@ static int cmd_health_recover(struct dl *dl) return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); } -enum devlink_health_reporter_state { - DEVLINK_HEALTH_REPORTER_STATE_HEALTHY, - DEVLINK_HEALTH_REPORTER_STATE_ERROR, -}; - -static const char *health_state_name(uint8_t state) +static int fmsg_value_show(struct dl *dl, int type, struct nlattr *nl_data) { - switch (state) { - case DEVLINK_HEALTH_REPORTER_STATE_HEALTHY: - return HEALTH_REPORTER_STATE_HEALTHY_STR; - case DEVLINK_HEALTH_REPORTER_STATE_ERROR: - return HEALTH_REPORTER_STATE_ERROR_STR; + uint8_t *data; + uint32_t len; + + check_indent_newline(dl); + switch (type) { + case MNL_TYPE_FLAG: + print_bool(PRINT_ANY, NULL, "%s", mnl_attr_get_u8(nl_data)); + break; + case MNL_TYPE_U8: + print_uint(PRINT_ANY, NULL, "%u", mnl_attr_get_u8(nl_data)); + break; + case MNL_TYPE_U16: + print_uint(PRINT_ANY, NULL, "%u", mnl_attr_get_u16(nl_data)); + break; + case MNL_TYPE_U32: + print_uint(PRINT_ANY, NULL, "%u", mnl_attr_get_u32(nl_data)); + break; + case MNL_TYPE_U64: + print_u64(PRINT_ANY, NULL, "%"PRIu64, mnl_attr_get_u64(nl_data)); + break; + case MNL_TYPE_NUL_STRING: + print_string(PRINT_ANY, NULL, "%s", mnl_attr_get_str(nl_data)); + break; + case MNL_TYPE_BINARY: + len = mnl_attr_get_payload_len(nl_data); + data = mnl_attr_get_payload(nl_data); + pr_out_binary_value(dl, data, len); + break; default: - return ""; + return -EINVAL; } + return MNL_CB_OK; } -static void format_logtime(uint64_t time_ms, char *ts_date, char *ts_time) +static void pr_out_fmsg_name(struct dl *dl, char **name) { - struct sysinfo s_info; - struct tm *info; - time_t now, sec; - int err; + if (!*name) + return; - time(&now); - info = localtime(&now); - err = sysinfo(&s_info); - if (err) - goto out; - /* Subtract uptime in sec from now yields the time of system - * uptime. To this, add time_ms which is the amount of - * milliseconds elapsed between uptime and the dump taken. - */ - sec = now - s_info.uptime + time_ms / 1000; - info = localtime(&sec); -out: - strftime(ts_date, HEALTH_REPORTER_TIMESTAMP_FMT_LEN, "%Y-%m-%d", info); - strftime(ts_time, HEALTH_REPORTER_TIMESTAMP_FMT_LEN, "%H:%M:%S", info); + pr_out_name(dl, *name); + free(*name); + *name = NULL; } -static void pr_out_health(struct dl *dl, struct nlattr **tb_health) +struct nest_entry { + int attr_type; + struct list_head list; +}; + +struct fmsg_cb_data { + char *name; + struct dl *dl; + uint8_t value_type; + struct list_head entry_list; +}; + +static int cmd_fmsg_nest_queue(struct fmsg_cb_data *fmsg_data, + uint8_t *attr_value, bool insert) { - struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; - enum devlink_health_reporter_state state; - const struct nlattr *attr; - uint64_t time_ms; - int err; + struct nest_entry *entry; - err = mnl_attr_parse_nested(tb_health[DEVLINK_ATTR_HEALTH_REPORTER], - attr_cb, tb); - if (err != MNL_CB_OK) - return; + if (insert) { + entry = malloc(sizeof(struct nest_entry)); + if (!entry) + return -ENOMEM; - if (!tb[DEVLINK_ATTR_HEALTH_REPORTER_NAME] || + entry->attr_type = *attr_value; + list_add(&entry->list, &fmsg_data->entry_list); + } else { + if (list_empty(&fmsg_data->entry_list)) + return MNL_CB_ERROR; + entry = list_first_entry(&fmsg_data->entry_list, + struct nest_entry, list); + *attr_value = entry->attr_type; + list_del(&entry->list); + free(entry); + } + return MNL_CB_OK; +} + +static void pr_out_fmsg_group_start(struct dl *dl, char **name) +{ + __pr_out_newline(); + pr_out_fmsg_name(dl, name); + __pr_out_newline(); + __pr_out_indent_inc(); +} + +static void pr_out_fmsg_group_end(struct dl *dl) +{ + __pr_out_newline(); + __pr_out_indent_dec(); +} + +static void pr_out_fmsg_start_object(struct dl *dl, char **name) +{ + if (dl->json_output) { + pr_out_fmsg_name(dl, name); + open_json_object(NULL); + } else { + pr_out_fmsg_group_start(dl, name); + } +} + +static void pr_out_fmsg_end_object(struct dl *dl) +{ + if (dl->json_output) + close_json_object(); + else + pr_out_fmsg_group_end(dl); +} + +static void pr_out_fmsg_start_array(struct dl *dl, char **name) +{ + if (dl->json_output) { + pr_out_fmsg_name(dl, name); + open_json_array(PRINT_JSON, NULL); + } else { + pr_out_fmsg_group_start(dl, name); + } +} + +static void pr_out_fmsg_end_array(struct dl *dl) +{ + if (dl->json_output) + close_json_array(PRINT_JSON, NULL); + else + pr_out_fmsg_group_end(dl); +} + +static int cmd_fmsg_nest(struct fmsg_cb_data *fmsg_data, uint8_t nest_value, + bool start) +{ + struct dl *dl = fmsg_data->dl; + uint8_t value = nest_value; + int err; + + err = cmd_fmsg_nest_queue(fmsg_data, &value, start); + if (err != MNL_CB_OK) + return err; + + switch (value) { + case DEVLINK_ATTR_FMSG_OBJ_NEST_START: + if (start) + pr_out_fmsg_start_object(dl, &fmsg_data->name); + else + pr_out_fmsg_end_object(dl); + break; + case DEVLINK_ATTR_FMSG_PAIR_NEST_START: + break; + case DEVLINK_ATTR_FMSG_ARR_NEST_START: + if (start) + pr_out_fmsg_start_array(dl, &fmsg_data->name); + else + pr_out_fmsg_end_array(dl); + break; + default: + return -EINVAL; + } + return MNL_CB_OK; +} + +static int cmd_fmsg_object_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct fmsg_cb_data *fmsg_data = data; + struct dl *dl = fmsg_data->dl; + struct nlattr *nla_object; + int attr_type; + int err; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_FMSG]) + return MNL_CB_ERROR; + + mnl_attr_for_each_nested(nla_object, tb[DEVLINK_ATTR_FMSG]) { + attr_type = mnl_attr_get_type(nla_object); + switch (attr_type) { + case DEVLINK_ATTR_FMSG_OBJ_NEST_START: + case DEVLINK_ATTR_FMSG_PAIR_NEST_START: + case DEVLINK_ATTR_FMSG_ARR_NEST_START: + err = cmd_fmsg_nest(fmsg_data, attr_type, true); + if (err != MNL_CB_OK) + return err; + break; + case DEVLINK_ATTR_FMSG_NEST_END: + err = cmd_fmsg_nest(fmsg_data, attr_type, false); + if (err != MNL_CB_OK) + return err; + break; + case DEVLINK_ATTR_FMSG_OBJ_NAME: + free(fmsg_data->name); + fmsg_data->name = strdup(mnl_attr_get_str(nla_object)); + if (!fmsg_data->name) + return -ENOMEM; + break; + case DEVLINK_ATTR_FMSG_OBJ_VALUE_TYPE: + fmsg_data->value_type = mnl_attr_get_u8(nla_object); + break; + case DEVLINK_ATTR_FMSG_OBJ_VALUE_DATA: + pr_out_fmsg_name(dl, &fmsg_data->name); + err = fmsg_value_show(dl, fmsg_data->value_type, + nla_object); + if (err != MNL_CB_OK) + return err; + break; + default: + return -EINVAL; + } + } + return MNL_CB_OK; +} + +static void cmd_fmsg_init(struct dl *dl, struct fmsg_cb_data *data) +{ + /* FMSG is dynamic: opening of an object or array causes a + * newline. JSON starts with an { or [, but plain text should + * not start with a new line. Ensure this by setting + * g_new_line_count to 1: avoiding newline before the first + * print. + */ + g_new_line_count = 1; + data->name = NULL; + data->dl = dl; + INIT_LIST_HEAD(&data->entry_list); +} + +static int cmd_health_object_common(struct dl *dl, uint8_t cmd, uint16_t flags) +{ + struct fmsg_cb_data data; + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, cmd, flags | NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, + DL_OPT_HANDLE | DL_OPT_HEALTH_REPORTER_NAME, 0); + if (err) + return err; + + cmd_fmsg_init(dl, &data); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_fmsg_object_cb, &data); + free(data.name); + return err; +} + +static int cmd_health_dump_show(struct dl *dl) +{ + return cmd_health_object_common(dl, + DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET, + NLM_F_DUMP); +} + +static int cmd_health_diagnose(struct dl *dl) +{ + return cmd_health_object_common(dl, + DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE, + 0); +} + +static int cmd_health_recover(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_HEALTH_REPORTER_RECOVER, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, + DL_OPT_HANDLE | DL_OPT_HEALTH_REPORTER_NAME, 0); + if (err) + return err; + + dl_opts_put(nlh, dl); + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +enum devlink_health_reporter_state { + DEVLINK_HEALTH_REPORTER_STATE_HEALTHY, + DEVLINK_HEALTH_REPORTER_STATE_ERROR, +}; + +static const char *health_state_name(uint8_t state) +{ + switch (state) { + case DEVLINK_HEALTH_REPORTER_STATE_HEALTHY: + return HEALTH_REPORTER_STATE_HEALTHY_STR; + case DEVLINK_HEALTH_REPORTER_STATE_ERROR: + return HEALTH_REPORTER_STATE_ERROR_STR; + default: + return ""; + } +} + +static void pr_out_dump_reporter_format_logtime(struct dl *dl, const struct nlattr *attr) +{ + char dump_date[HEALTH_REPORTER_TIMESTAMP_FMT_LEN]; + char dump_time[HEALTH_REPORTER_TIMESTAMP_FMT_LEN]; + uint64_t time_ms = mnl_attr_get_u64(attr); + struct sysinfo s_info; + struct tm *info; + time_t now, sec; + int err; + + time(&now); + info = localtime(&now); + err = sysinfo(&s_info); + if (err) + goto out; + /* Subtract uptime in sec from now yields the time of system + * uptime. To this, add time_ms which is the amount of + * milliseconds elapsed between uptime and the dump taken. + */ + sec = now - s_info.uptime + time_ms / 1000; + info = localtime(&sec); +out: + strftime(dump_date, HEALTH_REPORTER_TIMESTAMP_FMT_LEN, "%Y-%m-%d", info); + strftime(dump_time, HEALTH_REPORTER_TIMESTAMP_FMT_LEN, "%H:%M:%S", info); + check_indent_newline(dl); + print_string(PRINT_ANY, "last_dump_date", "last_dump_date %s", dump_date); + print_string(PRINT_ANY, "last_dump_time", " last_dump_time %s", dump_time); +} + +static void pr_out_dump_report_timestamp(struct dl *dl, const struct nlattr *attr) +{ + char dump_date[HEALTH_REPORTER_TIMESTAMP_FMT_LEN]; + char dump_time[HEALTH_REPORTER_TIMESTAMP_FMT_LEN]; + time_t tv_sec; + struct tm *tm; + uint64_t ts; + + ts = mnl_attr_get_u64(attr); + tv_sec = ts / 1000000000; + tm = localtime(&tv_sec); + + strftime(dump_date, HEALTH_REPORTER_TIMESTAMP_FMT_LEN, "%Y-%m-%d", tm); + strftime(dump_time, HEALTH_REPORTER_TIMESTAMP_FMT_LEN, "%H:%M:%S", tm); + + check_indent_newline(dl); + print_string(PRINT_ANY, "last_dump_date", "last_dump_date %s", dump_date); + print_string(PRINT_ANY, "last_dump_time", " last_dump_time %s", dump_time); +} + +static void pr_out_health(struct dl *dl, struct nlattr **tb_health) +{ + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + enum devlink_health_reporter_state state; + int err; + + err = mnl_attr_parse_nested(tb_health[DEVLINK_ATTR_HEALTH_REPORTER], + attr_cb, tb); + if (err != MNL_CB_OK) + return; + + if (!tb[DEVLINK_ATTR_HEALTH_REPORTER_NAME] || !tb[DEVLINK_ATTR_HEALTH_REPORTER_ERR_COUNT] || !tb[DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT] || !tb[DEVLINK_ATTR_HEALTH_REPORTER_STATE]) @@ -5893,35 +6962,33 @@ static void pr_out_health(struct dl *dl, struct nlattr **tb_health) pr_out_handle_start_arr(dl, tb_health); - pr_out_str(dl, "name", - mnl_attr_get_str(tb[DEVLINK_ATTR_HEALTH_REPORTER_NAME])); + check_indent_newline(dl); + print_string(PRINT_ANY, "reporter", "reporter %s", + mnl_attr_get_str(tb[DEVLINK_ATTR_HEALTH_REPORTER_NAME])); if (!dl->json_output) { __pr_out_newline(); __pr_out_indent_inc(); } state = mnl_attr_get_u8(tb[DEVLINK_ATTR_HEALTH_REPORTER_STATE]); - pr_out_str(dl, "state", health_state_name(state)); + check_indent_newline(dl); + print_string(PRINT_ANY, "state", "state %s", health_state_name(state)); pr_out_u64(dl, "error", mnl_attr_get_u64(tb[DEVLINK_ATTR_HEALTH_REPORTER_ERR_COUNT])); pr_out_u64(dl, "recover", mnl_attr_get_u64(tb[DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT])); - if (tb[DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS]) { - char dump_date[HEALTH_REPORTER_TIMESTAMP_FMT_LEN]; - char dump_time[HEALTH_REPORTER_TIMESTAMP_FMT_LEN]; - - attr = tb[DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS]; - time_ms = mnl_attr_get_u64(attr); - format_logtime(time_ms, dump_date, dump_time); - - pr_out_str(dl, "last_dump_date", dump_date); - pr_out_str(dl, "last_dump_time", dump_time); - } + if (tb[DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS_NS]) + pr_out_dump_report_timestamp(dl, tb[DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS_NS]); + else if (tb[DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS]) + pr_out_dump_reporter_format_logtime(dl, tb[DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS]); if (tb[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD]) pr_out_u64(dl, "grace_period", mnl_attr_get_u64(tb[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD])); if (tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]) - pr_out_bool(dl, "auto_recover", - mnl_attr_get_u8(tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER])); + print_bool(PRINT_ANY, "auto_recover", " auto_recover %s", + mnl_attr_get_u8(tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER])); + if (tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP]) + print_bool(PRINT_ANY, "auto_dump", " auto_dump %s", + mnl_attr_get_u8(tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP])); __pr_out_indent_dec(); pr_out_handle_end(dl); @@ -5972,6 +7039,13 @@ static void cmd_health_help(void) { pr_err("Usage: devlink health show [ dev DEV reporter REPORTER_NAME ]\n"); pr_err(" devlink health recover DEV reporter REPORTER_NAME\n"); + pr_err(" devlink health diagnose DEV reporter REPORTER_NAME\n"); + pr_err(" devlink health dump show DEV reporter REPORTER_NAME\n"); + pr_err(" devlink health dump clear DEV reporter REPORTER_NAME\n"); + pr_err(" devlink health set DEV reporter REPORTER_NAME\n"); + pr_err(" [ grace_period MSEC ]\n"); + pr_err(" [ auto_recover { true | false } ]\n"); + pr_err(" [ auto_dump { true | false } ]\n"); } static int cmd_health(struct dl *dl) @@ -5986,6 +7060,383 @@ static int cmd_health(struct dl *dl) } else if (dl_argv_match(dl, "recover")) { dl_arg_inc(dl); return cmd_health_recover(dl); + } else if (dl_argv_match(dl, "diagnose")) { + dl_arg_inc(dl); + return cmd_health_diagnose(dl); + } else if (dl_argv_match(dl, "dump")) { + dl_arg_inc(dl); + if (dl_argv_match(dl, "show")) { + dl_arg_inc(dl); + return cmd_health_dump_show(dl); + } else if (dl_argv_match(dl, "clear")) { + dl_arg_inc(dl); + return cmd_health_dump_clear(dl); + } + } else if (dl_argv_match(dl, "set")) { + dl_arg_inc(dl); + return cmd_health_set_params(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static const char *trap_type_name(uint8_t type) +{ + switch (type) { + case DEVLINK_TRAP_TYPE_DROP: + return "drop"; + case DEVLINK_TRAP_TYPE_EXCEPTION: + return "exception"; + case DEVLINK_TRAP_TYPE_CONTROL: + return "control"; + default: + return ""; + } +} + +static const char *trap_action_name(uint8_t action) +{ + switch (action) { + case DEVLINK_TRAP_ACTION_DROP: + return "drop"; + case DEVLINK_TRAP_ACTION_TRAP: + return "trap"; + case DEVLINK_TRAP_ACTION_MIRROR: + return "mirror"; + default: + return ""; + } +} + +static const char *trap_metadata_name(const struct nlattr *attr) +{ + switch (attr->nla_type) { + case DEVLINK_ATTR_TRAP_METADATA_TYPE_IN_PORT: + return "input_port"; + case DEVLINK_ATTR_TRAP_METADATA_TYPE_FA_COOKIE: + return "flow_action_cookie"; + default: + return ""; + } +} +static void pr_out_trap_metadata(struct dl *dl, struct nlattr *attr) +{ + struct nlattr *attr_metadata; + + pr_out_array_start(dl, "metadata"); + mnl_attr_for_each_nested(attr_metadata, attr) { + check_indent_newline(dl); + print_string(PRINT_ANY, NULL, "%s", + trap_metadata_name(attr_metadata)); + } + pr_out_array_end(dl); +} + +static void pr_out_trap(struct dl *dl, struct nlattr **tb, bool array) +{ + uint8_t action = mnl_attr_get_u8(tb[DEVLINK_ATTR_TRAP_ACTION]); + uint8_t type = mnl_attr_get_u8(tb[DEVLINK_ATTR_TRAP_TYPE]); + + if (array) + pr_out_handle_start_arr(dl, tb); + else + __pr_out_handle_start(dl, tb, true, false); + + check_indent_newline(dl); + print_string(PRINT_ANY, "name", "name %s", + mnl_attr_get_str(tb[DEVLINK_ATTR_TRAP_NAME])); + print_string(PRINT_ANY, "type", " type %s", trap_type_name(type)); + print_bool(PRINT_ANY, "generic", " generic %s", !!tb[DEVLINK_ATTR_TRAP_GENERIC]); + print_string(PRINT_ANY, "action", " action %s", trap_action_name(action)); + print_string(PRINT_ANY, "group", " group %s", + mnl_attr_get_str(tb[DEVLINK_ATTR_TRAP_GROUP_NAME])); + if (dl->verbose) + pr_out_trap_metadata(dl, tb[DEVLINK_ATTR_TRAP_METADATA]); + pr_out_stats(dl, tb[DEVLINK_ATTR_STATS]); + pr_out_handle_end(dl); +} + +static int cmd_trap_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct dl *dl = data; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_TRAP_NAME] || !tb[DEVLINK_ATTR_TRAP_TYPE] || + !tb[DEVLINK_ATTR_TRAP_ACTION] || + !tb[DEVLINK_ATTR_TRAP_GROUP_NAME] || + !tb[DEVLINK_ATTR_TRAP_METADATA] || !tb[DEVLINK_ATTR_STATS]) + return MNL_CB_ERROR; + + pr_out_trap(dl, tb, true); + + return MNL_CB_OK; +} + +static void cmd_trap_help(void) +{ + pr_err("Usage: devlink trap set DEV trap TRAP [ action { trap | drop | mirror } ]\n"); + pr_err(" devlink trap show [ DEV trap TRAP ]\n"); + pr_err(" devlink trap group set DEV group GROUP [ action { trap | drop | mirror } ]\n"); + pr_err(" [ policer POLICER ] [ nopolicer ]\n"); + pr_err(" devlink trap group show [ DEV group GROUP ]\n"); + pr_err(" devlink trap policer set DEV policer POLICER [ rate RATE ] [ burst BURST ]\n"); + pr_err(" devlink trap policer show DEV policer POLICER\n"); +} + +static int cmd_trap_show(struct dl *dl) +{ + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + struct nlmsghdr *nlh; + int err; + + if (dl_argc(dl) == 0) + flags |= NLM_F_DUMP; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_GET, flags); + + if (dl_argc(dl) > 0) { + err = dl_argv_parse_put(nlh, dl, + DL_OPT_HANDLE | DL_OPT_TRAP_NAME, 0); + if (err) + return err; + } + + pr_out_section_start(dl, "trap"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_trap_show_cb, dl); + pr_out_section_end(dl); + + return err; +} + +static int cmd_trap_set(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_SET, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_TRAP_NAME, + DL_OPT_TRAP_ACTION); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static void pr_out_trap_group(struct dl *dl, struct nlattr **tb, bool array) +{ + if (array) + pr_out_handle_start_arr(dl, tb); + else + __pr_out_handle_start(dl, tb, true, false); + + check_indent_newline(dl); + print_string(PRINT_ANY, "name", "name %s", + mnl_attr_get_str(tb[DEVLINK_ATTR_TRAP_GROUP_NAME])); + print_bool(PRINT_ANY, "generic", " generic %s", !!tb[DEVLINK_ATTR_TRAP_GENERIC]); + if (tb[DEVLINK_ATTR_TRAP_POLICER_ID]) + print_uint(PRINT_ANY, "policer", " policer %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_TRAP_POLICER_ID])); + pr_out_stats(dl, tb[DEVLINK_ATTR_STATS]); + pr_out_handle_end(dl); +} + +static int cmd_trap_group_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct dl *dl = data; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_TRAP_GROUP_NAME] || !tb[DEVLINK_ATTR_STATS]) + return MNL_CB_ERROR; + + pr_out_trap_group(dl, tb, true); + + return MNL_CB_OK; +} + +static int cmd_trap_group_show(struct dl *dl) +{ + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + struct nlmsghdr *nlh; + int err; + + if (dl_argc(dl) == 0) + flags |= NLM_F_DUMP; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_GROUP_GET, flags); + + if (dl_argc(dl) > 0) { + err = dl_argv_parse_put(nlh, dl, + DL_OPT_HANDLE | DL_OPT_TRAP_GROUP_NAME, + 0); + if (err) + return err; + } + + pr_out_section_start(dl, "trap_group"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_trap_group_show_cb, dl); + pr_out_section_end(dl); + + return err; +} + +static int cmd_trap_group_set(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_GROUP_SET, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, + DL_OPT_HANDLE | DL_OPT_TRAP_GROUP_NAME, + DL_OPT_TRAP_ACTION | DL_OPT_TRAP_POLICER_ID); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_trap_group(struct dl *dl) +{ + if (dl_argv_match(dl, "help")) { + cmd_trap_help(); + return 0; + } else if (dl_argv_match(dl, "show") || + dl_argv_match(dl, "list") || dl_no_arg(dl)) { + dl_arg_inc(dl); + return cmd_trap_group_show(dl); + } else if (dl_argv_match(dl, "set")) { + dl_arg_inc(dl); + return cmd_trap_group_set(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static void pr_out_trap_policer(struct dl *dl, struct nlattr **tb, bool array) +{ + if (array) + pr_out_handle_start_arr(dl, tb); + else + __pr_out_handle_start(dl, tb, true, false); + + check_indent_newline(dl); + print_uint(PRINT_ANY, "policer", "policer %u", + mnl_attr_get_u32(tb[DEVLINK_ATTR_TRAP_POLICER_ID])); + print_u64(PRINT_ANY, "rate", " rate %llu", + mnl_attr_get_u64(tb[DEVLINK_ATTR_TRAP_POLICER_RATE])); + print_u64(PRINT_ANY, "burst", " burst %llu", + mnl_attr_get_u64(tb[DEVLINK_ATTR_TRAP_POLICER_BURST])); + if (tb[DEVLINK_ATTR_STATS]) + pr_out_stats(dl, tb[DEVLINK_ATTR_STATS]); + pr_out_handle_end(dl); +} + +static int cmd_trap_policer_show_cb(const struct nlmsghdr *nlh, void *data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct dl *dl = data; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_TRAP_POLICER_ID] || + !tb[DEVLINK_ATTR_TRAP_POLICER_RATE] || + !tb[DEVLINK_ATTR_TRAP_POLICER_BURST]) + return MNL_CB_ERROR; + + pr_out_trap_policer(dl, tb, true); + + return MNL_CB_OK; +} + +static int cmd_trap_policer_show(struct dl *dl) +{ + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; + struct nlmsghdr *nlh; + int err; + + if (dl_argc(dl) == 0) + flags |= NLM_F_DUMP; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_POLICER_GET, flags); + + if (dl_argc(dl) > 0) { + err = dl_argv_parse_put(nlh, dl, + DL_OPT_HANDLE | DL_OPT_TRAP_POLICER_ID, + 0); + if (err) + return err; + } + + pr_out_section_start(dl, "trap_policer"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_trap_policer_show_cb, dl); + pr_out_section_end(dl); + + return err; +} + +static int cmd_trap_policer_set(struct dl *dl) +{ + struct nlmsghdr *nlh; + int err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_POLICER_SET, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, + DL_OPT_HANDLE | DL_OPT_TRAP_POLICER_ID, + DL_OPT_TRAP_POLICER_RATE | + DL_OPT_TRAP_POLICER_BURST); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + +static int cmd_trap_policer(struct dl *dl) +{ + if (dl_argv_match(dl, "help")) { + cmd_trap_help(); + return 0; + } else if (dl_argv_match(dl, "show") || + dl_argv_match(dl, "list") || dl_no_arg(dl)) { + dl_arg_inc(dl); + return cmd_trap_policer_show(dl); + } else if (dl_argv_match(dl, "set")) { + dl_arg_inc(dl); + return cmd_trap_policer_set(dl); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + +static int cmd_trap(struct dl *dl) +{ + if (dl_argv_match(dl, "help")) { + cmd_trap_help(); + return 0; + } else if (dl_argv_match(dl, "show") || + dl_argv_match(dl, "list") || dl_no_arg(dl)) { + dl_arg_inc(dl); + return cmd_trap_show(dl); + } else if (dl_argv_match(dl, "set")) { + dl_arg_inc(dl); + return cmd_trap_set(dl); + } else if (dl_argv_match(dl, "group")) { + dl_arg_inc(dl); + return cmd_trap_group(dl); + } else if (dl_argv_match(dl, "policer")) { + dl_arg_inc(dl); + return cmd_trap_policer(dl); } pr_err("Command \"%s\" not found\n", dl_argv(dl)); return -ENOENT; @@ -5994,9 +7445,9 @@ static int cmd_health(struct dl *dl) static void help(void) { pr_err("Usage: devlink [ OPTIONS ] OBJECT { COMMAND | help }\n" - " devlink [ -f[orce] ] -b[atch] filename\n" - "where OBJECT := { dev | port | sb | monitor | dpipe | resource | region | health }\n" - " OPTIONS := { -V[ersion] | -n[o-nice-names] | -j[son] | -p[retty] | -v[erbose] }\n"); + " devlink [ -f[orce] ] -b[atch] filename -N[etns] netnsname\n" + "where OBJECT := { dev | port | sb | monitor | dpipe | resource | region | health | trap }\n" + " OPTIONS := { -V[ersion] | -n[o-nice-names] | -j[son] | -p[retty] | -v[erbose] -s[tatistics] }\n"); } static int dl_cmd(struct dl *dl, int argc, char **argv) @@ -6031,6 +7482,9 @@ static int dl_cmd(struct dl *dl, int argc, char **argv) } else if (dl_argv_match(dl, "health")) { dl_arg_inc(dl); return cmd_health(dl); + } else if (dl_argv_match(dl, "trap")) { + dl_arg_inc(dl); + return cmd_trap(dl); } pr_err("Object \"%s\" not found\n", dl_argv(dl)); return -ENOENT; @@ -6051,18 +7505,9 @@ static int dl_init(struct dl *dl) pr_err("Failed to create index map\n"); goto err_ifname_map_create; } - if (dl->json_output) { - dl->jw = jsonw_new(stdout); - if (!dl->jw) { - pr_err("Failed to create JSON writer\n"); - goto err_json_new; - } - jsonw_pretty(dl->jw, dl->pretty_output); - } + new_json_obj_plain(dl->json_output); return 0; -err_json_new: - ifname_map_fini(dl); err_ifname_map_create: mnlg_socket_close(dl->nlg); return err; @@ -6070,8 +7515,7 @@ err_ifname_map_create: static void dl_fini(struct dl *dl) { - if (dl->json_output) - jsonw_destroy(&dl->jw); + delete_json_obj_plain(); ifname_map_fini(dl); mnlg_socket_close(dl->nlg); } @@ -6140,6 +7584,8 @@ int main(int argc, char **argv) { "json", no_argument, NULL, 'j' }, { "pretty", no_argument, NULL, 'p' }, { "verbose", no_argument, NULL, 'v' }, + { "statistics", no_argument, NULL, 's' }, + { "Netns", required_argument, NULL, 'N' }, { NULL, 0, NULL, 0 } }; const char *batch_file = NULL; @@ -6155,12 +7601,12 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - while ((opt = getopt_long(argc, argv, "Vfb:njpv", + while ((opt = getopt_long(argc, argv, "Vfb:njpvsN:", long_options, NULL)) >= 0) { switch (opt) { case 'V': - printf("devlink utility, iproute2-ss%s\n", SNAPSHOT); + printf("devlink utility, iproute2-%s\n", version); ret = EXIT_SUCCESS; goto dl_free; case 'f': @@ -6176,11 +7622,20 @@ int main(int argc, char **argv) dl->json_output = true; break; case 'p': - dl->pretty_output = true; + pretty = true; break; case 'v': dl->verbose = true; break; + case 's': + dl->stats = true; + break; + case 'N': + if (netns_switch(optarg)) { + ret = EXIT_FAILURE; + goto dl_free; + } + break; default: pr_err("Unknown option.\n"); help();