From 8348800871925a4bd1f8ba99470062139444ab58 Mon Sep 17 00:00:00 2001 From: Jie Deng Date: Thu, 9 Aug 2018 17:42:15 +0800 Subject: [PATCH] dm: virtio_rnd: use delayed blocking IO to make virtio_rnd works on Linux based SOS Randomness sourced from /dev/random which does not block once it has been seeded at bootup and you will always get something when you read from that file. This is true on Freebsd but unfortunately things are not the same on Linux. Most cases, you can't read anything from /dev/random especially on current acrn platform which lacking random events. virtio_rnd inherted from freebsd doesn't work anymore. This patch makes virtio_rnd working on Linux based SOS. It uses blocking IO to sevice the front-end random driver and delays the read operation into a new thread to avoid blocking the main notify thread. Signed-off-by: Jie Deng Reviewed-by: Shuo Liu Acked-by: Yu Wang --- devicemodel/hw/pci/virtio/virtio_rnd.c | 99 +++++++++++++++----------- 1 file changed, 59 insertions(+), 40 deletions(-) diff --git a/devicemodel/hw/pci/virtio/virtio_rnd.c b/devicemodel/hw/pci/virtio/virtio_rnd.c index 033552b87..0a9320418 100644 --- a/devicemodel/hw/pci/virtio/virtio_rnd.c +++ b/devicemodel/hw/pci/virtio/virtio_rnd.c @@ -27,8 +27,6 @@ /* * virtio entropy device emulation. - * Randomness is sourced from /dev/random which does not block - * once it has been seeded at bootup. */ #include @@ -57,6 +55,10 @@ struct virtio_rnd { pthread_mutex_t mtx; uint64_t cfg; int fd; + int in_progress; + pthread_t rx_tid; + pthread_mutex_t rx_mtx; + pthread_cond_t rx_cond; /* VBS-K variables */ struct { enum VBS_K_STATUS status; @@ -295,37 +297,52 @@ virtio_rnd_reset(void *base) } } +static void * +virtio_rnd_get_entropy(void *param) +{ + struct virtio_rnd *rnd = param; + struct virtio_vq_info *vq = &rnd->vq; + struct iovec iov; + uint16_t idx; + int len, error; + + for (;;) { + pthread_mutex_lock(&rnd->rx_mtx); + rnd->in_progress = 0; + error = pthread_cond_wait(&rnd->rx_cond, &rnd->rx_mtx); + assert(error == 0); + + rnd->in_progress = 1; + pthread_mutex_unlock(&rnd->rx_mtx); + + while(vq_has_descs(vq)) { + vq_getchain(vq, &idx, &iov, 1, NULL); + + len = read(rnd->fd, iov.iov_base, iov.iov_len); + assert(len > 0); + + /* release this chain and handle more */ + vq_relchain(vq, idx, len); + } + + vq_endchains(vq, 1); + } +} + static void virtio_rnd_notify(void *base, struct virtio_vq_info *vq) { - struct iovec iov; - struct virtio_rnd *rnd; - int len; - uint16_t idx; + struct virtio_rnd *rnd = base; - rnd = base; - - if (rnd->fd < 0) { - vq_endchains(vq, 0); + /* Any ring entries to process */ + if (!vq_has_descs(vq)) return; - } - while (vq_has_descs(vq)) { - vq_getchain(vq, &idx, &iov, 1, NULL); - - len = read(rnd->fd, iov.iov_base, iov.iov_len); - - DPRINTF(("%s: %d\r\n", __func__, len)); - - /* Catastrophe if unable to read from /dev/random */ - assert(len > 0); - - /* - * Release this chain and handle more - */ - vq_relchain(vq, idx, len); - } - vq_endchains(vq, 1); /* Generate interrupt if appropriate. */ + /* Signal the thread for processing */ + pthread_mutex_lock(&rnd->rx_mtx); + if (rnd->in_progress == 0) + pthread_cond_signal(&rnd->rx_cond); + pthread_mutex_unlock(&rnd->rx_mtx); } static int @@ -333,13 +350,12 @@ virtio_rnd_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) { struct virtio_rnd *rnd = NULL; int fd; - int len; - uint8_t v; pthread_mutexattr_t attr; int rc; char *opt; char *vbs_k_opt = NULL; enum VBS_K_STATUS kstat = VIRTIO_DEV_INITIAL; + char tname[MAXCOMLEN + 1]; while ((opt = strsep(&opts, ",")) != NULL) { /* vbs_k_opt should be kernel=on */ @@ -355,19 +371,9 @@ virtio_rnd_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) /* * Should always be able to open /dev/random. */ - fd = open("/dev/random", O_RDONLY | O_NONBLOCK); - + fd = open("/dev/random", O_RDONLY); assert(fd >= 0); - /* - * Check that device is seeded and non-blocking. - */ - len = read(fd, &v, sizeof(v)); - if (len <= 0) { - WPRINTF(("virtio_rnd: /dev/random not ready, read(): %d", len)); - goto fail; - } - rnd = calloc(1, sizeof(struct virtio_rnd)); if (!rnd) { WPRINTF(("virtio_rnd: calloc returns NULL\n")); @@ -434,6 +440,15 @@ virtio_rnd_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) virtio_set_io_bar(&rnd->base, 0); + rnd->in_progress = 0; + pthread_mutex_init(&rnd->rx_mtx, NULL); + pthread_cond_init(&rnd->rx_cond, NULL); + pthread_create(&rnd->rx_tid, NULL, virtio_rnd_get_entropy, + (void *)rnd); + snprintf(tname, sizeof(tname), "vtrnd-%d:%d tx", dev->slot, + dev->func); + pthread_setname_np(rnd->rx_tid, tname); + return 0; fail: @@ -452,6 +467,7 @@ static void virtio_rnd_deinit(struct vmctx *ctx, struct pci_vdev *dev, char *opts) { struct virtio_rnd *rnd; + void *jval; rnd = dev->arg; if (rnd == NULL) { @@ -459,6 +475,9 @@ virtio_rnd_deinit(struct vmctx *ctx, struct pci_vdev *dev, char *opts) return; } + pthread_cancel(rnd->rx_tid); + pthread_join(rnd->rx_tid, &jval); + if (rnd->vbs_k.status == VIRTIO_DEV_STARTED) { DPRINTF(("%s: deinit virtio_rnd_k!\n", __func__)); virtio_rnd_kernel_stop(rnd);