Commit 6e7be47a authored by Mark Zhang's avatar Mark Zhang Committed by Jason Gunthorpe
Browse files

RDMA/nldev: Allow get default counter statistics through RDMA netlink



This patch adds the ability to return the hwstats of per-port default
counters (which can also be queried through sysfs nodes).

Signed-off-by: default avatarMark Zhang <markz@mellanox.com>
Signed-off-by: default avatarLeon Romanovsky <leonro@mellanox.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@mellanox.com>
parent 83c2c1fc
Loading
Loading
Loading
Loading
+97 −1
Original line number Diff line number Diff line
@@ -1799,6 +1799,99 @@ 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 nlattr *table_attr;
	struct ib_device *device;
	int ret, num_cnts, i;
	struct sk_buff *msg;
	u32 index, port;
	u64 v;

	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;

	if (!device->ops.alloc_hw_stats || !device->ops.get_hw_stats) {
		ret = -EINVAL;
		goto err;
	}

	port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
	if (!rdma_is_port_valid(device, port)) {
		ret = -EINVAL;
		goto err;
	}

	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
	if (!msg) {
		ret = -ENOMEM;
		goto err;
	}

	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)) {
		ret = -EMSGSIZE;
		goto err_msg;
	}

	stats = device->port_data ? device->port_data[port].hw_stats : NULL;
	if (stats == NULL) {
		ret = -EINVAL;
		goto err_msg;
	}
	mutex_lock(&stats->lock);

	num_cnts = device->ops.get_hw_stats(device, stats, port, 0);
	if (num_cnts < 0) {
		ret = -EINVAL;
		goto err_stats;
	}

	table_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_STAT_HWCOUNTERS);
	if (!table_attr) {
		ret = -EMSGSIZE;
		goto err_stats;
	}
	for (i = 0; i < num_cnts; i++) {
		v = stats->value[i] +
			rdma_counter_get_hwstat_value(device, port, i);
		if (fill_stat_hwcounter_entry(msg, stats->names[i], v)) {
			ret = -EMSGSIZE;
			goto err_table;
		}
	}
	nla_nest_end(msg, table_attr);

	mutex_unlock(&stats->lock);
	nlmsg_end(msg, nlh);
	ib_device_put(device);
	return rdma_nl_unicast(msg, NETLINK_CB(skb).portid);

err_table:
	nla_nest_cancel(msg, table_attr);
err_stats:
	mutex_unlock(&stats->lock);
err_msg:
	nlmsg_free(msg);
err:
	ib_device_put(device);
	return ret;
}

static int stat_get_doit_qp(struct sk_buff *skb, struct nlmsghdr *nlh,
			    struct netlink_ext_ack *extack, struct nlattr *tb[])

@@ -1871,9 +1964,12 @@ static int nldev_stat_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,

	ret = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
			  nldev_policy, extack);
	if (ret || !tb[RDMA_NLDEV_ATTR_STAT_RES])
	if (ret)
		return -EINVAL;

	if (!tb[RDMA_NLDEV_ATTR_STAT_RES])
		return stat_get_doit_default_counter(skb, nlh, extack, tb);

	switch (nla_get_u32(tb[RDMA_NLDEV_ATTR_STAT_RES])) {
	case RDMA_NLDEV_ATTR_RES_QP:
		ret = stat_get_doit_qp(skb, nlh, extack, tb);
+6 −0
Original line number Diff line number Diff line
@@ -1003,6 +1003,8 @@ static void setup_hw_stats(struct ib_device *device, struct ib_port *port,
			goto err;
		port->hw_stats_ag = hsag;
		port->hw_stats = stats;
		if (device->port_data)
			device->port_data[port_num].hw_stats = stats;
	} else {
		struct kobject *kobj = &device->dev.kobj;
		ret = sysfs_create_group(kobj, hsag);
@@ -1293,6 +1295,8 @@ const struct attribute_group ib_dev_attr_group = {

void ib_free_port_attrs(struct ib_core_device *coredev)
{
	struct ib_device *device = rdma_device_to_ibdev(&coredev->dev);
	bool is_full_dev = &device->coredev == coredev;
	struct kobject *p, *t;

	list_for_each_entry_safe(p, t, &coredev->port_list, entry) {
@@ -1302,6 +1306,8 @@ void ib_free_port_attrs(struct ib_core_device *coredev)
		if (port->hw_stats_ag)
			free_hsag(&port->kobj, port->hw_stats_ag);
		kfree(port->hw_stats);
		if (device->port_data && is_full_dev)
			device->port_data[port->port_num].hw_stats = NULL;

		if (port->pma_table)
			sysfs_remove_group(p, port->pma_table);
+1 −0
Original line number Diff line number Diff line
@@ -2124,6 +2124,7 @@ struct ib_port_data {
	struct net_device __rcu *netdev;
	struct hlist_node ndev_hash_link;
	struct rdma_port_counter port_counter;
	struct rdma_hw_stats *hw_stats;
};

/* rdma netdev type - specifies protocol type */