diff mbox series

[rdma-next,v1,06/11] RDMA/nldev: Add support to get status of all counters

Message ID 86b8a508d7e782b003d60acb06536681f0d4c721.1631660727.git.leonro@nvidia.com (mailing list archive)
State Superseded
Delegated to: Jason Gunthorpe
Headers show
Series Optional counter statistics support | expand

Commit Message

Leon Romanovsky Sept. 14, 2021, 11:07 p.m. UTC
From: Aharon Landau <aharonl@nvidia.com>

This patch adds the ability to get the name, index and status of all
counters for each link through RDMA netlink. This can be used for
user-space to get the current optional-counter mode.

Examples:
$ rdma statistic mode
link rocep8s0f0/1 optional-counters cc_rx_ce_pkts

$ rdma statistic mode supported
link rocep8s0f0/1 supported optional-counters cc_rx_ce_pkts,cc_rx_cnp_pkts,cc_tx_cnp_pkts
link rocep8s0f1/1 supported optional-counters cc_rx_ce_pkts,cc_rx_cnp_pkts,cc_tx_cnp_pkts

Signed-off-by: Aharon Landau <aharonl@nvidia.com>
Signed-off-by: Neta Ostrovsky <netao@nvidia.com>
Reviewed-by: Mark Zhang <markzhang@nvidia.com>
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
 drivers/infiniband/core/nldev.c  | 154 +++++++++++++++++++++++--------
 include/uapi/rdma/rdma_netlink.h |   3 +
 2 files changed, 121 insertions(+), 36 deletions(-)

Comments

Jason Gunthorpe Sept. 27, 2021, 5:30 p.m. UTC | #1
On Wed, Sep 15, 2021 at 02:07:25AM +0300, Leon Romanovsky wrote:
> +static int stat_get_doit_default_counter(struct sk_buff *skb,
> +					 struct nlmsghdr *nlh,
> +					 struct netlink_ext_ack *extack,
> +					 struct nlattr *tb[])
> +{
> +	struct rdma_hw_stats *stats;
> +	struct ib_device *device;
> +	u32 index, port;
> +	int ret;
> +
> +	if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_PORT_INDEX])
> +		return -EINVAL;
> +
> +	index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
> +	device = ib_device_get_by_index(sock_net(skb->sk), index);
> +	if (!device)
> +		return -EINVAL;
> +
> +	port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
> +	if (!rdma_is_port_valid(device, port)) {
> +		ret = -EINVAL;
> +		goto end;
> +	}
> +
> +	stats = ib_get_hw_stats_port(device, port);
> +	if (!stats) {
> +		ret = -EINVAL;
> +		goto end;
> +	}
> +
> +	if (tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC])
> +		ret = stat_get_doit_stats_list(skb, nlh, extack, tb,
> +					       device, port, stats);
> +	else
> +		ret = stat_get_doit_stats_values(skb, nlh, extack, tb, device,
> +						 port, stats);

This seems strange, why is the output of a get contingent on a ignored
input attribute? Shouldn't the HWCOUNTER_DYNAMIC just always be
emitted?

Jason
Mark Zhang Sept. 28, 2021, 9:12 a.m. UTC | #2
On 9/28/2021 1:30 AM, Jason Gunthorpe wrote:
> On Wed, Sep 15, 2021 at 02:07:25AM +0300, Leon Romanovsky wrote:
>> +static int stat_get_doit_default_counter(struct sk_buff *skb,
>> +					 struct nlmsghdr *nlh,
>> +					 struct netlink_ext_ack *extack,
>> +					 struct nlattr *tb[])
>> +{
>> +	struct rdma_hw_stats *stats;
>> +	struct ib_device *device;
>> +	u32 index, port;
>> +	int ret;
>> +
>> +	if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_PORT_INDEX])
>> +		return -EINVAL;
>> +
>> +	index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
>> +	device = ib_device_get_by_index(sock_net(skb->sk), index);
>> +	if (!device)
>> +		return -EINVAL;
>> +
>> +	port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
>> +	if (!rdma_is_port_valid(device, port)) {
>> +		ret = -EINVAL;
>> +		goto end;
>> +	}
>> +
>> +	stats = ib_get_hw_stats_port(device, port);
>> +	if (!stats) {
>> +		ret = -EINVAL;
>> +		goto end;
>> +	}
>> +
>> +	if (tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC])
>> +		ret = stat_get_doit_stats_list(skb, nlh, extack, tb,
>> +					       device, port, stats);
>> +	else
>> +		ret = stat_get_doit_stats_values(skb, nlh, extack, tb, device,
>> +						 port, stats);
> 
> This seems strange, why is the output of a get contingent on a ignored
> input attribute? Shouldn't the HWCOUNTER_DYNAMIC just always be
> emitted?

