Page MenuHomeFreeBSD

D60895.1793800959.diff
No OneTemporary

Authored By
Unknown
Size
15 KB
Referenced Files
None
Subscribers
None

D60895.1793800959.diff

diff --git a/share/man/man4/virtio_console.4 b/share/man/man4/virtio_console.4
--- a/share/man/man4/virtio_console.4
+++ b/share/man/man4/virtio_console.4
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd October 22, 2014
+.Dd October 18, 2026
.Dt VIRTIO_CONSOLE 4
.Os
.Sh NAME
@@ -36,6 +36,7 @@
.Pp
In
.Xr loader.conf 5 :
+.Cd hw.vtcon.console= Ns Ar 0
.Cd virtio_console_load="YES"
.Sh DESCRIPTION
The
@@ -46,12 +47,40 @@
Each port is similar to a simple serial interface, and
each port is accessible through
.Xr tty 4 .
+.Pp
+A port the host designates as a console becomes a system console, usable
+for kernel messages,
+.Xr ddb 4 ,
+and a login session through
+.Xr getty 8 .
+Without multiport support, the host's only port is the console.
+Because the driver attaches during autoconfiguration rather than at
+.Fn cninit ,
+kernel messages from before it attaches are not shown there; they remain
+available through
+.Xr dmesg 8 .
+If the host offers the emergency write feature, the console falls back to
+it when the transmit virtqueue cannot be used, such as after the device has
+been reset at shutdown.
+.Sh LOADER TUNABLES
+Tunables can be set at the
+.Xr loader 8
+prompt before booting the kernel or stored in
+.Xr loader.conf 5 .
+.Bl -tag -width indent
+.It Va hw.vtcon.console
+Set to 0 to not use console ports as system consoles.
+The default value is 1.
+.El
.Sh FILES
.Bl -tag -width "/dev/ttyV?.??" -compact
.It Pa /dev/ttyV?.??
.El
.Sh SEE ALSO
+.Xr ddb 4 ,
.Xr tty 4 ,
-.Xr virtio 4
+.Xr virtio 4 ,
+.Xr ttys 5 ,
+.Xr getty 8
.Sh AUTHORS
.An Bryan Venteicher Aq Mt bryanv@FreeBSD.org
diff --git a/sys/dev/virtio/console/virtio_console.c b/sys/dev/virtio/console/virtio_console.c
--- a/sys/dev/virtio/console/virtio_console.c
+++ b/sys/dev/virtio/console/virtio_console.c
@@ -104,8 +104,16 @@
struct mtx vcsp_mtx;
int vcsp_flags;
#define VTCON_SCPORT_FLAG_TXSTOP 0x01
+#define VTCON_SCPORT_FLAG_TXWEDGED 0x02
+#define VTCON_SCPORT_FLAG_CONSOLE 0x04
+#define VTCON_SCPORT_FLAG_CNADDED 0x08
u_int vcsp_txbusy;
char vcsp_txbuf[VTCON_TX_NBUFS][VTCON_BULK_BUFSZ];
+ struct consdev vcsp_consdev;
+ u_int vcsp_cn_grab;
+ char *vcsp_cn_rxbuf;
+ uint32_t vcsp_cn_rxlen;
+ uint32_t vcsp_cn_rxpos;
};
#define VTCON_SCPORT_LOCK(_scport) mtx_lock_spin(&(_scport)->vcsp_mtx)
@@ -121,6 +129,7 @@
#define VTCON_FLAG_SIZE 0x02
#define VTCON_FLAG_MULTIPORT 0x04
#define VTCON_FLAG_READY 0x08
+#define VTCON_FLAG_EMERG_WRITE 0x10
/*
* Ports can be added and removed during runtime, but we have
@@ -152,7 +161,8 @@
#define VTCON_FEATURES \
(VIRTIO_CONSOLE_F_SIZE | \
- VIRTIO_CONSOLE_F_MULTIPORT)
+ VIRTIO_CONSOLE_F_MULTIPORT | \
+ VIRTIO_CONSOLE_F_EMERG_WRITE)
static struct virtio_feature_desc vtcon_feature_desc[] = {
{ VIRTIO_CONSOLE_F_SIZE, "ConsoleSize" },
@@ -189,6 +199,14 @@
static int vtcon_scport_tx(struct vtcon_softc_port *, const void *,
int);
+static void vtcon_cn_register(struct vtcon_softc *, int);
+static void vtcon_cn_port_gone(struct vtcon_softc_port *);
+static void vtcon_cn_deregister(struct vtcon_softc *);
+static bool vtcon_cn_lock(struct vtcon_softc_port *);
+static void vtcon_cn_unlock(struct vtcon_softc_port *);
+static void vtcon_cn_rx_release(struct vtcon_softc_port *);
+static void vtcon_cn_emerg_putc(struct vtcon_softc *, int);
+
static int vtcon_ctrl_event_enqueue(struct vtcon_softc *,
struct virtio_console_control *);
static int vtcon_ctrl_event_create(struct vtcon_softc *);
@@ -215,7 +233,7 @@
static void vtcon_ctrl_send_control(struct vtcon_softc *, uint32_t,
uint16_t, uint16_t);
-static int vtcon_port_enqueue_buf(struct vtcon_port *, void *, size_t);
+static int vtcon_port_enqueue_buf(struct virtqueue *, void *, size_t);
static int vtcon_port_create_buf(struct vtcon_port *);
static void vtcon_port_requeue_buf(struct vtcon_port *, void *);
static int vtcon_port_populate(struct vtcon_port *);
@@ -258,6 +276,13 @@
static int vtcon_pending_free;
+static SYSCTL_NODE(_hw, OID_AUTO, vtcon, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "VirtIO Console driver parameters");
+
+static int vtcon_console = 1;
+SYSCTL_INT(_hw_vtcon, OID_AUTO, console, CTLFLAG_RDTUN, &vtcon_console, 0,
+ "Use host-designated console ports as system consoles");
+
static struct ttydevsw vtcon_tty_class = {
.tsw_flags = 0,
.tsw_open = vtcon_tty_open,
@@ -266,6 +291,24 @@
.tsw_free = vtcon_tty_free,
};
+static cn_probe_t vtcon_cnprobe;
+static cn_init_t vtcon_cninit;
+static cn_term_t vtcon_cnterm;
+static cn_getc_t vtcon_cngetc;
+static cn_putc_t vtcon_cnputc;
+static cn_grab_t vtcon_cngrab;
+static cn_ungrab_t vtcon_cnungrab;
+
+static const struct consdev_ops vtcon_cnops = {
+ .cn_probe = vtcon_cnprobe,
+ .cn_init = vtcon_cninit,
+ .cn_term = vtcon_cnterm,
+ .cn_getc = vtcon_cngetc,
+ .cn_putc = vtcon_cnputc,
+ .cn_grab = vtcon_cngrab,
+ .cn_ungrab = vtcon_cnungrab,
+};
+
static device_method_t vtcon_methods[] = {
/* Device methods. */
DEVMETHOD(device_probe, vtcon_probe),
@@ -421,6 +464,7 @@
vtcon_ctrl_deinit(sc);
}
+ vtcon_cn_deregister(sc);
vtcon_destroy_ports(sc);
mtx_destroy(&sc->vtcon_mtx);
mtx_destroy(&sc->vtcon_ctrl_tx_mtx);
@@ -440,7 +484,9 @@
vtcon_attach_completed(device_t dev)
{
struct vtcon_softc *sc;
+ struct vtcon_softc_port *scport;
struct vtcon_port *port;
+ int i;
sc = device_get_softc(dev);
@@ -448,10 +494,16 @@
sc->vtcon_flags |= VTCON_FLAG_READY;
if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT)
virtqueue_notify(sc->vtcon_ctrl_rxvq);
- port = sc->vtcon_ports[0].vcsp_port;
+ scport = &sc->vtcon_ports[0];
+ port = scport->vcsp_port;
if (port != NULL) {
VTCON_PORT_LOCK(port);
+ /* Without multiport, the only port is the console. */
+ if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0)
+ port->vtcport_flags |= VTCON_PORT_FLAG_CONSOLE;
+ VTCON_SCPORT_LOCK(scport);
virtqueue_notify(port->vtcport_invq);
+ VTCON_SCPORT_UNLOCK(scport);
VTCON_PORT_UNLOCK(port);
}
VTCON_UNLOCK(sc);
@@ -459,6 +511,9 @@
vtcon_ctrl_send_control(sc, VIRTIO_CONSOLE_BAD_ID,
VIRTIO_CONSOLE_DEVICE_READY, 1);
+ for (i = 0; i < sc->vtcon_max_ports; i++)
+ vtcon_cn_register(sc, i);
+
return (0);
}
@@ -510,6 +565,8 @@
sc->vtcon_flags |= VTCON_FLAG_SIZE;
if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_MULTIPORT))
sc->vtcon_flags |= VTCON_FLAG_MULTIPORT;
+ if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_EMERG_WRITE))
+ sc->vtcon_flags |= VTCON_FLAG_EMERG_WRITE;
return (0);
}
@@ -777,6 +834,287 @@
return (0);
}
+static void
+vtcon_cn_register(struct vtcon_softc *sc, int id)
+{
+ struct vtcon_softc_port *scport;
+ struct vtcon_port *port;
+ struct consdev *cp;
+ bool add;
+
+ if (vtcon_console == 0)
+ return;
+
+ scport = &sc->vtcon_ports[id];
+ cp = &scport->vcsp_consdev;
+
+ VTCON_LOCK(sc);
+ port = scport->vcsp_port;
+ if (port == NULL ||
+ (sc->vtcon_flags & (VTCON_FLAG_READY | VTCON_FLAG_DETACHED)) !=
+ VTCON_FLAG_READY) {
+ VTCON_UNLOCK(sc);
+ return;
+ }
+ VTCON_PORT_LOCK(port);
+ VTCON_UNLOCK(sc);
+
+ if ((port->vtcport_flags & VTCON_PORT_FLAG_CONSOLE) == 0) {
+ VTCON_PORT_UNLOCK(port);
+ return;
+ }
+
+ if (cp->cn_ops == NULL) {
+ cp->cn_ops = &vtcon_cnops;
+ cp->cn_arg = scport;
+ cp->cn_pri = CN_NORMAL;
+ strlcpy(cp->cn_name, tty_devname(port->vtcport_tty),
+ sizeof(cp->cn_name));
+ }
+
+ VTCON_SCPORT_LOCK(scport);
+ scport->vcsp_flags &= ~VTCON_SCPORT_FLAG_TXWEDGED;
+ scport->vcsp_flags |= VTCON_SCPORT_FLAG_CONSOLE;
+ add = (scport->vcsp_flags & VTCON_SCPORT_FLAG_CNADDED) == 0;
+ scport->vcsp_flags |= VTCON_SCPORT_FLAG_CNADDED;
+ VTCON_SCPORT_UNLOCK(scport);
+ VTCON_PORT_UNLOCK(port);
+
+ if (!add)
+ return;
+
+ if (cnadd(cp) != 0) {
+ device_printf(sc->vtcon_dev, "cannot add console %s\n",
+ cp->cn_name);
+ VTCON_SCPORT_LOCK(scport);
+ scport->vcsp_flags &=
+ ~(VTCON_SCPORT_FLAG_CONSOLE | VTCON_SCPORT_FLAG_CNADDED);
+ VTCON_SCPORT_UNLOCK(scport);
+ } else
+ device_printf(sc->vtcon_dev, "console %s\n", cp->cn_name);
+}
+
+static void
+vtcon_cn_port_gone(struct vtcon_softc_port *scport)
+{
+ char *buf;
+
+ VTCON_SCPORT_LOCK(scport);
+ scport->vcsp_flags &= ~VTCON_SCPORT_FLAG_CONSOLE;
+ buf = scport->vcsp_cn_rxbuf;
+ scport->vcsp_cn_rxbuf = NULL;
+ VTCON_SCPORT_UNLOCK(scport);
+
+ free(buf, M_DEVBUF);
+}
+
+static void
+vtcon_cn_deregister(struct vtcon_softc *sc)
+{
+ struct vtcon_softc_port *scport;
+ int i;
+
+ if (sc->vtcon_ports == NULL)
+ return;
+
+ for (i = 0; i < sc->vtcon_max_ports; i++) {
+ scport = &sc->vtcon_ports[i];
+ if ((scport->vcsp_flags & VTCON_SCPORT_FLAG_CNADDED) == 0)
+ continue;
+
+ cnremove(&scport->vcsp_consdev);
+ vtcon_cn_port_gone(scport);
+ VTCON_SCPORT_LOCK(scport);
+ scport->vcsp_flags &= ~VTCON_SCPORT_FLAG_CNADDED;
+ VTCON_SCPORT_UNLOCK(scport);
+ }
+}
+
+static bool
+vtcon_cn_lock(struct vtcon_softc_port *scport)
+{
+ struct mtx *m;
+
+ m = &scport->vcsp_mtx;
+
+ if (SCHEDULER_STOPPED())
+ return (mtx_owner(m) == NULL);
+ if (mtx_owned(m))
+ return (false);
+ if (kdb_active)
+ return (mtx_trylock_spin(m) != 0);
+ mtx_lock_spin(m);
+
+ return (true);
+}
+
+static void
+vtcon_cn_unlock(struct vtcon_softc_port *scport)
+{
+ if (!SCHEDULER_STOPPED())
+ mtx_unlock_spin(&scport->vcsp_mtx);
+}
+
+static void
+vtcon_cn_rx_release(struct vtcon_softc_port *scport)
+{
+ struct virtqueue *vq;
+ int error __diagused;
+
+ if (scport->vcsp_cn_rxbuf == NULL)
+ return;
+
+ vq = scport->vcsp_invq;
+ error = vtcon_port_enqueue_buf(vq, scport->vcsp_cn_rxbuf,
+ VTCON_BULK_BUFSZ);
+ KASSERT(error == 0,
+ ("%s: cannot requeue input buffer %d", __func__, error));
+ virtqueue_notify(vq);
+ scport->vcsp_cn_rxbuf = NULL;
+}
+
+static void
+vtcon_cn_emerg_putc(struct vtcon_softc *sc, int c)
+{
+ virtio_write_dev_config_4(sc->vtcon_dev,
+ offsetof(struct virtio_console_config, emerg_wr), (uint8_t)c);
+}
+
+static void
+vtcon_cnprobe(struct consdev *cp)
+{
+}
+
+static void
+vtcon_cninit(struct consdev *cp)
+{
+}
+
+static void
+vtcon_cnterm(struct consdev *cp)
+{
+}
+
+static int
+vtcon_cngetc(struct consdev *cp)
+{
+ struct vtcon_softc_port *scport;
+ struct virtqueue *vq;
+ char *buf;
+ uint32_t len;
+ int c, error __diagused;
+
+ scport = cp->cn_arg;
+ vq = scport->vcsp_invq;
+ c = -1;
+
+ if (!vtcon_cn_lock(scport))
+ return (c);
+
+ if ((scport->vcsp_flags & (VTCON_SCPORT_FLAG_CONSOLE |
+ VTCON_SCPORT_FLAG_TXSTOP)) != VTCON_SCPORT_FLAG_CONSOLE)
+ goto out;
+
+ while (scport->vcsp_cn_rxbuf == NULL) {
+ buf = virtqueue_dequeue(vq, &len);
+ if (buf == NULL)
+ goto out;
+ len = min(len, VTCON_BULK_BUFSZ);
+ if (len == 0) {
+ error = vtcon_port_enqueue_buf(vq, buf,
+ VTCON_BULK_BUFSZ);
+ KASSERT(error == 0, ("%s: cannot requeue input "
+ "buffer %d", __func__, error));
+ virtqueue_notify(vq);
+ continue;
+ }
+ scport->vcsp_cn_rxbuf = buf;
+ scport->vcsp_cn_rxlen = len;
+ scport->vcsp_cn_rxpos = 0;
+ }
+
+ c = (u_char)scport->vcsp_cn_rxbuf[scport->vcsp_cn_rxpos++];
+ if (scport->vcsp_cn_rxpos == scport->vcsp_cn_rxlen)
+ vtcon_cn_rx_release(scport);
+out:
+ vtcon_cn_unlock(scport);
+
+ return (c);
+}
+
+static void
+vtcon_cnputc(struct consdev *cp, int c)
+{
+ struct vtcon_softc_port *scport;
+ struct vtcon_softc *sc;
+ u_int timeout, us;
+ char ch;
+ int error;
+
+ scport = cp->cn_arg;
+ sc = scport->vcsp_sc;
+ ch = c;
+ error = ENXIO;
+
+ if (vtcon_cn_lock(scport)) {
+ if ((scport->vcsp_flags & (VTCON_SCPORT_FLAG_CONSOLE |
+ VTCON_SCPORT_FLAG_TXSTOP)) == VTCON_SCPORT_FLAG_CONSOLE) {
+ timeout = (scport->vcsp_flags &
+ VTCON_SCPORT_FLAG_TXWEDGED) != 0 ? 0 :
+ VTCON_TX_TIMEOUT;
+ for (us = 0;; us++) {
+ error = vtcon_scport_tx(scport, &ch, 1);
+ if (error != EBUSY || us >= timeout)
+ break;
+ DELAY(1);
+ }
+ if (error == EBUSY)
+ scport->vcsp_flags |=
+ VTCON_SCPORT_FLAG_TXWEDGED;
+ else
+ scport->vcsp_flags &=
+ ~VTCON_SCPORT_FLAG_TXWEDGED;
+ }
+ vtcon_cn_unlock(scport);
+ }
+
+ if (error != 0 && (sc->vtcon_flags & VTCON_FLAG_EMERG_WRITE) != 0)
+ vtcon_cn_emerg_putc(sc, ch);
+}
+
+static void
+vtcon_cngrab(struct consdev *cp)
+{
+ struct vtcon_softc_port *scport;
+
+ scport = cp->cn_arg;
+
+ if (!vtcon_cn_lock(scport))
+ return;
+ if (scport->vcsp_cn_grab++ == 0 &&
+ (scport->vcsp_flags & VTCON_SCPORT_FLAG_CONSOLE) != 0)
+ virtqueue_disable_intr(scport->vcsp_invq);
+ vtcon_cn_unlock(scport);
+}
+
+static void
+vtcon_cnungrab(struct consdev *cp)
+{
+ struct vtcon_softc_port *scport;
+
+ scport = cp->cn_arg;
+
+ if (!vtcon_cn_lock(scport))
+ return;
+ if (scport->vcsp_cn_grab > 0 && --scport->vcsp_cn_grab == 0 &&
+ (scport->vcsp_flags & (VTCON_SCPORT_FLAG_CONSOLE |
+ VTCON_SCPORT_FLAG_TXSTOP)) == VTCON_SCPORT_FLAG_CONSOLE) {
+ vtcon_cn_rx_release(scport);
+ (void)virtqueue_enable_intr(scport->vcsp_invq);
+ }
+ vtcon_cn_unlock(scport);
+}
+
static int
vtcon_ctrl_event_enqueue(struct vtcon_softc *sc,
struct virtio_console_control *control)
@@ -959,6 +1297,8 @@
port->vtcport_flags |= VTCON_PORT_FLAG_CONSOLE;
vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
VTCON_PORT_UNLOCK(port);
+
+ vtcon_cn_register(sc, id);
}
static void
@@ -1219,15 +1559,12 @@
}
static int
-vtcon_port_enqueue_buf(struct vtcon_port *port, void *buf, size_t len)
+vtcon_port_enqueue_buf(struct virtqueue *vq, void *buf, size_t len)
{
struct sglist_seg segs[2];
struct sglist sg;
- struct virtqueue *vq;
int error;
- vq = port->vtcport_invq;
-
sglist_init(&sg, 2, segs);
error = sglist_append(&sg, buf, len);
KASSERT(error == 0,
@@ -1248,7 +1585,8 @@
if (buf == NULL)
return (ENOMEM);
- error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ);
+ error = vtcon_port_enqueue_buf(port->vtcport_invq, buf,
+ VTCON_BULK_BUFSZ);
if (error)
free(buf, M_DEVBUF);
@@ -1260,7 +1598,8 @@
{
int error __diagused;
- error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ);
+ error = vtcon_port_enqueue_buf(port->vtcport_invq, buf,
+ VTCON_BULK_BUFSZ);
KASSERT(error == 0,
("%s: cannot requeue input buffer %d", __func__, error));
}
@@ -1441,6 +1780,7 @@
tp = port->vtcport_tty;
port->vtcport_flags |= VTCON_PORT_FLAG_GONE;
+ vtcon_cn_port_gone(port->vtcport_scport);
if (tp != NULL) {
atomic_add_int(&vtcon_pending_free, 1);
@@ -1518,19 +1858,29 @@
static void
vtcon_port_in(struct vtcon_port *port)
{
+ struct vtcon_softc_port *scport;
struct virtqueue *vq;
struct tty *tp;
char *buf;
uint32_t len;
- int i, deq;
+ int i, deq, pending;
+ scport = port->vtcport_scport;
tp = port->vtcport_tty;
vq = port->vtcport_invq;
again:
deq = 0;
- while ((buf = virtqueue_dequeue(vq, &len)) != NULL) {
+ for (;;) {
+ VTCON_SCPORT_LOCK(scport);
+ buf = NULL;
+ if (scport->vcsp_cn_grab == 0)
+ buf = virtqueue_dequeue(vq, &len);
+ VTCON_SCPORT_UNLOCK(scport);
+ if (buf == NULL)
+ break;
+
len = min(len, VTCON_BULK_BUFSZ);
for (i = 0; i < len; i++) {
#if defined(KDB)
@@ -1540,15 +1890,23 @@
#endif
ttydisc_rint(tp, buf[i], 0);
}
+
+ VTCON_SCPORT_LOCK(scport);
vtcon_port_requeue_buf(port, buf);
+ VTCON_SCPORT_UNLOCK(scport);
deq++;
}
ttydisc_rint_done(tp);
+ VTCON_SCPORT_LOCK(scport);
if (deq > 0)
virtqueue_notify(vq);
+ pending = 0;
+ if (scport->vcsp_cn_grab == 0)
+ pending = virtqueue_enable_intr(vq);
+ VTCON_SCPORT_UNLOCK(scport);
- if (virtqueue_enable_intr(vq) != 0)
+ if (pending != 0)
goto again;
}

File Metadata

Mime Type
text/plain
Expires
Wed, Nov 4, 2:02 PM (5 h, 37 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41436404
Default Alt Text
D60895.1793800959.diff (15 KB)

Event Timeline