diff options
author | David Ahern <dsahern@gmail.com> | 2018-03-13 17:48:10 -0700 |
---|---|---|
committer | David Ahern <dsahern@gmail.com> | 2018-03-13 17:48:10 -0700 |
commit | e9625d6aead11c7ca8b64af3ad06ea451e06ea9e (patch) | |
tree | f2736bbf90ddc7b633195385904bd4e3dde8ef52 | |
parent | a121129df91104ade8f51ac9ccd6e3a84bbddf27 (diff) | |
parent | 527f85141c9e6982f73f043f85949eaf7ff498bc (diff) |
Merge branch 'iproute2-master' into iproute2-next
Conflicts:
bridge/mdb.c
Updated bridge/bridge.c per removal of check_if_color_enabled by commit
1ca4341d2c6b ("color: disable color when json output is requested")
Signed-off-by: David Ahern <dsahern@gmail.com>
40 files changed, 1015 insertions, 129 deletions
@@ -1,5 +1,8 @@ This is a set of utilities for Linux networking. +Information: + https://wiki.linuxfoundation.org/networking/iproute2 + Download: http://www.kernel.org/pub/linux/utils/net/iproute2/ diff --git a/bridge/bridge.c b/bridge/bridge.c index e5b4c3c2..7fcfe111 100644 --- a/bridge/bridge.c +++ b/bridge/bridge.c @@ -200,8 +200,8 @@ main(int argc, char **argv) _SL_ = oneline ? "\\" : "\n"; - if (json) - check_if_color_enabled(); + if (color && !json) + enable_color(); if (batch_file) return batch(batch_file); diff --git a/devlink/devlink.c b/devlink/devlink.c index 220b2bb2..69c3c5d9 100644 --- a/devlink/devlink.c +++ b/devlink/devlink.c @@ -35,6 +35,8 @@ #define ESWITCH_INLINE_MODE_NETWORK "network" #define ESWITCH_INLINE_MODE_TRANSPORT "transport" +static int g_new_line_count; + #define pr_err(args...) fprintf(stderr, ##args) #define pr_out(args...) \ do { \ @@ -43,6 +45,7 @@ g_indent_newline = false; \ } \ fprintf(stdout, ##args); \ + g_new_line_count = 0; \ } while (0) #define pr_out_sp(num, args...) \ @@ -50,6 +53,7 @@ int ret = fprintf(stdout, ##args); \ if (ret < num) \ fprintf(stdout, "%*s", num - ret, ""); \ + g_new_line_count = 0; \ } while (0) static int g_indent_level; @@ -77,8 +81,11 @@ static void __pr_out_indent_dec(void) static void __pr_out_newline(void) { - pr_out("\n"); - g_indent_newline = true; + if (g_new_line_count < 1) { + pr_out("\n"); + g_indent_newline = true; + } + g_new_line_count++; } static int _mnlg_socket_recv_run(struct mnlg_socket *nlg, @@ -178,6 +185,8 @@ static void ifname_map_free(struct ifname_map *ifname_map) #define DL_OPT_DPIPE_TABLE_NAME BIT(13) #define DL_OPT_DPIPE_TABLE_COUNTERS BIT(14) #define DL_OPT_ESWITCH_ENCAP_MODE BIT(15) +#define DL_OPT_RESOURCE_PATH BIT(16) +#define DL_OPT_RESOURCE_SIZE BIT(17) struct dl_opts { uint32_t present; /* flags of present items */ @@ -198,6 +207,10 @@ struct dl_opts { const char *dpipe_table_name; bool dpipe_counters_enable; bool eswitch_encap_mode; + const char *resource_path; + uint32_t resource_size; + uint32_t resource_id; + bool resource_id_valid; }; struct dl { @@ -946,6 +959,20 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required, if (err) return err; o_found |= DL_OPT_ESWITCH_ENCAP_MODE; + } else if (dl_argv_match(dl, "path") && + (o_all & DL_OPT_RESOURCE_PATH)) { + dl_arg_inc(dl); + err = dl_argv_str(dl, &opts->resource_path); + if (err) + return err; + o_found |= DL_OPT_RESOURCE_PATH; + } 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); + if (err) + return err; + o_found |= DL_OPT_RESOURCE_SIZE; } else { pr_err("Unknown option \"%s\"\n", dl_argv(dl)); return -EINVAL; @@ -1088,6 +1115,12 @@ static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl) if (opts->present & DL_OPT_ESWITCH_ENCAP_MODE) mnl_attr_put_u8(nlh, DEVLINK_ATTR_ESWITCH_ENCAP_MODE, opts->eswitch_encap_mode); + if ((opts->present & DL_OPT_RESOURCE_PATH) && opts->resource_id_valid) + mnl_attr_put_u64(nlh, DEVLINK_ATTR_RESOURCE_ID, + opts->resource_id); + if (opts->present & DL_OPT_RESOURCE_SIZE) + mnl_attr_put_u64(nlh, DEVLINK_ATTR_RESOURCE_SIZE, + opts->resource_size); } static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl, @@ -1146,6 +1179,7 @@ static void cmd_dev_help(void) pr_err(" [ inline-mode { none | link | network | transport } ]\n"); pr_err(" [ encap { disable | enable } ]\n"); pr_err(" devlink dev eswitch show DEV\n"); + pr_err(" devlink dev reload DEV\n"); } static bool cmp_arr_last_handle(struct dl *dl, const char *bus_name, @@ -1401,20 +1435,22 @@ static void pr_out_array_start(struct dl *dl, const char *name) jsonw_name(dl->jw, name); jsonw_start_array(dl->jw); } else { - if (!g_indent_newline) - __pr_out_newline(); - pr_out("%s:", name); + __pr_out_indent_inc(); __pr_out_newline(); + pr_out("%s:", name); __pr_out_indent_inc(); + __pr_out_newline(); } } static void pr_out_array_end(struct dl *dl) { - if (dl->json_output) + if (dl->json_output) { jsonw_end_array(dl->jw); - else + } else { + __pr_out_indent_dec(); __pr_out_indent_dec(); + } } static void pr_out_entry_start(struct dl *dl) @@ -1585,6 +1621,31 @@ 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(); + 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); + if (err) + return err; + + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +} + static int cmd_dev(struct dl *dl) { if (dl_argv_match(dl, "help")) { @@ -1597,6 +1658,9 @@ static int cmd_dev(struct dl *dl) } else if (dl_argv_match(dl, "eswitch")) { dl_arg_inc(dl); return cmd_dev_eswitch(dl); + } else if (dl_argv_match(dl, "reload")) { + dl_arg_inc(dl); + return cmd_dev_reload(dl); } pr_err("Command \"%s\" not found\n", dl_argv(dl)); return -ENOENT; @@ -2675,12 +2739,112 @@ struct dpipe_header { unsigned int fields_count; }; +struct dpipe_table { + struct list_head list; + char *name; + unsigned int resource_id; + bool resource_valid; +}; + +struct dpipe_tables { + struct list_head table_list; +}; + +struct resource { + char *name; + uint64_t size; + uint64_t size_new; + uint64_t size_min; + uint64_t size_max; + uint64_t size_gran; + enum devlink_resource_unit unit; + bool size_valid; + uint64_t size_occ; + bool occ_valid; + uint64_t id; + struct list_head list; + struct list_head resource_list; + struct resource *parent; +}; + +struct resources { + struct list_head resource_list; +}; + +struct resource_ctx { + struct dl *dl; + int err; + struct resources *resources; + struct dpipe_tables *tables; + bool print_resources; + bool pending_change; +}; + +static struct resource *resource_alloc(void) +{ + struct resource *resource; + + resource = calloc(1, sizeof(struct resource)); + if (!resource) + return NULL; + INIT_LIST_HEAD(&resource->resource_list); + return resource; +} + +static void resource_free(struct resource *resource) +{ + struct resource *child_resource, *tmp; + + list_for_each_entry_safe(child_resource, tmp, &resource->resource_list, + list) { + free(child_resource->name); + resource_free(child_resource); + } + free(resource); +} + +static struct resources *resources_alloc(void) +{ + struct resources *resources; + + resources = calloc(1, sizeof(struct resources)); + if (!resources) + return NULL; + INIT_LIST_HEAD(&resources->resource_list); + return resources; +} + +static void resources_free(struct resources *resources) +{ + struct resource *resource, *tmp; + + list_for_each_entry_safe(resource, tmp, &resources->resource_list, list) + resource_free(resource); +} + +static int resource_ctx_init(struct resource_ctx *ctx, struct dl *dl) +{ + ctx->resources = resources_alloc(); + if (!ctx->resources) + return -ENOMEM; + ctx->dl = dl; + return 0; +} + +static void resource_ctx_fini(struct resource_ctx *ctx) +{ + resources_free(ctx->resources); +} + struct dpipe_ctx { struct dl *dl; int err; struct list_head global_headers; struct list_head local_headers; + struct dpipe_tables *tables; + struct resources *resources; bool print_headers; + bool print_tables; }; static struct dpipe_header *dpipe_header_alloc(unsigned int fields_count) @@ -2733,25 +2897,49 @@ static void dpipe_header_del(struct dpipe_header *header) list_del(&header->list); } -static struct dpipe_ctx *dpipe_ctx_alloc(struct dl *dl) +static struct dpipe_table *dpipe_table_alloc(void) +{ + return calloc(1, sizeof(struct dpipe_table)); +} + +static void dpipe_table_free(struct dpipe_table *table) { - struct dpipe_ctx *ctx; + free(table); +} - ctx = calloc(1, sizeof(struct dpipe_ctx)); - if (!ctx) +static struct dpipe_tables *dpipe_tables_alloc(void) +{ + struct dpipe_tables *tables; + + tables = calloc(1, sizeof(struct dpipe_tables)); + if (!tables) return NULL; - ctx->dl = dl; - INIT_LIST_HEAD(&ctx->global_headers); - INIT_LIST_HEAD(&ctx->local_headers); - return ctx; + INIT_LIST_HEAD(&tables->table_list); + return tables; } -static void dpipe_ctx_free(struct dpipe_ctx *ctx) +static void dpipe_tables_free(struct dpipe_tables *tables) { - free(ctx); + struct dpipe_table *table, *tmp; + + list_for_each_entry_safe(table, tmp, &tables->table_list, list) + dpipe_table_free(table); + free(tables); +} + +static int dpipe_ctx_init(struct dpipe_ctx *ctx, struct dl *dl) +{ + ctx->tables = dpipe_tables_alloc(); + if (!ctx->tables) + return -ENOMEM; + + ctx->dl = dl; + INIT_LIST_HEAD(&ctx->global_headers); + INIT_LIST_HEAD(&ctx->local_headers); + return 0; } -static void dpipe_ctx_clear(struct dpipe_ctx *ctx) +static void dpipe_ctx_fini(struct dpipe_ctx *ctx) { struct dpipe_header *header, *tmp; @@ -2767,6 +2955,7 @@ static void dpipe_ctx_clear(struct dpipe_ctx *ctx) dpipe_header_clear(header); dpipe_header_free(header); } + dpipe_tables_free(ctx->tables); } static const char *dpipe_header_id2s(struct dpipe_ctx *ctx, @@ -3022,7 +3211,7 @@ static int cmd_dpipe_header_cb(const struct nlmsghdr *nlh, void *data) static int cmd_dpipe_headers_show(struct dl *dl) { struct nlmsghdr *nlh; - struct dpipe_ctx *ctx; + struct dpipe_ctx ctx = {}; uint16_t flags = NLM_F_REQUEST | NLM_F_ACK; int err; @@ -3032,20 +3221,19 @@ static int cmd_dpipe_headers_show(struct dl *dl) if (err) return err; - ctx = dpipe_ctx_alloc(dl); - if (!ctx) - return -ENOMEM; + err = dpipe_ctx_init(&ctx, dl); + if (err) + return err; - ctx->print_headers = true; + ctx.print_headers = true; pr_out_section_start(dl, "header"); - err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, ctx); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, &ctx); if (err) - pr_err("error get headers %s\n", strerror(ctx->err)); + pr_err("error get headers %s\n", strerror(ctx.err)); pr_out_section_end(dl); - dpipe_ctx_clear(ctx); - dpipe_ctx_free(ctx); + dpipe_ctx_fini(&ctx); return err; } @@ -3239,11 +3427,73 @@ err_match_parse: return -EINVAL; } +static struct resource * +resource_find(struct resources *resources, struct resource *resource, + uint64_t resource_id) +{ + struct list_head *list_head; + + if (!resource) + list_head = &resources->resource_list; + else + list_head = &resource->resource_list; + + list_for_each_entry(resource, list_head, list) { + struct resource *child_resource; + + if (resource->id == resource_id) + return resource; + + child_resource = resource_find(resources, resource, + resource_id); + if (child_resource) + return child_resource; + } + return NULL; +} + +static void +resource_path_print(struct dl *dl, struct resources *resources, + uint64_t resource_id) +{ + struct resource *resource, *parent_resource; + const char del[] = "/"; + int path_len = 0; + char *path; + + resource = resource_find(resources, NULL, resource_id); + if (!resource) + return; + + for (parent_resource = resource; parent_resource; + parent_resource = parent_resource->parent) + path_len += strlen(parent_resource->name) + 1; + + path_len++; + path = calloc(1, path_len); + if (!path) + return; + + path += path_len - 1; + for (parent_resource = resource; parent_resource; + parent_resource = parent_resource->parent) { + path -= strlen(parent_resource->name); + memcpy(path, parent_resource->name, + strlen(parent_resource->name)); + path -= strlen(del); + memcpy(path, del, strlen(del)); + } + pr_out_str(dl, "resource_path", path); + free(path); +} + static int dpipe_table_show(struct dpipe_ctx *ctx, struct nlattr *nl) { struct nlattr *nla_table[DEVLINK_ATTR_MAX + 1] = {}; + struct dpipe_table *table; + uint32_t resource_units; bool counters_enabled; - const char *name; + bool resource_valid; uint32_t size; int err; @@ -3259,15 +3509,36 @@ static int dpipe_table_show(struct dpipe_ctx *ctx, struct nlattr *nl) return -EINVAL; } - name = mnl_attr_get_str(nla_table[DEVLINK_ATTR_DPIPE_TABLE_NAME]); + table = dpipe_table_alloc(); + if (!table) + return -ENOMEM; + + table->name = strdup(mnl_attr_get_str(nla_table[DEVLINK_ATTR_DPIPE_TABLE_NAME])); size = mnl_attr_get_u32(nla_table[DEVLINK_ATTR_DPIPE_TABLE_SIZE]); counters_enabled = !!mnl_attr_get_u8(nla_table[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED]); - pr_out_str(ctx->dl, "name", name); + resource_valid = !!nla_table[DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID]; + if (resource_valid) { + table->resource_id = mnl_attr_get_u64(nla_table[DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID]); + table->resource_valid = true; + } + + list_add_tail(&table->list, &ctx->tables->table_list); + 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"); + 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); + } + pr_out_array_start(ctx->dl, "match"); if (dpipe_table_matches_show(ctx, nla_table[DEVLINK_ATTR_DPIPE_TABLE_MATCHES])) goto err_matches_show; @@ -3292,15 +3563,18 @@ static int dpipe_tables_show(struct dpipe_ctx *ctx, struct nlattr **tb) struct nlattr *nla_table; mnl_attr_for_each_nested(nla_table, nla_tables) { - pr_out_handle_start_arr(ctx->dl, tb); + if (ctx->print_tables) + pr_out_handle_start_arr(ctx->dl, tb); if (dpipe_table_show(ctx, nla_table)) goto err_table_show; - pr_out_handle_end(ctx->dl); + if (ctx->print_tables) + pr_out_handle_end(ctx->dl); } return 0; err_table_show: - pr_out_handle_end(ctx->dl); + if (ctx->print_tables) + pr_out_handle_end(ctx->dl); return -EINVAL; } @@ -3320,39 +3594,68 @@ static int cmd_dpipe_table_show_cb(const struct nlmsghdr *nlh, void *data) return MNL_CB_OK; } +static int cmd_resource_dump_cb(const struct nlmsghdr *nlh, void *data); + static int cmd_dpipe_table_show(struct dl *dl) { struct nlmsghdr *nlh; - struct dpipe_ctx *ctx; + struct dpipe_ctx dpipe_ctx = {}; + struct resource_ctx resource_ctx = {}; uint16_t flags = NLM_F_REQUEST; int err; - ctx = dpipe_ctx_alloc(dl); - if (!ctx) - return -ENOMEM; - err = dl_argv_parse(dl, DL_OPT_HANDLE, DL_OPT_DPIPE_TABLE_NAME); if (err) - goto out; + return err; nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_HEADERS_GET, flags); + + err = dpipe_ctx_init(&dpipe_ctx, dl); + if (err) + return err; + + dpipe_ctx.print_tables = true; + dl_opts_put(nlh, dl); - err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, ctx); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, + &dpipe_ctx); if (err) { - pr_err("error get headers %s\n", strerror(ctx->err)); - goto out; + pr_err("error get headers %s\n", strerror(dpipe_ctx.err)); + goto err_headers_get; } + err = resource_ctx_init(&resource_ctx, dl); + if (err) + goto err_resource_ctx_init; + + resource_ctx.print_resources = false; + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RESOURCE_DUMP, flags); + dl_opts_put(nlh, dl); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_resource_dump_cb, + &resource_ctx); + if (err) { + pr_err("error get resources %s\n", strerror(resource_ctx.err)); + goto err_resource_dump; + } + + dpipe_ctx.resources = resource_ctx.resources; flags = NLM_F_REQUEST | NLM_F_ACK; nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_TABLE_GET, flags); dl_opts_put(nlh, dl); pr_out_section_start(dl, "table"); - _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_table_show_cb, ctx); + _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_table_show_cb, &dpipe_ctx); pr_out_section_end(dl); -out: - dpipe_ctx_clear(ctx); - dpipe_ctx_free(ctx); + + resource_ctx_fini(&resource_ctx); + dpipe_ctx_fini(&dpipe_ctx); + return 0; + +err_resource_dump: + resource_ctx_fini(&resource_ctx); +err_resource_ctx_init: +err_headers_get: + dpipe_ctx_fini(&dpipe_ctx); return err; } @@ -3720,13 +4023,13 @@ static int cmd_dpipe_table_entry_dump_cb(const struct nlmsghdr *nlh, void *data) static int cmd_dpipe_table_dump(struct dl *dl) { struct nlmsghdr *nlh; - struct dpipe_ctx *ctx; + struct dpipe_ctx ctx = {}; uint16_t flags = NLM_F_REQUEST; int err; - ctx = dpipe_ctx_alloc(dl); - if (!ctx) - return -ENOMEM; + err = dpipe_ctx_init(&ctx, dl); + if (err) + return err; err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_DPIPE_TABLE_NAME, 0); if (err) @@ -3734,9 +4037,9 @@ static int cmd_dpipe_table_dump(struct dl *dl) nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_HEADERS_GET, flags); dl_opts_put(nlh, dl); - err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, ctx); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, &ctx); if (err) { - pr_err("error get headers %s\n", strerror(ctx->err)); + pr_err("error get headers %s\n", strerror(ctx.err)); goto out; } @@ -3745,11 +4048,10 @@ static int cmd_dpipe_table_dump(struct dl *dl) dl_opts_put(nlh, dl); pr_out_section_start(dl, "table_entry"); - _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_table_entry_dump_cb, ctx); + _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_table_entry_dump_cb, &ctx); pr_out_section_end(dl); out: - dpipe_ctx_clear(ctx); - dpipe_ctx_free(ctx); + dpipe_ctx_fini(&ctx); return err; } @@ -3800,11 +4102,368 @@ static int cmd_dpipe(struct dl *dl) return -ENOENT; } +static int +resource_parse(struct resource_ctx *ctx, struct resource *resource, + struct nlattr **nla_resource) +{ + if (!nla_resource[DEVLINK_ATTR_RESOURCE_NAME] || + !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE] || + !nla_resource[DEVLINK_ATTR_RESOURCE_ID] || + !nla_resource[DEVLINK_ATTR_RESOURCE_UNIT] || + !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MIN] || + !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MAX] || + !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_GRAN]) { + return -EINVAL; + } + + resource->name = strdup(mnl_attr_get_str(nla_resource[DEVLINK_ATTR_RESOURCE_NAME])); + resource->size = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE]); + resource->id = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_ID]); + resource->unit = mnl_attr_get_u8(nla_resource[DEVLINK_ATTR_RESOURCE_UNIT]); + resource->size_min = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MIN]); + resource->size_max = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MAX]); + resource->size_gran = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_GRAN]); + + if (nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_NEW]) + resource->size_new = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_NEW]); + else + resource->size_new = resource->size; + + if (nla_resource[DEVLINK_ATTR_RESOURCE_OCC]) { + resource->size_occ = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_OCC]); + resource->occ_valid = true; + } + + if (resource->size_new != resource->size) + ctx->pending_change = true; + + return 0; +} + +static int +resource_get(struct resource_ctx *ctx, struct resource *resource, + struct resource *parent_resource, struct nlattr *nl) +{ + struct nlattr *nla_resource[DEVLINK_ATTR_MAX + 1] = {}; + struct nlattr *nla_child_resource; + struct nlattr *nla_resources; + bool top = false; + int err; + + if (!resource) { + nla_resources = nl; + top = true; + goto out; + } + + err = mnl_attr_parse_nested(nl, attr_cb, nla_resource); + if (err != MNL_CB_OK) + return -EINVAL; + + err = resource_parse(ctx, resource, nla_resource); + if (err) + return err; + + resource->parent = parent_resource; + if (!nla_resource[DEVLINK_ATTR_RESOURCE_LIST]) + return 0; + + resource->size_valid = !!mnl_attr_get_u8(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_VALID]); + nla_resources = nla_resource[DEVLINK_ATTR_RESOURCE_LIST]; +out: + mnl_attr_for_each_nested(nla_child_resource, nla_resources) { + struct resource *child_resource; + struct list_head *list; + + child_resource = resource_alloc(); + if (!child_resource) + return -ENOMEM; + + if (top) + list = &ctx->resources->resource_list; + else + list = &resource->resource_list; + + list_add_tail(&child_resource->list, list); + err = resource_get(ctx, child_resource, resource, + nla_child_resource); + if (err) + return err; + } + + return 0; +} + +static const char *resource_unit_str_get(enum devlink_resource_unit unit) +{ + switch (unit) { + case DEVLINK_RESOURCE_UNIT_ENTRY: return "entry"; + default: return "<unknown unit>"; + } +} + +static void resource_show(struct resource *resource, + struct resource_ctx *ctx) +{ + struct resource *child_resource; + struct dpipe_table *table; + struct dl *dl = ctx->dl; + bool array = false; + + pr_out_str(dl, "name", resource->name); + if (dl->verbose) + resource_path_print(dl, ctx->resources, resource->id); + pr_out_uint(dl, "size", resource->size); + if (resource->size != resource->size_new) + pr_out_uint(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)); + + if (resource->size_min != resource->size_max) { + pr_out_uint(dl, "size_min", resource->size_min); + pr_out_uint(dl, "size_max", resource->size_max); + pr_out_uint(dl, "size_gran", resource->size_gran); + } + + list_for_each_entry(table, &ctx->tables->table_list, list) + if (table->resource_id == resource->id && + table->resource_valid) + array = true; + + if (array) + pr_out_array_start(dl, "dpipe_tables"); + else + pr_out_str(dl, "dpipe_tables", "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); + pr_out_entry_end(dl); + } + if (array) + pr_out_array_end(dl); + + if (list_empty(&resource->resource_list)) + return; + + if (ctx->pending_change) + pr_out_str(dl, "size_valid", 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); + resource_show(child_resource, ctx); + pr_out_entry_end(dl); + } + pr_out_array_end(dl); +} + +static void +resources_show(struct resource_ctx *ctx, struct nlattr **tb) +{ + struct resources *resources = ctx->resources; + struct resource *resource; + + list_for_each_entry(resource, &resources->resource_list, list) { + pr_out_handle_start_arr(ctx->dl, tb); + resource_show(resource, ctx); + pr_out_handle_end(ctx->dl); + } +} + +static int resources_get(struct resource_ctx *ctx, struct nlattr **tb) +{ + return resource_get(ctx, NULL, NULL, tb[DEVLINK_ATTR_RESOURCE_LIST]); +} + +static int cmd_resource_dump_cb(const struct nlmsghdr *nlh, void *data) +{ + struct resource_ctx *ctx = data; + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + int err; + + mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); + if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || + !tb[DEVLINK_ATTR_RESOURCE_LIST]) + return MNL_CB_ERROR; + + err = resources_get(ctx, tb); + if (err) { + ctx->err = err; + return MNL_CB_ERROR; + } + + if (ctx->print_resources) + resources_show(ctx, tb); + + return MNL_CB_OK; +} + +static int cmd_resource_show(struct dl *dl) +{ + struct nlmsghdr *nlh; + struct dpipe_ctx dpipe_ctx = {}; + struct resource_ctx resource_ctx = {}; + int err; + + err = dl_argv_parse(dl, DL_OPT_HANDLE, 0); + if (err) + return err; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_TABLE_GET, + NLM_F_REQUEST); + dl_opts_put(nlh, dl); + + err = dpipe_ctx_init(&dpipe_ctx, dl); + if (err) + return err; + + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_table_show_cb, + &dpipe_ctx); + if (err) { + pr_err("error get tables %s\n", strerror(dpipe_ctx.err)); + goto out; + } + + err = resource_ctx_init(&resource_ctx, dl); + if (err) + goto out; + + resource_ctx.print_resources = true; + resource_ctx.tables = dpipe_ctx.tables; + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RESOURCE_DUMP, + NLM_F_REQUEST | NLM_F_ACK); + dl_opts_put(nlh, dl); + pr_out_section_start(dl, "resources"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_resource_dump_cb, + &resource_ctx); + pr_out_section_end(dl); + resource_ctx_fini(&resource_ctx); +out: + dpipe_ctx_fini(&dpipe_ctx); + return err; +} + +static void cmd_resource_help(void) +{ + pr_err("Usage: devlink resource show DEV\n" + " devlink resource set DEV path PATH size SIZE\n"); +} + +static struct resource * +resource_find_by_name(struct list_head *list, char *name) +{ + struct resource *resource; + + list_for_each_entry(resource, list, list) { + if (!strcmp(resource->name, name)) + return resource; + } + return NULL; +} + +static int +resource_path_parse(struct resource_ctx *ctx, const char *resource_path, + uint32_t *p_resource_id, bool *p_resource_valid) +{ + struct resource *resource; + uint32_t resource_id = 0; + char *resource_path_dup; + struct list_head *list; + const char del[] = "/"; + char *resource_name; + + resource_path_dup = strdup(resource_path); + list = &ctx->resources->resource_list; + resource_name = strtok(resource_path_dup, del); + while (resource_name != NULL) { + resource = resource_find_by_name(list, resource_name); + if (!resource) + goto err_resource_lookup; + + list = &resource->resource_list; + resource_name = strtok(NULL, del); + resource_id = resource->id; + } + free(resource_path_dup); + *p_resource_valid = true; + *p_resource_id = resource_id; + return 0; + +err_resource_lookup: + free(resource_path_dup); + return -EINVAL; +} + +static int cmd_resource_set(struct dl *dl) +{ + struct nlmsghdr *nlh; + struct resource_ctx ctx = {}; + int err; + + err = resource_ctx_init(&ctx, dl); + if (err) + return err; + + ctx.print_resources = false; + err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_RESOURCE_PATH | + DL_OPT_RESOURCE_SIZE, 0); + if (err) + goto out; + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RESOURCE_DUMP, + NLM_F_REQUEST); + dl_opts_put(nlh, dl); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_resource_dump_cb, &ctx); + if (err) { + pr_err("error getting resources %s\n", strerror(ctx.err)); + goto out; + } + + err = resource_path_parse(&ctx, dl->opts.resource_path, + &dl->opts.resource_id, + &dl->opts.resource_id_valid); + if (err) { + pr_err("error parsing resource path %s\n", strerror(err)); + goto out; + } + + nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RESOURCE_SET, + NLM_F_REQUEST | NLM_F_ACK); + + dl_opts_put(nlh, dl); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); +out: + resource_ctx_fini(&ctx); + return err; +} + +static int cmd_resource(struct dl *dl) +{ + if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { + cmd_resource_help(); + return 0; + } else if (dl_argv_match(dl, "show")) { + dl_arg_inc(dl); + return cmd_resource_show(dl); + } else if (dl_argv_match(dl, "set")) { + dl_arg_inc(dl); + return cmd_resource_set(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 }\n" + "where OBJECT := { dev | port | sb | monitor | dpipe | resource }\n" " OPTIONS := { -V[ersion] | -n[no-nice-names] | -j[json] | -p[pretty] | -v[verbose] }\n"); } @@ -3831,6 +4490,9 @@ static int dl_cmd(struct dl *dl, int argc, char **argv) } else if (dl_argv_match(dl, "dpipe")) { dl_arg_inc(dl); return cmd_dpipe(dl); + } else if (dl_argv_match(dl, "resource")) { + dl_arg_inc(dl); + return cmd_resource(dl); } pr_err("Object \"%s\" not found\n", dl_argv(dl)); return -ENOENT; diff --git a/devlink/mnlg.c b/devlink/mnlg.c index 9e27de27..3d28453a 100644 --- a/devlink/mnlg.c +++ b/devlink/mnlg.c @@ -18,6 +18,8 @@ #include <libmnl/libmnl.h> #include <linux/genetlink.h> +#include "libnetlink.h" +#include "utils.h" #include "mnlg.h" struct mnlg_socket { @@ -60,6 +62,39 @@ int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh) return mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len); } +static int mnlg_cb_noop(const struct nlmsghdr *nlh, void *data) +{ + return MNL_CB_OK; +} + +static int mnlg_cb_error(const struct nlmsghdr *nlh, void *data) +{ + const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh); + + /* Netlink subsystems returns the errno value with different signess */ + if (err->error < 0) + errno = -err->error; + else + errno = err->error; + + if (nl_dump_ext_ack(nlh, NULL)) + return MNL_CB_ERROR; + + return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR; +} + +static int mnlg_cb_stop(const struct nlmsghdr *nlh, void *data) +{ + return MNL_CB_STOP; +} + +static mnl_cb_t mnlg_cb_array[NLMSG_MIN_TYPE] = { + [NLMSG_NOOP] = mnlg_cb_noop, + [NLMSG_ERROR] = mnlg_cb_error, + [NLMSG_DONE] = mnlg_cb_stop, + [NLMSG_OVERRUN] = mnlg_cb_noop, +}; + int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data) { int err; @@ -69,8 +104,9 @@ int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data) MNL_SOCKET_BUFFER_SIZE); if (err <= 0) break; - err = mnl_cb_run(nlg->buf, err, nlg->seq, nlg->portid, - data_cb, data); + err = mnl_cb_run2(nlg->buf, err, nlg->seq, nlg->portid, + data_cb, data, mnlg_cb_array, + ARRAY_SIZE(mnlg_cb_array)); } while (err > 0); return err; @@ -220,6 +256,7 @@ struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version) { struct mnlg_socket *nlg; struct nlmsghdr *nlh; + int one = 1; int err; nlg = malloc(sizeof(*nlg)); @@ -234,6 +271,16 @@ struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version) if (!nlg->nl) goto err_mnl_socket_open; + err = mnl_socket_setsockopt(nlg->nl, NETLINK_CAP_ACK, &one, + sizeof(one)); + if (err) + goto err_mnl_set_ack; + + err = mnl_socket_setsockopt(nlg->nl, NETLINK_EXT_ACK, &one, + sizeof(one)); + if (err) + goto err_mnl_set_ext_ack; + err = mnl_socket_bind(nlg->nl, 0, MNL_SOCKET_AUTOPID); if (err < 0) goto err_mnl_socket_bind; @@ -258,6 +305,8 @@ struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version) err_mnlg_socket_recv_run: err_mnlg_socket_send: err_mnl_socket_bind: +err_mnl_set_ext_ack: +err_mnl_set_ack: mnl_socket_close(nlg->nl); err_mnl_socket_open: free(nlg->buf); diff --git a/include/color.h b/include/color.h index f6c351b7..c80359d3 100644 --- a/include/color.h +++ b/include/color.h @@ -13,7 +13,6 @@ enum color_attr { }; void enable_color(void); -void check_if_color_enabled(void); void set_color_palette(void); int color_fprintf(FILE *fp, enum color_attr attr, const char *fmt, ...); enum color_attr ifa_family_color(__u8 ifa_family); diff --git a/include/json_writer.h b/include/json_writer.h index 1516aafb..45459fa2 100644 --- a/include/json_writer.h +++ b/include/json_writer.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */ /* * Simple streaming JSON writer * * This takes care of the annoying bits of JSON syntax like the commas * after elements * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * * Authors: Stephen Hemminger <stephen@networkplumber.org> */ diff --git a/include/libnetlink.h b/include/libnetlink.h index d6322190..9d9249e6 100644 --- a/include/libnetlink.h +++ b/include/libnetlink.h @@ -109,6 +109,7 @@ int rtnl_send(struct rtnl_handle *rth, const void *buf, int) __attribute__((warn_unused_result)); int rtnl_send_check(struct rtnl_handle *rth, const void *buf, int) __attribute__((warn_unused_result)); +int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn); int addattr(struct nlmsghdr *n, int maxlen, int type); int addattr8(struct nlmsghdr *n, int maxlen, int type, __u8 data); diff --git a/include/list.h b/include/list.h index 5af737c7..5d86b131 100644 --- a/include/list.h +++ b/include/list.h @@ -108,6 +108,11 @@ static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) n->pprev = &h->first; } +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + #define hlist_for_each(pos, head) \ for (pos = (head)->first; pos ; pos = pos->next) @@ -172,6 +172,7 @@ int main(int argc, char **argv) { char *basename; char *batch_file = NULL; + int color = 0; basename = strrchr(argv[0], '/'); if (basename == NULL) @@ -271,7 +272,7 @@ int main(int argc, char **argv) } rcvbuf = size; } else if (matches(opt, "-color") == 0) { - enable_color(); + ++color; } else if (matches(opt, "-help") == 0) { usage(); } else if (matches(opt, "-netns") == 0) { @@ -291,8 +292,8 @@ int main(int argc, char **argv) _SL_ = oneline ? "\\" : "\n"; - if (json) - check_if_color_enabled(); + if (color && !json) + enable_color(); if (batch_file) return batch(batch_file); diff --git a/ip/ipaddress.c b/ip/ipaddress.c index 087fa68a..aecc9a1d 100644 --- a/ip/ipaddress.c +++ b/ip/ipaddress.c @@ -416,10 +416,10 @@ static void print_vfinfo(FILE *fp, struct rtattr *vfinfo) } if (vf_tx_rate->rate) - print_int(PRINT_ANY, - "tx_rate", - ", tx rate %d (Mbps)", - vf_tx_rate->rate); + print_uint(PRINT_ANY, + "tx_rate", + ", tx rate %u (Mbps)", + vf_tx_rate->rate); if (vf[IFLA_VF_RATE]) { struct ifla_vf_rate *vf_rate = RTA_DATA(vf[IFLA_VF_RATE]); @@ -428,14 +428,14 @@ static void print_vfinfo(FILE *fp, struct rtattr *vfinfo) if (is_json_context()) { open_json_object("rate"); - print_int(PRINT_JSON, "max_tx", NULL, max_tx); - print_int(PRINT_ANY, "min_tx", NULL, min_tx); + print_uint(PRINT_JSON, "max_tx", NULL, max_tx); + print_uint(PRINT_ANY, "min_tx", NULL, min_tx); close_json_object(); } else { if (max_tx) - fprintf(fp, ", max_tx_rate %dMbps", max_tx); + fprintf(fp, ", max_tx_rate %uMbps", max_tx); if (min_tx) - fprintf(fp, ", min_tx_rate %dMbps", min_tx); + fprintf(fp, ", min_tx_rate %uMbps", min_tx); } } diff --git a/ip/iplink.c b/ip/iplink.c index 02042ed6..29b7062f 100644 --- a/ip/iplink.c +++ b/ip/iplink.c @@ -275,8 +275,9 @@ static int nl_get_ll_addr_len(unsigned int dev_index) return -1; } + len = RTA_PAYLOAD(tb[IFLA_ADDRESS]); free(answer); - return RTA_PAYLOAD(tb[IFLA_ADDRESS]); + return len; } static void iplink_parse_vf_vlan_info(int vf, int *argcp, char ***argvp, @@ -2039,6 +2039,7 @@ static int bpf_apply_relo_data(struct bpf_elf_ctx *ctx, insns[ioff].code != (BPF_LD | BPF_IMM | BPF_DW)) { fprintf(stderr, "ELF contains relo data for non ld64 instruction at offset %u! Compiler bug?!\n", ioff); + fprintf(stderr, " - Current section: %s\n", data_relo->sec_name); if (ioff < num_insns && insns[ioff].code == (BPF_JMP | BPF_CALL)) fprintf(stderr, " - Try to annotate functions with always_inline attribute!\n"); diff --git a/lib/color.c b/lib/color.c index a13a4930..da1f516c 100644 --- a/lib/color.c +++ b/lib/color.c @@ -92,14 +92,6 @@ void set_color_palette(void) is_dark_bg = 1; } -void check_if_color_enabled(void) -{ - if (color_is_enabled) { - fprintf(stderr, "Option \"-json\" conflicts with \"-color\".\n"); - exit(1); - } -} - int color_fprintf(FILE *fp, enum color_attr attr, const char *fmt, ...) { int ret = 0; diff --git a/lib/json_writer.c b/lib/json_writer.c index 0d910dc0..68401ae3 100644 --- a/lib/json_writer.c +++ b/lib/json_writer.c @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */ /* * Simple streaming JSON writer * * This takes care of the annoying bits of JSON syntax like the commas * after elements * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * * Authors: Stephen Hemminger <stephen@networkplumber.org> */ diff --git a/lib/libnetlink.c b/lib/libnetlink.c index 7ca47b22..928de1dd 100644 --- a/lib/libnetlink.c +++ b/lib/libnetlink.c @@ -65,7 +65,7 @@ static int err_attr_cb(const struct nlattr *attr, void *data) } /* dump netlink extended ack error message */ -static int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn) +int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn) { struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {}; const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh); @@ -120,7 +120,7 @@ static int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn) #warning "libmnl required for error support" /* No extended error ack without libmnl */ -static int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn) +int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn) { return 0; } @@ -670,8 +670,9 @@ next: free(buf); if (h->nlmsg_seq == seq) return 0; - else + else if (i < iovlen) goto next; + return 0; } if (rtnl->proto != NETLINK_SOCK_DIAG && diff --git a/lib/namespace.c b/lib/namespace.c index 30b51388..6f3356d0 100644 --- a/lib/namespace.c +++ b/lib/namespace.c @@ -7,6 +7,7 @@ * 2 of the License, or (at your option) any later version. */ +#include <sys/statvfs.h> #include <fcntl.h> #include <dirent.h> #include <limits.h> @@ -46,6 +47,8 @@ int netns_switch(char *name) { char net_path[PATH_MAX]; int netns; + unsigned long mountflags = 0; + struct statvfs fsstat; snprintf(net_path, sizeof(net_path), "%s/%s", NETNS_RUN_DIR, name); netns = open(net_path, O_RDONLY | O_CLOEXEC); @@ -73,12 +76,25 @@ int netns_switch(char *name) strerror(errno)); return -1; } + /* Mount a version of /sys that describes the network namespace */ - if (umount2("/sys", MNT_DETACH) < 0) { - fprintf(stderr, "umount of /sys failed: %s\n", strerror(errno)); + + if (statvfs("/sys", &fsstat) < 0) { + fprintf(stderr, "could not stat /sys (not mounted?): %s\n",strerror(errno)); return -1; } - if (mount(name, "/sys", "sysfs", 0, NULL) < 0) { + if (fsstat.f_flag & ST_RDONLY) { + /* If /sys is not writable (e.g. in a container), we can't + * unmount the old /sys instance, but we can still mount a new + * read-only instance over it. */ + mountflags = MS_RDONLY; + } else { + if (umount2("/sys", MNT_DETACH) < 0) { + fprintf(stderr, "umount of /sys failed: %s\n", strerror(errno)); + return -1; + } + } + if (mount(name, "/sys", "sysfs", mountflags, NULL) < 0) { fprintf(stderr, "mount of /sys failed: %s\n",strerror(errno)); return -1; } diff --git a/lib/utils.c b/lib/utils.c index 2b8e4e8e..d6c2395d 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -1010,6 +1010,25 @@ const char *rt_addr_n2a_r(int af, int len, } case AF_PACKET: return ll_addr_n2a(addr, len, ARPHRD_VOID, buf, buflen); + case AF_BRIDGE: + { + const union { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + } *sa = addr; + + switch (sa->sa.sa_family) { + case AF_INET: + return inet_ntop(AF_INET, &sa->sin.sin_addr, + buf, buflen); + case AF_INET6: + return inet_ntop(AF_INET6, &sa->sin6.sin6_addr, + buf, buflen); + } + + /* fallthrough */ + } default: return "???"; } diff --git a/man/man8/devlink-dev.8 b/man/man8/devlink-dev.8 index b074d57a..7c749dda 100644 --- a/man/man8/devlink-dev.8 +++ b/man/man8/devlink-dev.8 @@ -42,6 +42,10 @@ devlink-dev \- devlink device configuration .BR "devlink dev eswitch show" .IR DEV +.ti -8 +.BR "devlink dev reload" +.IR DEV + .SH "DESCRIPTION" .SS devlink dev show - display devlink device attributes @@ -94,6 +98,12 @@ Set eswitch encapsulation support .I enable - Enable encapsulation support +.SS devlink dev reload - perform hot reload of the driver. + +.PP +.I "DEV" +- Specifies the devlink device to reload. + .SH "EXAMPLES" .PP devlink dev show @@ -114,6 +124,11 @@ Shows the eswitch mode of specified devlink device. devlink dev eswitch set pci/0000:01:00.0 mode switchdev .RS 4 Sets the eswitch mode of specified devlink device to switchdev. +.RE +.PP +devlink dev reload pci/0000:01:00.0 +.RS 4 +Performs hot reload of specified devlink device. .SH SEE ALSO .BR devlink (8), diff --git a/man/man8/devlink-resource.8 b/man/man8/devlink-resource.8 new file mode 100644 index 00000000..b8f78806 --- /dev/null +++ b/man/man8/devlink-resource.8 @@ -0,0 +1,78 @@ +.TH DEVLINK\-RESOURCE 8 "11 Feb 2018" "iproute2" "Linux" +.SH NAME +devlink-resource \- devlink device resource configuration +.SH SYNOPSIS +.sp +.ad l +.in +8 +.ti -8 +.B devlink +.RI "[ " OPTIONS " ]" +.B resource +.RI " { " COMMAND " | " +.BR help " }" +.sp + +.ti -8 +.IR OPTIONS " := { " +\fB\-v\fR[\fIerbose\fR] } + +.ti -8 +.B devlink resource show +.IR DEV + +.ti -8 +.B devlink resource help + +.ti -8 +.BR "devlink resource set" +.IR DEV +.BI path " RESOURCE_PATH" +.BI size " RESOURCE_SIZE" + +.SH "DESCRIPTION" +.SS devlink resource show - display devlink device's resosources + +.PP +.I "DEV" +- specifies the devlink device to show. + +.in +4 +Format is: +.in +2 +BUS_NAME/BUS_ADDRESS + +.SS devlink resource set - sets resource size of specific resource + +.PP +.I "DEV" +- specifies the devlink device. + +.TP +.BI path " RESOURCE_PATH" +Resource's path. + +.TP +.BI size " RESOURCE_SIZE" +The new resource's size. + +.SH "EXAMPLES" +.PP +devlink resource show pci/0000:01:00.0 +.RS 4 +Shows the resources of the specified devlink device. +.RE +.PP +devlink resource set pci/0000:01:00.0 /kvd/linear 98304 +.RS 4 +Sets the size of the specified resource for the specified devlink device. + +.SH SEE ALSO +.BR devlink (8), +.BR devlink-port (8), +.BR devlink-sb (8), +.BR devlink-monitor (8), +.br + +.SH AUTHOR +Arkadi Sharshevsky <arkadis@mellanox.com> diff --git a/man/man8/devlink.8 b/man/man8/devlink.8 index a975ef34..b83909da 100644 --- a/man/man8/devlink.8 +++ b/man/man8/devlink.8 @@ -103,6 +103,7 @@ Exit status is 0 if command was successful or a positive integer upon failure. .BR devlink-port (8), .BR devlink-monitor (8), .BR devlink-sb (8), +.BR devlink-resource (8), .br .SH REPORTING BUGS diff --git a/man/man8/tc-bpf.8 b/man/man8/tc-bpf.8 index 2e9812ed..d311f295 100644 --- a/man/man8/tc-bpf.8 +++ b/man/man8/tc-bpf.8 @@ -14,6 +14,10 @@ CLS_NAME ] [ UDS_FILE ] [ .B verbose ] [ +.B direct-action +| +.B da +] [ .B skip_hw | .B skip_sw @@ -141,6 +145,11 @@ if set, it will dump the eBPF verifier output, even if loading the eBPF program was successful. By default, only on error, the verifier log is being emitted to the user. +.SS direct-action | da +instructs eBPF classifier to not invoke external TC actions, instead use the +TC actions return codes (\fBTC_ACT_OK\fR, \fBTC_ACT_SHOT\fR etc.) for +classifiers. + .SS skip_hw | skip_sw hardware offload control flags. By default TC will try to offload filters to hardware if possible. diff --git a/man/man8/tc.8 b/man/man8/tc.8 index a20b06bb..3dc30ee4 100644 --- a/man/man8/tc.8 +++ b/man/man8/tc.8 @@ -82,12 +82,20 @@ tc \- show / manipulate traffic control settings \fIBLOCK_INDEX\fR .P +.B tc +.RI "[ " OPTIONS " ]" +.B monitor [ file +\fIFILENAME\fR +.B ] + +.P .ti 8 .IR OPTIONS " := {" \fB[ -force ] -b\fR[\fIatch\fR] \fB[ filename ] \fR| \fB[ \fB-n\fR[\fIetns\fR] name \fB] \fR| \fB[ \fB-nm \fR| \fB-nam\fR[\fIes\fR] \fB] \fR| -\fB[ \fR{ \fB-cf \fR| \fB-c\fR[\fIonf\fR] \fR} \fB[ filename ] \fB] \fR} +\fB[ \fR{ \fB-cf \fR| \fB-c\fR[\fIonf\fR] \fR} \fB[ filename ] \fB] \fR +\fB[ -t\fR[imestamp\fR] \fB\] \fR| \fB[ -t\fR[short\fR] \fB]\fR } .ti 8 .IR FORMAT " := {" @@ -617,6 +625,17 @@ link Only available for qdiscs and performs a replace where the node must exist already. +.SH MONITOR +The\fB\ tc\fR\ utility can monitor events generated by the kernel such as +adding/deleting qdiscs, filters or actions, or modifying existing ones. + +The following command is available for\fB\ monitor\fR\ : +.TP +\fBfile\fR +If the file option is given, the \fBtc\fR does not listen to kernel events, but opens +the given file and dumps its contents. The file has to be in binary +format and contain netlink messages. + .SH OPTIONS .TP @@ -654,6 +673,16 @@ to specifies path to the config file. This option is used in conjunction with other options (e.g. .BR -nm ")." +.TP +.BR "\-t", " \-timestamp" +When\fB\ tc monitor\fR\ runs, print timestamp before the event message in format: + Timestamp: <Day> <Month> <DD> <hh:mm:ss> <YYYY> <usecs> usec + +.TP +.BR "\-ts", " \-tshort" +When\fB\ tc monitor\fR\ runs, prints short timestamp before the event message in format: + [<YYYY>-<MM>-<DD>T<hh:mm:ss>.<ms>] + .SH FORMAT The show command has additional formatting options: @@ -239,6 +239,7 @@ struct filter { uint64_t families; struct ssfilter *f; bool kill; + struct rtnl_handle *rth_for_killing; }; #define FAMILY_MASK(family) ((uint64_t)1 << (family)) @@ -1196,10 +1197,15 @@ newline: /* Render buffered output with spacing and delimiters, then free up buffers */ static void render(int screen_width) { - struct buf_token *token = (struct buf_token *)buffer.head->data; + struct buf_token *token; int printed, line_started = 0; struct column *f; + if (!buffer.head) + return; + + token = (struct buf_token *)buffer.head->data; + /* Ensure end alignment of last token, it wasn't necessarily flushed */ buffer.tail->end += buffer.cur->len % 2; @@ -4265,6 +4271,7 @@ static int generic_show_sock(const struct sockaddr_nl *addr, switch (r->sdiag_family) { case AF_INET: case AF_INET6: + inet_arg.rth = inet_arg.f->rth_for_killing; return show_one_inet_sock(addr, nlh, &inet_arg); case AF_UNIX: return unix_show_sock(addr, nlh, arg); @@ -4283,7 +4290,7 @@ static int handle_follow_request(struct filter *f) { int ret = 0; int groups = 0; - struct rtnl_handle rth; + struct rtnl_handle rth, rth2; if (f->families & FAMILY_MASK(AF_INET) && f->dbs & (1 << TCP_DB)) groups |= 1 << (SKNLGRP_INET_TCP_DESTROY - 1); @@ -4303,10 +4310,20 @@ static int handle_follow_request(struct filter *f) rth.dump = 0; rth.local.nl_pid = 0; + if (f->kill) { + if (rtnl_open_byproto(&rth2, groups, NETLINK_SOCK_DIAG)) { + rtnl_close(&rth); + return -1; + } + f->rth_for_killing = &rth2; + } + if (rtnl_dump_filter(&rth, generic_show_sock, f)) ret = -1; rtnl_close(&rth); + if (f->rth_for_killing) + rtnl_close(f->rth_for_killing); return ret; } @@ -395,8 +395,10 @@ static int res_qp_parse_cb(const struct nlmsghdr *nlh, void *data) comm = get_task_name(pid); } - if (rd_check_is_filtered(rd, "pid", pid)) + if (rd_check_is_filtered(rd, "pid", pid)) { + free(comm); continue; + } if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) /* discard const from mnl_attr_get_str */ diff --git a/tc/m_action.c b/tc/m_action.c index 744bde41..6c3049c7 100644 --- a/tc/m_action.c +++ b/tc/m_action.c @@ -365,7 +365,7 @@ tc_print_action(FILE *f, const struct rtattr *arg, unsigned short tot_acts) return tc_print_action_flush(f, tb[0]); open_json_array(PRINT_JSON, "actions"); - for (i = 0; i < tot_acts; i++) { + for (i = 0; i <= tot_acts; i++) { if (tb[i]) { open_json_object(NULL); print_uint(PRINT_ANY, "order", @@ -128,7 +128,6 @@ opt_bpf: parse_action_control_dflt(&argc, &argv, &parm.action, false, TC_ACT_PIPE); - NEXT_ARG_FWD(); if (argc) { if (matches(*argv, "index") == 0) { diff --git a/tc/m_connmark.c b/tc/m_connmark.c index 56d1db92..a6ee532d 100644 --- a/tc/m_connmark.c +++ b/tc/m_connmark.c @@ -82,7 +82,6 @@ parse_connmark(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, } parse_action_control_dflt(&argc, &argv, &sel.action, false, TC_ACT_PIPE); - NEXT_ARG_FWD(); if (argc) { if (matches(*argv, "index") == 0) { diff --git a/tc/m_csum.c b/tc/m_csum.c index 42d37aba..8391071d 100644 --- a/tc/m_csum.c +++ b/tc/m_csum.c @@ -124,7 +124,6 @@ parse_csum(struct action_util *a, int *argc_p, } parse_action_control_dflt(&argc, &argv, &sel.action, false, TC_ACT_OK); - NEXT_ARG_FWD(); if (argc) { if (matches(*argv, "index") == 0) { diff --git a/tc/m_gact.c b/tc/m_gact.c index ccad4f22..a0a3c33d 100644 --- a/tc/m_gact.c +++ b/tc/m_gact.c @@ -87,12 +87,10 @@ parse_gact(struct action_util *a, int *argc_p, char ***argv_p, if (argc < 0) return -1; - if (matches(*argv, "gact") != 0 && - parse_action_control(&argc, &argv, &p.action, false) == -1) { + if (!matches(*argv, "gact")) + NEXT_ARG_FWD(); + if (parse_action_control(&argc, &argv, &p.action, false)) usage(); /* does not return */ - } - - NEXT_ARG_FWD(); #ifdef CONFIG_GACT_PROB if (argc > 0) { @@ -113,7 +111,6 @@ parse_gact(struct action_util *a, int *argc_p, char ***argv_p, if (parse_action_control(&argc, &argv, &pp.paction, false) == -1) usage(); - NEXT_ARG_FWD(); if (get_u16(&pp.pval, *argv, 10)) { fprintf(stderr, "Illegal probability val 0x%x\n", @@ -159,7 +159,6 @@ static int parse_ife(struct action_util *a, int *argc_p, char ***argv_p, parse_action_control_dflt(&argc, &argv, &p.action, false, TC_ACT_PIPE); - NEXT_ARG_FWD(); if (argc) { if (matches(*argv, "index") == 0) { NEXT_ARG(); diff --git a/tc/m_mirred.c b/tc/m_mirred.c index b25b9acc..c7f7318b 100644 --- a/tc/m_mirred.c +++ b/tc/m_mirred.c @@ -103,6 +103,7 @@ parse_direction(struct action_util *a, int *argc_p, char ***argv_p, while (argc > 0) { if (matches(*argv, "action") == 0) { + NEXT_ARG(); break; } else if (!egress && matches(*argv, "egress") == 0) { egress = 1; @@ -200,10 +201,8 @@ parse_direction(struct action_util *a, int *argc_p, char ***argv_p, } - if (p.eaction == TCA_EGRESS_MIRROR || p.eaction == TCA_INGRESS_MIRROR) { + if (p.eaction == TCA_EGRESS_MIRROR || p.eaction == TCA_INGRESS_MIRROR) parse_action_control(&argc, &argv, &p.action, false); - NEXT_ARG_FWD(); - } if (argc) { if (iok && matches(*argv, "index") == 0) { @@ -116,7 +116,6 @@ parse_nat(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct parse_action_control_dflt(&argc, &argv, &sel.action, false, TC_ACT_OK); - NEXT_ARG_FWD(); if (argc) { if (matches(*argv, "index") == 0) { NEXT_ARG(); diff --git a/tc/m_pedit.c b/tc/m_pedit.c index 2d41f0a0..b9cb5ffb 100644 --- a/tc/m_pedit.c +++ b/tc/m_pedit.c @@ -672,7 +672,6 @@ int parse_pedit(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, parse_action_control_dflt(&argc, &argv, &sel.sel.action, false, TC_ACT_OK); - NEXT_ARG_FWD(); if (argc) { if (matches(*argv, "index") == 0) { NEXT_ARG(); diff --git a/tc/m_police.c b/tc/m_police.c index f85da49f..f3b07f7b 100644 --- a/tc/m_police.c +++ b/tc/m_police.c @@ -150,15 +150,18 @@ int act_parse_police(struct action_util *a, int *argc_p, char ***argv_p, matches(*argv, "shot") == 0 || matches(*argv, "continue") == 0 || matches(*argv, "pass") == 0 || + matches(*argv, "ok") == 0 || matches(*argv, "pipe") == 0 || matches(*argv, "goto") == 0) { - if (parse_action_control(&argc, &argv, &p.action, false)) - return -1; + if (!parse_action_control(&argc, &argv, &p.action, false)) + goto action_ctrl_ok; + return -1; } else if (strcmp(*argv, "conform-exceed") == 0) { NEXT_ARG(); - if (parse_action_control_slash(&argc, &argv, &p.action, - &presult, true)) - return -1; + if (!parse_action_control_slash(&argc, &argv, &p.action, + &presult, true)) + goto action_ctrl_ok; + return -1; } else if (matches(*argv, "overhead") == 0) { NEXT_ARG(); if (get_u16(&overhead, *argv, 10)) { @@ -174,8 +177,9 @@ int act_parse_police(struct action_util *a, int *argc_p, char ***argv_p, } else { break; } + NEXT_ARG_FWD(); +action_ctrl_ok: ok++; - argc--; argv++; } if (!ok) diff --git a/tc/m_sample.c b/tc/m_sample.c index fe892adc..f6e579f8 100644 --- a/tc/m_sample.c +++ b/tc/m_sample.c @@ -100,7 +100,6 @@ static int parse_sample(struct action_util *a, int *argc_p, char ***argv_p, parse_action_control_dflt(&argc, &argv, &p.action, false, TC_ACT_PIPE); - NEXT_ARG_FWD(); if (argc) { if (matches(*argv, "index") == 0) { NEXT_ARG(); diff --git a/tc/m_skbedit.c b/tc/m_skbedit.c index c1eda30c..db5c64ca 100644 --- a/tc/m_skbedit.c +++ b/tc/m_skbedit.c @@ -123,7 +123,6 @@ parse_skbedit(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, parse_action_control_dflt(&argc, &argv, &sel.action, false, TC_ACT_PIPE); - NEXT_ARG_FWD(); if (argc) { if (matches(*argv, "index") == 0) { NEXT_ARG(); diff --git a/tc/m_skbmod.c b/tc/m_skbmod.c index 6721f87d..2dd1bb7e 100644 --- a/tc/m_skbmod.c +++ b/tc/m_skbmod.c @@ -124,7 +124,6 @@ static int parse_skbmod(struct action_util *a, int *argc_p, char ***argv_p, parse_action_control_dflt(&argc, &argv, &p.action, false, TC_ACT_PIPE); - NEXT_ARG_FWD(); if (argc) { if (matches(*argv, "index") == 0) { NEXT_ARG(); diff --git a/tc/m_tunnel_key.c b/tc/m_tunnel_key.c index 8d4cee12..bac3c07f 100644 --- a/tc/m_tunnel_key.c +++ b/tc/m_tunnel_key.c @@ -174,7 +174,6 @@ static int parse_tunnel_key(struct action_util *a, int *argc_p, char ***argv_p, parse_action_control_dflt(&argc, &argv, &parm.action, false, TC_ACT_PIPE); - NEXT_ARG_FWD(); if (argc) { if (matches(*argv, "index") == 0) { NEXT_ARG(); diff --git a/tc/m_vlan.c b/tc/m_vlan.c index 9ee52da2..412f6aa1 100644 --- a/tc/m_vlan.c +++ b/tc/m_vlan.c @@ -131,7 +131,6 @@ static int parse_vlan(struct action_util *a, int *argc_p, char ***argv_p, parse_action_control_dflt(&argc, &argv, &parm.action, false, TC_ACT_PIPE); - NEXT_ARG_FWD(); if (argc) { if (matches(*argv, "index") == 0) { NEXT_ARG(); diff --git a/tc/tc_util.c b/tc/tc_util.c index 7f6a8e31..e0c96291 100644 --- a/tc/tc_util.c +++ b/tc/tc_util.c @@ -599,6 +599,7 @@ static int __parse_action_control(int *argc_p, char ***argv_p, int *result_p, } result |= jump_cnt; } + NEXT_ARG_FWD(); *argc_p = argc; *argv_p = argv; *result_p = result; @@ -695,8 +696,8 @@ out: int parse_action_control_slash(int *argc_p, char ***argv_p, int *result1_p, int *result2_p, bool allow_num) { + int result1, result2, argc = *argc_p; char **argv = *argv_p; - int result1, result2; char *p = strchr(*argv, '/'); if (!p) @@ -715,6 +716,9 @@ int parse_action_control_slash(int *argc_p, char ***argv_p, *result1_p = result1; *result2_p = result2; + NEXT_ARG_FWD(); + *argc_p = argc; + *argv_p = argv; return 0; } |