mei: add a spin lock to protect rd_completed queue
authorAlexander Usyskin <alexander.usyskin@intel.com>
Tue, 18 Aug 2020 11:51:40 +0000 (14:51 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 18 Aug 2020 13:44:43 +0000 (15:44 +0200)
In order to support vtags we need to access read completed
queue out of driver big lock.
Add a spin lock to protect rd_completed queue.

Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Link: https://lore.kernel.org/r/20200818115147.2567012-7-tomas.winkler@intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/misc/mei/bus.c
drivers/misc/mei/client.c
drivers/misc/mei/client.h
drivers/misc/mei/main.c
drivers/misc/mei/mei_dev.h

index 2e7ac53..fc20a0d 100644 (file)
@@ -152,7 +152,7 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length,
                if (timeout) {
                        rets = wait_event_interruptible_timeout
                                        (cl->rx_wait,
-                                       (!list_empty(&cl->rd_completed)) ||
+                                       mei_cl_read_cb(cl, NULL) ||
                                        (!mei_cl_is_connected(cl)),
                                        msecs_to_jiffies(timeout));
                        if (rets == 0)
@@ -165,7 +165,7 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length,
                } else {
                        if (wait_event_interruptible
                                        (cl->rx_wait,
-                                       (!list_empty(&cl->rd_completed)) ||
+                                       mei_cl_read_cb(cl, NULL) ||
                                        (!mei_cl_is_connected(cl)))) {
                                if (signal_pending(current))
                                        return -EINTR;
@@ -198,7 +198,7 @@ copy:
        rets = r_length;
 
 free:
-       mei_io_cb_free(cb);
+       mei_cl_del_rd_completed(cl, cb);
 out:
        mutex_unlock(&bus->device_lock);
 
index a527995..276021f 100644 (file)
@@ -507,15 +507,19 @@ struct mei_cl_cb *mei_cl_enqueue_ctrl_wr_cb(struct mei_cl *cl, size_t length,
  *
  * Return: cb on success, NULL if cb is not found
  */
-struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp)
+struct mei_cl_cb *mei_cl_read_cb(struct mei_cl *cl, const struct file *fp)
 {
        struct mei_cl_cb *cb;
+       struct mei_cl_cb *ret_cb = NULL;
 
+       spin_lock(&cl->rd_completed_lock);
        list_for_each_entry(cb, &cl->rd_completed, list)
-               if (!fp || fp == cb->fp)
-                       return cb;
-
-       return NULL;
+               if (!fp || fp == cb->fp) {
+                       ret_cb = cb;
+                       break;
+               }
+       spin_unlock(&cl->rd_completed_lock);
+       return ret_cb;
 }
 
 /**
@@ -541,7 +545,9 @@ int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp)
        mei_io_list_flush_cl(&cl->dev->ctrl_wr_list, cl);
        mei_io_list_flush_cl(&cl->dev->ctrl_rd_list, cl);
        mei_io_list_free_fp(&cl->rd_pending, fp);
+       spin_lock(&cl->rd_completed_lock);
        mei_io_list_free_fp(&cl->rd_completed, fp);
+       spin_unlock(&cl->rd_completed_lock);
 
        return 0;
 }
@@ -559,6 +565,7 @@ static void mei_cl_init(struct mei_cl *cl, struct mei_device *dev)
        init_waitqueue_head(&cl->rx_wait);
        init_waitqueue_head(&cl->tx_wait);
        init_waitqueue_head(&cl->ev_wait);
+       spin_lock_init(&cl->rd_completed_lock);
        INIT_LIST_HEAD(&cl->rd_completed);
        INIT_LIST_HEAD(&cl->rd_pending);
        INIT_LIST_HEAD(&cl->link);
@@ -1230,6 +1237,34 @@ static int mei_cl_tx_flow_ctrl_creds_reduce(struct mei_cl *cl)
        return 0;
 }
 
+/**
+ * mei_cl_add_rd_completed - add read completed callback to list with lock
+ *
+ * @cl: host client
+ * @cb: callback block
+ *
+ */
+void mei_cl_add_rd_completed(struct mei_cl *cl, struct mei_cl_cb *cb)
+{
+       spin_lock(&cl->rd_completed_lock);
+       list_add_tail(&cb->list, &cl->rd_completed);
+       spin_unlock(&cl->rd_completed_lock);
+}
+
+/**
+ * mei_cl_del_rd_completed - free read completed callback with lock
+ *
+ * @cl: host client
+ * @cb: callback block
+ *
+ */
+void mei_cl_del_rd_completed(struct mei_cl *cl, struct mei_cl_cb *cb)
+{
+       spin_lock(&cl->rd_completed_lock);
+       mei_io_cb_free(cb);
+       spin_unlock(&cl->rd_completed_lock);
+}
+
 /**
  *  mei_cl_notify_fop2req - convert fop to proper request
  *
@@ -1897,7 +1932,7 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
                break;
 
        case MEI_FOP_READ:
-               list_add_tail(&cb->list, &cl->rd_completed);
+               mei_cl_add_rd_completed(cl, cb);
                if (!mei_cl_is_fixed_address(cl) &&
                    !WARN_ON(!cl->rx_flow_ctrl_creds))
                        cl->rx_flow_ctrl_creds--;
index 0d0f363..bd57c64 100644 (file)
@@ -133,8 +133,11 @@ int mei_cl_unlink(struct mei_cl *cl);
 
 struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev);
 
-struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl,
-                                const struct file *fp);
+struct mei_cl_cb *mei_cl_read_cb(struct mei_cl *cl, const struct file *fp);
+
+void mei_cl_add_rd_completed(struct mei_cl *cl, struct mei_cl_cb *cb);
+void mei_cl_del_rd_completed(struct mei_cl *cl, struct mei_cl_cb *cb);
+
 struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length,
                                  enum mei_cb_file_ops type,
                                  const struct file *fp);
index 86ef5c1..441bdea 100644 (file)
@@ -178,7 +178,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
 
        mutex_unlock(&dev->device_lock);
        if (wait_event_interruptible(cl->rx_wait,
-                                    !list_empty(&cl->rd_completed) ||
+                                    mei_cl_read_cb(cl, file) ||
                                     !mei_cl_is_connected(cl))) {
                if (signal_pending(current))
                        return -EINTR;
@@ -229,7 +229,7 @@ copy_buffer:
                goto out;
 
 free:
-       mei_io_cb_free(cb);
+       mei_cl_del_rd_completed(cl, cb);
        *offset = 0;
 
 out:
@@ -572,7 +572,7 @@ static __poll_t mei_poll(struct file *file, poll_table *wait)
        if (req_events & (EPOLLIN | EPOLLRDNORM)) {
                poll_wait(file, &cl->rx_wait, wait);
 
-               if (!list_empty(&cl->rd_completed))
+               if (mei_cl_read_cb(cl, file))
                        mask |= EPOLLIN | EPOLLRDNORM;
                else
                        mei_cl_read_start(cl, mei_cl_mtu(cl), file);
index fefa5b5..1219ede 100644 (file)
@@ -217,6 +217,7 @@ struct mei_cl_cb {
  * @tx_cb_queued: number of tx callbacks in queue
  * @writing_state: state of the tx
  * @rd_pending: pending read credits
+ * @rd_completed_lock: protects rd_completed queue
  * @rd_completed: completed read
  *
  * @cldev: device on the mei client bus
@@ -242,6 +243,7 @@ struct mei_cl {
        u8 tx_cb_queued;
        enum mei_file_transaction_states writing_state;
        struct list_head rd_pending;
+       spinlock_t rd_completed_lock; /* protects rd_completed queue */
        struct list_head rd_completed;
 
        struct mei_cl_device *cldev;