Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit af336cab authored by Alexander Usyskin's avatar Alexander Usyskin Committed by Greg Kroah-Hartman
Browse files

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: default avatarAlexander Usyskin <alexander.usyskin@intel.com>
Signed-off-by: default avatarTomas Winkler <tomas.winkler@intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 257355a4
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -45,3 +45,12 @@ Contact: Tomas Winkler <tomas.winkler@intel.com>
Description:	Display the driver HBM protocol version.

		The HBM protocol version supported by the driver.

What:		/sys/class/mei/meiN/tx_queue_limit
Date:		Jan 2018
KernelVersion:	4.16
Contact:	Tomas Winkler <tomas.winkler@intel.com>
Description:	Configure tx queue limit

		Set maximal number of pending writes
		per opened session.
+17 −0
Original line number Diff line number Diff line
@@ -74,6 +74,23 @@ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
		goto out;
	}

	while (cl->tx_cb_queued >= bus->tx_queue_limit) {
		mutex_unlock(&bus->device_lock);
		rets = wait_event_interruptible(cl->tx_wait,
				cl->writing_state == MEI_WRITE_COMPLETE ||
				(!mei_cl_is_connected(cl)));
		mutex_lock(&bus->device_lock);
		if (rets) {
			if (signal_pending(current))
				rets = -EINTR;
			goto out;
		}
		if (!mei_cl_is_connected(cl)) {
			rets = -ENODEV;
			goto out;
		}
	}

	cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, NULL);
	if (!cb) {
		rets = -ENOMEM;
+50 −32
Original line number Diff line number Diff line
@@ -349,6 +349,36 @@ void mei_io_cb_free(struct mei_cl_cb *cb)
	kfree(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
 *
@@ -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,
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);
+4 −3
Original line number Diff line number Diff line
@@ -97,7 +97,7 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf,
	int pos = 0;
	int ret;

#define HDR "   |me|host|state|rd|wr|\n"
#define HDR "   |me|host|state|rd|wr|wrq\n"

	if (!dev)
		return -ENODEV;
@@ -130,9 +130,10 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf,
	list_for_each_entry(cl, &dev->file_list, link) {

		pos += scnprintf(buf + pos, bufsz - pos,
			"%3d|%2d|%4d|%5d|%2d|%2d|\n",
			"%3d|%2d|%4d|%5d|%2d|%2d|%3u\n",
			i, mei_cl_me_id(cl), cl->host_client_id, cl->state,
			!list_empty(&cl->rd_completed), cl->writing_state);
			!list_empty(&cl->rd_completed), cl->writing_state,
			cl->tx_cb_queued);
		i++;
	}
out:
+1 −0
Original line number Diff line number Diff line
@@ -383,6 +383,7 @@ void mei_device_init(struct mei_device *dev,
	INIT_LIST_HEAD(&dev->write_waiting_list);
	INIT_LIST_HEAD(&dev->ctrl_wr_list);
	INIT_LIST_HEAD(&dev->ctrl_rd_list);
	dev->tx_queue_limit = MEI_TX_QUEUE_LIMIT_DEFAULT;

	INIT_DELAYED_WORK(&dev->timer_work, mei_timer);
	INIT_WORK(&dev->reset_work, mei_reset_work);
Loading