diff mbox series

[v19,2/3] vpci: acquire d->pci_lock in I/O handlers

Message ID 20250415165404.435506-3-stewart.hildebrand@amd.com (mailing list archive)
State Superseded
Headers show
Series PCI devices passthrough on Arm, part 3 | expand

Commit Message

Stewart Hildebrand April 15, 2025, 4:54 p.m. UTC
In preparation for translating virtual PCI bus topology (where a pdev
lookup will be needed in the vPCI I/O handlers), acquire d->pci_lock in
the vPCI I/O handlers.

While here, adjust indentation in vpci_ecam_{read,write}.

Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com>
---
v18->v19:
* new patch

While the current patch series focus is Arm, this is also prerequisite
for eventually enabling x86 PVH domU passthrough. Similar to Arm, a call
to vpci_translate_virtual_device() would be added in
xen/arch/x86/hvm/io.c:vpci_portio_{read,write} and
vpci_mmcfg_{read,write}.
---
 xen/arch/arm/vpci.c     | 11 +++++++++--
 xen/arch/x86/hvm/io.c   | 10 +++++++++-
 xen/drivers/vpci/vpci.c | 21 ++++++++++-----------
 3 files changed, 28 insertions(+), 14 deletions(-)

Comments

Jan Beulich April 16, 2025, 3:27 p.m. UTC | #1
On 15.04.2025 18:54, Stewart Hildebrand wrote:
> In preparation for translating virtual PCI bus topology (where a pdev
> lookup will be needed in the vPCI I/O handlers), acquire d->pci_lock in
> the vPCI I/O handlers.

I find it concerning that the locked regions (it's a domain-wide lock
after all) are further grown.