The CMD_STAT_GET is originally used to get the default hwcounter 
statistic (the value of all hwstats), now we also want to use this 
command to get a list of counters (just name and status), so kernel 
differentiates these 2 cases based on HWCOUNTER_DYNAMIC attr.
Jason Gunthorpe Sept. 28, 2021, 11:52 a.m. UTC | #3
On Tue, Sep 28, 2021 at 05:12:39PM +0800, Mark Zhang wrote:
> On 9/28/2021 1:30 AM, Jason Gunthorpe wrote:
> > On Wed, Sep 15, 2021 at 02:07:25AM +0300, Leon Romanovsky wrote:
> > > +static int stat_get_doit_default_counter(struct sk_buff *skb,
> > > +					 struct nlmsghdr *nlh,
> > > +					 struct netlink_ext_ack *extack,
> > > +					 struct nlattr *tb[])
> > > +{
> > > +	struct rdma_hw_stats *stats;
> > > +	struct ib_device *device;
> > > +	u32 index, port;
> > > +	int ret;
> > > +
> > > +	if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_PORT_INDEX])
> > > +		return -EINVAL;
> > > +
> > > +	index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
> > > +	device = ib_device_get_by_index(sock_net(skb->sk), index);
> > > +	if (!device)
> > > +		return -EINVAL;
> > > +
> > > +	port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
> > > +	if (!rdma_is_port_valid(device, port)) {
> > > +		ret = -EINVAL;
> > > +		goto end;
> > > +	}
> > > +
> > > +	stats = ib_get_hw_stats_port(device, port);
> > > +	if (!stats) {
> > > +		ret = -EINVAL;
> > > +		goto end;
> > > +	}
> > > +
> > > +	if (tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC])
> > > +		ret = stat_get_doit_stats_list(skb, nlh, extack, tb,
> > > +					       device, port, stats);
> > > +	else
> > > +		ret = stat_get_doit_stats_values(skb, nlh, extack, tb, device,
> > > +						 port, stats);
> > 
> > This seems strange, why is the output of a get contingent on a ignored
> > input attribute? Shouldn't the HWCOUNTER_DYNAMIC just always be
> > emitted?
> 
> The CMD_STAT_GET is originally used to get the default hwcounter statistic
> (the value of all hwstats), now we also want to use this command to get a
> list of counters (just name and status), so kernel differentiates these 2
> cases based on HWCOUNTER_DYNAMIC attr.

Don't do that, it is not how netlink works. Either the whole attribute
should be returned or you need a new get command

