From nobody Mon May 11 11:28:40 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1BB26C433F5 for ; Tue, 26 Apr 2022 14:50:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1351727AbiDZOxF (ORCPT ); Tue, 26 Apr 2022 10:53:05 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36654 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231352AbiDZOw5 (ORCPT ); Tue, 26 Apr 2022 10:52:57 -0400 Received: from mga05.intel.com (mga05.intel.com [192.55.52.43]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2E31440A38; Tue, 26 Apr 2022 07:49:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1650984590; x=1682520590; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=1pKNQ/jYfZZYbMBsWQ6uNeoADUxZXdWMQXBuv7J6lhE=; b=L6fwYOW4P+j9qiy9J0m6JV9EVRiTFDnoIp91ZmZYAMu5Wbg5NBfEhxBN xCgGeJ5qlQCRs/KTr0Lbl8jTwkQyicJ6XIb4+NAvCTHNvMQOCOc62CUjM 6ytF3f+uoeeRSOK2WJcsAYNRMFd50SGjJkMHRYiPhS3gr5+QdCgw8jILT xJBKLs/Ks/FyvwNSCPoLU+AiEhkBmNdxKVeHYHgguU0u6enqxCqUwpb/N 3BxyrHUU44BqR6j01892Tw+2nv+KWZNdqqLPBvbQp324KK9ElAFBTnTWI 2zaG7Qfjs9T4kj4QkN33aZVl9W6aPt9uJfEgnHYSaD6pv1VC0t+B7PPd7 Q==; X-IronPort-AV: E=McAfee;i="6400,9594,10328"; a="352040413" X-IronPort-AV: E=Sophos;i="5.90,291,1643702400"; d="scan'208";a="352040413" Received: from orsmga008.jf.intel.com ([10.7.209.65]) by fmsmga105.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 26 Apr 2022 07:49:49 -0700 X-IronPort-AV: E=Sophos;i="5.90,291,1643702400"; d="scan'208";a="579932957" Received: from mmilkovx-mobl.amr.corp.intel.com (HELO ijarvine-MOBL2.ger.corp.intel.com) ([10.249.47.245]) by orsmga008-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 26 Apr 2022 07:49:47 -0700 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= To: linux-serial@vger.kernel.org, Greg KH , Jiri Slaby , Andy Shevchenko Cc: linux-kernel@vger.kernel.org, Gilles Buloz , Johan Hovold , =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Subject: [PATCH v4 1/3] tty: Rework receive flow control char logic Date: Tue, 26 Apr 2022 17:49:33 +0300 Message-Id: <20220426144935.54893-2-ilpo.jarvinen@linux.intel.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20220426144935.54893-1-ilpo.jarvinen@linux.intel.com> References: <20220426144935.54893-1-ilpo.jarvinen@linux.intel.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add a helper to check if the character is a flow control one. This rework prepares for adding lookahead done check cleanly to n_tty_receive_char_flow_ctrl() between n_tty_is_char_flow_ctrl() and the actions taken on the flow control characters. No functional changes intended. Signed-off-by: Ilpo J=C3=A4rvinen --- drivers/tty/n_tty.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 88af1cdd2fd1..640c9e871044 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1220,20 +1220,26 @@ n_tty_receive_signal_char(struct tty_struct *tty, i= nt signal, unsigned char c) process_echoes(tty); } =20 +static bool n_tty_is_char_flow_ctrl(struct tty_struct *tty, unsigned char = c) +{ + return c =3D=3D START_CHAR(tty) || c =3D=3D STOP_CHAR(tty); +} + /* Returns true if c is consumed as flow-control character */ static bool n_tty_receive_char_flow_ctrl(struct tty_struct *tty, unsigned = char c) { + if (!n_tty_is_char_flow_ctrl(tty, c)) + return false; + if (c =3D=3D START_CHAR(tty)) { start_tty(tty); process_echoes(tty); return true; } - if (c =3D=3D STOP_CHAR(tty)) { - stop_tty(tty); - return true; - } =20 - return false; + /* STOP_CHAR */ + stop_tty(tty); + return true; } =20 static void n_tty_receive_char_special(struct tty_struct *tty, unsigned ch= ar c) --=20 2.30.2 From nobody Mon May 11 11:28:40 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 47162C433F5 for ; Tue, 26 Apr 2022 14:50:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1351733AbiDZOxL (ORCPT ); Tue, 26 Apr 2022 10:53:11 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36986 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1351715AbiDZOxC (ORCPT ); Tue, 26 Apr 2022 10:53:02 -0400 Received: from mga05.intel.com (mga05.intel.com [192.55.52.43]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E5EC540E41; Tue, 26 Apr 2022 07:49:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1650984593; x=1682520593; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=zQ2ULawNW1Pz8OmvaxG7+96rSNZ30t1pZ+yOPcDwHkE=; b=dsSlsU/DcpXXVnWIog1CqcPCSIZGn/KjGxP0vNoy/BlaXmZ45jkHp4j1 JbI28+oEB76nrMHcpb1RlV7e7Atd0b9HqKFotvd+Z61gBGc44cfdHdUaa 7zhQFGdcEpGtuspedFzsmBWlfVP3jf2wmNw+8zjeWORXeEW4OAG+7MXkY H7OejTeLoLb2NTRvr8UK1OFQY1PDnfj2yVFzSH3OVMR+Uu3H7PkammrNn FdV9Enz2WDZGlCHjrrUGTYezyrbt48Zym5ERpcvO7sd2zbxqw0kFYlLOF bdYwdc8dJYarij7qA02KgDspBJ/VIR9V/MyhMB37Zip92Uv2f+eYFeWOJ Q==; X-IronPort-AV: E=McAfee;i="6400,9594,10328"; a="352040426" X-IronPort-AV: E=Sophos;i="5.90,291,1643702400"; d="scan'208";a="352040426" Received: from orsmga008.jf.intel.com ([10.7.209.65]) by fmsmga105.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 26 Apr 2022 07:49:53 -0700 X-IronPort-AV: E=Sophos;i="5.90,291,1643702400"; d="scan'208";a="579932990" Received: from mmilkovx-mobl.amr.corp.intel.com (HELO ijarvine-MOBL2.ger.corp.intel.com) ([10.249.47.245]) by orsmga008-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 26 Apr 2022 07:49:50 -0700 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= To: linux-serial@vger.kernel.org, Greg KH , Jiri Slaby , Andy Shevchenko Cc: linux-kernel@vger.kernel.org, Gilles Buloz , Johan Hovold , =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Subject: [PATCH v4 2/3] tty: Implement lookahead to process XON/XOFF timely Date: Tue, 26 Apr 2022 17:49:34 +0300 Message-Id: <20220426144935.54893-3-ilpo.jarvinen@linux.intel.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20220426144935.54893-1-ilpo.jarvinen@linux.intel.com> References: <20220426144935.54893-1-ilpo.jarvinen@linux.intel.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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 callchain. 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(). Reported-by: Gilles Buloz Signed-off-by: Ilpo J=C3=A4rvinen --- drivers/tty/n_tty.c | 90 +++++++++++++++++++++++++++++++------- drivers/tty/tty_buffer.c | 59 +++++++++++++++++++++---- drivers/tty/tty_port.c | 21 +++++++++ include/linux/tty_buffer.h | 1 + include/linux/tty_ldisc.h | 13 ++++++ include/linux/tty_port.h | 2 + 6 files changed, 161 insertions(+), 25 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 640c9e871044..917b5970b2e0 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -118,6 +118,9 @@ struct n_tty_data { size_t read_tail; size_t line_start; =20 + /* # of chars looked ahead (to find software flow control chars) */ + size_t lookahead_count; + /* protected by output lock */ unsigned int column; unsigned int canon_column; @@ -333,6 +336,8 @@ static void reset_buffer_flags(struct n_tty_data *ldata) ldata->erasing =3D 0; bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); ldata->push =3D 0; + + ldata->lookahead_count =3D 0; } =20 static void n_tty_packet_mode_flush(struct tty_struct *tty) @@ -1225,12 +1230,30 @@ static bool n_tty_is_char_flow_ctrl(struct tty_stru= ct *tty, unsigned char c) return c =3D=3D START_CHAR(tty) || c =3D=3D STOP_CHAR(tty); } =20 -/* Returns true if c is consumed as flow-control character */ -static bool n_tty_receive_char_flow_ctrl(struct tty_struct *tty, unsigned = char c) +/** + * n_tty_receive_char_flow_ctrl - receive flow control chars + * @tty: terminal device + * @c: character + * @lookahead_done: lookahead has processed this character already + * + * Receive and process flow control character actions. + * + * In case lookahead for flow control chars already handled the character = in + * advance to the normal receive, the actions are skipped during normal + * receive. + * + * Returns true if c is consumed as flow-control character, the character + * must not be treated as normal character. + */ +static bool n_tty_receive_char_flow_ctrl(struct tty_struct *tty, unsigned = char c, + bool lookahead_done) { if (!n_tty_is_char_flow_ctrl(tty, c)) return false; =20 + if (lookahead_done) + return true; + if (c =3D=3D START_CHAR(tty)) { start_tty(tty); process_echoes(tty); @@ -1242,11 +1265,12 @@ static bool n_tty_receive_char_flow_ctrl(struct tty= _struct *tty, unsigned char c return true; } =20 -static void n_tty_receive_char_special(struct tty_struct *tty, unsigned ch= ar c) +static void n_tty_receive_char_special(struct tty_struct *tty, unsigned ch= ar c, + bool lookahead_done) { struct n_tty_data *ldata =3D tty->disc_data; =20 - if (I_IXON(tty) && n_tty_receive_char_flow_ctrl(tty, c)) + if (I_IXON(tty) && n_tty_receive_char_flow_ctrl(tty, c, lookahead_done)) return; =20 if (L_ISIG(tty)) { @@ -1401,7 +1425,8 @@ static void n_tty_receive_char(struct tty_struct *tty= , unsigned char c) put_tty_queue(c, ldata); } =20 -static void n_tty_receive_char_closing(struct tty_struct *tty, unsigned ch= ar c) +static void n_tty_receive_char_closing(struct tty_struct *tty, unsigned ch= ar c, + bool lookahead_done) { if (I_ISTRIP(tty)) c &=3D 0x7f; @@ -1409,9 +1434,12 @@ static void n_tty_receive_char_closing(struct tty_st= ruct *tty, unsigned char c) c =3D tolower(c); =20 if (I_IXON(tty)) { - if (c =3D=3D STOP_CHAR(tty)) - stop_tty(tty); - else if (c =3D=3D START_CHAR(tty) || + if (c =3D=3D STOP_CHAR(tty)) { + if (!lookahead_done) + stop_tty(tty); + } else if (c =3D=3D START_CHAR(tty) && lookahead_done) { + return; + } else if (c =3D=3D START_CHAR(tty) || (tty->flow.stopped && !tty->flow.tco_stopped && I_IXANY(tty) && c !=3D INTR_CHAR(tty) && c !=3D QUIT_CHAR(tty) && c !=3D SUSP_CHAR(tty))) { @@ -1457,6 +1485,26 @@ n_tty_receive_char_lnext(struct tty_struct *tty, uns= igned char c, char flag) n_tty_receive_char_flagged(tty, c, flag); } =20 +static void n_tty_lookahead_flow_ctrl(struct tty_struct *tty, const unsign= ed char *cp, + const unsigned char *fp, unsigned int count) +{ + struct n_tty_data *ldata =3D tty->disc_data; + unsigned char flag =3D TTY_NORMAL; + + ldata->lookahead_count +=3D count; + + if (!I_IXON(tty)) + return; + + while (count--) { + if (fp) + flag =3D *fp++; + if (likely(flag =3D=3D TTY_NORMAL)) + n_tty_receive_char_flow_ctrl(tty, *cp, false); + cp++; + } +} + static void n_tty_receive_buf_real_raw(struct tty_struct *tty, const unsigned char *cp, const char *fp, int count) @@ -1496,7 +1544,7 @@ n_tty_receive_buf_raw(struct tty_struct *tty, const u= nsigned char *cp, =20 static void n_tty_receive_buf_closing(struct tty_struct *tty, const unsigned char *cp, - const char *fp, int count) + const char *fp, int count, bool lookahead_done) { char flag =3D TTY_NORMAL; =20 @@ -1504,12 +1552,12 @@ n_tty_receive_buf_closing(struct tty_struct *tty, c= onst unsigned char *cp, if (fp) flag =3D *fp++; if (likely(flag =3D=3D TTY_NORMAL)) - n_tty_receive_char_closing(tty, *cp++); + n_tty_receive_char_closing(tty, *cp++, lookahead_done); } } =20 static void n_tty_receive_buf_standard(struct tty_struct *tty, - const unsigned char *cp, const char *fp, int count) + const unsigned char *cp, const char *fp, int count, bool lookahead_done) { struct n_tty_data *ldata =3D tty->disc_data; char flag =3D TTY_NORMAL; @@ -1540,7 +1588,7 @@ static void n_tty_receive_buf_standard(struct tty_str= uct *tty, } =20 if (test_bit(c, ldata->char_map)) - n_tty_receive_char_special(tty, c); + n_tty_receive_char_special(tty, c, lookahead_done); else n_tty_receive_char(tty, c); } @@ -1551,21 +1599,30 @@ static void __receive_buf(struct tty_struct *tty, c= onst unsigned char *cp, { struct n_tty_data *ldata =3D tty->disc_data; bool preops =3D I_ISTRIP(tty) || (I_IUCLC(tty) && L_IEXTEN(tty)); + size_t la_count =3D min_t(size_t, ldata->lookahead_count, count); =20 if (ldata->real_raw) n_tty_receive_buf_real_raw(tty, cp, fp, count); else if (ldata->raw || (L_EXTPROC(tty) && !preops)) n_tty_receive_buf_raw(tty, cp, fp, count); - else if (tty->closing && !L_EXTPROC(tty)) - n_tty_receive_buf_closing(tty, cp, fp, count); - else { - n_tty_receive_buf_standard(tty, cp, fp, count); + else if (tty->closing && !L_EXTPROC(tty)) { + if (la_count > 0) + n_tty_receive_buf_closing(tty, cp, fp, la_count, true); + if (count > la_count) + n_tty_receive_buf_closing(tty, cp, fp, count - la_count, false); + } else { + if (la_count > 0) + n_tty_receive_buf_standard(tty, cp, fp, la_count, true); + if (count > la_count) + n_tty_receive_buf_standard(tty, cp, fp, count - la_count, false); =20 flush_echoes(tty); if (tty->ops->flush_chars) tty->ops->flush_chars(tty); } =20 + ldata->lookahead_count -=3D la_count; + if (ldata->icanon && !L_EXTPROC(tty)) return; =20 @@ -2446,6 +2503,7 @@ static struct tty_ldisc_ops n_tty_ops =3D { .receive_buf =3D n_tty_receive_buf, .write_wakeup =3D n_tty_write_wakeup, .receive_buf2 =3D n_tty_receive_buf2, + .lookahead_buf =3D n_tty_lookahead_flow_ctrl, }; =20 /** diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 646510476c30..e237f2a022dc 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -5,6 +5,7 @@ =20 #include #include +#include #include #include #include @@ -104,6 +105,7 @@ static void tty_buffer_reset(struct tty_buffer *p, size= _t size) p->size =3D size; p->next =3D NULL; p->commit =3D 0; + p->lookahead =3D 0; p->read =3D 0; p->flags =3D 0; } @@ -233,6 +235,7 @@ void tty_buffer_flush(struct tty_struct *tty, struct tt= y_ldisc *ld) buf->head =3D next; } buf->head->read =3D buf->head->commit; + buf->head->lookahead =3D buf->head->read; =20 if (ld && ld->ops->flush_buffer) ld->ops->flush_buffer(tty); @@ -275,13 +278,15 @@ static int __tty_buffer_request_room(struct tty_port = *port, size_t size, if (n !=3D NULL) { n->flags =3D flags; buf->tail =3D n; - /* paired w/ acquire in flush_to_ldisc(); ensures - * flush_to_ldisc() sees buffer data. + /* + * Paired w/ acquire in flush_to_ldisc() and lookahead_bufs() + * ensures they see all buffer data. */ smp_store_release(&b->commit, b->used); - /* paired w/ acquire in flush_to_ldisc(); ensures the - * latest commit value can be read before the head is - * advanced to the next buffer + /* + * Paired w/ acquire in flush_to_ldisc() and lookahead_bufs() + * ensures the latest commit value can be read before the head + * is advanced to the next buffer. */ smp_store_release(&b->next, n); } else if (change) @@ -458,6 +463,40 @@ int tty_ldisc_receive_buf(struct tty_ldisc *ld, const = unsigned char *p, } EXPORT_SYMBOL_GPL(tty_ldisc_receive_buf); =20 +static void lookahead_bufs(struct tty_port *port, struct tty_buffer *head) +{ + head->lookahead =3D max(head->lookahead, head->read); + + while (head) { + struct tty_buffer *next; + unsigned char *p, *f =3D NULL; + unsigned int count; + + /* + * Paired w/ release in __tty_buffer_request_room(); + * ensures commit value read is not stale if the head + * is advancing to the next buffer. + */ + next =3D smp_load_acquire(&head->next); + /* + * Paired w/ release in __tty_buffer_request_room() or in + * tty_buffer_flush(); ensures we see the committed buffer data. + */ + count =3D smp_load_acquire(&head->commit) - head->lookahead; + if (!count) { + head =3D next; + continue; + } + + p =3D char_buf_ptr(head, head->lookahead); + if (~head->flags & TTYB_NORMAL) + f =3D flag_buf_ptr(head, head->lookahead); + + port->client_ops->lookahead_buf(port, p, f, count); + head->lookahead +=3D count; + } +} + static int receive_buf(struct tty_port *port, struct tty_buffer *head, int count) { @@ -495,7 +534,7 @@ static void flush_to_ldisc(struct work_struct *work) while (1) { struct tty_buffer *head =3D buf->head; struct tty_buffer *next; - int count; + int count, rcvd; =20 /* Ldisc or user is trying to gain exclusive access */ if (atomic_read(&buf->priority)) @@ -518,10 +557,12 @@ static void flush_to_ldisc(struct work_struct *work) continue; } =20 - count =3D receive_buf(port, head, count); - if (!count) + rcvd =3D receive_buf(port, head, count); + head->read +=3D rcvd; + if (rcvd < count) + lookahead_bufs(port, head); + if (!rcvd) break; - head->read +=3D count; =20 if (need_resched()) cond_resched(); 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; } =20 +static void tty_port_default_lookahead_buf(struct tty_port *port, const un= signed char *p, + const unsigned char *f, unsigned int count) +{ + struct tty_struct *tty; + struct tty_ldisc *disc; + + tty =3D READ_ONCE(port->itty); + if (!tty) + return; + + disc =3D 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 =3D tty_port_tty_get(port); @@ -55,6 +75,7 @@ static void tty_port_default_wakeup(struct tty_port *port) =20 const struct tty_port_client_operations tty_port_default_client_ops =3D { .receive_buf =3D tty_port_default_receive_buf, + .lookahead_buf =3D tty_port_default_lookahead_buf, .write_wakeup =3D tty_port_default_wakeup, }; EXPORT_SYMBOL_GPL(tty_port_default_client_ops); diff --git a/include/linux/tty_buffer.h b/include/linux/tty_buffer.h index 3b9d77604291..1796648c2907 100644 --- a/include/linux/tty_buffer.h +++ b/include/linux/tty_buffer.h @@ -15,6 +15,7 @@ struct tty_buffer { int used; int size; int commit; + int lookahead; /* Lazy update on recv, can become less than "read" */ int read; int flags; /* Data points here */ diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index e85002b56752..bf8143fbcff3 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -186,6 +186,17 @@ int ldsem_down_write_nested(struct ld_semaphore *sem, = int subclass, * indicate all data received is %TTY_NORMAL. If assigned, prefer this * function for automatic flow control. * + * @lookahead_buf: [DRV] ``void ()(struct tty_struct *tty, + * const unsigned char *cp, const char *fp, int count) + * + * This function is called by the low-level tty driver for characters + * not eaten by receive_buf or receive_buf2. It is useful for processing + * high-priority characters such as software flow-control characters that + * could otherwise get stuck into the intermediate buffer until tty has + * room to receive them. Ldisc must be able to handle later a receive_buf + * or receive_buf2 call for the same characters (e.g. by skipping the + * actions for high-priority characters already handled by lookahead_buf). + * * @owner: module containting this ldisc (for reference counting) * * This structure defines the interface between the tty line discipline @@ -229,6 +240,8 @@ struct tty_ldisc_ops { void (*dcd_change)(struct tty_struct *tty, unsigned int status); int (*receive_buf2)(struct tty_struct *tty, const unsigned char *cp, const char *fp, int count); + void (*lookahead_buf)(struct tty_struct *tty, const unsigned char *cp, + const unsigned char *fp, unsigned int count); =20 struct module *owner; }; diff --git a/include/linux/tty_port.h b/include/linux/tty_port.h index 58e9619116b7..fa3c3bdaa234 100644 --- a/include/linux/tty_port.h +++ b/include/linux/tty_port.h @@ -40,6 +40,8 @@ struct tty_port_operations { =20 struct tty_port_client_operations { int (*receive_buf)(struct tty_port *port, const unsigned char *, const un= signed char *, size_t); + void (*lookahead_buf)(struct tty_port *port, const unsigned char *cp, + const unsigned char *fp, unsigned int count); void (*write_wakeup)(struct tty_port *port); }; =20 --=20 2.30.2 From nobody Mon May 11 11:28:40 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8ABEFC433EF for ; Tue, 26 Apr 2022 14:50:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1351744AbiDZOxP (ORCPT ); Tue, 26 Apr 2022 10:53:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37134 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1351723AbiDZOxE (ORCPT ); Tue, 26 Apr 2022 10:53:04 -0400 Received: from mga05.intel.com (mga05.intel.com [192.55.52.43]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 487E840E41; Tue, 26 Apr 2022 07:49:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1650984597; x=1682520597; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=+cv5qN1igNw1Zx5NSV2OW8gEhkMunXxsP88oD3ZR3ck=; b=kR88/utOkfTzfasYLxAKmUEKYAAwWSAPJeC6y4IB4W1H6vViE+y1R5Gm vV/OiWUxowq1glBYxXKMMhpCNs6XttxzfESQdC6D84QCEyp0JpCmUMgA3 tkiASAjHZx3B7QmWXJvS8dGQRrQH9lbU/8sYrPPYNTLyQUVTNrmfBw3g9 I5dySo2kSo6FXl2gml9gcMhMtyI4E0iFVJKwUrX9UKkWB7imRBFjbERNo JPins1ZmLjFjv5GNazNKUgIEjwjr5F5cmrflYshAJpQpHrb9U92CguS84 L/T2Um7iZ+1jMBQroMwwYC+kV+LFeWdyoycYclGAwy6O9ceXMlBHvrvaS g==; X-IronPort-AV: E=McAfee;i="6400,9594,10328"; a="352040437" X-IronPort-AV: E=Sophos;i="5.90,291,1643702400"; d="scan'208";a="352040437" Received: from orsmga008.jf.intel.com ([10.7.209.65]) by fmsmga105.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 26 Apr 2022 07:49:57 -0700 X-IronPort-AV: E=Sophos;i="5.90,291,1643702400"; d="scan'208";a="579933023" Received: from mmilkovx-mobl.amr.corp.intel.com (HELO ijarvine-MOBL2.ger.corp.intel.com) ([10.249.47.245]) by orsmga008-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 26 Apr 2022 07:49:54 -0700 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= To: linux-serial@vger.kernel.org, Greg KH , Jiri Slaby , Andy Shevchenko Cc: linux-kernel@vger.kernel.org, Gilles Buloz , Johan Hovold , =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Subject: [PATCH v4 3/3] tty: Use flow-control char function on closing path Date: Tue, 26 Apr 2022 17:49:35 +0300 Message-Id: <20220426144935.54893-4-ilpo.jarvinen@linux.intel.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20220426144935.54893-1-ilpo.jarvinen@linux.intel.com> References: <20220426144935.54893-1-ilpo.jarvinen@linux.intel.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Use n_tty_receive_char_flow_ctrl also on the closing path. This makes the code cleaner and consistent. However, there a small change of regression! The earlier closing path has a small difference compared with the normal receive path. If START_CHAR and STOP_CHAR are equal, their precedence is different depending on which path a character is processed. I don't know whether this difference was intentional or not, and if equal START_CHAR and STOP_CHAR is actually used anywhere. But it feels not so useful corner case. While this change would logically belong to those earlier changes, having a separate patch for this is useful. If this regresses, bisect can pinpoint this change rather than the large patch. Also, this change is not necessary to minimal fix for the issue addressed in the previous patch. Reviewed-by: Andy Shevchenko Signed-off-by: Ilpo J=C3=A4rvinen --- drivers/tty/n_tty.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 917b5970b2e0..ea4dc316eafb 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1434,15 +1434,10 @@ static void n_tty_receive_char_closing(struct tty_s= truct *tty, unsigned char c, c =3D tolower(c); =20 if (I_IXON(tty)) { - if (c =3D=3D STOP_CHAR(tty)) { - if (!lookahead_done) - stop_tty(tty); - } else if (c =3D=3D START_CHAR(tty) && lookahead_done) { - return; - } else if (c =3D=3D START_CHAR(tty) || - (tty->flow.stopped && !tty->flow.tco_stopped && I_IXANY(tty) && - c !=3D INTR_CHAR(tty) && c !=3D QUIT_CHAR(tty) && - c !=3D SUSP_CHAR(tty))) { + if (!n_tty_receive_char_flow_ctrl(tty, c, lookahead_done) && + tty->flow.stopped && !tty->flow.tco_stopped && I_IXANY(tty) && + c !=3D INTR_CHAR(tty) && c !=3D QUIT_CHAR(tty) && + c !=3D SUSP_CHAR(tty)) { start_tty(tty); process_echoes(tty); } --=20 2.30.2