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:
parent
ee6bc2e7df
commit
b0d70b76cb
5 changed files with 143 additions and 42 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue