[dpdk-dev] [PATCH v3 2/4] nfp-uio: new uio driver for netronome nfp6000 card

Alejandro.Lucero alejandro.lucero at netronome.com
Fri Oct 16 12:45:22 CEST 2015


From: "Alejandro.Lucero" <alejandro.lucero at netronome.com>

This patch adds a new UIO kernel driver for supporting PCI VFs with Netronome
nfp6000 card. Future PCI PF support will be based on changes to this module.

Signed-off-by: Alejandro.Lucero <alejandro.lucero at netronome.com>
Signed-off-by: Rolf.Neugebauer <rolf.neugebauer at netronome.com>
---
 lib/librte_eal/common/include/rte_pci.h   |    1 +
 lib/librte_eal/linuxapp/Makefile          |    3 +
 lib/librte_eal/linuxapp/eal/eal_pci.c     |    4 +
 lib/librte_eal/linuxapp/eal/eal_pci_uio.c |    2 +-
 lib/librte_eal/linuxapp/nfp_uio/Makefile  |   53 +++
 lib/librte_eal/linuxapp/nfp_uio/nfp_uio.c |  497 +++++++++++++++++++++++++++++
 lib/librte_ether/rte_ethdev.c             |    1 +
 7 files changed, 560 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_eal/linuxapp/nfp_uio/Makefile
 create mode 100644 lib/librte_eal/linuxapp/nfp_uio/nfp_uio.c

