[PATCH iproute2-next v1 3/3] rdma: Add optional-counters set/unset support

Mark Zhang
2021-10-14T07:54:52Z

From: Neta Ostrovsky <netao@nvidia.com>

This patch provides an extension to the rdma statistics tool
that allows to set/unset optional counters set dynamically,
using new netlink commands.
Note that the optional counter statistic implementation is
driver-specific and may impact the performance.

Examples:
To enable a set of optional counters on link rocep8s0f0/1:
$ sudo rdma statistic set link rocep8s0f0/1 optional-counters cc_rx_ce_pkts,cc_rx_cnp_pkts
To disable all optional counters on link rocep8s0f0/1:
$ sudo rdma statistic unset link rocep8s0f0/1 optional-counters

Signed-off-by: Neta Ostrovsky <netao@nvidia.com>
Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: Mark Zhang <markzhang@nvidia.com>
---
man/man8/rdma-statistic.8 | 32 +++++++
rdma/stat.c | 178 ++++++++++++++++++++++++++++++++++++++
2 files changed, 210 insertions(+)

diff --git a/man/man8/rdma-statistic.8 b/man/man8/rdma-statistic.8
index 885769bc..7dd2b02c 100644
--- a/man/man8/rdma-statistic.8
+++ b/man/man8/rdma-statistic.8
@@ -65,6 +65,21 @@ rdma-statistic \- RDMA statistic counter configuration
.B link
.RI "[ " DEV/PORT_INDEX " ]"

+.ti -8
+.B rdma statistic
+.B set
+.B link
+.RI "[ " DEV/PORT_INDEX " ]"
+.B optional-counters
+.RI "[ " OPTIONAL-COUNTERS " ]"
+
+.ti -8
+.B rdma statistic
+.B unset
+.B link
+.RI "[ " DEV/PORT_INDEX " ]"
+.B optional-counters
+
.ti -8
.IR COUNTER_SCOPE " := "
.RB "{ " link " | " dev " }"
@@ -111,6 +126,13 @@ If this argument is omitted then a new counter will be allocated.

.SS rdma statistic mode supported - Display the supported optional counters for each link.

+.SS rdma statistic set - Enable a set of optional counters for a specific device/port.
+
+.I "OPTIONAL-COUNTERS"
+- specifies the name of the optional counters to enable. Optional counters that are not specified will be disabled. Note that optional counters are driver-specific.
+
+.SS rdma statistic unset - Disable all optional counters for a specific device/port.
+
.SH "EXAMPLES"
.PP
rdma statistic show
@@ -207,6 +229,16 @@ rdma statistic mode supported link mlx5_2/1
.RS 4
Display the optional counters that mlx5_2/1 supports.
.RE
+.PP
+rdma statistic set link mlx5_2/1 optional-counters cc_rx_ce_pkts,cc_rx_cnp_pkts
+.RS 4
+Enable the cc_rx_ce_pkts,cc_rx_cnp_pkts counters on device mlx5_2 port 1.
+.RE
+.PP
+rdma statistic unset link mlx5_2/1 optional-counters
+.RS 4
+Disable all the optional counters on device mlx5_2 port 1.
+.RE

.SH SEE ALSO
.BR rdma (8),
diff --git a/rdma/stat.c b/rdma/stat.c
index 7d645d8f..adfcd34a 100644
--- a/rdma/stat.c
+++ b/rdma/stat.c
@@ -22,6 +22,8 @@ static int stat_help(struct rd *rd)
pr_out(" %s statistic show link [ DEV/PORT_INDEX ]\n", rd->filename);
pr_out(" %s statistic mode [ supported ]\n", rd->filename);
pr_out(" %s statistic mode [ supported ] link [ DEV/PORT_INDEX ]\n", rd->filename);
+ pr_out(" %s statistic set link [ DEV/PORT_INDEX ] optional-counters [ OPTIONAL-COUNTERS ]\n", rd->filename);
+ pr_out(" %s statistic unset link [ DEV/PORT_INDEX ] optional-counters\n", rd->filename);
pr_out("where OBJECT: = { qp }\n");
pr_out(" CRITERIA : = { type }\n");
pr_out(" COUNTER_SCOPE: = { link | dev }\n");
@@ -43,6 +45,8 @@ static int stat_help(struct rd *rd)
pr_out(" %s statistic mode link mlx5_2/1\n", rd->filename);
pr_out(" %s statistic mode supported\n", rd->filename);
pr_out(" %s statistic mode supported link mlx5_2/1\n", rd->filename);
+ pr_out(" %s statistic set link mlx5_2/1 optional-counters cc_rx_ce_pkts,cc_rx_cnp_pkts\n", rd->filename);
+ pr_out(" %s statistic unset link mlx5_2/1 optional-counters\n", rd->filename);

return 0;
}
@@ -499,6 +503,30 @@ static int stat_qp_set(struct rd *rd)
return rd_exec_cmd(rd, cmds, "parameter");
}

