Changeset View
Changeset View
Standalone View
Standalone View
sys/x86/iommu/intel_qi.c
Show First 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | |||||
#include <dev/iommu/busdma_iommu.h> | #include <dev/iommu/busdma_iommu.h> | ||||
#include <x86/iommu/intel_reg.h> | #include <x86/iommu/intel_reg.h> | ||||
#include <x86/iommu/intel_dmar.h> | #include <x86/iommu/intel_dmar.h> | ||||
static bool | static bool | ||||
dmar_qi_seq_processed(const struct dmar_unit *unit, | dmar_qi_seq_processed(const struct dmar_unit *unit, | ||||
const struct iommu_qi_genseq *pseq) | const struct iommu_qi_genseq *pseq) | ||||
{ | { | ||||
u_int gen; | |||||
return (pseq->gen < unit->inv_waitd_gen || | gen = unit->inv_waitd_gen; | ||||
(pseq->gen == unit->inv_waitd_gen && | return (pseq->gen < gen || | ||||
pseq->seq <= unit->inv_waitd_seq_hw)); | (pseq->gen == gen && pseq->seq <= unit->inv_waitd_seq_hw)); | ||||
} | } | ||||
static int | static int | ||||
dmar_enable_qi(struct dmar_unit *unit) | dmar_enable_qi(struct dmar_unit *unit) | ||||
{ | { | ||||
int error; | int error; | ||||
DMAR_ASSERT_LOCKED(unit); | DMAR_ASSERT_LOCKED(unit); | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | if (bytes <= unit->inv_queue_avail) | ||||
break; | break; | ||||
/* | /* | ||||
* No space in the queue, do busy wait. Hardware must | * No space in the queue, do busy wait. Hardware must | ||||
* make a progress. But first advance the tail to | * make a progress. But first advance the tail to | ||||
* inform the descriptor streamer about entries we | * inform the descriptor streamer about entries we | ||||
* might have already filled, otherwise they could | * might have already filled, otherwise they could | ||||
* clog the whole queue.. | * clog the whole queue.. | ||||
* | |||||
* See dmar_qi_invalidate_locked() for a discussion | |||||
* about data race prevention. | |||||
*/ | */ | ||||
dmar_qi_advance_tail(unit); | dmar_qi_advance_tail(unit); | ||||
unit->inv_queue_full++; | unit->inv_queue_full++; | ||||
cpu_spinwait(); | cpu_spinwait(); | ||||
} | } | ||||
unit->inv_queue_avail -= bytes; | unit->inv_queue_avail -= bytes; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | dmar_qi_emit_wait_seq(struct dmar_unit *unit, struct iommu_qi_genseq *pseq, | ||||
pseq->gen = unit->inv_waitd_gen; | pseq->gen = unit->inv_waitd_gen; | ||||
pseq->seq = seq; | pseq->seq = seq; | ||||
if (emit_wait) { | if (emit_wait) { | ||||
dmar_qi_ensure(unit, 1); | dmar_qi_ensure(unit, 1); | ||||
dmar_qi_emit_wait_descr(unit, seq, true, true, false); | dmar_qi_emit_wait_descr(unit, seq, true, true, false); | ||||
} | } | ||||
} | } | ||||
/* | |||||
* To avoid missed wakeups, callers must increment the unit's waiters count | |||||
* before advancing the tail past the wait descriptor. | |||||
*/ | |||||
static void | static void | ||||
dmar_qi_wait_for_seq(struct dmar_unit *unit, const struct iommu_qi_genseq *gseq, | dmar_qi_wait_for_seq(struct dmar_unit *unit, const struct iommu_qi_genseq *gseq, | ||||
bool nowait) | bool nowait) | ||||
{ | { | ||||
DMAR_ASSERT_LOCKED(unit); | DMAR_ASSERT_LOCKED(unit); | ||||
unit->inv_seq_waiters++; | KASSERT(unit->inv_seq_waiters > 0, ("%s: no waiters", __func__)); | ||||
while (!dmar_qi_seq_processed(unit, gseq)) { | while (!dmar_qi_seq_processed(unit, gseq)) { | ||||
if (cold || nowait) { | if (cold || nowait) { | ||||
cpu_spinwait(); | cpu_spinwait(); | ||||
} else { | } else { | ||||
msleep(&unit->inv_seq_waiters, &unit->iommu.lock, 0, | msleep(&unit->inv_seq_waiters, &unit->iommu.lock, 0, | ||||
"dmarse", hz); | "dmarse", hz); | ||||
} | } | ||||
} | } | ||||
unit->inv_seq_waiters--; | unit->inv_seq_waiters--; | ||||
} | } | ||||
void | static void | ||||
dmar_qi_invalidate_locked(struct dmar_domain *domain, iommu_gaddr_t base, | dmar_qi_invalidate_emit(struct dmar_domain *domain, iommu_gaddr_t base, | ||||
iommu_gaddr_t size, struct iommu_qi_genseq *pseq, bool emit_wait) | iommu_gaddr_t size, struct iommu_qi_genseq *pseq, bool emit_wait) | ||||
{ | { | ||||
struct dmar_unit *unit; | struct dmar_unit *unit; | ||||
iommu_gaddr_t isize; | iommu_gaddr_t isize; | ||||
int am; | int am; | ||||
unit = domain->dmar; | unit = domain->dmar; | ||||
DMAR_ASSERT_LOCKED(unit); | DMAR_ASSERT_LOCKED(unit); | ||||
for (; size > 0; base += isize, size -= isize) { | for (; size > 0; base += isize, size -= isize) { | ||||
am = calc_am(unit, base, size, &isize); | am = calc_am(unit, base, size, &isize); | ||||
dmar_qi_ensure(unit, 1); | dmar_qi_ensure(unit, 1); | ||||
dmar_qi_emit(unit, DMAR_IQ_DESCR_IOTLB_INV | | dmar_qi_emit(unit, DMAR_IQ_DESCR_IOTLB_INV | | ||||
DMAR_IQ_DESCR_IOTLB_PAGE | DMAR_IQ_DESCR_IOTLB_DW | | DMAR_IQ_DESCR_IOTLB_PAGE | DMAR_IQ_DESCR_IOTLB_DW | | ||||
DMAR_IQ_DESCR_IOTLB_DR | | DMAR_IQ_DESCR_IOTLB_DR | | ||||
DMAR_IQ_DESCR_IOTLB_DID(domain->domain), | DMAR_IQ_DESCR_IOTLB_DID(domain->domain), | ||||
base | am); | base | am); | ||||
} | } | ||||
dmar_qi_emit_wait_seq(unit, pseq, emit_wait); | dmar_qi_emit_wait_seq(unit, pseq, emit_wait); | ||||
} | |||||
/* | |||||
* The caller must not be using the entry's dmamap_link field. | |||||
*/ | |||||
void | |||||
dmar_qi_invalidate_locked(struct dmar_domain *domain, | |||||
struct iommu_map_entry *entry, bool emit_wait) | |||||
{ | |||||
struct dmar_unit *unit; | |||||
unit = domain->dmar; | |||||
DMAR_ASSERT_LOCKED(unit); | |||||
dmar_qi_invalidate_emit(domain, entry->start, entry->end - | |||||
entry->start, &entry->gseq, emit_wait); | |||||
/* | |||||
* To avoid a data race in dmar_qi_task(), the entry's gseq must be | |||||
* initialized before the entry is added to the TLB flush list, and the | |||||
* entry must be added to that list before the tail is advanced. More | |||||
* precisely, the tail must not be advanced past the wait descriptor | |||||
* that will generate the interrupt that schedules dmar_qi_task() for | |||||
* execution before the entry is added to the list. While an earlier | |||||
* call to dmar_qi_ensure() might have advanced the tail, it will not | |||||
* advance it past the wait descriptor. | |||||
* | |||||
* See the definition of struct dmar_unit for more information on | |||||
* synchronization. | |||||
kib: So tlb_flush_tail entry cannot be freed before or while we are storing to it's tlb_flush_next… | |||||
Done Inline ActionsYou are correct. alc: You are correct. | |||||
*/ | |||||
entry->tlb_flush_next = NULL; | |||||
atomic_store_rel_ptr((uintptr_t *)&unit->tlb_flush_tail->tlb_flush_next, | |||||
(uintptr_t)entry); | |||||
unit->tlb_flush_tail = entry; | |||||
dmar_qi_advance_tail(unit); | dmar_qi_advance_tail(unit); | ||||
} | } | ||||
void | void | ||||
dmar_qi_invalidate_sync(struct dmar_domain *domain, iommu_gaddr_t base, | dmar_qi_invalidate_sync(struct dmar_domain *domain, iommu_gaddr_t base, | ||||
iommu_gaddr_t size, bool cansleep) | iommu_gaddr_t size, bool cansleep) | ||||
{ | { | ||||
struct dmar_unit *unit; | struct dmar_unit *unit; | ||||
struct iommu_qi_genseq gseq; | struct iommu_qi_genseq gseq; | ||||
unit = domain->dmar; | unit = domain->dmar; | ||||
DMAR_LOCK(unit); | DMAR_LOCK(unit); | ||||
dmar_qi_invalidate_locked(domain, base, size, &gseq, true); | dmar_qi_invalidate_emit(domain, base, size, &gseq, true); | ||||
/* | |||||
* To avoid a missed wakeup in dmar_qi_task(), the unit's waiters count | |||||
* must be incremented before the tail is advanced. | |||||
*/ | |||||
unit->inv_seq_waiters++; | |||||
dmar_qi_advance_tail(unit); | |||||
dmar_qi_wait_for_seq(unit, &gseq, !cansleep); | dmar_qi_wait_for_seq(unit, &gseq, !cansleep); | ||||
DMAR_UNLOCK(unit); | DMAR_UNLOCK(unit); | ||||
} | } | ||||
void | void | ||||
dmar_qi_invalidate_ctx_glob_locked(struct dmar_unit *unit) | dmar_qi_invalidate_ctx_glob_locked(struct dmar_unit *unit) | ||||
{ | { | ||||
struct iommu_qi_genseq gseq; | struct iommu_qi_genseq gseq; | ||||
DMAR_ASSERT_LOCKED(unit); | DMAR_ASSERT_LOCKED(unit); | ||||
dmar_qi_ensure(unit, 2); | dmar_qi_ensure(unit, 2); | ||||
dmar_qi_emit(unit, DMAR_IQ_DESCR_CTX_INV | DMAR_IQ_DESCR_CTX_GLOB, 0); | dmar_qi_emit(unit, DMAR_IQ_DESCR_CTX_INV | DMAR_IQ_DESCR_CTX_GLOB, 0); | ||||
dmar_qi_emit_wait_seq(unit, &gseq, true); | dmar_qi_emit_wait_seq(unit, &gseq, true); | ||||
/* See dmar_qi_invalidate_sync(). */ | |||||
unit->inv_seq_waiters++; | |||||
dmar_qi_advance_tail(unit); | dmar_qi_advance_tail(unit); | ||||
dmar_qi_wait_for_seq(unit, &gseq, false); | dmar_qi_wait_for_seq(unit, &gseq, false); | ||||
} | } | ||||
void | void | ||||
dmar_qi_invalidate_iotlb_glob_locked(struct dmar_unit *unit) | dmar_qi_invalidate_iotlb_glob_locked(struct dmar_unit *unit) | ||||
{ | { | ||||
struct iommu_qi_genseq gseq; | struct iommu_qi_genseq gseq; | ||||
DMAR_ASSERT_LOCKED(unit); | DMAR_ASSERT_LOCKED(unit); | ||||
dmar_qi_ensure(unit, 2); | dmar_qi_ensure(unit, 2); | ||||
dmar_qi_emit(unit, DMAR_IQ_DESCR_IOTLB_INV | DMAR_IQ_DESCR_IOTLB_GLOB | | dmar_qi_emit(unit, DMAR_IQ_DESCR_IOTLB_INV | DMAR_IQ_DESCR_IOTLB_GLOB | | ||||
DMAR_IQ_DESCR_IOTLB_DW | DMAR_IQ_DESCR_IOTLB_DR, 0); | DMAR_IQ_DESCR_IOTLB_DW | DMAR_IQ_DESCR_IOTLB_DR, 0); | ||||
dmar_qi_emit_wait_seq(unit, &gseq, true); | dmar_qi_emit_wait_seq(unit, &gseq, true); | ||||
/* See dmar_qi_invalidate_sync(). */ | |||||
unit->inv_seq_waiters++; | |||||
dmar_qi_advance_tail(unit); | dmar_qi_advance_tail(unit); | ||||
dmar_qi_wait_for_seq(unit, &gseq, false); | dmar_qi_wait_for_seq(unit, &gseq, false); | ||||
} | } | ||||
void | void | ||||
dmar_qi_invalidate_iec_glob(struct dmar_unit *unit) | dmar_qi_invalidate_iec_glob(struct dmar_unit *unit) | ||||
{ | { | ||||
struct iommu_qi_genseq gseq; | struct iommu_qi_genseq gseq; | ||||
DMAR_ASSERT_LOCKED(unit); | DMAR_ASSERT_LOCKED(unit); | ||||
dmar_qi_ensure(unit, 2); | dmar_qi_ensure(unit, 2); | ||||
dmar_qi_emit(unit, DMAR_IQ_DESCR_IEC_INV, 0); | dmar_qi_emit(unit, DMAR_IQ_DESCR_IEC_INV, 0); | ||||
dmar_qi_emit_wait_seq(unit, &gseq, true); | dmar_qi_emit_wait_seq(unit, &gseq, true); | ||||
/* See dmar_qi_invalidate_sync(). */ | |||||
Not Done Inline Actionsdmar_qi_invalidate_locked() ? kib: dmar_qi_invalidate_locked() ? | |||||
Done Inline Actionsdmar_qi_invalidate_sync() is where the comment about lost wakeups resides. alc: dmar_qi_invalidate_sync() is where the comment about lost wakeups resides. | |||||
unit->inv_seq_waiters++; | |||||
dmar_qi_advance_tail(unit); | dmar_qi_advance_tail(unit); | ||||
dmar_qi_wait_for_seq(unit, &gseq, false); | dmar_qi_wait_for_seq(unit, &gseq, false); | ||||
} | } | ||||
void | void | ||||
dmar_qi_invalidate_iec(struct dmar_unit *unit, u_int start, u_int cnt) | dmar_qi_invalidate_iec(struct dmar_unit *unit, u_int start, u_int cnt) | ||||
{ | { | ||||
struct iommu_qi_genseq gseq; | struct iommu_qi_genseq gseq; | ||||
u_int c, l; | u_int c, l; | ||||
DMAR_ASSERT_LOCKED(unit); | DMAR_ASSERT_LOCKED(unit); | ||||
KASSERT(start < unit->irte_cnt && start < start + cnt && | KASSERT(start < unit->irte_cnt && start < start + cnt && | ||||
start + cnt <= unit->irte_cnt, | start + cnt <= unit->irte_cnt, | ||||
("inv iec overflow %d %d %d", unit->irte_cnt, start, cnt)); | ("inv iec overflow %d %d %d", unit->irte_cnt, start, cnt)); | ||||
for (; cnt > 0; cnt -= c, start += c) { | for (; cnt > 0; cnt -= c, start += c) { | ||||
l = ffs(start | cnt) - 1; | l = ffs(start | cnt) - 1; | ||||
c = 1 << l; | c = 1 << l; | ||||
dmar_qi_ensure(unit, 1); | dmar_qi_ensure(unit, 1); | ||||
dmar_qi_emit(unit, DMAR_IQ_DESCR_IEC_INV | | dmar_qi_emit(unit, DMAR_IQ_DESCR_IEC_INV | | ||||
DMAR_IQ_DESCR_IEC_IDX | DMAR_IQ_DESCR_IEC_IIDX(start) | | DMAR_IQ_DESCR_IEC_IDX | DMAR_IQ_DESCR_IEC_IIDX(start) | | ||||
DMAR_IQ_DESCR_IEC_IM(l), 0); | DMAR_IQ_DESCR_IEC_IM(l), 0); | ||||
} | } | ||||
dmar_qi_ensure(unit, 1); | dmar_qi_ensure(unit, 1); | ||||
dmar_qi_emit_wait_seq(unit, &gseq, true); | dmar_qi_emit_wait_seq(unit, &gseq, true); | ||||
/* | |||||
* Since dmar_qi_wait_for_seq() will not sleep, this increment's | |||||
* placement relative to advancing the tail doesn't matter. | |||||
*/ | |||||
unit->inv_seq_waiters++; | |||||
dmar_qi_advance_tail(unit); | dmar_qi_advance_tail(unit); | ||||
/* | /* | ||||
* The caller of the function, in particular, | * The caller of the function, in particular, | ||||
* dmar_ir_program_irte(), may be called from the context | * dmar_ir_program_irte(), may be called from the context | ||||
* where the sleeping is forbidden (in fact, the | * where the sleeping is forbidden (in fact, the | ||||
* intr_table_lock mutex may be held, locked from | * intr_table_lock mutex may be held, locked from | ||||
* intr_shuffle_irqs()). Wait for the invalidation completion | * intr_shuffle_irqs()). Wait for the invalidation completion | ||||
Show All 20 Lines | dmar_qi_intr(void *arg) | ||||
taskqueue_enqueue(unit->qi_taskqueue, &unit->qi_task); | taskqueue_enqueue(unit->qi_taskqueue, &unit->qi_task); | ||||
return (FILTER_HANDLED); | return (FILTER_HANDLED); | ||||
} | } | ||||
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; | struct iommu_domain *domain; | ||||
struct iommu_map_entry *entry, *head; | |||||
uint32_t ics; | uint32_t ics; | ||||
unit = arg; | unit = arg; | ||||
/* | /* | ||||
* 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); | ||||
} | } | ||||
DMAR_LOCK(unit); | |||||
for (;;) { | for (;;) { | ||||
entry = TAILQ_FIRST(&unit->tlb_flush_entries); | head = unit->tlb_flush_head; | ||||
entry = (struct iommu_map_entry *) | |||||
atomic_load_acq_ptr((uintptr_t *)&head->tlb_flush_next); | |||||
Done Inline ActionsShould I rename "head" to "zombie" to align with the description in intel_dmar.h? alc: Should I rename "head" to "zombie" to align with the description in intel_dmar.h? | |||||
Not Done Inline ActionsIMO no, it is quite clear what it means both in the comment and in the code usage. Head indeed points to the list' head, and simultaneously this entry does not participate in the gas, so it is zombie. I believe that use of both terms there in fact makes the description cleaner. kib: IMO no, it is quite clear what it means both in the comment and in the code usage. Head indeed… | |||||
if (entry == NULL) | if (entry == NULL) | ||||
break; | break; | ||||
if (!dmar_qi_seq_processed(unit, &entry->gseq)) | if (!dmar_qi_seq_processed(unit, &entry->gseq)) | ||||
break; | break; | ||||
TAILQ_REMOVE(&unit->tlb_flush_entries, entry, dmamap_link); | unit->tlb_flush_head = entry; | ||||
DMAR_UNLOCK(unit); | iommu_gas_free_entry(head->domain, head); | ||||
dmar_domain_free_entry(entry, true); | domain = entry->domain; | ||||
DMAR_LOCK(unit); | IOMMU_DOMAIN_LOCK(domain); | ||||
if ((entry->flags & IOMMU_MAP_ENTRY_RMRR) != 0) | |||||
iommu_gas_free_region(domain, entry); | |||||
else | |||||
iommu_gas_free_space(domain, entry); | |||||
IOMMU_DOMAIN_UNLOCK(domain); | |||||
} | } | ||||
if (unit->inv_seq_waiters > 0) | if (unit->inv_seq_waiters > 0) { | ||||
/* | |||||
Not Done Inline ActionsWhy this lock is needed? kib: Why this lock is needed? | |||||
Done Inline ActionsSo that we do not call wakeup before the incrementing thread has atomically slept and released the DMAR lock. alc: So that we do not call wakeup before the incrementing thread has atomically slept and released… | |||||
* Acquire the DMAR lock so that wakeup() is called only after | |||||
* the waiter is sleeping. | |||||
*/ | |||||
DMAR_LOCK(unit); | |||||
wakeup(&unit->inv_seq_waiters); | wakeup(&unit->inv_seq_waiters); | ||||
DMAR_UNLOCK(unit); | DMAR_UNLOCK(unit); | ||||
} | } | ||||
} | |||||
int | int | ||||
dmar_init_qi(struct dmar_unit *unit) | dmar_init_qi(struct dmar_unit *unit) | ||||
{ | { | ||||
uint64_t iqa; | uint64_t iqa; | ||||
uint32_t ics; | uint32_t ics; | ||||
int qi_sz; | int qi_sz; | ||||
if (!DMAR_HAS_QI(unit) || (unit->hw_cap & DMAR_CAP_CM) != 0) | if (!DMAR_HAS_QI(unit) || (unit->hw_cap & DMAR_CAP_CM) != 0) | ||||
return (0); | return (0); | ||||
unit->qi_enabled = 1; | unit->qi_enabled = 1; | ||||
TUNABLE_INT_FETCH("hw.dmar.qi", &unit->qi_enabled); | TUNABLE_INT_FETCH("hw.dmar.qi", &unit->qi_enabled); | ||||
if (!unit->qi_enabled) | if (!unit->qi_enabled) | ||||
return (0); | return (0); | ||||
TAILQ_INIT(&unit->tlb_flush_entries); | unit->tlb_flush_head = unit->tlb_flush_tail = | ||||
iommu_gas_alloc_entry(NULL, 0); | |||||
TASK_INIT(&unit->qi_task, 0, dmar_qi_task, unit); | TASK_INIT(&unit->qi_task, 0, dmar_qi_task, unit); | ||||
unit->qi_taskqueue = taskqueue_create_fast("dmarqf", M_WAITOK, | unit->qi_taskqueue = taskqueue_create_fast("dmarqf", M_WAITOK, | ||||
taskqueue_thread_enqueue, &unit->qi_taskqueue); | taskqueue_thread_enqueue, &unit->qi_taskqueue); | ||||
taskqueue_start_threads(&unit->qi_taskqueue, 1, PI_AV, | taskqueue_start_threads(&unit->qi_taskqueue, 1, PI_AV, | ||||
"dmar%d qi taskq", unit->iommu.unit); | "dmar%d qi taskq", unit->iommu.unit); | ||||
unit->inv_waitd_gen = 0; | unit->inv_waitd_gen = 0; | ||||
unit->inv_waitd_seq = 1; | unit->inv_waitd_seq = 1; | ||||
Show All 39 Lines | dmar_fini_qi(struct dmar_unit *unit) | ||||
taskqueue_drain(unit->qi_taskqueue, &unit->qi_task); | taskqueue_drain(unit->qi_taskqueue, &unit->qi_task); | ||||
taskqueue_free(unit->qi_taskqueue); | taskqueue_free(unit->qi_taskqueue); | ||||
unit->qi_taskqueue = NULL; | unit->qi_taskqueue = NULL; | ||||
DMAR_LOCK(unit); | DMAR_LOCK(unit); | ||||
/* quisce */ | /* quisce */ | ||||
dmar_qi_ensure(unit, 1); | dmar_qi_ensure(unit, 1); | ||||
dmar_qi_emit_wait_seq(unit, &gseq, true); | dmar_qi_emit_wait_seq(unit, &gseq, true); | ||||
/* See dmar_qi_invalidate_sync_locked(). */ | |||||
unit->inv_seq_waiters++; | |||||
dmar_qi_advance_tail(unit); | dmar_qi_advance_tail(unit); | ||||
dmar_qi_wait_for_seq(unit, &gseq, false); | dmar_qi_wait_for_seq(unit, &gseq, false); | ||||
/* only after the quisce, disable queue */ | /* only after the quisce, disable queue */ | ||||
dmar_disable_qi_intr(unit); | dmar_disable_qi_intr(unit); | ||||
dmar_disable_qi(unit); | dmar_disable_qi(unit); | ||||
KASSERT(unit->inv_seq_waiters == 0, | KASSERT(unit->inv_seq_waiters == 0, | ||||
("dmar%d: waiters on disabled queue", unit->iommu.unit)); | ("dmar%d: waiters on disabled queue", unit->iommu.unit)); | ||||
DMAR_UNLOCK(unit); | DMAR_UNLOCK(unit); | ||||
Show All 31 Lines |
So tlb_flush_tail entry cannot be freed before or while we are storing to it's tlb_flush_next, because dmar_qi_task never frees the last descriptor, and there we are guaranteed that tlb_flush_tail is the last because of the DMAR lock. Am I reading the algorithm correctly?
I believe this should be explained in the comment.