diff --git a/devicemodel/core/main.c b/devicemodel/core/main.c index a0ed4cc49..1ffa6f0b4 100644 --- a/devicemodel/core/main.c +++ b/devicemodel/core/main.c @@ -65,6 +65,7 @@ #include "vmcfg_config.h" #include "vmcfg.h" #include "tpm.h" +#include "virtio.h" #define GUEST_NIO_PORT 0x488 /* guest upcalls via i/o port */ @@ -703,6 +704,7 @@ enum { CMD_OPT_VSBL = 1000, CMD_OPT_PART_INFO, CMD_OPT_TRUSTY_ENABLE, + CMD_OPT_VIRTIO_POLL_ENABLE, CMD_OPT_PTDEV_NO_RESET, CMD_OPT_DEBUGEXIT, CMD_OPT_VMCFG, @@ -739,6 +741,7 @@ static struct option long_options[] = { {"part_info", required_argument, 0, CMD_OPT_PART_INFO}, {"enable_trusty", no_argument, 0, CMD_OPT_TRUSTY_ENABLE}, + {"virtio_poll", required_argument, 0, CMD_OPT_VIRTIO_POLL_ENABLE}, {"ptdev_no_reset", no_argument, 0, CMD_OPT_PTDEV_NO_RESET}, {"debugexit", no_argument, 0, CMD_OPT_DEBUGEXIT}, @@ -862,6 +865,14 @@ dm_run(int argc, char *argv[]) case CMD_OPT_TRUSTY_ENABLE: trusty_enabled = 1; break; + case CMD_OPT_VIRTIO_POLL_ENABLE: + if (acrn_parse_virtio_poll_interval(optarg) != 0) { + errx(EX_USAGE, + "invalid virtio poll interval %s", + optarg); + exit(1); + } + break; case CMD_OPT_PTDEV_NO_RESET: ptdev_no_reset(true); break; diff --git a/devicemodel/hw/pci/virtio/virtio.c b/devicemodel/hw/pci/virtio/virtio.c index e4cc381ac..1d5dc3150 100644 --- a/devicemodel/hw/pci/virtio/virtio.c +++ b/devicemodel/hw/pci/virtio/virtio.c @@ -28,10 +28,13 @@ #include #include #include +#include +#include #include "dm.h" #include "pci_core.h" #include "virtio.h" +#include "timer.h" /* * Functions for dealing with generalized "virtual devices" as @@ -45,6 +48,61 @@ */ #define DEV_STRUCT(vs) ((void *)(vs)) +static uint8_t virtio_poll_enabled; +static size_t virtio_poll_interval; + +static void +virtio_start_timer(struct acrn_timer *timer, time_t sec, time_t nsec) +{ + struct itimerspec ts; + + /* setting the interval time */ + ts.it_interval.tv_sec = 0; + ts.it_interval.tv_nsec = 0; + /* set the delay time it will be started when timer_setting */ + ts.it_value.tv_sec = sec; + ts.it_value.tv_nsec = nsec; + assert(acrn_timer_settime(timer, &ts) == 0); +} + +static void +virtio_poll_timer(void *arg) +{ + struct virtio_base *base; + struct virtio_ops *vops; + struct virtio_vq_info *vq; + const char *name; + int i; + + base = arg; + vops = base->vops; + name = vops->name; + + if (base->mtx) + pthread_mutex_lock(base->mtx); + + base->polling_in_progress = 1; + + for (i = 0; i < base->vops->nvq; i++) { + vq = &base->queues[i]; + vq->used->flags |= ACRN_VRING_USED_F_NO_NOTIFY; + /* TODO: call notify when necessary */ + if (vq->notify) + (*vq->notify)(DEV_STRUCT(base), vq); + else if (vops->qnotify) + (*vops->qnotify)(DEV_STRUCT(base), vq); + else + fprintf(stderr, + "%s: qnotify queue %d: missing vq/vops notify\r\n", + name, i); + } + + if (base->mtx) + pthread_mutex_unlock(base->mtx); + + virtio_start_timer(&base->polling_timer, 0, virtio_poll_interval); +} + /** * @brief Link a virtio_base to its constants, the virtio device, * and the PCI emulation. @@ -60,7 +118,8 @@ void virtio_linkup(struct virtio_base *base, struct virtio_ops *vops, void *pci_virtio_dev, struct pci_vdev *dev, - struct virtio_vq_info *queues) + struct virtio_vq_info *queues, + int backend_type) { int i; @@ -69,6 +128,7 @@ virtio_linkup(struct virtio_base *base, struct virtio_ops *vops, base->vops = vops; base->dev = dev; dev->arg = base; + base->backend_type = backend_type; base->queues = queues; for (i = 0; i < vops->nvq; i++) { @@ -100,6 +160,9 @@ virtio_reset_dev(struct virtio_base *base) /* if (base->mtx) */ /* assert(pthread_mutex_isowned_np(base->mtx)); */ + acrn_timer_deinit(&base->polling_timer); + base->polling_in_progress = 0; + nvq = base->vops->nvq; for (vq = base->queues, i = 0; i < nvq; vq++, i++) { vq->flags = 0; @@ -570,6 +633,30 @@ vq_endchains(struct virtio_vq_info *vq, int used_all_avail) vq_interrupt(base, vq); } +/** + * @brief Helper function for clearing used ring flags. + * + * Driver should always use this helper function to clear used ring flags. + * For virtio poll mode, in order to avoid trap, we should never really + * clear used ring flags. + * + * @param base Pointer to struct virtio_base. + * @param vq Pointer to struct virtio_vq_info. + * + * @return None + */ +void vq_clear_used_ring_flags(struct virtio_base *base, struct virtio_vq_info *vq) +{ + int backend_type = base->backend_type; + int polling_in_progress = base->polling_in_progress; + + /* we should never unmask notification in polling mode */ + if (virtio_poll_enabled && backend_type == BACKEND_VBSU && polling_in_progress == 1) + return; + + vq->used->flags &= ~ACRN_VRING_USED_F_NO_NOTIFY; +} + struct config_reg { uint16_t offset; /* register offset */ uint8_t size; /* size (bytes) */ @@ -877,6 +964,17 @@ bad: (*vops->set_status)(DEV_STRUCT(base), value); if (value == 0) (*vops->reset)(DEV_STRUCT(base)); + if ((value & VIRTIO_CR_STATUS_DRIVER_OK) && + base->backend_type == BACKEND_VBSU && + virtio_poll_enabled) { + base->polling_timer.clockid = CLOCK_MONOTONIC; + acrn_timer_init(&base->polling_timer, virtio_poll_timer, base); + /* wait 5s to start virtio poll mode + * skip vsbl and make sure device initialization completed + * FIXME: Need optimization in the future + */ + virtio_start_timer(&base->polling_timer, 5, 0); + } break; case VIRTIO_CR_CFGVEC: base->msix_cfg_idx = value; @@ -1302,6 +1400,7 @@ virtio_common_cfg_write(struct pci_vdev *dev, uint64_t offset, int size, (*vops->set_status)(DEV_STRUCT(base), value); if (base->status == 0) (*vops->reset)(DEV_STRUCT(base)); + /* TODO: virtio poll mode for modern devices */ break; case VIRTIO_COMMON_Q_SELECT: /* @@ -1878,3 +1977,26 @@ virtio_pci_modern_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_vdev *dev, return -1; } + +/** + * @brief Get the virtio poll parameters + * + * @param optarg Pointer to parameters string. + * + * @return fail -1 success 0 + */ +int +acrn_parse_virtio_poll_interval(const char *optarg) +{ + char *ptr; + + virtio_poll_interval = strtoul(optarg, &ptr, 0); + + /* poll interval is limited from 1us to 10ms */ + if (virtio_poll_interval < 1 || virtio_poll_interval > 10000000) + return -1; + + virtio_poll_enabled = 1; + + return 0; +} \ No newline at end of file diff --git a/devicemodel/hw/pci/virtio/virtio_audio.c b/devicemodel/hw/pci/virtio/virtio_audio.c index 919fdb78a..26bee5c19 100644 --- a/devicemodel/hw/pci/virtio/virtio_audio.c +++ b/devicemodel/hw/pci/virtio/virtio_audio.c @@ -316,7 +316,8 @@ virtio_audio_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) &virtio_audio_ops_k, virt_audio, dev, - virt_audio->vq); + virt_audio->vq, + BACKEND_VBSK); rc = virtio_audio_kernel_init(virt_audio); if (rc < 0) { diff --git a/devicemodel/hw/pci/virtio/virtio_block.c b/devicemodel/hw/pci/virtio/virtio_block.c index beb0c4162..4c405983b 100644 --- a/devicemodel/hw/pci/virtio/virtio_block.c +++ b/devicemodel/hw/pci/virtio/virtio_block.c @@ -384,7 +384,7 @@ virtio_blk_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) "error %d!\n", rc)); /* init virtio struct and virtqueues */ - virtio_linkup(&blk->base, &virtio_blk_ops, blk, dev, &blk->vq); + virtio_linkup(&blk->base, &virtio_blk_ops, blk, dev, &blk->vq, BACKEND_VBSU); blk->base.mtx = &blk->mtx; blk->vq.qsize = VIRTIO_BLK_RINGSZ; diff --git a/devicemodel/hw/pci/virtio/virtio_console.c b/devicemodel/hw/pci/virtio/virtio_console.c index 03376453f..e44db8fae 100644 --- a/devicemodel/hw/pci/virtio/virtio_console.c +++ b/devicemodel/hw/pci/virtio/virtio_console.c @@ -828,7 +828,7 @@ virtio_console_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) "error %d!\n", rc)); virtio_linkup(&console->base, &virtio_console_ops, console, dev, - console->queues); + console->queues, BACKEND_VBSU); console->base.mtx = &console->mtx; console->base.device_caps = VIRTIO_CONSOLE_S_HOSTCAPS; diff --git a/devicemodel/hw/pci/virtio/virtio_coreu.c b/devicemodel/hw/pci/virtio/virtio_coreu.c index f30c2728e..05ebf1c57 100644 --- a/devicemodel/hw/pci/virtio/virtio_coreu.c +++ b/devicemodel/hw/pci/virtio/virtio_coreu.c @@ -281,7 +281,7 @@ virtio_coreu_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) DPRINTF(("vcoreu init: using VBS-U...\n")); virtio_linkup(&vcoreu->base, &virtio_coreu_ops, - vcoreu, dev, vcoreu->queues); + vcoreu, dev, vcoreu->queues, BACKEND_VBSU); vcoreu->base.mtx = &vcoreu->mtx; vcoreu->queues[0].qsize = VIRTIO_COREU_RINGSZ; diff --git a/devicemodel/hw/pci/virtio/virtio_hdcp.c b/devicemodel/hw/pci/virtio/virtio_hdcp.c index e2cfa9e80..6ce00fc56 100644 --- a/devicemodel/hw/pci/virtio/virtio_hdcp.c +++ b/devicemodel/hw/pci/virtio/virtio_hdcp.c @@ -413,7 +413,7 @@ virtio_hdcp_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) DPRINTF(("vhdcp init: using VBS-U...\n")); virtio_linkup(&vhdcp->base, &virtio_hdcp_ops, - vhdcp, dev, vhdcp->queues); + vhdcp, dev, vhdcp->queues, BACKEND_VBSU); vhdcp->base.mtx = &vhdcp->mtx; vhdcp->queues[0].qsize = VIRTIO_HDCP_RINGSZ; diff --git a/devicemodel/hw/pci/virtio/virtio_hyper_dmabuf.c b/devicemodel/hw/pci/virtio/virtio_hyper_dmabuf.c index 12fabb3b9..04294e272 100644 --- a/devicemodel/hw/pci/virtio/virtio_hyper_dmabuf.c +++ b/devicemodel/hw/pci/virtio/virtio_hyper_dmabuf.c @@ -299,7 +299,8 @@ virtio_hyper_dmabuf_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) &virtio_hyper_dmabuf_ops_k, hyper_dmabuf, dev, - hyper_dmabuf->vq); + hyper_dmabuf->vq, + BACKEND_VBSK); rc = virtio_hyper_dmabuf_k_init(); if (rc < 0) { diff --git a/devicemodel/hw/pci/virtio/virtio_input.c b/devicemodel/hw/pci/virtio/virtio_input.c index e93c4d4ba..fc284d86c 100644 --- a/devicemodel/hw/pci/virtio/virtio_input.c +++ b/devicemodel/hw/pci/virtio/virtio_input.c @@ -640,7 +640,7 @@ virtio_input_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) goto fail; } - virtio_linkup(&vi->base, &virtio_input_ops, vi, dev, vi->queues); + virtio_linkup(&vi->base, &virtio_input_ops, vi, dev, vi->queues, BACKEND_VBSU); vi->base.mtx = &vi->mtx; vi->base.device_caps = VIRTIO_INPUT_S_HOSTCAPS; diff --git a/devicemodel/hw/pci/virtio/virtio_ipu.c b/devicemodel/hw/pci/virtio/virtio_ipu.c index ef3214823..e4b1f7bf0 100644 --- a/devicemodel/hw/pci/virtio/virtio_ipu.c +++ b/devicemodel/hw/pci/virtio/virtio_ipu.c @@ -320,7 +320,8 @@ virtio_ipu_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) &virtio_ipu_ops_k, ipu, dev, - ipu->vq); + ipu->vq, + BACKEND_VBSK); rc = virtio_ipu_k_init(ipu); if (rc < 0) { diff --git a/devicemodel/hw/pci/virtio/virtio_mei.c b/devicemodel/hw/pci/virtio/virtio_mei.c index f50c587a1..89eb0a1b4 100644 --- a/devicemodel/hw/pci/virtio/virtio_mei.c +++ b/devicemodel/hw/pci/virtio/virtio_mei.c @@ -1577,7 +1577,7 @@ vmei_notify_tx(void *data, struct virtio_vq_info *vq) pthread_mutex_lock(&vmei->tx_mutex); DPRINTF("TX: New OUT buffer available!\n"); - vq->used->flags &= ~ACRN_VRING_USED_F_NO_NOTIFY; + vq_clear_used_ring_flags(&vmei->base, vq); pthread_mutex_unlock(&vmei->tx_mutex); } @@ -1870,7 +1870,7 @@ static void *vmei_rx_thread(void *param) while (vmei->status != VMEI_STST_DEINIT) { /* note - rx mutex is locked here */ while (vq_ring_ready(vq)) { - vq->used->flags &= ~ACRN_VRING_USED_F_NO_NOTIFY; + vq_clear_used_ring_flags(&vmei->base, vq); mb(); if (vq_has_descs(vq) && vmei->rx_need_sched && @@ -2255,7 +2255,7 @@ init: goto fail; } - virtio_linkup(&vmei->base, &virtio_mei_ops, vmei, dev, vmei->vqs); + virtio_linkup(&vmei->base, &virtio_mei_ops, vmei, dev, vmei->vqs, BACKEND_VBSU); vmei->base.mtx = &vmei->mutex; for (i = 0; i < VMEI_VQ_NUM; i++) diff --git a/devicemodel/hw/pci/virtio/virtio_net.c b/devicemodel/hw/pci/virtio/virtio_net.c index 32fd085ca..e33dc357f 100644 --- a/devicemodel/hw/pci/virtio/virtio_net.c +++ b/devicemodel/hw/pci/virtio/virtio_net.c @@ -555,7 +555,7 @@ virtio_net_tx_thread(void *param) for (;;) { /* note - tx mutex is locked here */ while (net->resetting || !vq_has_descs(vq)) { - vq->used->flags &= ~ACRN_VRING_USED_F_NO_NOTIFY; + vq_clear_used_ring_flags(&net->base, vq); /* memory barrier */ mb(); if (!net->resetting && vq_has_descs(vq)) @@ -724,7 +724,7 @@ virtio_net_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) char nstr[80]; char tname[MAXCOMLEN + 1]; struct virtio_net *net; - char *devname; + char *devname = NULL; char *vtopts; char *opt; int mac_provided; @@ -751,25 +751,10 @@ virtio_net_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) DPRINTF(("virtio_net: pthread_mutex_init failed with " "error %d!\n", rc)); - virtio_linkup(&net->base, &virtio_net_ops, net, dev, net->queues); - net->base.mtx = &net->mtx; - net->base.device_caps = VIRTIO_NET_S_HOSTCAPS; - - net->queues[VIRTIO_NET_RXQ].qsize = VIRTIO_NET_RINGSZ; - net->queues[VIRTIO_NET_RXQ].notify = virtio_net_ping_rxq; - net->queues[VIRTIO_NET_TXQ].qsize = VIRTIO_NET_RINGSZ; - net->queues[VIRTIO_NET_TXQ].notify = virtio_net_ping_txq; -#ifdef notyet - net->queues[VIRTIO_NET_CTLQ].qsize = VIRTIO_NET_RINGSZ; - net->queues[VIRTIO_NET_CTLQ].notify = virtio_net_ping_ctlq; -#endif - /* - * Attempt to open the tap device and read the MAC address - * if specified + * Read the MAC address if specified */ mac_provided = 0; - net->tapfd = -1; net->vhost_net = NULL; if (opts != NULL) { int err; @@ -795,14 +780,38 @@ virtio_net_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) mac_provided = 1; } } - - if (strncmp(devname, "tap", 3) == 0 || - strncmp(devname, "vmnet", 5) == 0) - virtio_net_tap_setup(net, devname); - - free(devname); } + virtio_linkup(&net->base, &virtio_net_ops, net, dev, net->queues, + net->use_vhost ? BACKEND_VHOST : BACKEND_VBSU); + net->base.mtx = &net->mtx; + net->base.device_caps = VIRTIO_NET_S_HOSTCAPS; + + net->queues[VIRTIO_NET_RXQ].qsize = VIRTIO_NET_RINGSZ; + net->queues[VIRTIO_NET_RXQ].notify = virtio_net_ping_rxq; + net->queues[VIRTIO_NET_TXQ].qsize = VIRTIO_NET_RINGSZ; + net->queues[VIRTIO_NET_TXQ].notify = virtio_net_ping_txq; +#ifdef notyet + net->queues[VIRTIO_NET_CTLQ].qsize = VIRTIO_NET_RINGSZ; + net->queues[VIRTIO_NET_CTLQ].notify = virtio_net_ping_ctlq; +#endif + + /* + * Attempt to open the tap device + */ + net->tapfd = -1; + + if (!devname) { + WPRINTF(("virtio_net: devname NULL\n")); + return -1; + } + + if (strncmp(devname, "tap", 3) == 0 || + strncmp(devname, "vmnet", 5) == 0) + virtio_net_tap_setup(net, devname); + + free(devname); + /* * The default MAC address is the standard NetApp OUI of 00-a0-98, * followed by an MD5 of the PCI slot/func number and dev name diff --git a/devicemodel/hw/pci/virtio/virtio_rnd.c b/devicemodel/hw/pci/virtio/virtio_rnd.c index cfddcd0c8..2b30f6ca8 100644 --- a/devicemodel/hw/pci/virtio/virtio_rnd.c +++ b/devicemodel/hw/pci/virtio/virtio_rnd.c @@ -404,7 +404,7 @@ virtio_rnd_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) if (rnd->vbs_k.status == VIRTIO_DEV_PRE_INIT) { DPRINTF(("%s: VBS-K option detected!\n", __func__)); virtio_linkup(&rnd->base, &virtio_rnd_ops_k, - rnd, dev, &rnd->vq); + rnd, dev, &rnd->vq, BACKEND_VBSK); rc = virtio_rnd_kernel_init(rnd); if (rc < 0) { WPRINTF(("virtio_rnd: VBS-K init failed,error %d!\n", @@ -417,7 +417,7 @@ virtio_rnd_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) if (rnd->vbs_k.status == VIRTIO_DEV_INITIAL || rnd->vbs_k.status != VIRTIO_DEV_INIT_SUCCESS) { DPRINTF(("%s: fallback to VBS-U...\n", __func__)); - virtio_linkup(&rnd->base, &virtio_rnd_ops, rnd, dev, &rnd->vq); + virtio_linkup(&rnd->base, &virtio_rnd_ops, rnd, dev, &rnd->vq, BACKEND_VBSU); } rnd->base.mtx = &rnd->mtx; diff --git a/devicemodel/hw/pci/virtio/virtio_rpmb.c b/devicemodel/hw/pci/virtio/virtio_rpmb.c index e72498479..34a7dca67 100644 --- a/devicemodel/hw/pci/virtio/virtio_rpmb.c +++ b/devicemodel/hw/pci/virtio/virtio_rpmb.c @@ -512,7 +512,7 @@ virtio_rpmb_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) goto out; } - virtio_linkup(&rpmb->base, &virtio_rpmb_ops, rpmb, dev, &rpmb->vq); + virtio_linkup(&rpmb->base, &virtio_rpmb_ops, rpmb, dev, &rpmb->vq, BACKEND_VBSU); rpmb->base.mtx = &rpmb->mtx; rpmb->vq.qsize = VIRTIO_RPMB_RINGSZ; diff --git a/devicemodel/include/virtio.h b/devicemodel/include/virtio.h index 3a07fe545..bcce4fafc 100644 --- a/devicemodel/include/virtio.h +++ b/devicemodel/include/virtio.h @@ -127,6 +127,7 @@ */ #include "types.h" +#include "timer.h" /** * @brief virtio API @@ -171,6 +172,14 @@ struct virtio_vring_used { /* uint16_t avail_event; -- after N ring entries */ } __attribute__((packed)); +enum { + BACKEND_UNKNOWN = 0, + BACKEND_VBSU, + BACKEND_VBSK, + BACKEND_VHOST, + BACKEND_MAX +}; + /* * The address of any given virtual queue is determined by a single * Page Frame Number register. The guest writes the PFN into the @@ -520,6 +529,9 @@ struct virtio_base { uint32_t device_feature_select; /**< current selected device feature */ uint32_t driver_feature_select; /**< current selected guest feature */ int cfg_coff; /**< PCI cfg access capability offset */ + int backend_type; /**< VBSU, VBSK or VHOST */ + struct acrn_timer polling_timer; /**< timer for polling mode */ + int polling_in_progress; /**< The polling status */ }; #define VIRTIO_BASE_LOCK(vb) \ @@ -703,12 +715,23 @@ struct iovec; * @param pci_virtio_dev Pointer to instance of certain virtio device. * @param dev Pointer to struct pci_vdev which emulates a PCI device. * @param queues Pointer to struct virtio_vq_info, normally an array. + * @param backend_type can be VBSU, VBSK or VHOST * * @return None */ void virtio_linkup(struct virtio_base *base, struct virtio_ops *vops, void *pci_virtio_dev, struct pci_vdev *dev, - struct virtio_vq_info *queues); + struct virtio_vq_info *queues, + int backend_type); + +/** + * @brief Get the virtio poll parameters + * + * @param optarg Pointer to parameters string. + * + * @return fail -1 success 0 + */ +int acrn_parse_virtio_poll_interval(const char *optarg); /** * @brief Initialize MSI-X vector capabilities if we're to use MSI-X, @@ -816,6 +839,20 @@ void vq_relchain(struct virtio_vq_info *vq, uint16_t idx, uint32_t iolen); */ void vq_endchains(struct virtio_vq_info *vq, int used_all_avail); +/** + * @brief Helper function for clearing used ring flags. + * + * Driver should always use this helper function to clear used ring flags. + * For virtio poll mode, in order to avoid trap, we should never really + * clear used ring flags. + * + * @param base Pointer to struct virtio_base. + * @param vq Pointer to struct virtio_vq_info. + * + * @return None + */ +void vq_clear_used_ring_flags(struct virtio_base *base, struct virtio_vq_info *vq); + /** * @brief Handle PCI configuration space reads. *