mei: allow read concurrency
authorTomas Winkler <tomas.winkler@intel.com>
Tue, 10 Feb 2015 08:39:46 +0000 (10:39 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 2 Mar 2015 03:37:00 +0000 (19:37 -0800)
Replace clunky read state machine with read stack
implemented as per client read list, this is important
mostly for mei drivers with unsolicited reads

Signed-off-by: Tomas Winkler <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/debugfs.c
drivers/misc/mei/init.c
drivers/misc/mei/interrupt.c
drivers/misc/mei/main.c
drivers/misc/mei/mei_dev.h

index b5385372693dbadb6bf180078d2122b26dc8322f..17ca7e20fb6adfa56d9be501c300fa3644782d22 100644 (file)
@@ -288,19 +288,20 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
 
        mutex_lock(&dev->device_lock);
 
-       if (!cl->read_cb) {
-               rets = mei_cl_read_start(cl, length, NULL);
-               if (rets < 0)
-                       goto out;
-       }
+       cb = mei_cl_read_cb(cl, NULL);
+       if (cb)
+               goto copy;
+
+       rets = mei_cl_read_start(cl, length, NULL);
+       if (rets && rets != -EBUSY)
+               goto out;
 
-       if (cl->reading_state != MEI_READ_COMPLETE &&
-           !waitqueue_active(&cl->rx_wait)) {
+       if (list_empty(&cl->rd_completed) && !waitqueue_active(&cl->rx_wait)) {
 
                mutex_unlock(&dev->device_lock);
 
                if (wait_event_interruptible(cl->rx_wait,
-                               cl->reading_state == MEI_READ_COMPLETE  ||
+                               (!list_empty(&cl->rd_completed)) ||
                                mei_cl_is_transitioning(cl))) {
 
                        if (signal_pending(current))
@@ -309,15 +310,20 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
                }
 
                mutex_lock(&dev->device_lock);
-       }
 
+               if (mei_cl_is_transitioning(cl)) {
+                       rets = -EBUSY;
+                       goto out;
+               }
+       }
 
-       if (cl->reading_state != MEI_READ_COMPLETE) {
+       cb = mei_cl_read_cb(cl, NULL);
+       if (!cb) {
                rets = 0;
                goto out;
        }
 
-       cb = cl->read_cb;
+copy:
        if (cb->status) {
                rets = cb->status;
                goto free;
@@ -329,9 +335,6 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
 
 free:
        mei_io_cb_free(cb);
-       cl->read_cb = NULL;
-       cl->reading_state = MEI_IDLE;
-
 out:
        mutex_unlock(&dev->device_lock);
 
@@ -443,7 +446,7 @@ int mei_cl_enable_device(struct mei_cl_device *device)
 
        mutex_unlock(&dev->device_lock);
 
-       if (device->event_cb && !cl->read_cb)
+       if (device->event_cb)
                mei_cl_read_start(device->cl, 0, NULL);
 
        if (!device->ops || !device->ops->enable)
@@ -485,8 +488,7 @@ int mei_cl_disable_device(struct mei_cl_device *device)
        }
 
        /* Flush queues and remove any pending read */
-       mei_cl_flush_queues(cl);
-       mei_io_cb_free(cl->read_cb);
+       mei_cl_flush_queues(cl, NULL);
 
        device->event_cb = NULL;
 
index 624bf0182a504bc7380192dd06012ad7ae5cc1f3..98a5363e1e8a18cd863aaf13af6ec8ad8fff9b30 100644 (file)
@@ -457,14 +457,56 @@ struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length,
        return cb;
 }
 
+/**
+ * mei_cl_read_cb - find this cl's callback in the read list
+ *     for a specific file
+ *
+ * @cl: host client
+ * @fp: file pointer (matching cb file object), may be NULL
+ *
+ * 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 *cb;
+
+       list_for_each_entry(cb, &cl->rd_completed, list)
+               if (!fp || fp == cb->file_object)
+                       return cb;
+
+       return NULL;
+}
+
+/**
+ * mei_cl_read_cb_flush - free client's read pending and completed cbs
+ *   for a specific file
+ *
+ * @cl: host client
+ * @fp: file pointer (matching cb file object), may be NULL
+ */
+void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp)
+{
+       struct mei_cl_cb *cb, *next;
+
+       list_for_each_entry_safe(cb, next, &cl->rd_completed, list)
+               if (!fp || fp == cb->file_object)
+                       mei_io_cb_free(cb);
+
+
+       list_for_each_entry_safe(cb, next, &cl->rd_pending, list)
+               if (!fp || fp == cb->file_object)
+                       mei_io_cb_free(cb);
+}
+
 /**
  * mei_cl_flush_queues - flushes queue lists belonging to cl.
  *
  * @cl: host client
+ * @fp: file pointer (matching cb file object), may be NULL
  *
  * Return: 0 on success, -EINVAL if cl or cl->dev is NULL.
  */
