VT-d: size qinval queue dynamically

With the present synchronous model, we need two slots for every
operation (the operation itself and a wait descriptor).  There can be
one such pair of requests pending per CPU. To ensure that under all
normal circumstances a slot is always available when one is requested,
size the queue ring according to the number of present CPUs.

This is part of XSA-373 / CVE-2021-28692.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Paul Durrant <paul@xen.org>
This commit is contained in:
Jan Beulich authored and Andrew Cooper committed 2021-06-08 17:43:06 +01:00
1 parent fd5dc41cea
commit cbfa62bb14
2 files changed
+36 -20

No files matched your search

+1 -9
View File
@@ -452,17 +452,9 @@ struct qinval_entry {
}q;
};
/* Order of queue invalidation pages(max is 8) */
#define QINVAL_PAGE_ORDER 2
#define QINVAL_ARCH_PAGE_ORDER (QINVAL_PAGE_ORDER + PAGE_SHIFT_4K - PAGE_SHIFT)
#define QINVAL_ARCH_PAGE_NR ( QINVAL_ARCH_PAGE_ORDER < 0 ? \
1 : \
1 << QINVAL_ARCH_PAGE_ORDER )
/* Each entry is 16 bytes, so 2^8 entries per page */
#define QINVAL_ENTRY_ORDER ( PAGE_SHIFT - 4 )
#define QINVAL_ENTRY_NR (1 << (QINVAL_PAGE_ORDER + 8))
#define QINVAL_MAX_ENTRY_NR (1u << (7 + QINVAL_ENTRY_ORDER))
/* Status data flag */
#define QINVAL_STAT_INIT 0
+35 -11
View File
@@ -31,6 +31,9 @@
#define VTD_QI_TIMEOUT 1
static unsigned int __read_mostly qi_pg_order;
static unsigned int __read_mostly qi_entry_nr;
static int __must_check invalidate_sync(struct vtd_iommu *iommu);
static void print_qi_regs(const struct vtd_iommu *iommu)
@@ -47,7 +50,7 @@ static unsigned int qinval_next_index(struct vtd_iommu *iommu)
tail >>= QINVAL_INDEX_SHIFT;
/* (tail+1 == head) indicates a full queue, wait for HW */
while ( (tail + 1) % QINVAL_ENTRY_NR ==
while ( ((tail + 1) & (qi_entry_nr - 1)) ==
(dmar_readl(iommu->reg, DMAR_IQH_REG) >> QINVAL_INDEX_SHIFT) )
cpu_relax();
@@ -60,7 +63,7 @@ static void qinval_update_qtail(struct vtd_iommu *iommu, unsigned int index)
/* Need hold register lock when update tail */
ASSERT( spin_is_locked(&iommu->register_lock) );
val = (index + 1) % QINVAL_ENTRY_NR;
val = (index + 1) & (qi_entry_nr - 1);
dmar_writel(iommu->reg, DMAR_IQT_REG, val << QINVAL_INDEX_SHIFT);
}
@@ -395,8 +398,28 @@ int enable_qinval(struct vtd_iommu *iommu)
if ( iommu->qinval_maddr == 0 )
{
iommu->qinval_maddr = alloc_pgtable_maddr(QINVAL_ARCH_PAGE_NR,
iommu->node);
if ( !qi_entry_nr )
{
/*
* With the present synchronous model, we need two slots for every
* operation (the operation itself and a wait descriptor). There
* can be one such pair of requests pending per CPU. One extra
* entry is needed as the ring is considered full when there's
* only one entry left.
*/
BUILD_BUG_ON(CONFIG_NR_CPUS * 2 >= QINVAL_MAX_ENTRY_NR);
qi_pg_order = get_order_from_bytes((num_present_cpus() * 2 + 1) <<
(PAGE_SHIFT -
QINVAL_ENTRY_ORDER));
qi_entry_nr = 1u << (qi_pg_order + QINVAL_ENTRY_ORDER);
dprintk(XENLOG_INFO VTDPREFIX,
"QI: using %u-entry ring(s)\n", qi_entry_nr);
}
iommu->qinval_maddr =
alloc_pgtable_maddr(qi_entry_nr >> QINVAL_ENTRY_ORDER,
iommu->node);
if ( iommu->qinval_maddr == 0 )
{
dprintk(XENLOG_WARNING VTDPREFIX,
@@ -410,15 +433,16 @@ int enable_qinval(struct vtd_iommu *iommu)
spin_lock_irqsave(&iommu->register_lock, flags);
/* Setup Invalidation Queue Address(IQA) register with the
* address of the page we just allocated. QS field at
* bits[2:0] to indicate size of queue is one 4KB page.
* That's 256 entries. Queued Head (IQH) and Queue Tail (IQT)
* registers are automatically reset to 0 with write
* to IQA register.
/*
* Setup Invalidation Queue Address (IQA) register with the address of the
* pages we just allocated. The QS field at bits[2:0] indicates the size
* (page order) of the queue.
*
* Queued Head (IQH) and Queue Tail (IQT) registers are automatically
* reset to 0 with write to IQA register.
*/
dmar_writeq(iommu->reg, DMAR_IQA_REG,
iommu->qinval_maddr | QINVAL_PAGE_ORDER);
iommu->qinval_maddr | qi_pg_order);
dmar_writeq(iommu->reg, DMAR_IQT_REG, 0);
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.