QI interrupt handler and QI startup. If the IWC field was already
Set at the time of setting this field, it is not treated as a new
interrupt conditions.
In QI interrupt handler, Check IP field of Invalidation Event Control
register after scan domain status. if IP field is Set, scan agian,
instead of generating another interrupt. then, Clear IM fild of
Invalidation Event Control Register for no masking of QI interrupt.

Signed-off-by: Quan Xu <quan...@intel.com>
---
 xen/drivers/passthrough/vtd/iommu.c | 59 +++++++++++++++++++++++++++++++++++++
 xen/drivers/passthrough/vtd/iommu.h |  6 ++++
 2 files changed, 65 insertions(+)

diff --git a/xen/drivers/passthrough/vtd/iommu.c 
b/xen/drivers/passthrough/vtd/iommu.c
index 0e912fb..e3acea5 100644
--- a/xen/drivers/passthrough/vtd/iommu.c
+++ b/xen/drivers/passthrough/vtd/iommu.c
@@ -1070,6 +1070,27 @@ static hw_irq_controller dma_msi_type = {
 };
 
 /* IOMMU Queued Invalidation(QI). */
+static void qi_clear_iwc(struct iommu *iommu)
+{
+    unsigned long flags;
+
+    spin_lock_irqsave(&iommu->register_lock, flags);
+    dmar_writel(iommu->reg, DMAR_ICS_REG, RW1CS);
+    spin_unlock_irqrestore(&iommu->register_lock, flags);
+}
+
+static int _qi_msi_ip(struct iommu *iommu)
+{
+    u32 sts;
+    unsigned long flags;
+
+    /* Get IP bit of DMAR_IECTL_REG. */
+    spin_lock_irqsave(&iommu->register_lock, flags);
+    sts = dmar_readl(iommu->reg, DMAR_IECTL_REG);
+    spin_unlock_irqrestore(&iommu->register_lock, flags);
+    return (sts & DMA_IECTL_IP);
+}
+
 static void _qi_msi_unmask(struct iommu *iommu)
 {
     u32 sts;
@@ -1101,6 +1122,14 @@ static void _do_iommu_qi(struct iommu *iommu)
     unsigned long nr_dom, i;
     struct domain *d = NULL;
 
+scan_again:
+    /*
+     * If the IWC field in the Invalidation Completion Status register was 
already
+     * Set at the time of setting this field, it is not treated as a new 
interrupt
+     * condition.
+     */
+    qi_clear_iwc(iommu);
+
     nr_dom = cap_ndoms(iommu->cap);
     i = find_first_bit(iommu->domid_bitmap, nr_dom);
     while ( i < nr_dom )
@@ -1120,6 +1149,28 @@ static void _do_iommu_qi(struct iommu *iommu)
         }
         i = find_next_bit(iommu->domid_bitmap, nr_dom, i+1);
     }
+
+    /*
+     * IP is interrupt pending and the 30 bit of Invalidation Event Control
+     * Register. The IP field is kept Set by hardware while the interrupt
+     * message is held pending. The IP field is cleared by hardware as soon
+     * as the interrupt message pending condition  is serviced. IP could be
+     * cleard due to either:
+     *
+     * - Clear IM field in the Invalidation Event Control Register. A QI
+     *   interrupt is generated along with clearing the IP field.
+     * - Clear IWC field in the Invalidateion Coompletion Status register.
+     *
+     * If the Ip is Set, scan agian, instead of generating another interrupt.
+     */
+    if ( _qi_msi_ip(iommu) )
+        goto scan_again;
+
+    /*
+     * No masking of QI interrupt. when a QI interrupt event condition is
+     * detected, hardware issues an interrupt message.
+     */
+    _qi_msi_unmask(iommu);
 }
 
 static void do_iommu_qi_completion(unsigned long data)
@@ -1154,6 +1205,14 @@ static void qi_msi_mask(struct irq_desc *desc)
 
 static unsigned int qi_msi_startup(struct irq_desc *desc)
 {
+    struct iommu *iommu = desc->action->dev_id;
+
+    /*
+     * If the IWC field in the Invalidation Completion Status register was 
already
+     * Set at the time of setting this field, it is not treated as a new 
interrupt
+     * condition.
+     */
+    qi_clear_iwc(iommu);
     qi_msi_unmask(desc);
     return 0;
 }
diff --git a/xen/drivers/passthrough/vtd/iommu.h 
b/xen/drivers/passthrough/vtd/iommu.h
index f2ee56d..e6278ee 100644
--- a/xen/drivers/passthrough/vtd/iommu.h
+++ b/xen/drivers/passthrough/vtd/iommu.h
@@ -54,6 +54,11 @@
 #define    DMAR_ICS_REG    0x9C    /* invalidation completion status register 
*/
 #define    DMAR_IRTA_REG   0xB8    /* intr remap */
 
+/*
+ * Register Attributes.
+ */
+#define RW1CS  1  /* A status may be cleard by writing a 1. */
+
 #define OFFSET_STRIDE        (9)
 #define dmar_readl(dmar, reg) readl((dmar) + (reg))
 #define dmar_readq(dmar, reg) readq((dmar) + (reg))
@@ -172,6 +177,7 @@
 
 /* IECTL_REG */
 #define DMA_IECTL_IM (((u64)1) << 31)
+#define DMA_IECTL_IP (((u64)1) << 30)
 
 
 /* FSTS_REG */
-- 
1.8.3.2


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
http://lists.xen.org/xen-devel

Reply via email to