IOMMU fixes for Linux v7.3-rc3:

- RISC-V IOMMU:
   - Serialize command queue publication to prevent concurrent producers
     from exposing incomplete or out-of-order commands to hardware.
   - Wait for queue space outside the command queue lock.
   - Avoid waiting for IOFENCE completion when command enqueue failed.
 
 - AMD IOMMU:
   - Prevent GA log buffers from being reallocated and leaked during
     resume, where allocation also occurs in an unsuitable syscore
     callback context.
   - Fix a regression on older systems whose firmware advertises
     incorrect IOMMU features.
   - Preserve allocation errors when assigning host domain IDs to nested
     domains.
 
 - s390 IOMMU:
   - Prevent a NULL dereference when translating an unmapped IOVA with
     five-level ZPCI translation tables.
 
 - Miscellaneous:
   - Remove a stale MAINTAINERS entry and clean up unused or redundant
     AMD IOMMU declarations, macros, and checks.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEr9jSbILcajRFYWYyK/BELZcBGuMFAmqkH6kACgkQK/BELZcB
 GuPgZhAAtYhzeVbH6IKimy29zj6TZKvzdlWtMOuAk7wj/URPWPK6kA+LC44uH9OB
 +WY+6P922vC5pqcL5YkA7GNt8KQaJEgTFp9zdQ15hY7PFAw+GIM66HVVn96iCv5a
 RY1485YJopQEHSfpb3e2GyuR6mdW6b7ReEmzFr0P9Md1Flm/6TaGvCmSCjLLVrto
 BkJ6DfWQ0zNC3eByAHFE8HOh3T6Eea8J64tC+Tt6jOFO6ynQL7+acwv2zG66z1RM
 bZAjgNMAdPO7AHU3Kue7OT8mVFKy75G3AOtT55dobiBWFJ4jPgziw6ZhjEEMNZDs
 eADcQT1reXLdQkiOYGxZt5fNAI9RQiZRNizIidFG4KmaYGxWaPUR2nQUxyCSvlig
 Wv8GMHaFskZxkJyqAonYtlUxaUk6XAF3L4nQrDZW2KJsHZdopcDnUeGi526J414h
 bvD/p4IxSNhlzib+70mQ3XPHqzVAyDjjRBuQBK+B4MXfKMet68/O1qBzxBQVqgQX
 gJaz++7hGSE4uvt4NCANjhpC6aTKGZdeWsFc8xs44krHjQo6ssPZSlxl3nclJOUQ
 O6N50eV6nItnE/kqbdKxpdZIzYHmW6DLxL+2bvueZjRMXxpCuPmSuCpJux+Hl2RW
 RqMVhUAgGPm35RNmRTkAh534WzElNc2K66VM6952ck8xrZ6+Hy8=
 =2Cze
 -----END PGP SIGNATURE-----

Merge tag 'iommu-fixes-v7.3-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux

Pull IOMMU fixes from Joerg Roedel:
 "RISC-V:

   - Serialize command queue publication to prevent concurrent producers
     from exposing incomplete or out-of-order commands to hardware

   - Wait for queue space outside the command queue lock

   - Avoid waiting for IOFENCE completion when command enqueue failed

  AMD:

   - Prevent GA log buffers from being reallocated and leaked during
     resume, where allocation also occurs in an unsuitable syscore
     callback context

   - Fix a regression on older systems whose firmware advertises
     incorrect IOMMU features

   - Preserve allocation errors when assigning host domain IDs to nested
     domains

  s390:

   - Prevent a NULL dereference when translating an unmapped IOVA with
     five-level ZPCI translation tables

  Misc:

   - Remove a stale MAINTAINERS entry and clean up unused or redundant
     AMD IOMMU declarations, macros, and checks"

