git: 7f46deccbed7 - main - x86/iommu: Reduce the number of queued invalidation interrupts
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sat, 06 Aug 2022 18:06:34 UTC
The branch main has been updated by alc:
URL: https://cgit.FreeBSD.org/src/commit/?id=7f46deccbed74436b62f8fd02655ff4ad89f1023
commit 7f46deccbed74436b62f8fd02655ff4ad89f1023
Author: Alan Cox <alc@FreeBSD.org>
AuthorDate: 2022-07-31 19:28:30 +0000
Commit: Alan Cox <alc@FreeBSD.org>
CommitDate: 2022-08-06 18:05:58 +0000
x86/iommu: Reduce the number of queued invalidation interrupts
Restructure dmar_qi_task() so as to reduce the number of invalidation
completion interrupts. Specifically, because processing completed
invalidations in dmar_qi_task() can take quite some time, don't reenable
completion interrupts until processing has completed a first time. Then,
check a second time after reenabling completion interrupts, so that
any invalidations that complete just before interrupts are reenabled
do not linger until a future invalidation might raise an interrupt.
(Recent changes have made checking for completed invalidations cheap; no
locking is required.)
Reviewed by: kib
MFC after: 1 week
Differential Revision: https://reviews.freebsd.org/D36054
---
sys/x86/iommu/intel_qi.c | 45 +++++++++++++++++++++++++++++----------------
1 file changed, 29 insertions(+), 16 deletions(-)
diff --git a/sys/x86/iommu/intel_qi.c b/sys/x86/iommu/intel_qi.c
index baaf5b472a2c..8a8e656083e3 100644
--- a/sys/x86/iommu/intel_qi.c
+++ b/sys/x86/iommu/intel_qi.c
@@ -411,14 +411,34 @@ dmar_qi_intr(void *arg)
return (FILTER_HANDLED);
}
+static void
+dmar_qi_drain_tlb_flush(struct dmar_unit *unit)
+{
+ struct iommu_map_entry *entry, *head;
+
+ for (head = unit->tlb_flush_head;; head = entry) {
+ entry = (struct iommu_map_entry *)
+ atomic_load_acq_ptr((uintptr_t *)&head->tlb_flush_next);
+ if (entry == NULL ||
+ !dmar_qi_seq_processed(unit, &entry->gseq))
+ break;
+ unit->tlb_flush_head = entry;
+ iommu_gas_free_entry(head);
+ if ((entry->flags & IOMMU_MAP_ENTRY_RMRR) != 0)
+ iommu_gas_free_region(entry);
+ else
+ iommu_gas_free_space(entry);
+ }
+}
+
static void
dmar_qi_task(void *arg, int pending __unused)
{
struct dmar_unit *unit;
- struct iommu_map_entry *entry, *head;
uint32_t ics;
unit = arg;
+ dmar_qi_drain_tlb_flush(unit);
/*
* Request an interrupt on the completion of the next invalidation
@@ -428,23 +448,16 @@ dmar_qi_task(void *arg, int pending __unused)
if ((ics & DMAR_ICS_IWC) != 0) {
ics = DMAR_ICS_IWC;
dmar_write4(unit, DMAR_ICS_REG, ics);
- }
- for (;;) {
- head = unit->tlb_flush_head;
- entry = (struct iommu_map_entry *)
- atomic_load_acq_ptr((uintptr_t *)&head->tlb_flush_next);
- if (entry == NULL)
- break;
- if (!dmar_qi_seq_processed(unit, &entry->gseq))
- break;
- unit->tlb_flush_head = entry;
- iommu_gas_free_entry(head);
- if ((entry->flags & IOMMU_MAP_ENTRY_RMRR) != 0)
- iommu_gas_free_region(entry);
- else
- iommu_gas_free_space(entry);
+ /*
+ * Drain a second time in case the DMAR processes an entry
+ * after the first call and before clearing DMAR_ICS_IWC.
+ * Otherwise, such entries will linger until a later entry
+ * that requests an interrupt is processed.
+ */
+ dmar_qi_drain_tlb_flush(unit);
}
+
if (unit->inv_seq_waiters > 0) {
/*
* Acquire the DMAR lock so that wakeup() is called only after