Changeset View
Changeset View
Standalone View
Standalone View
sys/x86/iommu/intel_qi.c
Show First 20 Lines • Show All 91 Lines • ▼ Show 20 Lines | |||||
unit = arg; | unit = arg; | ||||
KASSERT(unit->qi_enabled, ("dmar%d: QI is not enabled", | KASSERT(unit->qi_enabled, ("dmar%d: QI is not enabled", | ||||
unit->iommu.unit)); | unit->iommu.unit)); | ||||
taskqueue_enqueue(unit->qi_taskqueue, &unit->qi_task); | taskqueue_enqueue(unit->qi_taskqueue, &unit->qi_task); | ||||
return (FILTER_HANDLED); | 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 | static void | ||||
dmar_qi_task(void *arg, int pending __unused) | dmar_qi_task(void *arg, int pending __unused) | ||||
{ | { | ||||
struct dmar_unit *unit; | struct dmar_unit *unit; | ||||
struct iommu_map_entry *entry, *head; | |||||
uint32_t ics; | uint32_t ics; | ||||
unit = arg; | unit = arg; | ||||
dmar_qi_drain_tlb_flush(unit); | |||||
/* | /* | ||||
* Request an interrupt on the completion of the next invalidation | * Request an interrupt on the completion of the next invalidation | ||||
* wait descriptor with the IF field set. | * wait descriptor with the IF field set. | ||||
*/ | */ | ||||
ics = dmar_read4(unit, DMAR_ICS_REG); | ics = dmar_read4(unit, DMAR_ICS_REG); | ||||
if ((ics & DMAR_ICS_IWC) != 0) { | if ((ics & DMAR_ICS_IWC) != 0) { | ||||
ics = DMAR_ICS_IWC; | ics = DMAR_ICS_IWC; | ||||
dmar_write4(unit, DMAR_ICS_REG, ics); | dmar_write4(unit, DMAR_ICS_REG, ics); | ||||
} | |||||
for (;;) { | /* | ||||
head = unit->tlb_flush_head; | * Drain a second time in case the DMAR processes an entry | ||||
entry = (struct iommu_map_entry *) | * after the first call and before clearing DMAR_ICS_IWC. | ||||
atomic_load_acq_ptr((uintptr_t *)&head->tlb_flush_next); | * Otherwise, such entries will linger until a later entry | ||||
if (entry == NULL) | * that requests an interrupt is processed. | ||||
break; | */ | ||||
if (!dmar_qi_seq_processed(unit, &entry->gseq)) | dmar_qi_drain_tlb_flush(unit); | ||||
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); | |||||
} | } | ||||
if (unit->inv_seq_waiters > 0) { | if (unit->inv_seq_waiters > 0) { | ||||
/* | /* | ||||
* Acquire the DMAR lock so that wakeup() is called only after | * Acquire the DMAR lock so that wakeup() is called only after | ||||
* the waiter is sleeping. | * the waiter is sleeping. | ||||
*/ | */ | ||||
DMAR_LOCK(unit); | DMAR_LOCK(unit); | ||||
wakeup(&unit->inv_seq_waiters); | wakeup(&unit->inv_seq_waiters); | ||||
DMAR_UNLOCK(unit); | DMAR_UNLOCK(unit); | ||||
▲ Show 20 Lines • Show All 91 Lines • Show Last 20 Lines |