* tag 'iommu-fixes-v7.3-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux:
  iommu/amd: Remove unused macro
  iommu/amd: Remove redundant checks from interrupt handler path
  iommu/amd: Remove redundant check in irq_remapping_select()
  iommu/amd: Make iommu_sva_set_dev_pasid as static
  MAINTAINERS: Drop the nonexistent vsi-iommu.h file entry
  iommu/amd: Fix ineffective error check in nested domain allocation
  iommu/amd: Fix premature break in init_iommu_one() again
  iommu/amd: Do not reallocate GA log buffers on resume
  iommu/s390: Fix NULL dereference in iova_to_phys() with ZPCI_TABLE_TYPE_RFX
  iommu/riscv: Avoid waiting on failed command enqueue
  iommu/riscv: Serialize command queue publishing
  iommu/riscv: Add command queue lock
This commit is contained in:
Linus Torvalds 2026-09-12 07:55:52 -07:00
commit f6e213d5a2
11 changed files with 98 additions and 93 deletions

View File

@ -28609,7 +28609,6 @@ L: iommu@lists.linux.dev
S: Maintained
F: Documentation/devicetree/bindings/iommu/verisilicon,iommu.yaml
F: drivers/iommu/vsi-iommu.c
F: include/linux/vsi-iommu.h
VF610 NAND DRIVER
M: Stefan Agner <stefan@agner.ch>

View File

@ -54,9 +54,6 @@ struct protection_domain *protection_domain_alloc(void);
struct iommu_domain *amd_iommu_domain_alloc_sva(struct device *dev,
struct mm_struct *mm);
void amd_iommu_domain_free(struct iommu_domain *dom);
int iommu_sva_set_dev_pasid(struct iommu_domain *domain,
struct device *dev, ioasid_t pasid,
struct iommu_domain *old);
void amd_iommu_remove_dev_pasid(struct device *dev, ioasid_t pasid,
struct iommu_domain *domain);

View File

@ -39,18 +39,6 @@
#define MMIO_RANGE_OFFSET 0x0c
#define MMIO_MISC_OFFSET 0x10
/* Masks, shifts and macros to parse the device range capability */
#define MMIO_RANGE_LD_MASK 0xff000000
#define MMIO_RANGE_FD_MASK 0x00ff0000
#define MMIO_RANGE_BUS_MASK 0x0000ff00
#define MMIO_RANGE_LD_SHIFT 24
#define MMIO_RANGE_FD_SHIFT 16
#define MMIO_RANGE_BUS_SHIFT 8
#define MMIO_GET_LD(x) (((x) & MMIO_RANGE_LD_MASK) >> MMIO_RANGE_LD_SHIFT)
#define MMIO_GET_FD(x) (((x) & MMIO_RANGE_FD_MASK) >> MMIO_RANGE_FD_SHIFT)
#define MMIO_GET_BUS(x) (((x) & MMIO_RANGE_BUS_MASK) >> MMIO_RANGE_BUS_SHIFT)
#define MMIO_MSI_NUM(x) ((x) & 0x1f)
/* Used offsets into the MMIO space */
#define MMIO_DEV_TABLE_OFFSET 0x0000
#define MMIO_CMD_BUF_OFFSET 0x0008
@ -247,7 +235,6 @@
/* constants to configure the command buffer */
#define CMD_BUFFER_SIZE 8192
#define CMD_BUFFER_UNINITIALIZED 1
#define CMD_BUFFER_ENTRIES 512
#define MMIO_CMD_SIZE_SHIFT 56
#define MMIO_CMD_SIZE_512 (0x9ULL << MMIO_CMD_SIZE_SHIFT)
@ -434,9 +421,6 @@ struct irq_remap_table {
u32 *table;
};
/* Interrupt remapping feature used? */
extern bool amd_iommu_irq_remap;
extern const struct iommu_ops amd_iommu_ops;
/* IVRS indicates that pre-boot remapping was enabled */

View File