-int mei_cl_flush_queues(struct mei_cl *cl)
+int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp)
 {
        struct mei_device *dev;
 
@@ -474,13 +516,15 @@ int mei_cl_flush_queues(struct mei_cl *cl)
        dev = cl->dev;
 
        cl_dbg(dev, cl, "remove list entry belonging to cl\n");
-       mei_io_list_flush(&cl->dev->read_list, cl);
        mei_io_list_free(&cl->dev->write_list, cl);
        mei_io_list_free(&cl->dev->write_waiting_list, cl);
        mei_io_list_flush(&cl->dev->ctrl_wr_list, cl);
        mei_io_list_flush(&cl->dev->ctrl_rd_list, cl);
        mei_io_list_flush(&cl->dev->amthif_cmd_list, cl);
        mei_io_list_flush(&cl->dev->amthif_rd_complete_list, cl);
+
+       mei_cl_read_cb_flush(cl, fp);
+
        return 0;
 }
 
@@ -497,9 +541,10 @@ void mei_cl_init(struct mei_cl *cl, struct mei_device *dev)
        init_waitqueue_head(&cl->wait);
        init_waitqueue_head(&cl->rx_wait);
        init_waitqueue_head(&cl->tx_wait);
+       INIT_LIST_HEAD(&cl->rd_completed);
+       INIT_LIST_HEAD(&cl->rd_pending);
        INIT_LIST_HEAD(&cl->link);
        INIT_LIST_HEAD(&cl->device_link);
-       cl->reading_state = MEI_IDLE;
        cl->writing_state = MEI_IDLE;
        cl->dev = dev;
 }
@@ -523,24 +568,6 @@ struct mei_cl *mei_cl_allocate(struct mei_device *dev)
        return cl;
 }
 
-/**
- * mei_cl_find_read_cb - find this cl's callback in the read list
- *
- * @cl: host client
- *
- * Return: cb on success, NULL on error
- */
-struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl)
-{
-       struct mei_device *dev = cl->dev;
-       struct mei_cl_cb *cb;
-
-       list_for_each_entry(cb, &dev->read_list.list, list)
-               if (mei_cl_cmp_id(cl, cb->cl))
-                       return cb;
-       return NULL;
-}
-
 /**
  * mei_cl_link - allocate host id in the host map
  *
@@ -1006,10 +1033,10 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp)
        if (!mei_cl_is_connected(cl))
                return -ENODEV;
 
-       if (cl->read_cb) {
-               cl_dbg(dev, cl, "read is pending.\n");
+       /* HW currently supports only one pending read */
+       if (!list_empty(&cl->rd_pending))
                return -EBUSY;
-       }
+
        me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id);
        if (!me_cl) {
                cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
@@ -1036,13 +1063,11 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp)
                if (rets < 0)
                        goto out;
 
-               list_add_tail(&cb->list, &dev->read_list.list);
+               list_add_tail(&cb->list, &cl->rd_pending);
        } else {
                list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
        }
 
-       cl->read_cb = cb;
-
 out:
        cl_dbg(dev, cl, "rpm: autosuspend\n");
        pm_runtime_mark_last_busy(dev->dev);
@@ -1268,9 +1293,8 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
                if (waitqueue_active(&cl->tx_wait))
                        wake_up_interruptible(&cl->tx_wait);
 
-       } else if (cb->fop_type == MEI_FOP_READ &&
-                       MEI_READING == cl->reading_state) {
-               cl->reading_state = MEI_READ_COMPLETE;
+       } else if (cb->fop_type == MEI_FOP_READ) {
+               list_add_tail(&cb->list, &cl->rd_completed);
                if (waitqueue_active(&cl->rx_wait))
                        wake_up_interruptible(&cl->rx_wait);
                else
index c3d0e200a642d31b00f207307e903c1628d796bd..eb02f34b2fe090e359be3d4384ed37cbda19a686 100644 (file)
@@ -77,11 +77,12 @@ int mei_cl_unlink(struct mei_cl *cl);
 
 struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev, int id);
 
-int mei_cl_flush_queues(struct mei_cl *cl);
-struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl);
-
+struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl,
+                                const struct file *fp);
+void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp);
 struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length,
                                  enum mei_cb_file_ops type, struct file *fp);
+int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp);
 
 int mei_cl_flow_ctrl_creds(struct mei_cl *cl);
 
