为什么设备驱动程序可能会禁用MSI和MSI-X中断?

时间:2016-07-11 14:02:24

标签: c linux linux-kernel linux-device-driver xen

为什么驱动程序开发人员可以启用或禁用MSI和MSI-X?

我正在使用基于XenServer 6.5sp1的系统,该系统使用pci passthrough。 最近的一个Hotfix(XS65ESP1021)包含对内核pciback驱动程序的更改(在pciback_ops.c中),它打破了pci passthrough。 请注意,链接的修补程序包含XenServer中使用的源。链接的源文件取自一般的linux源代码作为参考,包含我正在讨论的更改。

发布说明并未明确此更改的动机。根据经验,似乎pciback_ops.c的更改有效地禁用了pci-passthrough设备的MSI和MSI-X中断。这种变化的动机可能是什么?

我对硬件中断有相当好的理解,但我不太了解MSI和MSI-X有何不同。我可以恢复这个改变并“解决”我的问题,但我想了解我正在改变什么。

改变:

--- linux-3.10.41-339/drivers/xen/xen-pciback/pciback_ops.c 2015-06-23 08:54:22.000000000 -0500
+++ linux-3.10.41-349/drivers/xen/xen-pciback/pciback_ops.c 2015-12-08 08:53:49.000000000 -0600
@@ -67,6 +67,13 @@
        enable ? "enable" : "disable");

    if (enable) {
+       /*
+        * The MSI or MSI-X should not have an IRQ handler. Otherwise
+        * if the guest terminates we BUG_ON in free_msi_irqs.
+        */
+       if (dev->msi_enabled || dev->msix_enabled)
+           goto out;
+
        rc = request_irq(dev_data->irq,
                xen_pcibk_guest_interrupt, IRQF_SHARED,
                dev_data->irq_name, dev);
@@ -141,7 +148,12 @@
    if (unlikely(verbose_request))
        printk(KERN_DEBUG DRV_NAME ": %s: enable MSI\n", pci_name(dev));

-   status = pci_enable_msi(dev);
+   if (dev->msi_enabled)
+       status = -EALREADY;
+   else if (dev->msix_enabled)
+       status = -ENXIO;
+   else
+       status = pci_enable_msi(dev);

    if (status) {
        pr_warn_ratelimited(DRV_NAME ": %s: error enabling MSI for guest %u: err %d\n",
@@ -170,20 +182,23 @@
 int xen_pcibk_disable_msi(struct xen_pcibk_device *pdev,
              struct pci_dev *dev, struct xen_pci_op *op)
 {
-   struct xen_pcibk_dev_data *dev_data;
-
    if (unlikely(verbose_request))
        printk(KERN_DEBUG DRV_NAME ": %s: disable MSI\n",
               pci_name(dev));
-   pci_disable_msi(dev);

+   if (dev->msi_enabled) {
+       struct xen_pcibk_dev_data *dev_data;
+
+       pci_disable_msi(dev);
+
+       dev_data = pci_get_drvdata(dev);
+       if (dev_data)
+           dev_data->ack_intr = 1;
+   }
    op->value = dev->irq ? xen_pirq_from_irq(dev->irq) : 0;
    if (unlikely(verbose_request))
        printk(KERN_DEBUG DRV_NAME ": %s: MSI: %d\n", pci_name(dev),
            op->value);
-   dev_data = pci_get_drvdata(dev);
-   if (dev_data)
-       dev_data->ack_intr = 1;
    return 0;
 }

@@ -194,13 +209,26 @@
    struct xen_pcibk_dev_data *dev_data;
    int i, result;
    struct msix_entry *entries;
+   u16 cmd;

    if (unlikely(verbose_request))
        printk(KERN_DEBUG DRV_NAME ": %s: enable MSI-X\n",
               pci_name(dev));
+
    if (op->value > SH_INFO_MAX_VEC)
        return -EINVAL;

+   if (dev->msix_enabled)
+       return -EALREADY;
+
+   /*
+    * PCI_COMMAND_MEMORY must be enabled, otherwise we may not be able
+    * to access the BARs where the MSI-X entries reside.
+    */
+   pci_read_config_word(dev, PCI_COMMAND, &cmd);
+   if (dev->msi_enabled || !(cmd & PCI_COMMAND_MEMORY))
+       return -ENXIO;
+
    entries = kmalloc(op->value * sizeof(*entries), GFP_KERNEL);
    if (entries == NULL)
        return -ENOMEM;
@@ -242,23 +270,27 @@
 int xen_pcibk_disable_msix(struct xen_pcibk_device *pdev,
               struct pci_dev *dev, struct xen_pci_op *op)
 {
-   struct xen_pcibk_dev_data *dev_data;
    if (unlikely(verbose_request))
        printk(KERN_DEBUG DRV_NAME ": %s: disable MSI-X\n",
            pci_name(dev));
-   pci_disable_msix(dev);

+   if (dev->msix_enabled) {
+       struct xen_pcibk_dev_data *dev_data;
+
+       pci_disable_msix(dev);
+
+       dev_data = pci_get_drvdata(dev);
+       if (dev_data)
+           dev_data->ack_intr = 1;
+   }
    /*
     * SR-IOV devices (which don't have any legacy IRQ) have
     * an undefined IRQ value of zero.
     */
    op->value = dev->irq ? xen_pirq_from_irq(dev->irq) : 0;
    if (unlikely(verbose_request))
-       printk(KERN_DEBUG DRV_NAME ": %s: MSI-X: %d\n", pci_name(dev),
-           op->value);
-   dev_data = pci_get_drvdata(dev);
-   if (dev_data)
-       dev_data->ack_intr = 1;
+       printk(KERN_DEBUG DRV_NAME ": %s: MSI-X: %d\n",
+              pci_name(dev), op->value);
    return 0;
 }
 #endif
@@ -295,9 +327,11 @@
        container_of(data, struct xen_pcibk_device, op_work);
    struct pci_dev *dev;
    struct xen_pcibk_dev_data *dev_data = NULL;
-   struct xen_pci_op *op = &pdev->sh_info->op;
+   struct xen_pci_op *op = &pdev->op;
    int test_intx = 0;

+   *op = pdev->sh_info->op;
+   barrier();
    dev = xen_pcibk_get_pci_dev(pdev, op->domain, op->bus, op->devfn);

    if (dev == NULL)
@@ -339,6 +373,17 @@
        if ((dev_data->enable_intx != test_intx))
            xen_pcibk_control_isr(dev, 0 /* no reset */);
    }
+   pdev->sh_info->op.err = op->err;
+   pdev->sh_info->op.value = op->value;
+#ifdef CONFIG_PCI_MSI
+   if (op->cmd == XEN_PCI_OP_enable_msix && op->err == 0) {
+       unsigned int i;
+
+       for (i = 0; i < op->value; i++)
+           pdev->sh_info->op.msix_entries[i].vector =
+               op->msix_entries[i].vector;
+   }
+#endif
    /* Tell the driver domain that we're done. */
    wmb();
    clear_bit(_XEN_PCIF_active, (unsigned long *)&pdev->sh_info->flags);

我在开发者邮件列表上询问过这个问题,但没有得到明确答案:xs-devel mailing list discussion

0 个答案:

没有答案