|
[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
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |