From patchwork Thu Apr 15 20:51:34 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tom Lyon X-Patchwork-Id: 92834 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.3/8.14.3) with ESMTP id o3FKsqCQ005905 for ; Thu, 15 Apr 2010 20:54:53 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756809Ab0DOUy2 (ORCPT ); Thu, 15 Apr 2010 16:54:28 -0400 Received: from sj-iport-4.cisco.com ([171.68.10.86]:6498 "EHLO sj-iport-4.cisco.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756795Ab0DOUy1 (ORCPT ); Thu, 15 Apr 2010 16:54:27 -0400 Authentication-Results: sj-iport-4.cisco.com; dkim=neutral (message not signed) header.i=none X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: Ah8MAJsZx0tAaHte/2dsb2JhbACbbXEcAQGkepoohQ4Egyk X-IronPort-AV: E=Sophos;i="4.52,214,1270425600"; d="scan'208";a="115977610" Received: from hkg-core-1.cisco.com ([64.104.123.94]) by sj-iport-4.cisco.com with ESMTP; 15 Apr 2010 20:54:23 +0000 Received: from t60pugs.nuovasystems.com (dhcp-171-71-13-237.cisco.com [171.71.13.237]) by hkg-core-1.cisco.com (8.13.8/8.14.3) with SMTP id o3FKsKM4028675; Thu, 15 Apr 2010 20:54:21 GMT Date: Thu, 15 Apr 2010 13:51:34 -0700 From: "Tom Lyon" To: mst@redhat.com, hjk@linutronix.de, gregkh@suse.de, chrisw@sous-sol.org, joro@8bytes.org, avi@redhat.com, kvm@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH V2] drivers/uio/uio_pci_generic.c: allow access for non-privileged processes Message-ID: <4bc77c56.HDpFJH76e7Og+o/S%pugs@cisco.com> User-Agent: Heirloom mailx 12.2 01/07/07 MIME-Version: 1.0 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Thu, 15 Apr 2010 20:54:53 +0000 (UTC) --- linux-2.6.33/drivers/uio/uio_pci_generic.c 2010-02-24 10:52:17.000000000 -0800 +++ uio-2.6.33/drivers/uio/uio_pci_generic.c 2010-04-15 13:44:25.000000000 -0700 @@ -14,9 +14,9 @@ * # ls -l /sys/bus/pci/devices/0000:00:19.0/driver * .../0000:00:19.0/driver -> ../../../bus/pci/drivers/uio_pci_generic * - * Driver won't bind to devices which do not support the Interrupt Disable Bit + * Driver won't bind to devices which do not support MSI, MSI-x, or the Interrupt Disable Bit * in the command register. All devices compliant to PCI 2.3 (circa 2002) and - * all compliant PCI Express devices should support this bit. + * all compliant PCI Express devices should support one of these. */ #include @@ -41,6 +41,39 @@ return container_of(info, struct uio_pci_generic_dev, info); } +/* Read/modify/write command register to disable interrupts. + * Note: we could cache the value and optimize the read if there was a way to + * get notified of user changes to command register through sysfs. + * */ +static void irqtoggle(struct uio_pci_generic_dev *gdev, int irq_on) +{ + struct pci_dev *pdev = gdev->pdev; + unsigned long flags; + u16 orig, new; + + spin_lock_irqsave(&gdev->lock, flags); + pci_block_user_cfg_access(pdev); + pci_read_config_word(pdev, PCI_COMMAND, &orig); + new = irq_on ? (orig & ~PCI_COMMAND_INTX_DISABLE) + : (orig | PCI_COMMAND_INTX_DISABLE); + if (new != orig) + pci_write_config_word(gdev->pdev, PCI_COMMAND, new); + pci_unblock_user_cfg_access(pdev); + spin_unlock_irqrestore(&gdev->lock, flags); +} + +/* irqcontrol is use by userspace to enable/disable interrupts. */ +/* A privileged app can write the PCI_COMMAND register directly, + * but we need this for normal apps + */ +static int irqcontrol(struct uio_info *info, s32 irq_on) +{ + struct uio_pci_generic_dev *gdev = to_uio_pci_generic_dev(info); + + irqtoggle(gdev, irq_on); + return 0; +} + /* Interrupt handler. Read/modify/write the command register to disable * the interrupt. */ static irqreturn_t irqhandler(int irq, struct uio_info *info) @@ -89,7 +122,7 @@ /* Verify that the device supports Interrupt Disable bit in command register, * per PCI 2.3, by flipping this bit and reading it back: this bit was readonly * in PCI 2.2. */ -static int __devinit verify_pci_2_3(struct pci_dev *pdev) +static int verify_pci_2_3(struct pci_dev *pdev) { u16 orig, new; int err = 0; @@ -121,17 +154,51 @@ return err; } -static int __devinit probe(struct pci_dev *pdev, +/* we could've used the generic pci sysfs stuff for mmap, + * but this way we can allow non-privileged users as long + * as /dev/uio* has the right permissions + */ +static void uio_do_maps(struct uio_pci_generic_dev *gdev) +{ + struct pci_dev *pdev = gdev->pdev; + struct uio_info *info = &gdev->info; + int i, j; + char *name; + + for (i=0, j=0; imem[j].name = name; + info->mem[j].addr = pci_resource_start(pdev, i); + info->mem[j].size = pci_resource_len(pdev, i); + info->mem[j].memtype = UIO_MEM_PHYS; + j++; + } + } + for (i=0, j=0; iport[j].name = name; + info->port[j].start = pci_resource_start(pdev, i); + info->port[j].size = pci_resource_len(pdev, i); + info->port[j].porttype = UIO_PORT_X86; + j++; + } + } +} + +static int probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct uio_pci_generic_dev *gdev; int err; - - if (!pdev->irq) { - dev_warn(&pdev->dev, "No IRQ assigned to device: " - "no support for interrupts?\n"); - return -ENODEV; - } + int msi=0; err = pci_enable_device(pdev); if (err) { @@ -140,9 +207,26 @@ return err; } - err = verify_pci_2_3(pdev); - if (err) - goto err_verify; + if (pci_find_capability(pdev, PCI_CAP_ID_MSI)) { + msi++; + pci_disable_msi(pdev); + } + if (pci_find_capability(pdev, PCI_CAP_ID_MSIX)) { + msi++; + pci_disable_msix(pdev); + } + + if (!msi && !pdev->irq) { + dev_warn(&pdev->dev, "No MSI, MSIX, or IRQ assigned to device: " + "no support for interrupts?\n"); + return -ENODEV; + } + + if (pdev->irq) { + err = verify_pci_2_3(pdev); + if (err) + goto err_verify; + } gdev = kzalloc(sizeof(struct uio_pci_generic_dev), GFP_KERNEL); if (!gdev) { @@ -152,10 +236,15 @@ gdev->info.name = "uio_pci_generic"; gdev->info.version = DRIVER_VERSION; - gdev->info.irq = pdev->irq; - gdev->info.irq_flags = IRQF_SHARED; - gdev->info.handler = irqhandler; + if (pdev->irq) { + gdev->info.irq = pdev->irq; + gdev->info.irq_flags = IRQF_SHARED; + gdev->info.handler = irqhandler; + gdev->info.irqcontrol = irqcontrol; + } else + gdev->info.irq = -1; gdev->pdev = pdev; + uio_do_maps(gdev); spin_lock_init(&gdev->lock); if (uio_register_device(&pdev->dev, &gdev->info))