From: <suravee.suthikulpanit-5C7GfCeVMHo@public.gmane.org>
To: iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org,
joro-zLv9SwRftAIdnm+yROfE0A@public.gmane.org
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Subject: [PATCH 1/2 V2] iommu/amd: Add workaround for ERBT1312
Date: Mon, 15 Apr 2013 02:07:46 -0500 [thread overview]
Message-ID: <1366009666-44792-1-git-send-email-suravee.suthikulpanit@amd.com> (raw)
From: Suravee Suthikulpanit <suravee.suthikulpanit-5C7GfCeVMHo@public.gmane.org>
The IOMMU interrupt handling in bottom half must clear the PPR log interrupt
and event log interrupt bits to re-enable the interrupt. This is done by
writing 1 to the memory mapped register to clear the bit. Due to hardware bug,
if the driver tries to clear this bit while the IOMMU hardware also setting
this bit, the conflict will result with the bit being set. If the interrupt
handling code does not make sure to clear this bit, subsequent changes in the
event/PPR logs will no longer generating interrupts, and would result if
buffer overflow. After clearing the bits, the driver must read back
the register to verify.
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit-5C7GfCeVMHo@public.gmane.org>
---
drivers/iommu/amd_iommu.c | 145 +++++++++++++++++++++++++++++----------------
1 file changed, 93 insertions(+), 52 deletions(-)
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index e5db3e5..419af1d 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -803,22 +803,43 @@ retry:
static void iommu_poll_events(struct amd_iommu *iommu)
{
u32 head, tail;
+ u32 status;
unsigned long flags;
- /* enable event interrupts again */
- writel(MMIO_STATUS_EVT_INT_MASK, iommu->mmio_base + MMIO_STATUS_OFFSET);
-
spin_lock_irqsave(&iommu->lock, flags);
- head = readl(iommu->mmio_base + MMIO_EVT_HEAD_OFFSET);
- tail = readl(iommu->mmio_base + MMIO_EVT_TAIL_OFFSET);
+ status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET);
- while (head != tail) {
- iommu_handle_event(iommu, iommu->evt_buf + head);
- head = (head + EVENT_ENTRY_SIZE) % iommu->evt_buf_size;
- }
+ while (status & MMIO_STATUS_EVT_INT_MASK) {
+
+ /* enable event interrupts again */
+ writel(MMIO_STATUS_EVT_INT_MASK,
+ iommu->mmio_base + MMIO_STATUS_OFFSET);
- writel(head, iommu->mmio_base + MMIO_EVT_HEAD_OFFSET);
+ head = readl(iommu->mmio_base + MMIO_EVT_HEAD_OFFSET);
+ tail = readl(iommu->mmio_base + MMIO_EVT_TAIL_OFFSET);
+
+ while (head != tail) {
+ iommu_handle_event(iommu, iommu->evt_buf + head);
+ head = (head + EVENT_ENTRY_SIZE) % iommu->evt_buf_size;
+ }
+
+ writel(head, iommu->mmio_base + MMIO_EVT_HEAD_OFFSET);
+
+ /*
+ * Hardware bug: When re-enabling interrupt (by writing 1
+ * to clear the bit), the hardware might also try to set
+ * the interrupt bit in the event status register.
+ * In this scenario, the bit will be set, and disable
+ * subsequent interrupts.
+ *
+ * Workaround: The IOMMU driver should read back the
+ * status register and check if the interrupt bits are cleared.
+ * If not, driver will need to go through the interrupt handler
+ * again and re-clear the bits
+ */
+ status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET);
+ }
spin_unlock_irqrestore(&iommu->lock, flags);
}
@@ -846,65 +867,85 @@ static void iommu_handle_ppr_entry(struct amd_iommu *iommu, u64 *raw)
static void iommu_poll_ppr_log(struct amd_iommu *iommu)
{
unsigned long flags;
+ u32 status;
u32 head, tail;
if (iommu->ppr_log == NULL)
return;
- /* enable ppr interrupts again */
- writel(MMIO_STATUS_PPR_INT_MASK, iommu->mmio_base + MMIO_STATUS_OFFSET);
-
spin_lock_irqsave(&iommu->lock, flags);
- head = readl(iommu->mmio_base + MMIO_PPR_HEAD_OFFSET);
- tail = readl(iommu->mmio_base + MMIO_PPR_TAIL_OFFSET);
+ status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET);
- while (head != tail) {
- volatile u64 *raw;
- u64 entry[2];
- int i;
+ while (status & MMIO_STATUS_PPR_INT_MASK) {
+ /* enable ppr interrupts again */
+ writel(MMIO_STATUS_PPR_INT_MASK,
+ iommu->mmio_base + MMIO_STATUS_OFFSET);
- raw = (u64 *)(iommu->ppr_log + head);
+ head = readl(iommu->mmio_base + MMIO_PPR_HEAD_OFFSET);
+ tail = readl(iommu->mmio_base + MMIO_PPR_TAIL_OFFSET);
- /*
- * Hardware bug: Interrupt may arrive before the entry is
- * written to memory. If this happens we need to wait for the
- * entry to arrive.
- */
- for (i = 0; i < LOOP_TIMEOUT; ++i) {
- if (PPR_REQ_TYPE(raw[0]) != 0)
- break;
- udelay(1);
- }
+ while (head != tail) {
+ volatile u64 *raw;
+ u64 entry[2];
+ int i;
- /* Avoid memcpy function-call overhead */
- entry[0] = raw[0];
- entry[1] = raw[1];
+ raw = (u64 *)(iommu->ppr_log + head);
- /*
- * To detect the hardware bug we need to clear the entry
- * back to zero.
- */
- raw[0] = raw[1] = 0UL;
+ /*
+ * Hardware bug: Interrupt may arrive before the entry
+ * is written to memory. If this happens we need to wait
+ * for the entry to arrive.
+ */
+ for (i = 0; i < LOOP_TIMEOUT; ++i) {
+ if (PPR_REQ_TYPE(raw[0]) != 0)
+ break;
+ udelay(1);
+ }
- /* Update head pointer of hardware ring-buffer */
- head = (head + PPR_ENTRY_SIZE) % PPR_LOG_SIZE;
- writel(head, iommu->mmio_base + MMIO_PPR_HEAD_OFFSET);
+ /* Avoid memcpy function-call overhead */
+ entry[0] = raw[0];
+ entry[1] = raw[1];
- /*
- * Release iommu->lock because ppr-handling might need to
- * re-acquire it
- */
- spin_unlock_irqrestore(&iommu->lock, flags);
+ /*
+ * To detect the hardware bug we need to clear the entry
+ * back to zero.
+ */
+ raw[0] = raw[1] = 0UL;
+
+ /* Update head pointer of hardware ring-buffer */
+ head = (head + PPR_ENTRY_SIZE) % PPR_LOG_SIZE;
+ writel(head, iommu->mmio_base + MMIO_PPR_HEAD_OFFSET);
- /* Handle PPR entry */
- iommu_handle_ppr_entry(iommu, entry);
+ /*
+ * Release iommu->lock because ppr-handling might need
+ * to re-acquire it
+ */
+ spin_unlock_irqrestore(&iommu->lock, flags);
- spin_lock_irqsave(&iommu->lock, flags);
+ /* Handle PPR entry */
+ iommu_handle_ppr_entry(iommu, entry);
- /* Refresh ring-buffer information */
- head = readl(iommu->mmio_base + MMIO_PPR_HEAD_OFFSET);
- tail = readl(iommu->mmio_base + MMIO_PPR_TAIL_OFFSET);
+ spin_lock_irqsave(&iommu->lock, flags);
+
+ /* Refresh ring-buffer information */
+ head = readl(iommu->mmio_base + MMIO_PPR_HEAD_OFFSET);
+ tail = readl(iommu->mmio_base + MMIO_PPR_TAIL_OFFSET);
+ }
+
+ /*
+ * Hardware bug: When re-enabling interrupt (by writing 1
+ * to clear the bit), the hardware might also try to set
+ * the interrupt bit in the event status register.
+ * In this scenario, the bit will be set, and disable
+ * subsequent interrupts.
+ *
+ * Workaround: The IOMMU driver should read back the
+ * status register and check if the interrupt bits are cleared.
+ * If not, driver will need to go through the interrupt handler
+ * again and re-clear the bits
+ */
+ status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET);
}
spin_unlock_irqrestore(&iommu->lock, flags);
--
1.7.10.4
next reply other threads:[~2013-04-15 7:07 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-04-15 7:07 suravee.suthikulpanit-5C7GfCeVMHo [this message]
2013-04-18 16:02 ` [PATCH 1/2 V2] iommu/amd: Add workaround for ERBT1312 Joerg Roedel
2013-04-18 16:13 ` Suravee Suthikulanit
[not found] ` <51701B9F.10003-5C7GfCeVMHo@public.gmane.org>
2013-04-18 16:28 ` Joerg Roedel
[not found] ` <20130418162856.GA13891-zLv9SwRftAIdnm+yROfE0A@public.gmane.org>
2013-04-18 16:59 ` Suravee Suthikulanit
[not found] ` <5170268E.8080706-5C7GfCeVMHo@public.gmane.org>
2013-04-18 18:35 ` Joerg Roedel
[not found] ` <20130418183538.GA17148-zLv9SwRftAIdnm+yROfE0A@public.gmane.org>
2013-04-18 18:56 ` Suravee Suthikulpanit
[not found] ` <517041EA.70407-5C7GfCeVMHo@public.gmane.org>
2013-04-18 20:06 ` Joerg Roedel
[not found] ` <20130418200613.GB17148-zLv9SwRftAIdnm+yROfE0A@public.gmane.org>
2013-04-22 21:43 ` Suravee Suthikulanit
2013-04-23 13:22 ` Don Dutile
[not found] ` <51768B25.1060501-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2013-04-24 10:46 ` Joerg Roedel
[not found] ` <20130424104616.GH17148-zLv9SwRftAIdnm+yROfE0A@public.gmane.org>
2013-04-24 13:52 ` Don Dutile
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1366009666-44792-1-git-send-email-suravee.suthikulpanit@amd.com \
--to=suravee.suthikulpanit-5c7gfcevmho@public.gmane.org \
--cc=iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org \
--cc=joro-zLv9SwRftAIdnm+yROfE0A@public.gmane.org \
--cc=linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).