Merge branch 'devlink-trap' into next

Ido Schimmel  says:

====================

From: Ido Schimmel <idosch@mellanox.com>

This patchset adds devlink-trap support in iproute2.

Patch #1 increases the number of options devlink can handle.

Patches #2-#3 gradually add support for all devlink-trap commands.

Patch #4 adds a man page for devlink-trap.

See individual commit messages for example usage and output.

Changes in v2:
* Remove report option and monitor command since monitoring is done
  using drop monitor

====================

Signed-off-by: David Ahern <dsahern@gmail.com>
This commit is contained in:
David Ahern 2019-08-18 11:51:38 -07:00
commit efaef0be95
4 changed files with 581 additions and 19 deletions

View File

@ -233,9 +233,12 @@ static void ifname_map_free(struct ifname_map *ifname_map)
#define DL_OPT_HEALTH_REPORTER_NAME BIT(27) #define DL_OPT_HEALTH_REPORTER_NAME BIT(27)
#define DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD 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_AUTO_RECOVER BIT(28)
#define DL_OPT_TRAP_NAME BIT(29)
#define DL_OPT_TRAP_ACTION BIT(30)
#define DL_OPT_TRAP_GROUP_NAME BIT(31)
struct dl_opts { struct dl_opts {
uint32_t present; /* flags of present items */ uint64_t present; /* flags of present items */
char *bus_name; char *bus_name;
char *dev_name; char *dev_name;
uint32_t port_index; uint32_t port_index;
@ -269,6 +272,9 @@ struct dl_opts {
const char *reporter_name; const char *reporter_name;
uint64_t reporter_graceful_period; uint64_t reporter_graceful_period;
bool reporter_auto_recover; bool reporter_auto_recover;
const char *trap_name;
const char *trap_group_name;
enum devlink_trap_action trap_action;
}; };
struct dl { struct dl {
@ -282,6 +288,7 @@ struct dl {
bool json_output; bool json_output;
bool pretty_output; bool pretty_output;
bool verbose; bool verbose;
bool stats;
struct { struct {
bool present; bool present;
char *bus_name; char *bus_name;
@ -436,6 +443,19 @@ 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_RECOVER_COUNT] = MNL_TYPE_U64,
[DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS] = MNL_TYPE_U64, [DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS] = MNL_TYPE_U64,
[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] = MNL_TYPE_U64, [DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] = 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,
};
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,
}; };
static int attr_cb(const struct nlattr *attr, void *data) static int attr_cb(const struct nlattr *attr, void *data)
@ -454,6 +474,25 @@ static int attr_cb(const struct nlattr *attr, void *data)
return MNL_CB_OK; 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) static int ifname_map_cb(const struct nlmsghdr *nlh, void *data)
{ {
struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
@ -735,7 +774,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, static int dl_argv_handle_both(struct dl *dl, char **p_bus_name,
char **p_dev_name, uint32_t *p_port_index, 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); char *str = dl_argv_next(dl);
unsigned int slash_count; unsigned int slash_count;
@ -1014,8 +1053,22 @@ static int param_cmode_get(const char *cmodestr,
return 0; 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 {
pr_err("Unknown trap action \"%s\"\n", actionstr);
return -EINVAL;
}
return 0;
}
struct dl_args_metadata { struct dl_args_metadata {
uint32_t o_flag; uint64_t o_flag;
char err_msg[DL_ARGS_REQUIRED_MAX_ERR_LEN]; char err_msg[DL_ARGS_REQUIRED_MAX_ERR_LEN];
}; };
@ -1040,12 +1093,14 @@ static const struct dl_args_metadata dl_args_required[] = {
{DL_OPT_REGION_ADDRESS, "Region address value expected."}, {DL_OPT_REGION_ADDRESS, "Region address value expected."},
{DL_OPT_REGION_LENGTH, "Region length value expected."}, {DL_OPT_REGION_LENGTH, "Region length value expected."},
{DL_OPT_HEALTH_REPORTER_NAME, "Reporter's name is 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, static int dl_args_finding_required_validate(uint64_t o_required,
uint32_t o_found) uint64_t o_found)
{ {
uint32_t o_flag; uint64_t o_flag;
int i; int i;
for (i = 0; i < ARRAY_SIZE(dl_args_required); i++) { for (i = 0; i < ARRAY_SIZE(dl_args_required); i++) {
@ -1058,16 +1113,16 @@ static int dl_args_finding_required_validate(uint32_t o_required,
return 0; return 0;
} }
static int dl_argv_parse(struct dl *dl, uint32_t o_required, static int dl_argv_parse(struct dl *dl, uint64_t o_required,
uint32_t o_optional) uint64_t o_optional)
{ {
struct dl_opts *opts = &dl->opts; struct dl_opts *opts = &dl->opts;
uint32_t o_all = o_required | o_optional; uint64_t o_all = o_required | o_optional;
uint32_t o_found = 0; uint64_t o_found = 0;
int err; int err;
if (o_required & DL_OPT_HANDLE && o_required & DL_OPT_HANDLEP) { 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, err = dl_argv_handle_both(dl, &opts->bus_name, &opts->dev_name,
&opts->port_index, &handle_bit); &opts->port_index, &handle_bit);
@ -1329,6 +1384,32 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required,
if (err) if (err)
return err; return err;
o_found |= DL_OPT_HEALTH_REPORTER_AUTO_RECOVER; o_found |= DL_OPT_HEALTH_REPORTER_AUTO_RECOVER;
} 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 { } else {
pr_err("Unknown option \"%s\"\n", dl_argv(dl)); pr_err("Unknown option \"%s\"\n", dl_argv(dl));
return -EINVAL; return -EINVAL;
@ -1442,11 +1523,20 @@ static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl)
if (opts->present & DL_OPT_HEALTH_REPORTER_AUTO_RECOVER) if (opts->present & DL_OPT_HEALTH_REPORTER_AUTO_RECOVER)
mnl_attr_put_u8(nlh, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER, mnl_attr_put_u8(nlh, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER,
opts->reporter_auto_recover); opts->reporter_auto_recover);
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);
} }
static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl, 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; int err;
@ -1945,6 +2035,30 @@ static void pr_out_entry_end(struct dl *dl)
__pr_out_newline(); __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]));
pr_out_object_end(dl);
pr_out_object_end(dl);
}
static const char *param_cmode_name(uint8_t cmode) static const char *param_cmode_name(uint8_t cmode)
{ {
switch (cmode) { switch (cmode) {
@ -3764,6 +3878,14 @@ static const char *cmd_name(uint8_t cmd)
case DEVLINK_CMD_REGION_SET: return "set"; case DEVLINK_CMD_REGION_SET: return "set";
case DEVLINK_CMD_REGION_NEW: return "new"; case DEVLINK_CMD_REGION_NEW: return "new";
case DEVLINK_CMD_REGION_DEL: return "del"; case DEVLINK_CMD_REGION_DEL: return "del";
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";
default: return "<unknown cmd>"; default: return "<unknown cmd>";
} }
} }
@ -3792,6 +3914,16 @@ static const char *cmd_obj(uint8_t cmd)
case DEVLINK_CMD_REGION_NEW: case DEVLINK_CMD_REGION_NEW:
case DEVLINK_CMD_REGION_DEL: case DEVLINK_CMD_REGION_DEL:
return "region"; return "region";
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";
default: return "<unknown obj>"; default: return "<unknown obj>";
} }
} }
@ -3817,6 +3949,8 @@ static bool cmd_filter_check(struct dl *dl, uint8_t cmd)
} }
static void pr_out_region(struct dl *dl, struct nlattr **tb); static void pr_out_region(struct dl *dl, struct nlattr **tb);
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 int cmd_mon_show_cb(const struct nlmsghdr *nlh, void *data) static int cmd_mon_show_cb(const struct nlmsghdr *nlh, void *data)
{ {
@ -3872,6 +4006,34 @@ static int cmd_mon_show_cb(const struct nlmsghdr *nlh, void *data)
pr_out_mon_header(genl->cmd); pr_out_mon_header(genl->cmd);
pr_out_region(dl, tb); pr_out_region(dl, tb);
break; 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);
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);
break;
} }
return MNL_CB_OK; return MNL_CB_OK;
} }
@ -3885,7 +4047,9 @@ static int cmd_mon_show(struct dl *dl)
while ((cur_obj = dl_argv_index(dl, index++))) { while ((cur_obj = dl_argv_index(dl, index++))) {
if (strcmp(cur_obj, "all") != 0 && if (strcmp(cur_obj, "all") != 0 &&
strcmp(cur_obj, "dev") != 0 && strcmp(cur_obj, "dev") != 0 &&
strcmp(cur_obj, "port") != 0) { strcmp(cur_obj, "port") != 0 &&
strcmp(cur_obj, "trap") != 0 &&
strcmp(cur_obj, "trap-group") != 0) {
pr_err("Unknown object \"%s\"\n", cur_obj); pr_err("Unknown object \"%s\"\n", cur_obj);
return -EINVAL; return -EINVAL;
} }
@ -3902,7 +4066,7 @@ static int cmd_mon_show(struct dl *dl)
static void cmd_mon_help(void) static void cmd_mon_help(void)
{ {
pr_err("Usage: devlink monitor [ all | OBJECT-LIST ]\n" pr_err("Usage: devlink monitor [ all | OBJECT-LIST ]\n"
"where OBJECT-LIST := { dev | port }\n"); "where OBJECT-LIST := { dev | port | trap | trap-group }\n");
} }
static int cmd_mon(struct dl *dl) static int cmd_mon(struct dl *dl)
@ -6330,12 +6494,255 @@ static int cmd_health(struct dl *dl)
return -ENOENT; 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";
default:
return "<unknown type>";
}
}
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";
default:
return "<unknown action>";
}
}
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";
default:
return "<unknown metadata type>";
}
}
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)
pr_out_str_value(dl, 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);
pr_out_str(dl, "name", mnl_attr_get_str(tb[DEVLINK_ATTR_TRAP_NAME]));
pr_out_str(dl, "type", trap_type_name(type));
pr_out_bool(dl, "generic", !!tb[DEVLINK_ATTR_TRAP_GENERIC]);
pr_out_str(dl, "action", trap_action_name(action));
pr_out_str(dl, "group",
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 } ]\n");
pr_err(" devlink trap show [ DEV trap TRAP ]\n");
pr_err(" devlink trap group set DEV group GROUP [ action { trap | drop } ]\n");
pr_err(" devlink trap group show [ DEV group GROUP ]\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);
pr_out_str(dl, "name",
mnl_attr_get_str(tb[DEVLINK_ATTR_TRAP_GROUP_NAME]));
pr_out_bool(dl, "generic", !!tb[DEVLINK_ATTR_TRAP_GENERIC]);
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);
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 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);
}
pr_err("Command \"%s\" not found\n", dl_argv(dl));
return -ENOENT;
}
static void help(void) static void help(void)
{ {
pr_err("Usage: devlink [ OPTIONS ] OBJECT { COMMAND | help }\n" pr_err("Usage: devlink [ OPTIONS ] OBJECT { COMMAND | help }\n"
" devlink [ -f[orce] ] -b[atch] filename\n" " devlink [ -f[orce] ] -b[atch] filename\n"
"where OBJECT := { dev | port | sb | monitor | dpipe | resource | region | health }\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] }\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) static int dl_cmd(struct dl *dl, int argc, char **argv)
@ -6370,6 +6777,9 @@ static int dl_cmd(struct dl *dl, int argc, char **argv)
} else if (dl_argv_match(dl, "health")) { } else if (dl_argv_match(dl, "health")) {
dl_arg_inc(dl); dl_arg_inc(dl);
return cmd_health(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)); pr_err("Object \"%s\" not found\n", dl_argv(dl));
return -ENOENT; return -ENOENT;
@ -6479,6 +6889,7 @@ int main(int argc, char **argv)
{ "json", no_argument, NULL, 'j' }, { "json", no_argument, NULL, 'j' },
{ "pretty", no_argument, NULL, 'p' }, { "pretty", no_argument, NULL, 'p' },
{ "verbose", no_argument, NULL, 'v' }, { "verbose", no_argument, NULL, 'v' },
{ "statistics", no_argument, NULL, 's' },
{ NULL, 0, NULL, 0 } { NULL, 0, NULL, 0 }
}; };
const char *batch_file = NULL; const char *batch_file = NULL;
@ -6494,7 +6905,7 @@ int main(int argc, char **argv)
return EXIT_FAILURE; return EXIT_FAILURE;
} }
while ((opt = getopt_long(argc, argv, "Vfb:njpv", while ((opt = getopt_long(argc, argv, "Vfb:njpvs",
long_options, NULL)) >= 0) { long_options, NULL)) >= 0) {
switch (opt) { switch (opt) {
@ -6520,6 +6931,9 @@ int main(int argc, char **argv)
case 'v': case 'v':
dl->verbose = true; dl->verbose = true;
break; break;
case 's':
dl->stats = true;
break;
default: default:
pr_err("Unknown option.\n"); pr_err("Unknown option.\n");
help(); help();

View File

@ -21,7 +21,7 @@ command is the first in the command line and then the object list.
.I OBJECT-LIST .I OBJECT-LIST
is the list of object types that we want to monitor. is the list of object types that we want to monitor.
It may contain It may contain
.BR dev ", " port ". .BR dev ", " port ", " trap ", " trap-group .
.B devlink .B devlink
opens Devlink Netlink socket, listens on it and dumps state changes. opens Devlink Netlink socket, listens on it and dumps state changes.
@ -31,6 +31,7 @@ opens Devlink Netlink socket, listens on it and dumps state changes.
.BR devlink-dev (8), .BR devlink-dev (8),
.BR devlink-sb (8), .BR devlink-sb (8),
.BR devlink-port (8), .BR devlink-port (8),
.BR devlink-trap (8),
.br .br
.SH AUTHOR .SH AUTHOR

138
man/man8/devlink-trap.8 Normal file
View File

@ -0,0 +1,138 @@
.TH DEVLINK\-TRAP 8 "2 August 2019" "iproute2" "Linux"
.SH NAME
devlink-trap \- devlink trap configuration
.SH SYNOPSIS
.sp
.ad l
.in +8
.ti -8
.B devlink
.RI "[ " OPTIONS " ]"
.B trap
.RI "{ " COMMAND " |"
.BR help " }"
.sp
.ti -8
.IR OPTIONS " := { "
\fB\-v\fR[\fIerbose\fR] |
\fB\-s\fR[\fItatistics\fR] }
.ti -8
.B "devlink trap show"
.RI "[ " DEV
.B trap
.IR TRAP " ]"
.ti -8
.BI "devlink trap set " DEV " trap " TRAP
.RB "[ " action " { " trap " | " drop " } ]"
.ti -8
.B "devlink trap group show"
.RI "[ " DEV
.B group
.IR GROUP " ]"
.ti -8
.BI "devlink trap group set " DEV " group " GROUP
.RB "[ " action " { " trap " | " drop " } ]"
.ti -8
.B devlink trap help
.SH "DESCRIPTION"
.SS devlink trap show - display available packet traps and their attributes
.PP
.I "DEV"
- specifies the devlink device from which to show packet traps.
If this argument is omitted all packet traps of all devices are listed.
.PP
.BI "trap " TRAP
- specifies the packet trap.
Only applicable if a devlink device is also specified.
.SS devlink trap set - set attributes of a packet trap
.PP
.I "DEV"
- specifies the devlink device the packet trap belongs to.
.PP
.BI "trap " TRAP
- specifies the packet trap.
.TP
.BR action " { " trap " | " drop " } "
packet trap action.
.I trap
- the sole copy of the packet is sent to the CPU.
.I drop
- the packet is dropped by the underlying device and a copy is not sent to the CPU.
.SS devlink trap group show - display available packet trap groups and their attributes
.PP
.I "DEV"
- specifies the devlink device from which to show packet trap groups.
If this argument is omitted all packet trap groups of all devices are listed.
.PP
.BI "group " GROUP
- specifies the packet trap group.
Only applicable if a devlink device is also specified.
.SS devlink trap group set - set attributes of a packet trap group
.PP
.I "DEV"
- specifies the devlink device the packet trap group belongs to.
.PP
.BI "group " GROUP
- specifies the packet trap group.
.TP
.BR action " { " trap " | " drop " } "
packet trap action. The action is set for all the packet traps member in the
trap group. The actions of non-drop traps cannot be changed and are thus
skipped.
.SH "EXAMPLES"
.PP
devlink trap show
.RS 4
List available packet traps.
.RE
.PP
devlink trap group show
.RS 4
List available packet trap groups.
.RE
.PP
devlink -vs trap show pci/0000:01:00.0 trap source_mac_is_multicast
.RS 4
Show attributes and statistics of a specific packet trap.
.RE
.PP
devlink -s trap group show pci/0000:01:00.0 group l2_drops
.RS 4
Show attributes and statistics of a specific packet trap group.
.RE
.PP
devlink trap set pci/0000:01:00.0 trap source_mac_is_multicast action trap
.RS 4
Set the action of a specific packet trap to 'trap'.
.SH SEE ALSO
.BR devlink (8),
.BR devlink-dev (8),
.BR devlink-monitor (8),
.br
.SH AUTHOR
Ido Schimmel <idosch@mellanox.com>

View File

@ -7,7 +7,7 @@ devlink \- Devlink tool
.in +8 .in +8
.ti -8 .ti -8
.B devlink .B devlink
.RI "[ " OPTIONS " ] { " dev | port | monitor | sb | resource | region | health " } { " COMMAND " | " .RI "[ " OPTIONS " ] { " dev | port | monitor | sb | resource | region | health | trap " } { " COMMAND " | "
.BR help " }" .BR help " }"
.sp .sp
@ -51,6 +51,10 @@ When combined with -j generate a pretty JSON output.
.BR "\-v" , " --verbose" .BR "\-v" , " --verbose"
Turn on verbose output. Turn on verbose output.
.TP
.BR "\-s" , " --statistics"
Output statistics.
.SS .SS
.I OBJECT .I OBJECT
@ -82,6 +86,10 @@ Turn on verbose output.
.B health .B health
- devlink reporting and recovery - devlink reporting and recovery
.TP
.B trap
- devlink trap configuration
.SS .SS
.I COMMAND .I COMMAND
@ -114,6 +122,7 @@ Exit status is 0 if command was successful or a positive integer upon failure.
.BR devlink-resource (8), .BR devlink-resource (8),
.BR devlink-region (8), .BR devlink-region (8),
.BR devlink-health (8), .BR devlink-health (8),
.BR devlink-trap (8),
.br .br
.SH REPORTING BUGS .SH REPORTING BUGS