The attached patch series makes /dev/console and /dev/constty MP-safe,
with careful rules around constty access so it doesn't add contention
or mutexes to the paths used by printf(9).
This passes some manual testing in qemu with TIOCCONS on a viocon(4)
device, but since this touches a very intimate part of the kernel, I
figure I should post it for review and testing before barging ahead
and committing.
So please give this a whirl on whatever systems you have that are a
pain to get diagnostics out of, or on systems with X and xconsole or
xterm that use TIOCCONS, so we can find console/constty bugs early
before you need those diagnostics for something else!
>From b1dac0b91583b768a457ae946751e7c69c4554a1 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell
Date: Sat, 1 Oct 2022 14:00:43 +
Subject: [PATCH 1/5] cons(9): New function cn_set_tab.
Increment of progress toward eliminating bare access to cn_tab so we
can make more things MP-safe without the kernel lock (and maybe some
day better formalize console detection and switching).
---
sys/dev/cons.c | 16
sys/dev/cons.h | 2 ++
2 files changed, 18 insertions(+)
diff --git a/sys/dev/cons.c b/sys/dev/cons.c
index eef38cbbc510..270fe6e1bbee 100644
--- a/sys/dev/cons.c
+++ b/sys/dev/cons.c
@@ -87,6 +87,22 @@ struct tty *constty = NULL;/* virtual console
output device */
struct consdev *cn_tab;/* physical console device info */
struct vnode *cn_devvp[2]; /* vnode for underlying device. */
+void
+cn_set_tab(struct consdev *tab)
+{
+
+ /*
+* This is a point that we should have KASSERT(cold) or add
+* synchronization in case this can happen after cold boot.
+* However, cn_tab initialization is so critical to any
+* diagnostics or debugging that we need to tread carefully
+* about introducing new ways to crash. So let's put the
+* assertion in only after we've audited most or all of the
+* cn_tab updates.
+*/
+ cn_tab = tab;
+}
+
int
cnopen(dev_t dev, int flag, int mode, struct lwp *l)
{
diff --git a/sys/dev/cons.h b/sys/dev/cons.h
index 7ccdc5d6e74c..0d0356a3e1ff 100644
--- a/sys/dev/cons.h
+++ b/sys/dev/cons.h
@@ -76,6 +76,8 @@ struct consdev {
extern struct consdev constab[];
extern struct consdev *cn_tab;
+void cn_set_tab(struct consdev *);
+
void cninit(void);
intcngetc(void);
intcngetsn(char *, int);
>From 7d7b7315a8443f9c619d332ba1b897b34ec1f65b Mon Sep 17 00:00:00 2001
From: Taylor R Campbell
Date: Sat, 1 Oct 2022 14:03:22 +
Subject: [PATCH 2/5] cons(9): Serialize open and close.
Kernel lock wasn't enough for this -- cdevvp, vn_lock, or VOP_OPEN
could block, allowing another thread to re-enter open.
---
sys/dev/cons.c | 49 ++---
1 file changed, 42 insertions(+), 7 deletions(-)
diff --git a/sys/dev/cons.c b/sys/dev/cons.c
index 270fe6e1bbee..1bb05a90fae1 100644
--- a/sys/dev/cons.c
+++ b/sys/dev/cons.c
@@ -53,6 +53,7 @@ __KERNEL_RCSID(0, "$NetBSD: cons.c,v 1.79 2022/08/22 00:20:56
riastradh Exp $");
#include
#include
#include
+#include
#include
@@ -83,6 +84,8 @@ const struct cdevsw cons_cdevsw = {
.d_flag = D_TTY
};
+static struct kmutex cn_lock;
+
struct tty *constty = NULL;/* virtual console output device */
struct consdev *cn_tab;/* physical console device info */
struct vnode *cn_devvp[2]; /* vnode for underlying device. */
@@ -113,8 +116,12 @@ cnopen(dev_t dev, int flag, int mode, struct lwp *l)
if (unit > 1)
return ENODEV;
- if (cn_tab == NULL)
- return (0);
+ mutex_enter(_lock);
+
+ if (cn_tab == NULL) {
+ error = 0;
+ goto out;
+ }
/*
* always open the 'real' console device, so we don't get nailed
@@ -145,15 +152,19 @@ cnopen(dev_t dev, int flag, int mode, struct lwp *l)
*/
panic("cnopen: cn_tab->cn_dev == dev");
}
- if (cn_devvp[unit] != NULLVP)
- return 0;
+ if (cn_devvp[unit] != NULLVP) {
+ error = 0;
+ goto out;
+ }
if ((error = cdevvp(cndev, _devvp[unit])) != 0) {
printf("cnopen: unable to get vnode reference\n");
- return error;
+ goto out;
}
vn_lock(cn_devvp[unit], LK_EXCLUSIVE | LK_RETRY);
error = VOP_OPEN(cn_devvp[unit], flag, kauth_cred_get());
VOP_UNLOCK(cn_devvp[unit]);
+
+out: mutex_exit(_lock);
return error;
}
@@ -165,8 +176,12 @@ cnclose(dev_t dev, int flag, int mode, struct lwp *l)
unit = minor(dev);
- if (cn_tab == NULL)
- return (0);
+ mutex_enter(_lock);
+
+ if (cn_tab == NULL) {
+ error = 0;
+ goto out;
+ }
vp = cn_devvp[unit];
cn_devvp[unit] = NULL;
@@ -174,6 +189,8 @@ cnclose(dev_t dev,