From 7a495fabb6ae5cf98d48289e0ab6c63529d745ba Mon Sep 17 00:00:00 2001 From: Bowen Wang Date: Wed, 13 Mar 2024 19:42:42 +0800 Subject: [PATCH] pci/pci_uio_ivshmem: pci uio ivshmem msix interrupt support 1. Change pci uio ivshmem driver from pci bus based tp pci-ivshmem bus based; 2. Add the interrupt support by usin the pci_ivshmem API; Signed-off-by: Bowen Wang --- drivers/pci/Kconfig | 12 ++ drivers/pci/pci_uio_ivshmem.c | 379 +++++++++++++++++++++++++++------- 2 files changed, 316 insertions(+), 75 deletions(-) diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index cfafb329ecd9d..8722e164ca8e5 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -47,10 +47,22 @@ config PCI_IVSHMEM config PCI_UIO_IVSHMEM bool "Enable uio ivshmem driver support" default n + depends on PCI_IVSHMEM ---help--- When this option is enabled, char ivshmem driver will register char device with name: "/dev/uioX" to the VFS, then application can open this device and use `mmap()` to get the share memory provided by ivshmem device. +config PCI_UIO_IVSHMEM_IDTABLE + string "The id table of uio ivshmem device" + depends on PCI_UIO_IVSHMEM + ---help--- + "id0;id1;id2", e.g.: "0;1;2" + +config PCI_UIO_IVSHMEM_NPOLLWAITERS + int "PCI uio ivshmem poll waiter number" + default 2 + depends on PCI_UIO_IVSHMEM + endif # PCI diff --git a/drivers/pci/pci_uio_ivshmem.c b/drivers/pci/pci_uio_ivshmem.c index 2fa855f3f567f..06fe49a040c4c 100644 --- a/drivers/pci/pci_uio_ivshmem.c +++ b/drivers/pci/pci_uio_ivshmem.c @@ -26,29 +26,61 @@ #include #include +#include +#include #include +#include #include #include -#include - -#include "pci_drivers.h" +#include +#include +#include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ -#define UIO_IVSHMEM_SHMEM_BAR 2 +#define dev_to_udev(dev) \ + ((FAR struct uio_ivshmem_dev_s *)ivshmem_get_driver(dev)) /**************************************************************************** * Private Types ****************************************************************************/ +struct uio_ivshmem_notify_s; +typedef CODE void (*uio_ivshmem_notify_t)( + FAR struct uio_ivshmem_notify_s *notify, int32_t newevent); + +struct uio_ivshmem_notify_s +{ + struct list_node node; + uio_ivshmem_notify_t cb; +}; + +struct uio_ivshmem_read_s +{ + struct uio_ivshmem_notify_s notify; + sem_t wait; +}; + +struct uio_ivshmem_poll_s +{ + struct uio_ivshmem_notify_s notify; + FAR struct pollfd *fds; + FAR void **ppriv; +}; + struct uio_ivshmem_dev_s { - FAR void *shmem; - size_t shmem_size; - char name[32]; + struct ivshmem_driver_s drv; + FAR struct ivshmem_device_s *dev; + char name[32]; + + spinlock_t lock; + int32_t event_count; + struct list_node notify_list; + struct uio_ivshmem_poll_s polls[CONFIG_PCI_UIO_IVSHMEM_NPOLLWAITERS]; }; /**************************************************************************** @@ -66,9 +98,13 @@ static int uio_ivshmem_unmap(FAR struct task_group_s *group, FAR void *start, size_t length); static int uio_ivshmem_mmap(FAR struct file *filep, FAR struct mm_map_entry_s *map); +static int uio_ivshmem_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup); + +static int uio_ivshmem_probe(FAR struct ivshmem_device_s *dev); +static void uio_ivshmem_remove(FAR struct ivshmem_device_s *dev); -static int uio_ivshmem_probe(FAR struct pci_device_s *dev); -static void uio_ivshmem_remove(FAR struct pci_device_s *dev); +static int uio_ivshmem_interrupt(int irq, FAR void *context, FAR void *arg); /**************************************************************************** * Private Data @@ -84,38 +120,61 @@ static const struct file_operations g_uio_ivshmem_fops = NULL, /* ioctl */ uio_ivshmem_mmap, /* mmap */ NULL, /* truncate */ - NULL /* poll */ + uio_ivshmem_poll /* poll */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , NULL /* unlink */ #endif }; -static const struct pci_device_id_s g_uio_ivshmem_ids[] = -{ - { PCI_DEVICE(0x1af4, 0x1110) }, - { 0, } -}; +/**************************************************************************** + * Private Functions + ****************************************************************************/ -static struct pci_driver_s g_uio_ivshmem_drv = +/**************************************************************************** + * Name: uio_ivshmem_add_notify + ****************************************************************************/ + +static inline void +uio_ivshmem_add_notify(FAR struct uio_ivshmem_dev_s *dev, + FAR struct uio_ivshmem_notify_s *notify) { - g_uio_ivshmem_ids, /* PCI id_tables */ - uio_ivshmem_probe, /* Probe function */ - uio_ivshmem_remove, /* Remove function */ -}; + irqstate_t flags; -static int g_uio_ivshmem_idx = 0; + flags = spin_lock_irqsave(&dev->lock); + list_add_tail(&dev->notify_list, ¬ify->node); + spin_unlock_irqrestore(&dev->lock, flags); +} /**************************************************************************** - * Private Functions + * Name: uio_ivshmem_remove_notify ****************************************************************************/ +static inline void +uio_ivshmem_remove_notify(FAR struct uio_ivshmem_dev_s *dev, + FAR struct uio_ivshmem_notify_s *notify) +{ + irqstate_t flags; + + flags = spin_lock_irqsave(&dev->lock); + list_delete(¬ify->node); + spin_unlock_irqrestore(&dev->lock, flags); +} + /**************************************************************************** * Name: uio_ivshmem_open ****************************************************************************/ static int uio_ivshmem_open(FAR struct file *filep) { - UNUSED(filep); + FAR struct uio_ivshmem_dev_s *dev; + irqstate_t flags; + + DEBUGASSERT(filep->f_inode != NULL && filep->f_inode->i_private != NULL); + dev = filep->f_inode->i_private; + + flags = spin_lock_irqsave(&dev->lock); + filep->f_priv = (FAR void *)(uintptr_t)dev->event_count; + spin_unlock_irqrestore(&dev->lock, flags); return 0; } @@ -129,6 +188,19 @@ static int uio_ivshmem_close(FAR struct file *filep) return 0; } +/**************************************************************************** + * Name: uio_ivshmem_notify_read + ****************************************************************************/ + +static void uio_ivshmem_notify_read(FAR struct uio_ivshmem_notify_s *notify, + int32_t newevent) +{ + FAR struct uio_ivshmem_read_s *read = + (FAR struct uio_ivshmem_read_s *)notify; + + nxsem_post(&read->wait); +} + /**************************************************************************** * Name: uio_ivshmem_read ****************************************************************************/ @@ -136,9 +208,49 @@ static int uio_ivshmem_close(FAR struct file *filep) static ssize_t uio_ivshmem_read(FAR struct file *filep, FAR char *buffer, size_t buflen) { - UNUSED(filep); - UNUSED(buffer); - return buflen; + FAR struct uio_ivshmem_dev_s *dev; + struct uio_ivshmem_read_s read; + irqstate_t flags; + int ret = OK; + + if (buflen != sizeof(int32_t)) + { + return -EINVAL; + } + + DEBUGASSERT(filep->f_inode != NULL && filep->f_inode->i_private != NULL); + dev = filep->f_inode->i_private; + + nxsem_init(&read.wait, 0, 0); + read.notify.cb = uio_ivshmem_notify_read; + uio_ivshmem_add_notify(dev, &read.notify); + + for (; ; ) + { + flags = spin_lock_irqsave(&dev->lock); + if (dev->event_count != (int32_t)(uintptr_t)filep->f_priv) + { + filep->f_priv = (FAR void *)(uintptr_t)dev->event_count; + memcpy(buffer, &dev->event_count, sizeof(int32_t)); + spin_unlock_irqrestore(&dev->lock, flags); + ret = sizeof(int32_t); + break; + } + + spin_unlock_irqrestore(&dev->lock, flags); + + if (filep->f_oflags & O_NONBLOCK) + { + ret = -EAGAIN; + break; + } + + nxsem_wait_uninterruptible(&read.wait); + } + + uio_ivshmem_remove_notify(dev, &read.notify); + nxsem_destroy(&read.wait); + return ret; } /**************************************************************************** @@ -148,9 +260,24 @@ static ssize_t uio_ivshmem_read(FAR struct file *filep, FAR char *buffer, static ssize_t uio_ivshmem_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) { - UNUSED(filep); - UNUSED(buffer); - return buflen; + FAR struct uio_ivshmem_dev_s *dev; + int32_t irq_on; + + DEBUGASSERT(filep->f_inode != NULL && filep->f_inode->i_private != NULL); + dev = filep->f_inode->i_private; + + if (buflen != sizeof(int32_t)) + { + return -EINVAL; + } + + irq_on = *(FAR int32_t *)buffer; + if (irq_on != 0 && irq_on != 1) + { + return -EINVAL; + } + + return ivshmem_control_irq(dev->dev, irq_on); } /**************************************************************************** @@ -199,22 +326,23 @@ static int uio_ivshmem_unmap(FAR struct task_group_s *group, static int uio_ivshmem_mmap(FAR struct file *filep, FAR struct mm_map_entry_s *map) { - FAR struct uio_ivshmem_dev_s *priv; + FAR struct uio_ivshmem_dev_s *dev; + size_t shmem_size; + FAR void *shmem; DEBUGASSERT(filep->f_inode != NULL && filep->f_inode->i_private != NULL); + dev = filep->f_inode->i_private; - /* Recover our private data from the struct file instance */ + shmem = ivshmem_get_shmem(dev->dev, &shmem_size); - priv = filep->f_inode->i_private; - - if (map->offset < 0 || map->offset >= priv->shmem_size || - map->length == 0 || map->offset + map->length > priv->shmem_size) + if (map->offset < 0 || map->offset >= shmem_size || + map->length == 0 || map->offset + map->length > shmem_size) { return -EINVAL; } - map->vaddr = (FAR char *)priv->shmem + map->offset; - map->priv.p = priv; + map->vaddr = (FAR char *)shmem + map->offset; + map->priv.p = dev; map->munmap = uio_ivshmem_unmap; /* Not allow mapped memory overlap */ @@ -228,59 +356,140 @@ static int uio_ivshmem_mmap(FAR struct file *filep, } /**************************************************************************** - * Name: uio_ivshmem_probe + * Name: uio_ivshmem_notify_poll ****************************************************************************/ -static int uio_ivshmem_probe(FAR struct pci_device_s *dev) +void uio_ivshmem_notify_poll(FAR struct uio_ivshmem_notify_s *notify, + int32_t newevent) { - FAR struct uio_ivshmem_dev_s *priv; - int ret; + FAR struct uio_ivshmem_poll_s *poll = + (FAR struct uio_ivshmem_poll_s *)notify; - priv = kmm_zalloc(sizeof(*priv)); - if (priv == NULL) + if (newevent != (int32_t)(uintptr_t)*poll->ppriv) { - return -ENOMEM; + poll_notify(&poll->fds, 1, POLLIN | POLLRDNORM); } +} - /* Configure the ivshmem device and get share memory address */ +/**************************************************************************** + * Name: uio_ivshmem_poll + ****************************************************************************/ - ret = pci_enable_device(dev); - if (ret < 0) +static int uio_ivshmem_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup) +{ + FAR struct uio_ivshmem_poll_s *poll; + FAR struct uio_ivshmem_dev_s *dev; + irqstate_t flags; + int i; + + DEBUGASSERT(filep->f_inode != NULL && filep->f_inode->i_private != NULL); + dev = filep->f_inode->i_private; + + if (setup) { - pcierr("ERROR: Enable device failed, ret=%d\n", ret); - goto err_priv; + flags = spin_lock_irqsave(&dev->lock); + + for (i = 0; i < CONFIG_PCI_UIO_IVSHMEM_NPOLLWAITERS; i++) + { + poll = &dev->polls[i]; + if (poll->fds == NULL) + { + /* Bind the poll structure and this slot */ + + poll->fds = fds; + fds->priv = poll; + break; + } + } + + if (i >= CONFIG_PCI_UIO_IVSHMEM_NPOLLWAITERS) + { + spin_unlock_irqrestore(&dev->lock, flags); + return -EBUSY; + } + + if (dev->event_count != (int32_t)(uintptr_t)filep->f_priv) + { + spin_unlock_irqrestore(&dev->lock, flags); + poll_notify(&fds, 1, POLLIN | POLLRDNORM); + } + else + { + spin_unlock_irqrestore(&dev->lock, flags); + } + + poll->notify.cb = uio_ivshmem_notify_poll; + poll->ppriv = &filep->f_priv; + uio_ivshmem_add_notify(dev, &poll->notify); } + else if (fds->priv != NULL) + { + poll = fds->priv; - pci_set_master(dev); + uio_ivshmem_remove_notify(dev, &poll->notify); - priv->shmem = pci_map_bar(dev, UIO_IVSHMEM_SHMEM_BAR); - if (priv->shmem == NULL) + flags = spin_lock_irqsave(&dev->lock); + poll->fds = NULL; + fds->priv = NULL; + spin_unlock_irqrestore(&dev->lock, flags); + } + + return 0; +} + +/**************************************************************************** + * Name: uio_ivshmem_interrupt + ****************************************************************************/ + +static int uio_ivshmem_interrupt(int irq, FAR void *context, FAR void *arg) +{ + FAR struct uio_ivshmem_notify_s *notify; + FAR struct uio_ivshmem_dev_s *dev = arg; + irqstate_t flags; + + flags = spin_lock_irqsave(&dev->lock); + dev->event_count++; + list_for_every_entry(&dev->notify_list, notify, + struct uio_ivshmem_notify_s, node) { - ret = -ENOTSUP; - pcierr("ERROR: Device not support share memory bar\n"); - goto err_master; + notify->cb(notify, dev->event_count); } - priv->shmem_size = pci_resource_len(dev, UIO_IVSHMEM_SHMEM_BAR); + spin_unlock_irqrestore(&dev->lock, flags); + return 0; +} - pciinfo("shmem addr=%p size=%zu\n", priv->shmem, priv->shmem_size); +/**************************************************************************** + * Name: uio_ivshmem_probe + ****************************************************************************/ - snprintf(priv->name, sizeof(priv->name), "/dev/uio%d", g_uio_ivshmem_idx); - ret = register_driver(priv->name, &g_uio_ivshmem_fops, 0666, priv); +static int uio_ivshmem_probe(FAR struct ivshmem_device_s *dev) +{ + FAR struct uio_ivshmem_dev_s *udev = dev_to_udev(dev); + int ret; + + udev->dev = dev; + spin_lock_init(&udev->lock); + list_initialize(&udev->notify_list); + + /* Init the irq and ignore error */ + + ivshmem_attach_irq(dev, uio_ivshmem_interrupt, udev); + ivshmem_control_irq(dev, true); + + snprintf(udev->name, sizeof(udev->name), "/dev/uio%d", udev->drv.id); + ret = register_driver(udev->name, &g_uio_ivshmem_fops, 0666, udev); if (ret < 0) { pcierr("ERROR: Ivshmem register_driver failed, ret=%d\n", ret); - goto err_master; + goto err; } - g_uio_ivshmem_idx++; return ret; -err_master: - pci_clear_master(dev); - pci_disable_device(dev); -err_priv: - kmm_free(priv); +err: + ivshmem_unregister_driver(&udev->drv); return ret; } @@ -288,15 +497,13 @@ static int uio_ivshmem_probe(FAR struct pci_device_s *dev) * Name: uio_ivshmem_remove ****************************************************************************/ -static void uio_ivshmem_remove(FAR struct pci_device_s *dev) +static void uio_ivshmem_remove(FAR struct ivshmem_device_s *dev) { - FAR struct uio_ivshmem_dev_s *priv = dev->priv; + FAR struct uio_ivshmem_dev_s *udev = dev_to_udev(dev); - dev->priv = NULL; - unregister_driver(priv->name); - pci_clear_master(dev); - pci_disable_device(dev); - kmm_free(priv); + unregister_driver(udev->name); + ivshmem_detach_irq(dev); + kmm_free(udev); } /**************************************************************************** @@ -305,6 +512,28 @@ static void uio_ivshmem_remove(FAR struct pci_device_s *dev) int pci_register_uio_ivshmem_driver(void) { - return pci_register_driver(&g_uio_ivshmem_drv); -} + FAR struct uio_ivshmem_dev_s *dev; + FAR char *start = CONFIG_PCI_UIO_IVSHMEM_IDTABLE; + + do + { + dev = kmm_zalloc(sizeof(*dev)); + if (dev == NULL) + { + return -ENOMEM; + } + + dev->drv.id = strtoul(start, &start, 0); + dev->drv.probe = uio_ivshmem_probe; + dev->drv.remove = uio_ivshmem_remove; + if (ivshmem_register_driver(&dev->drv) < 0) + { + kmm_free(dev); + } + + pciinfo("Register ivshmem driver, id=%d\n", dev->drv.id); + } + while (*start++ != '\0'); + return 0; +}