summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorStefan Fritsch <sf@cvs.openbsd.org>2014-06-15 11:18:40 +0000
committerStefan Fritsch <sf@cvs.openbsd.org>2014-06-15 11:18:40 +0000
commitd298da5848fd4950a16c8b5d273f299d3067eb32 (patch)
tree13f3297787fe52f1f75d9e100ee6af5ffc80489b /sys/dev
parentdb8fe6d3f09beef2aba9d0cfb56931f6d8aca034 (diff)
Fix hang with virtio event_idx feature
When using the RING_EVENT_IDX feature, we must first call publish_avail_idx() and then read VQ_AVAIL_EVENT(vq), or there is a race condition that may cause us to miss that the host needs to be notified. This resulted in an occasional hang of network in vio(4).
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/pci/virtio.c13
-rw-r--r--sys/dev/pci/virtiovar.h3
2 files changed, 6 insertions, 10 deletions
diff --git a/sys/dev/pci/virtio.c b/sys/dev/pci/virtio.c
index 933b7120467..72e8df48f23 100644
--- a/sys/dev/pci/virtio.c
+++ b/sys/dev/pci/virtio.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: virtio.c,v 1.5 2013/03/10 21:58:02 sf Exp $ */
+/* $OpenBSD: virtio.c,v 1.6 2014/06/15 11:18:39 sf Exp $ */
/* $NetBSD: virtio.c,v 1.3 2011/11/02 23:05:52 njoly Exp $ */
/*
@@ -277,7 +277,6 @@ virtio_init_vq(struct virtio_softc *sc, struct virtqueue *vq, int reinit)
/* enqueue/dequeue status */
vq->vq_avail_idx = 0;
- vq->vq_avail_signalled = 0xffff;
vq->vq_used_idx = 0;
vq_sync_aring(sc, vq, BUS_DMASYNC_PREWRITE);
vq_sync_uring(sc, vq, BUS_DMASYNC_PREREAD);
@@ -684,15 +683,13 @@ virtio_enqueue_commit(struct virtio_softc *sc, struct virtqueue *vq,
notify:
if (notifynow) {
if (vq->vq_owner->sc_features & VIRTIO_F_RING_EVENT_IDX) {
- uint16_t o = vq->vq_avail_signalled;
+ uint16_t o = vq->vq_avail->idx;
uint16_t n = vq->vq_avail_idx;
- uint16_t t = VQ_AVAIL_EVENT(vq) + 1;
+ uint16_t t;
publish_avail_idx(sc, vq);
- if ((o < n && o < t && t <= n)
- || (o > n && (o < t || t <= n))) {
+ t = VQ_AVAIL_EVENT(vq) + 1;
+ if ((uint16_t)(n - t) < (uint16_t)(n - o))
sc->sc_ops->kick(sc, vq->vq_index);
- vq->vq_avail_signalled = n;
- }
} else {
publish_avail_idx(sc, vq);
if (!(vq->vq_used->flags & VRING_USED_F_NO_NOTIFY))
diff --git a/sys/dev/pci/virtiovar.h b/sys/dev/pci/virtiovar.h
index 5bd7f38c600..2e3094b01d3 100644
--- a/sys/dev/pci/virtiovar.h
+++ b/sys/dev/pci/virtiovar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: virtiovar.h,v 1.4 2013/03/10 21:58:02 sf Exp $ */
+/* $OpenBSD: virtiovar.h,v 1.5 2014/06/15 11:18:39 sf Exp $ */
/* $NetBSD: virtiovar.h,v 1.1 2011/10/30 12:12:21 hannken Exp $ */
/*
@@ -120,7 +120,6 @@ struct virtqueue {
/* enqueue/dequeue status */
uint16_t vq_avail_idx;
- uint16_t vq_avail_signalled;
uint16_t vq_used_idx;
int vq_queued;
struct mutex *vq_aring_lock;