@@ -304,6 +304,7 @@ extern void opal_configure_cores(void);
extern int opal_get_chars(uint32_t vtermno, char *buf, int count);
extern int opal_put_chars(uint32_t vtermno, const char *buf, int total_len);
extern int opal_put_chars_nonatomic(uint32_t vtermno, const char *buf, int total_len);
+extern int opal_flush_chars(uint32_t vtermno, long timeout);
extern int opal_flush_console(uint32_t vtermno);
extern void hvc_opal_init_early(void);
@@ -370,12 +370,8 @@ static int __opal_put_chars(uint32_t vtermno, const char *data, int total_len, b
olen = cpu_to_be64(total_len);
rc = opal_console_write(vtermno, &olen, data);
if (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
- if (rc == OPAL_BUSY_EVENT) {
- mdelay(OPAL_BUSY_DELAY_MS);
+ if (rc == OPAL_BUSY_EVENT)
opal_poll_events(NULL);
- } else if (rc == OPAL_BUSY_EVENT) {
- mdelay(OPAL_BUSY_DELAY_MS);
- }
written = -EAGAIN;
goto out;
}
@@ -401,15 +397,6 @@ static int __opal_put_chars(uint32_t vtermno, const char *data, int total_len, b
if (atomic)
spin_unlock_irqrestore(&opal_write_lock, flags);
- /* In the -EAGAIN case, callers loop, so we have to flush the console
- * here in case they have interrupts off (and we don't want to wait
- * for async flushing if we can make immediate progress here). If
- * necessary the API could be made entirely non-flushing if the
- * callers had a ->flush API to use.
- */
- if (written == -EAGAIN)
- opal_flush_console(vtermno);
-
return written;
}
@@ -427,40 +414,63 @@ int opal_put_chars_nonatomic(uint32_t vtermno, const char *data, int total_len)
return __opal_put_chars(vtermno, data, total_len, false);
}
-int opal_flush_console(uint32_t vtermno)
+static int __opal_flush_console(uint32_t vtermno, long timeout)
{
s64 rc;
+ unsigned long end = jiffies + msecs_to_jiffies(timeout);
if (!opal_check_token(OPAL_CONSOLE_FLUSH)) {
- __be64 evt;
-
WARN_ONCE(1, "opal: OPAL_CONSOLE_FLUSH missing.\n");
/*
* If OPAL_CONSOLE_FLUSH is not implemented in the firmware,
* the console can still be flushed by calling the polling
* function while it has OPAL_EVENT_CONSOLE_OUTPUT events.
*/
- do {
- opal_poll_events(&evt);
- } while (be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_OUTPUT);
+ for (;;) {
+ __be64 evt;
- return OPAL_SUCCESS;
+ opal_poll_events(&evt);
+ if (!(be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_OUTPUT))
+ return OPAL_SUCCESS;
+
+ if (timeout) {
+ if ((timeout != -1) && time_after(jiffies, end))
+ return OPAL_BUSY;
+ schedule_timeout(msecs_to_jiffies(OPAL_BUSY_DELAY_MS));
+ } else {
+ mdelay(1);
+ }
+ }
}
- do {
- rc = OPAL_BUSY;
- while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
- rc = opal_console_flush(vtermno);
- if (rc == OPAL_BUSY_EVENT) {
- mdelay(OPAL_BUSY_DELAY_MS);
- opal_poll_events(NULL);
- } else if (rc == OPAL_BUSY) {
- mdelay(OPAL_BUSY_DELAY_MS);
+ for (;;) {
+ rc = opal_console_flush(vtermno);
+ if (rc == OPAL_BUSY_EVENT || rc == OPAL_BUSY || rc == OPAL_PARTIAL) {
+ if (timeout) {
+ if ((timeout != -1) && time_after(jiffies, end))
+ return OPAL_BUSY;
+ schedule_timeout(msecs_to_jiffies(OPAL_BUSY_DELAY_MS));
+ } else {
+ mdelay(1);
}
+ if (rc == OPAL_BUSY_EVENT)
+ opal_poll_events(NULL);
+
+ } else {
+ return opal_error_code(rc);
}
- } while (rc == OPAL_PARTIAL); /* More to flush */
+ }
+
+}
- return opal_error_code(rc);
+int opal_flush_console(uint32_t vtermno)
+{
+ return __opal_flush_console(vtermno, 0);
+}
+
+int opal_flush_chars(uint32_t vtermno, long timeout)
+{
+ return __opal_flush_console(vtermno, timeout);
}
static int opal_recover_mce(struct pt_regs *regs,
@@ -110,6 +110,23 @@ static struct hvc_struct *hvc_get_by_index(int index)
return hp;
}
+static int hvc_flush_default(uint32_t vtermno, long timeout)
+{
+ if (timeout)
+ msleep(1);
+ else
+ mdelay(1);
+
+ return 0;
+}
+
+static int do_hvc_flush_op(const struct hv_ops *ops, uint32_t vtermno, long timeout)
+{
+ if (ops->flush)
+ return ops->flush(vtermno, timeout);
+ else
+ return hvc_flush_default(vtermno, timeout);
+}
/*
* Initial console vtermnos for console API usage prior to full console
@@ -153,11 +170,14 @@ static void hvc_console_print(struct console *co, const char *b,
}
} else {
r = cons_ops[index]->put_chars(vtermnos[index], c, i);
- if (r <= 0) {
- /* throw away characters on error
- * but spin in case of -EAGAIN */
- if (r != -EAGAIN)
+ if (r == 0 || r == -EAGAIN) {
+ r = do_hvc_flush_op(cons_ops[index],
+ vtermnos[index], 0);
+ if (r)
i = 0;
+ } else if (r < 0) {
+ /* throw away characters on error */
+ i = 0;
} else if (r > 0) {
i -= r;
if (i > 0)
@@ -480,6 +500,15 @@ static int hvc_push(struct hvc_struct *hp)
return n;
}
+/*
+ * Wait for the console to flush before writing more to it. This sleeps.
+ */
+static int hvc_flush(struct hvc_struct *hp)
+{
+ might_sleep();
+ return do_hvc_flush_op(hp->ops, hp->vtermno, -1);
+}
+
static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count)
{
struct hvc_struct *hp = tty->driver_data;
@@ -494,23 +523,37 @@ 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) {
+ cond_resched();
- /* 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);
+ spin_lock_irqsave(&hp->lock, flags);
+
+ rsize = hp->outbuf_size - hp->n_outbuf;
+ if (hp->n_outbuf && rsize < count) {
+ /* Push pending writes */
+ 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;
+
+ hvc_push(hp);
+ }
+
+ spin_unlock_irqrestore(&hp->lock, flags);
+
+ if (count) {
+ /* Flush the console and try again */
+ hvc_flush(hp);
+ }
}
- spin_unlock_irqrestore(&hp->lock, flags);
/*
* Racy, but harmless, kick thread if there is still pending data.
@@ -612,6 +655,12 @@ int hvc_poll(struct hvc_struct *hp)
timeout = (written_total) ? 0 : MIN_TIMEOUT;
}
+ spin_unlock_irqrestore(&hp->lock, flags);
+
+ cond_resched();
+
+ spin_lock_irqsave(&hp->lock, flags);
+
/* No tty attached, just skip */
tty = tty_port_tty_get(&hp->port);
if (tty == NULL)
@@ -810,11 +859,22 @@ static void hvc_poll_put_char(struct tty_driver *driver, int line, char ch)
{
struct tty_struct *tty = driver->ttys[0];
struct hvc_struct *hp = tty->driver_data;
- int n;
- do {
- n = hp->ops->put_chars(hp->vtermno, &ch, 1);
- } while (n <= 0);
+ /*
+ * Can't do much error handling, just drop characters.
+ */
+ for (;;) {
+ int rc;
+
+ rc = hp->ops->put_chars(hp->vtermno, &ch, 1);
+ if (rc == 0 || rc == -EAGAIN) {
+ rc = do_hvc_flush_op(hp->ops, hp->vtermno, 0);
+ if (rc)
+ break;
+ continue;
+ }
+ break;
+ }
}
#endif
@@ -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, long timeout);
/* Callbacks for notification. Called in open, close and hangup */
int (*notifier_add)(struct hvc_struct *hp, int irq);
@@ -52,6 +52,7 @@ static u32 hvc_opal_boot_termno;
static const struct hv_ops hvc_opal_raw_ops = {
.get_chars = opal_get_chars,
.put_chars = opal_put_chars_nonatomic,
+ .flush = opal_flush_chars,
.notifier_add = notifier_add_irq,
.notifier_del = notifier_del_irq,
.notifier_hangup = notifier_hangup_irq,
@@ -141,6 +142,7 @@ static int hvc_opal_hvsi_tiocmset(struct hvc_struct *hp, unsigned int set,
static const struct hv_ops hvc_opal_hvsi_ops = {
.get_chars = hvc_opal_hvsi_get_chars,
.put_chars = hvc_opal_hvsi_put_chars,
+ .flush = opal_flush_chars,
.notifier_add = hvc_opal_hvsi_open,
.notifier_del = hvc_opal_hvsi_close,
.notifier_hangup = hvc_opal_hvsi_hangup,