Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F175778845
D60538.id189475.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
7 KB
Referenced Files
None
Subscribers
None
D60538.id189475.diff
View Options
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
@@ -59,6 +59,7 @@
#define VTCON_TTY_ALIAS_PREFIX "vtcon"
#define VTCON_BULK_BUFSZ 128
#define VTCON_CTRL_BUFSZ 128
+#define VTCON_TX_TIMEOUT 100000 /* microseconds */
/*
* The buffers cannot cross more than one page boundary due to the
@@ -94,12 +95,20 @@
#define VTCON_PORT_UNLOCK(_port) mtx_unlock(&(_port)->vtcport_mtx)
struct vtcon_softc_port {
- struct vtcon_softc *vcsp_sc;
- struct vtcon_port *vcsp_port;
- struct virtqueue *vcsp_invq;
- struct virtqueue *vcsp_outvq;
+ struct vtcon_softc *vcsp_sc;
+ struct vtcon_port *vcsp_port;
+ struct virtqueue *vcsp_invq;
+ struct virtqueue *vcsp_outvq;
+ struct mtx vcsp_mtx;
+ int vcsp_flags;
+#define VTCON_SCPORT_FLAG_TXBUSY 0x01
+#define VTCON_SCPORT_FLAG_TXSTOP 0x02
+ char vcsp_txbuf[VTCON_BULK_BUFSZ];
};
+#define VTCON_SCPORT_LOCK(_scport) mtx_lock_spin(&(_scport)->vcsp_mtx)
+#define VTCON_SCPORT_UNLOCK(_scport) mtx_unlock_spin(&(_scport)->vcsp_mtx)
+
struct vtcon_softc {
device_t vtcon_dev;
struct mtx vtcon_mtx;
@@ -121,6 +130,7 @@
struct virtqueue *vtcon_ctrl_rxvq;
struct virtqueue *vtcon_ctrl_txvq;
struct mtx vtcon_ctrl_tx_mtx;
+ bool vtcon_ctrl_txstop;
};
#define VTCON_LOCK(_sc) mtx_lock(&(_sc)->vtcon_mtx)
@@ -154,6 +164,7 @@
static int vtcon_probe(device_t);
static int vtcon_attach(device_t);
static int vtcon_detach(device_t);
+static int vtcon_shutdown(device_t);
static int vtcon_config_change(device_t);
static int vtcon_setup_features(struct vtcon_softc *);
@@ -166,8 +177,14 @@
static void vtcon_determine_max_ports(struct vtcon_softc *,
struct virtio_console_config *);
static void vtcon_destroy_ports(struct vtcon_softc *);
+static void vtcon_stop_tx(struct vtcon_softc *);
static void vtcon_stop(struct vtcon_softc *);
+static bool vtcon_scport_tx_reclaim(struct vtcon_softc_port *);
+static bool vtcon_scport_tx_wait(struct vtcon_softc_port *, u_int);
+static int vtcon_scport_tx(struct vtcon_softc_port *, const void *,
+ int);
+
static int vtcon_ctrl_event_enqueue(struct vtcon_softc *,
struct virtio_console_control *);
static int vtcon_ctrl_event_create(struct vtcon_softc *);
@@ -250,6 +267,7 @@
DEVMETHOD(device_probe, vtcon_probe),
DEVMETHOD(device_attach, vtcon_attach),
DEVMETHOD(device_detach, vtcon_detach),
+ DEVMETHOD(device_shutdown, vtcon_shutdown),
/* VirtIO methods. */
DEVMETHOD(virtio_config_change, vtcon_config_change),
@@ -408,6 +426,14 @@
return (0);
}
+static int
+vtcon_shutdown(device_t dev)
+{
+ vtcon_stop_tx(device_get_softc(dev));
+
+ return (0);
+}
+
static int
vtcon_config_change(device_t dev)
{
@@ -504,6 +530,7 @@
for (i = 0; i < max; i++) {
scport = &sc->vtcon_ports[i];
scport->vcsp_sc = sc;
+ mtx_init(&scport->vcsp_mtx, "vtconvqmtx", NULL, MTX_SPIN);
}
return (0);
@@ -598,21 +625,113 @@
vq = scport->vcsp_invq;
if (vq != NULL)
vtcon_port_drain_bufs(vq);
+
+ if ((vq = scport->vcsp_outvq) != NULL) {
+ for (int last = 0; virtqueue_drain(vq, &last) != NULL;)
+ ;
+ }
}
VTCON_UNLOCK(sc);
+ for (i = 0; i < sc->vtcon_max_ports; i++)
+ mtx_destroy(&sc->vtcon_ports[i].vcsp_mtx);
free(sc->vtcon_ports, M_DEVBUF);
sc->vtcon_ports = NULL;
}
+static void
+vtcon_stop_tx(struct vtcon_softc *sc)
+{
+ struct vtcon_softc_port *scport;
+ int i;
+
+ VTCON_CTRL_TX_LOCK(sc);
+ sc->vtcon_ctrl_txstop = true;
+ VTCON_CTRL_TX_UNLOCK(sc);
+
+ if (sc->vtcon_ports == NULL)
+ return;
+
+ for (i = 0; i < sc->vtcon_max_ports; i++) {
+ scport = &sc->vtcon_ports[i];
+
+ VTCON_SCPORT_LOCK(scport);
+ if (scport->vcsp_outvq != NULL)
+ (void)vtcon_scport_tx_wait(scport, VTCON_TX_TIMEOUT);
+ scport->vcsp_flags |= VTCON_SCPORT_FLAG_TXSTOP;
+ VTCON_SCPORT_UNLOCK(scport);
+ }
+}
+
static void
vtcon_stop(struct vtcon_softc *sc)
{
+ vtcon_stop_tx(sc);
vtcon_disable_interrupts(sc);
virtio_stop(sc->vtcon_dev);
}
+static bool
+vtcon_scport_tx_reclaim(struct vtcon_softc_port *scport)
+{
+ if ((scport->vcsp_flags & VTCON_SCPORT_FLAG_TXBUSY) != 0 &&
+ virtqueue_dequeue(scport->vcsp_outvq, NULL) != NULL)
+ scport->vcsp_flags &= ~VTCON_SCPORT_FLAG_TXBUSY;
+
+ return ((scport->vcsp_flags & VTCON_SCPORT_FLAG_TXBUSY) == 0);
+}
+
+static bool
+vtcon_scport_tx_wait(struct vtcon_softc_port *scport, u_int timeout)
+{
+ u_int us;
+
+ for (us = 0; !vtcon_scport_tx_reclaim(scport); us++) {
+ if (us >= timeout)
+ return (false);
+ DELAY(1);
+ }
+
+ return (true);
+}
+
+static int
+vtcon_scport_tx(struct vtcon_softc_port *scport, const void *buf, int len)
+{
+ struct sglist_seg segs[2];
+ struct sglist sg;
+ struct virtqueue *vq;
+ int error;
+
+ mtx_assert(&scport->vcsp_mtx, MA_OWNED);
+ KASSERT(len > 0 && len <= (int)sizeof(scport->vcsp_txbuf),
+ ("%s: bad length %d", __func__, len));
+
+ vq = scport->vcsp_outvq;
+
+ if (scport->vcsp_flags & VTCON_SCPORT_FLAG_TXSTOP)
+ return (ENXIO);
+ if (!vtcon_scport_tx_reclaim(scport))
+ return (EBUSY);
+
+ memcpy(scport->vcsp_txbuf, buf, len);
+
+ sglist_init(&sg, 2, segs);
+ error = sglist_append(&sg, scport->vcsp_txbuf, len);
+ KASSERT(error == 0, ("%s: error %d adding buffer to sglist",
+ __func__, error));
+
+ error = virtqueue_enqueue(vq, scport->vcsp_txbuf, &sg, sg.sg_nseg, 0);
+ if (error)
+ return (error);
+
+ scport->vcsp_flags |= VTCON_SCPORT_FLAG_TXBUSY;
+ virtqueue_notify(vq);
+
+ return (0);
+}
+
static int
vtcon_ctrl_event_enqueue(struct vtcon_softc *sc,
struct virtio_console_control *control)
@@ -1023,6 +1142,10 @@
* ordering.
*/
VTCON_CTRL_TX_LOCK(sc);
+ if (sc->vtcon_ctrl_txstop) {
+ VTCON_CTRL_TX_UNLOCK(sc);
+ return;
+ }
KASSERT(virtqueue_empty(vq),
("%s: virtqueue is not empty", __func__));
error = virtqueue_enqueue(vq, control, &sg, sg.sg_nseg, 0);
@@ -1147,13 +1270,15 @@
* Free any data left over from when this virtqueue was in use by a
* prior port. We have not yet notified the host that the port is
* ready, so assume nothing in the virtqueue can be for us.
+ *
+ * Also, a buffer the prior port handed to the host may still be the
+ * host's, and the transmit state belongs to the softc port, so it
+ * carries over to this one and the transmit virtqueue is left alone.
*/
vtcon_port_drain(port);
KASSERT(virtqueue_empty(port->vtcport_invq),
("%s: in virtqueue is not empty", __func__));
- KASSERT(virtqueue_empty(port->vtcport_outvq),
- ("%s: out virtqueue is not empty", __func__));
error = vtcon_port_populate(port);
if (error)
@@ -1406,24 +1531,23 @@
static void
vtcon_port_out(struct vtcon_port *port, void *buf, int bufsize)
{
- struct sglist_seg segs[2];
- struct sglist sg;
- struct virtqueue *vq;
+ struct vtcon_softc_port *scport;
int error;
- vq = port->vtcport_outvq;
- KASSERT(virtqueue_empty(vq),
- ("%s: port %p out virtqueue not empty", __func__, port));
-
- sglist_init(&sg, 2, segs);
- error = sglist_append(&sg, buf, bufsize);
- KASSERT(error == 0, ("%s: error %d adding buffer to sglist",
- __func__, error));
+ scport = port->vtcport_scport;
- error = virtqueue_enqueue(vq, buf, &sg, sg.sg_nseg, 0);
- if (error == 0) {
- virtqueue_notify(vq);
- virtqueue_poll(vq, NULL);
+ /*
+ * Wait for the host to finish with the previous buffer with the spin
+ * mutex dropped. A host that never completes it stalls the writer,
+ * but only until the device is stopped.
+ */
+ for (;;) {
+ VTCON_SCPORT_LOCK(scport);
+ error = vtcon_scport_tx(scport, buf, bufsize);
+ VTCON_SCPORT_UNLOCK(scport);
+ if (error != EBUSY)
+ break;
+ DELAY(1);
}
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Oct 14, 7:16 AM (16 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40671493
Default Alt Text
D60538.id189475.diff (7 KB)
Attached To
Mode
D60538: virtio_console: Make the transmit path usable without the tty lock
Attached
Detach File
Event Timeline
Log In to Comment