index 50fc6635fab1efcd31a90acebc975840494c0fb8..d9cd7e6ee48466acf4afcbbb19b2655ca3eb3439 100644 (file)
@@ -117,7 +117,7 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf,
                pos += scnprintf(buf + pos, bufsz - pos,
                        "%2d|%2d|%4d|%5d|%2d|%2d|\n",
                        i, cl->me_client_id, cl->host_client_id, cl->state,
-                       cl->reading_state, cl->writing_state);
+                       !list_empty(&cl->rd_completed), cl->writing_state);
                i++;
        }
 out:
index 106c054f573f56bacfe949c2e436f632d383637f..4596401888e571e82d646c444b50828a06d25774 100644 (file)
@@ -395,7 +395,6 @@ void mei_device_init(struct mei_device *dev,
        dev->dev_state = MEI_DEV_INITIALIZING;
        dev->reset_count = 0;
 
-       mei_io_list_init(&dev->read_list);
        mei_io_list_init(&dev->write_list);
        mei_io_list_init(&dev->write_waiting_list);
        mei_io_list_init(&dev->ctrl_wr_list);
index 1e2f3c77485371249b6250f99847227bc75c4a3e..3f23629759db870a7df621ebef1d857295cfe818 100644 (file)
@@ -68,18 +68,6 @@ static inline int mei_cl_hbm_equal(struct mei_cl *cl,
        return cl->host_client_id == mei_hdr->host_addr &&
                cl->me_client_id == mei_hdr->me_addr;
 }
-/**
- * mei_cl_is_reading - checks if the client is in reading state
- *
- * @cl: mei client
- *
- * Return: true if the client is reading
- */
-static bool mei_cl_is_reading(struct mei_cl *cl)
-{
-       return cl->state == MEI_FILE_CONNECTED &&
-               cl->reading_state != MEI_READ_COMPLETE;
-}
 
 /**
  * mei_irq_discard_msg  - discard received message
@@ -116,24 +104,18 @@ int mei_cl_irq_read_msg(struct mei_cl *cl,
        struct mei_cl_cb *cb;
        unsigned char *buffer = NULL;
 
-       list_for_each_entry(cb, &dev->read_list.list, list) {
-               if (cl == cb->cl)
-                       break;
-       }
-
-       if (&cb->list == &dev->read_list.list) {
-               dev_err(dev->dev, "no reader found\n");
+       cb = list_first_entry_or_null(&cl->rd_pending, struct mei_cl_cb, list);
+       if (!cb) {
+               cl_err(dev, cl, "pending read cb not found\n");
                goto out;
        }
 
-       if (!mei_cl_is_reading(cl)) {
-               cl_err(dev, cl, "cl is not reading state=%d reading state=%d\n",
-                       cl->state, cl->reading_state);
+       if (cl->state != MEI_FILE_CONNECTED) {
+               cl_dbg(dev, cl, "not connected\n");
+               cb->status = -ENODEV;
                goto out;
        }
 
-       cl->reading_state = MEI_READING;
-
        if (cb->buf.size == 0 || cb->buf.data == NULL) {
                cl_err(dev, cl, "response buffer is not allocated.\n");
                list_move_tail(&cb->list, &complete_list->list);
@@ -163,8 +145,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl,
 
        if (mei_hdr->msg_complete) {
                cb->read_time = jiffies;
-               cl_dbg(dev, cl, "completed read length = %lu\n",
-                       cb->buf_idx);
+               cl_dbg(dev, cl, "completed read length = %lu\n", cb->buf_idx);
                list_move_tail(&cb->list, &complete_list->list);
        }
 
@@ -281,7 +262,7 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb,
                return ret;
        }
 
-       list_move_tail(&cb->list, &dev->read_list.list);
+       list_move_tail(&cb->list, &cl->rd_pending);
 
        return 0;
 }
index c34853be963f0b7f407e4e62dc6d498507142cf4..d80867e0d803809f666f0cdf943bdd7e5542fdba 100644 (file)
@@ -112,14 +112,11 @@ static int mei_release(struct inode *inode, struct file *file)
                cl_dbg(dev, cl, "disconnecting\n");
                rets = mei_cl_disconnect(cl);
        }
-       mei_cl_flush_queues(cl);
+       mei_cl_flush_queues(cl, file);
        cl_dbg(dev, cl, "removing\n");
 
        mei_cl_unlink(cl);
 
-       mei_io_cb_free(cl->read_cb);
-       cl->read_cb = NULL;
-
        file->private_data = NULL;
 
        kfree(cl);
@@ -143,8 +140,8 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
                        size_t length, loff_t *offset)
 {
        struct mei_cl *cl = file->private_data;
-       struct mei_cl_cb *cb = NULL;
        struct mei_device *dev;
+       struct mei_cl_cb *cb = NULL;
        int rets;
        int err;
 
@@ -171,7 +168,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
                goto out;
        }
 
-       cb = cl->read_cb;
+       cb = mei_cl_read_cb(cl, file);
        if (cb) {
                /* read what left */
                if (cb->buf_idx > *offset)
@@ -196,9 +193,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
                goto out;
        }
 
