diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-09-12 07:55:52 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-09-12 07:55:52 -0700 |
| commit | f6e213d5a2a94255b31926f0e9f7c1eb234bbf22 (patch) | |
| tree | ee541b499a544b516aad68c0766cdad53b95acdd | |
| parent | 52311be52f66f1a3c71bc808d156482feb1eb79f (diff) | |
| parent | b63c3c26726576e2a87baeee80bc202a5a43c9e5 (diff) | |
| download | linux-f6e213d5a2a94255b31926f0e9f7c1eb234bbf22.tar.gz linux-f6e213d5a2a94255b31926f0e9f7c1eb234bbf22.zip | |
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
| -rw-r--r-- | MAINTAINERS | 1 | ||||
| -rw-r--r-- | drivers/iommu/amd/amd_iommu.h | 3 | ||||
| -rw-r--r-- | drivers/iommu/amd/amd_iommu_types.h | 16 | ||||
| -rw-r--r-- | drivers/iommu/amd/init.c | 18 | ||||
| -rw-r--r-- | drivers/iommu/amd/iommu.c | 6 | ||||
| -rw-r--r-- | drivers/iommu/amd/nested.c | 7 | ||||
| -rw-r--r-- | drivers/iommu/amd/pasid.c | 6 | ||||
| -rw-r--r-- | drivers/iommu/amd/ppr.c | 3 | ||||
| -rw-r--r-- | drivers/iommu/riscv/iommu.c | 117 | ||||
| -rw-r--r-- | drivers/iommu/riscv/iommu.h | 2 | ||||
| -rw-r--r-- | drivers/iommu/s390-iommu.c | 2 |
11 files changed, 93 insertions, 88 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index f0d033fc8a85..c2414447892c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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> diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h index a2fe804b038b..71113e860859 100644 --- a/drivers/iommu/amd/amd_iommu.h +++ b/drivers/iommu/amd/amd_iommu.h @@ -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); diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h index 3dbe20023456..8241ef922519 100644 --- a/drivers/iommu/amd/amd_iommu_types.h +++ b/drivers/iommu/amd/amd_iommu_types.h @@ -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 */ diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c index 40726dfef273..8a410d4aa370 100644 --- a/drivers/iommu/amd/init.c +++ b/drivers/iommu/amd/init.c @@ -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; + 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; } - if (h->efr_attr & BIT(IOMMU_IVHD_ATTR_HATDIS_SHIFT)) { - pr_warn_once("Host Address Translation is not supported.\n"); - amd_iommu_hatdis = true; - } - early_iommu_features_init(iommu, h); break; diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c index 4dc306a4b5c6..56262f6b1f70 100644 --- a/drivers/iommu/amd/iommu.c +++ b/drivers/iommu/amd/iommu.c @@ -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)) diff --git a/drivers/iommu/amd/nested.c b/drivers/iommu/amd/nested.c index 63b53b29e029..f1c7987fc585 100644 --- a/drivers/iommu/amd/nested.c +++ b/drivers/iommu/amd/nested.c @@ -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); diff --git a/drivers/iommu/amd/pasid.c b/drivers/iommu/amd/pasid.c index d708c6532480..40be5902087c 100644 --- a/drivers/iommu/amd/pasid.c +++ b/drivers/iommu/amd/pasid.c @@ -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); diff --git a/drivers/iommu/amd/ppr.c b/drivers/iommu/amd/ppr.c index 76296079bb8b..2039a9dd71ac 100644 --- a/drivers/iommu/amd/ppr.c +++ b/drivers/iommu/amd/ppr.c @@ -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); diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c index cec3ddd7ab10..fe8e6d0f8a23 100644 --- a/drivers/iommu/riscv/iommu.c +++ b/drivers/iommu/riscv/iommu.c @@ -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 prod; unsigned int head; unsigned int tail; + unsigned int hw_head; unsigned long flags; + int ret; - /* Do not preempt submission flow. */ - local_irq_save(flags); + 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; - /* 1. Allocate some space in the queue */ - prod = atomic_inc_return(&queue->prod) - 1; + 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); - /* 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)) - goto err_busy; - } else if ((prod - head) == queue->mask) { - const unsigned int last = Q_ITEM(queue, head); + 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 long flags; + int ret; + + /* 1. Wait for space availability and reserve the next slot. */ + for (;;) { + raw_spin_lock_irqsave(&queue->lock, flags); + + prod = atomic_read(&queue->tail); + head = atomic_read(&queue->head); + + if ((prod - head) < queue->mask) + break; + + head = Q_ITEM(queue, head); + raw_spin_unlock_irqrestore(&queue->lock, flags); - if (riscv_iommu_readl_timeout(queue->iommu, Q_HEAD(queue), head, - !(head & ~queue->mask) && head != last, - 0, RISCV_IOMMU_QUEUE_TIMEOUT)) + ret = riscv_iommu_queue_wait_for_space(queue, head); + if (ret) 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); diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h index 46df79dd5495..5676001548cc 100644 --- a/drivers/iommu/riscv/iommu.h +++ b/drivers/iommu/riscv/iommu.h @@ -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 */ diff --git a/drivers/iommu/s390-iommu.c b/drivers/iommu/s390-iommu.c index f148f559ac56..58ca7727b7f2 100644 --- a/drivers/iommu/s390-iommu.c +++ b/drivers/iommu/s390-iommu.c @@ -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)) |
