]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/commitdiff
IB/core: Protect against concurrent access to hardware stats
authorMark Bloch <markb@mellanox.com>
Tue, 27 Mar 2018 12:51:05 +0000 (15:51 +0300)
committerJason Gunthorpe <jgg@mellanox.com>
Tue, 27 Mar 2018 21:07:21 +0000 (15:07 -0600)
Currently access to hardware stats buffer isn't protected, this can
result in multiple writes and reads at the same time to the same
memory location. This can lead to providing an incorrect value to
the user. Add a mutex to protect against it.

Fixes: b40f4757daa1 ("IB/core: Make device counter infrastructure dynamic")
Signed-off-by: Mark Bloch <markb@mellanox.com>
Signed-off-by: Leon Romanovsky <leonro@mellanox.com>
Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>
drivers/infiniband/core/sysfs.c
include/rdma/ib_verbs.h

index cf36ff1f0068e5693bc3b9984137bd39d0770ffc..9b0fbab41dc6359e3b2a93c1e699ee7ca6e49864 100644 (file)
@@ -811,10 +811,15 @@ static ssize_t show_hw_stats(struct kobject *kobj, struct attribute *attr,
                dev = port->ibdev;
                stats = port->hw_stats;
        }
+       mutex_lock(&stats->lock);
        ret = update_hw_stats(dev, stats, hsa->port_num, hsa->index);
        if (ret)
-               return ret;
-       return print_hw_stat(stats, hsa->index, buf);
+               goto unlock;
+       ret = print_hw_stat(stats, hsa->index, buf);
+unlock:
+       mutex_unlock(&stats->lock);
+
+       return ret;
 }
 
 static ssize_t show_stats_lifespan(struct kobject *kobj,
@@ -822,17 +827,25 @@ static ssize_t show_stats_lifespan(struct kobject *kobj,
                                   char *buf)
 {
        struct hw_stats_attribute *hsa;
+       struct rdma_hw_stats *stats;
        int msecs;
 
        hsa = container_of(attr, struct hw_stats_attribute, attr);
        if (!hsa->port_num) {
                struct ib_device *dev = container_of((struct device *)kobj,
                                                     struct ib_device, dev);
-               msecs = jiffies_to_msecs(dev->hw_stats->lifespan);
+
+               stats = dev->hw_stats;
        } else {
                struct ib_port *p = container_of(kobj, struct ib_port, kobj);
-               msecs = jiffies_to_msecs(p->hw_stats->lifespan);
+
+               stats = p->hw_stats;
        }
+
+       mutex_lock(&stats->lock);
+       msecs = jiffies_to_msecs(stats->lifespan);
+       mutex_unlock(&stats->lock);
+
        return sprintf(buf, "%d\n", msecs);
 }
 
@@ -841,6 +854,7 @@ static ssize_t set_stats_lifespan(struct kobject *kobj,
                                  const char *buf, size_t count)
 {
        struct hw_stats_attribute *hsa;
+       struct rdma_hw_stats *stats;
        int msecs;
        int jiffies;
        int ret;
@@ -855,11 +869,18 @@ static ssize_t set_stats_lifespan(struct kobject *kobj,
        if (!hsa->port_num) {
                struct ib_device *dev = container_of((struct device *)kobj,
                                                     struct ib_device, dev);
-               dev->hw_stats->lifespan = jiffies;
+
+               stats = dev->hw_stats;
        } else {
                struct ib_port *p = container_of(kobj, struct ib_port, kobj);
-               p->hw_stats->lifespan = jiffies;
+
+               stats = p->hw_stats;
        }
+
+       mutex_lock(&stats->lock);
+       stats->lifespan = jiffies;
+       mutex_unlock(&stats->lock);
+
        return count;
 }
 
@@ -952,6 +973,7 @@ static void setup_hw_stats(struct ib_device *device, struct ib_port *port,
                sysfs_attr_init(hsag->attrs[i]);
        }
 
+       mutex_init(&stats->lock);
        /* treat an error here as non-fatal */
        hsag->attrs[i] = alloc_hsa_lifespan("lifespan", port_num);
        if (hsag->attrs[i])
index e9288d0f627e5d432437579990b7a9c617f3abe4..48f416fabe0cf70bb648b5c033d3e44dc74dfd89 100644 (file)
@@ -470,6 +470,9 @@ enum ib_port_speed {
 
 /**
  * struct rdma_hw_stats
+ * @lock - Mutex to protect parallel write access to lifespan and values
+ *    of counters, which are 64bits and not guaranteeed to be written
+ *    atomicaly on 32bits systems.
  * @timestamp - Used by the core code to track when the last update was
  * @lifespan - Used by the core code to determine how old the counters
  *   should be before being updated again.  Stored in jiffies, defaults
@@ -485,6 +488,7 @@ enum ib_port_speed {
  *   filled in by the drivers get_stats routine
  */
 struct rdma_hw_stats {
+       struct mutex    lock; /* Protect lifespan and values[] */
        unsigned long   timestamp;
        unsigned long   lifespan;
        const char * const *names;