tty: hvc: hvc_write() may sleep
authorNicholas Piggin <npiggin@gmail.com>
Mon, 30 Apr 2018 14:55:56 +0000 (00:55 +1000)
committerMichael Ellerman <mpe@ellerman.id.au>
Mon, 23 Jul 2018 10:12:32 +0000 (20:12 +1000)
Rework the hvc_write loop to drop and re-take the spinlock on each
iteration, add a cond_resched. Don't bother with an initial hvc_push
initially, which makes the logic simpler -- just do a hvc_push on
each time around the loop.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
drivers/tty/hvc/hvc_console.c

index 2abfc0b..6131d50 100644 (file)
@@ -493,23 +493,29 @@ static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count
        if (hp->port.count <= 0)
                return -EIO;
 
-       spin_lock_irqsave(&hp->lock, flags);
+       while (count > 0) {
+               spin_lock_irqsave(&hp->lock, flags);
 
-       /* Push pending writes */
-       if (hp->n_outbuf > 0)
-               hvc_push(hp);
-
-       while (count > 0 && (rsize = hp->outbuf_size - hp->n_outbuf) > 0) {
-               if (rsize > count)
-                       rsize = count;
-               memcpy(hp->outbuf + hp->n_outbuf, buf, rsize);
-               count -= rsize;
-               buf += rsize;
-               hp->n_outbuf += rsize;
-               written += rsize;
-               hvc_push(hp);
+               rsize = hp->outbuf_size - hp->n_outbuf;
+
+               if (rsize) {
+                       if (rsize > count)
+                               rsize = count;
+                       memcpy(hp->outbuf + hp->n_outbuf, buf, rsize);
+                       count -= rsize;
+                       buf += rsize;
+                       hp->n_outbuf += rsize;
+                       written += rsize;
+               }
+
+               if (hp->n_outbuf > 0)
+                       hvc_push(hp);
+
+               spin_unlock_irqrestore(&hp->lock, flags);
+
+               if (count)
+                       cond_resched();
        }
-       spin_unlock_irqrestore(&hp->lock, flags);
 
        /*
         * Racy, but harmless, kick thread if there is still pending data.