@ -152,7 +152,7 @@ struct ivmd_header {
} __attribute__((packed));
bool amd_iommu_dump;
bool amd_iommu_irq_remap __read_mostly;
static bool amd_iommu_irq_remap __read_mostly;
enum protection_domain_mode amd_iommu_pgtable = PD_MODE_V1;
/* Virtual address size */
@ -909,7 +909,9 @@ static void free_ga_log(struct amd_iommu *iommu)
{
#ifdef CONFIG_IRQ_REMAP
iommu_free_pages(iommu->ga_log);
iommu->ga_log = NULL;
iommu_free_pages(iommu->ga_log_tail);
iommu->ga_log_tail = NULL;
#endif
}
@ -956,6 +958,9 @@ static int iommu_init_ga_log(struct amd_iommu *iommu)
if (WARN_ON_ONCE(!AMD_IOMMU_GUEST_IR_VAPIC(amd_iommu_guest_ir)))
return -EINVAL;
if (iommu->ga_log && iommu->ga_log_tail)
return 0;
iommu->ga_log = iommu_alloc_pages_node_sz(nid, GFP_KERNEL, GA_LOG_SIZE);
if (!iommu->ga_log)
goto err_out;
@ -1917,19 +1922,20 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h,
else
iommu->mmio_phys_end = MMIO_CNTR_CONF_OFFSET;
/* XT and GAM require GA mode. */
if ((h->efr_reg & (0x1 << IOMMU_EFR_GASUP_SHIFT)) == 0) {
amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_LEGACY;
} else {
if (h->efr_reg & BIT(IOMMU_EFR_XTSUP_SHIFT))
amd_iommu_xt_mode = IRQ_REMAP_X2APIC_MODE;
}
if (h->efr_attr & BIT(IOMMU_IVHD_ATTR_HATDIS_SHIFT)) {
pr_warn_once("Host Address Translation is not supported.\n");
amd_iommu_hatdis = true;
}
/* XT and GAM require GA mode. */
if ((h->efr_reg & (0x1 << IOMMU_EFR_GASUP_SHIFT)) == 0) {
amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_LEGACY;
break;
} else {
if (h->efr_reg & BIT(IOMMU_EFR_XTSUP_SHIFT))
amd_iommu_xt_mode = IRQ_REMAP_X2APIC_MODE;
}
early_iommu_features_init(iommu, h);
break;

View File

