summaryrefslogtreecommitdiff
path: root/drivers/tty/tty_port.c
diff options
context:
space:
mode:
authorIlpo Järvinen <ilpo.jarvinen@linux.intel.com>2022-06-06 18:36:51 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2022-06-10 13:51:31 +0200
commit6bb6fa6908ebd3cb4e14cd4f0ce272ec885d2eb0 (patch)
tree9d29bb37457ad995881181e024b93bc968a0dd30 /drivers/tty/tty_port.c
parent84f2faa7852e1f55d89bb0c99b3a672b87b11f87 (diff)
tty: Implement lookahead to process XON/XOFF timely
When tty is not read from, XON/XOFF may get stuck into an intermediate buffer. As those characters are there to do software flow-control, it is not very useful. In the case where neither end reads from ttys, the receiving ends might not be able receive the XOFF characters and just keep sending more data to the opposite direction. This problem is almost guaranteed to occur with DMA which sends data in large chunks. If TTY is slow to process characters, that is, eats less than given amount in receive_buf, invoke lookahead for the rest of the chars to process potential XON/XOFF characters. We need to keep track of how many characters have been processed by the lookahead to avoid processing the flow control char again on the normal path. Bookkeeping occurs parallel on two layers (tty_buffer and n_tty) to avoid passing the lookahead_count through the whole call chain. When a flow-control char is processed, two things must occur: a) it must not be treated as normal char b) if not yet processed, flow-control actions need to be taken The return value of n_tty_receive_char_flow_ctrl() tells caller a), and b) is kept internal to n_tty_receive_char_flow_ctrl(). If characters were previous looked ahead, __receive_buf() makes two calls to the appropriate n_tty_receive_buf_* function. First call is made with lookahead_done=true for the characters that were subject to lookahead earlier and then with lookahead=false for the new characters. Either of the calls might be skipped when it has no characters to handle. Reported-by: Gilles Buloz <gilles.buloz@kontron.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Link: https://lore.kernel.org/r/20220606153652.63554-2-ilpo.jarvinen@linux.intel.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/tty_port.c')
-rw-r--r--drivers/tty/tty_port.c21
1 files changed, 21 insertions, 0 deletions
diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c
index 880608a65773..dce08a6d7b5e 100644
--- a/drivers/tty/tty_port.c
+++ b/drivers/tty/tty_port.c
@@ -43,6 +43,26 @@ static int tty_port_default_receive_buf(struct tty_port *port,
return ret;
}
+static void tty_port_default_lookahead_buf(struct tty_port *port, const unsigned char *p,
+ const unsigned char *f, unsigned int count)
+{
+ struct tty_struct *tty;
+ struct tty_ldisc *disc;
+
+ tty = READ_ONCE(port->itty);
+ if (!tty)
+ return;
+
+ disc = tty_ldisc_ref(tty);
+ if (!disc)
+ return;
+
+ if (disc->ops->lookahead_buf)
+ disc->ops->lookahead_buf(disc->tty, p, f, count);
+
+ tty_ldisc_deref(disc);
+}
+
static void tty_port_default_wakeup(struct tty_port *port)
{
struct tty_struct *tty = tty_port_tty_get(port);
@@ -55,6 +75,7 @@ static void tty_port_default_wakeup(struct tty_port *port)
const struct tty_port_client_operations tty_port_default_client_ops = {
.receive_buf = tty_port_default_receive_buf,
+ .lookahead_buf = tty_port_default_lookahead_buf,
.write_wakeup = tty_port_default_wakeup,
};
EXPORT_SYMBOL_GPL(tty_port_default_client_ops);