> --- a/xen/drivers/vpci/vpci.c
> +++ b/xen/drivers/vpci/vpci.c
> @@ -440,6 +440,8 @@ uint32_t vpci_read(pci_sbdf_t sbdf, unsigned int reg, unsigned int size)
>      unsigned int data_offset = 0;
>      uint32_t data = ~(uint32_t)0;
>  
> +    ASSERT(rw_is_locked(&d->pci_lock));
> +
>      if ( !size )
>      {
>          ASSERT_UNREACHABLE();
> @@ -452,15 +454,11 @@ uint32_t vpci_read(pci_sbdf_t sbdf, unsigned int reg, unsigned int size)
>       * If this is hwdom and the device is assigned to DomXEN, acquiring hwdom's
>       * pci_lock is sufficient.
>       */
> -    read_lock(&d->pci_lock);

The comment now becomes meaningless in its context.

> @@ -570,7 +569,6 @@ void vpci_write(pci_sbdf_t sbdf, unsigned int reg, unsigned int size,
>       * TODO: We need to take pci_locks in exclusive mode only if we
>       * are modifying BARs, so there is a room for improvement.
>       */
> -    write_lock(&d->pci_lock);

Same here.

Jan
Stewart Hildebrand April 17, 2025, 2:22 p.m. UTC | #2
On 4/16/25 11:27, Jan Beulich wrote:
> On 15.04.2025 18:54, Stewart Hildebrand wrote:
>> In preparation for translating virtual PCI bus topology (where a pdev
>> lookup will be needed in the vPCI I/O handlers), acquire d->pci_lock in
>> the vPCI I/O handlers.
> 
> I find it concerning that the locked regions (it's a domain-wide lock
> after all) are further grown.

Hm. Right. Another approach may be to call
vpci_translate_virtual_device from within locked context of
vpci_{read,write}.
diff mbox series

Patch

diff --git a/xen/arch/arm/vpci.c b/xen/arch/arm/vpci.c
index b63a356bb4a8..828c5c745bd9 100644
--- a/xen/arch/arm/vpci.c
+++ b/xen/arch/arm/vpci.c
@@ -34,15 +34,18 @@  static int vpci_mmio_read(struct vcpu *v, mmio_info_t *info,
     /* data is needed to prevent a pointer cast on 32bit */
     unsigned long data;
 
+    read_lock(&v->domain->pci_lock);
     if ( vpci_ecam_read(sbdf, ECAM_REG_OFFSET(info->gpa),
                         1U << info->dabt.size, &data) )
     {
         *r = data;
+        read_unlock(&v->domain->pci_lock);
         return 1;
     }
 
     *r = invalid;
 
+    read_unlock(&v->domain->pci_lock);
     return 0;
 }
 
@@ -51,9 +54,13 @@  static int vpci_mmio_write(struct vcpu *v, mmio_info_t *info,
 {
     struct pci_host_bridge *bridge = p;
     pci_sbdf_t sbdf = vpci_sbdf_from_gpa(bridge, info->gpa);
+    int ret;
 
-    return vpci_ecam_write(sbdf, ECAM_REG_OFFSET(info->gpa),
-                           1U << info->dabt.size, r);
+    write_lock(&v->domain->pci_lock);
+    ret = vpci_ecam_write(sbdf, ECAM_REG_OFFSET(info->gpa),
+                          1U << info->dabt.size, r);
+    write_unlock(&v->domain->pci_lock);
+    return ret;
 }
 
 static const struct mmio_handler_ops vpci_mmio_handler = {
diff --git a/xen/arch/x86/hvm/io.c b/xen/arch/x86/hvm/io.c
index de6ee6c4dd4d..0b9036528abe 100644
--- a/xen/arch/x86/hvm/io.c
+++ b/xen/arch/x86/hvm/io.c
@@ -261,7 +261,7 @@  static int cf_check vpci_portio_read(
     const struct hvm_io_handler *handler, uint64_t addr, uint32_t size,
     uint64_t *data)
 {
-    const struct domain *d = current->domain;
+    struct domain *d = current->domain;
     unsigned int reg;
     pci_sbdf_t sbdf;
     uint32_t cf8;
@@ -285,7 +285,9 @@  static int cf_check vpci_portio_read(
     if ( !vpci_access_allowed(reg, size) )
         return X86EMUL_OKAY;
 
+    read_lock(&d->pci_lock);
     *data = vpci_read(sbdf, reg, size);
+    read_unlock(&d->pci_lock);
 
     return X86EMUL_OKAY;
 }
@@ -316,7 +318,9 @@  static int cf_check vpci_portio_write(
     if ( !vpci_access_allowed(reg, size) )
         return X86EMUL_OKAY;
 
+    write_lock(&d->pci_lock);
     vpci_write(sbdf, reg, size, data);
+    write_unlock(&d->pci_lock);
 
     return X86EMUL_OKAY;
 }
@@ -424,7 +428,9 @@  static int cf_check vpci_mmcfg_read(
     read_unlock(&d->arch.hvm.mmcfg_lock);
 
     /* Failed reads are not propagated to the caller */
+    read_lock(&d->pci_lock);
     vpci_ecam_read(sbdf, reg, len, data);
+    read_unlock(&d->pci_lock);
 
     return X86EMUL_OKAY;
 }
@@ -449,7 +455,9 @@  static int cf_check vpci_mmcfg_write(
     read_unlock(&d->arch.hvm.mmcfg_lock);
 
     /* Failed writes are not propagated to the caller */
+    write_lock(&d->pci_lock);
     vpci_ecam_write(sbdf, reg, len, data);
+    write_unlock(&d->pci_lock);
 
     return X86EMUL_OKAY;
 }
diff --git a/xen/drivers/vpci/vpci.c b/xen/drivers/vpci/vpci.c
index 1e6aa5d799b9..d01fb7be226a 100644
--- a/xen/drivers/vpci/vpci.c
+++ b/xen/drivers/vpci/vpci.c
@@ -440,6 +440,8 @@  uint32_t vpci_read(pci_sbdf_t sbdf, unsigned int reg, unsigned int size)
     unsigned int data_offset = 0;
     uint32_t data = ~(uint32_t)0;
 
+    ASSERT(rw_is_locked(&d->pci_lock));
+
     if ( !size )
     {
         ASSERT_UNREACHABLE();
@@ -452,15 +454,11 @@  uint32_t vpci_read(pci_sbdf_t sbdf, unsigned int reg, unsigned int size)
      * If this is hwdom and the device is assigned to DomXEN, acquiring hwdom's
      * pci_lock is sufficient.
      */
-    read_lock(&d->pci_lock);
     pdev = pci_get_pdev(d, sbdf);
     if ( !pdev && is_hardware_domain(d) )
         pdev = pci_get_pdev(dom_xen, sbdf);
     if ( !pdev || !pdev->vpci )
-    {
-        read_unlock(&d->pci_lock);
         return vpci_read_hw(sbdf, reg, size);
-    }
 
     spin_lock(&pdev->vpci->lock);
 
@@ -507,7 +505,6 @@  uint32_t vpci_read(pci_sbdf_t sbdf, unsigned int reg, unsigned int size)
         ASSERT(data_offset < size);
     }
     spin_unlock(&pdev->vpci->lock);
-    read_unlock(&d->pci_lock);
 
     if ( data_offset < size )
     {
@@ -555,6 +552,8 @@  void vpci_write(pci_sbdf_t sbdf, unsigned int reg, unsigned int size,
     const struct vpci_register *r;
     unsigned int data_offset = 0;
 
+    ASSERT(rw_is_write_locked(&d->pci_lock));
+
     if ( !size )
     {
         ASSERT_UNREACHABLE();
@@ -570,7 +569,6 @@  void vpci_write(pci_sbdf_t sbdf, unsigned int reg, unsigned int size,
      * TODO: We need to take pci_locks in exclusive mode only if we
      * are modifying BARs, so there is a room for improvement.
      */
-    write_lock(&d->pci_lock);
     pdev = pci_get_pdev(d, sbdf);
     if ( !pdev && is_hardware_domain(d) )
         pdev = pci_get_pdev(dom_xen, sbdf);
@@ -579,8 +577,6 @@  void vpci_write(pci_sbdf_t sbdf, unsigned int reg, unsigned int size,
         /* Ignore writes to read-only devices, which have no ->vpci. */
         const unsigned long *ro_map = pci_get_ro_map(sbdf.seg);
 
-        write_unlock(&d->pci_lock);
-
         if ( !ro_map || !test_bit(sbdf.bdf, ro_map) )
             vpci_write_hw(sbdf, reg, size, data);
         return;
@@ -622,7 +618,6 @@  void vpci_write(pci_sbdf_t sbdf, unsigned int reg, unsigned int size,
         ASSERT(data_offset < size);
     }
     spin_unlock(&pdev->vpci->lock);
-    write_unlock(&d->pci_lock);
 
     if ( data_offset < size )
         /* Tailing gap, write the remaining. */
@@ -651,8 +646,10 @@  bool vpci_access_allowed(unsigned int reg, unsigned int len)
 }
 
 bool vpci_ecam_write(pci_sbdf_t sbdf, unsigned int reg, unsigned int len,
-                         unsigned long data)
+                     unsigned long data)
 {
+    ASSERT(rw_is_write_locked(&current->domain->pci_lock));
+
     if ( !vpci_access_allowed(reg, len) ||
          (reg + len) > PCI_CFG_SPACE_EXP_SIZE )
         return false;
@@ -667,8 +664,10 @@  bool vpci_ecam_write(pci_sbdf_t sbdf, unsigned int reg, unsigned int len,
 }
 
 bool vpci_ecam_read(pci_sbdf_t sbdf, unsigned int reg, unsigned int len,
-                        unsigned long *data)
+                    unsigned long *data)
 {
+    ASSERT(rw_is_locked(&current->domain->pci_lock));
+
     if ( !vpci_access_allowed(reg, len) ||
          (reg + len) > PCI_CFG_SPACE_EXP_SIZE )
         return false;