terminal-util: don't query terminal size on virtual consoles

terminal_fix_size() queries the terminal for its dimensions with CSI 18
and DSR. On a virtual console that is pointless: the kernel renders the
screen itself, so TIOCGWINSZ is already authoritative.

It is also slow. PID 1 queries /dev/console from console_setup(), and
writes there go through redirected_tty_write(), so while plymouth holds a
TIOCCONS redirect the query never reaches the VT and no reply is ever
generated. Each query then waits out CONSOLE_ANSI_SEQUENCE_TIMEOUT_USEC.
Previously the write failed immediately with EAGAIN — the kernel forces
O_NONBLOCK on /dev/console — so the query was abandoned at no cost; since
ba10d789a8 it is retried until the timeout instead. On a Steam Deck
booting with console=tty1 and plymouth the two queries cost 667ms, about a
quarter of userspace boot time, and no log level shows it because main()
runs console_setup() between log_close() and log_open().

So skip the query on a virtual console and keep the dimensions the kernel
gave us, reporting success rather than an error: declining to ask is not a
failure, and the "dimensions already match" path below returns 0 for the
same reason.

Note this also skips the query on a /dev/ttyN that would have answered,
and does not help a serial console behind a TIOCCONS redirect.

Fixes: ba10d789a8
Fixes: d91a184509
(cherry picked from commit 4ca7ac0275)
This commit is contained in:
Robert Beckett
2026-10-16 11:36:32 +01:00
committed by Luca Boccassi
parent 412876f581
commit ffb5f4bf45
2 changed files with 54 additions and 7 deletions
+32
View File
@@ -217,6 +217,38 @@ TEST(terminal_get_size_dsr) {
log_notice("terminal size via ioctl: rows=%u columns=%u", ws.ws_row, ws.ws_col);
}
TEST(terminal_fix_size_vc) {
struct winsize before = {}, after = {};
int r;
/* On a virtual console terminal_fix_size() must not query the terminal at all: it should report
* success without touching the dimensions the kernel gave us. */
_cleanup_close_ int fd = open("/dev/tty1", O_RDWR|O_NOCTTY|O_CLOEXEC);
if (fd < 0)
return (void) log_notice_errno(errno, "Can't open /dev/tty1, skipping: %m");
_cleanup_free_ char *name = NULL;
r = getttyname_harder(fd, &name);
if (r < 0)
return (void) log_notice_errno(r, "Can't get name of /dev/tty1, skipping: %m");
if (!tty_is_vc_resolve(name))
return (void) log_notice("/dev/tty1 is not a virtual console, skipping.");
if (ioctl(fd, TIOCGWINSZ, &before) < 0)
return (void) log_notice_errno(errno, "Can't get terminal size via ioctl, skipping: %m");
usec_t n = now(CLOCK_MONOTONIC);
r = terminal_fix_size(fd, fd);
log_info("%s took %s", __func__+5,
FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC), n), USEC_PER_MSEC));
ASSERT_OK_ZERO(r);
ASSERT_OK_ERRNO(ioctl(fd, TIOCGWINSZ, &after));
ASSERT_EQ(before.ws_row, after.ws_row);
ASSERT_EQ(before.ws_col, after.ws_col);
}
TEST(terminal_fix_size) {
int r;