summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '0040-PCI-simplify-and-thus-correct-pci_get_pdev-_by_domai.patch')
-rw-r--r--0040-PCI-simplify-and-thus-correct-pci_get_pdev-_by_domai.patch153
1 files changed, 153 insertions, 0 deletions
diff --git a/0040-PCI-simplify-and-thus-correct-pci_get_pdev-_by_domai.patch b/0040-PCI-simplify-and-thus-correct-pci_get_pdev-_by_domai.patch
new file mode 100644
index 0000000..c29e5ac
--- /dev/null
+++ b/0040-PCI-simplify-and-thus-correct-pci_get_pdev-_by_domai.patch
@@ -0,0 +1,153 @@
+From 9acedc3c58c31930737edbe212f2ccf437a0b757 Mon Sep 17 00:00:00 2001
+From: Jan Beulich <jbeulich@suse.com>
+Date: Mon, 15 Aug 2022 15:44:23 +0200
+Subject: [PATCH 40/67] PCI: simplify (and thus correct)
+ pci_get_pdev{,_by_domain}()
+
+The last "wildcard" use of either function went away with f591755823a7
+("IOMMU/PCI: don't let domain cleanup continue when device de-assignment
+failed"). Don't allow them to be called this way anymore. Besides
+simplifying the code this also fixes two bugs:
+
+1) When seg != -1, the outer loops should have been terminated after the
+ first iteration, or else a device with the same BDF but on another
+ segment could be found / returned.
+
+Reported-by: Rahul Singh <rahul.singh@arm.com>
+
+2) When seg == -1 calling get_pseg() is bogus. The function (taking a
+ u16) would look for segment 0xffff, which might exist. If it exists,
+ we might then find / return a wrong device.
+
+In pci_get_pdev_by_domain() also switch from using the per-segment list
+to using the per-domain one, with the exception of the hardware domain
+(see the code comment there).
+
+While there also constify "pseg" and drop "pdev"'s already previously
+unnecessary initializer.
+
+Signed-off-by: Jan Beulich <jbeulich@suse.com>
+Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
+Reviewed-by: Rahul Singh <rahul.singh@arm.com>
+Tested-by: Rahul Singh <rahul.singh@arm.com>
+master commit: 8cf6e0738906fc269af40135ed82a07815dd3b9c
+master date: 2022-08-12 08:34:33 +0200
+---
+ xen/drivers/passthrough/pci.c | 61 +++++++++++++++--------------------
+ xen/include/xen/pci.h | 6 ++--
+ 2 files changed, 29 insertions(+), 38 deletions(-)
+
+diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c
+index bbacbe41dac4..9b81b941c8bb 100644
+--- a/xen/drivers/passthrough/pci.c
++++ b/xen/drivers/passthrough/pci.c
+@@ -528,30 +528,19 @@ int __init pci_ro_device(int seg, int bus, int devfn)
+ return 0;
+ }
+
+-struct pci_dev *pci_get_pdev(int seg, int bus, int devfn)
++struct pci_dev *pci_get_pdev(uint16_t seg, uint8_t bus, uint8_t devfn)
+ {
+- struct pci_seg *pseg = get_pseg(seg);
+- struct pci_dev *pdev = NULL;
++ const struct pci_seg *pseg = get_pseg(seg);
++ struct pci_dev *pdev;
+
+ ASSERT(pcidevs_locked());
+- ASSERT(seg != -1 || bus == -1);
+- ASSERT(bus != -1 || devfn == -1);
+
+ if ( !pseg )
+- {
+- if ( seg == -1 )
+- radix_tree_gang_lookup(&pci_segments, (void **)&pseg, 0, 1);
+- if ( !pseg )
+- return NULL;
+- }
++ return NULL;
+
+- do {
+- list_for_each_entry ( pdev, &pseg->alldevs_list, alldevs_list )
+- if ( (pdev->bus == bus || bus == -1) &&
+- (pdev->devfn == devfn || devfn == -1) )
+- return pdev;
+- } while ( radix_tree_gang_lookup(&pci_segments, (void **)&pseg,
+- pseg->nr + 1, 1) );
++ list_for_each_entry ( pdev, &pseg->alldevs_list, alldevs_list )
++ if ( pdev->bus == bus && pdev->devfn == devfn )
++ return pdev;
+
+ return NULL;
+ }
+@@ -577,31 +566,33 @@ struct pci_dev *pci_get_real_pdev(int seg, int bus, int devfn)
+ return pdev;
+ }
+
+-struct pci_dev *pci_get_pdev_by_domain(const struct domain *d, int seg,
+- int bus, int devfn)
++struct pci_dev *pci_get_pdev_by_domain(const struct domain *d, uint16_t seg,
++ uint8_t bus, uint8_t devfn)
+ {
+- struct pci_seg *pseg = get_pseg(seg);
+- struct pci_dev *pdev = NULL;
++ struct pci_dev *pdev;
+
+- ASSERT(seg != -1 || bus == -1);
+- ASSERT(bus != -1 || devfn == -1);
+-
+- if ( !pseg )
++ /*
++ * The hardware domain owns the majority of the devices in the system.
++ * When there are multiple segments, traversing the per-segment list is
++ * likely going to be faster, whereas for a single segment the difference
++ * shouldn't be that large.
++ */
++ if ( is_hardware_domain(d) )
+ {
+- if ( seg == -1 )
+- radix_tree_gang_lookup(&pci_segments, (void **)&pseg, 0, 1);
++ const struct pci_seg *pseg = get_pseg(seg);
++
+ if ( !pseg )
+ return NULL;
+- }
+
+- do {
+ list_for_each_entry ( pdev, &pseg->alldevs_list, alldevs_list )
+- if ( (pdev->bus == bus || bus == -1) &&
+- (pdev->devfn == devfn || devfn == -1) &&
+- (pdev->domain == d) )
++ if ( pdev->bus == bus && pdev->devfn == devfn &&
++ pdev->domain == d )
++ return pdev;
++ }
++ else
++ list_for_each_entry ( pdev, &d->pdev_list, domain_list )
++ if ( pdev->bus == bus && pdev->devfn == devfn )
+ return pdev;
+- } while ( radix_tree_gang_lookup(&pci_segments, (void **)&pseg,
+- pseg->nr + 1, 1) );
+
+ return NULL;
+ }
+diff --git a/xen/include/xen/pci.h b/xen/include/xen/pci.h
+index 8e3d4d94543a..cd238ae852b0 100644
+--- a/xen/include/xen/pci.h
++++ b/xen/include/xen/pci.h
+@@ -166,10 +166,10 @@ int pci_add_device(u16 seg, u8 bus, u8 devfn,
+ int pci_remove_device(u16 seg, u8 bus, u8 devfn);
+ int pci_ro_device(int seg, int bus, int devfn);
+ int pci_hide_device(unsigned int seg, unsigned int bus, unsigned int devfn);
+-struct pci_dev *pci_get_pdev(int seg, int bus, int devfn);
++struct pci_dev *pci_get_pdev(uint16_t seg, uint8_t bus, uint8_t devfn);
+ struct pci_dev *pci_get_real_pdev(int seg, int bus, int devfn);
+-struct pci_dev *pci_get_pdev_by_domain(const struct domain *, int seg,
+- int bus, int devfn);
++struct pci_dev *pci_get_pdev_by_domain(const struct domain *, uint16_t seg,
++ uint8_t bus, uint8_t devfn);
+ void pci_check_disable_device(u16 seg, u8 bus, u8 devfn);
+
+ uint8_t pci_conf_read8(pci_sbdf_t sbdf, unsigned int reg);
+--
+2.37.3
+