Jason
Mark Zhang Sept. 28, 2021, 12:51 p.m. UTC | #4
On 9/28/2021 7:52 PM, Jason Gunthorpe wrote:
> On Tue, Sep 28, 2021 at 05:12:39PM +0800, Mark Zhang wrote:
>> On 9/28/2021 1:30 AM, Jason Gunthorpe wrote:
>>> On Wed, Sep 15, 2021 at 02:07:25AM +0300, Leon Romanovsky wrote:
>>>> +static int stat_get_doit_default_counter(struct sk_buff *skb,
>>>> +					 struct nlmsghdr *nlh,
>>>> +					 struct netlink_ext_ack *extack,
>>>> +					 struct nlattr *tb[])
>>>> +{
>>>> +	struct rdma_hw_stats *stats;
>>>> +	struct ib_device *device;
>>>> +	u32 index, port;
>>>> +	int ret;
>>>> +
>>>> +	if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_PORT_INDEX])
>>>> +		return -EINVAL;
>>>> +
>>>> +	index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
>>>> +	device = ib_device_get_by_index(sock_net(skb->sk), index);
>>>> +	if (!device)
>>>> +		return -EINVAL;
>>>> +
>>>> +	port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
>>>> +	if (!rdma_is_port_valid(device, port)) {
>>>> +		ret = -EINVAL;
>>>> +		goto end;
>>>> +	}
>>>> +
>>>> +	stats = ib_get_hw_stats_port(device, port);
>>>> +	if (!stats) {
>>>> +		ret = -EINVAL;
>>>> +		goto end;
>>>> +	}
>>>> +
>>>> +	if (tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC])
>>>> +		ret = stat_get_doit_stats_list(skb, nlh, extack, tb,
>>>> +					       device, port, stats);
>>>> +	else
>>>> +		ret = stat_get_doit_stats_values(skb, nlh, extack, tb, device,
>>>> +						 port, stats);
>>>
>>> This seems strange, why is the output of a get contingent on a ignored
>>> input attribute? Shouldn't the HWCOUNTER_DYNAMIC just always be
>>> emitted?
>>
>> The CMD_STAT_GET is originally used to get the default hwcounter statistic
>> (the value of all hwstats), now we also want to use this command to get a
>> list of counters (just name and status), so kernel differentiates these 2
>> cases based on HWCOUNTER_DYNAMIC attr.
> 
> Don't do that, it is not how netlink works. Either the whole attribute
> should be returned or you need a new get command

Will add a new get command for backward compatibility, thanks.
Leon Romanovsky Sept. 29, 2021, 12:26 p.m. UTC | #5
On Tue, Sep 28, 2021 at 08:52:17AM -0300, Jason Gunthorpe wrote:
> On Tue, Sep 28, 2021 at 05:12:39PM +0800, Mark Zhang wrote:
> > On 9/28/2021 1:30 AM, Jason Gunthorpe wrote:
> > > On Wed, Sep 15, 2021 at 02:07:25AM +0300, Leon Romanovsky wrote:
> > > > +static int stat_get_doit_default_counter(struct sk_buff *skb,
> > > > +					 struct nlmsghdr *nlh,
> > > > +					 struct netlink_ext_ack *extack,
> > > > +					 struct nlattr *tb[])
> > > > +{
> > > > +	struct rdma_hw_stats *stats;
> > > > +	struct ib_device *device;
> > > > +	u32 index, port;
> > > > +	int ret;
> > > > +
> > > > +	if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_PORT_INDEX])
> > > > +		return -EINVAL;
> > > > +
> > > > +	index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
> > > > +	device = ib_device_get_by_index(sock_net(skb->sk), index);
> > > > +	if (!device)
> > > > +		return -EINVAL;
> > > > +
> > > > +	port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
> > > > +	if (!rdma_is_port_valid(device, port)) {
> > > > +		ret = -EINVAL;
> > > > +		goto end;
> > > > +	}
> > > > +
> > > > +	stats = ib_get_hw_stats_port(device, port);
> > > > +	if (!stats) {
> > > > +		ret = -EINVAL;
> > > > +		goto end;
> > > > +	}
> > > > +
> > > > +	if (tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC])
> > > > +		ret = stat_get_doit_stats_list(skb, nlh, extack, tb,
> > > > +					       device, port, stats);
> > > > +	else
> > > > +		ret = stat_get_doit_stats_values(skb, nlh, extack, tb, device,
> > > > +						 port, stats);
> > > 
> > > This seems strange, why is the output of a get contingent on a ignored
> > > input attribute? Shouldn't the HWCOUNTER_DYNAMIC just always be
> > > emitted?
> > 
> > The CMD_STAT_GET is originally used to get the default hwcounter statistic
> > (the value of all hwstats), now we also want to use this command to get a
> > list of counters (just name and status), so kernel differentiates these 2
> > cases based on HWCOUNTER_DYNAMIC attr.
> 
> Don't do that, it is not how netlink works. Either the whole attribute
> should be returned or you need a new get command

