[PATCH v1] intel_iommu: Support concurrent page fault handling with PRI

Clément MATHIEU--DRIF posted 1 patch 3 weeks, 2 days ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20260903145044.2245609-1-clement.mathieu--drif@bull.com
Maintainers: "Michael S. Tsirkin" <mst@redhat.com>, Jason Wang <jasowangio@gmail.com>, Yi Liu <yi.l.liu@intel.com>, "Clément Mathieu--Drif" <clement.mathieu--drif@bull.com>, Paolo Bonzini <pbonzini@redhat.com>, Richard Henderson <richard.henderson@linaro.org>
hw/i386/intel_iommu.c | 91 +++++++++++++++++++++++++++----------------
1 file changed, 57 insertions(+), 34 deletions(-)
[PATCH v1] intel_iommu: Support concurrent page fault handling with PRI
Posted by Clément MATHIEU--DRIF 3 weeks, 2 days ago
PRQ slot allocation must be thread safe to avoid posting multiple
commands in the same slot so we take a lock in vtd_pri_request_page.

This allows multiple devices to call the page request interface
of the same iommu instance.

Signed-off-by: Clement Mathieu--Drif <clement.mathieu--drif@bull.com>
---
 hw/i386/intel_iommu.c | 91 +++++++++++++++++++++++++++----------------
 1 file changed, 57 insertions(+), 34 deletions(-)

diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index 9f21622a4f..350d2b7753 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -3627,15 +3627,26 @@ static void vtd_handle_iectl_write(IntelIOMMUState *s)
 
 static void vtd_handle_prs_write(IntelIOMMUState *s)
 {
-    uint32_t prs = vtd_get_long_raw(s, DMAR_PRS_REG);
+    uint32_t prs;
+
+    vtd_iommu_lock(s);
+
+    prs = vtd_get_long_raw(s, DMAR_PRS_REG);
     if (!(prs & VTD_PR_STATUS_PPR) && !(prs & VTD_PR_STATUS_PRO)) {
         vtd_set_clear_mask_long(s, DMAR_PECTL_REG, VTD_PR_PECTL_IP, 0);
     }
+
+    vtd_iommu_unlock(s);
 }
 
 static void vtd_handle_pectl_write(IntelIOMMUState *s)
 {
-    uint32_t pectl = vtd_get_long_raw(s, DMAR_PECTL_REG);
+    uint32_t pectl;
+
+    vtd_iommu_lock(s);
+
+    pectl = vtd_get_long_raw(s, DMAR_PECTL_REG);
+
     if ((pectl & VTD_PR_PECTL_IP) && !(pectl & VTD_PR_PECTL_IM)) {
         /*
          * If IP field was 1 when software clears the IM field,
@@ -3644,6 +3655,8 @@ static void vtd_handle_pectl_write(IntelIOMMUState *s)
         vtd_set_clear_mask_long(s, DMAR_PECTL_REG, VTD_PR_PECTL_IP, 0);
         vtd_generate_interrupt(s, DMAR_PEADDR_REG, DMAR_PEDATA_REG);
     }
+
+    vtd_iommu_unlock(s);
 }
 
 static uint64_t vtd_mem_read(void *opaque, hwaddr addr, unsigned size)
@@ -5378,19 +5391,18 @@ static int vtd_pri_request_page(PCIBus *bus, void *opaque, int devfn,
 {
     IntelIOMMUState *s = opaque;
     VTDAddressSpace *vtd_as;
+    uint64_t queue_addr_reg;
+    uint64_t queue_tail_offset_reg;
+    uint64_t new_queue_tail_offset;
+    uint64_t queue_head_offset_reg;
+    hwaddr queue_tail;
+    uint32_t old_pr_status;
+    uint16_t sid;
+    VTDPRDesc desc;
+    int ret = 0;
 
     vtd_as = vtd_find_add_as(s, bus, devfn, pasid);
-
-    uint64_t queue_addr_reg = vtd_get_quad(s, DMAR_PQA_REG);
-    uint64_t queue_tail_offset_reg = vtd_get_quad(s, DMAR_PQT_REG);
-    uint64_t new_queue_tail_offset = (
-                                (queue_tail_offset_reg + VTD_PQA_ENTRY_SIZE) %
-                                (vtd_prq_size(s) * VTD_PQA_ENTRY_SIZE));
-    uint64_t queue_head_offset_reg = vtd_get_quad(s, DMAR_PQH_REG);
-    hwaddr queue_tail = (queue_addr_reg & VTD_PQA_ADDR) + queue_tail_offset_reg;
-    uint32_t old_pr_status = vtd_get_long(s, DMAR_PRS_REG);
-    uint16_t sid = PCI_BUILD_BDF(pci_bus_num(vtd_as->bus), vtd_as->devfn);
-    VTDPRDesc desc;
+    sid = PCI_BUILD_BDF(pci_bus_num(vtd_as->bus), vtd_as->devfn);
 
     if (!(s->ecap & VTD_ECAP_PRS)) {
         return -EPERM;
@@ -5414,52 +5426,63 @@ static int vtd_pri_request_page(PCIBus *bus, void *opaque, int devfn,
         return -EPERM;
     }
 
+    /* Prepare the descriptor */
+    desc.lo = VTD_PRD_TYPE | VTD_PRD_PP(true) | VTD_PRD_RID(sid) |
+              VTD_PRD_PASID(vtd_as->pasid) | VTD_PRD_PMR(priv_req);
+    desc.hi = VTD_PRD_RDR(is_read) | VTD_PRD_WRR(is_write) |
+              VTD_PRD_LPIG(lpig) | VTD_PRD_PRGI(prgi) | VTD_PRD_ADDR(addr);
+
+    desc.lo = cpu_to_le64(desc.lo);
+    desc.hi = cpu_to_le64(desc.hi);
+
+    if (vtd_pri_perform_implicit_invalidation(vtd_as, addr)) {
+        return -EINVAL;
+    }
+
+    vtd_iommu_lock(s);
+
+    queue_addr_reg = vtd_get_quad(s, DMAR_PQA_REG);
+    queue_tail_offset_reg = vtd_get_quad(s, DMAR_PQT_REG);
+    new_queue_tail_offset = ((queue_tail_offset_reg + VTD_PQA_ENTRY_SIZE) %
+                            (vtd_prq_size(s) * VTD_PQA_ENTRY_SIZE));
+    queue_head_offset_reg = vtd_get_quad(s, DMAR_PQH_REG);
+    queue_tail = (queue_addr_reg & VTD_PQA_ADDR) + queue_tail_offset_reg;
+    old_pr_status = vtd_get_long(s, DMAR_PRS_REG);
+
     if (old_pr_status & VTD_PR_STATUS_PRO) {
         /*
          * No action is taken by hardware to report a fault
          * or generate an event
          */
-        return -ENOSPC;
+        ret = -ENOSPC;
+        goto out;
     }
 
     /* Check for overflow */
     if (new_queue_tail_offset == queue_head_offset_reg) {
         vtd_set_clear_mask_long(s, DMAR_PRS_REG, 0, VTD_PR_STATUS_PRO);
         vtd_generate_page_request_event(s, old_pr_status);
-        return -ENOSPC;
-    }
-
-    if (vtd_pri_perform_implicit_invalidation(vtd_as, addr)) {
-        return -EINVAL;
+        ret = -ENOSPC;
+        goto out;
     }
 
-    desc.lo = VTD_PRD_TYPE | VTD_PRD_PP(true) | VTD_PRD_RID(sid) |
-              VTD_PRD_PASID(vtd_as->pasid) | VTD_PRD_PMR(priv_req);
-    desc.hi = VTD_PRD_RDR(is_read) | VTD_PRD_WRR(is_write) |
-              VTD_PRD_LPIG(lpig) | VTD_PRD_PRGI(prgi) | VTD_PRD_ADDR(addr);
-
-    desc.lo = cpu_to_le64(desc.lo);
-    desc.hi = cpu_to_le64(desc.hi);
     if (dma_memory_write(&address_space_memory, queue_tail, &desc, sizeof(desc),
                          MEMTXATTRS_UNSPECIFIED)) {
         error_report_once("IO error, the PQ tail cannot be updated");
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     /* increment the tail register and set the pending request bit */
     vtd_set_quad(s, DMAR_PQT_REG, new_queue_tail_offset);
-    /*
-     * read status again so that the kernel does not miss a request.
-     * in some cases, we can trigger an unecessary interrupt but this strategy
-     * drastically improves performance as we don't need to take a lock.
-     */
-    old_pr_status = vtd_get_long(s, DMAR_PRS_REG);
     if (!(old_pr_status & VTD_PR_STATUS_PPR)) {
         vtd_set_clear_mask_long(s, DMAR_PRS_REG, 0, VTD_PR_STATUS_PPR);
         vtd_generate_page_request_event(s, old_pr_status);
     }
 
-    return 0;
+out:
+    vtd_iommu_unlock(s);
+    return ret;
 }
 
 static void vtd_init_iotlb_notifier(PCIBus *bus, void *opaque, int devfn,
-- 
2.54.0
RE: [PATCH v1] intel_iommu: Support concurrent page fault handling with PRI
Posted by Duan, Zhenzhong 2 weeks, 4 days ago

>-----Original Message-----
>From: Clément MATHIEU--DRIF <clement.mathieu--drif@bull.com>
>Subject: [PATCH v1] intel_iommu: Support concurrent page fault handling with PRI
>
>PRQ slot allocation must be thread safe to avoid posting multiple
>commands in the same slot so we take a lock in vtd_pri_request_page.
>
>This allows multiple devices to call the page request interface
>of the same iommu instance.

Aren't these devices' emulations serialized by BQL?

>
>Signed-off-by: Clement Mathieu--Drif <clement.mathieu--drif@bull.com>
>---
> hw/i386/intel_iommu.c | 91 +++++++++++++++++++++++++++----------------
> 1 file changed, 57 insertions(+), 34 deletions(-)
>
>diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
>index 9f21622a4f..350d2b7753 100644
>--- a/hw/i386/intel_iommu.c
>+++ b/hw/i386/intel_iommu.c
>@@ -3627,15 +3627,26 @@ static void vtd_handle_iectl_write(IntelIOMMUState
>*s)
>
> static void vtd_handle_prs_write(IntelIOMMUState *s)
> {
>-    uint32_t prs = vtd_get_long_raw(s, DMAR_PRS_REG);
>+    uint32_t prs;
>+
>+    vtd_iommu_lock(s);
>+
>+    prs = vtd_get_long_raw(s, DMAR_PRS_REG);
>     if (!(prs & VTD_PR_STATUS_PPR) && !(prs & VTD_PR_STATUS_PRO)) {
>         vtd_set_clear_mask_long(s, DMAR_PECTL_REG, VTD_PR_PECTL_IP, 0);
>     }
>+
>+    vtd_iommu_unlock(s);
> }
>
> static void vtd_handle_pectl_write(IntelIOMMUState *s)
> {
>-    uint32_t pectl = vtd_get_long_raw(s, DMAR_PECTL_REG);
>+    uint32_t pectl;
>+
>+    vtd_iommu_lock(s);
>+
>+    pectl = vtd_get_long_raw(s, DMAR_PECTL_REG);
>+
>     if ((pectl & VTD_PR_PECTL_IP) && !(pectl & VTD_PR_PECTL_IM)) {
>         /*
>          * If IP field was 1 when software clears the IM field,
>@@ -3644,6 +3655,8 @@ static void vtd_handle_pectl_write(IntelIOMMUState
>*s)
>         vtd_set_clear_mask_long(s, DMAR_PECTL_REG, VTD_PR_PECTL_IP, 0);
>         vtd_generate_interrupt(s, DMAR_PEADDR_REG, DMAR_PEDATA_REG);
>     }
>+
>+    vtd_iommu_unlock(s);
> }
>
> static uint64_t vtd_mem_read(void *opaque, hwaddr addr, unsigned size)
>@@ -5378,19 +5391,18 @@ static int vtd_pri_request_page(PCIBus *bus, void
>*opaque, int devfn,
> {
>     IntelIOMMUState *s = opaque;
>     VTDAddressSpace *vtd_as;
>+    uint64_t queue_addr_reg;
>+    uint64_t queue_tail_offset_reg;
>+    uint64_t new_queue_tail_offset;
>+    uint64_t queue_head_offset_reg;
>+    hwaddr queue_tail;
>+    uint32_t old_pr_status;
>+    uint16_t sid;
>+    VTDPRDesc desc;
>+    int ret = 0;
>
>     vtd_as = vtd_find_add_as(s, bus, devfn, pasid);
>-
>-    uint64_t queue_addr_reg = vtd_get_quad(s, DMAR_PQA_REG);
>-    uint64_t queue_tail_offset_reg = vtd_get_quad(s, DMAR_PQT_REG);
>-    uint64_t new_queue_tail_offset = (
>-                                (queue_tail_offset_reg + VTD_PQA_ENTRY_SIZE) %
>-                                (vtd_prq_size(s) * VTD_PQA_ENTRY_SIZE));
>-    uint64_t queue_head_offset_reg = vtd_get_quad(s, DMAR_PQH_REG);
>-    hwaddr queue_tail = (queue_addr_reg & VTD_PQA_ADDR) +
>queue_tail_offset_reg;
>-    uint32_t old_pr_status = vtd_get_long(s, DMAR_PRS_REG);
>-    uint16_t sid = PCI_BUILD_BDF(pci_bus_num(vtd_as->bus), vtd_as->devfn);
>-    VTDPRDesc desc;
>+    sid = PCI_BUILD_BDF(pci_bus_num(vtd_as->bus), vtd_as->devfn);
>
>     if (!(s->ecap & VTD_ECAP_PRS)) {
>         return -EPERM;
>@@ -5414,52 +5426,63 @@ static int vtd_pri_request_page(PCIBus *bus, void
>*opaque, int devfn,
>         return -EPERM;
>     }
>
>+    /* Prepare the descriptor */
>+    desc.lo = VTD_PRD_TYPE | VTD_PRD_PP(true) | VTD_PRD_RID(sid) |
>+              VTD_PRD_PASID(vtd_as->pasid) | VTD_PRD_PMR(priv_req);
>+    desc.hi = VTD_PRD_RDR(is_read) | VTD_PRD_WRR(is_write) |
>+              VTD_PRD_LPIG(lpig) | VTD_PRD_PRGI(prgi) | VTD_PRD_ADDR(addr);
>+
>+    desc.lo = cpu_to_le64(desc.lo);
>+    desc.hi = cpu_to_le64(desc.hi);
>+
>+    if (vtd_pri_perform_implicit_invalidation(vtd_as, addr)) {
>+        return -EINVAL;
>+    }
>+
>+    vtd_iommu_lock(s);
>+
>+    queue_addr_reg = vtd_get_quad(s, DMAR_PQA_REG);
>+    queue_tail_offset_reg = vtd_get_quad(s, DMAR_PQT_REG);
>+    new_queue_tail_offset = ((queue_tail_offset_reg + VTD_PQA_ENTRY_SIZE) %
>+                            (vtd_prq_size(s) * VTD_PQA_ENTRY_SIZE));
>+    queue_head_offset_reg = vtd_get_quad(s, DMAR_PQH_REG);
>+    queue_tail = (queue_addr_reg & VTD_PQA_ADDR) + queue_tail_offset_reg;
>+    old_pr_status = vtd_get_long(s, DMAR_PRS_REG);
>+
>     if (old_pr_status & VTD_PR_STATUS_PRO) {
>         /*
>          * No action is taken by hardware to report a fault
>          * or generate an event
>          */
>-        return -ENOSPC;
>+        ret = -ENOSPC;
>+        goto out;
>     }
>
>     /* Check for overflow */
>     if (new_queue_tail_offset == queue_head_offset_reg) {
>         vtd_set_clear_mask_long(s, DMAR_PRS_REG, 0, VTD_PR_STATUS_PRO);
>         vtd_generate_page_request_event(s, old_pr_status);
>-        return -ENOSPC;
>-    }
>-
>-    if (vtd_pri_perform_implicit_invalidation(vtd_as, addr)) {
>-        return -EINVAL;
>+        ret = -ENOSPC;
>+        goto out;
>     }
>
>-    desc.lo = VTD_PRD_TYPE | VTD_PRD_PP(true) | VTD_PRD_RID(sid) |
>-              VTD_PRD_PASID(vtd_as->pasid) | VTD_PRD_PMR(priv_req);
>-    desc.hi = VTD_PRD_RDR(is_read) | VTD_PRD_WRR(is_write) |
>-              VTD_PRD_LPIG(lpig) | VTD_PRD_PRGI(prgi) | VTD_PRD_ADDR(addr);
>-
>-    desc.lo = cpu_to_le64(desc.lo);
>-    desc.hi = cpu_to_le64(desc.hi);
>     if (dma_memory_write(&address_space_memory, queue_tail, &desc,
>sizeof(desc),
>                          MEMTXATTRS_UNSPECIFIED)) {
>         error_report_once("IO error, the PQ tail cannot be updated");
>-        return -EIO;
>+        ret = -EIO;
>+        goto out;
>     }
>
>     /* increment the tail register and set the pending request bit */
>     vtd_set_quad(s, DMAR_PQT_REG, new_queue_tail_offset);
>-    /*
>-     * read status again so that the kernel does not miss a request.
>-     * in some cases, we can trigger an unecessary interrupt but this strategy
>-     * drastically improves performance as we don't need to take a lock.
>-     */
>-    old_pr_status = vtd_get_long(s, DMAR_PRS_REG);
>     if (!(old_pr_status & VTD_PR_STATUS_PPR)) {
>         vtd_set_clear_mask_long(s, DMAR_PRS_REG, 0, VTD_PR_STATUS_PPR);
>         vtd_generate_page_request_event(s, old_pr_status);
>     }
>
>-    return 0;
>+out:
>+    vtd_iommu_unlock(s);
>+    return ret;
> }
>
> static void vtd_init_iotlb_notifier(PCIBus *bus, void *opaque, int devfn,
>--
>2.54.0
Re: [PATCH v1] intel_iommu: Support concurrent page fault handling with PRI
Posted by Clément MATHIEU--DRIF 2 weeks, 4 days ago
On Tue, 2026-09-08 at 07:01 +0000, Duan, Zhenzhong wrote:
> Caution: External email. Do not open attachments or click links, unless this email comes from a known sender and you know the content is safe.
> 
> 
> 
> > -----Original Message-----
> > From: Clément MATHIEU--DRIF <[clement.mathieu--drif@bull.com](mailto:clement.mathieu--drif@bull.com)>
> > Subject: [PATCH v1] intel_iommu: Support concurrent page fault handling with PRI
> > 
> > PRQ slot allocation must be thread safe to avoid posting multiple
> > commands in the same slot so we take a lock in vtd_pri_request_page.
> > 
> > This allows multiple devices to call the page request interface
> > of the same iommu instance.
> 
> 
> Aren't these devices' emulations serialized by BQL?
> 

Hi,

Not really, emulated devices calling the PRI interface  
are discouraged from taking the BQL to avoid deadlocks.  
A device's DMA module is likely to have an internal lock.  
The following concurrent paths can lead to deadlocks:

- DMA lock -> ... internal logic -> BQL lock + PRI request
- PRI response -> MMIO with BQL in VT-d -> PRI notifier -> DMA lock

DMA lock and BQL taken in reverse order.

Thanks,  
cmd

> 
> 
> > Signed-off-by: Clement Mathieu--Drif <[clement.mathieu--drif@bull.com](mailto:clement.mathieu--drif@bull.com)>
> > ---
> > hw/i386/intel_iommu.c | 91 +++++++++++++++++++++++++++----------------
> > 1 file changed, 57 insertions(+), 34 deletions(-)
> > 
> > diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
> > index 9f21622a4f..350d2b7753 100644
> > --- a/hw/i386/intel_iommu.c
> > +++ b/hw/i386/intel_iommu.c
> > @@ -3627,15 +3627,26 @@ static void vtd_handle_iectl_write(IntelIOMMUState
> > *s)
> > 
> > static void vtd_handle_prs_write(IntelIOMMUState *s)
> > {
> > -    uint32_t prs = vtd_get_long_raw(s, DMAR_PRS_REG);
> > +    uint32_t prs;
> > +
> > +    vtd_iommu_lock(s);
> > +
> > +    prs = vtd_get_long_raw(s, DMAR_PRS_REG);
> >     if (!(prs & VTD_PR_STATUS_PPR) && !(prs & VTD_PR_STATUS_PRO)) {
> >         vtd_set_clear_mask_long(s, DMAR_PECTL_REG, VTD_PR_PECTL_IP, 0);
> >     }
> > +
> > +    vtd_iommu_unlock(s);
> > }
> > 
> > static void vtd_handle_pectl_write(IntelIOMMUState *s)
> > {
> > -    uint32_t pectl = vtd_get_long_raw(s, DMAR_PECTL_REG);
> > +    uint32_t pectl;
> > +
> > +    vtd_iommu_lock(s);
> > +
> > +    pectl = vtd_get_long_raw(s, DMAR_PECTL_REG);
> > +
> >     if ((pectl & VTD_PR_PECTL_IP) && !(pectl & VTD_PR_PECTL_IM)) {
> >         /*
> >          * If IP field was 1 when software clears the IM field,
> > @@ -3644,6 +3655,8 @@ static void vtd_handle_pectl_write(IntelIOMMUState
> > *s)
> >         vtd_set_clear_mask_long(s, DMAR_PECTL_REG, VTD_PR_PECTL_IP, 0);
> >         vtd_generate_interrupt(s, DMAR_PEADDR_REG, DMAR_PEDATA_REG);
> >     }
> > +
> > +    vtd_iommu_unlock(s);
> > }
> > 
> > static uint64_t vtd_mem_read(void *opaque, hwaddr addr, unsigned size)
> > @@ -5378,19 +5391,18 @@ static int vtd_pri_request_page(PCIBus *bus, void
> > *opaque, int devfn,
> > {
> >     IntelIOMMUState *s = opaque;
> >     VTDAddressSpace *vtd_as;
> > +    uint64_t queue_addr_reg;
> > +    uint64_t queue_tail_offset_reg;
> > +    uint64_t new_queue_tail_offset;
> > +    uint64_t queue_head_offset_reg;
> > +    hwaddr queue_tail;
> > +    uint32_t old_pr_status;
> > +    uint16_t sid;
> > +    VTDPRDesc desc;
> > +    int ret = 0;
> > 
> >     vtd_as = vtd_find_add_as(s, bus, devfn, pasid);
> > -
> > -    uint64_t queue_addr_reg = vtd_get_quad(s, DMAR_PQA_REG);
> > -    uint64_t queue_tail_offset_reg = vtd_get_quad(s, DMAR_PQT_REG);
> > -    uint64_t new_queue_tail_offset = (
> > -                                (queue_tail_offset_reg + VTD_PQA_ENTRY_SIZE) %
> > -                                (vtd_prq_size(s) * VTD_PQA_ENTRY_SIZE));
> > -    uint64_t queue_head_offset_reg = vtd_get_quad(s, DMAR_PQH_REG);
> > -    hwaddr queue_tail = (queue_addr_reg & VTD_PQA_ADDR) +
> > queue_tail_offset_reg;
> > -    uint32_t old_pr_status = vtd_get_long(s, DMAR_PRS_REG);
> > -    uint16_t sid = PCI_BUILD_BDF(pci_bus_num(vtd_as->bus), vtd_as->devfn);
> > -    VTDPRDesc desc;
> > +    sid = PCI_BUILD_BDF(pci_bus_num(vtd_as->bus), vtd_as->devfn);
> > 
> >     if (!(s->ecap & VTD_ECAP_PRS)) {
> >         return -EPERM;
> > @@ -5414,52 +5426,63 @@ static int vtd_pri_request_page(PCIBus *bus, void
> > *opaque, int devfn,
> >         return -EPERM;
> >     }
> > 
> > +    /* Prepare the descriptor */
> > +    desc.lo = VTD_PRD_TYPE | VTD_PRD_PP(true) | VTD_PRD_RID(sid) |
> > +              VTD_PRD_PASID(vtd_as->pasid) | VTD_PRD_PMR(priv_req);
> > +    desc.hi = VTD_PRD_RDR(is_read) | VTD_PRD_WRR(is_write) |
> > +              VTD_PRD_LPIG(lpig) | VTD_PRD_PRGI(prgi) | VTD_PRD_ADDR(addr);
> > +
> > +    desc.lo = cpu_to_le64(desc.lo);
> > +    desc.hi = cpu_to_le64(desc.hi);
> > +
> > +    if (vtd_pri_perform_implicit_invalidation(vtd_as, addr)) {
> > +        return -EINVAL;
> > +    }
> > +
> > +    vtd_iommu_lock(s);
> > +
> > +    queue_addr_reg = vtd_get_quad(s, DMAR_PQA_REG);
> > +    queue_tail_offset_reg = vtd_get_quad(s, DMAR_PQT_REG);
> > +    new_queue_tail_offset = ((queue_tail_offset_reg + VTD_PQA_ENTRY_SIZE) %
> > +                            (vtd_prq_size(s) * VTD_PQA_ENTRY_SIZE));
> > +    queue_head_offset_reg = vtd_get_quad(s, DMAR_PQH_REG);
> > +    queue_tail = (queue_addr_reg & VTD_PQA_ADDR) + queue_tail_offset_reg;
> > +    old_pr_status = vtd_get_long(s, DMAR_PRS_REG);
> > +
> >     if (old_pr_status & VTD_PR_STATUS_PRO) {
> >         /*
> >          * No action is taken by hardware to report a fault
> >          * or generate an event
> >          */
> > -        return -ENOSPC;
> > +        ret = -ENOSPC;
> > +        goto out;
> >     }
> > 
> >     /* Check for overflow */
> >     if (new_queue_tail_offset == queue_head_offset_reg) {
> >         vtd_set_clear_mask_long(s, DMAR_PRS_REG, 0, VTD_PR_STATUS_PRO);
> >         vtd_generate_page_request_event(s, old_pr_status);
> > -        return -ENOSPC;
> > -    }
> > -
> > -    if (vtd_pri_perform_implicit_invalidation(vtd_as, addr)) {
> > -        return -EINVAL;
> > +        ret = -ENOSPC;
> > +        goto out;
> >     }
> > 
> > -    desc.lo = VTD_PRD_TYPE | VTD_PRD_PP(true) | VTD_PRD_RID(sid) |
> > -              VTD_PRD_PASID(vtd_as->pasid) | VTD_PRD_PMR(priv_req);
> > -    desc.hi = VTD_PRD_RDR(is_read) | VTD_PRD_WRR(is_write) |
> > -              VTD_PRD_LPIG(lpig) | VTD_PRD_PRGI(prgi) | VTD_PRD_ADDR(addr);
> > -
> > -    desc.lo = cpu_to_le64(desc.lo);
> > -    desc.hi = cpu_to_le64(desc.hi);
> >     if (dma_memory_write(&address_space_memory, queue_tail, &desc,
> > sizeof(desc),
> >                          MEMTXATTRS_UNSPECIFIED)) {
> >         error_report_once("IO error, the PQ tail cannot be updated");
> > -        return -EIO;
> > +        ret = -EIO;
> > +        goto out;
> >     }
> > 
> >     /* increment the tail register and set the pending request bit */
> >     vtd_set_quad(s, DMAR_PQT_REG, new_queue_tail_offset);
> > -    /*
> > -     * read status again so that the kernel does not miss a request.
> > -     * in some cases, we can trigger an unecessary interrupt but this strategy
> > -     * drastically improves performance as we don't need to take a lock.
> > -     */
> > -    old_pr_status = vtd_get_long(s, DMAR_PRS_REG);
> >     if (!(old_pr_status & VTD_PR_STATUS_PPR)) {
> >         vtd_set_clear_mask_long(s, DMAR_PRS_REG, 0, VTD_PR_STATUS_PPR);
> >         vtd_generate_page_request_event(s, old_pr_status);
> >     }
> > 
> > -    return 0;
> > +out:
> > +    vtd_iommu_unlock(s);
> > +    return ret;
> > }
> > 
> > static void vtd_init_iotlb_notifier(PCIBus *bus, void *opaque, int devfn,
> > --
> > 2.54.0
> 
RE: [PATCH v1] intel_iommu: Support concurrent page fault handling with PRI
Posted by Duan, Zhenzhong 2 weeks, 4 days ago

>-----Original Message-----
>From: Clément MATHIEU--DRIF <clement.mathieu--drif@bull.com>
>Subject: Re: [PATCH v1] intel_iommu: Support concurrent page fault handling with
>PRI
>
>
>On Tue, 2026-09-08 at 07:01 +0000, Duan, Zhenzhong wrote:
>> Caution: External email. Do not open attachments or click links, unless this email
>comes from a known sender and you know the content is safe.
>>
>>
>>
>> > -----Original Message-----
>> > From: Clément MATHIEU--DRIF <[clement.mathieu--
>drif@bull.com](mailto:clement.mathieu--drif@bull.com)>
>> > Subject: [PATCH v1] intel_iommu: Support concurrent page fault handling with
>PRI
>> >
>> > PRQ slot allocation must be thread safe to avoid posting multiple
>> > commands in the same slot so we take a lock in vtd_pri_request_page.
>> >
>> > This allows multiple devices to call the page request interface
>> > of the same iommu instance.
>>
>>
>> Aren't these devices' emulations serialized by BQL?
>>
>
>Hi,
>
>Not really, emulated devices calling the PRI interface
>are discouraged from taking the BQL to avoid deadlocks.
>A device's DMA module is likely to have an internal lock.
>The following concurrent paths can lead to deadlocks:
>
>- DMA lock -> ... internal logic -> BQL lock + PRI request

I see BQL is taken in prepare_mmio_access(), isn't DMA lock taken
after calling prepare_mmio_access()?

>- PRI response -> MMIO with BQL in VT-d -> PRI notifier -> DMA lock
>
>DMA lock and BQL taken in reverse order.