[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] xen/pciback: drop the device reference if the export fails



xen_pcibk_export_device() takes a pcistub_device reference through
pcistub_get_pci_dev_by_slot().  On success xen_pcibk_add_pci_dev()
stores the device on the backend's device list and the reference is
released later by xen_pcibk_release_devices().

When the add fails before the device was queued -- the list entry
allocation fails, or for the vpci backend the device is a bridge or no
virtual slot is free -- the reference is never dropped.  psdev->pdev
then keeps pointing at the pdev that free_pdev() frees, and the device
can never be exported to a guest again because
pcistub_device_get_pci_dev() rejects a psdev that already has an owner.

Drop the reference on these paths, taking the device lock which
pcistub_put_pci_dev() requires.  Whether the device was queued is
checked with the coordinates the frontend sees, obtained through
xen_pcibk_get_pcifront_dev(), because the vpci backend hides the real
topology behind a single virtual bus.  The reference is kept when the
failure came from the publish callback, which runs after
list_add_tail() and is undone by xen_pcibk_release_devices().

Fixes: 30edc14bf39a ("xen/pciback: xen pci backend driver.")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: DeepSeek-V4.1-Flash and OpenAI Codex
Signed-off-by: Wentao Liang <vulab@xxxxxxxxxxx>
---
 drivers/xen/xen-pciback/xenbus.c | 33 +++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/drivers/xen/xen-pciback/xenbus.c b/drivers/xen/xen-pciback/xenbus.c
index b34785ba72e1..07e89d4593bb 100644
--- a/drivers/xen/xen-pciback/xenbus.c
+++ b/drivers/xen/xen-pciback/xenbus.c
@@ -223,6 +223,22 @@ static int xen_pcibk_publish_pci_dev(struct 
xen_pcibk_device *pdev,
        return err;
 }
 
+/* Is @dev on the device list of the backend behind @pdev? */
+static bool xen_pcibk_dev_is_exported(struct xen_pcibk_device *pdev,
+                                     struct pci_dev *dev)
+{
+       unsigned int domain, bus, devfn;
+
+       /*
+        * The lists are keyed on the coordinates the frontend sees, which
+        * xen_pcibk_get_pcifront_dev() reports for @dev.
+        */
+       if (!xen_pcibk_get_pcifront_dev(dev, pdev, &domain, &bus, &devfn))
+               return false;
+
+       return xen_pcibk_get_pci_dev(pdev, domain, bus, devfn) == dev;
+}
+
 static int xen_pcibk_export_device(struct xen_pcibk_device *pdev,
                                 int domain, int bus, int slot, int func,
                                 int devid)
@@ -246,8 +262,23 @@ static int xen_pcibk_export_device(struct xen_pcibk_device 
*pdev,
 
        err = xen_pcibk_add_pci_dev(pdev, dev, devid,
                                    xen_pcibk_publish_pci_dev);
-       if (err)
+       if (err) {
+               /*
+                * The reference taken above is only handed over once the
+                * device is queued on the backend's device list.  Failures
+                * before that (no entry, bridge on the vpci bus, no free
+                * virtual slot) have to drop it here, otherwise the device
+                * stays pinned to a pdev that is about to be freed.  A
+                * failing publish callback comes after the queueing and is
+                * undone by xen_pcibk_release_devices().
+                */
+               if (!xen_pcibk_dev_is_exported(pdev, dev)) {
+                       device_lock(&dev->dev);
+                       pcistub_put_pci_dev(dev);
+                       device_unlock(&dev->dev);
+               }
                goto out;
+       }
 
        dev_info(&dev->dev, "registering for %d\n", pdev->xdev->otherend_id);
        if (xen_register_device_domain_owner(dev,
-- 
2.34.1




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.