From patchwork Thu Mar 25 06:09:36 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cam Macdonell X-Patchwork-Id: 48498 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 0C7ECB7C98 for ; Thu, 25 Mar 2010 17:21:44 +1100 (EST) Received: from localhost ([127.0.0.1]:50861 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1NugQs-0001TV-3E for incoming@patchwork.ozlabs.org; Thu, 25 Mar 2010 02:20:42 -0400 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1NugJd-0008Sa-RT for qemu-devel@nongnu.org; Thu, 25 Mar 2010 02:13:13 -0400 Received: from [140.186.70.92] (port=47864 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1NugJc-0008RJ-4W for qemu-devel@nongnu.org; Thu, 25 Mar 2010 02:13:13 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.69) (envelope-from ) id 1NugJZ-0003sa-Lc for qemu-devel@nongnu.org; Thu, 25 Mar 2010 02:13:12 -0400 Received: from fleet.cs.ualberta.ca ([129.128.22.22]:54457) by eggs.gnu.org with esmtp (Exim 4.69) (envelope-from ) id 1NugJZ-0003sP-9S for qemu-devel@nongnu.org; Thu, 25 Mar 2010 02:13:09 -0400 Received: from localhost.localdomain (botha10.cs.ualberta.ca [129.128.22.140]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by smtp-auth.cs.ualberta.ca (Postfix) with ESMTP id 06C4E28013; Thu, 25 Mar 2010 00:13:09 -0600 (MDT) From: Cam Macdonell To: kvm@vger.kernel.org Date: Thu, 25 Mar 2010 00:09:36 -0600 Message-Id: <1269497376-21903-1-git-send-email-cam@cs.ualberta.ca> X-Mailer: git-send-email 1.6.6.1 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6, seldom 2.4 (older, 4) Cc: Cam Macdonell , qemu-devel@nongnu.org Subject: [Qemu-devel] [PATCH v3 1/1] Shared memory uio_pci driver X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This patch adds a driver for my shared memory PCI device using the uio_pci interface. The driver has three memory regions. The first memory region is for device registers for sending interrupts. The second BAR is for receiving MSI-X interrupts and the third memory region maps the shared memory. The device only exports the first and third memory regions to userspace. This driver supports MSI-X and regular pin interrupts. Currently, the number of MSI vectors is set to 4 which could be increased, but the driver will work with fewer vectors. If MSI is not available, then regular interrupts will be used. --- drivers/uio/Kconfig | 8 ++ drivers/uio/Makefile | 1 + drivers/uio/uio_ivshmem.c | 235 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 244 insertions(+), 0 deletions(-) create mode 100644 drivers/uio/uio_ivshmem.c diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig index 1da73ec..b92cded 100644 --- a/drivers/uio/Kconfig +++ b/drivers/uio/Kconfig @@ -74,6 +74,14 @@ config UIO_SERCOS3 If you compile this as a module, it will be called uio_sercos3. +config UIO_IVSHMEM + tristate "KVM shared memory PCI driver" + default n + help + Userspace I/O interface for the KVM shared memory device. This + driver will make available two memory regions, the first is + registers and the second is a region for sharing between VMs. + config UIO_PCI_GENERIC tristate "Generic driver for PCI 2.3 and PCI Express cards" depends on PCI diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile index 18fd818..25c1ca5 100644 --- a/drivers/uio/Makefile +++ b/drivers/uio/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_UIO_AEC) += uio_aec.o obj-$(CONFIG_UIO_SERCOS3) += uio_sercos3.o obj-$(CONFIG_UIO_PCI_GENERIC) += uio_pci_generic.o obj-$(CONFIG_UIO_NETX) += uio_netx.o +obj-$(CONFIG_UIO_IVSHMEM) += uio_ivshmem.o diff --git a/drivers/uio/uio_ivshmem.c b/drivers/uio/uio_ivshmem.c new file mode 100644 index 0000000..607435b --- /dev/null +++ b/drivers/uio/uio_ivshmem.c @@ -0,0 +1,235 @@ +/* + * UIO IVShmem Driver + * + * (C) 2009 Cam Macdonell + * based on Hilscher CIF card driver (C) 2007 Hans J. Koch + * + * Licensed under GPL version 2 only. + * + */ + +#include +#include +#include +#include + +#include + +#define IntrStatus 0x04 +#define IntrMask 0x00 + +struct ivshmem_info { + struct uio_info *uio; + struct pci_dev *dev; + char (*msix_names)[256]; + struct msix_entry *msix_entries; + int nvectors; +}; + +static irqreturn_t ivshmem_handler(int irq, struct uio_info *dev_info) +{ + + void __iomem *plx_intscr = dev_info->mem[0].internal_addr + + IntrStatus; + u32 val; + + val = readl(plx_intscr); + if (val == 0) + return IRQ_NONE; + + printk(KERN_INFO "Regular interrupt (val = %d)\n", val); + return IRQ_HANDLED; +} + +static irqreturn_t ivshmem_msix_handler(int irq, void *opaque) +{ + + struct uio_info * dev_info = (struct uio_info *) opaque; + + /* we have to do this explicitly when using MSI-X */ + uio_event_notify(dev_info); + printk(KERN_INFO "MSI-X interrupt (%d)\n", irq); + return IRQ_HANDLED; + +} + +static int request_msix_vectors(struct ivshmem_info *ivs_info, int nvectors) +{ + int i, err; + const char *name = "ivshmem"; + + printk(KERN_INFO "devname is %s\n", name); + ivs_info->nvectors = nvectors; + + + ivs_info->msix_entries = kmalloc(nvectors * sizeof *ivs_info->msix_entries, + GFP_KERNEL); + ivs_info->msix_names = kmalloc(nvectors * sizeof *ivs_info->msix_names, + GFP_KERNEL); + + for (i = 0; i < nvectors; ++i) + ivs_info->msix_entries[i].entry = i; + + err = pci_enable_msix(ivs_info->dev, ivs_info->msix_entries, + ivs_info->nvectors); + if (err > 0) { + ivs_info->nvectors = err; /* msi-x positive error code + returns the number available*/ + err = pci_enable_msix(ivs_info->dev, ivs_info->msix_entries, + ivs_info->nvectors); + if (err > 0) { + printk(KERN_INFO "no MSI (%d). Back to INTx.\n", err); + return -ENOSPC; + } + } + + printk(KERN_INFO "err is %d\n", err); + if (err) return err; + + for (i = 0; i < ivs_info->nvectors; i++) { + + snprintf(ivs_info->msix_names[i], sizeof *ivs_info->msix_names, + "%s-config", name); + + ivs_info->msix_entries[i].entry = i; + err = request_irq(ivs_info->msix_entries[i].vector, + ivshmem_msix_handler, 0, + ivs_info->msix_names[i], ivs_info->uio); + + if (err) { + return -ENOSPC; + } + } + + return 0; +} + +static int __devinit ivshmem_pci_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + struct uio_info *info; + struct ivshmem_info * ivshmem_info; + int nvectors = 4; + + info = kzalloc(sizeof(struct uio_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + ivshmem_info = kzalloc(sizeof(struct ivshmem_info), GFP_KERNEL); + if (!ivshmem_info) { + kfree(info); + return -ENOMEM; + } + + if (pci_enable_device(dev)) + goto out_free; + + if (pci_request_regions(dev, "ivshmem")) + goto out_disable; + + info->mem[0].addr = pci_resource_start(dev, 0); + if (!info->mem[0].addr) + goto out_release; + + info->mem[0].size = pci_resource_len(dev, 0); + info->mem[0].internal_addr = pci_ioremap_bar(dev, 0); + if (!info->mem[0].internal_addr) { + printk(KERN_INFO "got a null"); + goto out_release; + } + + info->mem[0].memtype = UIO_MEM_PHYS; + + info->mem[1].addr = pci_resource_start(dev, 2); + if (!info->mem[1].addr) + goto out_unmap; + info->mem[1].internal_addr = pci_ioremap_bar(dev, 2); + if (!info->mem[1].internal_addr) + goto out_unmap; + + info->mem[1].size = pci_resource_len(dev, 2); + info->mem[1].memtype = UIO_MEM_PHYS; + + ivshmem_info->uio = info; + ivshmem_info->dev = dev; + + if (request_msix_vectors(ivshmem_info, nvectors) != 0) { + printk(KERN_INFO "regular IRQs\n"); + info->irq = dev->irq; + info->irq_flags = IRQF_SHARED; + info->handler = ivshmem_handler; + writel(0xffffffff, info->mem[0].internal_addr + IntrMask); + } else { + printk(KERN_INFO "MSI-X enabled\n"); + info->irq = -1; + } + + info->name = "ivshmem"; + info->version = "0.0.1"; + + if (uio_register_device(&dev->dev, info)) + goto out_unmap2; + + pci_set_drvdata(dev, info); + + + return 0; +out_unmap2: + iounmap(info->mem[2].internal_addr); +out_unmap: + iounmap(info->mem[0].internal_addr); +out_release: + pci_release_regions(dev); +out_disable: + pci_disable_device(dev); +out_free: + kfree (info); + return -ENODEV; +} + +static void ivshmem_pci_remove(struct pci_dev *dev) +{ + struct uio_info *info = pci_get_drvdata(dev); + + uio_unregister_device(info); + pci_release_regions(dev); + pci_disable_device(dev); + pci_set_drvdata(dev, NULL); + iounmap(info->mem[0].internal_addr); + + kfree (info); +} + +static struct pci_device_id ivshmem_pci_ids[] __devinitdata = { + { + .vendor = 0x1af4, + .device = 0x1110, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { 0, } +}; + +static struct pci_driver ivshmem_pci_driver = { + .name = "uio_ivshmem", + .id_table = ivshmem_pci_ids, + .probe = ivshmem_pci_probe, + .remove = ivshmem_pci_remove, +}; + +static int __init ivshmem_init_module(void) +{ + return pci_register_driver(&ivshmem_pci_driver); +} + +static void __exit ivshmem_exit_module(void) +{ + pci_unregister_driver(&ivshmem_pci_driver); +} + +module_init(ivshmem_init_module); +module_exit(ivshmem_exit_module); + +MODULE_DEVICE_TABLE(pci, ivshmem_pci_ids); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Cam Macdonell");