Page MenuHomeFreeBSD

D60538.id189267.diff
No OneTemporary

D60538.id189267.diff

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);
@@ -601,18 +628,105 @@
}
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_disable_interrupts(sc);
+ vtcon_stop_tx(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 +1137,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 +1265,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 +1526,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. As before, 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

Mime Type
text/plain
Expires
Tue, Oct 13, 8:18 PM (14 h, 47 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40558317
Default Alt Text
D60538.id189267.diff (7 KB)

Event Timeline