|
@@ -70,6 +70,13 @@ static void xen_pcibk_control_isr(struct pci_dev *dev, int reset)
|
|
|
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);
|
|
@@ -144,7 +151,12 @@ int xen_pcibk_enable_msi(struct xen_pcibk_device *pdev,
|
|
|
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("%s: error enabling MSI for guest %u: err %d\n",
|
|
@@ -173,20 +185,23 @@ static
|
|
|
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;
|
|
|
}
|
|
|
|
|
@@ -197,13 +212,26 @@ int xen_pcibk_enable_msix(struct xen_pcibk_device *pdev,
|
|
|
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;
|
|
@@ -245,23 +273,27 @@ static
|
|
|
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
|
|
@@ -298,9 +330,11 @@ void xen_pcibk_do_op(struct work_struct *data)
|
|
|
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)
|
|
@@ -342,6 +376,17 @@ void xen_pcibk_do_op(struct work_struct *data)
|
|
|
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);
|