+static int stat_get_arg_str(struct rd *rd, const char *arg, char **value, bool allow_empty)
+{
+ int len = 0;
+
+ if (strcmpx(rd_argv(rd), arg) != 0) {
+ pr_err("Unknown parameter '%s'.\n", rd_argv(rd));
+ return -EINVAL;
+ }
+
+ rd_arg_inc(rd);
+ if (!rd_no_arg(rd)) {
+ *value = strdup(rd_argv(rd));
+ len = strlen(*value);
+ rd_arg_inc(rd);
+ }
+
+ if ((allow_empty && len) || (!allow_empty && !len)) {
+ stat_help(rd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int stat_get_arg(struct rd *rd, const char *arg)
{
int value = 0;
@@ -877,6 +905,154 @@ static int stat_mode(struct rd *rd)
return rd_exec_cmd(rd, cmds, "parameter");
}

+static int stat_one_set_link_opcounters(const struct nlmsghdr *nlh, void *data)
+{
+ struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
+ struct nlattr *nla_entry, *tb_set;
+ int ret, flags = NLM_F_REQUEST | NLM_F_ACK;
+ char *opcnt, *opcnts;
+ struct rd *rd = data;
+ uint32_t seq;
+ bool found;
+
+ mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
+ if (!tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS])
+ return MNL_CB_ERROR;
+
+ if (rd_no_arg(rd)) {
+ stat_help(rd);
+ return -EINVAL;
+ }
+
+ ret = stat_get_arg_str(rd, "optional-counters", &opcnts, false);
+ if (ret)
+ return ret;
+
+ rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET, &seq, flags);
+ mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX,
+ rd->dev_idx);
+ mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX,
+ rd->port_idx);
+
+ tb_set = mnl_attr_nest_start(rd->nlh, RDMA_NLDEV_ATTR_STAT_HWCOUNTERS);
+
+ opcnt = strtok(opcnts, ",");
+ while (opcnt) {
+ found = false;
+ mnl_attr_for_each_nested(nla_entry,
+ tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS]) {
+ struct nlattr *cnt[RDMA_NLDEV_ATTR_MAX] = {}, *nm, *id;
+
+ if (mnl_attr_parse_nested(nla_entry, rd_attr_cb,
+ cnt) != MNL_CB_OK)
+ return -EINVAL;
+
+ nm = cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME];
+ id = cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_INDEX];
+ if (!nm || ! id)
+ return -EINVAL;
+
+ if (!cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC])
+ continue;
+
+ if (strcmp(opcnt, mnl_attr_get_str(nm)) == 0) {
+ mnl_attr_put_u32(rd->nlh,
+ RDMA_NLDEV_ATTR_STAT_HWCOUNTER_INDEX,
+ mnl_attr_get_u32(id));
+ found = true;
+ }
+ }
+
+ if (!found)
+ return -EINVAL;
+
+ opcnt = strtok(NULL, ",");
+ }
+ mnl_attr_nest_end(rd->nlh, tb_set);
+
+ return rd_sendrecv_msg(rd, seq);
+}
+
+static int stat_one_set_link(struct rd *rd)
+{
+ uint32_t seq;
+ int err;
+
+ if (!rd->port_idx)
+ return 0;
+
+ err = stat_one_link_get_status_req(rd, &seq);
+ if (err)
+ return err;
+
+ return rd_recv_msg(rd, stat_one_set_link_opcounters, rd, seq);
+}
+
+static int stat_set_link(struct rd *rd)
+{
+ return rd_exec_link(rd, stat_one_set_link, true);
+}
+
+static int stat_set(struct rd *rd)
+{
+ const struct rd_cmd cmds[] = {
+ { NULL, stat_help },
+ { "link", stat_set_link },
+ { "help", stat_help },
+ { 0 },
+ };
+ return rd_exec_cmd(rd, cmds, "parameter");
+}
+
+static int stat_one_unset_link_opcounters(struct rd *rd)
+{
+ int ret, flags = NLM_F_REQUEST | NLM_F_ACK;
+ struct nlattr *tbl;
+ uint32_t seq;
+ char *opcnts;
+
+ if (rd_no_arg(rd)) {
+ stat_help(rd);
+ return -EINVAL;
+ }
+
+ ret = stat_get_arg_str(rd, "optional-counters", &opcnts, true);
+ if (ret)
+ return ret;
+
+ rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET, &seq, flags);
+ mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX,
+ rd->dev_idx);
+ mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX,
+ rd->port_idx);
+
+ tbl = mnl_attr_nest_start(rd->nlh, RDMA_NLDEV_ATTR_STAT_HWCOUNTERS);
+ mnl_attr_nest_end(rd->nlh, tbl);
+
+ return rd_sendrecv_msg(rd, seq);
+}
+
+static int stat_one_unset_link(struct rd *rd)
+{
+ return stat_one_unset_link_opcounters(rd);
+}
+
+static int stat_unset_link(struct rd *rd)
+{
+ return rd_exec_link(rd, stat_one_unset_link, true);
+}
+
+static int stat_unset(struct rd *rd)
+{
+ const struct rd_cmd cmds[] = {
+ { NULL, stat_help },
+ { "link", stat_unset_link },
+ { "help", stat_help },
+ { 0 },
+ };
+ return rd_exec_cmd(rd, cmds, "parameter");
+}
+
static int stat_show_parse_cb(const struct nlmsghdr *nlh, void *data)
{
struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
@@ -949,6 +1125,8 @@ int cmd_stat(struct rd *rd)
{ "qp", stat_qp },
{ "mr", stat_mr },
{ "mode", stat_mode },
+ { "set", stat_set },
+ { "unset", stat_unset },
{ 0 }
};

--
2.26.2