tty: hvc: introduce the hv_ops.flush operation for hvc drivers
authorNicholas Piggin <npiggin@gmail.com>
Mon, 30 Apr 2018 14:55:57 +0000 (00:55 +1000)
committerMichael Ellerman <mpe@ellerman.id.au>
Mon, 23 Jul 2018 10:13:20 +0000 (20:13 +1000)
Use .flush to wait for drivers to flush their console outside of
the spinlock, to reduce lock/irq latencies.

Flush the hvc console driver after each write, which can help
messages make it out to the console after a crash.

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

index 6131d50..5414c4a 100644 (file)
@@ -110,6 +110,29 @@ static struct hvc_struct *hvc_get_by_index(int index)
        return hp;
 }
 
+static int __hvc_flush(const struct hv_ops *ops, uint32_t vtermno, bool wait)
+{
+       if (wait)
+               might_sleep();
+
+       if (ops->flush)
+               return ops->flush(vtermno, wait);
+       return 0;
+}
+
+static int hvc_console_flush(const struct hv_ops *ops, uint32_t vtermno)
+{
+       return __hvc_flush(ops, vtermno, false);
+}
+
+/*
+ * Wait for the console to flush before writing more to it. This sleeps.
+ */
+static int hvc_flush(struct hvc_struct *hp)
+{
+       return __hvc_flush(hp->ops, hp->vtermno, true);
+}
+
 /*
  * Initial console vtermnos for console API usage prior to full console
  * initialization.  Any vty adapter outside this range will not have usable
@@ -155,8 +178,12 @@ static void hvc_console_print(struct console *co, const char *b,
                        if (r <= 0) {
                                /* throw away characters on error
                                 * but spin in case of -EAGAIN */
-                               if (r != -EAGAIN)
+                               if (r != -EAGAIN) {
                                        i = 0;
+                               } else {
+                                       hvc_console_flush(cons_ops[index],
+                                                     vtermnos[index]);
+                               }
                        } else if (r > 0) {
                                i -= r;
                                if (i > 0)
@@ -164,6 +191,7 @@ static void hvc_console_print(struct console *co, const char *b,
                        }
                }
        }
+       hvc_console_flush(cons_ops[index], vtermnos[index]);
 }
 
 static struct tty_driver *hvc_console_device(struct console *c, int *index)
@@ -513,8 +541,11 @@ static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count
 
                spin_unlock_irqrestore(&hp->lock, flags);
 
-               if (count)
+               if (count) {
+                       if (hp->n_outbuf > 0)
+                               hvc_flush(hp);
                        cond_resched();
+               }
        }
 
        /*
index ea63090..e931995 100644 (file)
@@ -54,6 +54,7 @@ struct hvc_struct {
 struct hv_ops {
        int (*get_chars)(uint32_t vtermno, char *buf, int count);
        int (*put_chars)(uint32_t vtermno, const char *buf, int count);
+       int (*flush)(uint32_t vtermno, bool wait);
 
        /* Callbacks for notification. Called in open, close and hangup */
        int (*notifier_add)(struct hvc_struct *hp, int irq);