-       if (MEI_READ_COMPLETE != cl->reading_state &&
-               !waitqueue_active(&cl->rx_wait)) {
-
+       if (list_empty(&cl->rd_completed) && !waitqueue_active(&cl->rx_wait)) {
                if (file->f_flags & O_NONBLOCK) {
                        rets = -EAGAIN;
                        goto out;
@@ -207,7 +202,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
                mutex_unlock(&dev->device_lock);
 
                if (wait_event_interruptible(cl->rx_wait,
-                               MEI_READ_COMPLETE == cl->reading_state ||
+                               (!list_empty(&cl->rd_completed)) ||
                                mei_cl_is_transitioning(cl))) {
 
                        if (signal_pending(current))
@@ -222,14 +217,8 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
                }
        }
 
-       cb = cl->read_cb;
-
+       cb = mei_cl_read_cb(cl, file);
        if (!cb) {
-               rets = -ENODEV;
-               goto out;
-       }
-
-       if (cl->reading_state != MEI_READ_COMPLETE) {
                rets = 0;
                goto out;
        }
@@ -266,9 +255,7 @@ copy_buffer:
 
 free:
        mei_io_cb_free(cb);
-       cl->read_cb = NULL;
 
-       cl->reading_state = MEI_IDLE;
 out:
        dev_dbg(dev->dev, "end mei read rets= %d\n", rets);
        mutex_unlock(&dev->device_lock);
@@ -335,8 +322,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
                        timeout = write_cb->read_time +
                                mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
 
-                       if (time_after(jiffies, timeout) ||
-                           cl->reading_state == MEI_READ_COMPLETE) {
+                       if (time_after(jiffies, timeout)) {
                                *offset = 0;
                                mei_io_cb_free(write_cb);
                                write_cb = NULL;
@@ -344,19 +330,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
                }
        }
 
-       /* free entry used in read */
-       if (cl->reading_state == MEI_READ_COMPLETE) {
-               *offset = 0;
-               write_cb = mei_cl_find_read_cb(cl);
-               if (write_cb) {
-                       mei_io_cb_free(write_cb);
-                       write_cb = NULL;
-                       cl->read_cb = NULL;
-                       cl->reading_state = MEI_IDLE;
-               }
-       } else if (cl->reading_state == MEI_IDLE)
-               *offset = 0;
-
+       *offset = 0;
        write_cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, file);
        if (!write_cb) {
                rets = -ENOMEM;
index 1a0f6e9588b68caf422cdd4a37eaef22a6cd7a88..f066ecd719393aaa8f52a165d5d0b8b13416bf8e 100644 (file)
@@ -231,9 +231,9 @@ struct mei_cl_cb {
  * @me_client_id: me/fw id
  * @mei_flow_ctrl_creds: transmit flow credentials
  * @timer_count:  watchdog timer for operation completion
- * @reading_state: state of the rx
  * @writing_state: state of the tx
- * @read_cb: current pending reading callback
+ * @rd_pending: pending read credits
+ * @rd_completed: completed read
  *
  * @device: device on the mei client bus
  * @device_link:  link to bus clients
@@ -251,9 +251,9 @@ struct mei_cl {
        u8 me_client_id;
        u8 mei_flow_ctrl_creds;
        u8 timer_count;
-       enum mei_file_transaction_states reading_state;
        enum mei_file_transaction_states writing_state;
-       struct mei_cl_cb *read_cb;
+       struct list_head rd_pending;
+       struct list_head rd_completed;
 
        /* MEI CL bus data */
        struct mei_cl_device *device;
@@ -425,7 +425,6 @@ const char *mei_pg_state_str(enum mei_pg_state state);
  * @cdev        : character device
  * @minor       : minor number allocated for device
  *
- * @read_list   : read completion list
  * @write_list  : write pending list
  * @write_waiting_list : write completion list
  * @ctrl_wr_list : pending control write list
@@ -501,7 +500,6 @@ struct mei_device {
        struct cdev cdev;
        int minor;
 
-       struct mei_cl_cb read_list;
        struct mei_cl_cb write_list;
        struct mei_cl_cb write_waiting_list;
        struct mei_cl_cb ctrl_wr_list;