summaryrefslogtreecommitdiffstats
path: root/drivers/misc/mei/main.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/main.c
parent257355a44b9929e55d6fd47bfff66971dc4de948 (diff)
downloadlinux-stable-af336cabe08363ba8493e7d7e5d070353eb30caa.tar.gz
linux-stable-af336cabe08363ba8493e7d7e5d070353eb30caa.tar.bz2
linux-stable-af336cabe08363ba8493e7d7e5d070353eb30caa.zip
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/main.c')
-rw-r--r--drivers/misc/mei/main.c65
1 files changed, 65 insertions, 0 deletions
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c
index 758dc73602d5..401b1bc4d282 100644
--- a/drivers/misc/mei/main.c
+++ b/drivers/misc/mei/main.c
@@ -291,6 +291,27 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
goto out;
}
+ while (cl->tx_cb_queued >= dev->tx_queue_limit) {
+ if (file->f_flags & O_NONBLOCK) {
+ rets = -EAGAIN;
+ goto out;
+ }
+ mutex_unlock(&dev->device_lock);
+ rets = wait_event_interruptible(cl->tx_wait,
+ cl->writing_state == MEI_WRITE_COMPLETE ||
+ (!mei_cl_is_connected(cl)));
+ mutex_lock(&dev->device_lock);
+ if (rets) {
+ if (signal_pending(current))
+ rets = -EINTR;
+ goto out;
+ }
+ if (!mei_cl_is_connected(cl)) {
+ rets = -ENODEV;
+ goto out;
+ }
+ }
+
*offset = 0;
cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, file);
if (!cb) {
@@ -580,6 +601,12 @@ static __poll_t mei_poll(struct file *file, poll_table *wait)
mei_cl_read_start(cl, mei_cl_mtu(cl), file);
}
+ if (req_events & (POLLOUT | POLLWRNORM)) {
+ poll_wait(file, &cl->tx_wait, wait);
+ if (cl->tx_cb_queued < dev->tx_queue_limit)
+ mask |= POLLOUT | POLLWRNORM;
+ }
+
out:
mutex_unlock(&dev->device_lock);
return mask;
@@ -749,10 +776,48 @@ static ssize_t hbm_ver_drv_show(struct device *device,
}
static DEVICE_ATTR_RO(hbm_ver_drv);
+static ssize_t tx_queue_limit_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct mei_device *dev = dev_get_drvdata(device);
+ u8 size = 0;
+
+ mutex_lock(&dev->device_lock);
+ size = dev->tx_queue_limit;
+ mutex_unlock(&dev->device_lock);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", size);
+}
+
+static ssize_t tx_queue_limit_store(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mei_device *dev = dev_get_drvdata(device);
+ u8 limit;
+ unsigned int inp;
+ int err;
+
+ err = kstrtouint(buf, 10, &inp);
+ if (err)
+ return err;
+ if (inp > MEI_TX_QUEUE_LIMIT_MAX || inp < MEI_TX_QUEUE_LIMIT_MIN)
+ return -EINVAL;
+ limit = inp;
+
+ mutex_lock(&dev->device_lock);
+ dev->tx_queue_limit = limit;
+ mutex_unlock(&dev->device_lock);
+
+ return count;
+}
+static DEVICE_ATTR_RW(tx_queue_limit);
+
static struct attribute *mei_attrs[] = {
&dev_attr_fw_status.attr,
&dev_attr_hbm_ver.attr,
&dev_attr_hbm_ver_drv.attr,
+ &dev_attr_tx_queue_limit.attr,
NULL
};
ATTRIBUTE_GROUPS(mei);