This patch set add memory-mapped-IO support for arm/arm64 archs in virtio-pmd driver. Patch creates ioport-mem device name /dev/igb_ioport. virtio_ethdev_init function to open that device file for once, mappes 4K page_size memory such that all entry in cat /proc/ioport {all PCI_IOBAR entry} mapped for once. For that two ancessor api;s - virtio_map_ioport : Maps all cat /proc/ioport pci iobars. - virtio_set_ioport_addr : manages the each pci_iobar slot as an offset.
Tested for thunderX/arm64 platform, by creating maximum guest kernel supported virtio-net-pci device i.e.. 31 max, then attaching all 32 interface to uio, Verified with tespmd io_fwd application. Signed-off-by: Santosh Shukla <sshukla at mvista.com> Signed-off-by: Rizwan Ansari <ransari at mvista.com> --- drivers/net/virtio/virtio_ethdev.c | 138 ++++++++++++++++++++++++++++- lib/librte_eal/linuxapp/igb_uio/igb_uio.c | 80 ++++++++++++++++- 2 files changed, 214 insertions(+), 4 deletions(-) diff --git a/drivers/net/virtio/virtio_ethdev.c b/drivers/net/virtio/virtio_ethdev.c index 74c00ee..93b8784 100644 --- a/drivers/net/virtio/virtio_ethdev.c +++ b/drivers/net/virtio/virtio_ethdev.c @@ -63,6 +63,30 @@ #include "virtqueue.h" #include "virtio_rxtx.h" +#ifdef RTE_EXEC_ENV_LINUXAPP +/* start address of first pci_iobar slot (user-space virtual-addres) */ +void *ioport_map; +#if defined(RTE_ARCH_ARM) || defined(RTE_ARCH_ARM64) + +#include <sys/mman.h> +#define DEV_NAME "/dev/igb_ioport" + +/* Keeping pci_ioport_size = 4k. + * So maximum mmaped pci_iobar supported = + * (ioport_size/pci_dev->mem_resource[0].len) + * + * note: kernel could allow maximum 32 virtio-net-pci interface, that mean + * maximum 32 PCI_IOBAR(s) where each PCI_IOBAR_LEN=0x20, so virtio_map_ioport() + * func by theory gonna support 4k/0x20 ==> 128 PCI_IOBAR(s), more than + * max-virtio-net-pci interface. + */ +#define PAGE_SIZE 4096 +#define PCI_IOPORT_SIZE PAGE_SIZE +#define PCI_IOPORT_MAX 128 /* 4k / 0x20 */ + +int ioport_map_cnt; +#endif /* ARM, ARM64 */ +#endif /* RTE_EXEC_ENV_LINUXAPP */ static int eth_virtio_dev_init(struct rte_eth_dev *eth_dev); static int eth_virtio_dev_uninit(struct rte_eth_dev *eth_dev); @@ -497,6 +521,18 @@ virtio_dev_close(struct rte_eth_dev *dev) hw->started = 0; virtio_dev_free_mbufs(dev); virtio_free_queues(dev); + +#ifdef RTE_EXEC_ENV_LINUXAPP +#if defined(RTE_ARCH_ARM) || defined(RTE_ARCH_ARM64) + + /* unmap ioport memory */ + ioport_map_cnt--; + if (!ioport_map_cnt) + munmap(ioport_map, PCI_IOPORT_SIZE); + + PMD_INIT_LOG(DEBUG, "unmapping ioport_mem %d\n", ioport_map_cnt); +#endif +#endif } static void @@ -1172,7 +1208,6 @@ static int virtio_resource_init_by_ioports(struct rte_pci_device *pci_dev) sscanf(ptr, "%04hx-%04hx", &start, &end); size = end - start + 1; - break; } } @@ -1256,6 +1291,81 @@ rx_func_get(struct rte_eth_dev *eth_dev) eth_dev->rx_pkt_burst = &virtio_recv_pkts; } +#ifdef RTE_EXEC_ENV_LINUXAPP +#if defined(RTE_ARCH_ARM) || defined(RTE_ARCH_ARM64) + +static int +virtio_map_ioport(void **resource_addr) +{ + int fd; + int ret = 0; + + fd = open(DEV_NAME, O_RDWR); + if (fd < 0) { + PMD_INIT_LOG(ERR, "device file %s open error: %d\n", + DEV_NAME, fd); + ret = -1; + goto out; + } + + ioport_map = mmap(NULL, PCI_IOPORT_SIZE, + PROT_EXEC | PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0); + + if (ioport_map == MAP_FAILED) { + PMD_INIT_LOG(ERR, "mmap: failed to map bar Address=%p\n", + *resource_addr); + ret = -ENOMEM; + goto out1; + } + + PMD_INIT_LOG(INFO, "First pci_iobar mapped at %p\n", ioport_map); + +out1: + close(fd); +out: + return ret; +} + +static int +virtio_set_ioport_addr(void **resource_addr, unsigned long offset) +{ + int ret = 0; + + if (ioport_map_cnt >= PCI_IOPORT_MAX) { + ret = -1; + PMD_INIT_LOG(ERR, + "ioport_map_cnt(%d) greater than" + "PCI_IOPORT_MAX(%d)\n", + ioport_map_cnt, PCI_IOPORT_MAX); + goto out; + } + *resource_addr = (void *)((char *)ioport_map + (ioport_map_cnt)*offset); + ioport_map_cnt++; + + PMD_INIT_LOG(DEBUG, "pci.resource_addr %p ioport_map_cnt %d\n", + *resource_addr, ioport_map_cnt); +out: + return ret; +} +#else /* !ARM, !ARM64 */ +static int +virtio_map_ioport(void *resource_addr) +{ + (void)resource_addr; + return 0; +} + +static int +virtio_set_ioport_addr(void *resource_addr, unsigned long offset) +{ + (void)resource_addr; + (void)offset; + return 0; +} + +#endif /* ARM, ARM64 */ +#endif /* RTE_EXEC_ENV_LINUXAPP */ + /* * This function is based on probe() function in virtio_pci.c * It returns 0 on success. @@ -1294,8 +1404,31 @@ eth_virtio_dev_init(struct rte_eth_dev *eth_dev) if (virtio_resource_init(pci_dev) < 0) return -1; +#ifdef RTE_EXEC_ENV_LINUXAPP + int ret = 0; + + /* Map the all IOBAR entry from /proc/ioport to 4k page_size only once. + * Later virtio_set_ioport_addr() func will update correct bar_addr + * eachk ioport (i.e..pci_dev->mem_resource[0].addr) + */ + if (!ioport_map) { + ret = virtio_map_ioport(&pci_dev->mem_resource[0].addr); + if (ret < 0) + return -1; + } + + ret = virtio_set_ioport_addr(&pci_dev->mem_resource[0].addr, + pci_dev->mem_resource[0].len); + if (ret < 0) + return -1; + + PMD_INIT_LOG(INFO, "ioport_map %p resource_addr %p resource_len :%ld\n", + ioport_map, pci_dev->mem_resource[0].addr, + (unsigned long)pci_dev->mem_resource[0].len); + +#endif hw->use_msix = virtio_has_msix(&pci_dev->addr); - hw->io_base = (uint32_t)(uintptr_t)pci_dev->mem_resource[0].addr; + hw->io_base = (unsigned long)(uintptr_t)pci_dev->mem_resource[0].addr; /* Reset the device although not necessary at startup */ vtpci_reset(hw); @@ -1430,7 +1563,6 @@ eth_virtio_dev_uninit(struct rte_eth_dev *eth_dev) rte_intr_callback_unregister(&pci_dev->intr_handle, virtio_interrupt_handler, eth_dev); - PMD_INIT_LOG(DEBUG, "dev_uninit completed"); return 0; diff --git a/lib/librte_eal/linuxapp/igb_uio/igb_uio.c b/lib/librte_eal/linuxapp/igb_uio/igb_uio.c index f5617d2..b154a44 100644 --- a/lib/librte_eal/linuxapp/igb_uio/igb_uio.c +++ b/lib/librte_eal/linuxapp/igb_uio/igb_uio.c @@ -324,6 +324,61 @@ igbuio_dom0_pci_mmap(struct uio_info *info, struct vm_area_struct *vma) } #endif +#if defined(RTE_ARCH_ARM) || defined(RTE_ARCH_ARM64) +#ifdef CONFIG_HAS_IOPORT_MAP +/* + * mmap driver to map x86-style PCI_IOBAR (i.e..cat /proc/ioport pci-bar-memory) + * from kernel-space virtual address to user-space virtual address. This module + * required for non-x86 archs example arm/arm64, as those archs donot do + * IO_MAP_IO types access, Infact supports memory-mapped-IO. That is because + * arm/arm64 doesn't support direct IO instruction, so the design approach is to + * map `cat /proc/ioport` PCI_IOBAR's kernel-space virtual-addr to user-space + * virtual-addr. Therefore the need for mmap-driver. + */ +#include <linux/fs.h> /* file_operations */ +#include <linux/miscdevice.h> +#include <linux/mm.h> /* VM_IO */ +#include <linux/uaccess.h> +#include <asm/page.h> +#include <linux/sched.h> +#include <linux/pid.h> + +void *__iomem mapped_io; /* ioport addr of `cat /proc/ioport` */ + +static int igb_ioport_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct page *npage; + int ret = 0; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + npage = vmalloc_to_page(mapped_io); + ret = remap_pfn_range(vma, vma->vm_start, + page_to_pfn(npage), + vma->vm_end - vma->vm_start, + vma->vm_page_prot); + if (ret) { + pr_info("Error: Failed to remap pfn=%lu error=%d\n", + page_to_pfn(npage), ret); + } + return 0; +} + +static const struct file_operations igb_ioport_fops = { + .mmap = igb_ioport_mmap, +}; + +static struct miscdevice igb_ioport_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "igb_ioport", + .fops = &igb_ioport_fops +}; +#else /* !CONFIG_HAS_IOPORT_MAP */ + +#error "CONFIG_HAS_IOPORT_MAP not supported for $RTE_ARCH" + +#endif /* CONFIG_HAS_IOPORT_MAP */ +#endif /* RTE_ARCH_ARM, RTE_ARCH_ARM64 */ + /* Remap pci resources described by bar #pci_bar in uio resource n. */ static int igbuio_pci_setup_iomem(struct pci_dev *dev, struct uio_info *info, @@ -365,11 +420,22 @@ igbuio_pci_setup_ioport(struct pci_dev *dev, struct uio_info *info, if (addr == 0 || len == 0) return -EINVAL; +#if defined(RTE_ARCH_ARM) || defined(RTE_ARCH_ARM64) + /* + * TODO: KNI/procfs:: porttype entry need to be added for ARM/ARM64, + * for below mmap driver; + * */ + mapped_io = ioport_map(addr, 0); + info->port[n].name = name; + info->port[n].start = (unsigned long)(uintptr_t)mapped_io; + info->port[n].size = len; + info->port[n].porttype = UIO_PORT_X86; +#else info->port[n].name = name; info->port[n].start = addr; info->port[n].size = len; info->port[n].porttype = UIO_PORT_X86; - +#endif return 0; } @@ -615,6 +681,15 @@ igbuio_pci_init_module(void) { int ret; +#if defined(RTE_ARCH_ARM) || defined(RTE_ARCH_ARM64) + ret = misc_register(&igb_ioport_dev); + if (ret < 0) { + pr_info("Error: failed to register ioport map driver (%d)\n", + ret); + return ret; + } +#endif + ret = igbuio_config_intr_mode(intr_mode); if (ret < 0) return ret; @@ -625,6 +700,9 @@ igbuio_pci_init_module(void) static void __exit igbuio_pci_exit_module(void) { +#if defined(RTE_ARCH_ARM) || defined(RTE_ARCH_ARM64) + misc_deregister(&igb_ioport_dev); +#endif pci_unregister_driver(&igbuio_pci_driver); } -- 1.7.9.5