diff --git a/lib/librte_eal/common/include/rte_pci.h b/lib/librte_eal/common/include/rte_pci.h
index 83e3c28..89baaf6 100644
--- a/lib/librte_eal/common/include/rte_pci.h
+++ b/lib/librte_eal/common/include/rte_pci.h
@@ -146,6 +146,7 @@ struct rte_devargs;
 enum rte_kernel_driver {
 	RTE_KDRV_UNKNOWN = 0,
 	RTE_KDRV_IGB_UIO,
+	RTE_KDRV_NFP_UIO,
 	RTE_KDRV_VFIO,
 	RTE_KDRV_UIO_GENERIC,
 	RTE_KDRV_NIC_UIO,
diff --git a/lib/librte_eal/linuxapp/Makefile b/lib/librte_eal/linuxapp/Makefile
index d9c5233..f36dc4b 100644
--- a/lib/librte_eal/linuxapp/Makefile
+++ b/lib/librte_eal/linuxapp/Makefile
@@ -34,6 +34,9 @@ include $(RTE_SDK)/mk/rte.vars.mk
 ifeq ($(CONFIG_RTE_EAL_IGB_UIO),y)
 DIRS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += igb_uio
 endif
+ifeq ($(CONFIG_RTE_EAL_NFP_UIO),y)
+DIRS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += nfp_uio
+endif
 DIRS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal
 ifeq ($(CONFIG_RTE_KNI_KMOD),y)
 DIRS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += kni
diff --git a/lib/librte_eal/linuxapp/eal/eal_pci.c b/lib/librte_eal/linuxapp/eal/eal_pci.c
index bc5b5be..19a93fe 100644
--- a/lib/librte_eal/linuxapp/eal/eal_pci.c
+++ b/lib/librte_eal/linuxapp/eal/eal_pci.c
@@ -137,6 +137,7 @@ pci_map_device(struct rte_pci_device *dev)
 #endif
 		break;
 	case RTE_KDRV_IGB_UIO:
+	case RTE_KDRV_NFP_UIO:
 	case RTE_KDRV_UIO_GENERIC:
 		/* map resources for devices that use uio */
 		ret = pci_uio_map_resource(dev);
@@ -161,6 +162,7 @@ pci_unmap_device(struct rte_pci_device *dev)
 		RTE_LOG(ERR, EAL, "Hotplug doesn't support vfio yet\n");
 		break;
 	case RTE_KDRV_IGB_UIO:
+	case RTE_KDRV_NFP_UIO:
 	case RTE_KDRV_UIO_GENERIC:
 		/* unmap resources for devices that use uio */
 		pci_uio_unmap_resource(dev);
@@ -357,6 +359,8 @@ pci_scan_one(const char *dirname, uint16_t domain, uint8_t bus,
 			dev->kdrv = RTE_KDRV_VFIO;
 		else if (!strcmp(driver, "igb_uio"))
 			dev->kdrv = RTE_KDRV_IGB_UIO;
+		else if (!strcmp(driver, "nfp_uio"))
+			dev->kdrv = RTE_KDRV_NFP_UIO;
 		else if (!strcmp(driver, "uio_pci_generic"))
 			dev->kdrv = RTE_KDRV_UIO_GENERIC;
 		else
diff --git a/lib/librte_eal/linuxapp/eal/eal_pci_uio.c b/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
index ac50e13..29ec9cb 100644
--- a/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
+++ b/lib/librte_eal/linuxapp/eal/eal_pci_uio.c
@@ -270,7 +270,7 @@ pci_uio_alloc_resource(struct rte_pci_device *dev,
 		goto error;
 	}
 
-	if (dev->kdrv == RTE_KDRV_IGB_UIO)
+	if (dev->kdrv == RTE_KDRV_IGB_UIO || dev->kdrv == RTE_KDRV_NFP_UIO)
 		dev->intr_handle.type = RTE_INTR_HANDLE_UIO;
 	else {
 		dev->intr_handle.type = RTE_INTR_HANDLE_UIO_INTX;
diff --git a/lib/librte_eal/linuxapp/nfp_uio/Makefile b/lib/librte_eal/linuxapp/nfp_uio/Makefile
new file mode 100644
index 0000000..b9e2f0a
--- /dev/null
+++ b/lib/librte_eal/linuxapp/nfp_uio/Makefile
@@ -0,0 +1,53 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2014-2015 Netronome. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+#
+# module name and path
+#
+MODULE = nfp_uio
+MODULE_PATH = drivers/net/nfp_uio
+
+#
+# CFLAGS
+#
+MODULE_CFLAGS += -I$(SRCDIR) --param max-inline-insns-single=100
+MODULE_CFLAGS += -I$(RTE_OUTPUT)/include
+MODULE_CFLAGS += -Winline -Wall -Werror
+MODULE_CFLAGS += -include $(RTE_OUTPUT)/include/rte_config.h
+
+#
+# all source are stored in SRCS-y
+#
+SRCS-y := nfp_uio.c
+
+include $(RTE_SDK)/mk/rte.module.mk
diff --git a/lib/librte_eal/linuxapp/nfp_uio/nfp_uio.c b/lib/librte_eal/linuxapp/nfp_uio/nfp_uio.c
new file mode 100644
index 0000000..98192a5
--- /dev/null
+++ b/lib/librte_eal/linuxapp/nfp_uio/nfp_uio.c
@@ -0,0 +1,497 @@
+/*
+ * Copyright (c) 2014, 2015 Netronome Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ *  contributors may be used to endorse or promote products derived from this
+ *  software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Netronome DPDK uio kernel module
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/uio_driver.h>
+#include <linux/io.h>
+#include <linux/msi.h>
+#include <linux/version.h>
+
+#ifndef PCI_MSIX_ENTRY_SIZE
+#define PCI_MSIX_ENTRY_SIZE             16
+#define PCI_MSIX_ENTRY_LOWER_ADDR       0
+#define PCI_MSIX_ENTRY_UPPER_ADDR       4
+#define PCI_MSIX_ENTRY_DATA             8
+#define PCI_MSIX_ENTRY_VECTOR_CTRL      12
+#define PCI_MSIX_ENTRY_CTRL_MASKBIT     1
+#endif
+
+/* Ideally we should support two types of interrupts:
+ *
+ * - Link Status Change Interrupt
+ * - Exception Interrupt
+ *
+ * But the uio Linux kernel interface just admits one interrupt per uio device.
+ */
+#define NFP_NUM_MSI_VECTORS 1
+
+/*
+ * A structure describing the private information for a uio device.
+ */
+struct nfp_uio_pci_dev {
+	struct uio_info info;
+	struct pci_dev *pdev;
+	/* spinlock for accessing PCI config space or msix
+	 * data in multi tasks/isr
+	 */
+	spinlock_t lock;
+
+	/* pointer to the msix vectors to be allocated later */
+	struct msix_entry msix_entries[NFP_NUM_MSI_VECTORS];
+};
+
+#define PCI_VENDOR_ID_NETRONOME		0x19ee
+#define PCI_DEVICE_NFP6000_VF_NIC	0x6003
+
+#define RTE_PCI_DEV_ID_DECL_NETRO(vend, dev) {PCI_DEVICE(vend, dev)},
+
+/* PCI device id table */
+static struct pci_device_id nfp_uio_pci_ids[] = {
+RTE_PCI_DEV_ID_DECL_NETRO(PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_NFP6000_VF_NIC)
+{ 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, nfp_uio_pci_ids);
+
+static inline struct nfp_uio_pci_dev *
+nfp_uio_get_uio_pci_dev(struct uio_info *info)
+{
+	return container_of(info, struct nfp_uio_pci_dev, info);
+}
+
+static inline int
+pci_lock(struct pci_dev *pdev)
+{
+	/* Some function names changes between 3.2.0 and 3.3.0... */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+	pci_block_user_cfg_access(pdev);
+	return 1;
+#else
+	return pci_cfg_access_trylock(pdev);
+#endif
+}
+
+static inline void
+pci_unlock(struct pci_dev *pdev)
+{
+	/* Some function names changes between 3.2.0 and 3.3.0... */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+	pci_unblock_user_cfg_access(pdev);
+#else
+	pci_cfg_access_unlock(pdev);
+#endif
+}
+
+/*
+ * It masks the msix on/off of generating MSI-X messages.
+ */
+static int
+nfp_uio_msix_mask_irq(struct msi_desc *desc, int32_t state)
+{
+	u32 mask_bits = desc->masked;
+	unsigned offset = desc->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +
+						PCI_MSIX_ENTRY_VECTOR_CTRL;
+
+	if (state != 0)
+		mask_bits &= ~PCI_MSIX_ENTRY_CTRL_MASKBIT;
+	else
+		mask_bits |= PCI_MSIX_ENTRY_CTRL_MASKBIT;
+
+	if (mask_bits != desc->masked) {
+		writel(mask_bits, desc->mask_base + offset);
+		readl(desc->mask_base);
+		desc->masked = mask_bits;
+	}
+
+	return 0;
+}
+
+/**
+ * This function sets/clears the masks for generating LSC interrupts.
+ *
+ * @param info
+ *   The pointer to struct uio_info.
+ * @param on
+ *   The on/off flag of masking LSC.
+ * @return
+ *   -On success, zero value.
+ *   -On failure, a negative value.
+ */
+static int
+nfp_uio_set_interrupt_mask(struct nfp_uio_pci_dev *udev, int32_t state)
+{
+	struct pci_dev *pdev = udev->pdev;
+	struct msi_desc *desc;
+
+	/* TODO: Should we change this based on if the firmware advertises
+			 NFP_NET_CFG_CTRL_MSIXAUTO? */
+
+	list_for_each_entry(desc, &pdev->msi_list, list) {
+		nfp_uio_msix_mask_irq(desc, state);
+	}
+	return 0;
+}
+
+/**
+ * This is the irqcontrol callback to be registered to uio_info.
+ * It can be used to disable/enable interrupt from user space processes.
+ *
+ * @param info
+ *  pointer to uio_info.
+ * @param irq_state
+ *  state value. 1 to enable interrupt, 0 to disable interrupt.
+ *
+ * @return
+ *  - On success, 0.
+ *  - On failure, a negative value.
+ */
+static int
+nfp_uio_pci_irqcontrol(struct uio_info *info, s32 irq_state)
+{
+	unsigned long flags;
+	struct nfp_uio_pci_dev *udev = nfp_uio_get_uio_pci_dev(info);
+	struct pci_dev *pdev = udev->pdev;
+
+	spin_lock_irqsave(&udev->lock, flags);
+	if (!pci_lock(pdev)) {
+		spin_unlock_irqrestore(&udev->lock, flags);
+		return -1;
+	}
+
+	nfp_uio_set_interrupt_mask(udev, irq_state);
+
+	pci_unlock(pdev);
+	spin_unlock_irqrestore(&udev->lock, flags);
+
+	return 0;
+}
+
+/**
+ * This is interrupt handler which will check if the interrupt is for the right
+   device. If yes, disable it here and will be enable later.
+ */
+static irqreturn_t
+nfp_uio_pci_irqhandler(int irq, struct uio_info *info)
+{
+	irqreturn_t ret = IRQ_NONE;
+	unsigned long flags;
+	struct nfp_uio_pci_dev *udev = nfp_uio_get_uio_pci_dev(info);
+	struct pci_dev *pdev = udev->pdev;
+
+	spin_lock_irqsave(&udev->lock, flags);
+	/* block userspace PCI config reads/writes */
+	if (!pci_lock(pdev))
+		goto spin_unlock;
+
+	ret = IRQ_HANDLED;
+
+	/* unblock userspace PCI config reads/writes */
+	pci_unlock(pdev);
+spin_unlock:
+	spin_unlock_irqrestore(&udev->lock, flags);
+	dev_info(&pdev->dev, "irq 0x%x %s\n", irq,
+		 (ret == IRQ_HANDLED) ? "handled" : "not handled");
+
+	return ret;
+}
+
+/* Remap pci resources described by bar #pci_bar in uio resource n. */
+static int
+nfp_uio_pci_setup_iomem(struct pci_dev *dev, struct uio_info *info,
+			int n, int pci_bar, const char *name)
+{
+	unsigned long addr, len;
+	void *internal_addr;
+
+	if (ARRAY_SIZE(info->mem) <= n)
+		return -EINVAL;
+
+	addr = pci_resource_start(dev, pci_bar);
+	len = pci_resource_len(dev, pci_bar);
+	if (addr == 0 || len == 0)
+		return -1;
+	internal_addr = ioremap(addr, len);
+	if (!internal_addr)
+		return -1;
+	info->mem[n].name = name;
+	info->mem[n].addr = addr;
+	info->mem[n].internal_addr = internal_addr;
+	info->mem[n].size = len;
+	info->mem[n].memtype = UIO_MEM_PHYS;
+	return 0;
+}
+
+/* Get pci port io resources described by bar #pci_bar in uio resource n. */
+static int
+nfp_uio_pci_setup_ioport(struct pci_dev *dev, struct uio_info *info,
+			 int n, int pci_bar, const char *name)
+{
+	unsigned long addr, len;
+
+	if (ARRAY_SIZE(info->port) <= n)
+		return -EINVAL;
+
+	addr = pci_resource_start(dev, pci_bar);
+	len = pci_resource_len(dev, pci_bar);
+	if (addr == 0 || len == 0)
+		return -1;
+
+	info->port[n].name = name;
+	info->port[n].start = addr;
+	info->port[n].size = len;
+	info->port[n].porttype = UIO_PORT_X86;
+
+	return 0;
+}
+
+/* Unmap previously ioremap'd resources */
+static void
+nfp_uio_pci_release_iomem(struct uio_info *info)
+{
+	int i;
+
+	for (i = 0; i < MAX_UIO_MAPS; i++) {
+		if (info->mem[i].internal_addr)
+			iounmap(info->mem[i].internal_addr);
+	}
+}
+
+static int
+nfp_uio_setup_bars(struct pci_dev *dev, struct uio_info *info)
+{
+	int i, iom, iop, ret;
+	unsigned long flags;
+	static const char *bar_names[PCI_STD_RESOURCE_END + 1]  = {
+		"BAR0",
+		"BAR1",
+		"BAR2",
+		"BAR3",
+		"BAR4",
+		"BAR5",
+	};
+
+	iom = 0;
+	iop = 0;
+
+	for (i = 0; i != ARRAY_SIZE(bar_names); i++) {
+		if (pci_resource_len(dev, i) == 0 ||
+		    pci_resource_start(dev, i) == 0)
+			continue;
+
+		flags = pci_resource_flags(dev, i);
+		if (flags & IORESOURCE_MEM) {
+			ret = nfp_uio_pci_setup_iomem(dev, info, iom, i,
+						      bar_names[i]);
+			if (ret != 0)
+				return ret;
+			iom++;
+		} else if (flags & IORESOURCE_IO) {
+			ret = nfp_uio_pci_setup_ioport(dev, info, iop, i,
+						       bar_names[i]);
+			if (ret != 0)
+				return ret;
+			iop++;
+		}
+	}
+
+	return (iom != 0) ? ret : -ENOENT;
+}
+
+/* Configuring interrupt. First try MSI-X, then MSI. */
+static void
+init_interrupt(struct nfp_uio_pci_dev *udev)
+{
+	int vector;
+
+	for (vector = 0; vector < NFP_NUM_MSI_VECTORS; vector++)
+		udev->msix_entries[vector].entry = vector;
+
+	if (pci_enable_msix(udev->pdev, udev->msix_entries,
+			    NFP_NUM_MSI_VECTORS) == 0) {
+		udev->info.irq_flags = 0;
+		udev->info.irq = udev->msix_entries[0].vector;
+		dev_info(&udev->pdev->dev, "%s configured with MSI-X\n",
+			 udev->info.name);
+	} else
+		dev_info(&udev->pdev->dev, "%s MSI-X initialization error\n",
+			 udev->info.name);
+}
+
+static int
+nfp_uio_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	struct nfp_uio_pci_dev *udev;
+	void *map_addr;
+	dma_addr_t map_dma_addr;
+
+	udev = kzalloc(sizeof(*udev), GFP_KERNEL);
+	if (!udev)
+		return -ENOMEM;
+
+	/*
+	 * enable device: ask low-level code to enable I/O and
+	 * memory
+	 */
+	if (pci_enable_device(dev)) {
+		dev_err(&dev->dev, "Cannot enable PCI device\n");
+		goto fail_free;
+	}
+
+	/*
+	 * reserve device's PCI memory regions for use by this
+	 * module
+	 */
+	if (pci_request_regions(dev, "nfp_uio")) {
+		dev_err(&dev->dev, "Cannot request regions\n");
+		goto fail_disable;
+	}
+
+	/* enable bus mastering on the device */
+	pci_set_master(dev);
+
+	/* remap IO memory */
+	if (nfp_uio_setup_bars(dev, &udev->info))
+		goto fail_release_iomem;
+
+	/* set 40-bit DMA mask */
+	if (pci_set_dma_mask(dev,  DMA_BIT_MASK(40))) {
+		dev_err(&dev->dev, "Cannot set DMA mask\n");
+		goto fail_release_iomem;
+	} else if (pci_set_consistent_dma_mask(dev, DMA_BIT_MASK(40))) {
+		dev_err(&dev->dev, "Cannot set consistent DMA mask\n");
+		goto fail_release_iomem;
+	}
+
+	/* fill uio infos */
+	udev->info.name = "Netronome NFP UIO";
+	udev->info.version = "0.1";
+	udev->info.handler = nfp_uio_pci_irqhandler;
+	udev->info.irqcontrol = nfp_uio_pci_irqcontrol;
+	udev->info.priv = udev;
+	udev->pdev = dev;
+	spin_lock_init(&udev->lock);
+
+	init_interrupt(udev);
+
+	pci_set_drvdata(dev, &udev->info);
+	nfp_uio_pci_irqcontrol(&udev->info, 0);
+
+	/* register uio driver */
+	if (uio_register_device(&dev->dev, &udev->info))
+		goto fail_release_iomem;
+
+	dev_info(&dev->dev, "uio device registered with irq %lx\n",
+		 udev->info.irq);
+
+	/* When binding drivers to devices, some old kernels do not
+	 * link devices to iommu identity mapping if iommu=pt is used.
+	 *
+	 * This is not a problem if the driver does later some call to
+	 * the DMA API because the mapping can be done then. But DPDK
+	 * apps do not use that DMA API at all.
+	 *
+	 * Doing a harmless dma mapping for attaching the device to
+	 * the iommu identity mapping
+	 */
+
+	map_addr = dma_zalloc_coherent(&dev->dev, 1024,
+				       &map_dma_addr, GFP_KERNEL);
+
+	pr_info("nfp_uio: mapping 1K dma=%#llx host=%p\n",
+		(unsigned long long)map_dma_addr, map_addr);
+
+	dma_free_coherent(&dev->dev, 1024, map_addr, map_dma_addr);
+
+	pr_info("nfp_uio: unmapping 1K dma=%#llx host=%p\n",
+		(unsigned long long)map_dma_addr, map_addr);
+
+	return 0;
+
+fail_release_iomem:
+	nfp_uio_pci_release_iomem(&udev->info);
+	pci_disable_msix(udev->pdev);
+	pci_release_regions(dev);
+fail_disable:
+	pci_disable_device(dev);
+fail_free:
+	kfree(udev);
+
+	return -ENODEV;
+}
+
+static void
+nfp_uio_pci_remove(struct pci_dev *dev)
+{
+	struct uio_info *info = pci_get_drvdata(dev);
+
+	BUG_ON(!info);
+	BUG_ON(!info->priv);
+
+	uio_unregister_device(info);
+	nfp_uio_pci_release_iomem(info);
+	pci_disable_msix(dev);
+	pci_release_regions(dev);
+	pci_disable_device(dev);
+	pci_set_drvdata(dev, NULL);
+	kfree(info);
+}
+
+static struct pci_driver nfp_uio_pci_driver = {
+	.name = "nfp_uio",
+	.id_table = nfp_uio_pci_ids,
+	.probe = nfp_uio_pci_probe,
+	.remove = nfp_uio_pci_remove,
+};
+
+static int __init
+nfp_uio_pci_init_module(void)
+{
+	return pci_register_driver(&nfp_uio_pci_driver);
+}
+
+static void __exit
+nfp_uio_pci_exit_module(void)
+{
+	pci_unregister_driver(&nfp_uio_pci_driver);
+}
+
+module_init(nfp_uio_pci_init_module);
+module_exit(nfp_uio_pci_exit_module);
+
+MODULE_DESCRIPTION("UIO driver for Netronome NFP PCI cards");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Netronome Systems <support at netronome.com>");
diff --git a/lib/librte_ether/rte_ethdev.c b/lib/librte_ether/rte_ethdev.c
index f593f6e..a84bc63 100644
--- a/lib/librte_ether/rte_ethdev.c
+++ b/lib/librte_ether/rte_ethdev.c
@@ -513,6 +513,7 @@ rte_eth_dev_is_detachable(uint8_t port_id)
 	if (rte_eth_devices[port_id].dev_type == RTE_ETH_DEV_PCI) {
 		switch (rte_eth_devices[port_id].pci_dev->kdrv) {
 		case RTE_KDRV_IGB_UIO:
+		case RTE_KDRV_NFP_UIO:
 		case RTE_KDRV_UIO_GENERIC:
 		case RTE_KDRV_NIC_UIO:
 			break;
-- 
1.7.9.5



More information about the dev mailing list