@ -1076,9 +1076,6 @@ static void iommu_poll_ga_log(struct amd_iommu *iommu)
{
u32 head, tail;
if (iommu->ga_log == NULL)
return;
head = readl(iommu->mmio_base + MMIO_GA_HEAD_OFFSET);
tail = readl(iommu->mmio_base + MMIO_GA_TAIL_OFFSET);
@ -3976,9 +3973,6 @@ static int irq_remapping_select(struct irq_domain *d, struct irq_fwspec *fwspec,
struct amd_iommu *iommu;
int devid = -1;
if (!amd_iommu_irq_remap)
return 0;
if (x86_fwspec_is_ioapic(fwspec))
devid = get_ioapic_devid(fwspec->param[0]);
else if (x86_fwspec_is_hpet(fwspec))

View File

@ -96,7 +96,7 @@ struct iommu_domain *
amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
const struct iommu_user_data *user_data)
{
int ret;
int ret, hdom_id;
unsigned long irqflags;
struct nested_domain *ndom;
struct guest_domain_mapping_info *gdom_info;
@ -161,8 +161,8 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
}
/* The gDomID does not exist. We allocate new hdom_id */
gdom_info->hdom_id = amd_iommu_pdom_id_alloc();
if (gdom_info->hdom_id <= 0) {
hdom_id = amd_iommu_pdom_id_alloc();
if (hdom_id <= 0) {
__xa_cmpxchg(&aviommu->gdomid_array,
ndom->gdom_id, gdom_info, NULL, GFP_ATOMIC);
xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
@ -170,6 +170,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
goto out_err_gdom_info;
}
gdom_info->hdom_id = hdom_id;
ndom->gdom_info = gdom_info;
refcount_set(&gdom_info->users, 1);

View File

@ -99,9 +99,9 @@ static const struct mmu_notifier_ops sva_mn = {
.release = sva_mn_release,
};
int iommu_sva_set_dev_pasid(struct iommu_domain *domain,
struct device *dev, ioasid_t pasid,
struct iommu_domain *old)
static int iommu_sva_set_dev_pasid(struct iommu_domain *domain,
struct device *dev, ioasid_t pasid,
struct iommu_domain *old)
{
struct pdom_dev_data *pdom_dev_data;
struct protection_domain *sva_pdom = to_pdomain(domain);

View File

@ -165,9 +165,6 @@ void amd_iommu_poll_ppr_log(struct amd_iommu *iommu)
{
u32 head, tail;
if (iommu->ppr_log == NULL)
return;
head = readl(iommu->mmio_base + MMIO_PPR_HEAD_OFFSET);
tail = readl(iommu->mmio_base + MMIO_PPR_TAIL_OFFSET);

View File

@ -382,78 +382,97 @@ static int riscv_iommu_queue_wait(struct riscv_iommu_queue *queue,
(int)(cons - index) > 0, 0, timeout_us);
}
/* Enqueue an entry and wait to be processed if timeout_us > 0
*
* Error handling for IOMMU hardware not responding in reasonable time
* will be added as separate patch series along with other RAS features.
* For now, only report hardware failure and continue.
*/
static unsigned int riscv_iommu_queue_send(struct riscv_iommu_queue *queue,
void *entry, size_t entry_size)
static int riscv_iommu_queue_wait_for_space(struct riscv_iommu_queue *queue,
unsigned int last)
{
unsigned int head;
unsigned int tail;
unsigned int hw_head;
unsigned long flags;
int ret;
ret = riscv_iommu_readl_timeout(queue->iommu, Q_HEAD(queue), hw_head,
!(hw_head & ~queue->mask) && hw_head != last,
0, RISCV_IOMMU_QUEUE_TIMEOUT);
if (ret)
return ret;
raw_spin_lock_irqsave(&queue->lock, flags);
head = atomic_read(&queue->head);
tail = atomic_read(&queue->tail);
if ((tail - head) >= queue->mask) {
last = Q_ITEM(queue, head);
/*
* Re-read hw_head under the lock so that it is consistent with
* the freshly computed 'last'. Using the pre-lock snapshot
* could produce a stale value that wraps around relative to the
* new 'last', advancing the shadow head past entries that have
* not yet been consumed by the hardware.
*/
hw_head = riscv_iommu_readl(queue->iommu, Q_HEAD(queue));
if (!(hw_head & ~queue->mask) && hw_head != last)
atomic_add((hw_head - last) & queue->mask, &queue->head);
}
raw_spin_unlock_irqrestore(&queue->lock, flags);
return 0;
}
/* Enqueue an entry and publish it to the hardware queue. */
static int riscv_iommu_queue_send(struct riscv_iommu_queue *queue,
void *entry, size_t entry_size,
unsigned int *out_prod)
{
unsigned int prod;
unsigned int head;
unsigned int tail;
unsigned long flags;
int ret;
/* Do not preempt submission flow. */
local_irq_save(flags);
/* 1. Wait for space availability and reserve the next slot. */
for (;;) {
raw_spin_lock_irqsave(&queue->lock, flags);
/* 1. Allocate some space in the queue */
prod = atomic_inc_return(&queue->prod) - 1;
head = atomic_read(&queue->head);
prod = atomic_read(&queue->tail);
head = atomic_read(&queue->head);
/* 2. Wait for space availability. */
if ((prod - head) > queue->mask) {
if (readx_poll_timeout(atomic_read, &queue->head,
head, (prod - head) < queue->mask,
0, RISCV_IOMMU_QUEUE_TIMEOUT))
if ((prod - head) < queue->mask)
break;
head = Q_ITEM(queue, head);
raw_spin_unlock_irqrestore(&queue->lock, flags);
ret = riscv_iommu_queue_wait_for_space(queue, head);
if (ret)
goto err_busy;
} else if ((prod - head) == queue->mask) {
const unsigned int last = Q_ITEM(queue, head);
if (riscv_iommu_readl_timeout(queue->iommu, Q_HEAD(queue), head,
!(head & ~queue->mask) && head != last,
0, RISCV_IOMMU_QUEUE_TIMEOUT))
goto err_busy;
atomic_add((head - last) & queue->mask, &queue->head);
}
/* 3. Store entry in the ring buffer */
/* 2. Store entry in the ring buffer. */
memcpy(queue->base + Q_ITEM(queue, prod) * entry_size, entry, entry_size);
/* 4. Wait for all previous entries to be ready */
if (readx_poll_timeout(atomic_read, &queue->tail, tail, prod == tail,
0, RISCV_IOMMU_QUEUE_TIMEOUT))
goto err_busy;
/*
* 5. Make sure the ring buffer update (whether in normal or I/O memory) is
* completed and visible before signaling the tail doorbell to fetch
* the next command. 'fence ow, ow'
*/
/* 3. Make sure the entry is visible before updating the queue tail. */
dma_wmb();
riscv_iommu_writel(queue->iommu, Q_TAIL(queue), Q_ITEM(queue, prod + 1));
/*
* 6. Make sure the doorbell write to the device has finished before updating
* the shadow tail index in normal memory. 'fence o, w'
* 4. Make sure the doorbell write to the device has finished before
* updating the shadow tail index in normal memory. 'fence o, w'
*/
#ifdef CONFIG_MMIOWB
mmiowb();
#endif
atomic_inc(&queue->tail);
atomic_set(&queue->tail, prod + 1);
atomic_set(&queue->prod, prod + 1);
/* 7. Complete submission and restore local interrupts */
local_irq_restore(flags);
if (out_prod)
*out_prod = prod;
return prod;
raw_spin_unlock_irqrestore(&queue->lock, flags);
return 0;
err_busy:
local_irq_restore(flags);
/* Report the failure and continue; full RAS recovery is not implemented. */
dev_err_once(queue->iommu->dev, "Hardware error: command enqueue failed\n");
return prod;
return ret;
}
/*
@ -492,7 +511,7 @@ static irqreturn_t riscv_iommu_cmdq_process(int irq, void *data)
static void riscv_iommu_cmd_send(struct riscv_iommu_device *iommu,
struct riscv_iommu_command *cmd)
{
riscv_iommu_queue_send(&iommu->cmdq, cmd, sizeof(*cmd));
riscv_iommu_queue_send(&iommu->cmdq, cmd, sizeof(*cmd), NULL);
}
/* Send IOFENCE.C command and wait for all scheduled commands to complete. */
@ -501,9 +520,12 @@ static void riscv_iommu_cmd_sync(struct riscv_iommu_device *iommu,
{
struct riscv_iommu_command cmd;
unsigned int prod;
int ret;
riscv_iommu_cmd_iofence(&cmd);
prod = riscv_iommu_queue_send(&iommu->cmdq, &cmd, sizeof(cmd));
ret = riscv_iommu_queue_send(&iommu->cmdq, &cmd, sizeof(cmd), &prod);
if (ret)
return;
if (!timeout_us)
return;
@ -1560,6 +1582,7 @@ int riscv_iommu_init(struct riscv_iommu_device *iommu)
int rc;
RISCV_IOMMU_QUEUE_INIT(&iommu->cmdq, CQ);
raw_spin_lock_init(&iommu->cmdq.lock);
RISCV_IOMMU_QUEUE_INIT(&iommu->fltq, FQ);
rc = riscv_iommu_init_check(iommu);

View File

@ -12,6 +12,7 @@
#define _RISCV_IOMMU_H_
#include <linux/iommu.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/iopoll.h>
@ -23,6 +24,7 @@ struct riscv_iommu_queue {
atomic_t prod; /* unbounded producer allocation index */
atomic_t head; /* unbounded shadow ring buffer consumer index */
atomic_t tail; /* unbounded shadow ring buffer producer index */
raw_spinlock_t lock; /* serialize queue publishing */
unsigned int mask; /* index mask, queue length - 1 */
unsigned int irq; /* allocated interrupt number */
struct riscv_iommu_device *iommu; /* iommu device handling the queue when active */

View File

@ -974,6 +974,8 @@ static unsigned long *get_rto_from_iova(struct s390_domain *domain,
case ZPCI_TABLE_TYPE_RFX:
case ZPCI_TABLE_TYPE_RSX:
rso = get_rso_from_iova(domain, iova);
if (!rso)
return NULL;
rsx = calc_rsx(iova);
rse = READ_ONCE(rso[rsx]);
if (!reg_entry_isvalid(rse))