OSDN Git Service

RDMA/nldev: Allow get default counter statistics through RDMA netlink
authorMark Zhang <markz@mellanox.com>
Tue, 2 Jul 2019 10:02:46 +0000 (13:02 +0300)
committerJason Gunthorpe <jgg@mellanox.com>
Fri, 5 Jul 2019 13:22:55 +0000 (10:22 -0300)
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: Mark Zhang <markz@mellanox.com>
Signed-off-by: Leon Romanovsky <leonro@mellanox.com>
Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>
drivers/infiniband/core/nldev.c
drivers/infiniband/core/sysfs.c
include/rdma/ib_verbs.h

index 4993f47..a4431ed 100644 (file)
@@ -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);
index c59b80e..b477295 100644 (file)
@@ -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);
index 0c5151a..50806be 100644 (file)
@@ -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 */