linux/drivers/tty/n_tty.c

2551 lines
64 KiB
C
Raw Normal View History

tty: add SPDX identifiers to all remaining files in drivers/tty/ It's good to have SPDX identifiers in all files to make it easier to audit the kernel tree for correct licenses. Update the drivers/tty files files with the correct SPDX license identifier based on the license text in the file itself. The SPDX identifier is a legally binding shorthand, which can be used instead of the full boiler plate text. This work is based on a script and data from Thomas Gleixner, Philippe Ombredanne, and Kate Stewart. Cc: Jiri Slaby <jslaby@suse.com> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Paul Mackerras <paulus@samba.org> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Chris Metcalf <cmetcalf@mellanox.com> Cc: Jiri Kosina <jikos@kernel.org> Cc: David Sterba <dsterba@suse.com> Cc: James Hogan <jhogan@kernel.org> Cc: Rob Herring <robh@kernel.org> Cc: Eric Anholt <eric@anholt.net> Cc: Stefan Wahren <stefan.wahren@i2se.com> Cc: Florian Fainelli <f.fainelli@gmail.com> Cc: Ray Jui <rjui@broadcom.com> Cc: Scott Branden <sbranden@broadcom.com> Cc: bcm-kernel-feedback-list@broadcom.com Cc: "James E.J. Bottomley" <jejb@parisc-linux.org> Cc: Helge Deller <deller@gmx.de> Cc: Joachim Eastwood <manabian@gmail.com> Cc: Matthias Brugger <matthias.bgg@gmail.com> Cc: Masahiro Yamada <yamada.masahiro@socionext.com> Cc: Tobias Klauser <tklauser@distanz.ch> Cc: Russell King <linux@armlinux.org.uk> Cc: Vineet Gupta <vgupta@synopsys.com> Cc: Richard Genoud <richard.genoud@gmail.com> Cc: Alexander Shiyan <shc_work@mail.ru> Cc: Baruch Siach <baruch@tkos.co.il> Cc: "Maciej W. Rozycki" <macro@linux-mips.org> Cc: "Uwe Kleine-König" <kernel@pengutronix.de> Cc: Pat Gefre <pfg@sgi.com> Cc: "Guilherme G. Piccoli" <gpiccoli@linux.vnet.ibm.com> Cc: Jason Wessel <jason.wessel@windriver.com> Cc: Vladimir Zapolskiy <vz@mleia.com> Cc: Sylvain Lemieux <slemieux.tyco@gmail.com> Cc: Carlo Caione <carlo@caione.org> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Liviu Dudau <liviu.dudau@arm.com> Cc: Sudeep Holla <sudeep.holla@arm.com> Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Cc: Andy Gross <andy.gross@linaro.org> Cc: David Brown <david.brown@linaro.org> Cc: "Andreas Färber" <afaerber@suse.de> Cc: Kevin Cernekee <cernekee@gmail.com> Cc: Laxman Dewangan <ldewangan@nvidia.com> Cc: Thierry Reding <thierry.reding@gmail.com> Cc: Jonathan Hunter <jonathanh@nvidia.com> Cc: Barry Song <baohua@kernel.org> Cc: Patrice Chotard <patrice.chotard@st.com> Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com> Cc: Alexandre Torgue <alexandre.torgue@st.com> Cc: "David S. Miller" <davem@davemloft.net> Cc: Peter Korsgaard <jacmet@sunsite.dk> Cc: Timur Tabi <timur@tabi.org> Cc: Tony Prisk <linux@prisktech.co.nz> Cc: Michal Simek <michal.simek@xilinx.com> Cc: "Sören Brinkmann" <soren.brinkmann@xilinx.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Kate Stewart <kstewart@linuxfoundation.org> Cc: Philippe Ombredanne <pombredanne@nexb.com> Cc: Jiri Slaby <jslaby@suse.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-11-07 01:11:51 +08:00
// SPDX-License-Identifier: GPL-1.0+
/*
* n_tty.c --- implements the N_TTY line discipline.
*
* This code used to be in tty_io.c, but things are getting hairy
* enough that it made sense to split things off. (The N_TTY
* processing has changed so much that it's hardly recognizable,
* anyway...)
*
* Note that the open routine for N_TTY is guaranteed never to return
* an error. This is because Linux will fall back to setting a line
* to N_TTY if it can not switch to any other line discipline.
*
* Written by Theodore Ts'o, Copyright 1994.
*
* This file also contains code originally written by Linus Torvalds,
* Copyright 1991, 1992, 1993, and by Julian Cowley, Copyright 1994.
*
* Reduced memory usage for older ARM systems - Russell King.
*
* 2000/01/20 Fixed SMP locking on put_tty_queue using bits of
* the patch by Andrew J. Kroll <ag784@freenet.buffalo.edu>
* who actually finally proved there really was a race.
*
* 2002/03/18 Implemented n_tty_wakeup to send SIGIO POLL_OUTs to
* waiting writing processes-Sapan Bhatia <sapan@corewars.org>.
* Also fixed a bug in BLOCKING mode where n_tty_write returns
* EAGAIN
*/
#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/ctype.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/fcntl.h>
#include <linux/file.h>
#include <linux/jiffies.h>
#include <linux/math.h>
#include <linux/poll.h>
#include <linux/ratelimit.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/tty.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include "tty.h"
/*
* Until this number of characters is queued in the xmit buffer, select will
* return "we have room for writes".
*/
#define WAKEUP_CHARS 256
/*
* This defines the low- and high-watermarks for throttling and
* unthrottling the TTY driver. These watermarks are used for
* controlling the space in the read buffer.
*/
#define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */
#define TTY_THRESHOLD_UNTHROTTLE 128
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
/*
* Special byte codes used in the echo buffer to represent operations
* or special handling of characters. Bytes in the echo buffer that
* are not part of such special blocks are treated as normal character
* codes.
*/
#define ECHO_OP_START 0xff
#define ECHO_OP_MOVE_BACK_COL 0x80
#define ECHO_OP_SET_CANON_COL 0x81
#define ECHO_OP_ERASE_TAB 0x82
#define ECHO_COMMIT_WATERMARK 256
#define ECHO_BLOCK 256
#define ECHO_DISCARD_WATERMARK N_TTY_BUF_SIZE - (ECHO_BLOCK + 32)
#undef N_TTY_TRACE
#ifdef N_TTY_TRACE
# define n_tty_trace(f, args...) trace_printk(f, ##args)
#else
# define n_tty_trace(f, args...) no_printk(f, ##args)
#endif
struct n_tty_data {
/* producer-published */
size_t read_head;
size_t commit_head;
size_t canon_head;
size_t echo_head;
size_t echo_commit;
size_t echo_mark;
DECLARE_BITMAP(char_map, 256);
/* private to n_tty_receive_overrun (single-threaded) */
unsigned long overrun_time;
unsigned int num_overrun;
/* non-atomic */
bool no_room;
/* must hold exclusive termios_rwsem to reset these */
unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
n_tty: Fix buffer overruns with larger-than-4k pastes readline() inadvertently triggers an error recovery path when pastes larger than 4k overrun the line discipline buffer. The error recovery path discards input when the line discipline buffer is full and operating in canonical mode and no newline has been received. Because readline() changes the termios to non-canonical mode to read the line char-by-char, the line discipline buffer can become full, and then when readline() restores termios back to canonical mode for the caller, the now-full line discipline buffer triggers the error recovery. When changing termios from non-canon to canon mode and the read buffer contains data, simulate an EOF push _without_ the DISABLED_CHAR in the read buffer. Importantly for the readline() problem, the termios can be changed back to non-canonical mode without changes to the read buffer occurring; ie., as if the previous termios change had not happened (as long as no intervening read took place). Preserve existing userspace behavior which allows '\0's already received in non-canon mode to be read as '\0's in canon mode (rather than trigger add'l EOF pushes or an actual EOF). Patch based on original proposal and discussion here https://bugzilla.kernel.org/show_bug.cgi?id=55991 by Stas Sergeev <stsp@users.sourceforge.net> Reported-by: Margarita Manterola <margamanterola@gmail.com> Cc: Maximiliano Curia <maxy@gnuservers.com.ar> Cc: Pavel Machek <pavel@ucw.cz> Cc: Arkadiusz Miskiewicz <a.miskiewicz@gmail.com> Acked-by: Stas Sergeev <stsp@users.sourceforge.net> Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-12-11 06:12:02 +08:00
unsigned char push:1;
/* shared by producer and consumer */
u8 read_buf[N_TTY_BUF_SIZE];
DECLARE_BITMAP(read_flags, N_TTY_BUF_SIZE);
u8 echo_buf[N_TTY_BUF_SIZE];
/* consumer-published */
size_t read_tail;
size_t line_start;
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>
2022-06-06 23:36:51 +08:00
/* # 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;
size_t echo_tail;
struct mutex atomic_read_lock;
struct mutex output_lock;
};
#define MASK(x) ((x) & (N_TTY_BUF_SIZE - 1))
static inline size_t read_cnt(struct n_tty_data *ldata)
{
return ldata->read_head - ldata->read_tail;
}
static inline u8 read_buf(struct n_tty_data *ldata, size_t i)
{
return ldata->read_buf[MASK(i)];
}
static inline u8 *read_buf_addr(struct n_tty_data *ldata, size_t i)
{
return &ldata->read_buf[MASK(i)];
}
static inline u8 echo_buf(struct n_tty_data *ldata, size_t i)
{
n_tty: Access echo_* variables carefully. syzbot is reporting stalls at __process_echoes() [1]. This is because since ldata->echo_commit < ldata->echo_tail becomes true for some reason, the discard loop is serving as almost infinite loop. This patch tries to avoid falling into ldata->echo_commit < ldata->echo_tail situation by making access to echo_* variables more carefully. Since reset_buffer_flags() is called without output_lock held, it should not touch echo_* variables. And omit a call to reset_buffer_flags() from n_tty_open() by using vzalloc(). Since add_echo_byte() is called without output_lock held, it needs memory barrier between storing into echo_buf[] and incrementing echo_head counter. echo_buf() needs corresponding memory barrier before reading echo_buf[]. Lack of handling the possibility of not-yet-stored multi-byte operation might be the reason of falling into ldata->echo_commit < ldata->echo_tail situation, for if I do WARN_ON(ldata->echo_commit == tail + 1) prior to echo_buf(ldata, tail + 1), the WARN_ON() fires. Also, explicitly masking with buffer for the former "while" loop, and use ldata->echo_commit > tail for the latter "while" loop. [1] https://syzkaller.appspot.com/bug?id=17f23b094cd80df750e5b0f8982c521ee6bcbf40 Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Reported-by: syzbot <syzbot+108696293d7a21ab688f@syzkaller.appspotmail.com> Cc: Peter Hurley <peter@hurleysoftware.com> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-05-26 08:53:14 +08:00
smp_rmb(); /* Matches smp_wmb() in add_echo_byte(). */
return ldata->echo_buf[MASK(i)];
}
static inline u8 *echo_buf_addr(struct n_tty_data *ldata, size_t i)
{
return &ldata->echo_buf[MASK(i)];
}
/* If we are not echoing the data, perhaps this is a secret so erase it */
static void zero_buffer(const struct tty_struct *tty, u8 *buffer, size_t size)
{
if (L_ICANON(tty) && !L_ECHO(tty))
memset(buffer, 0, size);
}
static void tty_copy(const struct tty_struct *tty, void *to, size_t tail,
size_t n)
{
struct n_tty_data *ldata = tty->disc_data;
size_t size = N_TTY_BUF_SIZE - tail;
void *from = read_buf_addr(ldata, tail);
if (n > size) {
tty_audit_add_data(tty, from, size);
memcpy(to, from, size);
zero_buffer(tty, from, size);
to += size;
n -= size;
from = ldata->read_buf;
}
tty_audit_add_data(tty, from, n);
memcpy(to, from, n);
zero_buffer(tty, from, n);
}
/**
* n_tty_kick_worker - start input worker (if required)
* @tty: terminal
*
* Re-schedules the flip buffer work if it may have stopped.
*
* Locking:
* * Caller holds exclusive %termios_rwsem, or
* * n_tty_read()/consumer path:
* holds non-exclusive %termios_rwsem
*/
static void n_tty_kick_worker(const struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
n_tty: Eliminate receive_room() from consumer/exclusive paths The input worker never reschedules itself; it only processes input until either there is no more input or the read buffer is full. So the reader is responsible for restarting the input worker only if the read buffer was previously full (no_room == 1) _and_ space is now available to process more input because the reader has consumed data from the read buffer. However, computing the actual space available is not required to determine if the reader has consumed data from the read buffer. This condition is evaluated in 5 situations, each of which the space avail is already known: 1. n_tty_flush_buffer() - the read buffer is empty; kick the worker 2. n_tty_set_termios() - no data has been consumed; do not kick the worker (although it may have kicked the reader so data _will be_ consumed) 3. n_tty_check_unthrottle - avail space > 3968; kick the worker 4. n_tty_read, before leaving - only kick the worker if the reader has moved the tail. This prevents unnecessarily kicking the worker when timeout-style reading is used. 5. n_tty_read, before sleeping - although it is possible for the read buffer to be full and input_available_p() to be false, this can only happen when the input worker is racing the reader, in which case the reader will have been woken and won't sleep. Rename n_tty_set_room() to n_tty_kick_worker() to reflect what the function actually does. Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-01-17 04:05:34 +08:00
/* Did the input worker stop? Restart it */
tty: fix hang on tty device with no_room set It is possible to hang pty devices in this case, the reader was blocking at epoll on master side, the writer was sleeping at wait_woken inside n_tty_write on slave side, and the write buffer on tty_port was full, we found that the reader and writer would never be woken again and blocked forever. The problem was caused by a race between reader and kworker: n_tty_read(reader): n_tty_receive_buf_common(kworker): copy_from_read_buf()| |room = N_TTY_BUF_SIZE - (ldata->read_head - tail) |room <= 0 n_tty_kick_worker() | |ldata->no_room = true After writing to slave device, writer wakes up kworker to flush data on tty_port to reader, and the kworker finds that reader has no room to store data so room <= 0 is met. At this moment, reader consumes all the data on reader buffer and calls n_tty_kick_worker to check ldata->no_room which is false and reader quits reading. Then kworker sets ldata->no_room=true and quits too. If write buffer is not full, writer will wake kworker to flush data again after following writes, but if write buffer is full and writer goes to sleep, kworker will never be woken again and tty device is blocked. This problem can be solved with a check for read buffer size inside n_tty_receive_buf_common, if read buffer is empty and ldata->no_room is true, a call to n_tty_kick_worker is necessary to keep flushing data to reader. Cc: <stable@vger.kernel.org> Fixes: 42458f41d08f ("n_tty: Ensure reader restarts worker for next reader") Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Signed-off-by: Hui Li <caelli@tencent.com> Message-ID: <1680749090-14106-1-git-send-email-caelli@tencent.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-04-06 10:44:50 +08:00
if (unlikely(READ_ONCE(ldata->no_room))) {
WRITE_ONCE(ldata->no_room, 0);
WARN_RATELIMIT(tty->port->itty == NULL,
"scheduling with invalid itty\n");
/* see if ldisc has been killed - if so, this means that
* even though the ldisc has been halted and ->buf.work
* cancelled, ->buf.work is about to be rescheduled
*/
WARN_RATELIMIT(test_bit(TTY_LDISC_HALTED, &tty->flags),
"scheduling buffer work for halted ldisc\n");
tty_buffer_restart_work(tty->port);
}
Revert "tty: make receive_buf() return the amout of bytes received" This reverts commit b1c43f82c5aa265442f82dba31ce985ebb7aa71c. It was broken in so many ways, and results in random odd pty issues. It re-introduced the buggy schedule_work() in flush_to_ldisc() that can cause endless work-loops (see commit a5660b41af6a: "tty: fix endless work loop when the buffer fills up"). It also used an "unsigned int" return value fo the ->receive_buf() function, but then made multiple functions return a negative error code, and didn't actually check for the error in the caller. And it didn't actually work at all. BenH bisected down odd tty behavior to it: "It looks like the patch is causing some major malfunctions of the X server for me, possibly related to PTYs. For example, cat'ing a large file in a gnome terminal hangs the kernel for -minutes- in a loop of what looks like flush_to_ldisc/workqueue code, (some ftrace data in the quoted bits further down). ... Some more data: It -looks- like what happens is that the flush_to_ldisc work queue entry constantly re-queues itself (because the PTY is full ?) and the workqueue thread will basically loop forver calling it without ever scheduling, thus starving the consumer process that could have emptied the PTY." which is pretty much exactly the problem we fixed in a5660b41af6a. Milton Miller pointed out the 'unsigned int' issue. Reported-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Reported-by: Milton Miller <miltonm@bga.com> Cc: Stefan Bigler <stefan.bigler@keymile.com> Cc: Toby Gray <toby.gray@realvnc.com> Cc: Felipe Balbi <balbi@ti.com> Cc: Greg Kroah-Hartman <gregkh@suse.de> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-06-04 05:33:24 +08:00
}
static ssize_t chars_in_buffer(const struct tty_struct *tty)
{
const struct n_tty_data *ldata = tty->disc_data;
size_t head = ldata->icanon ? ldata->canon_head : ldata->commit_head;
return head - ldata->read_tail;
}
/**
* n_tty_write_wakeup - asynchronous I/O notifier
* @tty: tty device
*
* Required for the ptys, serial driver etc. since processes that attach
* themselves to the master and rely on ASYNC IO must be woken up.
*/
static void n_tty_write_wakeup(struct tty_struct *tty)
{
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
kill_fasync(&tty->fasync, SIGIO, POLL_OUT);
}
static void n_tty_check_throttle(struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
/*
* Check the remaining room for the input canonicalization
* mode. We don't want to throttle the driver if we're in
* canonical mode and don't have a newline yet!
*/
if (ldata->icanon && ldata->canon_head == ldata->read_tail)
return;
do {
tty_set_flow_change(tty, TTY_THROTTLE_SAFE);
if (N_TTY_BUF_SIZE - read_cnt(ldata) >= TTY_THRESHOLD_THROTTLE)
break;
} while (!tty_throttle_safe(tty));
__tty_set_flow_change(tty, 0);
}
static void n_tty_check_unthrottle(struct tty_struct *tty)
{
if (tty->driver->type == TTY_DRIVER_TYPE_PTY) {
if (chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE)
return;
n_tty: Eliminate receive_room() from consumer/exclusive paths The input worker never reschedules itself; it only processes input until either there is no more input or the read buffer is full. So the reader is responsible for restarting the input worker only if the read buffer was previously full (no_room == 1) _and_ space is now available to process more input because the reader has consumed data from the read buffer. However, computing the actual space available is not required to determine if the reader has consumed data from the read buffer. This condition is evaluated in 5 situations, each of which the space avail is already known: 1. n_tty_flush_buffer() - the read buffer is empty; kick the worker 2. n_tty_set_termios() - no data has been consumed; do not kick the worker (although it may have kicked the reader so data _will be_ consumed) 3. n_tty_check_unthrottle - avail space > 3968; kick the worker 4. n_tty_read, before leaving - only kick the worker if the reader has moved the tail. This prevents unnecessarily kicking the worker when timeout-style reading is used. 5. n_tty_read, before sleeping - although it is possible for the read buffer to be full and input_available_p() to be false, this can only happen when the input worker is racing the reader, in which case the reader will have been woken and won't sleep. Rename n_tty_set_room() to n_tty_kick_worker() to reflect what the function actually does. Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-01-17 04:05:34 +08:00
n_tty_kick_worker(tty);
tty_wakeup(tty->link);
return;
}
/* If there is enough space in the read buffer now, let the
* low-level driver know. We use chars_in_buffer() to
* check the buffer, as it now knows about canonical mode.
* Otherwise, if the driver is throttled and the line is
* longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode,
* we won't get any more characters.
*/
do {
tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE);
if (chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE)
break;
n_tty: Eliminate receive_room() from consumer/exclusive paths The input worker never reschedules itself; it only processes input until either there is no more input or the read buffer is full. So the reader is responsible for restarting the input worker only if the read buffer was previously full (no_room == 1) _and_ space is now available to process more input because the reader has consumed data from the read buffer. However, computing the actual space available is not required to determine if the reader has consumed data from the read buffer. This condition is evaluated in 5 situations, each of which the space avail is already known: 1. n_tty_flush_buffer() - the read buffer is empty; kick the worker 2. n_tty_set_termios() - no data has been consumed; do not kick the worker (although it may have kicked the reader so data _will be_ consumed) 3. n_tty_check_unthrottle - avail space > 3968; kick the worker 4. n_tty_read, before leaving - only kick the worker if the reader has moved the tail. This prevents unnecessarily kicking the worker when timeout-style reading is used. 5. n_tty_read, before sleeping - although it is possible for the read buffer to be full and input_available_p() to be false, this can only happen when the input worker is racing the reader, in which case the reader will have been woken and won't sleep. Rename n_tty_set_room() to n_tty_kick_worker() to reflect what the function actually does. Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-01-17 04:05:34 +08:00
n_tty_kick_worker(tty);
} while (!tty_unthrottle_safe(tty));
__tty_set_flow_change(tty, 0);
}
/**
* put_tty_queue - add character to tty
* @c: character
* @ldata: n_tty data
*
* Add a character to the tty read_buf queue.
*
* Locking:
* * n_tty_receive_buf()/producer path:
* caller holds non-exclusive %termios_rwsem
*/
static inline void put_tty_queue(u8 c, struct n_tty_data *ldata)
{
*read_buf_addr(ldata, ldata->read_head) = c;
ldata->read_head++;
}
/**
* reset_buffer_flags - reset buffer state
* @ldata: line disc data to reset
*
* Reset the read buffer counters and clear the flags. Called from
* n_tty_open() and n_tty_flush_buffer().
*
* Locking:
* * caller holds exclusive %termios_rwsem, or
* * (locking is not required)
*/
n_tty: Fully initialize ldisc before restarting buffer work Buffer work may already be pending when the n_tty ldisc is re-opened, eg., when setting the ldisc (via TIOCSETD ioctl) and when hanging up the tty. Since n_tty_set_room() may restart buffer work, first ensure the ldisc is completely initialized. Factor n_tty_set_room() out of reset_buffer_flags() (only 2 callers) and reorganize n_tty_open() to set termios last; buffer work will be restarted there if necessary, after the char_map is properly initialized. Fixes this WARNING: [ 549.561769] ------------[ cut here ]------------ [ 549.598755] WARNING: at drivers/tty/n_tty.c:160 n_tty_set_room+0xff/0x130() [ 549.604058] scheduling buffer work for halted ldisc [ 549.607741] Pid: 9417, comm: trinity-child28 Tainted: G D W 3.7.0-next-20121217-sasha-00023-g8689ef9 #219 [ 549.652580] Call Trace: [ 549.662754] [<ffffffff81c432cf>] ? n_tty_set_room+0xff/0x130 [ 549.665458] [<ffffffff8110cae7>] warn_slowpath_common+0x87/0xb0 [ 549.668257] [<ffffffff8110cb71>] warn_slowpath_fmt+0x41/0x50 [ 549.671007] [<ffffffff81c432cf>] n_tty_set_room+0xff/0x130 [ 549.673268] [<ffffffff81c44597>] reset_buffer_flags+0x137/0x150 [ 549.675607] [<ffffffff81c45b71>] n_tty_open+0x131/0x1c0 [ 549.677699] [<ffffffff81c47824>] tty_ldisc_open.isra.5+0x54/0x70 [ 549.680147] [<ffffffff81c482bf>] tty_ldisc_hangup+0x11f/0x1e0 [ 549.682409] [<ffffffff81c3fa17>] __tty_hangup+0x137/0x440 [ 549.684634] [<ffffffff81c3fd49>] tty_vhangup+0x9/0x10 [ 549.686443] [<ffffffff81c4a42c>] pty_close+0x14c/0x160 [ 549.688446] [<ffffffff81c41225>] tty_release+0xd5/0x490 [ 549.690460] [<ffffffff8127d8a2>] __fput+0x122/0x250 [ 549.692577] [<ffffffff8127d9d9>] ____fput+0x9/0x10 [ 549.694534] [<ffffffff811348c2>] task_work_run+0xb2/0xf0 [ 549.696349] [<ffffffff81113c6d>] do_exit+0x36d/0x580 [ 549.698286] [<ffffffff8107d964>] ? syscall_trace_enter+0x24/0x2e0 [ 549.702729] [<ffffffff81113f4a>] do_group_exit+0x8a/0xc0 [ 549.706775] [<ffffffff81113f92>] sys_exit_group+0x12/0x20 [ 549.711088] [<ffffffff83cfab18>] tracesys+0xe1/0xe6 [ 549.728001] ---[ end trace 73eb41728f11f87e ]--- Reported-by: Sasha Levin <levinsasha928@gmail.com> Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-03-12 04:44:32 +08:00
static void reset_buffer_flags(struct n_tty_data *ldata)
{
ldata->read_head = ldata->canon_head = ldata->read_tail = 0;
ldata->commit_head = 0;
ldata->line_start = 0;
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
ldata->erasing = 0;
bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
n_tty: Fix buffer overruns with larger-than-4k pastes readline() inadvertently triggers an error recovery path when pastes larger than 4k overrun the line discipline buffer. The error recovery path discards input when the line discipline buffer is full and operating in canonical mode and no newline has been received. Because readline() changes the termios to non-canonical mode to read the line char-by-char, the line discipline buffer can become full, and then when readline() restores termios back to canonical mode for the caller, the now-full line discipline buffer triggers the error recovery. When changing termios from non-canon to canon mode and the read buffer contains data, simulate an EOF push _without_ the DISABLED_CHAR in the read buffer. Importantly for the readline() problem, the termios can be changed back to non-canonical mode without changes to the read buffer occurring; ie., as if the previous termios change had not happened (as long as no intervening read took place). Preserve existing userspace behavior which allows '\0's already received in non-canon mode to be read as '\0's in canon mode (rather than trigger add'l EOF pushes or an actual EOF). Patch based on original proposal and discussion here https://bugzilla.kernel.org/show_bug.cgi?id=55991 by Stas Sergeev <stsp@users.sourceforge.net> Reported-by: Margarita Manterola <margamanterola@gmail.com> Cc: Maximiliano Curia <maxy@gnuservers.com.ar> Cc: Pavel Machek <pavel@ucw.cz> Cc: Arkadiusz Miskiewicz <a.miskiewicz@gmail.com> Acked-by: Stas Sergeev <stsp@users.sourceforge.net> Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-12-11 06:12:02 +08:00
ldata->push = 0;
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>
2022-06-06 23:36:51 +08:00
ldata->lookahead_count = 0;
}
static void n_tty_packet_mode_flush(struct tty_struct *tty)
{
unsigned long flags;
if (tty->link->ctrl.packet) {
spin_lock_irqsave(&tty->ctrl.lock, flags);
tty->ctrl.pktstatus |= TIOCPKT_FLUSHREAD;
spin_unlock_irqrestore(&tty->ctrl.lock, flags);
tty: fix stall caused by missing memory barrier in drivers/tty/n_tty.c My colleague ran into a program stall on a x86_64 server, where n_tty_read() was waiting for data even if there was data in the buffer in the pty. kernel stack for the stuck process looks like below. #0 [ffff88303d107b58] __schedule at ffffffff815c4b20 #1 [ffff88303d107bd0] schedule at ffffffff815c513e #2 [ffff88303d107bf0] schedule_timeout at ffffffff815c7818 #3 [ffff88303d107ca0] wait_woken at ffffffff81096bd2 #4 [ffff88303d107ce0] n_tty_read at ffffffff8136fa23 #5 [ffff88303d107dd0] tty_read at ffffffff81368013 #6 [ffff88303d107e20] __vfs_read at ffffffff811a3704 #7 [ffff88303d107ec0] vfs_read at ffffffff811a3a57 #8 [ffff88303d107f00] sys_read at ffffffff811a4306 #9 [ffff88303d107f50] entry_SYSCALL_64_fastpath at ffffffff815c86d7 There seems to be two problems causing this issue. First, in drivers/tty/n_tty.c, __receive_buf() stores the data and updates ldata->commit_head using smp_store_release() and then checks the wait queue using waitqueue_active(). However, since there is no memory barrier, __receive_buf() could return without calling wake_up_interactive_poll(), and at the same time, n_tty_read() could start to wait in wait_woken() as in the following chart. __receive_buf() n_tty_read() ------------------------------------------------------------------------ if (waitqueue_active(&tty->read_wait)) /* Memory operations issued after the RELEASE may be completed before the RELEASE operation has completed */ add_wait_queue(&tty->read_wait, &wait); ... if (!input_available_p(tty, 0)) { smp_store_release(&ldata->commit_head, ldata->read_head); ... timeout = wait_woken(&wait, TASK_INTERRUPTIBLE, timeout); ------------------------------------------------------------------------ The second problem is that n_tty_read() also lacks a memory barrier call and could also cause __receive_buf() to return without calling wake_up_interactive_poll(), and n_tty_read() to wait in wait_woken() as in the chart below. __receive_buf() n_tty_read() ------------------------------------------------------------------------ spin_lock_irqsave(&q->lock, flags); /* from add_wait_queue() */ ... if (!input_available_p(tty, 0)) { /* Memory operations issued after the RELEASE may be completed before the RELEASE operation has completed */ smp_store_release(&ldata->commit_head, ldata->read_head); if (waitqueue_active(&tty->read_wait)) __add_wait_queue(q, wait); spin_unlock_irqrestore(&q->lock,flags); /* from add_wait_queue() */ ... timeout = wait_woken(&wait, TASK_INTERRUPTIBLE, timeout); ------------------------------------------------------------------------ There are also other places in drivers/tty/n_tty.c which have similar calls to waitqueue_active(), so instead of adding many memory barrier calls, this patch simply removes the call to waitqueue_active(), leaving just wake_up*() behind. This fixes both problems because, even though the memory access before or after the spinlocks in both wake_up*() and add_wait_queue() can sneak into the critical section, it cannot go past it and the critical section assures that they will be serialized (please see "INTER-CPU ACQUIRING BARRIER EFFECTS" in Documentation/memory-barriers.txt for a better explanation). Moreover, the resulting code is much simpler. Latency measurement using a ping-pong test over a pty doesn't show any visible performance drop. Signed-off-by: Kosuke Tatsukawa <tatsu@ab.jp.nec.com> Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-10-02 16:27:05 +08:00
wake_up_interruptible(&tty->link->read_wait);
}
}
/**
* n_tty_flush_buffer - clean input queue
* @tty: terminal device
*
* Flush the input buffer. Called when the tty layer wants the buffer flushed
* (eg at hangup) or when the %N_TTY line discipline internally has to clean
* the pending queue (for example some signals).
*
* Holds %termios_rwsem to exclude producer/consumer while buffer indices are
* reset.
*
* Locking: %ctrl.lock, exclusive %termios_rwsem
*/
static void n_tty_flush_buffer(struct tty_struct *tty)
{
down_write(&tty->termios_rwsem);
n_tty: Fully initialize ldisc before restarting buffer work Buffer work may already be pending when the n_tty ldisc is re-opened, eg., when setting the ldisc (via TIOCSETD ioctl) and when hanging up the tty. Since n_tty_set_room() may restart buffer work, first ensure the ldisc is completely initialized. Factor n_tty_set_room() out of reset_buffer_flags() (only 2 callers) and reorganize n_tty_open() to set termios last; buffer work will be restarted there if necessary, after the char_map is properly initialized. Fixes this WARNING: [ 549.561769] ------------[ cut here ]------------ [ 549.598755] WARNING: at drivers/tty/n_tty.c:160 n_tty_set_room+0xff/0x130() [ 549.604058] scheduling buffer work for halted ldisc [ 549.607741] Pid: 9417, comm: trinity-child28 Tainted: G D W 3.7.0-next-20121217-sasha-00023-g8689ef9 #219 [ 549.652580] Call Trace: [ 549.662754] [<ffffffff81c432cf>] ? n_tty_set_room+0xff/0x130 [ 549.665458] [<ffffffff8110cae7>] warn_slowpath_common+0x87/0xb0 [ 549.668257] [<ffffffff8110cb71>] warn_slowpath_fmt+0x41/0x50 [ 549.671007] [<ffffffff81c432cf>] n_tty_set_room+0xff/0x130 [ 549.673268] [<ffffffff81c44597>] reset_buffer_flags+0x137/0x150 [ 549.675607] [<ffffffff81c45b71>] n_tty_open+0x131/0x1c0 [ 549.677699] [<ffffffff81c47824>] tty_ldisc_open.isra.5+0x54/0x70 [ 549.680147] [<ffffffff81c482bf>] tty_ldisc_hangup+0x11f/0x1e0 [ 549.682409] [<ffffffff81c3fa17>] __tty_hangup+0x137/0x440 [ 549.684634] [<ffffffff81c3fd49>] tty_vhangup+0x9/0x10 [ 549.686443] [<ffffffff81c4a42c>] pty_close+0x14c/0x160 [ 549.688446] [<ffffffff81c41225>] tty_release+0xd5/0x490 [ 549.690460] [<ffffffff8127d8a2>] __fput+0x122/0x250 [ 549.692577] [<ffffffff8127d9d9>] ____fput+0x9/0x10 [ 549.694534] [<ffffffff811348c2>] task_work_run+0xb2/0xf0 [ 549.696349] [<ffffffff81113c6d>] do_exit+0x36d/0x580 [ 549.698286] [<ffffffff8107d964>] ? syscall_trace_enter+0x24/0x2e0 [ 549.702729] [<ffffffff81113f4a>] do_group_exit+0x8a/0xc0 [ 549.706775] [<ffffffff81113f92>] sys_exit_group+0x12/0x20 [ 549.711088] [<ffffffff83cfab18>] tracesys+0xe1/0xe6 [ 549.728001] ---[ end trace 73eb41728f11f87e ]--- Reported-by: Sasha Levin <levinsasha928@gmail.com> Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-03-12 04:44:32 +08:00
reset_buffer_flags(tty->disc_data);
n_tty: Eliminate receive_room() from consumer/exclusive paths The input worker never reschedules itself; it only processes input until either there is no more input or the read buffer is full. So the reader is responsible for restarting the input worker only if the read buffer was previously full (no_room == 1) _and_ space is now available to process more input because the reader has consumed data from the read buffer. However, computing the actual space available is not required to determine if the reader has consumed data from the read buffer. This condition is evaluated in 5 situations, each of which the space avail is already known: 1. n_tty_flush_buffer() - the read buffer is empty; kick the worker 2. n_tty_set_termios() - no data has been consumed; do not kick the worker (although it may have kicked the reader so data _will be_ consumed) 3. n_tty_check_unthrottle - avail space > 3968; kick the worker 4. n_tty_read, before leaving - only kick the worker if the reader has moved the tail. This prevents unnecessarily kicking the worker when timeout-style reading is used. 5. n_tty_read, before sleeping - although it is possible for the read buffer to be full and input_available_p() to be false, this can only happen when the input worker is racing the reader, in which case the reader will have been woken and won't sleep. Rename n_tty_set_room() to n_tty_kick_worker() to reflect what the function actually does. Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-01-17 04:05:34 +08:00
n_tty_kick_worker(tty);
if (tty->link)
n_tty_packet_mode_flush(tty);
up_write(&tty->termios_rwsem);
}
/**
* is_utf8_continuation - utf8 multibyte check
* @c: byte to check
*
* Returns: true if the utf8 character @c is a multibyte continuation
* character. We use this to correctly compute the on-screen size of the
* character when printing.
*/
static inline int is_utf8_continuation(u8 c)
{
return (c & 0xc0) == 0x80;
}
/**
* is_continuation - multibyte check
* @c: byte to check
* @tty: terminal device
*
* Returns: true if the utf8 character @c is a multibyte continuation character
* and the terminal is in unicode mode.
*/
static inline int is_continuation(u8 c, const struct tty_struct *tty)
{
return I_IUTF8(tty) && is_utf8_continuation(c);
}
/**
* do_output_char - output one character
* @c: character (or partial unicode symbol)
* @tty: terminal device
* @space: space available in tty driver write buffer
*
* This is a helper function that handles one output character (including
* special characters like TAB, CR, LF, etc.), doing OPOST processing and
* putting the results in the tty driver's write buffer.
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
*
* Note that Linux currently ignores TABDLY, CRDLY, VTDLY, FFDLY and NLDLY.
* They simply aren't relevant in the world today. If you ever need them, add
* them here.
*
* Returns: the number of bytes of buffer space used or -1 if no space left.
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
*
* Locking: should be called under the %output_lock to protect the column state
* and space left in the buffer.
*/
static int do_output_char(u8 c, struct tty_struct *tty, int space)
{
struct n_tty_data *ldata = tty->disc_data;
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
int spaces;
if (!space)
return -1;
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
switch (c) {
case '\n':
if (O_ONLRET(tty))
ldata->column = 0;
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
if (O_ONLCR(tty)) {
if (space < 2)
return -1;
ldata->canon_column = ldata->column = 0;
tty->ops->write(tty, "\r\n", 2);
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
return 2;
}
ldata->canon_column = ldata->column;
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
break;
case '\r':
if (O_ONOCR(tty) && ldata->column == 0)
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
return 0;
if (O_OCRNL(tty)) {
c = '\n';
if (O_ONLRET(tty))
ldata->canon_column = ldata->column = 0;
break;
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
}
ldata->canon_column = ldata->column = 0;
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
break;
case '\t':
spaces = 8 - (ldata->column & 7);
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
if (O_TABDLY(tty) == XTABS) {
if (space < spaces)
return -1;
ldata->column += spaces;
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
tty->ops->write(tty, " ", spaces);
return spaces;
}
ldata->column += spaces;
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
break;
case '\b':
if (ldata->column > 0)
ldata->column--;
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
break;
default:
if (!iscntrl(c)) {
if (O_OLCUC(tty))
c = toupper(c);
if (!is_continuation(c, tty))
ldata->column++;
}
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
break;
}
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
tty_put_char(tty, c);
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
return 1;
}
/**
* process_output - output post processor
* @c: character (or partial unicode symbol)
* @tty: terminal device
*
* Output one character with OPOST processing.
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
*
* Returns: -1 when the output device is full and the character must be
* retried.
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
*
* Locking: %output_lock to protect column state and space left (also, this is
*called from n_tty_write() under the tty layer write lock).
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
*/
static int process_output(u8 c, struct tty_struct *tty)
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
{
struct n_tty_data *ldata = tty->disc_data;
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
int space, retval;
mutex_lock(&ldata->output_lock);
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
space = tty_write_room(tty);
retval = do_output_char(c, tty, space);
mutex_unlock(&ldata->output_lock);
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
if (retval < 0)
return -1;
else
return 0;
}
/**
* process_output_block - block post processor
* @tty: terminal device
* @buf: character buffer
* @nr: number of bytes to output
*
* Output a block of characters with OPOST processing.
*
* This path is used to speed up block console writes, among other things when
* processing blocks of output data. It handles only the simple cases normally
* found and helps to generate blocks of symbols for the console driver and
* thus improve performance.
*
* Returns: the number of characters output.
*
* Locking: %output_lock to protect column state and space left (also, this is
* called from n_tty_write() under the tty layer write lock).
*/
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
static ssize_t process_output_block(struct tty_struct *tty,
const u8 *buf, unsigned int nr)
{
struct n_tty_data *ldata = tty->disc_data;
int space;
int i;
const u8 *cp;
mutex_lock(&ldata->output_lock);
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
space = tty_write_room(tty);
if (space <= 0) {
mutex_unlock(&ldata->output_lock);
return space;
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
}
if (nr > space)
nr = space;
for (i = 0, cp = buf; i < nr; i++, cp++) {
u8 c = *cp;
switch (c) {
case '\n':
if (O_ONLRET(tty))
ldata->column = 0;
if (O_ONLCR(tty))
goto break_out;
ldata->canon_column = ldata->column;
break;
case '\r':
if (O_ONOCR(tty) && ldata->column == 0)
goto break_out;
if (O_OCRNL(tty))
goto break_out;
ldata->canon_column = ldata->column = 0;
break;
case '\t':
goto break_out;
case '\b':
if (ldata->column > 0)
ldata->column--;
break;
default:
if (!iscntrl(c)) {
if (O_OLCUC(tty))
goto break_out;
if (!is_continuation(c, tty))
ldata->column++;
}
break;
}
}
break_out:
i = tty->ops->write(tty, buf, i);
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
mutex_unlock(&ldata->output_lock);
return i;
}
static int n_tty_process_echo_ops(struct tty_struct *tty, size_t *tail,
int space)
{
struct n_tty_data *ldata = tty->disc_data;
u8 op;
/*
* Since add_echo_byte() is called without holding output_lock, we
* might see only portion of multi-byte operation.
*/
if (MASK(ldata->echo_commit) == MASK(*tail + 1))
return -ENODATA;
/*
* If the buffer byte is the start of a multi-byte operation, get the
* next byte, which is either the op code or a control character value.
*/
op = echo_buf(ldata, *tail + 1);
switch (op) {
case ECHO_OP_ERASE_TAB: {
unsigned int num_chars, num_bs;
if (MASK(ldata->echo_commit) == MASK(*tail + 2))
return -ENODATA;
num_chars = echo_buf(ldata, *tail + 2);
/*
* Determine how many columns to go back in order to erase the
* tab. This depends on the number of columns used by other
* characters within the tab area. If this (modulo 8) count is
* from the start of input rather than from a previous tab, we
* offset by canon column. Otherwise, tab spacing is normal.
*/
if (!(num_chars & 0x80))
num_chars += ldata->canon_column;
num_bs = 8 - (num_chars & 7);
if (num_bs > space)
return -ENOSPC;
space -= num_bs;
while (num_bs--) {
tty_put_char(tty, '\b');
if (ldata->column > 0)
ldata->column--;
}
*tail += 3;
break;
}
case ECHO_OP_SET_CANON_COL:
ldata->canon_column = ldata->column;
*tail += 2;
break;
case ECHO_OP_MOVE_BACK_COL:
if (ldata->column > 0)
ldata->column--;
*tail += 2;
break;
case ECHO_OP_START:
/* This is an escaped echo op start code */
if (!space)
return -ENOSPC;
tty_put_char(tty, ECHO_OP_START);
ldata->column++;
space--;
*tail += 2;
break;
default:
/*
* If the op is not a special byte code, it is a ctrl char
* tagged to be echoed as "^X" (where X is the letter
* representing the control char). Note that we must ensure
* there is enough space for the whole ctrl pair.
*/
if (space < 2)
return -ENOSPC;
tty_put_char(tty, '^');
tty_put_char(tty, op ^ 0100);
ldata->column += 2;
space -= 2;
*tail += 2;
break;
}
return space;
}
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
/**
* __process_echoes - write pending echo characters
* @tty: terminal device
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
*
* Write previously buffered echo (and other ldisc-generated) characters to the
* tty.
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
*
* Characters generated by the ldisc (including echoes) need to be buffered
* because the driver's write buffer can fill during heavy program output.
* Echoing straight to the driver will often fail under these conditions,
* causing lost characters and resulting mismatches of ldisc state information.
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
*
* Since the ldisc state must represent the characters actually sent to the
* driver at the time of the write, operations like certain changes in column
* state are also saved in the buffer and executed here.
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
*
* A circular fifo buffer is used so that the most recent characters are
* prioritized. Also, when control characters are echoed with a prefixed "^",
* the pair is treated atomically and thus not separated.
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
*
* Locking: callers must hold %output_lock.
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
*/
static size_t __process_echoes(struct tty_struct *tty)
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
{
struct n_tty_data *ldata = tty->disc_data;
int space, old_space;
size_t tail;
u8 c;
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
old_space = space = tty_write_room(tty);
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
tail = ldata->echo_tail;
n_tty: Access echo_* variables carefully. syzbot is reporting stalls at __process_echoes() [1]. This is because since ldata->echo_commit < ldata->echo_tail becomes true for some reason, the discard loop is serving as almost infinite loop. This patch tries to avoid falling into ldata->echo_commit < ldata->echo_tail situation by making access to echo_* variables more carefully. Since reset_buffer_flags() is called without output_lock held, it should not touch echo_* variables. And omit a call to reset_buffer_flags() from n_tty_open() by using vzalloc(). Since add_echo_byte() is called without output_lock held, it needs memory barrier between storing into echo_buf[] and incrementing echo_head counter. echo_buf() needs corresponding memory barrier before reading echo_buf[]. Lack of handling the possibility of not-yet-stored multi-byte operation might be the reason of falling into ldata->echo_commit < ldata->echo_tail situation, for if I do WARN_ON(ldata->echo_commit == tail + 1) prior to echo_buf(ldata, tail + 1), the WARN_ON() fires. Also, explicitly masking with buffer for the former "while" loop, and use ldata->echo_commit > tail for the latter "while" loop. [1] https://syzkaller.appspot.com/bug?id=17f23b094cd80df750e5b0f8982c521ee6bcbf40 Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Reported-by: syzbot <syzbot+108696293d7a21ab688f@syzkaller.appspotmail.com> Cc: Peter Hurley <peter@hurleysoftware.com> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-05-26 08:53:14 +08:00
while (MASK(ldata->echo_commit) != MASK(tail)) {
c = echo_buf(ldata, tail);
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
if (c == ECHO_OP_START) {
int ret = n_tty_process_echo_ops(tty, &tail, space);
if (ret == -ENODATA)
n_tty: Access echo_* variables carefully. syzbot is reporting stalls at __process_echoes() [1]. This is because since ldata->echo_commit < ldata->echo_tail becomes true for some reason, the discard loop is serving as almost infinite loop. This patch tries to avoid falling into ldata->echo_commit < ldata->echo_tail situation by making access to echo_* variables more carefully. Since reset_buffer_flags() is called without output_lock held, it should not touch echo_* variables. And omit a call to reset_buffer_flags() from n_tty_open() by using vzalloc(). Since add_echo_byte() is called without output_lock held, it needs memory barrier between storing into echo_buf[] and incrementing echo_head counter. echo_buf() needs corresponding memory barrier before reading echo_buf[]. Lack of handling the possibility of not-yet-stored multi-byte operation might be the reason of falling into ldata->echo_commit < ldata->echo_tail situation, for if I do WARN_ON(ldata->echo_commit == tail + 1) prior to echo_buf(ldata, tail + 1), the WARN_ON() fires. Also, explicitly masking with buffer for the former "while" loop, and use ldata->echo_commit > tail for the latter "while" loop. [1] https://syzkaller.appspot.com/bug?id=17f23b094cd80df750e5b0f8982c521ee6bcbf40 Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Reported-by: syzbot <syzbot+108696293d7a21ab688f@syzkaller.appspotmail.com> Cc: Peter Hurley <peter@hurleysoftware.com> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-05-26 08:53:14 +08:00
goto not_yet_stored;
if (ret < 0)
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
break;
space = ret;
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
} else {
if (O_OPOST(tty)) {
int retval = do_output_char(c, tty, space);
if (retval < 0)
break;
space -= retval;
} else {
if (!space)
break;
tty_put_char(tty, c);
space -= 1;
}
tail += 1;
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
}
}
/* If the echo buffer is nearly full (so that the possibility exists
* of echo overrun before the next commit), then discard enough
* data at the tail to prevent a subsequent overrun */
n_tty: Access echo_* variables carefully. syzbot is reporting stalls at __process_echoes() [1]. This is because since ldata->echo_commit < ldata->echo_tail becomes true for some reason, the discard loop is serving as almost infinite loop. This patch tries to avoid falling into ldata->echo_commit < ldata->echo_tail situation by making access to echo_* variables more carefully. Since reset_buffer_flags() is called without output_lock held, it should not touch echo_* variables. And omit a call to reset_buffer_flags() from n_tty_open() by using vzalloc(). Since add_echo_byte() is called without output_lock held, it needs memory barrier between storing into echo_buf[] and incrementing echo_head counter. echo_buf() needs corresponding memory barrier before reading echo_buf[]. Lack of handling the possibility of not-yet-stored multi-byte operation might be the reason of falling into ldata->echo_commit < ldata->echo_tail situation, for if I do WARN_ON(ldata->echo_commit == tail + 1) prior to echo_buf(ldata, tail + 1), the WARN_ON() fires. Also, explicitly masking with buffer for the former "while" loop, and use ldata->echo_commit > tail for the latter "while" loop. [1] https://syzkaller.appspot.com/bug?id=17f23b094cd80df750e5b0f8982c521ee6bcbf40 Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Reported-by: syzbot <syzbot+108696293d7a21ab688f@syzkaller.appspotmail.com> Cc: Peter Hurley <peter@hurleysoftware.com> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-05-26 08:53:14 +08:00
while (ldata->echo_commit > tail &&
ldata->echo_commit - tail >= ECHO_DISCARD_WATERMARK) {
if (echo_buf(ldata, tail) == ECHO_OP_START) {
if (echo_buf(ldata, tail + 1) == ECHO_OP_ERASE_TAB)
tail += 3;
else
tail += 2;
} else
tail++;
}
n_tty: Access echo_* variables carefully. syzbot is reporting stalls at __process_echoes() [1]. This is because since ldata->echo_commit < ldata->echo_tail becomes true for some reason, the discard loop is serving as almost infinite loop. This patch tries to avoid falling into ldata->echo_commit < ldata->echo_tail situation by making access to echo_* variables more carefully. Since reset_buffer_flags() is called without output_lock held, it should not touch echo_* variables. And omit a call to reset_buffer_flags() from n_tty_open() by using vzalloc(). Since add_echo_byte() is called without output_lock held, it needs memory barrier between storing into echo_buf[] and incrementing echo_head counter. echo_buf() needs corresponding memory barrier before reading echo_buf[]. Lack of handling the possibility of not-yet-stored multi-byte operation might be the reason of falling into ldata->echo_commit < ldata->echo_tail situation, for if I do WARN_ON(ldata->echo_commit == tail + 1) prior to echo_buf(ldata, tail + 1), the WARN_ON() fires. Also, explicitly masking with buffer for the former "while" loop, and use ldata->echo_commit > tail for the latter "while" loop. [1] https://syzkaller.appspot.com/bug?id=17f23b094cd80df750e5b0f8982c521ee6bcbf40 Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Reported-by: syzbot <syzbot+108696293d7a21ab688f@syzkaller.appspotmail.com> Cc: Peter Hurley <peter@hurleysoftware.com> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-05-26 08:53:14 +08:00
not_yet_stored:
ldata->echo_tail = tail;
return old_space - space;
}
static void commit_echoes(struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
size_t nr, old, echoed;
size_t head;
n_tty: Access echo_* variables carefully. syzbot is reporting stalls at __process_echoes() [1]. This is because since ldata->echo_commit < ldata->echo_tail becomes true for some reason, the discard loop is serving as almost infinite loop. This patch tries to avoid falling into ldata->echo_commit < ldata->echo_tail situation by making access to echo_* variables more carefully. Since reset_buffer_flags() is called without output_lock held, it should not touch echo_* variables. And omit a call to reset_buffer_flags() from n_tty_open() by using vzalloc(). Since add_echo_byte() is called without output_lock held, it needs memory barrier between storing into echo_buf[] and incrementing echo_head counter. echo_buf() needs corresponding memory barrier before reading echo_buf[]. Lack of handling the possibility of not-yet-stored multi-byte operation might be the reason of falling into ldata->echo_commit < ldata->echo_tail situation, for if I do WARN_ON(ldata->echo_commit == tail + 1) prior to echo_buf(ldata, tail + 1), the WARN_ON() fires. Also, explicitly masking with buffer for the former "while" loop, and use ldata->echo_commit > tail for the latter "while" loop. [1] https://syzkaller.appspot.com/bug?id=17f23b094cd80df750e5b0f8982c521ee6bcbf40 Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Reported-by: syzbot <syzbot+108696293d7a21ab688f@syzkaller.appspotmail.com> Cc: Peter Hurley <peter@hurleysoftware.com> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-05-26 08:53:14 +08:00
mutex_lock(&ldata->output_lock);
head = ldata->echo_head;
ldata->echo_mark = head;
old = ldata->echo_commit - ldata->echo_tail;
/* Process committed echoes if the accumulated # of bytes
* is over the threshold (and try again each time another
* block is accumulated) */
nr = head - ldata->echo_tail;
n_tty: Access echo_* variables carefully. syzbot is reporting stalls at __process_echoes() [1]. This is because since ldata->echo_commit < ldata->echo_tail becomes true for some reason, the discard loop is serving as almost infinite loop. This patch tries to avoid falling into ldata->echo_commit < ldata->echo_tail situation by making access to echo_* variables more carefully. Since reset_buffer_flags() is called without output_lock held, it should not touch echo_* variables. And omit a call to reset_buffer_flags() from n_tty_open() by using vzalloc(). Since add_echo_byte() is called without output_lock held, it needs memory barrier between storing into echo_buf[] and incrementing echo_head counter. echo_buf() needs corresponding memory barrier before reading echo_buf[]. Lack of handling the possibility of not-yet-stored multi-byte operation might be the reason of falling into ldata->echo_commit < ldata->echo_tail situation, for if I do WARN_ON(ldata->echo_commit == tail + 1) prior to echo_buf(ldata, tail + 1), the WARN_ON() fires. Also, explicitly masking with buffer for the former "while" loop, and use ldata->echo_commit > tail for the latter "while" loop. [1] https://syzkaller.appspot.com/bug?id=17f23b094cd80df750e5b0f8982c521ee6bcbf40 Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Reported-by: syzbot <syzbot+108696293d7a21ab688f@syzkaller.appspotmail.com> Cc: Peter Hurley <peter@hurleysoftware.com> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-05-26 08:53:14 +08:00
if (nr < ECHO_COMMIT_WATERMARK ||
(nr % ECHO_BLOCK > old % ECHO_BLOCK)) {
mutex_unlock(&ldata->output_lock);
return;
n_tty: Access echo_* variables carefully. syzbot is reporting stalls at __process_echoes() [1]. This is because since ldata->echo_commit < ldata->echo_tail becomes true for some reason, the discard loop is serving as almost infinite loop. This patch tries to avoid falling into ldata->echo_commit < ldata->echo_tail situation by making access to echo_* variables more carefully. Since reset_buffer_flags() is called without output_lock held, it should not touch echo_* variables. And omit a call to reset_buffer_flags() from n_tty_open() by using vzalloc(). Since add_echo_byte() is called without output_lock held, it needs memory barrier between storing into echo_buf[] and incrementing echo_head counter. echo_buf() needs corresponding memory barrier before reading echo_buf[]. Lack of handling the possibility of not-yet-stored multi-byte operation might be the reason of falling into ldata->echo_commit < ldata->echo_tail situation, for if I do WARN_ON(ldata->echo_commit == tail + 1) prior to echo_buf(ldata, tail + 1), the WARN_ON() fires. Also, explicitly masking with buffer for the former "while" loop, and use ldata->echo_commit > tail for the latter "while" loop. [1] https://syzkaller.appspot.com/bug?id=17f23b094cd80df750e5b0f8982c521ee6bcbf40 Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Reported-by: syzbot <syzbot+108696293d7a21ab688f@syzkaller.appspotmail.com> Cc: Peter Hurley <peter@hurleysoftware.com> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-05-26 08:53:14 +08:00
}
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
ldata->echo_commit = head;
echoed = __process_echoes(tty);
mutex_unlock(&ldata->output_lock);
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
if (echoed && tty->ops->flush_chars)
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
tty->ops->flush_chars(tty);
}
static void process_echoes(struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
size_t echoed;
if (ldata->echo_mark == ldata->echo_tail)
return;
mutex_lock(&ldata->output_lock);
ldata->echo_commit = ldata->echo_mark;
echoed = __process_echoes(tty);
mutex_unlock(&ldata->output_lock);
if (echoed && tty->ops->flush_chars)
tty->ops->flush_chars(tty);
}
/* NB: echo_mark and echo_head should be equivalent here */
static void flush_echoes(struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
if ((!L_ECHO(tty) && !L_ECHONL(tty)) ||
ldata->echo_commit == ldata->echo_head)
return;
mutex_lock(&ldata->output_lock);
ldata->echo_commit = ldata->echo_head;
__process_echoes(tty);
mutex_unlock(&ldata->output_lock);
}
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
/**
* add_echo_byte - add a byte to the echo buffer
* @c: unicode byte to echo
* @ldata: n_tty data
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
*
* Add a character or operation byte to the echo buffer.
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
*/
static inline void add_echo_byte(u8 c, struct n_tty_data *ldata)
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
{
n_tty: Access echo_* variables carefully. syzbot is reporting stalls at __process_echoes() [1]. This is because since ldata->echo_commit < ldata->echo_tail becomes true for some reason, the discard loop is serving as almost infinite loop. This patch tries to avoid falling into ldata->echo_commit < ldata->echo_tail situation by making access to echo_* variables more carefully. Since reset_buffer_flags() is called without output_lock held, it should not touch echo_* variables. And omit a call to reset_buffer_flags() from n_tty_open() by using vzalloc(). Since add_echo_byte() is called without output_lock held, it needs memory barrier between storing into echo_buf[] and incrementing echo_head counter. echo_buf() needs corresponding memory barrier before reading echo_buf[]. Lack of handling the possibility of not-yet-stored multi-byte operation might be the reason of falling into ldata->echo_commit < ldata->echo_tail situation, for if I do WARN_ON(ldata->echo_commit == tail + 1) prior to echo_buf(ldata, tail + 1), the WARN_ON() fires. Also, explicitly masking with buffer for the former "while" loop, and use ldata->echo_commit > tail for the latter "while" loop. [1] https://syzkaller.appspot.com/bug?id=17f23b094cd80df750e5b0f8982c521ee6bcbf40 Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Reported-by: syzbot <syzbot+108696293d7a21ab688f@syzkaller.appspotmail.com> Cc: Peter Hurley <peter@hurleysoftware.com> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-05-26 08:53:14 +08:00
*echo_buf_addr(ldata, ldata->echo_head) = c;
smp_wmb(); /* Matches smp_rmb() in echo_buf(). */
ldata->echo_head++;
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
}
/**
* echo_move_back_col - add operation to move back a column
* @ldata: n_tty data
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
*
* Add an operation to the echo buffer to move back one column.
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
*/
static void echo_move_back_col(struct n_tty_data *ldata)
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
{
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(ECHO_OP_MOVE_BACK_COL, ldata);
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
}
/**
* echo_set_canon_col - add operation to set the canon column
* @ldata: n_tty data
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
*
* Add an operation to the echo buffer to set the canon column to the current
* column.
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
*/
static void echo_set_canon_col(struct n_tty_data *ldata)
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
{
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(ECHO_OP_SET_CANON_COL, ldata);
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
}
/**
* echo_erase_tab - add operation to erase a tab
* @num_chars: number of character columns already used
* @after_tab: true if num_chars starts after a previous tab
* @ldata: n_tty data
*
* Add an operation to the echo buffer to erase a tab.
*
* Called by the eraser function, which knows how many character columns have
* been used since either a previous tab or the start of input. This
* information will be used later, along with canon column (if applicable), to
* go back the correct number of columns.
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
*/
static void echo_erase_tab(unsigned int num_chars, int after_tab,
struct n_tty_data *ldata)
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
{
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(ECHO_OP_ERASE_TAB, ldata);
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
/* We only need to know this modulo 8 (tab spacing) */
num_chars &= 7;
/* Set the high bit as a flag if num_chars is after a previous tab */
if (after_tab)
num_chars |= 0x80;
add_echo_byte(num_chars, ldata);
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
}
/**
* echo_char_raw - echo a character raw
* @c: unicode byte to echo
* @ldata: line disc data
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
*
* Echo user input back onto the screen. This must be called only when
* L_ECHO(tty) is true. Called from the &tty_driver.receive_buf() path.
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
*
* This variant does not treat control characters specially.
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
*/
static void echo_char_raw(u8 c, struct n_tty_data *ldata)
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
{
if (c == ECHO_OP_START) {
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(ECHO_OP_START, ldata);
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
} else {
add_echo_byte(c, ldata);
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
}
}
/**
* echo_char - echo a character
* @c: unicode byte to echo
* @tty: terminal device
*
* Echo user input back onto the screen. This must be called only when
* L_ECHO(tty) is true. Called from the &tty_driver.receive_buf() path.
*
* This variant tags control characters to be echoed as "^X" (where X is the
* letter representing the control char).
*/
static void echo_char(u8 c, const struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
if (c == ECHO_OP_START) {
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(ECHO_OP_START, ldata);
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
} else {
if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t')
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(c, ldata);
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
}
}
/**
* finish_erasing - complete erase
* @ldata: n_tty data
*/
static inline void finish_erasing(struct n_tty_data *ldata)
{
if (ldata->erasing) {
echo_char_raw('/', ldata);
ldata->erasing = 0;
}
}
/**
* eraser - handle erase function
* @c: character input
* @tty: terminal device
*
* Perform erase and necessary output when an erase character is present in the
* stream from the driver layer. Handles the complexities of UTF-8 multibyte
* symbols.
*
* Locking: n_tty_receive_buf()/producer path:
* caller holds non-exclusive %termios_rwsem
*/
static void eraser(u8 c, const struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
enum { ERASE, WERASE, KILL } kill_type;
size_t head;
size_t cnt;
int seen_alnums;
if (ldata->read_head == ldata->canon_head) {
/* process_output('\a', tty); */ /* what do you think? */
return;
}
if (c == ERASE_CHAR(tty))
kill_type = ERASE;
else if (c == WERASE_CHAR(tty))
kill_type = WERASE;
else {
if (!L_ECHO(tty)) {
ldata->read_head = ldata->canon_head;
return;
}
if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) {
ldata->read_head = ldata->canon_head;
finish_erasing(ldata);
echo_char(KILL_CHAR(tty), tty);
/* Add a newline if ECHOK is on and ECHOKE is off. */
if (L_ECHOK(tty))
echo_char_raw('\n', ldata);
return;
}
kill_type = KILL;
}
seen_alnums = 0;
while (MASK(ldata->read_head) != MASK(ldata->canon_head)) {
head = ldata->read_head;
/* erase a single possibly multibyte character */
do {
head--;
c = read_buf(ldata, head);
} while (is_continuation(c, tty) &&
MASK(head) != MASK(ldata->canon_head));
/* do not partially erase */
if (is_continuation(c, tty))
break;
if (kill_type == WERASE) {
/* Equivalent to BSD's ALTWERASE. */
if (isalnum(c) || c == '_')
seen_alnums++;
else if (seen_alnums)
break;
}
cnt = ldata->read_head - head;
ldata->read_head = head;
if (L_ECHO(tty)) {
if (L_ECHOPRT(tty)) {
if (!ldata->erasing) {
echo_char_raw('\\', ldata);
ldata->erasing = 1;
}
/* if cnt > 1, output a multi-byte character */
echo_char(c, tty);
while (--cnt > 0) {
head++;
echo_char_raw(read_buf(ldata, head), ldata);
echo_move_back_col(ldata);
}
} else if (kill_type == ERASE && !L_ECHOE(tty)) {
echo_char(ERASE_CHAR(tty), tty);
} else if (c == '\t') {
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
unsigned int num_chars = 0;
int after_tab = 0;
size_t tail = ldata->read_head;
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
/*
* Count the columns used for characters
* since the start of input or after a
* previous tab.
* This info is used to go back the correct
* number of columns.
*/
while (MASK(tail) != MASK(ldata->canon_head)) {
tail--;
c = read_buf(ldata, tail);
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
if (c == '\t') {
after_tab = 1;
break;
} else if (iscntrl(c)) {
if (L_ECHOCTL(tty))
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
num_chars += 2;
} else if (!is_continuation(c, tty)) {
num_chars++;
}
}
echo_erase_tab(num_chars, after_tab, ldata);
} else {
if (iscntrl(c) && L_ECHOCTL(tty)) {
echo_char_raw('\b', ldata);
echo_char_raw(' ', ldata);
echo_char_raw('\b', ldata);
}
if (!iscntrl(c) || L_ECHOCTL(tty)) {
echo_char_raw('\b', ldata);
echo_char_raw(' ', ldata);
echo_char_raw('\b', ldata);
}
}
}
if (kill_type == ERASE)
break;
}
if (ldata->read_head == ldata->canon_head && L_ECHO(tty))
finish_erasing(ldata);
}
static void __isig(int sig, struct tty_struct *tty)
{
struct pid *tty_pgrp = tty_get_pgrp(tty);
if (tty_pgrp) {
kill_pgrp(tty_pgrp, sig, 1);
put_pid(tty_pgrp);
}
}
/**
* isig - handle the ISIG optio
* @sig: signal
* @tty: terminal
*
* Called when a signal is being sent due to terminal input. Called from the
* &tty_driver.receive_buf() path, so serialized.
*
* Performs input and output flush if !NOFLSH. In this context, the echo
* buffer is 'output'. The signal is processed first to alert any current
* readers or writers to discontinue and exit their i/o loops.
*
* Locking: %ctrl.lock
*/
static void isig(int sig, struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
if (L_NOFLSH(tty)) {
/* signal only */
__isig(sig, tty);
} else { /* signal and flush */
up_read(&tty->termios_rwsem);
down_write(&tty->termios_rwsem);
__isig(sig, tty);
/* clear echo buffer */
mutex_lock(&ldata->output_lock);
ldata->echo_head = ldata->echo_tail = 0;
ldata->echo_mark = ldata->echo_commit = 0;
mutex_unlock(&ldata->output_lock);
/* clear output buffer */
tty_driver_flush_buffer(tty);
/* clear input buffer */
reset_buffer_flags(tty->disc_data);
/* notify pty master of flush */
if (tty->link)
n_tty_packet_mode_flush(tty);
up_write(&tty->termios_rwsem);
down_read(&tty->termios_rwsem);
}
}
/**
* n_tty_receive_break - handle break
* @tty: terminal
*
* An RS232 break event has been hit in the incoming bitstream. This can cause
* a variety of events depending upon the termios settings.
*
* Locking: n_tty_receive_buf()/producer path:
* caller holds non-exclusive termios_rwsem
*
* Note: may get exclusive %termios_rwsem if flushing input buffer
*/
static void n_tty_receive_break(struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
if (I_IGNBRK(tty))
return;
if (I_BRKINT(tty)) {
isig(SIGINT, tty);
return;
}
if (I_PARMRK(tty)) {
put_tty_queue('\377', ldata);
put_tty_queue('\0', ldata);
}
put_tty_queue('\0', ldata);
}
/**
* n_tty_receive_overrun - handle overrun reporting
* @tty: terminal
*
* Data arrived faster than we could process it. While the tty driver has
* flagged this the bits that were missed are gone forever.
*
* Called from the receive_buf path so single threaded. Does not need locking
* as num_overrun and overrun_time are function private.
*/
static void n_tty_receive_overrun(const struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
ldata->num_overrun++;
if (time_is_before_jiffies(ldata->overrun_time + HZ)) {
tty_warn(tty, "%u input overrun(s)\n", ldata->num_overrun);
ldata->overrun_time = jiffies;
ldata->num_overrun = 0;
}
}
/**
* n_tty_receive_parity_error - error notifier
* @tty: terminal device
* @c: character
*
* Process a parity error and queue the right data to indicate the error case
* if necessary.
*
* Locking: n_tty_receive_buf()/producer path:
* caller holds non-exclusive %termios_rwsem
*/
static void n_tty_receive_parity_error(const struct tty_struct *tty,
u8 c)
{
struct n_tty_data *ldata = tty->disc_data;
if (I_INPCK(tty)) {
if (I_IGNPAR(tty))
return;
if (I_PARMRK(tty)) {
put_tty_queue('\377', ldata);
put_tty_queue('\0', ldata);
put_tty_queue(c, ldata);
} else
put_tty_queue('\0', ldata);
} else
put_tty_queue(c, ldata);
}
static void
n_tty_receive_signal_char(struct tty_struct *tty, int signal, u8 c)
{
isig(signal, tty);
if (I_IXON(tty))
start_tty(tty);
if (L_ECHO(tty)) {
echo_char(c, tty);
commit_echoes(tty);
} else
process_echoes(tty);
}
static bool n_tty_is_char_flow_ctrl(struct tty_struct *tty, u8 c)
{
return c == START_CHAR(tty) || c == STOP_CHAR(tty);
}
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>
2022-06-06 23:36:51 +08:00
/**
* 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, u8 c,
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>
2022-06-06 23:36:51 +08:00
bool lookahead_done)
{
if (!n_tty_is_char_flow_ctrl(tty, c))
return false;
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>
2022-06-06 23:36:51 +08:00
if (lookahead_done)
return true;
if (c == START_CHAR(tty)) {
start_tty(tty);
process_echoes(tty);
return true;
}
/* STOP_CHAR */
stop_tty(tty);
return true;
}
static void n_tty_receive_handle_newline(struct tty_struct *tty, u8 c)
{
struct n_tty_data *ldata = tty->disc_data;
set_bit(MASK(ldata->read_head), ldata->read_flags);
put_tty_queue(c, ldata);
smp_store_release(&ldata->canon_head, ldata->read_head);
kill_fasync(&tty->fasync, SIGIO, POLL_IN);
wake_up_interruptible_poll(&tty->read_wait, EPOLLIN | EPOLLRDNORM);
}
static bool n_tty_receive_char_canon(struct tty_struct *tty, u8 c)
{
struct n_tty_data *ldata = tty->disc_data;
if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) ||
(c == WERASE_CHAR(tty) && L_IEXTEN(tty))) {
eraser(c, tty);
commit_echoes(tty);
return true;
}
if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) {
ldata->lnext = 1;
if (L_ECHO(tty)) {
finish_erasing(ldata);
if (L_ECHOCTL(tty)) {
echo_char_raw('^', ldata);
echo_char_raw('\b', ldata);
commit_echoes(tty);
}
}
return true;
}
if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && L_IEXTEN(tty)) {
size_t tail = ldata->canon_head;
finish_erasing(ldata);
echo_char(c, tty);
echo_char_raw('\n', ldata);
while (MASK(tail) != MASK(ldata->read_head)) {
echo_char(read_buf(ldata, tail), tty);
tail++;
}
commit_echoes(tty);
return true;
}
if (c == '\n') {
if (L_ECHO(tty) || L_ECHONL(tty)) {
echo_char_raw('\n', ldata);
commit_echoes(tty);
}
n_tty_receive_handle_newline(tty, c);
return true;
}
if (c == EOF_CHAR(tty)) {
c = __DISABLED_CHAR;
n_tty_receive_handle_newline(tty, c);
return true;
}
if ((c == EOL_CHAR(tty)) ||
(c == EOL2_CHAR(tty) && L_IEXTEN(tty))) {
/*
* XXX are EOL_CHAR and EOL2_CHAR echoed?!?
*/
if (L_ECHO(tty)) {
/* Record the column of first canon char. */
if (ldata->canon_head == ldata->read_head)
echo_set_canon_col(ldata);
echo_char(c, tty);
commit_echoes(tty);
}
/*
* XXX does PARMRK doubling happen for
* EOL_CHAR and EOL2_CHAR?
*/
if (c == '\377' && I_PARMRK(tty))
put_tty_queue(c, ldata);
n_tty_receive_handle_newline(tty, c);
return true;
}
return false;
}
static void n_tty_receive_char_special(struct tty_struct *tty, u8 c,
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>
2022-06-06 23:36:51 +08:00
bool lookahead_done)
{
struct n_tty_data *ldata = tty->disc_data;
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>
2022-06-06 23:36:51 +08:00
if (I_IXON(tty) && n_tty_receive_char_flow_ctrl(tty, c, lookahead_done))
return;
if (L_ISIG(tty)) {
if (c == INTR_CHAR(tty)) {
n_tty_receive_signal_char(tty, SIGINT, c);
return;
} else if (c == QUIT_CHAR(tty)) {
n_tty_receive_signal_char(tty, SIGQUIT, c);
return;
} else if (c == SUSP_CHAR(tty)) {
n_tty_receive_signal_char(tty, SIGTSTP, c);
return;
}
}
tty: cumulate and document tty_struct::flow* members Group the flow flags under a single struct called flow. The new struct contains 'stopped' and 'tco_stopped' bools which used to be bits in a bitfield. The struct also contains the lock protecting them to potentially share the same cache line. Note that commit c545b66c6922b (tty: Serialize tcflow() with other tty flow control changes) added a padding to the original bitfield. It was for the bitfield to occupy a whole 64b word to avoid interferring stores on Alpha (cannot we evaporate this arch with weird implications to C code yet?). But it doesn't work as expected as the padding (tty_struct::unused) is aligned to a 8B boundary too and occupies some bytes from the next word. So make it reliable by: 1) setting __aligned of the struct -- that aligns the start, and 2) making 'unsigned long unused[0]' as the last member of the struct -- pads the end. This is also the perfect time to start the documentation of tty_struct where all this lives. So we start by documenting what these bools actually serve for. And why we do all the alignment dances. Only the few up-to-date information from the Theodore's comment made it into this new Kerneldoc comment. Signed-off-by: Jiri Slaby <jslaby@suse.cz> Cc: "David S. Miller" <davem@davemloft.net> Cc: Jakub Kicinski <kuba@kernel.org> Cc: Jonathan Corbet <corbet@lwn.net> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Ulf Hansson <ulf.hansson@linaro.org> Cc: Heiko Carstens <hca@linux.ibm.com> Cc: Vasily Gorbik <gor@linux.ibm.com> Cc: Christian Borntraeger <borntraeger@de.ibm.com> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Vineet Gupta <vgupta@synopsys.com> Cc: "Maciej W. Rozycki" <macro@orcam.me.uk> Link: https://lore.kernel.org/r/20210505091928.22010-13-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-05-05 17:19:05 +08:00
if (tty->flow.stopped && !tty->flow.tco_stopped && I_IXON(tty) && I_IXANY(tty)) {
start_tty(tty);
process_echoes(tty);
}
if (c == '\r') {
if (I_IGNCR(tty))
return;
if (I_ICRNL(tty))
c = '\n';
} else if (c == '\n' && I_INLCR(tty))
c = '\r';
if (ldata->icanon && n_tty_receive_char_canon(tty, c))
return;
if (L_ECHO(tty)) {
finish_erasing(ldata);
if (c == '\n')
echo_char_raw('\n', ldata);
else {
/* Record the column of first canon char. */
if (ldata->canon_head == ldata->read_head)
echo_set_canon_col(ldata);
echo_char(c, tty);
}
commit_echoes(tty);
}
/* PARMRK doubling check */
if (c == '\377' && I_PARMRK(tty))
put_tty_queue(c, ldata);
put_tty_queue(c, ldata);
}
/**
* n_tty_receive_char - perform processing
* @tty: terminal device
* @c: character
*
* Process an individual character of input received from the driver. This is
* serialized with respect to itself by the rules for the driver above.
*
* Locking: n_tty_receive_buf()/producer path:
* caller holds non-exclusive %termios_rwsem
* publishes canon_head if canonical mode is active
*/
static void n_tty_receive_char(struct tty_struct *tty, u8 c)
{
struct n_tty_data *ldata = tty->disc_data;
tty: cumulate and document tty_struct::flow* members Group the flow flags under a single struct called flow. The new struct contains 'stopped' and 'tco_stopped' bools which used to be bits in a bitfield. The struct also contains the lock protecting them to potentially share the same cache line. Note that commit c545b66c6922b (tty: Serialize tcflow() with other tty flow control changes) added a padding to the original bitfield. It was for the bitfield to occupy a whole 64b word to avoid interferring stores on Alpha (cannot we evaporate this arch with weird implications to C code yet?). But it doesn't work as expected as the padding (tty_struct::unused) is aligned to a 8B boundary too and occupies some bytes from the next word. So make it reliable by: 1) setting __aligned of the struct -- that aligns the start, and 2) making 'unsigned long unused[0]' as the last member of the struct -- pads the end. This is also the perfect time to start the documentation of tty_struct where all this lives. So we start by documenting what these bools actually serve for. And why we do all the alignment dances. Only the few up-to-date information from the Theodore's comment made it into this new Kerneldoc comment. Signed-off-by: Jiri Slaby <jslaby@suse.cz> Cc: "David S. Miller" <davem@davemloft.net> Cc: Jakub Kicinski <kuba@kernel.org> Cc: Jonathan Corbet <corbet@lwn.net> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Ulf Hansson <ulf.hansson@linaro.org> Cc: Heiko Carstens <hca@linux.ibm.com> Cc: Vasily Gorbik <gor@linux.ibm.com> Cc: Christian Borntraeger <borntraeger@de.ibm.com> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Vineet Gupta <vgupta@synopsys.com> Cc: "Maciej W. Rozycki" <macro@orcam.me.uk> Link: https://lore.kernel.org/r/20210505091928.22010-13-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-05-05 17:19:05 +08:00
if (tty->flow.stopped && !tty->flow.tco_stopped && I_IXON(tty) && I_IXANY(tty)) {
start_tty(tty);
process_echoes(tty);
}
if (L_ECHO(tty)) {
finish_erasing(ldata);
/* Record the column of first canon char. */
if (ldata->canon_head == ldata->read_head)
echo_set_canon_col(ldata);
echo_char(c, tty);
commit_echoes(tty);
}
/* PARMRK doubling check */
if (c == '\377' && I_PARMRK(tty))
put_tty_queue(c, ldata);
put_tty_queue(c, ldata);
}
static void n_tty_receive_char_closing(struct tty_struct *tty, u8 c,
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>
2022-06-06 23:36:51 +08:00
bool lookahead_done)
{
if (I_ISTRIP(tty))
c &= 0x7f;
if (I_IUCLC(tty) && L_IEXTEN(tty))
c = tolower(c);
if (I_IXON(tty)) {
if (!n_tty_receive_char_flow_ctrl(tty, c, lookahead_done) &&
tty->flow.stopped && !tty->flow.tco_stopped && I_IXANY(tty) &&
c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) &&
c != SUSP_CHAR(tty)) {
start_tty(tty);
process_echoes(tty);
}
}
}
static void
n_tty_receive_char_flagged(struct tty_struct *tty, u8 c, u8 flag)
{
switch (flag) {
case TTY_BREAK:
n_tty_receive_break(tty);
break;
case TTY_PARITY:
case TTY_FRAME:
n_tty_receive_parity_error(tty, c);
break;
case TTY_OVERRUN:
n_tty_receive_overrun(tty);
break;
default:
tty_err(tty, "unknown flag %u\n", flag);
break;
}
}
static void
n_tty_receive_char_lnext(struct tty_struct *tty, u8 c, u8 flag)
{
struct n_tty_data *ldata = tty->disc_data;
ldata->lnext = 0;
if (likely(flag == TTY_NORMAL)) {
if (I_ISTRIP(tty))
c &= 0x7f;
if (I_IUCLC(tty) && L_IEXTEN(tty))
c = tolower(c);
n_tty_receive_char(tty, c);
} else
n_tty_receive_char_flagged(tty, c, flag);
}
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>
2022-06-06 23:36:51 +08:00
/* Caller must ensure count > 0 */
static void n_tty_lookahead_flow_ctrl(struct tty_struct *tty, const u8 *cp,
tty: use u8 for flags This makes all those 'char's an explicit 'u8'. This is part of the continuing unification of chars and flags to be consistent u8. This approaches tty_port_default_receive_buf(). Note that we do not change signedness as we compile with -funsigned-char. Signed-off-by: "Jiri Slaby (SUSE)" <jirislaby@kernel.org> Cc: William Hubbs <w.d.hubbs@gmail.com> Cc: Chris Brannon <chris@the-brannons.com> Cc: Kirk Reiser <kirk@reisers.ca> Cc: Samuel Thibault <samuel.thibault@ens-lyon.org> Cc: Marcel Holtmann <marcel@holtmann.org> Cc: Johan Hedberg <johan.hedberg@gmail.com> Cc: Luiz Augusto von Dentz <luiz.dentz@gmail.com> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com> Cc: Arnd Bergmann <arnd@arndb.de> Cc: "David S. Miller" <davem@davemloft.net> Cc: Eric Dumazet <edumazet@google.com> Cc: Jakub Kicinski <kuba@kernel.org> Cc: Paolo Abeni <pabeni@redhat.com> Cc: Max Staudt <max@enpas.org> Cc: Wolfgang Grandegger <wg@grandegger.com> Cc: Marc Kleine-Budde <mkl@pengutronix.de> Cc: Dario Binacchi <dario.binacchi@amarulasolutions.com> Cc: Andreas Koensgen <ajk@comnets.uni-bremen.de> Cc: Jeremy Kerr <jk@codeconstruct.com.au> Cc: Matt Johnston <matt@codeconstruct.com.au> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Cc: Liam Girdwood <lgirdwood@gmail.com> Cc: Mark Brown <broonie@kernel.org> Cc: Jaroslav Kysela <perex@perex.cz> Cc: Takashi Iwai <tiwai@suse.com> Acked-by: Mark Brown <broonie@kernel.org> Link: https://lore.kernel.org/r/20230810091510.13006-18-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-10 17:14:51 +08:00
const u8 *fp, size_t count)
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>
2022-06-06 23:36:51 +08:00
{
struct n_tty_data *ldata = tty->disc_data;
u8 flag = TTY_NORMAL;
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>
2022-06-06 23:36:51 +08:00
ldata->lookahead_count += count;
if (!I_IXON(tty))
return;
while (count--) {
if (fp)
flag = *fp++;
if (likely(flag == TTY_NORMAL))
n_tty_receive_char_flow_ctrl(tty, *cp, false);
cp++;
}
}
static void
n_tty_receive_buf_real_raw(const struct tty_struct *tty, const u8 *cp,
size_t count)
{
struct n_tty_data *ldata = tty->disc_data;
/* handle buffer wrap-around by a loop */
for (unsigned int i = 0; i < 2; i++) {
size_t head = MASK(ldata->read_head);
size_t n = min(count, N_TTY_BUF_SIZE - head);
memcpy(read_buf_addr(ldata, head), cp, n);
ldata->read_head += n;
cp += n;
count -= n;
}
}
static void
tty: use u8 for flags This makes all those 'char's an explicit 'u8'. This is part of the continuing unification of chars and flags to be consistent u8. This approaches tty_port_default_receive_buf(). Note that we do not change signedness as we compile with -funsigned-char. Signed-off-by: "Jiri Slaby (SUSE)" <jirislaby@kernel.org> Cc: William Hubbs <w.d.hubbs@gmail.com> Cc: Chris Brannon <chris@the-brannons.com> Cc: Kirk Reiser <kirk@reisers.ca> Cc: Samuel Thibault <samuel.thibault@ens-lyon.org> Cc: Marcel Holtmann <marcel@holtmann.org> Cc: Johan Hedberg <johan.hedberg@gmail.com> Cc: Luiz Augusto von Dentz <luiz.dentz@gmail.com> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com> Cc: Arnd Bergmann <arnd@arndb.de> Cc: "David S. Miller" <davem@davemloft.net> Cc: Eric Dumazet <edumazet@google.com> Cc: Jakub Kicinski <kuba@kernel.org> Cc: Paolo Abeni <pabeni@redhat.com> Cc: Max Staudt <max@enpas.org> Cc: Wolfgang Grandegger <wg@grandegger.com> Cc: Marc Kleine-Budde <mkl@pengutronix.de> Cc: Dario Binacchi <dario.binacchi@amarulasolutions.com> Cc: Andreas Koensgen <ajk@comnets.uni-bremen.de> Cc: Jeremy Kerr <jk@codeconstruct.com.au> Cc: Matt Johnston <matt@codeconstruct.com.au> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Cc: Liam Girdwood <lgirdwood@gmail.com> Cc: Mark Brown <broonie@kernel.org> Cc: Jaroslav Kysela <perex@perex.cz> Cc: Takashi Iwai <tiwai@suse.com> Acked-by: Mark Brown <broonie@kernel.org> Link: https://lore.kernel.org/r/20230810091510.13006-18-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-10 17:14:51 +08:00
n_tty_receive_buf_raw(struct tty_struct *tty, const u8 *cp, const u8 *fp,
size_t count)
{
struct n_tty_data *ldata = tty->disc_data;
tty: use u8 for flags This makes all those 'char's an explicit 'u8'. This is part of the continuing unification of chars and flags to be consistent u8. This approaches tty_port_default_receive_buf(). Note that we do not change signedness as we compile with -funsigned-char. Signed-off-by: "Jiri Slaby (SUSE)" <jirislaby@kernel.org> Cc: William Hubbs <w.d.hubbs@gmail.com> Cc: Chris Brannon <chris@the-brannons.com> Cc: Kirk Reiser <kirk@reisers.ca> Cc: Samuel Thibault <samuel.thibault@ens-lyon.org> Cc: Marcel Holtmann <marcel@holtmann.org> Cc: Johan Hedberg <johan.hedberg@gmail.com> Cc: Luiz Augusto von Dentz <luiz.dentz@gmail.com> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com> Cc: Arnd Bergmann <arnd@arndb.de> Cc: "David S. Miller" <davem@davemloft.net> Cc: Eric Dumazet <edumazet@google.com> Cc: Jakub Kicinski <kuba@kernel.org> Cc: Paolo Abeni <pabeni@redhat.com> Cc: Max Staudt <max@enpas.org> Cc: Wolfgang Grandegger <wg@grandegger.com> Cc: Marc Kleine-Budde <mkl@pengutronix.de> Cc: Dario Binacchi <dario.binacchi@amarulasolutions.com> Cc: Andreas Koensgen <ajk@comnets.uni-bremen.de> Cc: Jeremy Kerr <jk@codeconstruct.com.au> Cc: Matt Johnston <matt@codeconstruct.com.au> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Cc: Liam Girdwood <lgirdwood@gmail.com> Cc: Mark Brown <broonie@kernel.org> Cc: Jaroslav Kysela <perex@perex.cz> Cc: Takashi Iwai <tiwai@suse.com> Acked-by: Mark Brown <broonie@kernel.org> Link: https://lore.kernel.org/r/20230810091510.13006-18-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-10 17:14:51 +08:00
u8 flag = TTY_NORMAL;
while (count--) {
if (fp)
flag = *fp++;
if (likely(flag == TTY_NORMAL))
put_tty_queue(*cp++, ldata);
else
n_tty_receive_char_flagged(tty, *cp++, flag);
}
}
static void
tty: use u8 for flags This makes all those 'char's an explicit 'u8'. This is part of the continuing unification of chars and flags to be consistent u8. This approaches tty_port_default_receive_buf(). Note that we do not change signedness as we compile with -funsigned-char. Signed-off-by: "Jiri Slaby (SUSE)" <jirislaby@kernel.org> Cc: William Hubbs <w.d.hubbs@gmail.com> Cc: Chris Brannon <chris@the-brannons.com> Cc: Kirk Reiser <kirk@reisers.ca> Cc: Samuel Thibault <samuel.thibault@ens-lyon.org> Cc: Marcel Holtmann <marcel@holtmann.org> Cc: Johan Hedberg <johan.hedberg@gmail.com> Cc: Luiz Augusto von Dentz <luiz.dentz@gmail.com> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com> Cc: Arnd Bergmann <arnd@arndb.de> Cc: "David S. Miller" <davem@davemloft.net> Cc: Eric Dumazet <edumazet@google.com> Cc: Jakub Kicinski <kuba@kernel.org> Cc: Paolo Abeni <pabeni@redhat.com> Cc: Max Staudt <max@enpas.org> Cc: Wolfgang Grandegger <wg@grandegger.com> Cc: Marc Kleine-Budde <mkl@pengutronix.de> Cc: Dario Binacchi <dario.binacchi@amarulasolutions.com> Cc: Andreas Koensgen <ajk@comnets.uni-bremen.de> Cc: Jeremy Kerr <jk@codeconstruct.com.au> Cc: Matt Johnston <matt@codeconstruct.com.au> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Cc: Liam Girdwood <lgirdwood@gmail.com> Cc: Mark Brown <broonie@kernel.org> Cc: Jaroslav Kysela <perex@perex.cz> Cc: Takashi Iwai <tiwai@suse.com> Acked-by: Mark Brown <broonie@kernel.org> Link: https://lore.kernel.org/r/20230810091510.13006-18-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-10 17:14:51 +08:00
n_tty_receive_buf_closing(struct tty_struct *tty, const u8 *cp, const u8 *fp,
size_t count, bool lookahead_done)
{
u8 flag = TTY_NORMAL;
while (count--) {
if (fp)
flag = *fp++;
if (likely(flag == TTY_NORMAL))
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>
2022-06-06 23:36:51 +08:00
n_tty_receive_char_closing(tty, *cp++, lookahead_done);
}
}
static void n_tty_receive_buf_standard(struct tty_struct *tty, const u8 *cp,
const u8 *fp, size_t count,
bool lookahead_done)
{
struct n_tty_data *ldata = tty->disc_data;
tty: use u8 for flags This makes all those 'char's an explicit 'u8'. This is part of the continuing unification of chars and flags to be consistent u8. This approaches tty_port_default_receive_buf(). Note that we do not change signedness as we compile with -funsigned-char. Signed-off-by: "Jiri Slaby (SUSE)" <jirislaby@kernel.org> Cc: William Hubbs <w.d.hubbs@gmail.com> Cc: Chris Brannon <chris@the-brannons.com> Cc: Kirk Reiser <kirk@reisers.ca> Cc: Samuel Thibault <samuel.thibault@ens-lyon.org> Cc: Marcel Holtmann <marcel@holtmann.org> Cc: Johan Hedberg <johan.hedberg@gmail.com> Cc: Luiz Augusto von Dentz <luiz.dentz@gmail.com> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com> Cc: Arnd Bergmann <arnd@arndb.de> Cc: "David S. Miller" <davem@davemloft.net> Cc: Eric Dumazet <edumazet@google.com> Cc: Jakub Kicinski <kuba@kernel.org> Cc: Paolo Abeni <pabeni@redhat.com> Cc: Max Staudt <max@enpas.org> Cc: Wolfgang Grandegger <wg@grandegger.com> Cc: Marc Kleine-Budde <mkl@pengutronix.de> Cc: Dario Binacchi <dario.binacchi@amarulasolutions.com> Cc: Andreas Koensgen <ajk@comnets.uni-bremen.de> Cc: Jeremy Kerr <jk@codeconstruct.com.au> Cc: Matt Johnston <matt@codeconstruct.com.au> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Cc: Liam Girdwood <lgirdwood@gmail.com> Cc: Mark Brown <broonie@kernel.org> Cc: Jaroslav Kysela <perex@perex.cz> Cc: Takashi Iwai <tiwai@suse.com> Acked-by: Mark Brown <broonie@kernel.org> Link: https://lore.kernel.org/r/20230810091510.13006-18-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-10 17:14:51 +08:00
u8 flag = TTY_NORMAL;
while (count--) {
u8 c = *cp++;
if (fp)
flag = *fp++;
if (ldata->lnext) {
n_tty_receive_char_lnext(tty, c, flag);
continue;
}
if (unlikely(flag != TTY_NORMAL)) {
n_tty_receive_char_flagged(tty, c, flag);
continue;
}
if (I_ISTRIP(tty))
c &= 0x7f;
if (I_IUCLC(tty) && L_IEXTEN(tty))
c = tolower(c);
if (L_EXTPROC(tty)) {
put_tty_queue(c, ldata);
continue;
}
if (test_bit(c, ldata->char_map))
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>
2022-06-06 23:36:51 +08:00
n_tty_receive_char_special(tty, c, lookahead_done);
else
n_tty_receive_char(tty, c);
}
}
tty: use u8 for flags This makes all those 'char's an explicit 'u8'. This is part of the continuing unification of chars and flags to be consistent u8. This approaches tty_port_default_receive_buf(). Note that we do not change signedness as we compile with -funsigned-char. Signed-off-by: "Jiri Slaby (SUSE)" <jirislaby@kernel.org> Cc: William Hubbs <w.d.hubbs@gmail.com> Cc: Chris Brannon <chris@the-brannons.com> Cc: Kirk Reiser <kirk@reisers.ca> Cc: Samuel Thibault <samuel.thibault@ens-lyon.org> Cc: Marcel Holtmann <marcel@holtmann.org> Cc: Johan Hedberg <johan.hedberg@gmail.com> Cc: Luiz Augusto von Dentz <luiz.dentz@gmail.com> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com> Cc: Arnd Bergmann <arnd@arndb.de> Cc: "David S. Miller" <davem@davemloft.net> Cc: Eric Dumazet <edumazet@google.com> Cc: Jakub Kicinski <kuba@kernel.org> Cc: Paolo Abeni <pabeni@redhat.com> Cc: Max Staudt <max@enpas.org> Cc: Wolfgang Grandegger <wg@grandegger.com> Cc: Marc Kleine-Budde <mkl@pengutronix.de> Cc: Dario Binacchi <dario.binacchi@amarulasolutions.com> Cc: Andreas Koensgen <ajk@comnets.uni-bremen.de> Cc: Jeremy Kerr <jk@codeconstruct.com.au> Cc: Matt Johnston <matt@codeconstruct.com.au> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Cc: Liam Girdwood <lgirdwood@gmail.com> Cc: Mark Brown <broonie@kernel.org> Cc: Jaroslav Kysela <perex@perex.cz> Cc: Takashi Iwai <tiwai@suse.com> Acked-by: Mark Brown <broonie@kernel.org> Link: https://lore.kernel.org/r/20230810091510.13006-18-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-10 17:14:51 +08:00
static void __receive_buf(struct tty_struct *tty, const u8 *cp, const u8 *fp,
size_t count)
{
struct n_tty_data *ldata = tty->disc_data;
bool preops = I_ISTRIP(tty) || (I_IUCLC(tty) && L_IEXTEN(tty));
size_t la_count = min(ldata->lookahead_count, count);
if (ldata->real_raw)
n_tty_receive_buf_real_raw(tty, cp, count);
else if (ldata->raw || (L_EXTPROC(tty) && !preops))
n_tty_receive_buf_raw(tty, cp, fp, count);
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>
2022-06-06 23:36:51 +08:00
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);
flush_echoes(tty);
if (tty->ops->flush_chars)
tty->ops->flush_chars(tty);
}
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>
2022-06-06 23:36:51 +08:00
ldata->lookahead_count -= la_count;
if (ldata->icanon && !L_EXTPROC(tty))
return;
/* publish read_head to consumer */
smp_store_release(&ldata->commit_head, ldata->read_head);
if (read_cnt(ldata)) {
kill_fasync(&tty->fasync, SIGIO, POLL_IN);
n_tty: wake up poll(POLLRDNORM) on receiving data The poll man page says POLLRDNORM is equivalent to POLLIN when used as an event. $ man poll <snip> POLLRDNORM Equivalent to POLLIN. However, in n_tty driver, POLLRDNORM does not return until timeout even if there is terminal input, whereas POLLIN returns. The following test program works until kernel-3.17, but the test stops in poll() after commit 57087d515441 ("tty: Fix spurious poll() wakeups"). [Steps to run test program] $ cc -o test-pollrdnorm test-pollrdnorm.c $ ./test-pollrdnorm foo <-- Type in something from the terminal followed by [RET]. The string should be echoed back. ------------------------< test-pollrdnorm.c >------------------------ #include <stdio.h> #include <errno.h> #include <poll.h> #include <unistd.h> void main(void) { int n; unsigned char buf[8]; struct pollfd fds[1] = {{ 0, POLLRDNORM, 0 }}; n = poll(fds, 1, -1); if (n < 0) perror("poll"); n = read(0, buf, 8); if (n < 0) perror("read"); if (n > 0) write(1, buf, n); } ------------------------------------------------------------------------ The attached patch fixes this problem. Many calls to wake_up_interruptible_poll() in the kernel source code already specify "POLLIN | POLLRDNORM". Fixes: 57087d515441 ("tty: Fix spurious poll() wakeups") Cc: stable@vger.kernel.org Signed-off-by: Kosuke Tatsukawa <tatsu-ab1@nec.com> Link: https://lore.kernel.org/r/TYCPR01MB81901C0F932203D30E452B3EA5209@TYCPR01MB8190.jpnprd01.prod.outlook.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-01-27 07:35:02 +08:00
wake_up_interruptible_poll(&tty->read_wait, EPOLLIN | EPOLLRDNORM);
}
}
n_tty: Fix read buffer overwrite when no newline In canon mode, the read buffer head will advance over the buffer tail if the input > 4095 bytes without receiving a line termination char. Discard additional input until a line termination is received. Before evaluating for overflow, the 'room' value is normalized for I_PARMRK and 1 byte is reserved for line termination (even in !icanon mode, in case the mode is switched). The following table shows the transform: actual buffer | 'room' value before overflow calc space avail | !I_PARMRK | I_PARMRK -------------------------------------------------- 0 | -1 | -1 1 | 0 | 0 2 | 1 | 0 3 | 2 | 0 4+ | 3 | 1 When !icanon or when icanon and the read buffer contains newlines, normalized 'room' values of -1 and 0 are clamped to 0, and 'overflow' is 0, so read_head is not adjusted and the input i/o loop exits (setting no_room if called from flush_to_ldisc()). No input is discarded since the reader does have input available to read which ensures forward progress. When icanon and the read buffer does not contain newlines and the normalized 'room' value is 0, then overflow and room are reset to 1, so that the i/o loop will process the next input char normally (except for parity errors which are ignored). Thus, erasures, signalling chars, 7-bit mode, etc. will continue to be handled properly. If the input char processed was not a line termination char, then the canon_head index will not have advanced, so the normalized 'room' value will now be -1 and 'overflow' will be set, which indicates the read_head can safely be reset, effectively erasing the last char processed. If the input char processed was a line termination, then the canon_head index will have advanced, so 'overflow' is cleared to 0, the read_head is not reset, and 'room' is cleared to 0, which exits the i/o loop (because the reader now have input available to read which ensures forward progress). Note that it is possible for a line termination to be received, and for the reader to copy the line to the user buffer before the input i/o loop is ready to process the next input char. This is why the i/o loop recomputes the room/overflow state with every input char while handling overflow. Finally, if the input data was processed without receiving a line termination (so that overflow is still set), the pty driver must receive a write wakeup. A pty writer may be waiting to write more data in n_tty_write() but without unthrottling here that wakeup will not arrive, and forward progress will halt. (Normally, the pty writer is woken when the reader reads data out of the buffer and more space become available). Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-01-17 04:05:39 +08:00
/**
* n_tty_receive_buf_common - process input
* @tty: device to receive input
* @cp: input chars
* @fp: flags for each char (if %NULL, all chars are %TTY_NORMAL)
* @count: number of input chars in @cp
* @flow: enable flow control
*
* Called by the terminal driver when a block of characters has been received.
* This function must be called from soft contexts not from interrupt context.
* The driver is responsible for making calls one at a time and in order (or
* using flush_to_ldisc()).
*
* Returns: the # of input chars from @cp which were processed.
*
* In canonical mode, the maximum line length is 4096 chars (including the line
* termination char); lines longer than 4096 chars are truncated. After 4095
* chars, input data is still processed but not stored. Overflow processing
* ensures the tty can always receive more input until at least one line can be
* read.
*
* In non-canonical mode, the read buffer will only accept 4095 chars; this
* provides the necessary space for a newline char if the input mode is
* switched to canonical.
*
* Note it is possible for the read buffer to _contain_ 4096 chars in
* non-canonical mode: the read buffer could already contain the maximum canon
* line of 4096 chars when the mode is switched to non-canonical.
*
* Locking: n_tty_receive_buf()/producer path:
* claims non-exclusive %termios_rwsem
* publishes commit_head or canon_head
n_tty: Fix read buffer overwrite when no newline In canon mode, the read buffer head will advance over the buffer tail if the input > 4095 bytes without receiving a line termination char. Discard additional input until a line termination is received. Before evaluating for overflow, the 'room' value is normalized for I_PARMRK and 1 byte is reserved for line termination (even in !icanon mode, in case the mode is switched). The following table shows the transform: actual buffer | 'room' value before overflow calc space avail | !I_PARMRK | I_PARMRK -------------------------------------------------- 0 | -1 | -1 1 | 0 | 0 2 | 1 | 0 3 | 2 | 0 4+ | 3 | 1 When !icanon or when icanon and the read buffer contains newlines, normalized 'room' values of -1 and 0 are clamped to 0, and 'overflow' is 0, so read_head is not adjusted and the input i/o loop exits (setting no_room if called from flush_to_ldisc()). No input is discarded since the reader does have input available to read which ensures forward progress. When icanon and the read buffer does not contain newlines and the normalized 'room' value is 0, then overflow and room are reset to 1, so that the i/o loop will process the next input char normally (except for parity errors which are ignored). Thus, erasures, signalling chars, 7-bit mode, etc. will continue to be handled properly. If the input char processed was not a line termination char, then the canon_head index will not have advanced, so the normalized 'room' value will now be -1 and 'overflow' will be set, which indicates the read_head can safely be reset, effectively erasing the last char processed. If the input char processed was a line termination, then the canon_head index will have advanced, so 'overflow' is cleared to 0, the read_head is not reset, and 'room' is cleared to 0, which exits the i/o loop (because the reader now have input available to read which ensures forward progress). Note that it is possible for a line termination to be received, and for the reader to copy the line to the user buffer before the input i/o loop is ready to process the next input char. This is why the i/o loop recomputes the room/overflow state with every input char while handling overflow. Finally, if the input data was processed without receiving a line termination (so that overflow is still set), the pty driver must receive a write wakeup. A pty writer may be waiting to write more data in n_tty_write() but without unthrottling here that wakeup will not arrive, and forward progress will halt. (Normally, the pty writer is woken when the reader reads data out of the buffer and more space become available). Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-01-17 04:05:39 +08:00
*/
tty: make tty_ldisc_ops::*buf*() hooks operate on size_t Count passed to tty_ldisc_ops::receive_buf*(), ::lookahead_buf(), and returned from ::receive_buf2() is expected to be size_t. So set it to size_t to unify with the rest of the code. Signed-off-by: "Jiri Slaby (SUSE)" <jirislaby@kernel.org> Cc: William Hubbs <w.d.hubbs@gmail.com> Cc: Chris Brannon <chris@the-brannons.com> Cc: Kirk Reiser <kirk@reisers.ca> Cc: Samuel Thibault <samuel.thibault@ens-lyon.org> Cc: Marcel Holtmann <marcel@holtmann.org> Cc: Johan Hedberg <johan.hedberg@gmail.com> Cc: Luiz Augusto von Dentz <luiz.dentz@gmail.com> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com> Cc: Arnd Bergmann <arnd@arndb.de> Cc: "David S. Miller" <davem@davemloft.net> Cc: Eric Dumazet <edumazet@google.com> Cc: Jakub Kicinski <kuba@kernel.org> Cc: Paolo Abeni <pabeni@redhat.com> Cc: Max Staudt <max@enpas.org> Cc: Wolfgang Grandegger <wg@grandegger.com> Cc: Marc Kleine-Budde <mkl@pengutronix.de> Cc: Dario Binacchi <dario.binacchi@amarulasolutions.com> Cc: Andreas Koensgen <ajk@comnets.uni-bremen.de> Cc: Jeremy Kerr <jk@codeconstruct.com.au> Cc: Matt Johnston <matt@codeconstruct.com.au> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Cc: Liam Girdwood <lgirdwood@gmail.com> Cc: Mark Brown <broonie@kernel.org> Cc: Jaroslav Kysela <perex@perex.cz> Cc: Takashi Iwai <tiwai@suse.com> Acked-by: Mark Brown <broonie@kernel.org> Link: https://lore.kernel.org/r/20230810091510.13006-16-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-10 17:14:49 +08:00
static size_t
tty: use u8 for flags This makes all those 'char's an explicit 'u8'. This is part of the continuing unification of chars and flags to be consistent u8. This approaches tty_port_default_receive_buf(). Note that we do not change signedness as we compile with -funsigned-char. Signed-off-by: "Jiri Slaby (SUSE)" <jirislaby@kernel.org> Cc: William Hubbs <w.d.hubbs@gmail.com> Cc: Chris Brannon <chris@the-brannons.com> Cc: Kirk Reiser <kirk@reisers.ca> Cc: Samuel Thibault <samuel.thibault@ens-lyon.org> Cc: Marcel Holtmann <marcel@holtmann.org> Cc: Johan Hedberg <johan.hedberg@gmail.com> Cc: Luiz Augusto von Dentz <luiz.dentz@gmail.com> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com> Cc: Arnd Bergmann <arnd@arndb.de> Cc: "David S. Miller" <davem@davemloft.net> Cc: Eric Dumazet <edumazet@google.com> Cc: Jakub Kicinski <kuba@kernel.org> Cc: Paolo Abeni <pabeni@redhat.com> Cc: Max Staudt <max@enpas.org> Cc: Wolfgang Grandegger <wg@grandegger.com> Cc: Marc Kleine-Budde <mkl@pengutronix.de> Cc: Dario Binacchi <dario.binacchi@amarulasolutions.com> Cc: Andreas Koensgen <ajk@comnets.uni-bremen.de> Cc: Jeremy Kerr <jk@codeconstruct.com.au> Cc: Matt Johnston <matt@codeconstruct.com.au> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Cc: Liam Girdwood <lgirdwood@gmail.com> Cc: Mark Brown <broonie@kernel.org> Cc: Jaroslav Kysela <perex@perex.cz> Cc: Takashi Iwai <tiwai@suse.com> Acked-by: Mark Brown <broonie@kernel.org> Link: https://lore.kernel.org/r/20230810091510.13006-18-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-10 17:14:51 +08:00
n_tty_receive_buf_common(struct tty_struct *tty, const u8 *cp, const u8 *fp,
size_t count, bool flow)
{
struct n_tty_data *ldata = tty->disc_data;
size_t n, rcvd = 0;
int room, overflow;
down_read(&tty->termios_rwsem);
do {
/*
* When PARMRK is set, each input char may take up to 3 chars
* in the read buf; reduce the buffer space avail by 3x
*
* If we are doing input canonicalization, and there are no
* pending newlines, let characters through without limit, so
* that erase characters will be handled. Other excess
* characters will be beeped.
*
* paired with store in *_copy_from_read_buf() -- guarantees
* the consumer has loaded the data in read_buf up to the new
* read_tail (so this producer will not overwrite unread data)
*/
size_t tail = smp_load_acquire(&ldata->read_tail);
n_tty: Fix read buffer overwrite when no newline In canon mode, the read buffer head will advance over the buffer tail if the input > 4095 bytes without receiving a line termination char. Discard additional input until a line termination is received. Before evaluating for overflow, the 'room' value is normalized for I_PARMRK and 1 byte is reserved for line termination (even in !icanon mode, in case the mode is switched). The following table shows the transform: actual buffer | 'room' value before overflow calc space avail | !I_PARMRK | I_PARMRK -------------------------------------------------- 0 | -1 | -1 1 | 0 | 0 2 | 1 | 0 3 | 2 | 0 4+ | 3 | 1 When !icanon or when icanon and the read buffer contains newlines, normalized 'room' values of -1 and 0 are clamped to 0, and 'overflow' is 0, so read_head is not adjusted and the input i/o loop exits (setting no_room if called from flush_to_ldisc()). No input is discarded since the reader does have input available to read which ensures forward progress. When icanon and the read buffer does not contain newlines and the normalized 'room' value is 0, then overflow and room are reset to 1, so that the i/o loop will process the next input char normally (except for parity errors which are ignored). Thus, erasures, signalling chars, 7-bit mode, etc. will continue to be handled properly. If the input char processed was not a line termination char, then the canon_head index will not have advanced, so the normalized 'room' value will now be -1 and 'overflow' will be set, which indicates the read_head can safely be reset, effectively erasing the last char processed. If the input char processed was a line termination, then the canon_head index will have advanced, so 'overflow' is cleared to 0, the read_head is not reset, and 'room' is cleared to 0, which exits the i/o loop (because the reader now have input available to read which ensures forward progress). Note that it is possible for a line termination to be received, and for the reader to copy the line to the user buffer before the input i/o loop is ready to process the next input char. This is why the i/o loop recomputes the room/overflow state with every input char while handling overflow. Finally, if the input data was processed without receiving a line termination (so that overflow is still set), the pty driver must receive a write wakeup. A pty writer may be waiting to write more data in n_tty_write() but without unthrottling here that wakeup will not arrive, and forward progress will halt. (Normally, the pty writer is woken when the reader reads data out of the buffer and more space become available). Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-01-17 04:05:39 +08:00
room = N_TTY_BUF_SIZE - (ldata->read_head - tail);
if (I_PARMRK(tty))
room = DIV_ROUND_UP(room, 3);
n_tty: Fix read buffer overwrite when no newline In canon mode, the read buffer head will advance over the buffer tail if the input > 4095 bytes without receiving a line termination char. Discard additional input until a line termination is received. Before evaluating for overflow, the 'room' value is normalized for I_PARMRK and 1 byte is reserved for line termination (even in !icanon mode, in case the mode is switched). The following table shows the transform: actual buffer | 'room' value before overflow calc space avail | !I_PARMRK | I_PARMRK -------------------------------------------------- 0 | -1 | -1 1 | 0 | 0 2 | 1 | 0 3 | 2 | 0 4+ | 3 | 1 When !icanon or when icanon and the read buffer contains newlines, normalized 'room' values of -1 and 0 are clamped to 0, and 'overflow' is 0, so read_head is not adjusted and the input i/o loop exits (setting no_room if called from flush_to_ldisc()). No input is discarded since the reader does have input available to read which ensures forward progress. When icanon and the read buffer does not contain newlines and the normalized 'room' value is 0, then overflow and room are reset to 1, so that the i/o loop will process the next input char normally (except for parity errors which are ignored). Thus, erasures, signalling chars, 7-bit mode, etc. will continue to be handled properly. If the input char processed was not a line termination char, then the canon_head index will not have advanced, so the normalized 'room' value will now be -1 and 'overflow' will be set, which indicates the read_head can safely be reset, effectively erasing the last char processed. If the input char processed was a line termination, then the canon_head index will have advanced, so 'overflow' is cleared to 0, the read_head is not reset, and 'room' is cleared to 0, which exits the i/o loop (because the reader now have input available to read which ensures forward progress). Note that it is possible for a line termination to be received, and for the reader to copy the line to the user buffer before the input i/o loop is ready to process the next input char. This is why the i/o loop recomputes the room/overflow state with every input char while handling overflow. Finally, if the input data was processed without receiving a line termination (so that overflow is still set), the pty driver must receive a write wakeup. A pty writer may be waiting to write more data in n_tty_write() but without unthrottling here that wakeup will not arrive, and forward progress will halt. (Normally, the pty writer is woken when the reader reads data out of the buffer and more space become available). Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-01-17 04:05:39 +08:00
room--;
if (room <= 0) {
overflow = ldata->icanon && ldata->canon_head == tail;
if (overflow && room < 0)
ldata->read_head--;
room = overflow;
tty: fix hang on tty device with no_room set It is possible to hang pty devices in this case, the reader was blocking at epoll on master side, the writer was sleeping at wait_woken inside n_tty_write on slave side, and the write buffer on tty_port was full, we found that the reader and writer would never be woken again and blocked forever. The problem was caused by a race between reader and kworker: n_tty_read(reader): n_tty_receive_buf_common(kworker): copy_from_read_buf()| |room = N_TTY_BUF_SIZE - (ldata->read_head - tail) |room <= 0 n_tty_kick_worker() | |ldata->no_room = true After writing to slave device, writer wakes up kworker to flush data on tty_port to reader, and the kworker finds that reader has no room to store data so room <= 0 is met. At this moment, reader consumes all the data on reader buffer and calls n_tty_kick_worker to check ldata->no_room which is false and reader quits reading. Then kworker sets ldata->no_room=true and quits too. If write buffer is not full, writer will wake kworker to flush data again after following writes, but if write buffer is full and writer goes to sleep, kworker will never be woken again and tty device is blocked. This problem can be solved with a check for read buffer size inside n_tty_receive_buf_common, if read buffer is empty and ldata->no_room is true, a call to n_tty_kick_worker is necessary to keep flushing data to reader. Cc: <stable@vger.kernel.org> Fixes: 42458f41d08f ("n_tty: Ensure reader restarts worker for next reader") Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Signed-off-by: Hui Li <caelli@tencent.com> Message-ID: <1680749090-14106-1-git-send-email-caelli@tencent.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-04-06 10:44:50 +08:00
WRITE_ONCE(ldata->no_room, flow && !room);
n_tty: Fix read buffer overwrite when no newline In canon mode, the read buffer head will advance over the buffer tail if the input > 4095 bytes without receiving a line termination char. Discard additional input until a line termination is received. Before evaluating for overflow, the 'room' value is normalized for I_PARMRK and 1 byte is reserved for line termination (even in !icanon mode, in case the mode is switched). The following table shows the transform: actual buffer | 'room' value before overflow calc space avail | !I_PARMRK | I_PARMRK -------------------------------------------------- 0 | -1 | -1 1 | 0 | 0 2 | 1 | 0 3 | 2 | 0 4+ | 3 | 1 When !icanon or when icanon and the read buffer contains newlines, normalized 'room' values of -1 and 0 are clamped to 0, and 'overflow' is 0, so read_head is not adjusted and the input i/o loop exits (setting no_room if called from flush_to_ldisc()). No input is discarded since the reader does have input available to read which ensures forward progress. When icanon and the read buffer does not contain newlines and the normalized 'room' value is 0, then overflow and room are reset to 1, so that the i/o loop will process the next input char normally (except for parity errors which are ignored). Thus, erasures, signalling chars, 7-bit mode, etc. will continue to be handled properly. If the input char processed was not a line termination char, then the canon_head index will not have advanced, so the normalized 'room' value will now be -1 and 'overflow' will be set, which indicates the read_head can safely be reset, effectively erasing the last char processed. If the input char processed was a line termination, then the canon_head index will have advanced, so 'overflow' is cleared to 0, the read_head is not reset, and 'room' is cleared to 0, which exits the i/o loop (because the reader now have input available to read which ensures forward progress). Note that it is possible for a line termination to be received, and for the reader to copy the line to the user buffer before the input i/o loop is ready to process the next input char. This is why the i/o loop recomputes the room/overflow state with every input char while handling overflow. Finally, if the input data was processed without receiving a line termination (so that overflow is still set), the pty driver must receive a write wakeup. A pty writer may be waiting to write more data in n_tty_write() but without unthrottling here that wakeup will not arrive, and forward progress will halt. (Normally, the pty writer is woken when the reader reads data out of the buffer and more space become available). Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-01-17 04:05:39 +08:00
} else
overflow = 0;
n = min_t(size_t, count, room);
n_tty: Fix read buffer overwrite when no newline In canon mode, the read buffer head will advance over the buffer tail if the input > 4095 bytes without receiving a line termination char. Discard additional input until a line termination is received. Before evaluating for overflow, the 'room' value is normalized for I_PARMRK and 1 byte is reserved for line termination (even in !icanon mode, in case the mode is switched). The following table shows the transform: actual buffer | 'room' value before overflow calc space avail | !I_PARMRK | I_PARMRK -------------------------------------------------- 0 | -1 | -1 1 | 0 | 0 2 | 1 | 0 3 | 2 | 0 4+ | 3 | 1 When !icanon or when icanon and the read buffer contains newlines, normalized 'room' values of -1 and 0 are clamped to 0, and 'overflow' is 0, so read_head is not adjusted and the input i/o loop exits (setting no_room if called from flush_to_ldisc()). No input is discarded since the reader does have input available to read which ensures forward progress. When icanon and the read buffer does not contain newlines and the normalized 'room' value is 0, then overflow and room are reset to 1, so that the i/o loop will process the next input char normally (except for parity errors which are ignored). Thus, erasures, signalling chars, 7-bit mode, etc. will continue to be handled properly. If the input char processed was not a line termination char, then the canon_head index will not have advanced, so the normalized 'room' value will now be -1 and 'overflow' will be set, which indicates the read_head can safely be reset, effectively erasing the last char processed. If the input char processed was a line termination, then the canon_head index will have advanced, so 'overflow' is cleared to 0, the read_head is not reset, and 'room' is cleared to 0, which exits the i/o loop (because the reader now have input available to read which ensures forward progress). Note that it is possible for a line termination to be received, and for the reader to copy the line to the user buffer before the input i/o loop is ready to process the next input char. This is why the i/o loop recomputes the room/overflow state with every input char while handling overflow. Finally, if the input data was processed without receiving a line termination (so that overflow is still set), the pty driver must receive a write wakeup. A pty writer may be waiting to write more data in n_tty_write() but without unthrottling here that wakeup will not arrive, and forward progress will halt. (Normally, the pty writer is woken when the reader reads data out of the buffer and more space become available). Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-01-17 04:05:39 +08:00
if (!n)
break;
n_tty: Fix read buffer overwrite when no newline In canon mode, the read buffer head will advance over the buffer tail if the input > 4095 bytes without receiving a line termination char. Discard additional input until a line termination is received. Before evaluating for overflow, the 'room' value is normalized for I_PARMRK and 1 byte is reserved for line termination (even in !icanon mode, in case the mode is switched). The following table shows the transform: actual buffer | 'room' value before overflow calc space avail | !I_PARMRK | I_PARMRK -------------------------------------------------- 0 | -1 | -1 1 | 0 | 0 2 | 1 | 0 3 | 2 | 0 4+ | 3 | 1 When !icanon or when icanon and the read buffer contains newlines, normalized 'room' values of -1 and 0 are clamped to 0, and 'overflow' is 0, so read_head is not adjusted and the input i/o loop exits (setting no_room if called from flush_to_ldisc()). No input is discarded since the reader does have input available to read which ensures forward progress. When icanon and the read buffer does not contain newlines and the normalized 'room' value is 0, then overflow and room are reset to 1, so that the i/o loop will process the next input char normally (except for parity errors which are ignored). Thus, erasures, signalling chars, 7-bit mode, etc. will continue to be handled properly. If the input char processed was not a line termination char, then the canon_head index will not have advanced, so the normalized 'room' value will now be -1 and 'overflow' will be set, which indicates the read_head can safely be reset, effectively erasing the last char processed. If the input char processed was a line termination, then the canon_head index will have advanced, so 'overflow' is cleared to 0, the read_head is not reset, and 'room' is cleared to 0, which exits the i/o loop (because the reader now have input available to read which ensures forward progress). Note that it is possible for a line termination to be received, and for the reader to copy the line to the user buffer before the input i/o loop is ready to process the next input char. This is why the i/o loop recomputes the room/overflow state with every input char while handling overflow. Finally, if the input data was processed without receiving a line termination (so that overflow is still set), the pty driver must receive a write wakeup. A pty writer may be waiting to write more data in n_tty_write() but without unthrottling here that wakeup will not arrive, and forward progress will halt. (Normally, the pty writer is woken when the reader reads data out of the buffer and more space become available). Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-01-17 04:05:39 +08:00
/* ignore parity errors if handling overflow */
if (!overflow || !fp || *fp != TTY_PARITY)
__receive_buf(tty, cp, fp, n);
cp += n;
if (fp)
fp += n;
count -= n;
rcvd += n;
} while (!test_bit(TTY_LDISC_CHANGING, &tty->flags));
tty->receive_room = room;
n_tty: Fix read buffer overwrite when no newline In canon mode, the read buffer head will advance over the buffer tail if the input > 4095 bytes without receiving a line termination char. Discard additional input until a line termination is received. Before evaluating for overflow, the 'room' value is normalized for I_PARMRK and 1 byte is reserved for line termination (even in !icanon mode, in case the mode is switched). The following table shows the transform: actual buffer | 'room' value before overflow calc space avail | !I_PARMRK | I_PARMRK -------------------------------------------------- 0 | -1 | -1 1 | 0 | 0 2 | 1 | 0 3 | 2 | 0 4+ | 3 | 1 When !icanon or when icanon and the read buffer contains newlines, normalized 'room' values of -1 and 0 are clamped to 0, and 'overflow' is 0, so read_head is not adjusted and the input i/o loop exits (setting no_room if called from flush_to_ldisc()). No input is discarded since the reader does have input available to read which ensures forward progress. When icanon and the read buffer does not contain newlines and the normalized 'room' value is 0, then overflow and room are reset to 1, so that the i/o loop will process the next input char normally (except for parity errors which are ignored). Thus, erasures, signalling chars, 7-bit mode, etc. will continue to be handled properly. If the input char processed was not a line termination char, then the canon_head index will not have advanced, so the normalized 'room' value will now be -1 and 'overflow' will be set, which indicates the read_head can safely be reset, effectively erasing the last char processed. If the input char processed was a line termination, then the canon_head index will have advanced, so 'overflow' is cleared to 0, the read_head is not reset, and 'room' is cleared to 0, which exits the i/o loop (because the reader now have input available to read which ensures forward progress). Note that it is possible for a line termination to be received, and for the reader to copy the line to the user buffer before the input i/o loop is ready to process the next input char. This is why the i/o loop recomputes the room/overflow state with every input char while handling overflow. Finally, if the input data was processed without receiving a line termination (so that overflow is still set), the pty driver must receive a write wakeup. A pty writer may be waiting to write more data in n_tty_write() but without unthrottling here that wakeup will not arrive, and forward progress will halt. (Normally, the pty writer is woken when the reader reads data out of the buffer and more space become available). Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-01-17 04:05:39 +08:00
/* Unthrottle if handling overflow on pty */
if (tty->driver->type == TTY_DRIVER_TYPE_PTY) {
if (overflow) {
tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE);
tty_unthrottle_safe(tty);
__tty_set_flow_change(tty, 0);
}
} else
n_tty_check_throttle(tty);
tty: fix hang on tty device with no_room set It is possible to hang pty devices in this case, the reader was blocking at epoll on master side, the writer was sleeping at wait_woken inside n_tty_write on slave side, and the write buffer on tty_port was full, we found that the reader and writer would never be woken again and blocked forever. The problem was caused by a race between reader and kworker: n_tty_read(reader): n_tty_receive_buf_common(kworker): copy_from_read_buf()| |room = N_TTY_BUF_SIZE - (ldata->read_head - tail) |room <= 0 n_tty_kick_worker() | |ldata->no_room = true After writing to slave device, writer wakes up kworker to flush data on tty_port to reader, and the kworker finds that reader has no room to store data so room <= 0 is met. At this moment, reader consumes all the data on reader buffer and calls n_tty_kick_worker to check ldata->no_room which is false and reader quits reading. Then kworker sets ldata->no_room=true and quits too. If write buffer is not full, writer will wake kworker to flush data again after following writes, but if write buffer is full and writer goes to sleep, kworker will never be woken again and tty device is blocked. This problem can be solved with a check for read buffer size inside n_tty_receive_buf_common, if read buffer is empty and ldata->no_room is true, a call to n_tty_kick_worker is necessary to keep flushing data to reader. Cc: <stable@vger.kernel.org> Fixes: 42458f41d08f ("n_tty: Ensure reader restarts worker for next reader") Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Signed-off-by: Hui Li <caelli@tencent.com> Message-ID: <1680749090-14106-1-git-send-email-caelli@tencent.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-04-06 10:44:50 +08:00
if (unlikely(ldata->no_room)) {
/*
* Barrier here is to ensure to read the latest read_tail in
* chars_in_buffer() and to make sure that read_tail is not loaded
* before ldata->no_room is set.
*/
smp_mb();
if (!chars_in_buffer(tty))
n_tty_kick_worker(tty);
}
up_read(&tty->termios_rwsem);
return rcvd;
}
static void n_tty_receive_buf(struct tty_struct *tty, const u8 *cp,
tty: use u8 for flags This makes all those 'char's an explicit 'u8'. This is part of the continuing unification of chars and flags to be consistent u8. This approaches tty_port_default_receive_buf(). Note that we do not change signedness as we compile with -funsigned-char. Signed-off-by: "Jiri Slaby (SUSE)" <jirislaby@kernel.org> Cc: William Hubbs <w.d.hubbs@gmail.com> Cc: Chris Brannon <chris@the-brannons.com> Cc: Kirk Reiser <kirk@reisers.ca> Cc: Samuel Thibault <samuel.thibault@ens-lyon.org> Cc: Marcel Holtmann <marcel@holtmann.org> Cc: Johan Hedberg <johan.hedberg@gmail.com> Cc: Luiz Augusto von Dentz <luiz.dentz@gmail.com> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com> Cc: Arnd Bergmann <arnd@arndb.de> Cc: "David S. Miller" <davem@davemloft.net> Cc: Eric Dumazet <edumazet@google.com> Cc: Jakub Kicinski <kuba@kernel.org> Cc: Paolo Abeni <pabeni@redhat.com> Cc: Max Staudt <max@enpas.org> Cc: Wolfgang Grandegger <wg@grandegger.com> Cc: Marc Kleine-Budde <mkl@pengutronix.de> Cc: Dario Binacchi <dario.binacchi@amarulasolutions.com> Cc: Andreas Koensgen <ajk@comnets.uni-bremen.de> Cc: Jeremy Kerr <jk@codeconstruct.com.au> Cc: Matt Johnston <matt@codeconstruct.com.au> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Cc: Liam Girdwood <lgirdwood@gmail.com> Cc: Mark Brown <broonie@kernel.org> Cc: Jaroslav Kysela <perex@perex.cz> Cc: Takashi Iwai <tiwai@suse.com> Acked-by: Mark Brown <broonie@kernel.org> Link: https://lore.kernel.org/r/20230810091510.13006-18-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-10 17:14:51 +08:00
const u8 *fp, size_t count)
{
n_tty_receive_buf_common(tty, cp, fp, count, false);
}
static size_t n_tty_receive_buf2(struct tty_struct *tty, const u8 *cp,
tty: use u8 for flags This makes all those 'char's an explicit 'u8'. This is part of the continuing unification of chars and flags to be consistent u8. This approaches tty_port_default_receive_buf(). Note that we do not change signedness as we compile with -funsigned-char. Signed-off-by: "Jiri Slaby (SUSE)" <jirislaby@kernel.org> Cc: William Hubbs <w.d.hubbs@gmail.com> Cc: Chris Brannon <chris@the-brannons.com> Cc: Kirk Reiser <kirk@reisers.ca> Cc: Samuel Thibault <samuel.thibault@ens-lyon.org> Cc: Marcel Holtmann <marcel@holtmann.org> Cc: Johan Hedberg <johan.hedberg@gmail.com> Cc: Luiz Augusto von Dentz <luiz.dentz@gmail.com> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com> Cc: Arnd Bergmann <arnd@arndb.de> Cc: "David S. Miller" <davem@davemloft.net> Cc: Eric Dumazet <edumazet@google.com> Cc: Jakub Kicinski <kuba@kernel.org> Cc: Paolo Abeni <pabeni@redhat.com> Cc: Max Staudt <max@enpas.org> Cc: Wolfgang Grandegger <wg@grandegger.com> Cc: Marc Kleine-Budde <mkl@pengutronix.de> Cc: Dario Binacchi <dario.binacchi@amarulasolutions.com> Cc: Andreas Koensgen <ajk@comnets.uni-bremen.de> Cc: Jeremy Kerr <jk@codeconstruct.com.au> Cc: Matt Johnston <matt@codeconstruct.com.au> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Cc: Liam Girdwood <lgirdwood@gmail.com> Cc: Mark Brown <broonie@kernel.org> Cc: Jaroslav Kysela <perex@perex.cz> Cc: Takashi Iwai <tiwai@suse.com> Acked-by: Mark Brown <broonie@kernel.org> Link: https://lore.kernel.org/r/20230810091510.13006-18-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-10 17:14:51 +08:00
const u8 *fp, size_t count)
{
return n_tty_receive_buf_common(tty, cp, fp, count, true);
}
/**
* n_tty_set_termios - termios data changed
* @tty: terminal
* @old: previous data
*
* Called by the tty layer when the user changes termios flags so that the line
* discipline can plan ahead. This function cannot sleep and is protected from
* re-entry by the tty layer. The user is guaranteed that this function will
* not be re-entered or in progress when the ldisc is closed.
*
* Locking: Caller holds @tty->termios_rwsem
*/
static void n_tty_set_termios(struct tty_struct *tty, const struct ktermios *old)
{
struct n_tty_data *ldata = tty->disc_data;
n_tty: fix EXTPROC vs ICANON interaction with TIOCINQ (aka FIONREAD) We added support for EXTPROC back in 2010 in commit 26df6d13406d ("tty: Add EXTPROC support for LINEMODE") and the intent was to allow it to override some (all?) ICANON behavior. Quoting from that original commit message: There is a new bit in the termios local flag word, EXTPROC. When this bit is set, several aspects of the terminal driver are disabled. Input line editing, character echo, and mapping of signals are all disabled. This allows the telnetd to turn off these functions when in linemode, but still keep track of what state the user wants the terminal to be in. but the problem turns out that "several aspects of the terminal driver are disabled" is a bit ambiguous, and you can really confuse the n_tty layer by setting EXTPROC and then causing some of the ICANON invariants to no longer be maintained. This fixes at least one such case (TIOCINQ) becoming unhappy because of the confusion over whether ICANON really means ICANON when EXTPROC is set. This basically makes TIOCINQ match the case of read: if EXTPROC is set, we ignore ICANON. Also, make sure to reset the ICANON state ie EXTPROC changes, not just if ICANON changes. Fixes: 26df6d13406d ("tty: Add EXTPROC support for LINEMODE") Reported-by: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp> Reported-by: syzkaller <syzkaller@googlegroups.com> Cc: Jiri Slaby <jslaby@suse.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-12-21 09:57:06 +08:00
if (!old || (old->c_lflag ^ tty->termios.c_lflag) & (ICANON | EXTPROC)) {
bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
n_tty: Fix buffer overruns with larger-than-4k pastes readline() inadvertently triggers an error recovery path when pastes larger than 4k overrun the line discipline buffer. The error recovery path discards input when the line discipline buffer is full and operating in canonical mode and no newline has been received. Because readline() changes the termios to non-canonical mode to read the line char-by-char, the line discipline buffer can become full, and then when readline() restores termios back to canonical mode for the caller, the now-full line discipline buffer triggers the error recovery. When changing termios from non-canon to canon mode and the read buffer contains data, simulate an EOF push _without_ the DISABLED_CHAR in the read buffer. Importantly for the readline() problem, the termios can be changed back to non-canonical mode without changes to the read buffer occurring; ie., as if the previous termios change had not happened (as long as no intervening read took place). Preserve existing userspace behavior which allows '\0's already received in non-canon mode to be read as '\0's in canon mode (rather than trigger add'l EOF pushes or an actual EOF). Patch based on original proposal and discussion here https://bugzilla.kernel.org/show_bug.cgi?id=55991 by Stas Sergeev <stsp@users.sourceforge.net> Reported-by: Margarita Manterola <margamanterola@gmail.com> Cc: Maximiliano Curia <maxy@gnuservers.com.ar> Cc: Pavel Machek <pavel@ucw.cz> Cc: Arkadiusz Miskiewicz <a.miskiewicz@gmail.com> Acked-by: Stas Sergeev <stsp@users.sourceforge.net> Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-12-11 06:12:02 +08:00
ldata->line_start = ldata->read_tail;
if (!L_ICANON(tty) || !read_cnt(ldata)) {
ldata->canon_head = ldata->read_tail;
ldata->push = 0;
} else {
set_bit(MASK(ldata->read_head - 1), ldata->read_flags);
n_tty: Fix buffer overruns with larger-than-4k pastes readline() inadvertently triggers an error recovery path when pastes larger than 4k overrun the line discipline buffer. The error recovery path discards input when the line discipline buffer is full and operating in canonical mode and no newline has been received. Because readline() changes the termios to non-canonical mode to read the line char-by-char, the line discipline buffer can become full, and then when readline() restores termios back to canonical mode for the caller, the now-full line discipline buffer triggers the error recovery. When changing termios from non-canon to canon mode and the read buffer contains data, simulate an EOF push _without_ the DISABLED_CHAR in the read buffer. Importantly for the readline() problem, the termios can be changed back to non-canonical mode without changes to the read buffer occurring; ie., as if the previous termios change had not happened (as long as no intervening read took place). Preserve existing userspace behavior which allows '\0's already received in non-canon mode to be read as '\0's in canon mode (rather than trigger add'l EOF pushes or an actual EOF). Patch based on original proposal and discussion here https://bugzilla.kernel.org/show_bug.cgi?id=55991 by Stas Sergeev <stsp@users.sourceforge.net> Reported-by: Margarita Manterola <margamanterola@gmail.com> Cc: Maximiliano Curia <maxy@gnuservers.com.ar> Cc: Pavel Machek <pavel@ucw.cz> Cc: Arkadiusz Miskiewicz <a.miskiewicz@gmail.com> Acked-by: Stas Sergeev <stsp@users.sourceforge.net> Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-12-11 06:12:02 +08:00
ldata->canon_head = ldata->read_head;
ldata->push = 1;
}
ldata->commit_head = ldata->read_head;
ldata->erasing = 0;
ldata->lnext = 0;
}
ldata->icanon = (L_ICANON(tty) != 0);
if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) ||
I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) ||
I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) ||
I_PARMRK(tty)) {
bitmap_zero(ldata->char_map, 256);
if (I_IGNCR(tty) || I_ICRNL(tty))
set_bit('\r', ldata->char_map);
if (I_INLCR(tty))
set_bit('\n', ldata->char_map);
if (L_ICANON(tty)) {
set_bit(ERASE_CHAR(tty), ldata->char_map);
set_bit(KILL_CHAR(tty), ldata->char_map);
set_bit(EOF_CHAR(tty), ldata->char_map);
set_bit('\n', ldata->char_map);
set_bit(EOL_CHAR(tty), ldata->char_map);
if (L_IEXTEN(tty)) {
set_bit(WERASE_CHAR(tty), ldata->char_map);
set_bit(LNEXT_CHAR(tty), ldata->char_map);
set_bit(EOL2_CHAR(tty), ldata->char_map);
if (L_ECHO(tty))
set_bit(REPRINT_CHAR(tty),
ldata->char_map);
}
}
if (I_IXON(tty)) {
set_bit(START_CHAR(tty), ldata->char_map);
set_bit(STOP_CHAR(tty), ldata->char_map);
}
if (L_ISIG(tty)) {
set_bit(INTR_CHAR(tty), ldata->char_map);
set_bit(QUIT_CHAR(tty), ldata->char_map);
set_bit(SUSP_CHAR(tty), ldata->char_map);
}
clear_bit(__DISABLED_CHAR, ldata->char_map);
ldata->raw = 0;
ldata->real_raw = 0;
} else {
ldata->raw = 1;
if ((I_IGNBRK(tty) || (!I_BRKINT(tty) && !I_PARMRK(tty))) &&
(I_IGNPAR(tty) || !I_INPCK(tty)) &&
(tty->driver->flags & TTY_DRIVER_REAL_RAW))
ldata->real_raw = 1;
else
ldata->real_raw = 0;
}
/*
* Fix tty hang when I_IXON(tty) is cleared, but the tty
* been stopped by STOP_CHAR(tty) before it.
*/
tty: cumulate and document tty_struct::flow* members Group the flow flags under a single struct called flow. The new struct contains 'stopped' and 'tco_stopped' bools which used to be bits in a bitfield. The struct also contains the lock protecting them to potentially share the same cache line. Note that commit c545b66c6922b (tty: Serialize tcflow() with other tty flow control changes) added a padding to the original bitfield. It was for the bitfield to occupy a whole 64b word to avoid interferring stores on Alpha (cannot we evaporate this arch with weird implications to C code yet?). But it doesn't work as expected as the padding (tty_struct::unused) is aligned to a 8B boundary too and occupies some bytes from the next word. So make it reliable by: 1) setting __aligned of the struct -- that aligns the start, and 2) making 'unsigned long unused[0]' as the last member of the struct -- pads the end. This is also the perfect time to start the documentation of tty_struct where all this lives. So we start by documenting what these bools actually serve for. And why we do all the alignment dances. Only the few up-to-date information from the Theodore's comment made it into this new Kerneldoc comment. Signed-off-by: Jiri Slaby <jslaby@suse.cz> Cc: "David S. Miller" <davem@davemloft.net> Cc: Jakub Kicinski <kuba@kernel.org> Cc: Jonathan Corbet <corbet@lwn.net> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Ulf Hansson <ulf.hansson@linaro.org> Cc: Heiko Carstens <hca@linux.ibm.com> Cc: Vasily Gorbik <gor@linux.ibm.com> Cc: Christian Borntraeger <borntraeger@de.ibm.com> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Vineet Gupta <vgupta@synopsys.com> Cc: "Maciej W. Rozycki" <macro@orcam.me.uk> Link: https://lore.kernel.org/r/20210505091928.22010-13-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-05-05 17:19:05 +08:00
if (!I_IXON(tty) && old && (old->c_iflag & IXON) && !tty->flow.tco_stopped) {
start_tty(tty);
process_echoes(tty);
}
/* The termios change make the tty ready for I/O */
tty: fix stall caused by missing memory barrier in drivers/tty/n_tty.c My colleague ran into a program stall on a x86_64 server, where n_tty_read() was waiting for data even if there was data in the buffer in the pty. kernel stack for the stuck process looks like below. #0 [ffff88303d107b58] __schedule at ffffffff815c4b20 #1 [ffff88303d107bd0] schedule at ffffffff815c513e #2 [ffff88303d107bf0] schedule_timeout at ffffffff815c7818 #3 [ffff88303d107ca0] wait_woken at ffffffff81096bd2 #4 [ffff88303d107ce0] n_tty_read at ffffffff8136fa23 #5 [ffff88303d107dd0] tty_read at ffffffff81368013 #6 [ffff88303d107e20] __vfs_read at ffffffff811a3704 #7 [ffff88303d107ec0] vfs_read at ffffffff811a3a57 #8 [ffff88303d107f00] sys_read at ffffffff811a4306 #9 [ffff88303d107f50] entry_SYSCALL_64_fastpath at ffffffff815c86d7 There seems to be two problems causing this issue. First, in drivers/tty/n_tty.c, __receive_buf() stores the data and updates ldata->commit_head using smp_store_release() and then checks the wait queue using waitqueue_active(). However, since there is no memory barrier, __receive_buf() could return without calling wake_up_interactive_poll(), and at the same time, n_tty_read() could start to wait in wait_woken() as in the following chart. __receive_buf() n_tty_read() ------------------------------------------------------------------------ if (waitqueue_active(&tty->read_wait)) /* Memory operations issued after the RELEASE may be completed before the RELEASE operation has completed */ add_wait_queue(&tty->read_wait, &wait); ... if (!input_available_p(tty, 0)) { smp_store_release(&ldata->commit_head, ldata->read_head); ... timeout = wait_woken(&wait, TASK_INTERRUPTIBLE, timeout); ------------------------------------------------------------------------ The second problem is that n_tty_read() also lacks a memory barrier call and could also cause __receive_buf() to return without calling wake_up_interactive_poll(), and n_tty_read() to wait in wait_woken() as in the chart below. __receive_buf() n_tty_read() ------------------------------------------------------------------------ spin_lock_irqsave(&q->lock, flags); /* from add_wait_queue() */ ... if (!input_available_p(tty, 0)) { /* Memory operations issued after the RELEASE may be completed before the RELEASE operation has completed */ smp_store_release(&ldata->commit_head, ldata->read_head); if (waitqueue_active(&tty->read_wait)) __add_wait_queue(q, wait); spin_unlock_irqrestore(&q->lock,flags); /* from add_wait_queue() */ ... timeout = wait_woken(&wait, TASK_INTERRUPTIBLE, timeout); ------------------------------------------------------------------------ There are also other places in drivers/tty/n_tty.c which have similar calls to waitqueue_active(), so instead of adding many memory barrier calls, this patch simply removes the call to waitqueue_active(), leaving just wake_up*() behind. This fixes both problems because, even though the memory access before or after the spinlocks in both wake_up*() and add_wait_queue() can sneak into the critical section, it cannot go past it and the critical section assures that they will be serialized (please see "INTER-CPU ACQUIRING BARRIER EFFECTS" in Documentation/memory-barriers.txt for a better explanation). Moreover, the resulting code is much simpler. Latency measurement using a ping-pong test over a pty doesn't show any visible performance drop. Signed-off-by: Kosuke Tatsukawa <tatsu@ab.jp.nec.com> Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-10-02 16:27:05 +08:00
wake_up_interruptible(&tty->write_wait);
wake_up_interruptible(&tty->read_wait);
}
/**
* n_tty_close - close the ldisc for this tty
* @tty: device
*
* Called from the terminal layer when this line discipline is being shut down,
* either because of a close or becsuse of a discipline change. The function
* will not be called while other ldisc methods are in progress.
*/
static void n_tty_close(struct tty_struct *tty)
{
struct n_tty_data *ldata = tty->disc_data;
n_tty: Don't flush buffer when closing ldisc A buffer flush is both undesirable and unnecessary when the ldisc is closing. A buffer flush performs the following: 1. resets ldisc data fields to their initial state 2. resets tty->receive_room to indicate more data can be sent 3. schedules buffer work to receive more data 4. signals a buffer flush has happened to linked pty in packet mode Since the ldisc has been halted and the tty may soon be destructed, buffer work must not be scheduled as that work might access an invalid tty and ldisc state. Also, the ldisc read buffer is about to be freed, so that's pointless. Resetting the ldisc data fields is pointless as well since that structure is about to be freed. Resetting tty->receive_room is unnecessary, as it will be properly reset if a new ldisc is reopened. Besides, resetting the original receive_room value would be wrong since the read buffer will be gone. Since the packet mode flush is observable from userspace, this behavior has been preserved. The test jig originally authored by Ilya Zykov <ilya@ilyx.ru> and signed off by him is included below. The test jig prompts the following warnings which this patch fixes. [ 38.051111] ------------[ cut here ]------------ [ 38.052113] WARNING: at drivers/tty/n_tty.c:160 n_tty_set_room.part.6+0x8b/0xa0() [ 38.053916] Hardware name: Bochs [ 38.054819] Modules linked in: netconsole configfs bnep rfcomm bluetooth parport_pc ppdev snd_hda_intel snd_hda_codec snd_hwdep snd_pcm snd_seq_midi snd_rawmidi snd_seq_midi_event snd_seq psmouse snd_timer serio_raw mac_hid snd_seq_device snd microcode lp parport virtio_balloon soundcore i2c_piix4 snd_page_alloc floppy 8139too 8139cp [ 38.059704] Pid: 1564, comm: pty_kill Tainted: G W 3.7.0-next-20121130+ttydebug-xeon #20121130+ttydebug [ 38.061578] Call Trace: [ 38.062491] [<ffffffff81058b4f>] warn_slowpath_common+0x7f/0xc0 [ 38.063448] [<ffffffff81058baa>] warn_slowpath_null+0x1a/0x20 [ 38.064439] [<ffffffff8142dc2b>] n_tty_set_room.part.6+0x8b/0xa0 [ 38.065381] [<ffffffff8142dc82>] n_tty_set_room+0x42/0x80 [ 38.066323] [<ffffffff8142e6f2>] reset_buffer_flags+0x102/0x160 [ 38.077508] [<ffffffff8142e76d>] n_tty_flush_buffer+0x1d/0x90 [ 38.078782] [<ffffffff81046569>] ? default_spin_lock_flags+0x9/0x10 [ 38.079734] [<ffffffff8142e804>] n_tty_close+0x24/0x60 [ 38.080730] [<ffffffff81431b61>] tty_ldisc_close.isra.2+0x41/0x60 [ 38.081680] [<ffffffff81431bbb>] tty_ldisc_kill+0x3b/0x80 [ 38.082618] [<ffffffff81432a07>] tty_ldisc_release+0x77/0xe0 [ 38.083549] [<ffffffff8142b781>] tty_release+0x451/0x4d0 [ 38.084525] [<ffffffff811950be>] __fput+0xae/0x230 [ 38.085472] [<ffffffff8119524e>] ____fput+0xe/0x10 [ 38.086401] [<ffffffff8107aa88>] task_work_run+0xc8/0xf0 [ 38.087334] [<ffffffff8105ea56>] do_exit+0x196/0x4b0 [ 38.088304] [<ffffffff8106c77b>] ? __dequeue_signal+0x6b/0xb0 [ 38.089240] [<ffffffff8105ef34>] do_group_exit+0x44/0xa0 [ 38.090182] [<ffffffff8106f43d>] get_signal_to_deliver+0x20d/0x4e0 [ 38.091125] [<ffffffff81016979>] do_signal+0x29/0x130 [ 38.092096] [<ffffffff81431a9e>] ? tty_ldisc_deref+0xe/0x10 [ 38.093030] [<ffffffff8142a317>] ? tty_write+0xb7/0xf0 [ 38.093976] [<ffffffff81193f53>] ? vfs_write+0xb3/0x180 [ 38.094904] [<ffffffff81016b20>] do_notify_resume+0x80/0xc0 [ 38.095830] [<ffffffff81700492>] int_signal+0x12/0x17 [ 38.096788] ---[ end trace 5f6f7a9651cd999b ]--- [ 2730.570602] ------------[ cut here ]------------ [ 2730.572130] WARNING: at drivers/tty/n_tty.c:160 n_tty_set_room+0x107/0x140() [ 2730.574904] scheduling buffer work for halted ldisc [ 2730.578303] Pid: 9691, comm: trinity-child15 Tainted: G W 3.7.0-rc8-next-20121205-sasha-00023-g59f0d85 #207 [ 2730.588694] Call Trace: [ 2730.590486] [<ffffffff81c41d77>] ? n_tty_set_room+0x107/0x140 [ 2730.592559] [<ffffffff8110c827>] warn_slowpath_common+0x87/0xb0 [ 2730.595317] [<ffffffff8110c8b1>] warn_slowpath_fmt+0x41/0x50 [ 2730.599186] [<ffffffff81c41d77>] n_tty_set_room+0x107/0x140 [ 2730.603141] [<ffffffff81c42fe7>] reset_buffer_flags+0x137/0x150 [ 2730.607166] [<ffffffff81c43018>] n_tty_flush_buffer+0x18/0x90 [ 2730.610123] [<ffffffff81c430af>] n_tty_close+0x1f/0x60 [ 2730.612068] [<ffffffff81c461f2>] tty_ldisc_close.isra.4+0x52/0x60 [ 2730.614078] [<ffffffff81c462ab>] tty_ldisc_reinit+0x3b/0x70 [ 2730.615891] [<ffffffff81c46db2>] tty_ldisc_hangup+0x102/0x1e0 [ 2730.617780] [<ffffffff81c3e537>] __tty_hangup+0x137/0x440 [ 2730.619547] [<ffffffff81c3e869>] tty_vhangup+0x9/0x10 [ 2730.621266] [<ffffffff81c48f1c>] pty_close+0x14c/0x160 [ 2730.622952] [<ffffffff81c3fd45>] tty_release+0xd5/0x490 [ 2730.624674] [<ffffffff8127fbe2>] __fput+0x122/0x250 [ 2730.626195] [<ffffffff8127fd19>] ____fput+0x9/0x10 [ 2730.627758] [<ffffffff81134602>] task_work_run+0xb2/0xf0 [ 2730.629491] [<ffffffff811139ad>] do_exit+0x36d/0x580 [ 2730.631159] [<ffffffff81113c8a>] do_group_exit+0x8a/0xc0 [ 2730.632819] [<ffffffff81127351>] get_signal_to_deliver+0x501/0x5b0 [ 2730.634758] [<ffffffff8106de34>] do_signal+0x24/0x100 [ 2730.636412] [<ffffffff81204865>] ? user_exit+0xa5/0xd0 [ 2730.638078] [<ffffffff81183cd8>] ? trace_hardirqs_on_caller+0x118/0x140 [ 2730.640279] [<ffffffff81183d0d>] ? trace_hardirqs_on+0xd/0x10 [ 2730.642164] [<ffffffff8106df78>] do_notify_resume+0x48/0xa0 [ 2730.643966] [<ffffffff83cdff6a>] int_signal+0x12/0x17 [ 2730.645672] ---[ end trace a40d53149c07fce0 ]--- /* * pty_thrash.c * * Based on original test jig by Ilya Zykov <ilya@ilyx.ru> * * Signed-off-by: Peter Hurley <peter@hurleysoftware.com> * Signed-off-by: Ilya Zykov <ilya@ilyx.ru> */ static int fd; static void error_exit(char *f, ...) { va_list va; va_start(va, f); vprintf(f, va); printf(": %s\n", strerror(errno)); va_end(va); if (fd >= 0) close(fd); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { int parent; char pts_name[24]; int ptn, unlock; while (1) { fd = open("/dev/ptmx", O_RDWR); if (fd < 0) error_exit("opening pty master"); unlock = 0; if (ioctl(fd, TIOCSPTLCK, &unlock) < 0) error_exit("unlocking pty pair"); if (ioctl(fd, TIOCGPTN, &ptn) < 0) error_exit("getting pty #"); snprintf(pts_name, sizeof(pts_name), "/dev/pts/%d", ptn); child_id = fork(); if (child_id == -1) error_exit("forking child"); if (parent) { int err, id, status; char buf[128]; int n; n = read(fd, buf, sizeof(buf)); if (n < 0) error_exit("master reading"); printf("%.*s\n", n-1, buf); close(fd); err = kill(child_id, SIGKILL); if (err < 0) error_exit("killing child"); id = waitpid(child_id, &status, 0); if (id < 0 || id != child_id) error_exit("waiting for child"); } else { /* Child */ close(fd); printf("Test cycle on slave pty %s\n", pts_name); fd = open(pts_name, O_RDWR); if (fd < 0) error_exit("opening pty slave"); while (1) { char pattern[] = "test\n"; if (write(fd, pattern, strlen(pattern)) < 0) error_exit("slave writing"); } } } /* never gets here */ return 0; } Reported-by: Sasha Levin <levinsasha928@gmail.com> Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-03-12 04:44:23 +08:00
if (tty->link)
n_tty_packet_mode_flush(tty);
tty: Protect disc_data in n_tty_close and n_tty_flush_buffer n_tty_flush_buffer can happen in parallel with n_tty_close that the tty->disc_data will be set to NULL. n_tty_flush_buffer accesses tty->disc_data, so we must prevent n_tty_close clear tty->disc_data while n_tty_flush_buffer has a non-NULL view of tty->disc_data. So we need to make sure that accesses to disc_data are atomic using tty->termios_rwsem. There is an example I meet: When n_tty_flush_buffer accesses tty struct, the disc_data is right. However, then reset_buffer_flags accesses tty->disc_data, disc_data become NULL, So kernel crash when accesses tty->disc_data->real_tail. I guess there could be another thread change tty->disc_data to NULL, and during N_TTY line discipline, n_tty_close will set tty->disc_data to be NULL. So use tty->termios_rwsem to protect disc_data between close and flush_buffer. IP: reset_buffer_flags+0x9/0xf0 PGD 0 P4D 0 Oops: 0002 [#1] SMP CPU: 23 PID: 2087626 Comm: (agetty) Kdump: loaded Tainted: G Hardware name: UNISINSIGHT X3036P-G3/ST01M2C7S, BIOS 2.00.13 01/11/2019 task: ffff9c4e9da71e80 task.stack: ffffb30cfe898000 RIP: 0010:reset_buffer_flags+0x9/0xf0 RSP: 0018:ffffb30cfe89bca8 EFLAGS: 00010246 RAX: ffff9c4e9da71e80 RBX: ffff9c368d1bac00 RCX: 0000000000000000 RDX: 0000000000000000 RSI: ffff9c4ea17b50f0 RDI: 0000000000000000 RBP: ffffb30cfe89bcc8 R08: 0000000000000100 R09: 0000000000000001 R10: 0000000000000001 R11: 0000000000000000 R12: ffff9c368d1bacc0 R13: ffff9c20cfd18428 R14: ffff9c4ea17b50f0 R15: ffff9c368d1bac00 FS: 00007f9fbbe97940(0000) GS:ffff9c375c740000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000002260 CR3: 0000002f72233003 CR4: 00000000007606e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 PKRU: 55555554 Call Trace: ? n_tty_flush_buffer+0x2a/0x60 tty_buffer_flush+0x76/0x90 tty_ldisc_flush+0x22/0x40 vt_ioctl+0x5a7/0x10b0 ? n_tty_ioctl_helper+0x27/0x110 tty_ioctl+0xef/0x8c0 do_vfs_ioctl+0xa7/0x5e0 ? __audit_syscall_entry+0xaf/0x100 ? syscall_trace_enter+0x1d0/0x2b0 SyS_ioctl+0x79/0x90 do_syscall_64+0x6c/0x1b0 entry_SYSCALL64_slow_path+0x25/0x25 n_tty_flush_buffer --->tty->disc_data is OK ->reset_buffer_flags -->tty->disc_data is NULL Signed-off-by: Yan.Gao <gao.yanB@h3c.com> Reviewed-by: Xianting Tian <tian.xianting@h3c.com> Link: https://lore.kernel.org/r/20201210022507.30729-1-gao.yanB@h3c.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2020-12-10 10:25:07 +08:00
down_write(&tty->termios_rwsem);
vfree(ldata);
tty->disc_data = NULL;
tty: Protect disc_data in n_tty_close and n_tty_flush_buffer n_tty_flush_buffer can happen in parallel with n_tty_close that the tty->disc_data will be set to NULL. n_tty_flush_buffer accesses tty->disc_data, so we must prevent n_tty_close clear tty->disc_data while n_tty_flush_buffer has a non-NULL view of tty->disc_data. So we need to make sure that accesses to disc_data are atomic using tty->termios_rwsem. There is an example I meet: When n_tty_flush_buffer accesses tty struct, the disc_data is right. However, then reset_buffer_flags accesses tty->disc_data, disc_data become NULL, So kernel crash when accesses tty->disc_data->real_tail. I guess there could be another thread change tty->disc_data to NULL, and during N_TTY line discipline, n_tty_close will set tty->disc_data to be NULL. So use tty->termios_rwsem to protect disc_data between close and flush_buffer. IP: reset_buffer_flags+0x9/0xf0 PGD 0 P4D 0 Oops: 0002 [#1] SMP CPU: 23 PID: 2087626 Comm: (agetty) Kdump: loaded Tainted: G Hardware name: UNISINSIGHT X3036P-G3/ST01M2C7S, BIOS 2.00.13 01/11/2019 task: ffff9c4e9da71e80 task.stack: ffffb30cfe898000 RIP: 0010:reset_buffer_flags+0x9/0xf0 RSP: 0018:ffffb30cfe89bca8 EFLAGS: 00010246 RAX: ffff9c4e9da71e80 RBX: ffff9c368d1bac00 RCX: 0000000000000000 RDX: 0000000000000000 RSI: ffff9c4ea17b50f0 RDI: 0000000000000000 RBP: ffffb30cfe89bcc8 R08: 0000000000000100 R09: 0000000000000001 R10: 0000000000000001 R11: 0000000000000000 R12: ffff9c368d1bacc0 R13: ffff9c20cfd18428 R14: ffff9c4ea17b50f0 R15: ffff9c368d1bac00 FS: 00007f9fbbe97940(0000) GS:ffff9c375c740000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000002260 CR3: 0000002f72233003 CR4: 00000000007606e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 PKRU: 55555554 Call Trace: ? n_tty_flush_buffer+0x2a/0x60 tty_buffer_flush+0x76/0x90 tty_ldisc_flush+0x22/0x40 vt_ioctl+0x5a7/0x10b0 ? n_tty_ioctl_helper+0x27/0x110 tty_ioctl+0xef/0x8c0 do_vfs_ioctl+0xa7/0x5e0 ? __audit_syscall_entry+0xaf/0x100 ? syscall_trace_enter+0x1d0/0x2b0 SyS_ioctl+0x79/0x90 do_syscall_64+0x6c/0x1b0 entry_SYSCALL64_slow_path+0x25/0x25 n_tty_flush_buffer --->tty->disc_data is OK ->reset_buffer_flags -->tty->disc_data is NULL Signed-off-by: Yan.Gao <gao.yanB@h3c.com> Reviewed-by: Xianting Tian <tian.xianting@h3c.com> Link: https://lore.kernel.org/r/20201210022507.30729-1-gao.yanB@h3c.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2020-12-10 10:25:07 +08:00
up_write(&tty->termios_rwsem);
}
/**
* n_tty_open - open an ldisc
* @tty: terminal to open
*
* Called when this line discipline is being attached to the terminal device.
* Can sleep. Called serialized so that no other events will occur in parallel.
* No further open will occur until a close.
*/
static int n_tty_open(struct tty_struct *tty)
{
struct n_tty_data *ldata;
/* Currently a malloc failure here can panic */
n_tty: Access echo_* variables carefully. syzbot is reporting stalls at __process_echoes() [1]. This is because since ldata->echo_commit < ldata->echo_tail becomes true for some reason, the discard loop is serving as almost infinite loop. This patch tries to avoid falling into ldata->echo_commit < ldata->echo_tail situation by making access to echo_* variables more carefully. Since reset_buffer_flags() is called without output_lock held, it should not touch echo_* variables. And omit a call to reset_buffer_flags() from n_tty_open() by using vzalloc(). Since add_echo_byte() is called without output_lock held, it needs memory barrier between storing into echo_buf[] and incrementing echo_head counter. echo_buf() needs corresponding memory barrier before reading echo_buf[]. Lack of handling the possibility of not-yet-stored multi-byte operation might be the reason of falling into ldata->echo_commit < ldata->echo_tail situation, for if I do WARN_ON(ldata->echo_commit == tail + 1) prior to echo_buf(ldata, tail + 1), the WARN_ON() fires. Also, explicitly masking with buffer for the former "while" loop, and use ldata->echo_commit > tail for the latter "while" loop. [1] https://syzkaller.appspot.com/bug?id=17f23b094cd80df750e5b0f8982c521ee6bcbf40 Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Reported-by: syzbot <syzbot+108696293d7a21ab688f@syzkaller.appspotmail.com> Cc: Peter Hurley <peter@hurleysoftware.com> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-05-26 08:53:14 +08:00
ldata = vzalloc(sizeof(*ldata));
if (!ldata)
n_tty: Access echo_* variables carefully. syzbot is reporting stalls at __process_echoes() [1]. This is because since ldata->echo_commit < ldata->echo_tail becomes true for some reason, the discard loop is serving as almost infinite loop. This patch tries to avoid falling into ldata->echo_commit < ldata->echo_tail situation by making access to echo_* variables more carefully. Since reset_buffer_flags() is called without output_lock held, it should not touch echo_* variables. And omit a call to reset_buffer_flags() from n_tty_open() by using vzalloc(). Since add_echo_byte() is called without output_lock held, it needs memory barrier between storing into echo_buf[] and incrementing echo_head counter. echo_buf() needs corresponding memory barrier before reading echo_buf[]. Lack of handling the possibility of not-yet-stored multi-byte operation might be the reason of falling into ldata->echo_commit < ldata->echo_tail situation, for if I do WARN_ON(ldata->echo_commit == tail + 1) prior to echo_buf(ldata, tail + 1), the WARN_ON() fires. Also, explicitly masking with buffer for the former "while" loop, and use ldata->echo_commit > tail for the latter "while" loop. [1] https://syzkaller.appspot.com/bug?id=17f23b094cd80df750e5b0f8982c521ee6bcbf40 Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Reported-by: syzbot <syzbot+108696293d7a21ab688f@syzkaller.appspotmail.com> Cc: Peter Hurley <peter@hurleysoftware.com> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-05-26 08:53:14 +08:00
return -ENOMEM;
ldata->overrun_time = jiffies;
mutex_init(&ldata->atomic_read_lock);
mutex_init(&ldata->output_lock);
tty->disc_data = ldata;
tty->closing = 0;
n_tty: Fully initialize ldisc before restarting buffer work Buffer work may already be pending when the n_tty ldisc is re-opened, eg., when setting the ldisc (via TIOCSETD ioctl) and when hanging up the tty. Since n_tty_set_room() may restart buffer work, first ensure the ldisc is completely initialized. Factor n_tty_set_room() out of reset_buffer_flags() (only 2 callers) and reorganize n_tty_open() to set termios last; buffer work will be restarted there if necessary, after the char_map is properly initialized. Fixes this WARNING: [ 549.561769] ------------[ cut here ]------------ [ 549.598755] WARNING: at drivers/tty/n_tty.c:160 n_tty_set_room+0xff/0x130() [ 549.604058] scheduling buffer work for halted ldisc [ 549.607741] Pid: 9417, comm: trinity-child28 Tainted: G D W 3.7.0-next-20121217-sasha-00023-g8689ef9 #219 [ 549.652580] Call Trace: [ 549.662754] [<ffffffff81c432cf>] ? n_tty_set_room+0xff/0x130 [ 549.665458] [<ffffffff8110cae7>] warn_slowpath_common+0x87/0xb0 [ 549.668257] [<ffffffff8110cb71>] warn_slowpath_fmt+0x41/0x50 [ 549.671007] [<ffffffff81c432cf>] n_tty_set_room+0xff/0x130 [ 549.673268] [<ffffffff81c44597>] reset_buffer_flags+0x137/0x150 [ 549.675607] [<ffffffff81c45b71>] n_tty_open+0x131/0x1c0 [ 549.677699] [<ffffffff81c47824>] tty_ldisc_open.isra.5+0x54/0x70 [ 549.680147] [<ffffffff81c482bf>] tty_ldisc_hangup+0x11f/0x1e0 [ 549.682409] [<ffffffff81c3fa17>] __tty_hangup+0x137/0x440 [ 549.684634] [<ffffffff81c3fd49>] tty_vhangup+0x9/0x10 [ 549.686443] [<ffffffff81c4a42c>] pty_close+0x14c/0x160 [ 549.688446] [<ffffffff81c41225>] tty_release+0xd5/0x490 [ 549.690460] [<ffffffff8127d8a2>] __fput+0x122/0x250 [ 549.692577] [<ffffffff8127d9d9>] ____fput+0x9/0x10 [ 549.694534] [<ffffffff811348c2>] task_work_run+0xb2/0xf0 [ 549.696349] [<ffffffff81113c6d>] do_exit+0x36d/0x580 [ 549.698286] [<ffffffff8107d964>] ? syscall_trace_enter+0x24/0x2e0 [ 549.702729] [<ffffffff81113f4a>] do_group_exit+0x8a/0xc0 [ 549.706775] [<ffffffff81113f92>] sys_exit_group+0x12/0x20 [ 549.711088] [<ffffffff83cfab18>] tracesys+0xe1/0xe6 [ 549.728001] ---[ end trace 73eb41728f11f87e ]--- Reported-by: Sasha Levin <levinsasha928@gmail.com> Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-03-12 04:44:32 +08:00
/* indicate buffer work may resume */
clear_bit(TTY_LDISC_HALTED, &tty->flags);
n_tty_set_termios(tty, NULL);
tty_unthrottle(tty);
return 0;
}
static inline int input_available_p(const struct tty_struct *tty, int poll)
{
const struct n_tty_data *ldata = tty->disc_data;
int amt = poll && !TIME_CHAR(tty) && MIN_CHAR(tty) ? MIN_CHAR(tty) : 1;
if (ldata->icanon && !L_EXTPROC(tty))
return ldata->canon_head != ldata->read_tail;
else
return ldata->commit_head - ldata->read_tail >= amt;
}
/**
* copy_from_read_buf - copy read data directly
* @tty: terminal device
* @kbp: data
* @nr: size of data
*
* Helper function to speed up n_tty_read(). It is only called when %ICANON is
* off; it copies characters straight from the tty queue.
*
* Returns: true if it successfully copied data, but there is still more data
* to be had.
tty: teach n_tty line discipline about the new "cookie continuations" With the conversion to do the tty ldisc read operations in small chunks, the n_tty line discipline became noticeably slower for throughput oriented loads, because rather than read things in up to 2kB chunks, it would return at most 64 bytes per read() system call. The cost is mainly all in the "do system calls over and over", not really in the new "copy to an extra kernel buffer". This can be fixed by teaching the n_tty line discipline about the "cookie continuation" model, which the chunking code supports because things like hdlc need to be able to handle packets up to 64kB in size. Doing that doesn't just get us back to the old performace, but to much better performance: my stupid "copy 10MB of data over a pty" test program is now almost twice as fast as it used to be (going down from 0.1s to 0.054s). This is entirely because it now creates maximal chunks (which happens to be "one byte less than one page" due to how we do the circular tty buffers). NOTE! This case only handles the simpler non-icanon case, which is the one where people may care about throughput. I'm going to do the icanon case later too, because while performance isn't a major issue for that, there may be programs that think they'll always get a full line and don't like the 64-byte chunking for that reason. Such programs are arguably buggy (signals etc can cause random partial results from tty reads anyway), and good programs will handle such partial reads, but expecting everybody to write "good programs" has never been a winning policy for the kernel.. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2021-01-20 10:14:20 +08:00
*
* Locking:
* * called under the @ldata->atomic_read_lock sem
* * n_tty_read()/consumer path:
* caller holds non-exclusive %termios_rwsem;
* read_tail published
*/
static bool copy_from_read_buf(const struct tty_struct *tty, u8 **kbp,
size_t *nr)
{
struct n_tty_data *ldata = tty->disc_data;
size_t n;
bool is_eof;
size_t head = smp_load_acquire(&ldata->commit_head);
size_t tail = MASK(ldata->read_tail);
n = min3(head - ldata->read_tail, N_TTY_BUF_SIZE - tail, *nr);
if (!n)
return false;
u8 *from = read_buf_addr(ldata, tail);
memcpy(*kbp, from, n);
is_eof = n == 1 && *from == EOF_CHAR(tty);
tty_audit_add_data(tty, from, n);
zero_buffer(tty, from, n);
smp_store_release(&ldata->read_tail, ldata->read_tail + n);
/* Turn single EOF into zero-length read */
if (L_EXTPROC(tty) && ldata->icanon && is_eof &&
head == ldata->read_tail)
return false;
*kbp += n;
*nr -= n;
/* If we have more to copy, let the caller know */
return head != ldata->read_tail;
}
/**
* canon_copy_from_read_buf - copy read data in canonical mode
* @tty: terminal device
* @kbp: data
* @nr: size of data
*
* Helper function for n_tty_read(). It is only called when %ICANON is on; it
* copies one line of input up to and including the line-delimiting character
* into the result buffer.
*
* Note: When termios is changed from non-canonical to canonical mode and the
* read buffer contains data, n_tty_set_termios() simulates an EOF push (as if
* C-d were input) _without_ the %DISABLED_CHAR in the buffer. This causes data
* already processed as input to be immediately available as input although a
* newline has not been received.
*
* Locking:
* * called under the %atomic_read_lock mutex
* * n_tty_read()/consumer path:
* caller holds non-exclusive %termios_rwsem;
* read_tail published
*/
static bool canon_copy_from_read_buf(const struct tty_struct *tty, u8 **kbp,
size_t *nr)
{
struct n_tty_data *ldata = tty->disc_data;
size_t n, size, more, c;
size_t eol;
size_t tail, canon_head;
int found = 0;
/* N.B. avoid overrun if nr == 0 */
n_tty: Fix poll() after buffer-limited eof push read commit 40d5e0905a03 ("n_tty: Fix EOF push handling") fixed EOF push for reads. However, that approach still allows a condition mismatch between poll() and read(), where poll() returns POLLIN but read() blocks. This state can happen when a previous read() returned because the user buffer was full and the next character was an EOF not at the beginning of the line. While the next read() will properly identify the condition and advance the read buffer tail without improperly indicating an EOF file condition (ie., read() will not mistakenly return 0), poll() will mistakenly indicate POLLIN. Although a possible solution would be to peek at the input buffer in n_tty_poll(), the better solution in this patch is to eat the EOF during the previous read() (ie., fix the problem by eliminating the condition). The current canon line buffer copy limits the scan for next end-of-line to the smaller of either, a. the remaining user buffer size b. completed lines in the input buffer When the remaining user buffer size is exactly one less than the end-of-line marked by EOF push, the EOF is not scanned nor skipped but left for subsequent reads. In the example below, the scan index 'eol' has stopped at the EOF because it is past the scan limit of 5 (not because it has found the next set bit in read_flags) user buffer [*nr = 5] _ _ _ _ _ read_flags 0 0 0 0 0 1 input buffer h e l l o [EOF] ^ ^ / / tail eol result: found = 0, tail += 5, *nr += 5 Instead, allow the scan to peek ahead 1 byte (while still limiting the scan to completed lines in the input buffer). For the example above, result: found = 1, tail += 6, *nr += 5 Because the scan limit is now bumped +1 byte, when the scan is completed, the tail advance and the user buffer copy limit is re-clamped to *nr when EOF is _not_ found. Fixes: 40d5e0905a03 ("n_tty: Fix EOF push handling") Cc: <stable@vger.kernel.org> # 3.12+ Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-11-28 02:59:20 +08:00
if (!*nr)
return false;
canon_head = smp_load_acquire(&ldata->canon_head);
tty: n_tty: do not look ahead for EOL character past the end of the buffer Daniel Gibson reports that the n_tty code gets line termination wrong in very specific cases: "If you feed a line with exactly 64 chars + terminating newline, and directly afterwards (without reading) another line into a pseudo terminal, the the first read() on the other side will return the 64 char line *without* terminating newline, and the next read() will return the missing terminating newline AND the complete next line (if it fits in the buffer)" and bisected the behavior to commit 3b830a9c34d5 ("tty: convert tty_ldisc_ops 'read()' function to take a kernel pointer"). Now, digging deeper, it turns out that the behavior isn't exactly new: what changed in commit 3b830a9c34d5 was that the tty line discipline .read() function is now passed an intermediate kernel buffer rather than the final user space buffer. And that intermediate kernel buffer is 64 bytes in size - thus that special case with exactly 64 bytes plus terminating newline. The same problem did exist before, but historically the boundary was not the 64-byte chunk, but the user-supplied buffer size, which is obviously generally bigger (and potentially bigger than N_TTY_BUF_SIZE, which would hide the issue entirely). The reason is that the n_tty canon_copy_from_read_buf() code would look ahead for the EOL character one byte further than it would actually copy. It would then decide that it had found the terminator, and unmark it as an EOL character - which in turn explains why the next read wouldn't then be terminated by it. Now, the reason it did all this in the first place is related to some historical and pretty obscure EOF behavior, see commit ac8f3bf8832a ("n_tty: Fix poll() after buffer-limited eof push read") and commit 40d5e0905a03 ("n_tty: Fix EOF push handling"). And the reason for the EOL confusion is that we treat EOF as a special EOL condition, with the EOL character being NUL (aka "__DISABLED_CHAR" in the kernel sources). So that EOF look-ahead also affects the normal EOL handling. This patch just removes the look-ahead that causes problems, because EOL is much more critical than the historical "EOF in the middle of a line that coincides with the end of the buffer" handling ever was. Now, it is possible that we should indeed re-introduce the "look at next character to see if it's a EOF" behavior, but if so, that should be done not at the kernel buffer chunk boundary in canon_copy_from_read_buf(), but at a higher level, when we run out of the user buffer. In particular, the place to do that would be at the top of 'n_tty_read()', where we check if it's a continuation of a previously started read, and there is no more buffer space left, we could decide to just eat the __DISABLED_CHAR at that point. But that would be a separate patch, because I suspect nobody actually cares, and I'd like to get a report about it before bothering. Fixes: 3b830a9c34d5 ("tty: convert tty_ldisc_ops 'read()' function to take a kernel pointer") Fixes: ac8f3bf8832a ("n_tty: Fix poll() after buffer-limited eof push read") Fixes: 40d5e0905a03 ("n_tty: Fix EOF push handling") Link: https://bugzilla.kernel.org/show_bug.cgi?id=215611 Reported-and-tested-by: Daniel Gibson <metalcaedes@gmail.com> Cc: Peter Hurley <peter@hurleysoftware.com> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Jiri Slaby <jirislaby@kernel.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2022-02-16 07:28:00 +08:00
n = min(*nr, canon_head - ldata->read_tail);
n_tty: Fix poll() after buffer-limited eof push read commit 40d5e0905a03 ("n_tty: Fix EOF push handling") fixed EOF push for reads. However, that approach still allows a condition mismatch between poll() and read(), where poll() returns POLLIN but read() blocks. This state can happen when a previous read() returned because the user buffer was full and the next character was an EOF not at the beginning of the line. While the next read() will properly identify the condition and advance the read buffer tail without improperly indicating an EOF file condition (ie., read() will not mistakenly return 0), poll() will mistakenly indicate POLLIN. Although a possible solution would be to peek at the input buffer in n_tty_poll(), the better solution in this patch is to eat the EOF during the previous read() (ie., fix the problem by eliminating the condition). The current canon line buffer copy limits the scan for next end-of-line to the smaller of either, a. the remaining user buffer size b. completed lines in the input buffer When the remaining user buffer size is exactly one less than the end-of-line marked by EOF push, the EOF is not scanned nor skipped but left for subsequent reads. In the example below, the scan index 'eol' has stopped at the EOF because it is past the scan limit of 5 (not because it has found the next set bit in read_flags) user buffer [*nr = 5] _ _ _ _ _ read_flags 0 0 0 0 0 1 input buffer h e l l o [EOF] ^ ^ / / tail eol result: found = 0, tail += 5, *nr += 5 Instead, allow the scan to peek ahead 1 byte (while still limiting the scan to completed lines in the input buffer). For the example above, result: found = 1, tail += 6, *nr += 5 Because the scan limit is now bumped +1 byte, when the scan is completed, the tail advance and the user buffer copy limit is re-clamped to *nr when EOF is _not_ found. Fixes: 40d5e0905a03 ("n_tty: Fix EOF push handling") Cc: <stable@vger.kernel.org> # 3.12+ Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-11-28 02:59:20 +08:00
tail = MASK(ldata->read_tail);
size = min_t(size_t, tail + n, N_TTY_BUF_SIZE);
n_tty_trace("%s: nr:%zu tail:%zu n:%zu size:%zu\n",
__func__, *nr, tail, n, size);
eol = find_next_bit(ldata->read_flags, size, tail);
more = n - (size - tail);
if (eol == N_TTY_BUF_SIZE && more) {
/* scan wrapped without finding set bit */
eol = find_first_bit(ldata->read_flags, more);
found = eol != more;
} else
found = eol != size;
n = eol - tail;
if (n > N_TTY_BUF_SIZE)
n += N_TTY_BUF_SIZE;
n_tty: Fix poll() after buffer-limited eof push read commit 40d5e0905a03 ("n_tty: Fix EOF push handling") fixed EOF push for reads. However, that approach still allows a condition mismatch between poll() and read(), where poll() returns POLLIN but read() blocks. This state can happen when a previous read() returned because the user buffer was full and the next character was an EOF not at the beginning of the line. While the next read() will properly identify the condition and advance the read buffer tail without improperly indicating an EOF file condition (ie., read() will not mistakenly return 0), poll() will mistakenly indicate POLLIN. Although a possible solution would be to peek at the input buffer in n_tty_poll(), the better solution in this patch is to eat the EOF during the previous read() (ie., fix the problem by eliminating the condition). The current canon line buffer copy limits the scan for next end-of-line to the smaller of either, a. the remaining user buffer size b. completed lines in the input buffer When the remaining user buffer size is exactly one less than the end-of-line marked by EOF push, the EOF is not scanned nor skipped but left for subsequent reads. In the example below, the scan index 'eol' has stopped at the EOF because it is past the scan limit of 5 (not because it has found the next set bit in read_flags) user buffer [*nr = 5] _ _ _ _ _ read_flags 0 0 0 0 0 1 input buffer h e l l o [EOF] ^ ^ / / tail eol result: found = 0, tail += 5, *nr += 5 Instead, allow the scan to peek ahead 1 byte (while still limiting the scan to completed lines in the input buffer). For the example above, result: found = 1, tail += 6, *nr += 5 Because the scan limit is now bumped +1 byte, when the scan is completed, the tail advance and the user buffer copy limit is re-clamped to *nr when EOF is _not_ found. Fixes: 40d5e0905a03 ("n_tty: Fix EOF push handling") Cc: <stable@vger.kernel.org> # 3.12+ Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-11-28 02:59:20 +08:00
c = n + found;
tty: n_tty: do not look ahead for EOL character past the end of the buffer Daniel Gibson reports that the n_tty code gets line termination wrong in very specific cases: "If you feed a line with exactly 64 chars + terminating newline, and directly afterwards (without reading) another line into a pseudo terminal, the the first read() on the other side will return the 64 char line *without* terminating newline, and the next read() will return the missing terminating newline AND the complete next line (if it fits in the buffer)" and bisected the behavior to commit 3b830a9c34d5 ("tty: convert tty_ldisc_ops 'read()' function to take a kernel pointer"). Now, digging deeper, it turns out that the behavior isn't exactly new: what changed in commit 3b830a9c34d5 was that the tty line discipline .read() function is now passed an intermediate kernel buffer rather than the final user space buffer. And that intermediate kernel buffer is 64 bytes in size - thus that special case with exactly 64 bytes plus terminating newline. The same problem did exist before, but historically the boundary was not the 64-byte chunk, but the user-supplied buffer size, which is obviously generally bigger (and potentially bigger than N_TTY_BUF_SIZE, which would hide the issue entirely). The reason is that the n_tty canon_copy_from_read_buf() code would look ahead for the EOL character one byte further than it would actually copy. It would then decide that it had found the terminator, and unmark it as an EOL character - which in turn explains why the next read wouldn't then be terminated by it. Now, the reason it did all this in the first place is related to some historical and pretty obscure EOF behavior, see commit ac8f3bf8832a ("n_tty: Fix poll() after buffer-limited eof push read") and commit 40d5e0905a03 ("n_tty: Fix EOF push handling"). And the reason for the EOL confusion is that we treat EOF as a special EOL condition, with the EOL character being NUL (aka "__DISABLED_CHAR" in the kernel sources). So that EOF look-ahead also affects the normal EOL handling. This patch just removes the look-ahead that causes problems, because EOL is much more critical than the historical "EOF in the middle of a line that coincides with the end of the buffer" handling ever was. Now, it is possible that we should indeed re-introduce the "look at next character to see if it's a EOF" behavior, but if so, that should be done not at the kernel buffer chunk boundary in canon_copy_from_read_buf(), but at a higher level, when we run out of the user buffer. In particular, the place to do that would be at the top of 'n_tty_read()', where we check if it's a continuation of a previously started read, and there is no more buffer space left, we could decide to just eat the __DISABLED_CHAR at that point. But that would be a separate patch, because I suspect nobody actually cares, and I'd like to get a report about it before bothering. Fixes: 3b830a9c34d5 ("tty: convert tty_ldisc_ops 'read()' function to take a kernel pointer") Fixes: ac8f3bf8832a ("n_tty: Fix poll() after buffer-limited eof push read") Fixes: 40d5e0905a03 ("n_tty: Fix EOF push handling") Link: https://bugzilla.kernel.org/show_bug.cgi?id=215611 Reported-and-tested-by: Daniel Gibson <metalcaedes@gmail.com> Cc: Peter Hurley <peter@hurleysoftware.com> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Jiri Slaby <jirislaby@kernel.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2022-02-16 07:28:00 +08:00
if (!found || read_buf(ldata, eol) != __DISABLED_CHAR)
n_tty: Fix poll() after buffer-limited eof push read commit 40d5e0905a03 ("n_tty: Fix EOF push handling") fixed EOF push for reads. However, that approach still allows a condition mismatch between poll() and read(), where poll() returns POLLIN but read() blocks. This state can happen when a previous read() returned because the user buffer was full and the next character was an EOF not at the beginning of the line. While the next read() will properly identify the condition and advance the read buffer tail without improperly indicating an EOF file condition (ie., read() will not mistakenly return 0), poll() will mistakenly indicate POLLIN. Although a possible solution would be to peek at the input buffer in n_tty_poll(), the better solution in this patch is to eat the EOF during the previous read() (ie., fix the problem by eliminating the condition). The current canon line buffer copy limits the scan for next end-of-line to the smaller of either, a. the remaining user buffer size b. completed lines in the input buffer When the remaining user buffer size is exactly one less than the end-of-line marked by EOF push, the EOF is not scanned nor skipped but left for subsequent reads. In the example below, the scan index 'eol' has stopped at the EOF because it is past the scan limit of 5 (not because it has found the next set bit in read_flags) user buffer [*nr = 5] _ _ _ _ _ read_flags 0 0 0 0 0 1 input buffer h e l l o [EOF] ^ ^ / / tail eol result: found = 0, tail += 5, *nr += 5 Instead, allow the scan to peek ahead 1 byte (while still limiting the scan to completed lines in the input buffer). For the example above, result: found = 1, tail += 6, *nr += 5 Because the scan limit is now bumped +1 byte, when the scan is completed, the tail advance and the user buffer copy limit is re-clamped to *nr when EOF is _not_ found. Fixes: 40d5e0905a03 ("n_tty: Fix EOF push handling") Cc: <stable@vger.kernel.org> # 3.12+ Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-11-28 02:59:20 +08:00
n = c;
n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu tail:%zu more:%zu\n",
__func__, eol, found, n, c, tail, more);
tty_copy(tty, *kbp, tail, n);
*kbp += n;
*nr -= n;
if (found)
clear_bit(eol, ldata->read_flags);
smp_store_release(&ldata->read_tail, ldata->read_tail + c);
if (found) {
n_tty: Fix buffer overruns with larger-than-4k pastes readline() inadvertently triggers an error recovery path when pastes larger than 4k overrun the line discipline buffer. The error recovery path discards input when the line discipline buffer is full and operating in canonical mode and no newline has been received. Because readline() changes the termios to non-canonical mode to read the line char-by-char, the line discipline buffer can become full, and then when readline() restores termios back to canonical mode for the caller, the now-full line discipline buffer triggers the error recovery. When changing termios from non-canon to canon mode and the read buffer contains data, simulate an EOF push _without_ the DISABLED_CHAR in the read buffer. Importantly for the readline() problem, the termios can be changed back to non-canonical mode without changes to the read buffer occurring; ie., as if the previous termios change had not happened (as long as no intervening read took place). Preserve existing userspace behavior which allows '\0's already received in non-canon mode to be read as '\0's in canon mode (rather than trigger add'l EOF pushes or an actual EOF). Patch based on original proposal and discussion here https://bugzilla.kernel.org/show_bug.cgi?id=55991 by Stas Sergeev <stsp@users.sourceforge.net> Reported-by: Margarita Manterola <margamanterola@gmail.com> Cc: Maximiliano Curia <maxy@gnuservers.com.ar> Cc: Pavel Machek <pavel@ucw.cz> Cc: Arkadiusz Miskiewicz <a.miskiewicz@gmail.com> Acked-by: Stas Sergeev <stsp@users.sourceforge.net> Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-12-11 06:12:02 +08:00
if (!ldata->push)
ldata->line_start = ldata->read_tail;
else
ldata->push = 0;
tty_audit_push();
return false;
}
/* No EOL found - do a continuation retry if there is more data */
return ldata->read_tail != canon_head;
}
tty: n_tty: Restore EOF push handling behavior TTYs in ICANON mode have a special case that allows "pushing" a line without a regular EOL character (like newline), by using EOF (the EOT character - ASCII 0x4) as a pseudo-EOL. It is silently discarded, so the reader of the PTS will receive the line *without* EOF or any other terminating character. This special case has an edge case: What happens if the readers buffer is the same size as the line (without EOF)? Will they be able to tell if the whole line is received, i.e. if the next read() will return more of the same line or the next line? There are two possibilities, that both have (dis)advantages: 1. The next read() returns 0. FreeBSD (13.0) and OSX (10.11) do this. Advantage: The reader can interpret this as "the line is over". Disadvantage: read() returning 0 means EOF, the reader could also interpret it as "there's no more data" and stop reading or even close the PT. 2. The next read() returns the next line, the EOF is silently discarded. Solaris (or at least OpenIndiana 2021.10) does this, Linux has done do this since commit 40d5e0905a03 ("n_tty: Fix EOF push handling"); this behavior was recently broken by commit 359303076163 ("tty: n_tty: do not look ahead for EOL character past the end of the buffer"). Advantage: read() won't return 0 (EOF), reader less likely to be confused (and things like `while(read(..)>0)` don't break) Disadvantage: The reader can't really know if the read() continues the last line (that filled the whole read buffer) or starts a new line. As both options are defensible (and are used by other Unix-likes), it's best to stick to the "old" behavior since "n_tty: Fix EOF push handling" of 2013, i.e. silently discard that EOF. This patch - that I actually got from Linus for testing and only modified slightly - restores that behavior by skipping an EOF character if it's the next character after reading is done. Based on a patch from Linus Torvalds. Link: https://bugzilla.kernel.org/show_bug.cgi?id=215611 Fixes: 359303076163 ("tty: n_tty: do not look ahead for EOL character past the end of the buffer") Cc: Peter Hurley <peter@hurleysoftware.com> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Jiri Slaby <jirislaby@kernel.org> Reviewed-and-tested-by: Daniel Gibson <daniel@gibson.sh> Acked-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Daniel Gibson <daniel@gibson.sh> Link: https://lore.kernel.org/r/20220329235810.452513-2-daniel@gibson.sh Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-03-30 07:58:10 +08:00
/*
* If we finished a read at the exact location of an
* EOF (special EOL character that's a __DISABLED_CHAR)
* in the stream, silently eat the EOF.
*/
static void canon_skip_eof(struct n_tty_data *ldata)
tty: n_tty: Restore EOF push handling behavior TTYs in ICANON mode have a special case that allows "pushing" a line without a regular EOL character (like newline), by using EOF (the EOT character - ASCII 0x4) as a pseudo-EOL. It is silently discarded, so the reader of the PTS will receive the line *without* EOF or any other terminating character. This special case has an edge case: What happens if the readers buffer is the same size as the line (without EOF)? Will they be able to tell if the whole line is received, i.e. if the next read() will return more of the same line or the next line? There are two possibilities, that both have (dis)advantages: 1. The next read() returns 0. FreeBSD (13.0) and OSX (10.11) do this. Advantage: The reader can interpret this as "the line is over". Disadvantage: read() returning 0 means EOF, the reader could also interpret it as "there's no more data" and stop reading or even close the PT. 2. The next read() returns the next line, the EOF is silently discarded. Solaris (or at least OpenIndiana 2021.10) does this, Linux has done do this since commit 40d5e0905a03 ("n_tty: Fix EOF push handling"); this behavior was recently broken by commit 359303076163 ("tty: n_tty: do not look ahead for EOL character past the end of the buffer"). Advantage: read() won't return 0 (EOF), reader less likely to be confused (and things like `while(read(..)>0)` don't break) Disadvantage: The reader can't really know if the read() continues the last line (that filled the whole read buffer) or starts a new line. As both options are defensible (and are used by other Unix-likes), it's best to stick to the "old" behavior since "n_tty: Fix EOF push handling" of 2013, i.e. silently discard that EOF. This patch - that I actually got from Linus for testing and only modified slightly - restores that behavior by skipping an EOF character if it's the next character after reading is done. Based on a patch from Linus Torvalds. Link: https://bugzilla.kernel.org/show_bug.cgi?id=215611 Fixes: 359303076163 ("tty: n_tty: do not look ahead for EOL character past the end of the buffer") Cc: Peter Hurley <peter@hurleysoftware.com> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Jiri Slaby <jirislaby@kernel.org> Reviewed-and-tested-by: Daniel Gibson <daniel@gibson.sh> Acked-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Daniel Gibson <daniel@gibson.sh> Link: https://lore.kernel.org/r/20220329235810.452513-2-daniel@gibson.sh Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-03-30 07:58:10 +08:00
{
size_t tail, canon_head;
canon_head = smp_load_acquire(&ldata->canon_head);
tail = ldata->read_tail;
// No data?
if (tail == canon_head)
return;
// See if the tail position is EOF in the circular buffer
tail &= (N_TTY_BUF_SIZE - 1);
if (!test_bit(tail, ldata->read_flags))
return;
if (read_buf(ldata, tail) != __DISABLED_CHAR)
return;
// Clear the EOL bit, skip the EOF char.
clear_bit(tail, ldata->read_flags);
smp_store_release(&ldata->read_tail, ldata->read_tail + 1);
}
/**
* job_control - check job control
* @tty: tty
* @file: file handle
*
* Perform job control management checks on this @file/@tty descriptor and if
* appropriate send any needed signals and return a negative error code if
* action should be taken.
*
* Locking:
* * redirected write test is safe
* * current->signal->tty check is safe
* * ctrl.lock to safely reference @tty->ctrl.pgrp
*/
static int job_control(struct tty_struct *tty, struct file *file)
{
/* Job control check -- must be done at start and after
every sleep (POSIX.1 7.1.1.4). */
/* NOTE: not yet done after every sleep pending a thorough
check of the logic of this change. -- jlc */
/* don't stop on /dev/console */
if (file->f_op->write_iter == redirected_tty_write)
return 0;
return __tty_check_change(tty, SIGTTIN);
}
/**
* n_tty_read - read function for tty
* @tty: tty device
* @file: file object
* @kbuf: kernelspace buffer pointer
* @nr: size of I/O
* @cookie: if non-%NULL, this is a continuation read
* @offset: where to continue reading from (unused in n_tty)
*
* Perform reads for the line discipline. We are guaranteed that the line
* discipline will not be closed under us but we may get multiple parallel
* readers and must handle this ourselves. We may also get a hangup. Always
* called in user context, may sleep.
*
* This code must be sure never to sleep through a hangup.
*
* Locking: n_tty_read()/consumer path:
* claims non-exclusive termios_rwsem;
* publishes read_tail
*/
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, u8 *kbuf,
size_t nr, void **cookie, unsigned long offset)
{
struct n_tty_data *ldata = tty->disc_data;
u8 *kb = kbuf;
DEFINE_WAIT_FUNC(wait, woken_wake_function);
int minimum, time;
ssize_t retval;
long timeout;
bool packet;
size_t old_tail;
tty: teach n_tty line discipline about the new "cookie continuations" With the conversion to do the tty ldisc read operations in small chunks, the n_tty line discipline became noticeably slower for throughput oriented loads, because rather than read things in up to 2kB chunks, it would return at most 64 bytes per read() system call. The cost is mainly all in the "do system calls over and over", not really in the new "copy to an extra kernel buffer". This can be fixed by teaching the n_tty line discipline about the "cookie continuation" model, which the chunking code supports because things like hdlc need to be able to handle packets up to 64kB in size. Doing that doesn't just get us back to the old performace, but to much better performance: my stupid "copy 10MB of data over a pty" test program is now almost twice as fast as it used to be (going down from 0.1s to 0.054s). This is entirely because it now creates maximal chunks (which happens to be "one byte less than one page" due to how we do the circular tty buffers). NOTE! This case only handles the simpler non-icanon case, which is the one where people may care about throughput. I'm going to do the icanon case later too, because while performance isn't a major issue for that, there may be programs that think they'll always get a full line and don't like the 64-byte chunking for that reason. Such programs are arguably buggy (signals etc can cause random partial results from tty reads anyway), and good programs will handle such partial reads, but expecting everybody to write "good programs" has never been a winning policy for the kernel.. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2021-01-20 10:14:20 +08:00
/*
* Is this a continuation of a read started earler?
*
* If so, we still hold the atomic_read_lock and the
* termios_rwsem, and can just continue to copy data.
*/
if (*cookie) {
if (ldata->icanon && !L_EXTPROC(tty)) {
tty: n_tty: Restore EOF push handling behavior TTYs in ICANON mode have a special case that allows "pushing" a line without a regular EOL character (like newline), by using EOF (the EOT character - ASCII 0x4) as a pseudo-EOL. It is silently discarded, so the reader of the PTS will receive the line *without* EOF or any other terminating character. This special case has an edge case: What happens if the readers buffer is the same size as the line (without EOF)? Will they be able to tell if the whole line is received, i.e. if the next read() will return more of the same line or the next line? There are two possibilities, that both have (dis)advantages: 1. The next read() returns 0. FreeBSD (13.0) and OSX (10.11) do this. Advantage: The reader can interpret this as "the line is over". Disadvantage: read() returning 0 means EOF, the reader could also interpret it as "there's no more data" and stop reading or even close the PT. 2. The next read() returns the next line, the EOF is silently discarded. Solaris (or at least OpenIndiana 2021.10) does this, Linux has done do this since commit 40d5e0905a03 ("n_tty: Fix EOF push handling"); this behavior was recently broken by commit 359303076163 ("tty: n_tty: do not look ahead for EOL character past the end of the buffer"). Advantage: read() won't return 0 (EOF), reader less likely to be confused (and things like `while(read(..)>0)` don't break) Disadvantage: The reader can't really know if the read() continues the last line (that filled the whole read buffer) or starts a new line. As both options are defensible (and are used by other Unix-likes), it's best to stick to the "old" behavior since "n_tty: Fix EOF push handling" of 2013, i.e. silently discard that EOF. This patch - that I actually got from Linus for testing and only modified slightly - restores that behavior by skipping an EOF character if it's the next character after reading is done. Based on a patch from Linus Torvalds. Link: https://bugzilla.kernel.org/show_bug.cgi?id=215611 Fixes: 359303076163 ("tty: n_tty: do not look ahead for EOL character past the end of the buffer") Cc: Peter Hurley <peter@hurleysoftware.com> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Jiri Slaby <jirislaby@kernel.org> Reviewed-and-tested-by: Daniel Gibson <daniel@gibson.sh> Acked-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Daniel Gibson <daniel@gibson.sh> Link: https://lore.kernel.org/r/20220329235810.452513-2-daniel@gibson.sh Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-03-30 07:58:10 +08:00
/*
* If we have filled the user buffer, see
* if we should skip an EOF character before
* releasing the lock and returning done.
*/
if (!nr)
canon_skip_eof(ldata);
tty: n_tty: Restore EOF push handling behavior TTYs in ICANON mode have a special case that allows "pushing" a line without a regular EOL character (like newline), by using EOF (the EOT character - ASCII 0x4) as a pseudo-EOL. It is silently discarded, so the reader of the PTS will receive the line *without* EOF or any other terminating character. This special case has an edge case: What happens if the readers buffer is the same size as the line (without EOF)? Will they be able to tell if the whole line is received, i.e. if the next read() will return more of the same line or the next line? There are two possibilities, that both have (dis)advantages: 1. The next read() returns 0. FreeBSD (13.0) and OSX (10.11) do this. Advantage: The reader can interpret this as "the line is over". Disadvantage: read() returning 0 means EOF, the reader could also interpret it as "there's no more data" and stop reading or even close the PT. 2. The next read() returns the next line, the EOF is silently discarded. Solaris (or at least OpenIndiana 2021.10) does this, Linux has done do this since commit 40d5e0905a03 ("n_tty: Fix EOF push handling"); this behavior was recently broken by commit 359303076163 ("tty: n_tty: do not look ahead for EOL character past the end of the buffer"). Advantage: read() won't return 0 (EOF), reader less likely to be confused (and things like `while(read(..)>0)` don't break) Disadvantage: The reader can't really know if the read() continues the last line (that filled the whole read buffer) or starts a new line. As both options are defensible (and are used by other Unix-likes), it's best to stick to the "old" behavior since "n_tty: Fix EOF push handling" of 2013, i.e. silently discard that EOF. This patch - that I actually got from Linus for testing and only modified slightly - restores that behavior by skipping an EOF character if it's the next character after reading is done. Based on a patch from Linus Torvalds. Link: https://bugzilla.kernel.org/show_bug.cgi?id=215611 Fixes: 359303076163 ("tty: n_tty: do not look ahead for EOL character past the end of the buffer") Cc: Peter Hurley <peter@hurleysoftware.com> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Jiri Slaby <jirislaby@kernel.org> Reviewed-and-tested-by: Daniel Gibson <daniel@gibson.sh> Acked-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Daniel Gibson <daniel@gibson.sh> Link: https://lore.kernel.org/r/20220329235810.452513-2-daniel@gibson.sh Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-03-30 07:58:10 +08:00
else if (canon_copy_from_read_buf(tty, &kb, &nr))
return kb - kbuf;
} else {
if (copy_from_read_buf(tty, &kb, &nr))
return kb - kbuf;
}
tty: teach n_tty line discipline about the new "cookie continuations" With the conversion to do the tty ldisc read operations in small chunks, the n_tty line discipline became noticeably slower for throughput oriented loads, because rather than read things in up to 2kB chunks, it would return at most 64 bytes per read() system call. The cost is mainly all in the "do system calls over and over", not really in the new "copy to an extra kernel buffer". This can be fixed by teaching the n_tty line discipline about the "cookie continuation" model, which the chunking code supports because things like hdlc need to be able to handle packets up to 64kB in size. Doing that doesn't just get us back to the old performace, but to much better performance: my stupid "copy 10MB of data over a pty" test program is now almost twice as fast as it used to be (going down from 0.1s to 0.054s). This is entirely because it now creates maximal chunks (which happens to be "one byte less than one page" due to how we do the circular tty buffers). NOTE! This case only handles the simpler non-icanon case, which is the one where people may care about throughput. I'm going to do the icanon case later too, because while performance isn't a major issue for that, there may be programs that think they'll always get a full line and don't like the 64-byte chunking for that reason. Such programs are arguably buggy (signals etc can cause random partial results from tty reads anyway), and good programs will handle such partial reads, but expecting everybody to write "good programs" has never been a winning policy for the kernel.. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2021-01-20 10:14:20 +08:00
/* No more data - release locks and stop retries */
n_tty_kick_worker(tty);
n_tty_check_unthrottle(tty);
up_read(&tty->termios_rwsem);
mutex_unlock(&ldata->atomic_read_lock);
*cookie = NULL;
return kb - kbuf;
}
retval = job_control(tty, file);
if (retval < 0)
return retval;
n_tty: Fix termios_rwsem lockdep false positive Lockdep reports a circular lock dependency between atomic_read_lock and termios_rwsem [1]. However, a lock order deadlock is not possible since CPU1 only holds a read lock which cannot prevent CPU0 from also acquiring a read lock on the same r/w semaphore. Unfortunately, lockdep cannot currently distinguish whether the locks are read or write for any particular lock graph, merely that the locks _were_ previously read and/or write. Until lockdep is fixed, re-order atomic_read_lock so termios_rwsem can be dropped and reacquired without triggering lockdep. Patch based on original posted here https://lkml.org/lkml/2013/8/1/510 by Sergey Senozhatsky <sergey.senozhatsky@gmail.com> [1] Initial lockdep report from Artem Savkov <artem.savkov@gmail.com> ====================================================== [ INFO: possible circular locking dependency detected ] 3.11.0-rc3-next-20130730+ #140 Tainted: G W ------------------------------------------------------- bash/1198 is trying to acquire lock: (&tty->termios_rwsem){++++..}, at: [<ffffffff816aa3bb>] n_tty_read+0x49b/0x660 but task is already holding lock: (&ldata->atomic_read_lock){+.+...}, at: [<ffffffff816aa0f0>] n_tty_read+0x1d0/0x660 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (&ldata->atomic_read_lock){+.+...}: [<ffffffff811111cc>] validate_chain+0x73c/0x850 [<ffffffff811117e0>] __lock_acquire+0x500/0x5d0 [<ffffffff81111a29>] lock_acquire+0x179/0x1d0 [<ffffffff81d34b9c>] mutex_lock_interruptible_nested+0x7c/0x540 [<ffffffff816aa0f0>] n_tty_read+0x1d0/0x660 [<ffffffff816a3bb6>] tty_read+0x86/0xf0 [<ffffffff811f21d3>] vfs_read+0xc3/0x130 [<ffffffff811f2702>] SyS_read+0x62/0xa0 [<ffffffff81d45259>] system_call_fastpath+0x16/0x1b -> #0 (&tty->termios_rwsem){++++..}: [<ffffffff8111064f>] check_prev_add+0x14f/0x590 [<ffffffff811111cc>] validate_chain+0x73c/0x850 [<ffffffff811117e0>] __lock_acquire+0x500/0x5d0 [<ffffffff81111a29>] lock_acquire+0x179/0x1d0 [<ffffffff81d372c1>] down_read+0x51/0xa0 [<ffffffff816aa3bb>] n_tty_read+0x49b/0x660 [<ffffffff816a3bb6>] tty_read+0x86/0xf0 [<ffffffff811f21d3>] vfs_read+0xc3/0x130 [<ffffffff811f2702>] SyS_read+0x62/0xa0 [<ffffffff81d45259>] system_call_fastpath+0x16/0x1b other info that might help us debug this: Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(&ldata->atomic_read_lock); lock(&tty->termios_rwsem); lock(&ldata->atomic_read_lock); lock(&tty->termios_rwsem); *** DEADLOCK *** 2 locks held by bash/1198: #0: (&tty->ldisc_sem){.+.+.+}, at: [<ffffffff816ade04>] tty_ldisc_ref_wait+0x24/0x60 #1: (&ldata->atomic_read_lock){+.+...}, at: [<ffffffff816aa0f0>] n_tty_read+0x1d0/0x660 stack backtrace: CPU: 1 PID: 1198 Comm: bash Tainted: G W 3.11.0-rc3-next-20130730+ #140 Hardware name: Bochs Bochs, BIOS Bochs 01/01/2007 0000000000000000 ffff880019acdb28 ffffffff81d34074 0000000000000002 0000000000000000 ffff880019acdb78 ffffffff8110ed75 ffff880019acdb98 ffff880019fd0000 ffff880019acdb78 ffff880019fd0638 ffff880019fd0670 Call Trace: [<ffffffff81d34074>] dump_stack+0x59/0x7d [<ffffffff8110ed75>] print_circular_bug+0x105/0x120 [<ffffffff8111064f>] check_prev_add+0x14f/0x590 [<ffffffff81d3ab5f>] ? _raw_spin_unlock_irq+0x4f/0x70 [<ffffffff811111cc>] validate_chain+0x73c/0x850 [<ffffffff8110ae0f>] ? trace_hardirqs_off_caller+0x1f/0x190 [<ffffffff811117e0>] __lock_acquire+0x500/0x5d0 [<ffffffff81111a29>] lock_acquire+0x179/0x1d0 [<ffffffff816aa3bb>] ? n_tty_read+0x49b/0x660 [<ffffffff81d372c1>] down_read+0x51/0xa0 [<ffffffff816aa3bb>] ? n_tty_read+0x49b/0x660 [<ffffffff816aa3bb>] n_tty_read+0x49b/0x660 [<ffffffff810e4130>] ? try_to_wake_up+0x210/0x210 [<ffffffff816a3bb6>] tty_read+0x86/0xf0 [<ffffffff811f21d3>] vfs_read+0xc3/0x130 [<ffffffff811f2702>] SyS_read+0x62/0xa0 [<ffffffff815e24ee>] ? trace_hardirqs_on_thunk+0x3a/0x3f [<ffffffff81d45259>] system_call_fastpath+0x16/0x1b Reported-by: Artem Savkov <artem.savkov@gmail.com> Reported-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com> Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-08-11 20:04:23 +08:00
/*
* Internal serialization of reads.
*/
if (file->f_flags & O_NONBLOCK) {
if (!mutex_trylock(&ldata->atomic_read_lock))
return -EAGAIN;
} else {
if (mutex_lock_interruptible(&ldata->atomic_read_lock))
return -ERESTARTSYS;
}
down_read(&tty->termios_rwsem);
minimum = time = 0;
timeout = MAX_SCHEDULE_TIMEOUT;
if (!ldata->icanon) {
minimum = MIN_CHAR(tty);
if (minimum) {
time = (HZ / 10) * TIME_CHAR(tty);
} else {
timeout = (HZ / 10) * TIME_CHAR(tty);
minimum = 1;
}
}
packet = tty->ctrl.packet;
old_tail = ldata->read_tail;
add_wait_queue(&tty->read_wait, &wait);
while (nr) {
/* First test for status change. */
if (packet && tty->link->ctrl.pktstatus) {
u8 cs;
if (kb != kbuf)
break;
spin_lock_irq(&tty->link->ctrl.lock);
cs = tty->link->ctrl.pktstatus;
tty->link->ctrl.pktstatus = 0;
spin_unlock_irq(&tty->link->ctrl.lock);
*kb++ = cs;
nr--;
break;
}
if (!input_available_p(tty, 0)) {
up_read(&tty->termios_rwsem);
tty_buffer_flush_work(tty->port);
down_read(&tty->termios_rwsem);
if (!input_available_p(tty, 0)) {
if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
retval = -EIO;
break;
}
if (tty_hung_up_p(file))
break;
tty: make n_tty_read() always abort if hangup is in progress A tty is hung up by __tty_hangup() setting file->f_op to hung_up_tty_fops, which is skipped on ttys whose write operation isn't tty_write(). This means that, for example, /dev/console whose write op is redirected_tty_write() is never actually marked hung up. Because n_tty_read() uses the hung up status to decide whether to abort the waiting readers, the lack of hung-up marking can lead to the following scenario. 1. A session contains two processes. The leader and its child. The child ignores SIGHUP. 2. The leader exits and starts disassociating from the controlling terminal (/dev/console). 3. __tty_hangup() skips setting f_op to hung_up_tty_fops. 4. SIGHUP is delivered and ignored. 5. tty_ldisc_hangup() is invoked. It wakes up the waits which should clear the read lockers of tty->ldisc_sem. 6. The reader wakes up but because tty_hung_up_p() is false, it doesn't abort and goes back to sleep while read-holding tty->ldisc_sem. 7. The leader progresses to tty_ldisc_lock() in tty_ldisc_hangup() and is now stuck in D sleep indefinitely waiting for tty->ldisc_sem. The following is Alan's explanation on why some ttys aren't hung up. http://lkml.kernel.org/r/20171101170908.6ad08580@alans-desktop 1. It broke the serial consoles because they would hang up and close down the hardware. With tty_port that *should* be fixable properly for any cases remaining. 2. The console layer was (and still is) completely broken and doens't refcount properly. So if you turn on console hangups it breaks (as indeed does freeing consoles and half a dozen other things). As neither can be fixed quickly, this patch works around the problem by introducing a new flag, TTY_HUPPING, which is used solely to tell n_tty_read() that hang-up is in progress for the console and the readers should be aborted regardless of the hung-up status of the device. The following is a sample hung task warning caused by this issue. INFO: task agetty:2662 blocked for more than 120 seconds. Not tainted 4.11.3-dbg-tty-lockup-02478-gfd6c7ee-dirty #28 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. 0 2662 1 0x00000086 Call Trace: __schedule+0x267/0x890 schedule+0x36/0x80 schedule_timeout+0x23c/0x2e0 ldsem_down_write+0xce/0x1f6 tty_ldisc_lock+0x16/0x30 tty_ldisc_hangup+0xb3/0x1b0 __tty_hangup+0x300/0x410 disassociate_ctty+0x6c/0x290 do_exit+0x7ef/0xb00 do_group_exit+0x3f/0xa0 get_signal+0x1b3/0x5d0 do_signal+0x28/0x660 exit_to_usermode_loop+0x46/0x86 do_syscall_64+0x9c/0xb0 entry_SYSCALL64_slow_path+0x25/0x25 The following is the repro. Run "$PROG /dev/console". The parent process hangs in D state. #include <sys/types.h> #include <sys/stat.h> #include <sys/wait.h> #include <sys/ioctl.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <signal.h> #include <time.h> #include <termios.h> int main(int argc, char **argv) { struct sigaction sact = { .sa_handler = SIG_IGN }; struct timespec ts1s = { .tv_sec = 1 }; pid_t pid; int fd; if (argc < 2) { fprintf(stderr, "test-hung-tty /dev/$TTY\n"); return 1; } /* fork a child to ensure that it isn't already the session leader */ pid = fork(); if (pid < 0) { perror("fork"); return 1; } if (pid > 0) { /* top parent, wait for everyone */ while (waitpid(-1, NULL, 0) >= 0) ; if (errno != ECHILD) perror("waitpid"); return 0; } /* new session, start a new session and set the controlling tty */ if (setsid() < 0) { perror("setsid"); return 1; } fd = open(argv[1], O_RDWR); if (fd < 0) { perror("open"); return 1; } if (ioctl(fd, TIOCSCTTY, 1) < 0) { perror("ioctl"); return 1; } /* fork a child, sleep a bit and exit */ pid = fork(); if (pid < 0) { perror("fork"); return 1; } if (pid > 0) { nanosleep(&ts1s, NULL); printf("Session leader exiting\n"); exit(0); } /* * The child ignores SIGHUP and keeps reading from the controlling * tty. Because SIGHUP is ignored, the child doesn't get killed on * parent exit and the bug in n_tty makes the read(2) block the * parent's control terminal hangup attempt. The parent ends up in * D sleep until the child is explicitly killed. */ sigaction(SIGHUP, &sact, NULL); printf("Child reading tty\n"); while (1) { char buf[1024]; if (read(fd, buf, sizeof(buf)) < 0) { perror("read"); return 1; } } return 0; } Signed-off-by: Tejun Heo <tj@kernel.org> Cc: Alan Cox <alan@llwyncelyn.cymru> Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-02-13 23:38:08 +08:00
/*
* Abort readers for ttys which never actually
* get hung up. See __tty_hangup().
*/
if (test_bit(TTY_HUPPING, &tty->flags))
break;
if (!timeout)
break;
if (tty_io_nonblock(tty, file)) {
retval = -EAGAIN;
break;
}
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
up_read(&tty->termios_rwsem);
timeout = wait_woken(&wait, TASK_INTERRUPTIBLE,
timeout);
down_read(&tty->termios_rwsem);
continue;
}
}
if (ldata->icanon && !L_EXTPROC(tty)) {
if (canon_copy_from_read_buf(tty, &kb, &nr))
goto more_to_be_read;
} else {
/* Deal with packet mode. */
if (packet && kb == kbuf) {
*kb++ = TIOCPKT_DATA;
nr--;
}
tty: teach n_tty line discipline about the new "cookie continuations" With the conversion to do the tty ldisc read operations in small chunks, the n_tty line discipline became noticeably slower for throughput oriented loads, because rather than read things in up to 2kB chunks, it would return at most 64 bytes per read() system call. The cost is mainly all in the "do system calls over and over", not really in the new "copy to an extra kernel buffer". This can be fixed by teaching the n_tty line discipline about the "cookie continuation" model, which the chunking code supports because things like hdlc need to be able to handle packets up to 64kB in size. Doing that doesn't just get us back to the old performace, but to much better performance: my stupid "copy 10MB of data over a pty" test program is now almost twice as fast as it used to be (going down from 0.1s to 0.054s). This is entirely because it now creates maximal chunks (which happens to be "one byte less than one page" due to how we do the circular tty buffers). NOTE! This case only handles the simpler non-icanon case, which is the one where people may care about throughput. I'm going to do the icanon case later too, because while performance isn't a major issue for that, there may be programs that think they'll always get a full line and don't like the 64-byte chunking for that reason. Such programs are arguably buggy (signals etc can cause random partial results from tty reads anyway), and good programs will handle such partial reads, but expecting everybody to write "good programs" has never been a winning policy for the kernel.. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2021-01-20 10:14:20 +08:00
/*
* Copy data, and if there is more to be had
* and we have nothing more to wait for, then
* let's mark us for retries.
*
* NOTE! We return here with both the termios_sem
* and atomic_read_lock still held, the retries
* will release them when done.
*/
if (copy_from_read_buf(tty, &kb, &nr) && kb - kbuf >= minimum) {
more_to_be_read:
tty: teach n_tty line discipline about the new "cookie continuations" With the conversion to do the tty ldisc read operations in small chunks, the n_tty line discipline became noticeably slower for throughput oriented loads, because rather than read things in up to 2kB chunks, it would return at most 64 bytes per read() system call. The cost is mainly all in the "do system calls over and over", not really in the new "copy to an extra kernel buffer". This can be fixed by teaching the n_tty line discipline about the "cookie continuation" model, which the chunking code supports because things like hdlc need to be able to handle packets up to 64kB in size. Doing that doesn't just get us back to the old performace, but to much better performance: my stupid "copy 10MB of data over a pty" test program is now almost twice as fast as it used to be (going down from 0.1s to 0.054s). This is entirely because it now creates maximal chunks (which happens to be "one byte less than one page" due to how we do the circular tty buffers). NOTE! This case only handles the simpler non-icanon case, which is the one where people may care about throughput. I'm going to do the icanon case later too, because while performance isn't a major issue for that, there may be programs that think they'll always get a full line and don't like the 64-byte chunking for that reason. Such programs are arguably buggy (signals etc can cause random partial results from tty reads anyway), and good programs will handle such partial reads, but expecting everybody to write "good programs" has never been a winning policy for the kernel.. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2021-01-20 10:14:20 +08:00
remove_wait_queue(&tty->read_wait, &wait);
*cookie = cookie;
return kb - kbuf;
}
}
n_tty_check_unthrottle(tty);
if (kb - kbuf >= minimum)
break;
if (time)
timeout = time;
}
tty: fix hang on tty device with no_room set It is possible to hang pty devices in this case, the reader was blocking at epoll on master side, the writer was sleeping at wait_woken inside n_tty_write on slave side, and the write buffer on tty_port was full, we found that the reader and writer would never be woken again and blocked forever. The problem was caused by a race between reader and kworker: n_tty_read(reader): n_tty_receive_buf_common(kworker): copy_from_read_buf()| |room = N_TTY_BUF_SIZE - (ldata->read_head - tail) |room <= 0 n_tty_kick_worker() | |ldata->no_room = true After writing to slave device, writer wakes up kworker to flush data on tty_port to reader, and the kworker finds that reader has no room to store data so room <= 0 is met. At this moment, reader consumes all the data on reader buffer and calls n_tty_kick_worker to check ldata->no_room which is false and reader quits reading. Then kworker sets ldata->no_room=true and quits too. If write buffer is not full, writer will wake kworker to flush data again after following writes, but if write buffer is full and writer goes to sleep, kworker will never be woken again and tty device is blocked. This problem can be solved with a check for read buffer size inside n_tty_receive_buf_common, if read buffer is empty and ldata->no_room is true, a call to n_tty_kick_worker is necessary to keep flushing data to reader. Cc: <stable@vger.kernel.org> Fixes: 42458f41d08f ("n_tty: Ensure reader restarts worker for next reader") Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Signed-off-by: Hui Li <caelli@tencent.com> Message-ID: <1680749090-14106-1-git-send-email-caelli@tencent.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-04-06 10:44:50 +08:00
if (old_tail != ldata->read_tail) {
/*
* Make sure no_room is not read in n_tty_kick_worker()
* before setting ldata->read_tail in copy_from_read_buf().
*/
smp_mb();
n_tty: Eliminate receive_room() from consumer/exclusive paths The input worker never reschedules itself; it only processes input until either there is no more input or the read buffer is full. So the reader is responsible for restarting the input worker only if the read buffer was previously full (no_room == 1) _and_ space is now available to process more input because the reader has consumed data from the read buffer. However, computing the actual space available is not required to determine if the reader has consumed data from the read buffer. This condition is evaluated in 5 situations, each of which the space avail is already known: 1. n_tty_flush_buffer() - the read buffer is empty; kick the worker 2. n_tty_set_termios() - no data has been consumed; do not kick the worker (although it may have kicked the reader so data _will be_ consumed) 3. n_tty_check_unthrottle - avail space > 3968; kick the worker 4. n_tty_read, before leaving - only kick the worker if the reader has moved the tail. This prevents unnecessarily kicking the worker when timeout-style reading is used. 5. n_tty_read, before sleeping - although it is possible for the read buffer to be full and input_available_p() to be false, this can only happen when the input worker is racing the reader, in which case the reader will have been woken and won't sleep. Rename n_tty_set_room() to n_tty_kick_worker() to reflect what the function actually does. Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-01-17 04:05:34 +08:00
n_tty_kick_worker(tty);
tty: fix hang on tty device with no_room set It is possible to hang pty devices in this case, the reader was blocking at epoll on master side, the writer was sleeping at wait_woken inside n_tty_write on slave side, and the write buffer on tty_port was full, we found that the reader and writer would never be woken again and blocked forever. The problem was caused by a race between reader and kworker: n_tty_read(reader): n_tty_receive_buf_common(kworker): copy_from_read_buf()| |room = N_TTY_BUF_SIZE - (ldata->read_head - tail) |room <= 0 n_tty_kick_worker() | |ldata->no_room = true After writing to slave device, writer wakes up kworker to flush data on tty_port to reader, and the kworker finds that reader has no room to store data so room <= 0 is met. At this moment, reader consumes all the data on reader buffer and calls n_tty_kick_worker to check ldata->no_room which is false and reader quits reading. Then kworker sets ldata->no_room=true and quits too. If write buffer is not full, writer will wake kworker to flush data again after following writes, but if write buffer is full and writer goes to sleep, kworker will never be woken again and tty device is blocked. This problem can be solved with a check for read buffer size inside n_tty_receive_buf_common, if read buffer is empty and ldata->no_room is true, a call to n_tty_kick_worker is necessary to keep flushing data to reader. Cc: <stable@vger.kernel.org> Fixes: 42458f41d08f ("n_tty: Ensure reader restarts worker for next reader") Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Signed-off-by: Hui Li <caelli@tencent.com> Message-ID: <1680749090-14106-1-git-send-email-caelli@tencent.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-04-06 10:44:50 +08:00
}
up_read(&tty->termios_rwsem);
remove_wait_queue(&tty->read_wait, &wait);
mutex_unlock(&ldata->atomic_read_lock);
if (kb - kbuf)
retval = kb - kbuf;
return retval;
}
/**
* n_tty_write - write function for tty
* @tty: tty device
* @file: file object
* @buf: userspace buffer pointer
* @nr: size of I/O
*
* Write function of the terminal device. This is serialized with respect to
* other write callers but not to termios changes, reads and other such events.
* Since the receive code will echo characters, thus calling driver write
* methods, the %output_lock is used in the output processing functions called
* here as well as in the echo processing function to protect the column state
* and space left in the buffer.
*
* This code must be sure never to sleep through a hangup.
*
* Locking: output_lock to protect column state and space left
* (note that the process_output*() functions take this lock themselves)
*/
static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
const u8 *buf, size_t nr)
{
const u8 *b = buf;
DEFINE_WAIT_FUNC(wait, woken_wake_function);
ssize_t num, retval = 0;
/* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
if (L_TOSTOP(tty) && file->f_op->write_iter != redirected_tty_write) {
retval = tty_check_change(tty);
if (retval)
return retval;
}
down_read(&tty->termios_rwsem);
n_tty: Fix loss of echoed characters and remove bkl from n_tty Fixes the loss of echoed (and other ldisc-generated characters) when the tty is stopped or when the driver output buffer is full (happens frequently for input during continuous program output, such as ^C) and removes the Big Kernel Lock from the N_TTY line discipline. Adds an "echo buffer" to the N_TTY line discipline that handles all ldisc-generated output (including echoed characters). Along with the loss of characters, this also fixes the associated loss of sync between tty output and the ldisc state when characters cannot be immediately written to the tty driver. The echo buffer stores (in addition to characters) state operations that need to be done at the time of character output (like management of the column position). This allows echo to cooperate correctly with program output, since the ldisc state remains consistent with actual characters written. Since the echo buffer code now isolates the tty column state code to the process_out* and process_echoes functions, we can remove the Big Kernel Lock (BKL) and replace it with mutex locks. Highlights are: * Handles echo (and other ldisc output) when tty driver buffer is full - continuous program output can block echo * Saves echo when tty is in stopped state (e.g. ^S) - (e.g.: ^Q will correctly cause held characters to be released for output) * Control character pairs (e.g. "^C") are treated atomically and not split up by interleaved program output * Line discipline state is kept consistent with characters sent to the tty driver * Remove the big kernel lock (BKL) from N_TTY line discipline Signed-off-by: Joe Peterson <joe@skyrush.com> Signed-off-by: Alan Cox <alan@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-02 21:40:53 +08:00
/* Write out any echoed characters that are still pending */
process_echoes(tty);
add_wait_queue(&tty->write_wait, &wait);
while (1) {
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
retval = -EIO;
break;
}
if (O_OPOST(tty)) {
while (nr > 0) {
num = process_output_block(tty, b, nr);
if (num < 0) {
if (num == -EAGAIN)
break;
retval = num;
goto break_out;
}
b += num;
nr -= num;
if (nr == 0)
break;
if (process_output(*b, tty) < 0)
break;
b++; nr--;
}
if (tty->ops->flush_chars)
tty->ops->flush_chars(tty);
} else {
n_tty: Fix n_tty_write crash when echoing in raw mode The tty atomic_write_lock does not provide an exclusion guarantee for the tty driver if the termios settings are LECHO & !OPOST. And since it is unexpected and not allowed to call TTY buffer helpers like tty_insert_flip_string concurrently, this may lead to crashes when concurrect writers call pty_write. In that case the following two writers: * the ECHOing from a workqueue and * pty_write from the process race and can overflow the corresponding TTY buffer like follows. If we look into tty_insert_flip_string_fixed_flag, there is: int space = __tty_buffer_request_room(port, goal, flags); struct tty_buffer *tb = port->buf.tail; ... memcpy(char_buf_ptr(tb, tb->used), chars, space); ... tb->used += space; so the race of the two can result in something like this: A B __tty_buffer_request_room __tty_buffer_request_room memcpy(buf(tb->used), ...) tb->used += space; memcpy(buf(tb->used), ...) ->BOOM B's memcpy is past the tty_buffer due to the previous A's tb->used increment. Since the N_TTY line discipline input processing can output concurrently with a tty write, obtain the N_TTY ldisc output_lock to serialize echo output with normal tty writes. This ensures the tty buffer helper tty_insert_flip_string is not called concurrently and everything is fine. Note that this is nicely reproducible by an ordinary user using forkpty and some setup around that (raw termios + ECHO). And it is present in kernels at least after commit d945cb9cce20ac7143c2de8d88b187f62db99bdc (pty: Rework the pty layer to use the normal buffering logic) in 2.6.31-rc3. js: add more info to the commit log js: switch to bool js: lock unconditionally js: lock only the tty->ops->write call References: CVE-2014-0196 Reported-and-tested-by: Jiri Slaby <jslaby@suse.cz> Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Jiri Slaby <jslaby@suse.cz> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Cc: <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-05-03 20:04:59 +08:00
struct n_tty_data *ldata = tty->disc_data;
while (nr > 0) {
n_tty: Fix n_tty_write crash when echoing in raw mode The tty atomic_write_lock does not provide an exclusion guarantee for the tty driver if the termios settings are LECHO & !OPOST. And since it is unexpected and not allowed to call TTY buffer helpers like tty_insert_flip_string concurrently, this may lead to crashes when concurrect writers call pty_write. In that case the following two writers: * the ECHOing from a workqueue and * pty_write from the process race and can overflow the corresponding TTY buffer like follows. If we look into tty_insert_flip_string_fixed_flag, there is: int space = __tty_buffer_request_room(port, goal, flags); struct tty_buffer *tb = port->buf.tail; ... memcpy(char_buf_ptr(tb, tb->used), chars, space); ... tb->used += space; so the race of the two can result in something like this: A B __tty_buffer_request_room __tty_buffer_request_room memcpy(buf(tb->used), ...) tb->used += space; memcpy(buf(tb->used), ...) ->BOOM B's memcpy is past the tty_buffer due to the previous A's tb->used increment. Since the N_TTY line discipline input processing can output concurrently with a tty write, obtain the N_TTY ldisc output_lock to serialize echo output with normal tty writes. This ensures the tty buffer helper tty_insert_flip_string is not called concurrently and everything is fine. Note that this is nicely reproducible by an ordinary user using forkpty and some setup around that (raw termios + ECHO). And it is present in kernels at least after commit d945cb9cce20ac7143c2de8d88b187f62db99bdc (pty: Rework the pty layer to use the normal buffering logic) in 2.6.31-rc3. js: add more info to the commit log js: switch to bool js: lock unconditionally js: lock only the tty->ops->write call References: CVE-2014-0196 Reported-and-tested-by: Jiri Slaby <jslaby@suse.cz> Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Jiri Slaby <jslaby@suse.cz> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Cc: <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-05-03 20:04:59 +08:00
mutex_lock(&ldata->output_lock);
num = tty->ops->write(tty, b, nr);
n_tty: Fix n_tty_write crash when echoing in raw mode The tty atomic_write_lock does not provide an exclusion guarantee for the tty driver if the termios settings are LECHO & !OPOST. And since it is unexpected and not allowed to call TTY buffer helpers like tty_insert_flip_string concurrently, this may lead to crashes when concurrect writers call pty_write. In that case the following two writers: * the ECHOing from a workqueue and * pty_write from the process race and can overflow the corresponding TTY buffer like follows. If we look into tty_insert_flip_string_fixed_flag, there is: int space = __tty_buffer_request_room(port, goal, flags); struct tty_buffer *tb = port->buf.tail; ... memcpy(char_buf_ptr(tb, tb->used), chars, space); ... tb->used += space; so the race of the two can result in something like this: A B __tty_buffer_request_room __tty_buffer_request_room memcpy(buf(tb->used), ...) tb->used += space; memcpy(buf(tb->used), ...) ->BOOM B's memcpy is past the tty_buffer due to the previous A's tb->used increment. Since the N_TTY line discipline input processing can output concurrently with a tty write, obtain the N_TTY ldisc output_lock to serialize echo output with normal tty writes. This ensures the tty buffer helper tty_insert_flip_string is not called concurrently and everything is fine. Note that this is nicely reproducible by an ordinary user using forkpty and some setup around that (raw termios + ECHO). And it is present in kernels at least after commit d945cb9cce20ac7143c2de8d88b187f62db99bdc (pty: Rework the pty layer to use the normal buffering logic) in 2.6.31-rc3. js: add more info to the commit log js: switch to bool js: lock unconditionally js: lock only the tty->ops->write call References: CVE-2014-0196 Reported-and-tested-by: Jiri Slaby <jslaby@suse.cz> Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Jiri Slaby <jslaby@suse.cz> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Cc: <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-05-03 20:04:59 +08:00
mutex_unlock(&ldata->output_lock);
if (num < 0) {
retval = num;
goto break_out;
}
if (!num)
break;
b += num;
nr -= num;
}
}
if (!nr)
break;
if (tty_io_nonblock(tty, file)) {
retval = -EAGAIN;
break;
}
up_read(&tty->termios_rwsem);
wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
down_read(&tty->termios_rwsem);
}
break_out:
remove_wait_queue(&tty->write_wait, &wait);
if (nr && tty->fasync)
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
up_read(&tty->termios_rwsem);
return (b - buf) ? b - buf : retval;
}
/**
* n_tty_poll - poll method for N_TTY
* @tty: terminal device
* @file: file accessing it
* @wait: poll table
*
* Called when the line discipline is asked to poll() for data or for special
* events. This code is not serialized with respect to other events save
* open/close.
*
* This code must be sure never to sleep through a hangup.
*
* Locking: called without the kernel lock held -- fine.
*/
static __poll_t n_tty_poll(struct tty_struct *tty, struct file *file,
poll_table *wait)
{
__poll_t mask = 0;
poll_wait(file, &tty->read_wait, wait);
poll_wait(file, &tty->write_wait, wait);
if (input_available_p(tty, 1))
mask |= EPOLLIN | EPOLLRDNORM;
else {
tty_buffer_flush_work(tty->port);
if (input_available_p(tty, 1))
mask |= EPOLLIN | EPOLLRDNORM;
}
if (tty->ctrl.packet && tty->link->ctrl.pktstatus)
mask |= EPOLLPRI | EPOLLIN | EPOLLRDNORM;
if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
mask |= EPOLLHUP;
if (tty_hung_up_p(file))
mask |= EPOLLHUP;
if (tty->ops->write && !tty_is_writelocked(tty) &&
tty_chars_in_buffer(tty) < WAKEUP_CHARS &&
tty_write_room(tty) > 0)
mask |= EPOLLOUT | EPOLLWRNORM;
return mask;
}
static unsigned long inq_canon(struct n_tty_data *ldata)
{
size_t nr, head, tail;
if (ldata->canon_head == ldata->read_tail)
return 0;
head = ldata->canon_head;
tail = ldata->read_tail;
nr = head - tail;
/* Skip EOF-chars.. */
while (MASK(head) != MASK(tail)) {
if (test_bit(MASK(tail), ldata->read_flags) &&
read_buf(ldata, tail) == __DISABLED_CHAR)
nr--;
tail++;
}
return nr;
}
static int n_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
unsigned long arg)
{
struct n_tty_data *ldata = tty->disc_data;
unsigned int num;
switch (cmd) {
case TIOCOUTQ:
return put_user(tty_chars_in_buffer(tty), (int __user *) arg);
case TIOCINQ:
down_write(&tty->termios_rwsem);
n_tty: fix EXTPROC vs ICANON interaction with TIOCINQ (aka FIONREAD) We added support for EXTPROC back in 2010 in commit 26df6d13406d ("tty: Add EXTPROC support for LINEMODE") and the intent was to allow it to override some (all?) ICANON behavior. Quoting from that original commit message: There is a new bit in the termios local flag word, EXTPROC. When this bit is set, several aspects of the terminal driver are disabled. Input line editing, character echo, and mapping of signals are all disabled. This allows the telnetd to turn off these functions when in linemode, but still keep track of what state the user wants the terminal to be in. but the problem turns out that "several aspects of the terminal driver are disabled" is a bit ambiguous, and you can really confuse the n_tty layer by setting EXTPROC and then causing some of the ICANON invariants to no longer be maintained. This fixes at least one such case (TIOCINQ) becoming unhappy because of the confusion over whether ICANON really means ICANON when EXTPROC is set. This basically makes TIOCINQ match the case of read: if EXTPROC is set, we ignore ICANON. Also, make sure to reset the ICANON state ie EXTPROC changes, not just if ICANON changes. Fixes: 26df6d13406d ("tty: Add EXTPROC support for LINEMODE") Reported-by: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp> Reported-by: syzkaller <syzkaller@googlegroups.com> Cc: Jiri Slaby <jslaby@suse.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-12-21 09:57:06 +08:00
if (L_ICANON(tty) && !L_EXTPROC(tty))
num = inq_canon(ldata);
else
num = read_cnt(ldata);
up_write(&tty->termios_rwsem);
return put_user(num, (unsigned int __user *) arg);
default:
return n_tty_ioctl_helper(tty, cmd, arg);
}
}
static struct tty_ldisc_ops n_tty_ops = {
.owner = THIS_MODULE,
.num = N_TTY,
.name = "n_tty",
.open = n_tty_open,
.close = n_tty_close,
.flush_buffer = n_tty_flush_buffer,
.read = n_tty_read,
.write = n_tty_write,
.ioctl = n_tty_ioctl,
.set_termios = n_tty_set_termios,
.poll = n_tty_poll,
.receive_buf = n_tty_receive_buf,
.write_wakeup = n_tty_write_wakeup,
.receive_buf2 = n_tty_receive_buf2,
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>
2022-06-06 23:36:51 +08:00
.lookahead_buf = n_tty_lookahead_flow_ctrl,
};
/**
* n_tty_inherit_ops - inherit N_TTY methods
* @ops: struct tty_ldisc_ops where to save N_TTY methods
*
* Enables a 'subclass' line discipline to 'inherit' N_TTY methods.
*/
void n_tty_inherit_ops(struct tty_ldisc_ops *ops)
{
*ops = n_tty_ops;
ops->owner = NULL;
}
EXPORT_SYMBOL_GPL(n_tty_inherit_ops);
void __init n_tty_init(void)
{
tty_register_ldisc(&n_tty_ops);
}