xen-pciback: notify hypervisor about devices intended to be assigned to guests
authorJan Beulich <JBeulich@suse.com>
Tue, 12 Mar 2013 15:06:23 +0000 (15:06 +0000)
committerKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Fri, 22 Mar 2013 14:20:55 +0000 (10:20 -0400)
For MSI-X capable devices the hypervisor wants to write protect the
MSI-X table and PBA, yet it can't assume that resources have been
assigned to their final values at device enumeration time. Thus have
pciback do that notification, as having the device controlled by it is
a prerequisite to assigning the device to guests anyway.

This is the kernel part of hypervisor side commit 4245d33 ("x86/MSI:
add mechanism to fully protect MSI-X table from PV guest accesses") on
the master branch of git://xenbits.xen.org/xen.git.

CC: stable@vger.kernel.org
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
arch/x86/include/asm/xen/hypercall.h
drivers/xen/fallback.c
drivers/xen/xen-pciback/pci_stub.c
include/xen/interface/physdev.h

index c20d1ce..e709884 100644 (file)
@@ -382,14 +382,14 @@ HYPERVISOR_console_io(int cmd, int count, char *str)
        return _hypercall3(int, console_io, cmd, count, str);
 }
 
-extern int __must_check HYPERVISOR_physdev_op_compat(int, void *);
+extern int __must_check xen_physdev_op_compat(int, void *);
 
 static inline int
 HYPERVISOR_physdev_op(int cmd, void *arg)
 {
        int rc = _hypercall2(int, physdev_op, cmd, arg);
        if (unlikely(rc == -ENOSYS))
-               rc = HYPERVISOR_physdev_op_compat(cmd, arg);
+               rc = xen_physdev_op_compat(cmd, arg);
        return rc;
 }
 
index 0ef7c4d..b04fb64 100644 (file)
@@ -44,7 +44,7 @@ int xen_event_channel_op_compat(int cmd, void *arg)
 }
 EXPORT_SYMBOL_GPL(xen_event_channel_op_compat);
 
-int HYPERVISOR_physdev_op_compat(int cmd, void *arg)
+int xen_physdev_op_compat(int cmd, void *arg)
 {
        struct physdev_op op;
        int rc;
@@ -78,3 +78,4 @@ int HYPERVISOR_physdev_op_compat(int cmd, void *arg)
 
        return rc;
 }
+EXPORT_SYMBOL_GPL(xen_physdev_op_compat);
index 9204126..a2278ba 100644 (file)
@@ -17,6 +17,7 @@
 #include <xen/events.h>
 #include <asm/xen/pci.h>
 #include <asm/xen/hypervisor.h>
+#include <xen/interface/physdev.h>
 #include "pciback.h"
 #include "conf_space.h"
 #include "conf_space_quirks.h"
@@ -85,37 +86,52 @@ static struct pcistub_device *pcistub_device_alloc(struct pci_dev *dev)
 static void pcistub_device_release(struct kref *kref)
 {
        struct pcistub_device *psdev;
+       struct pci_dev *dev;
        struct xen_pcibk_dev_data *dev_data;
 
        psdev = container_of(kref, struct pcistub_device, kref);
-       dev_data = pci_get_drvdata(psdev->dev);
+       dev = psdev->dev;
+       dev_data = pci_get_drvdata(dev);
 
-       dev_dbg(&psdev->dev->dev, "pcistub_device_release\n");
+       dev_dbg(&dev->dev, "pcistub_device_release\n");
 
-       xen_unregister_device_domain_owner(psdev->dev);
+       xen_unregister_device_domain_owner(dev);
 
        /* Call the reset function which does not take lock as this
         * is called from "unbind" which takes a device_lock mutex.
         */
-       __pci_reset_function_locked(psdev->dev);
-       if (pci_load_and_free_saved_state(psdev->dev,
-                                         &dev_data->pci_saved_state)) {
-               dev_dbg(&psdev->dev->dev, "Could not reload PCI state\n");
-       } else
-               pci_restore_state(psdev->dev);
+       __pci_reset_function_locked(dev);
+       if (pci_load_and_free_saved_state(dev, &dev_data->pci_saved_state))
+               dev_dbg(&dev->dev, "Could not reload PCI state\n");
+       else
+               pci_restore_state(dev);
+
+       if (pci_find_capability(dev, PCI_CAP_ID_MSIX)) {
+               struct physdev_pci_device ppdev = {
+                       .seg = pci_domain_nr(dev->bus),
+                       .bus = dev->bus->number,
+                       .devfn = dev->devfn
+               };
+               int err = HYPERVISOR_physdev_op(PHYSDEVOP_release_msix,
+                                               &ppdev);
+
+               if (err)
+                       dev_warn(&dev->dev, "MSI-X release failed (%d)\n",
+                                err);
+       }
 
        /* Disable the device */
-       xen_pcibk_reset_device(psdev->dev);
+       xen_pcibk_reset_device(dev);
 
        kfree(dev_data);
-       pci_set_drvdata(psdev->dev, NULL);
+       pci_set_drvdata(dev, NULL);
 
        /* Clean-up the device */
-       xen_pcibk_config_free_dyn_fields(psdev->dev);
-       xen_pcibk_config_free_dev(psdev->dev);
+       xen_pcibk_config_free_dyn_fields(dev);
+       xen_pcibk_config_free_dev(dev);
 
-       psdev->dev->dev_flags &= ~PCI_DEV_FLAGS_ASSIGNED;
-       pci_dev_put(psdev->dev);
+       dev->dev_flags &= ~PCI_DEV_FLAGS_ASSIGNED;
+       pci_dev_put(dev);
 
        kfree(psdev);
 }
@@ -355,6 +371,19 @@ static int pcistub_init_device(struct pci_dev *dev)
        if (err)
                goto config_release;
 
+       if (pci_find_capability(dev, PCI_CAP_ID_MSIX)) {
+               struct physdev_pci_device ppdev = {
+                       .seg = pci_domain_nr(dev->bus),
+                       .bus = dev->bus->number,
+                       .devfn = dev->devfn
+               };
+
+               err = HYPERVISOR_physdev_op(PHYSDEVOP_prepare_msix, &ppdev);
+               if (err)
+                       dev_err(&dev->dev, "MSI-X preparation failed (%d)\n",
+                               err);
+       }
+
        /* We need the device active to save the state. */
        dev_dbg(&dev->dev, "save state of device\n");
        pci_save_state(dev);
index 1844d31..7000bb1 100644 (file)
@@ -251,6 +251,12 @@ struct physdev_pci_device_add {
 
 #define PHYSDEVOP_pci_device_remove     26
 #define PHYSDEVOP_restore_msi_ext       27
+/*
+ * Dom0 should use these two to announce MMIO resources assigned to
+ * MSI-X capable devices won't (prepare) or may (release) change.
+ */
+#define PHYSDEVOP_prepare_msix          30
+#define PHYSDEVOP_release_msix          31
 struct physdev_pci_device {
     /* IN */
     uint16_t seg;