Input: serio_raw - ensure we don't block in non-blocking read
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Sat, 21 Apr 2012 05:33:08 +0000 (22:33 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Sat, 21 Apr 2012 06:11:01 +0000 (23:11 -0700)
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>
drivers/input/serio/serio_raw.c

index 948fd5a045f75266ebdbc06648a3dd93d8a6b2ec..3e243621c0e30501eddd29916b289a368911a4d2 100644 (file)
@@ -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,