summaryrefslogtreecommitdiffstats
path: root/drivers/misc/mei/client.c
diff options
context:
space:
mode:
authorAlexander Usyskin <alexander.usyskin@intel.com>2018-02-25 20:07:05 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-03-14 19:33:13 +0100
commitaf336cabe08363ba8493e7d7e5d070353eb30caa (patch)
tree003025b642d6dcfab2a2f5ba220ce69ee69def65 /drivers/misc/mei/client.c
parent257355a44b9929e55d6fd47bfff66971dc4de948 (diff)
downloadlinux-af336cabe08363ba8493e7d7e5d070353eb30caa.tar.bz2
mei: limit the number of queued writes
Limit the number of queued writes per client. Writes above this threshold are blocked till place in the transmit queue is available. The limit is configurable via sysfs and defaults to 50. The implementation should provide blocking I/O behavior. Prior to this change one would end up in the hands of OOM. Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com> Signed-off-by: Tomas Winkler <tomas.winkler@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/misc/mei/client.c')
-rw-r--r--drivers/misc/mei/client.c82
1 files changed, 50 insertions, 32 deletions
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index bdfb4ecf848a..8d6197a88b54 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -350,6 +350,36 @@ void mei_io_cb_free(struct mei_cl_cb *cb)
}
/**
+ * mei_tx_cb_queue - queue tx callback
+ *
+ * Locking: called under "dev->device_lock" lock
+ *
+ * @cb: mei callback struct
+ * @head: an instance of list to queue on
+ */
+static inline void mei_tx_cb_enqueue(struct mei_cl_cb *cb,
+ struct list_head *head)
+{
+ list_add_tail(&cb->list, head);
+ cb->cl->tx_cb_queued++;
+}
+
+/**
+ * mei_tx_cb_dequeue - dequeue tx callback
+ *
+ * Locking: called under "dev->device_lock" lock
+ *
+ * @cb: mei callback struct to dequeue and free
+ */
+static inline void mei_tx_cb_dequeue(struct mei_cl_cb *cb)
+{
+ if (!WARN_ON(cb->cl->tx_cb_queued == 0))
+ cb->cl->tx_cb_queued--;
+
+ mei_io_cb_free(cb);
+}
+
+/**
* mei_io_cb_init - allocate and initialize io callback
*
* @cl: mei client
@@ -377,49 +407,37 @@ static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl,
}
/**
- * __mei_io_list_flush_cl - removes and frees cbs belonging to cl.
+ * mei_io_list_flush_cl - removes cbs belonging to the cl.
*
* @head: an instance of our list structure
- * @cl: host client, can be NULL for flushing the whole list
- * @free: whether to free the cbs
+ * @cl: host client
*/
-static void __mei_io_list_flush_cl(struct list_head *head,
- const struct mei_cl *cl, bool free)
+static void mei_io_list_flush_cl(struct list_head *head,
+ const struct mei_cl *cl)
{
struct mei_cl_cb *cb, *next;
- /* enable removing everything if no cl is specified */
list_for_each_entry_safe(cb, next, head, list) {
- if (!cl || mei_cl_cmp_id(cl, cb->cl)) {
+ if (mei_cl_cmp_id(cl, cb->cl))
list_del_init(&cb->list);
- if (free)
- mei_io_cb_free(cb);
- }
}
}
/**
- * mei_io_list_flush_cl - removes list entry belonging to cl.
+ * mei_io_tx_list_free_cl - removes cb belonging to the cl and free them
*
* @head: An instance of our list structure
* @cl: host client
*/
-static inline void mei_io_list_flush_cl(struct list_head *head,
- const struct mei_cl *cl)
+static void mei_io_tx_list_free_cl(struct list_head *head,
+ const struct mei_cl *cl)
{
- __mei_io_list_flush_cl(head, cl, false);
-}
+ struct mei_cl_cb *cb, *next;
-/**
- * mei_io_list_free_cl - removes cb belonging to cl and free them
- *
- * @head: An instance of our list structure
- * @cl: host client
- */
-static inline void mei_io_list_free_cl(struct list_head *head,
- const struct mei_cl *cl)
-{
- __mei_io_list_flush_cl(head, cl, true);
+ list_for_each_entry_safe(cb, next, head, list) {
+ if (mei_cl_cmp_id(cl, cb->cl))
+ mei_tx_cb_dequeue(cb);
+ }
}
/**
@@ -538,8 +556,8 @@ int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp)
dev = cl->dev;
cl_dbg(dev, cl, "remove list entry belonging to cl\n");
- mei_io_list_free_cl(&cl->dev->write_list, cl);
- mei_io_list_free_cl(&cl->dev->write_waiting_list, cl);
+ mei_io_tx_list_free_cl(&cl->dev->write_list, cl);
+ mei_io_tx_list_free_cl(&cl->dev->write_waiting_list, cl);
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);
@@ -756,8 +774,8 @@ static void mei_cl_set_disconnected(struct mei_cl *cl)
return;
cl->state = MEI_FILE_DISCONNECTED;
- mei_io_list_free_cl(&dev->write_list, cl);
- mei_io_list_free_cl(&dev->write_waiting_list, cl);
+ mei_io_tx_list_free_cl(&dev->write_list, cl);
+ mei_io_tx_list_free_cl(&dev->write_waiting_list, cl);
mei_io_list_flush_cl(&dev->ctrl_rd_list, cl);
mei_io_list_flush_cl(&dev->ctrl_wr_list, cl);
mei_cl_wake_all(cl);
@@ -1693,9 +1711,9 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
out:
if (mei_hdr.msg_complete)
- list_add_tail(&cb->list, &dev->write_waiting_list);
+ mei_tx_cb_enqueue(cb, &dev->write_waiting_list);
else
- list_add_tail(&cb->list, &dev->write_list);
+ mei_tx_cb_enqueue(cb, &dev->write_list);
cb = NULL;
if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) {
@@ -1741,7 +1759,7 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
switch (cb->fop_type) {
case MEI_FOP_WRITE:
- mei_io_cb_free(cb);
+ mei_tx_cb_dequeue(cb);
cl->writing_state = MEI_WRITE_COMPLETE;
if (waitqueue_active(&cl->tx_wait)) {
wake_up_interruptible(&cl->tx_wait);