diff options
| author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2012-04-21 01:33:08 -0400 |
|---|---|---|
| committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2012-04-21 02:11:01 -0400 |
| commit | 486c8aba39e5f194519cd5c0e85e5d1de8b74b03 (patch) | |
| tree | f82d9745cd40b94dfa4b3107e7c33c5224d69c77 /drivers/input | |
| parent | 71f3d070a309504cdfef87b9e98836395b75ae0e (diff) | |
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')
| -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 948fd5a045f..3e243621c0e 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, | |||
| 165 | struct serio_raw *serio_raw = client->serio_raw; | 165 | struct serio_raw *serio_raw = client->serio_raw; |
| 166 | char uninitialized_var(c); | 166 | char uninitialized_var(c); |
| 167 | ssize_t read = 0; | 167 | ssize_t read = 0; |
| 168 | int retval; | 168 | int error = 0; |
| 169 | 169 | ||
| 170 | if (serio_raw->dead) | 170 | do { |
| 171 | return -ENODEV; | 171 | if (serio_raw->dead) |
| 172 | return -ENODEV; | ||
| 172 | 173 | ||
| 173 | if (serio_raw->head == serio_raw->tail && (file->f_flags & O_NONBLOCK)) | 174 | if (serio_raw->head == serio_raw->tail && |
| 174 | return -EAGAIN; | 175 | (file->f_flags & O_NONBLOCK)) |
| 176 | return -EAGAIN; | ||
| 175 | 177 | ||
| 176 | retval = wait_event_interruptible(serio_raw->wait, | 178 | if (count == 0) |
| 177 | serio_raw->head != serio_raw->tail || serio_raw->dead); | 179 | break; |
| 178 | if (retval) | ||
| 179 | return retval; | ||
| 180 | 180 | ||
| 181 | if (serio_raw->dead) | 181 | while (read < count && serio_raw_fetch_byte(serio_raw, &c)) { |
| 182 | return -ENODEV; | 182 | if (put_user(c, buffer++)) { |
| 183 | error = -EFAULT; | ||
| 184 | goto out; | ||
| 185 | } | ||
| 186 | read++; | ||
| 187 | } | ||
| 183 | 188 | ||
| 184 | while (read < count && serio_raw_fetch_byte(serio_raw, &c)) { | 189 | if (read) |
| 185 | if (put_user(c, buffer++)) { | ||
| 186 | retval = -EFAULT; | ||
| 187 | break; | 190 | break; |
| 188 | } | ||
| 189 | read++; | ||
| 190 | } | ||
| 191 | 191 | ||
| 192 | return read ?: retval; | 192 | if (!(file->f_flags & O_NONBLOCK)) |
| 193 | error = wait_event_interruptible(serio_raw->wait, | ||
| 194 | serio_raw->head != serio_raw->tail || | ||
| 195 | serio_raw->dead); | ||
| 196 | } while (!error); | ||
| 197 | |||
| 198 | out: | ||
| 199 | return read ?: error; | ||
| 193 | } | 200 | } |
| 194 | 201 | ||
| 195 | static ssize_t serio_raw_write(struct file *file, const char __user *buffer, | 202 | static ssize_t serio_raw_write(struct file *file, const char __user *buffer, |
