diff options
author | Tomas Winkler <tomas.winkler@intel.com> | 2015-02-10 10:39:36 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-03-01 19:36:59 -0800 |
commit | 3d33ff2457355a9dd3c3178b04ab6669882b306c (patch) | |
tree | 692168d2c6df72783349246c879995212d3f7dd3 /drivers/misc | |
parent | 3908be6f9aa5517bc717f8ffdaaafd89a1b78471 (diff) |
mei: fix device reset on mei_cl_irq_read_msg allocation failure
On memory allocation failure mei_cl_irq_read_msg will
return with error that will cause device reset.
Instead we should propagate error to caller and
just clean the read queues.
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/mei/bus.c | 6 | ||||
-rw-r--r-- | drivers/misc/mei/interrupt.c | 117 | ||||
-rw-r--r-- | drivers/misc/mei/main.c | 17 | ||||
-rw-r--r-- | drivers/misc/mei/mei_dev.h | 2 |
4 files changed, 80 insertions, 62 deletions
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index be767f4db26a..025626f4467d 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -322,10 +322,16 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) goto out; } + if (cb->status) { + rets = cb->status; + goto free; + } + r_length = min_t(size_t, length, cb->buf_idx); memcpy(buf, cb->response_buffer.data, r_length); rets = r_length; +free: mei_io_cb_free(cb); cl->reading_state = MEI_IDLE; cl->read_cb = NULL; diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 711cddfa9c99..587cb04a3cf5 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -69,85 +69,91 @@ static inline int mei_cl_hbm_equal(struct mei_cl *cl, cl->me_client_id == mei_hdr->me_addr; } /** - * mei_cl_is_reading - checks if the client - * is the one to read this message + * mei_cl_is_reading - checks if the client is in reading state * * @cl: mei client - * @mei_hdr: header of mei message * - * Return: true on match and false otherwise + * Return: true if the client is reading */ -static bool mei_cl_is_reading(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr) +static bool mei_cl_is_reading(struct mei_cl *cl) { - return mei_cl_hbm_equal(cl, mei_hdr) && - cl->state == MEI_FILE_CONNECTED && + return cl->state == MEI_FILE_CONNECTED && cl->reading_state != MEI_READ_COMPLETE; } /** * mei_cl_irq_read_msg - process client message * - * @dev: the device structure + * @cl: reading client * @mei_hdr: header of mei client message - * @complete_list: An instance of our list structure + * @complete_list: completion list * - * Return: 0 on success, <0 on failure. + * Return: always 0 */ -static int mei_cl_irq_read_msg(struct mei_device *dev, +static int mei_cl_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr, struct mei_cl_cb *complete_list) { - struct mei_cl *cl; - struct mei_cl_cb *cb, *next; + struct mei_device *dev = cl->dev; + struct mei_cl_cb *cb; unsigned char *buffer = NULL; - list_for_each_entry_safe(cb, next, &dev->read_list.list, list) { - cl = cb->cl; - if (!mei_cl_is_reading(cl, mei_hdr)) - continue; + list_for_each_entry(cb, &dev->read_list.list, list) { + if (cl == cb->cl) + break; + } - cl->reading_state = MEI_READING; + if (&cb->list == &dev->read_list.list) { + dev_err(dev->dev, "no reader found\n"); + goto out; + } - if (cb->response_buffer.size == 0 || - cb->response_buffer.data == NULL) { - cl_err(dev, cl, "response buffer is not allocated.\n"); - list_del(&cb->list); - return -ENOMEM; - } + 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); + goto out; + } - if (cb->response_buffer.size < mei_hdr->length + cb->buf_idx) { - cl_dbg(dev, cl, "message overflow. size %d len %d idx %ld\n", - cb->response_buffer.size, - mei_hdr->length, cb->buf_idx); - buffer = krealloc(cb->response_buffer.data, - mei_hdr->length + cb->buf_idx, - GFP_KERNEL); - - if (!buffer) { - list_del(&cb->list); - return -ENOMEM; - } - cb->response_buffer.data = buffer; - cb->response_buffer.size = - mei_hdr->length + cb->buf_idx; - } + cl->reading_state = MEI_READING; - buffer = cb->response_buffer.data + cb->buf_idx; - mei_read_slots(dev, buffer, mei_hdr->length); + if (cb->response_buffer.size == 0 || + cb->response_buffer.data == NULL) { + cl_err(dev, cl, "response buffer is not allocated.\n"); + list_move_tail(&cb->list, &complete_list->list); + cb->status = -ENOMEM; + goto out; + } - cb->buf_idx += mei_hdr->length; - if (mei_hdr->msg_complete) { - cl->status = 0; - list_del(&cb->list); - cl_dbg(dev, cl, "completed read length = %lu\n", - cb->buf_idx); - list_add_tail(&cb->list, &complete_list->list); + if (cb->response_buffer.size < mei_hdr->length + cb->buf_idx) { + cl_dbg(dev, cl, "message overflow. size %d len %d idx %ld\n", + cb->response_buffer.size, mei_hdr->length, cb->buf_idx); + buffer = krealloc(cb->response_buffer.data, + mei_hdr->length + cb->buf_idx, + GFP_KERNEL); + + if (!buffer) { + cb->status = -ENOMEM; + list_move_tail(&cb->list, &complete_list->list); + goto out; } - break; + cb->response_buffer.data = buffer; + cb->response_buffer.size = mei_hdr->length + cb->buf_idx; } - dev_dbg(dev->dev, "message read\n"); + buffer = cb->response_buffer.data + cb->buf_idx; + mei_read_slots(dev, buffer, mei_hdr->length); + + cb->buf_idx += mei_hdr->length; + if (mei_hdr->msg_complete) { + cl_dbg(dev, cl, "completed read length = %lu\n", + cb->buf_idx); + list_move_tail(&cb->list, &complete_list->list); + } + +out: if (!buffer) { + /* assume that mei_hdr->length <= MEI_RD_MSG_BUF_SIZE */ + BUG_ON(mei_hdr->length > MEI_RD_MSG_BUF_SIZE); mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length); dev_dbg(dev->dev, "discarding message " MEI_HDR_FMT "\n", MEI_HDR_PRM(mei_hdr)); @@ -389,14 +395,10 @@ int mei_irq_read_handler(struct mei_device *dev, goto end; } } else { - ret = mei_cl_irq_read_msg(dev, mei_hdr, cmpl_list); - if (ret) { - dev_err(dev->dev, "mei_cl_irq_read_msg failed = %d\n", - ret); - goto end; - } + ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list); } + reset_slots: /* reset the number of slots and header */ *slots = mei_count_full_read_slots(dev); @@ -636,4 +638,3 @@ out: schedule_delayed_work(&dev->timer_work, 2 * HZ); mutex_unlock(&dev->device_lock); } - diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 3c019c0e60eb..cbdbf4af2bf7 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -192,8 +192,8 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, goto out; } - if (cl->read_cb) { - cb = cl->read_cb; + cb = cl->read_cb; + if (cb) { /* read what left */ if (cb->buf_idx > *offset) goto copy_buffer; @@ -218,7 +218,8 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, } if (MEI_READ_COMPLETE != cl->reading_state && - !waitqueue_active(&cl->rx_wait)) { + !waitqueue_active(&cl->rx_wait)) { + if (file->f_flags & O_NONBLOCK) { rets = -EAGAIN; goto out; @@ -248,12 +249,20 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, rets = -ENODEV; goto out; } + if (cl->reading_state != MEI_READ_COMPLETE) { rets = 0; goto out; } - /* now copy the data to user space */ + copy_buffer: + /* now copy the data to user space */ + if (cb->status) { + rets = cb->status; + dev_dbg(dev->dev, "read operation failed %d\n", rets); + goto free; + } + dev_dbg(dev->dev, "buf.size = %d buf.idx= %ld\n", cb->response_buffer.size, cb->buf_idx); if (length == 0 || ubuf == NULL || *offset > cb->buf_idx) { diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 102cc6603eba..195e426b08f0 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -199,6 +199,7 @@ struct mei_cl; * @buf_idx: last read index * @read_time: last read operation time stamp (iamthif) * @file_object: pointer to file structure + * @status: io status of the cb * @internal: communication between driver and FW flag */ struct mei_cl_cb { @@ -210,6 +211,7 @@ struct mei_cl_cb { unsigned long buf_idx; unsigned long read_time; struct file *file_object; + int status; u32 internal:1; }; |