diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2012-04-20 22:33:08 -0700 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2012-04-20 23:11:01 -0700 |
commit | 486c8aba39e5f194519cd5c0e85e5d1de8b74b03 (patch) | |
tree | f82d9745cd40b94dfa4b3107e7c33c5224d69c77 /drivers/input/serio/serio_raw.c | |
parent | 71f3d070a309504cdfef87b9e98836395b75ae0e (diff) | |
download | linux-486c8aba39e5f194519cd5c0e85e5d1de8b74b03.tar.bz2 |
Input: serio_raw - ensure we don't block in non-blocking read
Avoid calling wait_event_interruptible() if client requested non-blocking
read, since it is not guaranteed that another thread will not consume
event after we checked if serio_raw->head != serio_raw->tail.
Also ensure we do not return 0 but keep waiting instead in blocking case,
when another thread steals "our" byte.
Reviewed-by: David Herrmann <dh.herrmann@googlemail.com>
Reviewed-by: Che-Liang Chiou <clchiou@chromium.org>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/input/serio/serio_raw.c')
-rw-r--r-- | drivers/input/serio/serio_raw.c | 43 |
1 files changed, 25 insertions, 18 deletions
diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c index 948fd5a045f7..3e243621c0e3 100644 --- a/drivers/input/serio/serio_raw.c +++ b/drivers/input/serio/serio_raw.c @@ -165,31 +165,38 @@ static ssize_t serio_raw_read(struct file *file, char __user *buffer, struct serio_raw *serio_raw = client->serio_raw; char uninitialized_var(c); ssize_t read = 0; - int retval; + int error = 0; - if (serio_raw->dead) - return -ENODEV; + do { + if (serio_raw->dead) + return -ENODEV; - if (serio_raw->head == serio_raw->tail && (file->f_flags & O_NONBLOCK)) - return -EAGAIN; + if (serio_raw->head == serio_raw->tail && + (file->f_flags & O_NONBLOCK)) + return -EAGAIN; - retval = wait_event_interruptible(serio_raw->wait, - serio_raw->head != serio_raw->tail || serio_raw->dead); - if (retval) - return retval; + if (count == 0) + break; - if (serio_raw->dead) - return -ENODEV; + while (read < count && serio_raw_fetch_byte(serio_raw, &c)) { + if (put_user(c, buffer++)) { + error = -EFAULT; + goto out; + } + read++; + } - while (read < count && serio_raw_fetch_byte(serio_raw, &c)) { - if (put_user(c, buffer++)) { - retval = -EFAULT; + if (read) break; - } - read++; - } - return read ?: retval; + if (!(file->f_flags & O_NONBLOCK)) + error = wait_event_interruptible(serio_raw->wait, + serio_raw->head != serio_raw->tail || + serio_raw->dead); + } while (!error); + +out: + return read ?: error; } static ssize_t serio_raw_write(struct file *file, const char __user *buffer, |