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 <jie.deng@intel.com>
Reviewed-by: Shuo Liu <shuo.a.liu@intel.com>
Acked-by: Yu Wang <yu1.wang@intel.com>
This commit is contained in:
Jie Deng 2018-08-09 17:42:15 +08:00 committed by lijinxia
parent 98aa74bd6b
commit 8348800871

View File

@ -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 <fcntl.h>
@ -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);