diff --git a/sys/dev/virtio/random/virtio_random.c b/sys/dev/virtio/random/virtio_random.c --- a/sys/dev/virtio/random/virtio_random.c +++ b/sys/dev/virtio/random/virtio_random.c @@ -56,7 +56,11 @@ struct virtqueue *vtrnd_vq; eventhandler_tag eh; bool inactive; + struct sglist_seg vtrnd_segs[1]; + struct sglist vtrnd_sg; + uint32_t vtrnd_value[HARVESTSIZE] __aligned(sizeof(uint32_t) * HARVESTSIZE); }; +_Static_assert(sizeof(((struct vtrnd_softc *)NULL)->vtrnd_value) < PAGE_SIZE, "sglist assumption"); static int vtrnd_modevent(module_t, int, void *); @@ -69,6 +73,7 @@ static int vtrnd_setup_features(struct vtrnd_softc *); static int vtrnd_alloc_virtqueue(struct vtrnd_softc *); static int vtrnd_harvest(struct vtrnd_softc *, void *, size_t *); +static void vtrnd_enqueue(struct vtrnd_softc *sc); static unsigned vtrnd_read(void *, unsigned); #define VTRND_FEATURES 0 @@ -176,6 +181,8 @@ sc->inactive = false; random_source_register(&random_vtrnd); + vtrnd_enqueue(sc); + fail: if (error) vtrnd_detach(dev); @@ -187,6 +194,7 @@ vtrnd_detach(device_t dev) { struct vtrnd_softc *sc; + uint32_t rdlen; sc = device_get_softc(dev); KASSERT( @@ -199,6 +207,10 @@ sc->eh = NULL; } random_source_deregister(&random_vtrnd); + + /* clear the queue */ + virtqueue_poll(sc->vtrnd_vq, &rdlen); + atomic_store_explicit(&g_vtrnd_softc, NULL, memory_order_release); return (0); } @@ -253,49 +265,52 @@ return (virtio_alloc_virtqueues(dev, 0, 1, &vq_info)); } -static int -vtrnd_harvest(struct vtrnd_softc *sc, void *buf, size_t *sz) +static void +vtrnd_enqueue(struct vtrnd_softc *sc) { - struct sglist_seg segs[1]; - struct sglist sg; struct virtqueue *vq; - uint32_t value[HARVESTSIZE] __aligned(sizeof(uint32_t) * HARVESTSIZE); - uint32_t rdlen; + struct sglist *sg; int error; - _Static_assert(sizeof(value) < PAGE_SIZE, "sglist assumption"); - - if (sc->inactive) - return (EDEADLK); + sg = &sc->vtrnd_sg; + vq = sc->vtrnd_vq; - sglist_init(&sg, 1, segs); - error = sglist_append(&sg, value, *sz); + sglist_init(sg, 1, sc->vtrnd_segs); + error = sglist_append(sg, sc->vtrnd_value, sizeof(sc->vtrnd_value)); if (error != 0) panic("%s: sglist_append error=%d", __func__, error); - vq = sc->vtrnd_vq; KASSERT(virtqueue_empty(vq), ("%s: non-empty queue", __func__)); - error = virtqueue_enqueue(vq, buf, &sg, 0, 1); - if (error != 0) - return (error); + error = virtqueue_enqueue(vq, sc, sg, 0, 1); + KASSERT(error == 0, ("%s: virtqueue_enqueue returned error: %d", __func__, error)); - /* - * Poll for the response, but the command is likely already - * done when we return from the notify. - */ virtqueue_notify(vq); - virtqueue_poll(vq, &rdlen); +} - if (rdlen > *sz) - panic("%s: random device wrote %zu bytes beyond end of provided" - " buffer %p:%zu", __func__, (size_t)rdlen - *sz, - (void *)value, *sz); - else if (rdlen == 0) +int +vtrnd_harvest(struct vtrnd_softc *sc, void *buf, size_t *sz) +{ + struct virtqueue *vq; + void *cookie; + uint32_t rdlen; + + if (sc->inactive) + return (EDEADLK); + + vq = sc->vtrnd_vq; + + cookie = virtqueue_dequeue(vq, &rdlen); + + if (cookie == NULL) return (EAGAIN); + *sz = MIN(rdlen, *sz); - memcpy(buf, value, *sz); - explicit_bzero(value, *sz); + memcpy(buf, sc->vtrnd_value, *sz); + explicit_bzero(sc->vtrnd_value, *sz); + + vtrnd_enqueue(sc); + return (0); }