The netlink way is to be independent on returned parameter, if it not
supported, this parameter won't be available at all. This makes HWCOUNTER_DYNAMIC
to work exactly as netlink would do.

Thanks

> 
> Jason
Jason Gunthorpe Sept. 29, 2021, 11:56 p.m. UTC | #6
On Wed, Sep 29, 2021 at 03:26:25PM +0300, Leon Romanovsky wrote:
> On Tue, Sep 28, 2021 at 08:52:17AM -0300, Jason Gunthorpe wrote:
> > On Tue, Sep 28, 2021 at 05:12:39PM +0800, Mark Zhang wrote:
> > > On 9/28/2021 1:30 AM, Jason Gunthorpe wrote:
> > > > On Wed, Sep 15, 2021 at 02:07:25AM +0300, Leon Romanovsky wrote:
> > > > > +static int stat_get_doit_default_counter(struct sk_buff *skb,
> > > > > +					 struct nlmsghdr *nlh,
> > > > > +					 struct netlink_ext_ack *extack,
> > > > > +					 struct nlattr *tb[])
> > > > > +{
> > > > > +	struct rdma_hw_stats *stats;
> > > > > +	struct ib_device *device;
> > > > > +	u32 index, port;
> > > > > +	int ret;
> > > > > +
> > > > > +	if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_PORT_INDEX])
> > > > > +		return -EINVAL;
> > > > > +
> > > > > +	index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
> > > > > +	device = ib_device_get_by_index(sock_net(skb->sk), index);
> > > > > +	if (!device)
> > > > > +		return -EINVAL;
> > > > > +
> > > > > +	port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
> > > > > +	if (!rdma_is_port_valid(device, port)) {
> > > > > +		ret = -EINVAL;
> > > > > +		goto end;
> > > > > +	}
> > > > > +
> > > > > +	stats = ib_get_hw_stats_port(device, port);
> > > > > +	if (!stats) {
> > > > > +		ret = -EINVAL;
> > > > > +		goto end;
> > > > > +	}
> > > > > +
> > > > > +	if (tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC])
> > > > > +		ret = stat_get_doit_stats_list(skb, nlh, extack, tb,
> > > > > +					       device, port, stats);
> > > > > +	else
> > > > > +		ret = stat_get_doit_stats_values(skb, nlh, extack, tb, device,
> > > > > +						 port, stats);
> > > > 
> > > > This seems strange, why is the output of a get contingent on a ignored
> > > > input attribute? Shouldn't the HWCOUNTER_DYNAMIC just always be
> > > > emitted?
> > > 
> > > The CMD_STAT_GET is originally used to get the default hwcounter statistic
> > > (the value of all hwstats), now we also want to use this command to get a
> > > list of counters (just name and status), so kernel differentiates these 2
> > > cases based on HWCOUNTER_DYNAMIC attr.
> > 
> > Don't do that, it is not how netlink works. Either the whole attribute
> > should be returned or you need a new get command
> 
> The netlink way is to be independent on returned parameter, if it not
> supported, this parameter won't be available at all. This makes HWCOUNTER_DYNAMIC
> to work exactly as netlink would do.

The issue is making the output dependent on the input:

 +	if (tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC])

Setting HWCOUNTER_DYNAMIC as an input flag to get the GET to return a
completely different output format is not netlinky

Either always return HWCOUNTER_DYNAMIC or make another query to get it

Jason
diff mbox series

Patch

diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c
index 67519730b1ac..d9443983efdc 100644
--- a/drivers/infiniband/core/nldev.c
+++ b/drivers/infiniband/core/nldev.c
@@ -154,6 +154,8 @@  static const struct nla_policy nldev_policy[RDMA_NLDEV_ATTR_MAX] = {
 	[RDMA_NLDEV_NET_NS_FD]			= { .type = NLA_U32 },
 	[RDMA_NLDEV_SYS_ATTR_NETNS_MODE]	= { .type = NLA_U8 },
 	[RDMA_NLDEV_SYS_ATTR_COPY_ON_FORK]	= { .type = NLA_U8 },
+	[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_INDEX]	= { .type = NLA_U32 },
+	[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC] = { .type = NLA_U8 },
 };
 
 static int put_driver_name_print_type(struct sk_buff *msg, const char *name,
@@ -2046,49 +2048,90 @@  static int nldev_stat_del_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
 	return ret;
 }
 
-static int stat_get_doit_default_counter(struct sk_buff *skb,
-					 struct nlmsghdr *nlh,
-					 struct netlink_ext_ack *extack,
-					 struct nlattr *tb[])
+static int stat_get_doit_stats_list(struct sk_buff *skb,
+				    struct nlmsghdr *nlh,
+				    struct netlink_ext_ack *extack,
+				    struct nlattr *tb[],
+				    struct ib_device *device, u32 port,
+				    struct rdma_hw_stats *stats)
 {
-	struct rdma_hw_stats *stats;
-	struct nlattr *table_attr;
-	struct ib_device *device;
-	int ret, num_cnts, i;
+	struct nlattr *table, *entry;
 	struct sk_buff *msg;
-	u32 index, port;
-	u64 v;
+	int i;
 
-	if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_PORT_INDEX])
-		return -EINVAL;
+	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
 
-	index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
-	device = ib_device_get_by_index(sock_net(skb->sk), index);
-	if (!device)
-		return -EINVAL;
+	nlh = nlmsg_put(
+		msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
+		RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_STAT_GET), 0, 0);
 
-	if (!device->ops.alloc_hw_port_stats || !device->ops.get_hw_stats) {
-		ret = -EINVAL;
-		goto err;
-	}
+	if (fill_nldev_handle(msg, device) ||
+	    nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, port))
+		goto err_msg;
 
