X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=devlink%2Fdevlink.c;h=0982faef7d88eef07e1d2ff79035a492d62d7662;hb=fbef655568ee931a82ad463a6f46f01ce3fb27aa;hp=97b93730e6d6177f64ffd0f6c0ce4f62a1921a9d;hpb=ee09370a7206ad361653e437ca6429533a1c02a9;p=mirror_iproute2.git diff --git a/devlink/devlink.c b/devlink/devlink.c index 97b93730..0982faef 100644 --- a/devlink/devlink.c +++ b/devlink/devlink.c @@ -19,18 +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 -#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" @@ -39,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" @@ -96,6 +108,18 @@ pr_out_sp(unsigned int num, const char *fmt, ...) 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) @@ -135,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; @@ -146,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); } @@ -231,11 +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(27) -#define DL_OPT_HEALTH_REPORTER_AUTO_RECOVER BIT(28) +#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; @@ -251,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; @@ -269,6 +336,15 @@ struct dl_opts { 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 { @@ -278,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; @@ -310,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; @@ -348,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, @@ -436,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) @@ -454,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] = {}; @@ -634,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; @@ -735,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; @@ -971,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; @@ -1014,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]; }; @@ -1033,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."}, @@ -1040,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++) { @@ -1055,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); @@ -1206,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; @@ -1242,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; @@ -1329,6 +1485,82 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required, 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; @@ -1401,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); @@ -1442,11 +1674,36 @@ static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl) 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; @@ -1499,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"); } @@ -1552,19 +1809,17 @@ static void __pr_out_handle_start(struct dl *dl, struct nlattr **tb, 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) { @@ -1591,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(); } @@ -1653,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); @@ -1701,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 %"PRIu64, name, val); - else - pr_out(" %s %"PRIu64, 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(" %"PRIu64, 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, @@ -1852,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]); @@ -1862,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); } } @@ -1880,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(); @@ -1903,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(); @@ -1913,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(); @@ -1927,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(); @@ -1937,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) { @@ -1986,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); @@ -2142,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) @@ -2164,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, @@ -2181,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: @@ -2195,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: @@ -2209,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; } } @@ -2247,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, @@ -2518,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; } @@ -2549,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; @@ -2604,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(); } @@ -2624,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]) { @@ -2632,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(); @@ -2677,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; @@ -2689,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; } @@ -2710,33 +2970,212 @@ static int cmd_dev_info(struct dl *dl) return err; } -static void cmd_dev_flash_help(void) -{ - pr_err("Usage: devlink dev flash DEV file PATH [ component NAME ]\n"); -} +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 cmd_dev_flash(struct dl *dl) +static int nullstrcmp(const char *str1, const char *str2) { - struct nlmsghdr *nlh; - int err; - - if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { - cmd_dev_flash_help(); + if (str1 && str2) + return strcmp(str1, str2); + if (!str1 && !str2) return 0; - } - - nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_FLASH_UPDATE, - NLM_F_REQUEST | NLM_F_ACK); - - err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_FLASH_FILE_NAME, - DL_OPT_FLASH_COMPONENT); - if (err) - return err; - - return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); + return str1 ? 1 : -1; } -static int cmd_dev(struct dl *dl) +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) +{ + 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_help(); + return 0; + } + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_FLASH_UPDATE, + NLM_F_REQUEST | NLM_F_ACK); + + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_FLASH_FILE_NAME, + DL_OPT_FLASH_COMPONENT); + if (err) + return err; + + 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) { if (dl_argv_match(dl, "help")) { cmd_dev_help(); @@ -2793,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); } @@ -2962,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); } @@ -3039,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); } @@ -3131,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); } @@ -3229,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); } @@ -3459,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) @@ -3734,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 ""; } } @@ -3762,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) @@ -3786,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) { @@ -3807,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 */ @@ -3819,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 */ @@ -3830,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 */ @@ -3841,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; @@ -3855,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; } @@ -3863,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; @@ -3872,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) @@ -4199,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); } } @@ -4215,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, @@ -4397,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); @@ -4443,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) @@ -4525,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, @@ -4643,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); } @@ -4688,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"); @@ -4868,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 @@ -4876,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, @@ -4887,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[] = { @@ -4977,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); } } @@ -4998,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) { @@ -5115,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"); @@ -5211,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); @@ -5236,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)) { @@ -5366,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) @@ -5390,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) @@ -5406,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); @@ -5623,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("]"); } @@ -5670,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); } @@ -5847,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"); } @@ -5874,6 +6554,9 @@ 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; @@ -5888,7 +6571,8 @@ static int cmd_health_set_params(struct dl *dl) 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_RECOVER | + DL_OPT_HEALTH_REPORTER_AUTO_DUMP); if (err) return err; @@ -5918,24 +6602,25 @@ static int fmsg_value_show(struct dl *dl, int type, struct nlattr *nl_data) uint8_t *data; uint32_t len; + check_indent_newline(dl); switch (type) { case MNL_TYPE_FLAG: - pr_out_bool_value(dl, mnl_attr_get_u8(nl_data)); + print_bool(PRINT_ANY, NULL, "%s", mnl_attr_get_u8(nl_data)); break; case MNL_TYPE_U8: - pr_out_uint_value(dl, mnl_attr_get_u8(nl_data)); + print_uint(PRINT_ANY, NULL, "%u", mnl_attr_get_u8(nl_data)); break; case MNL_TYPE_U16: - pr_out_uint_value(dl, mnl_attr_get_u16(nl_data)); + print_uint(PRINT_ANY, NULL, "%u", mnl_attr_get_u16(nl_data)); break; case MNL_TYPE_U32: - pr_out_uint_value(dl, mnl_attr_get_u32(nl_data)); + print_uint(PRINT_ANY, NULL, "%u", mnl_attr_get_u32(nl_data)); break; case MNL_TYPE_U64: - pr_out_uint64_value(dl, mnl_attr_get_u64(nl_data)); + print_u64(PRINT_ANY, NULL, "%"PRIu64, mnl_attr_get_u64(nl_data)); break; case MNL_TYPE_NUL_STRING: - pr_out_str_value(dl, mnl_attr_get_str(nl_data)); + 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); @@ -5948,40 +6633,102 @@ static int fmsg_value_show(struct dl *dl, int type, struct nlattr *nl_data) return MNL_CB_OK; } -struct nest_qentry { +static void pr_out_fmsg_name(struct dl *dl, char **name) +{ + if (!*name) + return; + + pr_out_name(dl, *name); + free(*name); + *name = NULL; +} + +struct nest_entry { int attr_type; - TAILQ_ENTRY(nest_qentry) nest_entries; + struct list_head list; }; struct fmsg_cb_data { + char *name; struct dl *dl; uint8_t value_type; - TAILQ_HEAD(, nest_qentry) qhead; + struct list_head entry_list; }; static int cmd_fmsg_nest_queue(struct fmsg_cb_data *fmsg_data, uint8_t *attr_value, bool insert) { - struct nest_qentry *entry = NULL; + struct nest_entry *entry; if (insert) { - entry = malloc(sizeof(struct nest_qentry)); + entry = malloc(sizeof(struct nest_entry)); if (!entry) return -ENOMEM; entry->attr_type = *attr_value; - TAILQ_INSERT_HEAD(&fmsg_data->qhead, entry, nest_entries); + list_add(&entry->list, &fmsg_data->entry_list); } else { - if (TAILQ_EMPTY(&fmsg_data->qhead)) + if (list_empty(&fmsg_data->entry_list)) return MNL_CB_ERROR; - entry = TAILQ_FIRST(&fmsg_data->qhead); + entry = list_first_entry(&fmsg_data->entry_list, + struct nest_entry, list); *attr_value = entry->attr_type; - TAILQ_REMOVE(&fmsg_data->qhead, entry, nest_entries); + 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) { @@ -5996,26 +6743,17 @@ static int cmd_fmsg_nest(struct fmsg_cb_data *fmsg_data, uint8_t nest_value, switch (value) { case DEVLINK_ATTR_FMSG_OBJ_NEST_START: if (start) - pr_out_entry_start(dl); + pr_out_fmsg_start_object(dl, &fmsg_data->name); else - pr_out_entry_end(dl); + pr_out_fmsg_end_object(dl); break; case DEVLINK_ATTR_FMSG_PAIR_NEST_START: break; case DEVLINK_ATTR_FMSG_ARR_NEST_START: - if (dl->json_output) { - if (start) - jsonw_start_array(dl->jw); - else - jsonw_end_array(dl->jw); - } else { - if (start) { - __pr_out_newline(); - __pr_out_indent_inc(); - } else { - __pr_out_indent_dec(); - } - } + if (start) + pr_out_fmsg_start_array(dl, &fmsg_data->name); + else + pr_out_fmsg_end_array(dl); break; default: return -EINVAL; @@ -6053,12 +6791,16 @@ static int cmd_fmsg_object_cb(const struct nlmsghdr *nlh, void *data) return err; break; case DEVLINK_ATTR_FMSG_OBJ_NAME: - pr_out_name(dl, mnl_attr_get_str(nla_object)); + 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) @@ -6071,33 +6813,51 @@ static int cmd_fmsg_object_cb(const struct nlmsghdr *nlh, void *data) return MNL_CB_OK; } -static int cmd_health_object_common(struct dl *dl, uint8_t cmd) +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, NLM_F_REQUEST | NLM_F_ACK); + 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; - data.dl = dl; - TAILQ_INIT(&data.qhead); + 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); + 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); + return cmd_health_object_common(dl, + DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE, + 0); } static int cmd_health_recover(struct dl *dl) @@ -6134,8 +6894,11 @@ static const char *health_state_name(uint8_t state) } } -static void format_logtime(uint64_t time_ms, char *ts_date, char *ts_time) +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; @@ -6153,16 +6916,37 @@ static void format_logtime(uint64_t time_ms, char *ts_date, char *ts_time) 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); + 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; - const struct nlattr *attr; - uint64_t time_ms; int err; err = mnl_attr_parse_nested(tb_health[DEVLINK_ATTR_HEALTH_REPORTER], @@ -6178,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); @@ -6260,7 +7042,10 @@ static void cmd_health_help(void) 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 { grace_period | auto_recover } { msec | boolean }\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) @@ -6295,12 +7080,374 @@ static int cmd_health(struct dl *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; +} + 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) @@ -6335,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; @@ -6355,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; @@ -6374,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); } @@ -6444,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; @@ -6459,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': @@ -6480,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();