From: David Herrmann <dh.herrmann@xxxxxxxxxxxxxx> Consider two threads calling read() on the same uinput-fd, both non-blocking. Assume there is data-available so both will simultaneously pass: udev->head == udev->tail Then the first thread goes to sleep and the second one pops the message from the queue. Now assume udev->head == udev->tail. If the first thread wakes up it will call wait_event_*() and sleep in the waitq. This effectively turns the non-blocking FD into a blocking one. We fix this by never calling wait_event_*() for non-blocking FDs hence we will never sleep in the waitq here. Also, if we fail to retrieve an event because it was "stolen" by another thread, we will return -EAGAIN instead of 0 in case of nonblocking read. Signed-off-by: David Herrmann <dh.herrmann@xxxxxxxxxxxxxx> Signed-off-by: Dmitry Torokhov <dtor@xxxxxxx> --- drivers/input/misc/uinput.c | 26 ++++++++++++++------------ 1 files changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index eb9723a..5339c1d 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -460,16 +460,13 @@ static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count, if (count < input_event_size()) return -EINVAL; - if (udev->state != UIST_CREATED) - return -ENODEV; - - if (udev->head == udev->tail && (file->f_flags & O_NONBLOCK)) - return -EAGAIN; - - retval = wait_event_interruptible(udev->waitq, - udev->head != udev->tail || udev->state != UIST_CREATED); - if (retval) - return retval; + if (!(file->f_flags & O_NONBLOCK)) { + retval = wait_event_interruptible(udev->waitq, + udev->head != udev->tail || + udev->state != UIST_CREATED); + if (retval) + return retval; + } retval = mutex_lock_interruptible(&udev->mutex); if (retval) @@ -480,8 +477,10 @@ static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count, goto out; } - while (udev->head != udev->tail && retval + input_event_size() <= count) { - if (input_event_to_user(buffer + retval, &udev->buff[udev->tail])) { + while (udev->head != udev->tail && + retval + input_event_size() <= count) { + if (input_event_to_user(buffer + retval, + &udev->buff[udev->tail])) { retval = -EFAULT; goto out; } @@ -489,6 +488,9 @@ static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count, retval += input_event_size(); } + if (retval == 0 && (file->f_flags & O_NONBLOCK)) + retval = -EAGAIN; + out: mutex_unlock(&udev->mutex); -- 1.7.7.6 -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html