-	port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
-	stats = ib_get_hw_stats_port(device, port);
-	if (!stats) {
-		ret = -EINVAL;
-		goto err;
+	table = nla_nest_start(msg, RDMA_NLDEV_ATTR_STAT_HWCOUNTERS);
+	if (!table)
+		goto err_msg;
+
+	mutex_lock(&stats->lock);
+	for (i = 0; i < stats->num_counters; i++) {
+		entry = nla_nest_start(msg,
+				       RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY);
+		if (!entry)
+			goto err_msg_table;
+
+		if (nla_put_string(msg,
+				   RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME,
+				   stats->descs[i].name) ||
+		    nla_put_u32(msg, RDMA_NLDEV_ATTR_STAT_HWCOUNTER_INDEX, i))
+			goto err_msg_entry;
+
+		if ((stats->descs[i].flags & IB_STAT_FLAG_OPTIONAL) &&
+		    (nla_put_u8(msg, RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC,
+				!test_bit(i, stats->is_disabled))))
+			goto err_msg_entry;
+
+		nla_nest_end(msg, entry);
 	}
+	mutex_unlock(&stats->lock);
+
+	nla_nest_end(msg, table);
+	nlmsg_end(msg, nlh);
+	return rdma_nl_unicast(sock_net(skb->sk), msg, NETLINK_CB(skb).portid);
+
+err_msg_entry:
+	nla_nest_cancel(msg, entry);
+err_msg_table:
+	mutex_unlock(&stats->lock);
+	nla_nest_cancel(msg, table);
+err_msg:
+	nlmsg_free(msg);
+	return -EMSGSIZE;
+}
+
+static int stat_get_doit_stats_values(struct sk_buff *skb, struct nlmsghdr *nlh,
+				      struct netlink_ext_ack *extack,
+				      struct nlattr *tb[],
+				      struct ib_device *device, u32 port,
+				      struct rdma_hw_stats *stats)
+{
+	struct nlattr *table_attr;
+	int ret, num_cnts, i;
+	struct sk_buff *msg;
+	u64 v;
+
+	if (!device->ops.alloc_hw_port_stats || !device->ops.get_hw_stats)
+		return -EINVAL;
 
 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-	if (!msg) {
-		ret = -ENOMEM;
-		goto err;
-	}
+	if (!msg)
+		return -ENOMEM;
 
-	nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
-			RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
-					 RDMA_NLDEV_CMD_STAT_GET),
-			0, 0);
+	nlh = nlmsg_put(
+		msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
+		RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_STAT_GET), 0, 0);
 
 	if (fill_nldev_handle(msg, device) ||
 	    nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, port)) {
@@ -2098,7 +2141,8 @@  static int stat_get_doit_default_counter(struct sk_buff *skb,
 
 	mutex_lock(&stats->lock);
 
-	num_cnts = device->ops.get_hw_stats(device, stats, port, 0);
+	num_cnts = device->ops.get_hw_stats(device, stats, port,
+					    stats->num_counters);
 	if (num_cnts < 0) {
 		ret = -EINVAL;
 		goto err_stats;
@@ -2125,7 +2169,6 @@  static int stat_get_doit_default_counter(struct sk_buff *skb,
 
 	mutex_unlock(&stats->lock);
 	nlmsg_end(msg, nlh);
-	ib_device_put(device);
 	return rdma_nl_unicast(sock_net(skb->sk), msg, NETLINK_CB(skb).portid);
 
 err_table:
@@ -2134,7 +2177,46 @@  static int stat_get_doit_default_counter(struct sk_buff *skb,
 	mutex_unlock(&stats->lock);
 err_msg:
 	nlmsg_free(msg);
-err:
+	return ret;
+}
+
+static int stat_get_doit_default_counter(struct sk_buff *skb,
+					 struct nlmsghdr *nlh,
+					 struct netlink_ext_ack *extack,
+					 struct nlattr *tb[])
+{
+	struct rdma_hw_stats *stats;
+	struct ib_device *device;
+	u32 index, port;
+	int ret;
+
+	if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_PORT_INDEX])
+		return -EINVAL;
+
+	index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
+	device = ib_device_get_by_index(sock_net(skb->sk), index);
+	if (!device)
+		return -EINVAL;
+
+	port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
+	if (!rdma_is_port_valid(device, port)) {
+		ret = -EINVAL;
+		goto end;
+	}
+
+	stats = ib_get_hw_stats_port(device, port);
+	if (!stats) {
+		ret = -EINVAL;
+		goto end;
+	}
+
+	if (tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC])
+		ret = stat_get_doit_stats_list(skb, nlh, extack, tb,
+					       device, port, stats);
+	else
+		ret = stat_get_doit_stats_values(skb, nlh, extack, tb, device,
+						 port, stats);
+end:
 	ib_device_put(device);
 	return ret;
 }
diff --git a/include/uapi/rdma/rdma_netlink.h b/include/uapi/rdma/rdma_netlink.h
index 75a1ae2311d8..2017970279ed 100644
--- a/include/uapi/rdma/rdma_netlink.h
+++ b/include/uapi/rdma/rdma_netlink.h
@@ -549,6 +549,9 @@  enum rdma_nldev_attr {
 
 	RDMA_NLDEV_SYS_ATTR_COPY_ON_FORK,	/* u8 */
 
+	RDMA_NLDEV_ATTR_STAT_HWCOUNTER_INDEX,	/* u32 */
+	RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC, /* u8 */
+
 	/*
 	 * Always the end
 	 */