virtio-pci.c: polling mode support

1. Some platforms do not support interrupt mode (PCI_MSI/MSIX),
so add polling mode support, so these platforms can also use virtio-pci;
2. In some cases, we do not want to use the interrupt for virtio driver
to avoid time jitter, so add the polling mode support;
3. If CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0, interrupt mode.
   if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD > 0, polling mode.

Signed-off-by: wangyongrong <wangyongrong@xiaomi.com>
This commit is contained in:
wangyongrong 2024-05-16 21:30:35 +08:00 committed by Xiang Xiao
parent ee6bc2e7df
commit b0d70b76cb
5 changed files with 143 additions and 42 deletions

View file

@ -26,6 +26,15 @@ config DRIVERS_VIRTIO_PCI
bool "Virtio PCI Device Support"
default n
config DRIVERS_VIRTIO_PCI_POLLING_PERIOD
int "Virtio PCI Polling Period (us)"
depends on DRIVERS_VIRTIO_PCI
default 0
---help---
if Polling Period <= 0, not support polling mode.
if Polling Period > 0, support polling mode, and it represent
polling period (us).
config DRIVERS_VIRTIO_BLK
bool "Virtio block support"
depends on !DISABLE_MOUNTPOINT

View file

@ -107,7 +107,8 @@ static int
virtio_pci_legacy_create_virtqueue(FAR struct virtio_pci_device_s *vpdev,
FAR struct virtqueue *vq);
static int
virtio_pci_legacy_config_vector(FAR struct virtio_pci_device_s *vpdev);
virtio_pci_legacy_config_vector(FAR struct virtio_pci_device_s *vpdev,
bool enable);
static void
virtio_pci_legacy_delete_virtqueue(FAR struct virtio_device *vdev,
@ -186,7 +187,9 @@ static int
virtio_pci_legacy_create_virtqueue(FAR struct virtio_pci_device_s *vpdev,
FAR struct virtqueue *vq)
{
#if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0
uint16_t msix_vector;
#endif
/* Set the pci virtqueue register, active vq, enable vq */
@ -198,6 +201,8 @@ virtio_pci_legacy_create_virtqueue(FAR struct virtio_pci_device_s *vpdev,
pci_write_io_dword(vpdev->dev, vpdev->ioaddr + VIRTIO_PCI_QUEUE_PFN,
up_addrenv_va_to_pa(vq->vq_ring.desc) >>
VIRTIO_PCI_QUEUE_ADDR_SHIFT);
#if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0
pci_write_io_word(vpdev->dev, vpdev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR,
VIRTIO_PCI_INT_VQ);
pci_read_io_word(vpdev->dev, vpdev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR,
@ -209,6 +214,7 @@ virtio_pci_legacy_create_virtqueue(FAR struct virtio_pci_device_s *vpdev,
vrterr("Msix vector is 0\n");
return -EBUSY;
}
#endif
return OK;
}
@ -218,15 +224,18 @@ virtio_pci_legacy_create_virtqueue(FAR struct virtio_pci_device_s *vpdev,
****************************************************************************/
static int
virtio_pci_legacy_config_vector(FAR struct virtio_pci_device_s *vpdev)
virtio_pci_legacy_config_vector(FAR struct virtio_pci_device_s *vpdev,
bool enable)
{
uint16_t vector = enable ? 0 : VIRTIO_PCI_MSI_NO_VECTOR;
uint16_t rvector;
pci_write_io_word(vpdev->dev, vpdev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR,
VIRTIO_PCI_INT_CFG);
vector);
pci_read_io_word(vpdev->dev, vpdev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR,
&rvector);
if (rvector == VIRTIO_PCI_MSI_NO_VECTOR)
if (rvector != vector)
{
return -EINVAL;
}
@ -243,15 +252,20 @@ void virtio_pci_legacy_delete_virtqueue(FAR struct virtio_device *vdev,
{
FAR struct virtio_pci_device_s *vpdev =
(FAR struct virtio_pci_device_s *)vdev;
#if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0
uint8_t isr;
#endif
pci_write_io_word(vpdev->dev, vpdev->ioaddr + VIRTIO_PCI_QUEUE_SEL, index);
#if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0
pci_write_io_word(vpdev->dev, vpdev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR,
VIRTIO_PCI_MSI_NO_VECTOR);
/* Flush the write out to device */
pci_read_io_byte(vpdev->dev, vpdev->ioaddr + VIRTIO_PCI_ISR, &isr);
#endif
/* Select and deactivate the queue */
@ -295,13 +309,17 @@ static void virtio_pci_legacy_write_config(FAR struct virtio_device *vdev,
{
FAR struct virtio_pci_device_s *vpdev =
(FAR struct virtio_pci_device_s *)vdev;
#if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0
FAR char *config = vpdev->ioaddr + VIRTIO_PCI_CONFIG_OFF(true) + offset;
#else
FAR char *config = vpdev->ioaddr + VIRTIO_PCI_CONFIG_OFF(false) + offset;
#endif
FAR uint8_t *s = src;
int i;
for (i = 0; i < length; i++)
{
pci_write_io_byte(vpdev->dev, vpdev->ioaddr +
VIRTIO_PCI_CONFIG_OFF(true) + offset + i, s[i]);
pci_write_io_byte(vpdev->dev, config + i, s[i]);
}
}
@ -315,13 +333,17 @@ static void virtio_pci_legacy_read_config(FAR struct virtio_device *vdev,
{
FAR struct virtio_pci_device_s *vpdev =
(FAR struct virtio_pci_device_s *)vdev;
#if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0
FAR char *config = vpdev->ioaddr + VIRTIO_PCI_CONFIG_OFF(true) + offset;
#else
FAR char *config = vpdev->ioaddr + VIRTIO_PCI_CONFIG_OFF(false) + offset;
#endif
FAR uint8_t *d = dst;
int i;
for (i = 0; i < length; i++)
{
pci_read_io_byte(vpdev->dev, vpdev->ioaddr +
VIRTIO_PCI_CONFIG_OFF(true) + offset + i, &d[i]);
pci_read_io_byte(vpdev->dev, config + i, &d[i]);
}
}

View file

@ -159,7 +159,8 @@ static uint16_t
virtio_pci_modern_get_queue_len(FAR struct virtio_pci_device_s *vpdev,
int idx);
static int
virtio_pci_modern_config_vector(FAR struct virtio_pci_device_s *vpdev);
virtio_pci_modern_config_vector(FAR struct virtio_pci_device_s *vpdev,
bool enable);
static int
virtio_pci_modern_create_virtqueue(FAR struct virtio_pci_device_s *vpdev,
FAR struct virtqueue *vq);
@ -321,7 +322,9 @@ virtio_pci_modern_create_virtqueue(FAR struct virtio_pci_device_s *vpdev,
{
FAR struct virtio_pci_common_cfg_s *cfg = vpdev->common;
FAR struct virtio_vring_info *vrinfo;
#if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0
uint16_t msix_vector;
#endif
uint16_t off;
/* Activate the queue */
@ -336,6 +339,7 @@ virtio_pci_modern_create_virtqueue(FAR struct virtio_pci_device_s *vpdev,
pci_write_io_qword(vpdev->dev, &cfg->queue_used,
up_addrenv_va_to_pa(vq->vq_ring.used));
#if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0
pci_write_io_word(vpdev->dev, &cfg->queue_msix_vector,
VIRTIO_PCI_INT_VQ);
pci_read_io_word(vpdev->dev, &cfg->queue_msix_vector, &msix_vector);
@ -344,6 +348,7 @@ virtio_pci_modern_create_virtqueue(FAR struct virtio_pci_device_s *vpdev,
vrterr("Msix_vector is no vector\n");
return -EBUSY;
}
#endif
/* Enable vq */
@ -368,15 +373,16 @@ virtio_pci_modern_create_virtqueue(FAR struct virtio_pci_device_s *vpdev,
****************************************************************************/
static int
virtio_pci_modern_config_vector(FAR struct virtio_pci_device_s *vpdev)
virtio_pci_modern_config_vector(FAR struct virtio_pci_device_s *vpdev,
bool enable)
{
FAR struct virtio_pci_common_cfg_s *cfg = vpdev->common;
uint16_t vector = enable ? 0 : VIRTIO_PCI_MSI_NO_VECTOR;
uint16_t rvector;
pci_write_io_word(vpdev->dev, &cfg->config_msix_vector,
VIRTIO_PCI_INT_CFG);
pci_write_io_word(vpdev->dev, &cfg->config_msix_vector, vector);
pci_read_io_word(vpdev->dev, &cfg->config_msix_vector, &rvector);
if (rvector == VIRTIO_PCI_MSI_NO_VECTOR)
if (rvector != vector)
{
return -EINVAL;
}
@ -396,8 +402,11 @@ void virtio_pci_modern_delete_virtqueue(FAR struct virtio_device *vdev,
FAR struct virtio_pci_common_cfg_s *cfg = vpdev->common;
pci_write_io_word(vpdev->dev, &cfg->queue_select, index);
#if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0
pci_write_io_word(vpdev->dev, &cfg->queue_msix_vector,
VIRTIO_PCI_MSI_NO_VECTOR);
#endif
}
/****************************************************************************

View file

@ -29,10 +29,18 @@
#include <stdint.h>
#include <sys/param.h>
#include <nuttx/clock.h>
#include <nuttx/virtio/virtio-pci.h>
#include "virtio-pci.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define VIRTIO_PCI_WORK_DELAY \
USEC2TICK(CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD)
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
@ -66,24 +74,11 @@ static struct pci_driver_s g_virtio_pci_drv =
****************************************************************************/
/****************************************************************************
* Name: virtio_pci_config_changed
* Name: virtio_pci_vq_callback
****************************************************************************/
static int virtio_pci_config_changed(int irq, FAR void *context,
FAR void *arg)
static void virtio_pci_vq_callback(FAR struct virtio_pci_device_s *vpdev)
{
/* TODO: not support config changed notification */
return OK;
}
/****************************************************************************
* Name: virtio_pci_interrupt
****************************************************************************/
static int virtio_pci_interrupt(int irq, FAR void *context, FAR void *arg)
{
FAR struct virtio_pci_device_s *vpdev = arg;
FAR struct virtio_vring_info *vrings_info = vpdev->vdev.vrings_info;
FAR struct virtqueue *vq;
unsigned int i;
@ -97,10 +92,49 @@ static int virtio_pci_interrupt(int irq, FAR void *context, FAR void *arg)
vq->callback(vq);
}
}
}
#if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0
/****************************************************************************
* Name: virtio_pci_interrupt
****************************************************************************/
static int virtio_pci_interrupt(int irq, FAR void *context, FAR void *arg)
{
FAR struct virtio_pci_device_s *vpdev = arg;
virtio_pci_vq_callback(vpdev);
return OK;
}
/****************************************************************************
* Name: virtio_pci_config_changed
****************************************************************************/
static int virtio_pci_config_changed(int irq, FAR void *context,
FAR void *arg)
{
/* TODO: not support config changed notification */
return OK;
}
#else
/****************************************************************************
* Name: virtio_pci_wdog
****************************************************************************/
static void virtio_pci_wdog(wdparm_t arg)
{
FAR struct virtio_pci_device_s *vpdev =
(FAR struct virtio_pci_device_s *)arg;
virtio_pci_vq_callback(vpdev);
wd_start(&vpdev->wdog, VIRTIO_PCI_WORK_DELAY, virtio_pci_wdog,
(wdparm_t)vpdev);
}
#endif
/****************************************************************************
* Name: virtio_pci_probe
****************************************************************************/
@ -154,16 +188,18 @@ static int virtio_pci_probe(FAR struct pci_device_s *dev)
}
}
#if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0
/* Irq init */
ret = pci_alloc_irq(vpdev->dev, vpdev->irq, VIRTIO_PCI_INT_NUM);
if (ret != VIRTIO_PCI_INT_NUM)
{
vrterr("Failed to allocate MSI %d\n", ret);
vrterr("Failed to allocate MSI, ret=%d\n", ret);
goto err_with_enable;
}
vrtinfo("Attaching MSI %d to %p, %d to %p\n",
vrtinfo("Interrupt mode: attaching MSI %d to %p, %d to %p\n",
vpdev->irq[VIRTIO_PCI_INT_CFG], virtio_pci_config_changed,
vpdev->irq[VIRTIO_PCI_INT_VQ], virtio_pci_interrupt);
@ -177,6 +213,9 @@ static int virtio_pci_probe(FAR struct pci_device_s *dev)
irq_attach(vpdev->irq[VIRTIO_PCI_INT_CFG],
virtio_pci_config_changed, vpdev);
irq_attach(vpdev->irq[VIRTIO_PCI_INT_VQ], virtio_pci_interrupt, vpdev);
#else
vrtinfo("Polling mode\n");
#endif
virtio_set_status(vdev, VIRTIO_CONFIG_STATUS_RESET);
virtio_set_status(vdev, VIRTIO_CONFIG_STATUS_ACK);
@ -191,10 +230,12 @@ static int virtio_pci_probe(FAR struct pci_device_s *dev)
return ret;
err_with_attach:
#if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0
irq_detach(vpdev->irq[VIRTIO_PCI_INT_CFG]);
irq_detach(vpdev->irq[VIRTIO_PCI_INT_VQ]);
err_with_irq:
pci_release_irq(vpdev->dev, vpdev->irq, VIRTIO_PCI_INT_NUM);
#endif
err_with_enable:
pci_clear_master(dev);
pci_disable_device(dev);
@ -213,9 +254,11 @@ static void virtio_pci_remove(FAR struct pci_device_s *dev)
virtio_unregister_device(&vpdev->vdev);
#if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0
irq_detach(vpdev->irq[VIRTIO_PCI_INT_CFG]);
irq_detach(vpdev->irq[VIRTIO_PCI_INT_VQ]);
pci_release_irq(vpdev->dev, vpdev->irq, VIRTIO_PCI_INT_NUM);
#endif
pci_clear_master(dev);
pci_disable_device(dev);
@ -313,17 +356,10 @@ int virtio_pci_create_virtqueues(FAR struct virtio_device *vdev,
vdev->vrings_info = kmm_zalloc(sizeof(struct virtio_vring_info) * nvqs);
if (vdev->vrings_info == NULL)
{
vrterr("alloc vrings info failed\n");
vrterr("Alloc vrings info failed\n");
return -ENOMEM;
}
ret = vpdev->ops->config_vector(vpdev);
if (ret < 0)
{
vrterr("read virtio pci config msix vector failed\n");
return ret;
}
/* Alloc and init the virtqueue */
for (i = 0; i < nvqs; i++)
@ -335,10 +371,26 @@ int virtio_pci_create_virtqueues(FAR struct virtio_device *vdev,
}
}
/* Finally, enable the interrupt */
#if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0
ret = vpdev->ops->config_vector(vpdev, true);
if (ret < 0)
{
vrterr("Config vector failed, vector=%u\n", ret);
goto err;
}
up_enable_irq(vpdev->irq[VIRTIO_PCI_INT_CFG]);
up_enable_irq(vpdev->irq[VIRTIO_PCI_INT_VQ]);
#else
ret = wd_start(&vpdev->wdog, VIRTIO_PCI_WORK_DELAY, virtio_pci_wdog,
(wdparm_t)vpdev);
if (ret < 0)
{
pcierr("Wd_start failed: %d\n", ret);
goto err;
}
#endif
return ret;
err:
@ -357,10 +409,13 @@ void virtio_pci_delete_virtqueues(FAR struct virtio_device *vdev)
FAR struct virtio_vring_info *vrinfo;
unsigned int i;
/* Disable interrupt first */
#if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0
up_disable_irq(vpdev->irq[VIRTIO_PCI_INT_CFG]);
up_disable_irq(vpdev->irq[VIRTIO_PCI_INT_VQ]);
vpdev->ops->config_vector(vpdev, false);
#else
wd_cancel(&vpdev->wdog);
#endif
/* Free the memory */

View file

@ -31,6 +31,7 @@
#include <nuttx/pci/pci.h>
#include <nuttx/virtio/virtio.h>
#include <nuttx/wdog.h>
/****************************************************************************
* Pre-processor Definitions
@ -65,7 +66,8 @@ struct virtio_pci_ops_s
{
CODE uint16_t (*get_queue_len)(FAR struct virtio_pci_device_s *vpdev,
int idx);
CODE int (*config_vector)(FAR struct virtio_pci_device_s *vpdev);
CODE int (*config_vector)(FAR struct virtio_pci_device_s *vpdev,
bool enable);
CODE int (*create_virtqueue)(FAR struct virtio_pci_device_s *vpdev,
FAR struct virtqueue *vq);
CODE void (*delete_virtqueue)(FAR struct virtio_device *vdev, int index);
@ -80,7 +82,11 @@ struct virtio_pci_device_s
struct metal_io_region shm_io; /* Share memory io region,
* virtqueue use this io.
*/
#if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0
int irq[VIRTIO_PCI_INT_NUM];
#else
struct wdog_s wdog;
#endif
/* for modern */