Commit 13eaa5bd authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'iommu-updates-v5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu

Pull iommu updates from Joerg Roedel:

 - Identity domain support for virtio-iommu

 - Move flush queue code into iommu-dma

 - Some fixes for AMD IOMMU suspend/resume support when x2apic is used

 - Arm SMMU Updates from Will Deacon:
      - Revert evtq and priq back to their former sizes
      - Return early on short-descriptor page-table allocation failure
      - Fix page fault reporting for Adreno GPU on SMMUv2
      - Make SMMUv3 MMU notifier ops 'const'
      - Numerous new compatible strings for Qualcomm SMMUv2 implementations

 - Various smaller fixes and cleanups

* tag 'iommu-updates-v5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu: (38 commits)
  iommu/iova: Temporarily include dma-mapping.h from iova.h
  iommu: Move flush queue data into iommu_dma_cookie
  iommu/iova: Move flush queue code to iommu-dma
  iommu/iova: Consolidate flush queue code
  iommu/vt-d: Use put_pages_list
  iommu/amd: Use put_pages_list
  iommu/amd: Simplify pagetable freeing
  iommu/iova: Squash flush_cb abstraction
  iommu/iova: Squash entry_dtor abstraction
  iommu/iova: Fix race between FQ timeout and teardown
  iommu/amd: Fix typo in *glues … together* in comment
  iommu/vt-d: Remove unused dma_to_mm_pfn function
  iommu/vt-d: Drop duplicate check in dma_pte_free_pagetable()
  iommu/vt-d: Use bitmap_zalloc() when applicable
  iommu/amd: Remove useless irq affinity notifier
  iommu/amd: X2apic mode: mask/unmask interrupts on suspend/resume
  iommu/amd: X2apic mode: setup the INTX registers on mask/unmask
  iommu/amd: X2apic mode: re-enable after resume
  iommu/amd: Restore GA log/tail pointer on host resume
  iommu/iova: Move fast alloc size roundup into alloc_iova_fast()
  ...
parents 362f533a 66dc1b79
...@@ -38,10 +38,12 @@ properties: ...@@ -38,10 +38,12 @@ properties:
- qcom,sc7280-smmu-500 - qcom,sc7280-smmu-500
- qcom,sc8180x-smmu-500 - qcom,sc8180x-smmu-500
- qcom,sdm845-smmu-500 - qcom,sdm845-smmu-500
- qcom,sdx55-smmu-500
- qcom,sm6350-smmu-500 - qcom,sm6350-smmu-500
- qcom,sm8150-smmu-500 - qcom,sm8150-smmu-500
- qcom,sm8250-smmu-500 - qcom,sm8250-smmu-500
- qcom,sm8350-smmu-500 - qcom,sm8350-smmu-500
- qcom,sm8450-smmu-500
- const: arm,mmu-500 - const: arm,mmu-500
- description: Qcom Adreno GPUs implementing "arm,smmu-v2" - description: Qcom Adreno GPUs implementing "arm,smmu-v2"
items: items:
......
...@@ -645,8 +645,6 @@ struct amd_iommu { ...@@ -645,8 +645,6 @@ struct amd_iommu {
/* DebugFS Info */ /* DebugFS Info */
struct dentry *debugfs; struct dentry *debugfs;
#endif #endif
/* IRQ notifier for IntCapXT interrupt */
struct irq_affinity_notify intcapxt_notify;
}; };
static inline struct amd_iommu *dev_to_amd_iommu(struct device *dev) static inline struct amd_iommu *dev_to_amd_iommu(struct device *dev)
......
...@@ -806,16 +806,27 @@ static int iommu_ga_log_enable(struct amd_iommu *iommu) ...@@ -806,16 +806,27 @@ static int iommu_ga_log_enable(struct amd_iommu *iommu)
{ {
#ifdef CONFIG_IRQ_REMAP #ifdef CONFIG_IRQ_REMAP
u32 status, i; u32 status, i;
u64 entry;
if (!iommu->ga_log) if (!iommu->ga_log)
return -EINVAL; return -EINVAL;
status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET);
/* Check if already running */ /* Check if already running */
if (status & (MMIO_STATUS_GALOG_RUN_MASK)) status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET);
if (WARN_ON(status & (MMIO_STATUS_GALOG_RUN_MASK)))
return 0; return 0;
entry = iommu_virt_to_phys(iommu->ga_log) | GA_LOG_SIZE_512;
memcpy_toio(iommu->mmio_base + MMIO_GA_LOG_BASE_OFFSET,
&entry, sizeof(entry));
entry = (iommu_virt_to_phys(iommu->ga_log_tail) &
(BIT_ULL(52)-1)) & ~7ULL;
memcpy_toio(iommu->mmio_base + MMIO_GA_LOG_TAIL_OFFSET,
&entry, sizeof(entry));
writel(0x00, iommu->mmio_base + MMIO_GA_HEAD_OFFSET);
writel(0x00, iommu->mmio_base + MMIO_GA_TAIL_OFFSET);
iommu_feature_enable(iommu, CONTROL_GAINT_EN); iommu_feature_enable(iommu, CONTROL_GAINT_EN);
iommu_feature_enable(iommu, CONTROL_GALOG_EN); iommu_feature_enable(iommu, CONTROL_GALOG_EN);
...@@ -825,7 +836,7 @@ static int iommu_ga_log_enable(struct amd_iommu *iommu) ...@@ -825,7 +836,7 @@ static int iommu_ga_log_enable(struct amd_iommu *iommu)
break; break;
} }
if (i >= LOOP_TIMEOUT) if (WARN_ON(i >= LOOP_TIMEOUT))
return -EINVAL; return -EINVAL;
#endif /* CONFIG_IRQ_REMAP */ #endif /* CONFIG_IRQ_REMAP */
return 0; return 0;
...@@ -834,8 +845,6 @@ static int iommu_ga_log_enable(struct amd_iommu *iommu) ...@@ -834,8 +845,6 @@ static int iommu_ga_log_enable(struct amd_iommu *iommu)
static int iommu_init_ga_log(struct amd_iommu *iommu) static int iommu_init_ga_log(struct amd_iommu *iommu)
{ {
#ifdef CONFIG_IRQ_REMAP #ifdef CONFIG_IRQ_REMAP
u64 entry;
if (!AMD_IOMMU_GUEST_IR_VAPIC(amd_iommu_guest_ir)) if (!AMD_IOMMU_GUEST_IR_VAPIC(amd_iommu_guest_ir))
return 0; return 0;
...@@ -849,16 +858,6 @@ static int iommu_init_ga_log(struct amd_iommu *iommu) ...@@ -849,16 +858,6 @@ static int iommu_init_ga_log(struct amd_iommu *iommu)
if (!iommu->ga_log_tail) if (!iommu->ga_log_tail)
goto err_out; goto err_out;
entry = iommu_virt_to_phys(iommu->ga_log) | GA_LOG_SIZE_512;
memcpy_toio(iommu->mmio_base + MMIO_GA_LOG_BASE_OFFSET,
&entry, sizeof(entry));
entry = (iommu_virt_to_phys(iommu->ga_log_tail) &
(BIT_ULL(52)-1)) & ~7ULL;
memcpy_toio(iommu->mmio_base + MMIO_GA_LOG_TAIL_OFFSET,
&entry, sizeof(entry));
writel(0x00, iommu->mmio_base + MMIO_GA_HEAD_OFFSET);
writel(0x00, iommu->mmio_base + MMIO_GA_TAIL_OFFSET);
return 0; return 0;
err_out: err_out:
free_ga_log(iommu); free_ga_log(iommu);
...@@ -1523,7 +1522,7 @@ static void amd_iommu_ats_write_check_workaround(struct amd_iommu *iommu) ...@@ -1523,7 +1522,7 @@ static void amd_iommu_ats_write_check_workaround(struct amd_iommu *iommu)
} }
/* /*
* This function clues the initialization function for one IOMMU * This function glues the initialization function for one IOMMU
* together and also allocates the command buffer and programs the * together and also allocates the command buffer and programs the
* hardware. It does NOT enable the IOMMU. This is done afterwards. * hardware. It does NOT enable the IOMMU. This is done afterwards.
*/ */
...@@ -2016,48 +2015,18 @@ union intcapxt { ...@@ -2016,48 +2015,18 @@ union intcapxt {
}; };
} __attribute__ ((packed)); } __attribute__ ((packed));
/*
* There isn't really any need to mask/unmask at the irqchip level because
* the 64-bit INTCAPXT registers can be updated atomically without tearing
* when the affinity is being updated.
*/
static void intcapxt_unmask_irq(struct irq_data *data)
{
}
static void intcapxt_mask_irq(struct irq_data *data)
{
}
static struct irq_chip intcapxt_controller; static struct irq_chip intcapxt_controller;
static int intcapxt_irqdomain_activate(struct irq_domain *domain, static int intcapxt_irqdomain_activate(struct irq_domain *domain,
struct irq_data *irqd, bool reserve) struct irq_data *irqd, bool reserve)
{ {
struct amd_iommu *iommu = irqd->chip_data;
struct irq_cfg *cfg = irqd_cfg(irqd);
union intcapxt xt;
xt.capxt = 0ULL;
xt.dest_mode_logical = apic->dest_mode_logical;
xt.vector = cfg->vector;
xt.destid_0_23 = cfg->dest_apicid & GENMASK(23, 0);
xt.destid_24_31 = cfg->dest_apicid >> 24;
/**
* Current IOMMU implemtation uses the same IRQ for all
* 3 IOMMU interrupts.
*/
writeq(xt.capxt, iommu->mmio_base + MMIO_INTCAPXT_EVT_OFFSET);
writeq(xt.capxt, iommu->mmio_base + MMIO_INTCAPXT_PPR_OFFSET);
writeq(xt.capxt, iommu->mmio_base + MMIO_INTCAPXT_GALOG_OFFSET);
return 0; return 0;
} }
static void intcapxt_irqdomain_deactivate(struct irq_domain *domain, static void intcapxt_irqdomain_deactivate(struct irq_domain *domain,
struct irq_data *irqd) struct irq_data *irqd)
{ {
intcapxt_mask_irq(irqd);
} }
...@@ -2091,6 +2060,38 @@ static void intcapxt_irqdomain_free(struct irq_domain *domain, unsigned int virq ...@@ -2091,6 +2060,38 @@ static void intcapxt_irqdomain_free(struct irq_domain *domain, unsigned int virq
irq_domain_free_irqs_top(domain, virq, nr_irqs); irq_domain_free_irqs_top(domain, virq, nr_irqs);
} }
static void intcapxt_unmask_irq(struct irq_data *irqd)
{
struct amd_iommu *iommu = irqd->chip_data;
struct irq_cfg *cfg = irqd_cfg(irqd);
union intcapxt xt;
xt.capxt = 0ULL;
xt.dest_mode_logical = apic->dest_mode_logical;
xt.vector = cfg->vector;
xt.destid_0_23 = cfg->dest_apicid & GENMASK(23, 0);
xt.destid_24_31 = cfg->dest_apicid >> 24;
/**
* Current IOMMU implementation uses the same IRQ for all
* 3 IOMMU interrupts.
*/
writeq(xt.capxt, iommu->mmio_base + MMIO_INTCAPXT_EVT_OFFSET);
writeq(xt.capxt, iommu->mmio_base + MMIO_INTCAPXT_PPR_OFFSET);
writeq(xt.capxt, iommu->mmio_base + MMIO_INTCAPXT_GALOG_OFFSET);
}
static void intcapxt_mask_irq(struct irq_data *irqd)
{
struct amd_iommu *iommu = irqd->chip_data;
writeq(0, iommu->mmio_base + MMIO_INTCAPXT_EVT_OFFSET);
writeq(0, iommu->mmio_base + MMIO_INTCAPXT_PPR_OFFSET);
writeq(0, iommu->mmio_base + MMIO_INTCAPXT_GALOG_OFFSET);
}
static int intcapxt_set_affinity(struct irq_data *irqd, static int intcapxt_set_affinity(struct irq_data *irqd,
const struct cpumask *mask, bool force) const struct cpumask *mask, bool force)
{ {
...@@ -2100,8 +2101,12 @@ static int intcapxt_set_affinity(struct irq_data *irqd, ...@@ -2100,8 +2101,12 @@ static int intcapxt_set_affinity(struct irq_data *irqd,
ret = parent->chip->irq_set_affinity(parent, mask, force); ret = parent->chip->irq_set_affinity(parent, mask, force);
if (ret < 0 || ret == IRQ_SET_MASK_OK_DONE) if (ret < 0 || ret == IRQ_SET_MASK_OK_DONE)
return ret; return ret;
return 0;
}
return intcapxt_irqdomain_activate(irqd->domain, irqd, false); static int intcapxt_set_wake(struct irq_data *irqd, unsigned int on)
{
return on ? -EOPNOTSUPP : 0;
} }
static struct irq_chip intcapxt_controller = { static struct irq_chip intcapxt_controller = {
...@@ -2111,7 +2116,8 @@ static struct irq_chip intcapxt_controller = { ...@@ -2111,7 +2116,8 @@ static struct irq_chip intcapxt_controller = {
.irq_ack = irq_chip_ack_parent, .irq_ack = irq_chip_ack_parent,
.irq_retrigger = irq_chip_retrigger_hierarchy, .irq_retrigger = irq_chip_retrigger_hierarchy,
.irq_set_affinity = intcapxt_set_affinity, .irq_set_affinity = intcapxt_set_affinity,
.flags = IRQCHIP_SKIP_SET_WAKE, .irq_set_wake = intcapxt_set_wake,
.flags = IRQCHIP_MASK_ON_SUSPEND,
}; };
static const struct irq_domain_ops intcapxt_domain_ops = { static const struct irq_domain_ops intcapxt_domain_ops = {
...@@ -2173,7 +2179,6 @@ static int iommu_setup_intcapxt(struct amd_iommu *iommu) ...@@ -2173,7 +2179,6 @@ static int iommu_setup_intcapxt(struct amd_iommu *iommu)
return ret; return ret;
} }
iommu_feature_enable(iommu, CONTROL_INTCAPXT_EN);
return 0; return 0;
} }
...@@ -2196,6 +2201,10 @@ static int iommu_init_irq(struct amd_iommu *iommu) ...@@ -2196,6 +2201,10 @@ static int iommu_init_irq(struct amd_iommu *iommu)
iommu->int_enabled = true; iommu->int_enabled = true;
enable_faults: enable_faults:
if (amd_iommu_xt_mode == IRQ_REMAP_X2APIC_MODE)
iommu_feature_enable(iommu, CONTROL_INTCAPXT_EN);
iommu_feature_enable(iommu, CONTROL_EVT_INT_EN); iommu_feature_enable(iommu, CONTROL_EVT_INT_EN);
if (iommu->ppr_log != NULL) if (iommu->ppr_log != NULL)
......
...@@ -74,87 +74,61 @@ static u64 *first_pte_l7(u64 *pte, unsigned long *page_size, ...@@ -74,87 +74,61 @@ static u64 *first_pte_l7(u64 *pte, unsigned long *page_size,
* *
****************************************************************************/ ****************************************************************************/
static void free_page_list(struct page *freelist) static void free_pt_page(u64 *pt, struct list_head *freelist)
{ {
while (freelist != NULL) { struct page *p = virt_to_page(pt);
unsigned long p = (unsigned long)page_address(freelist);
freelist = freelist->freelist; list_add_tail(&p->lru, freelist);
free_page(p);
}
} }
static struct page *free_pt_page(unsigned long pt, struct page *freelist) static void free_pt_lvl(u64 *pt, struct list_head *freelist, int lvl)
{ {
struct page *p = virt_to_page((void *)pt); u64 *p;
int i;
p->freelist = freelist; for (i = 0; i < 512; ++i) {
/* PTE present? */
if (!IOMMU_PTE_PRESENT(pt[i]))
continue;
return p; /* Large PTE? */
} if (PM_PTE_LEVEL(pt[i]) == 0 ||
PM_PTE_LEVEL(pt[i]) == 7)
continue;
#define DEFINE_FREE_PT_FN(LVL, FN) \ /*
static struct page *free_pt_##LVL (unsigned long __pt, struct page *freelist) \ * Free the next level. No need to look at l1 tables here since
{ \ * they can only contain leaf PTEs; just free them directly.
unsigned long p; \ */
u64 *pt; \ p = IOMMU_PTE_PAGE(pt[i]);
int i; \ if (lvl > 2)
\ free_pt_lvl(p, freelist, lvl - 1);
pt = (u64 *)__pt; \ else
\ free_pt_page(p, freelist);
for (i = 0; i < 512; ++i) { \ }
/* PTE present? */ \
if (!IOMMU_PTE_PRESENT(pt[i])) \
continue; \
\
/* Large PTE? */ \
if (PM_PTE_LEVEL(pt[i]) == 0 || \
PM_PTE_LEVEL(pt[i]) == 7) \
continue; \
\
p = (unsigned long)IOMMU_PTE_PAGE(pt[i]); \
freelist = FN(p, freelist); \
} \
\
return free_pt_page((unsigned long)pt, freelist); \
}
DEFINE_FREE_PT_FN(l2, free_pt_page) free_pt_page(pt, freelist);
DEFINE_FREE_PT_FN(l3, free_pt_l2) }
DEFINE_FREE_PT_FN(l4, free_pt_l3)
DEFINE_FREE_PT_FN(l5, free_pt_l4)
DEFINE_FREE_PT_FN(l6, free_pt_l5)
static struct page *free_sub_pt(unsigned long root, int mode, static void free_sub_pt(u64 *root, int mode, struct list_head *freelist)
struct page *freelist)
{ {
switch (mode) { switch (mode) {
case PAGE_MODE_NONE: case PAGE_MODE_NONE:
case PAGE_MODE_7_LEVEL: case PAGE_MODE_7_LEVEL:
break; break;
case PAGE_MODE_1_LEVEL: case PAGE_MODE_1_LEVEL:
freelist = free_pt_page(root, freelist); free_pt_page(root, freelist);
break; break;
case PAGE_MODE_2_LEVEL: case PAGE_MODE_2_LEVEL:
freelist = free_pt_l2(root, freelist);
break;
case PAGE_MODE_3_LEVEL: case PAGE_MODE_3_LEVEL:
freelist = free_pt_l3(root, freelist);
break;
case PAGE_MODE_4_LEVEL: case PAGE_MODE_4_LEVEL:
freelist = free_pt_l4(root, freelist);
break;
case PAGE_MODE_5_LEVEL: case PAGE_MODE_5_LEVEL:
freelist = free_pt_l5(root, freelist);
break;
case PAGE_MODE_6_LEVEL: case PAGE_MODE_6_LEVEL:
freelist = free_pt_l6(root, freelist); free_pt_lvl(root, freelist, mode);
break; break;
default: default:
BUG(); BUG();
} }
return freelist;
} }
void amd_iommu_domain_set_pgtable(struct protection_domain *domain, void amd_iommu_domain_set_pgtable(struct protection_domain *domain,
...@@ -362,9 +336,9 @@ static u64 *fetch_pte(struct amd_io_pgtable *pgtable, ...@@ -362,9 +336,9 @@ static u64 *fetch_pte(struct amd_io_pgtable *pgtable,
return pte; return pte;
} }
static struct page *free_clear_pte(u64 *pte, u64 pteval, struct page *freelist) static void free_clear_pte(u64 *pte, u64 pteval, struct list_head *freelist)
{ {
unsigned long pt; u64 *pt;
int mode; int mode;
while (cmpxchg64(pte, pteval, 0) != pteval) { while (cmpxchg64(pte, pteval, 0) != pteval) {
...@@ -373,12 +347,12 @@ static struct page *free_clear_pte(u64 *pte, u64 pteval, struct page *freelist) ...@@ -373,12 +347,12 @@ static struct page *free_clear_pte(u64 *pte, u64 pteval, struct page *freelist)
} }
if (!IOMMU_PTE_PRESENT(pteval)) if (!IOMMU_PTE_PRESENT(pteval))
return freelist; return;
pt = (unsigned long)IOMMU_PTE_PAGE(pteval); pt = IOMMU_PTE_PAGE(pteval);
mode = IOMMU_PTE_MODE(pteval); mode = IOMMU_PTE_MODE(pteval);
return free_sub_pt(pt, mode, freelist); free_sub_pt(pt, mode, freelist);
} }
/* /*
...@@ -392,7 +366,7 @@ static int iommu_v1_map_page(struct io_pgtable_ops *ops, unsigned long iova, ...@@ -392,7 +366,7 @@ static int iommu_v1_map_page(struct io_pgtable_ops *ops, unsigned long iova,
phys_addr_t paddr, size_t size, int prot, gfp_t gfp) phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
{ {
struct protection_domain *dom = io_pgtable_ops_to_domain(ops); struct protection_domain *dom = io_pgtable_ops_to_domain(ops);
struct page *freelist = NULL; LIST_HEAD(freelist);
bool updated = false; bool updated = false;
u64 __pte, *pte; u64 __pte, *pte;
int ret, i, count; int ret, i, count;
...@@ -412,9 +386,9 @@ static int iommu_v1_map_page(struct io_pgtable_ops *ops, unsigned long iova, ...@@ -412,9 +386,9 @@ static int iommu_v1_map_page(struct io_pgtable_ops *ops, unsigned long iova,
goto out; goto out;
for (i = 0; i < count; ++i) for (i = 0; i < count; ++i)
freelist = free_clear_pte(&pte[i], pte[i], freelist); free_clear_pte(&pte[i], pte[i], &freelist);
if (freelist != NULL) if (!list_empty(&freelist))
updated = true; updated = true;
if (count > 1) { if (count > 1) {
...@@ -449,7 +423,7 @@ static int iommu_v1_map_page(struct io_pgtable_ops *ops, unsigned long iova, ...@@ -449,7 +423,7 @@ static int iommu_v1_map_page(struct io_pgtable_ops *ops, unsigned long iova,
} }
/* Everything flushed out, free pages now */ /* Everything flushed out, free pages now */
free_page_list(freelist); put_pages_list(&freelist);
return ret; return ret;
} }
...@@ -511,8 +485,7 @@ static void v1_free_pgtable(struct io_pgtable *iop) ...@@ -511,8 +485,7 @@ static void v1_free_pgtable(struct io_pgtable *iop)
{ {
struct amd_io_pgtable *pgtable = container_of(iop, struct amd_io_pgtable, iop); struct amd_io_pgtable *pgtable = container_of(iop, struct amd_io_pgtable, iop);
struct protection_domain *dom; struct protection_domain *dom;
struct page *freelist = NULL; LIST_HEAD(freelist);
unsigned long root;
if (pgtable->mode == PAGE_MODE_NONE) if (pgtable->mode == PAGE_MODE_NONE)
return; return;
...@@ -529,10 +502,9 @@ static void v1_free_pgtable(struct io_pgtable *iop) ...@@ -529,10 +502,9 @@ static void v1_free_pgtable(struct io_pgtable *iop)
BUG_ON(pgtable->mode < PAGE_MODE_NONE || BUG_ON(pgtable->mode < PAGE_MODE_NONE ||
pgtable->mode > PAGE_MODE_6_LEVEL); pgtable->mode > PAGE_MODE_6_LEVEL);
root = (unsigned long)pgtable->root; free_sub_pt(pgtable->root, pgtable->mode, &freelist);
freelist = free_sub_pt(root, pgtable->mode, freelist);
free_page_list(freelist); put_pages_list(&freelist);
} }
static struct io_pgtable *v1_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie) static struct io_pgtable *v1_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
......
...@@ -220,7 +220,7 @@ static void arm_smmu_mmu_notifier_free(struct mmu_notifier *mn) ...@@ -220,7 +220,7 @@ static void arm_smmu_mmu_notifier_free(struct mmu_notifier *mn)
kfree(mn_to_smmu(mn)); kfree(mn_to_smmu(mn));
} }
static struct mmu_notifier_ops arm_smmu_mmu_notifier_ops = { static const struct mmu_notifier_ops arm_smmu_mmu_notifier_ops = {
.invalidate_range = arm_smmu_mm_invalidate_range, .invalidate_range = arm_smmu_mm_invalidate_range,
.release = arm_smmu_mm_release, .release = arm_smmu_mm_release,
.free_notifier = arm_smmu_mmu_notifier_free, .free_notifier = arm_smmu_mmu_notifier_free,
......
...@@ -184,7 +184,6 @@ ...@@ -184,7 +184,6 @@
#else #else
#define Q_MAX_SZ_SHIFT (PAGE_SHIFT + MAX_ORDER - 1) #define Q_MAX_SZ_SHIFT (PAGE_SHIFT + MAX_ORDER - 1)
#endif #endif
#define Q_MIN_SZ_SHIFT (PAGE_SHIFT)
/* /*
* Stream table. * Stream table.
...@@ -374,7 +373,7 @@ ...@@ -374,7 +373,7 @@
/* Event queue */ /* Event queue */
#define EVTQ_ENT_SZ_SHIFT 5 #define EVTQ_ENT_SZ_SHIFT 5
#define EVTQ_ENT_DWORDS ((1 << EVTQ_ENT_SZ_SHIFT) >> 3) #define EVTQ_ENT_DWORDS ((1 << EVTQ_ENT_SZ_SHIFT) >> 3)
#define EVTQ_MAX_SZ_SHIFT (Q_MIN_SZ_SHIFT - EVTQ_ENT_SZ_SHIFT) #define EVTQ_MAX_SZ_SHIFT (Q_MAX_SZ_SHIFT - EVTQ_ENT_SZ_SHIFT)
#define EVTQ_0_ID GENMASK_ULL(7, 0) #define EVTQ_0_ID GENMASK_ULL(7, 0)
...@@ -400,7 +399,7 @@ ...@@ -400,7 +399,7 @@
/* PRI queue */ /* PRI queue */
#define PRIQ_ENT_SZ_SHIFT 4 #define PRIQ_ENT_SZ_SHIFT 4
#define PRIQ_ENT_DWORDS ((1 << PRIQ_ENT_SZ_SHIFT) >> 3) #define PRIQ_ENT_DWORDS ((1 << PRIQ_ENT_SZ_SHIFT) >> 3)
#define PRIQ_MAX_SZ_SHIFT (Q_MIN_SZ_SHIFT - PRIQ_ENT_SZ_SHIFT) #define PRIQ_MAX_SZ_SHIFT (Q_MAX_SZ_SHIFT - PRIQ_ENT_SZ_SHIFT)
#define PRIQ_0_SID GENMASK_ULL(31, 0) #define PRIQ_0_SID GENMASK_ULL(31, 0)
#define PRIQ_0_SSID GENMASK_ULL(51, 32) #define PRIQ_0_SSID GENMASK_ULL(51, 32)
......
...@@ -51,7 +51,7 @@ static void qcom_adreno_smmu_get_fault_info(const void *cookie, ...@@ -51,7 +51,7 @@ static void qcom_adreno_smmu_get_fault_info(const void *cookie,
info->fsynr1 = arm_smmu_cb_read(smmu, cfg->cbndx, ARM_SMMU_CB_FSYNR1); info->fsynr1 = arm_smmu_cb_read(smmu, cfg->cbndx, ARM_SMMU_CB_FSYNR1);
info->far = arm_smmu_cb_readq(smmu, cfg->cbndx, ARM_SMMU_CB_FAR); info->far = arm_smmu_cb_readq(smmu, cfg->cbndx, ARM_SMMU_CB_FAR);
info->cbfrsynra = arm_smmu_gr1_read(smmu, ARM_SMMU_GR1_CBFRSYNRA(cfg->cbndx)); info->cbfrsynra = arm_smmu_gr1_read(smmu, ARM_SMMU_GR1_CBFRSYNRA(cfg->cbndx));
info->ttbr0 = arm_smmu_cb_read(smmu, cfg->cbndx, ARM_SMMU_CB_TTBR0); info->ttbr0 = arm_smmu_cb_readq(smmu, cfg->cbndx, ARM_SMMU_CB_TTBR0);
info->contextidr = arm_smmu_cb_read(smmu, cfg->cbndx, ARM_SMMU_CB_CONTEXTIDR); info->contextidr = arm_smmu_cb_read(smmu, cfg->cbndx, ARM_SMMU_CB_CONTEXTIDR);
} }
...@@ -415,6 +415,7 @@ static const struct of_device_id __maybe_unused qcom_smmu_impl_of_match[] = { ...@@ -415,6 +415,7 @@ static const struct of_device_id __maybe_unused qcom_smmu_impl_of_match[] = {
{ .compatible = "qcom,sm8150-smmu-500" }, { .compatible = "qcom,sm8150-smmu-500" },
{ .compatible = "qcom,sm8250-smmu-500" }, { .compatible = "qcom,sm8250-smmu-500" },
{ .compatible = "qcom,sm8350-smmu-500" }, { .compatible = "qcom,sm8350-smmu-500" },
{ .compatible = "qcom,sm8450-smmu-500" },
{ } { }
}; };
......
...@@ -9,9 +9,12 @@ ...@@ -9,9 +9,12 @@
*/ */
#include <linux/acpi_iort.h> #include <linux/acpi_iort.h>
#include <linux/atomic.h>
#include <linux/crash_dump.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/dma-map-ops.h> #include <linux/dma-direct.h>
#include <linux/dma-iommu.h> #include <linux/dma-iommu.h>
#include <linux/dma-map-ops.h>
#include <linux/gfp.h> #include <linux/gfp.h>
#include <linux/huge_mm.h> #include <linux/huge_mm.h>
#include <linux/iommu.h> #include <linux/iommu.h>
...@@ -20,11 +23,10 @@ ...@@ -20,11 +23,10 @@
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/swiotlb.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/spinlock.h>
#include <linux/swiotlb.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/crash_dump.h>
#include <linux/dma-direct.h>
struct iommu_dma_msi_page { struct iommu_dma_msi_page {
struct list_head list; struct list_head list;
...@@ -41,7 +43,19 @@ struct iommu_dma_cookie { ...@@ -41,7 +43,19 @@ struct iommu_dma_cookie {
enum iommu_dma_cookie_type type; enum iommu_dma_cookie_type type;
union { union {
/* Full allocator for IOMMU_DMA_IOVA_COOKIE */ /* Full allocator for IOMMU_DMA_IOVA_COOKIE */
struct {
struct iova_domain iovad; struct iova_domain iovad;
struct iova_fq __percpu *fq; /* Flush queue */
/* Number of TLB flushes that have been started */
atomic64_t fq_flush_start_cnt;
/* Number of TLB flushes that have been finished */
atomic64_t fq_flush_finish_cnt;
/* Timer to regularily empty the flush queues */
struct timer_list fq_timer;
/* 1 when timer is active, 0 when not */
atomic_t fq_timer_on;
};
/* Trivial linear page allocator for IOMMU_DMA_MSI_COOKIE */ /* Trivial linear page allocator for IOMMU_DMA_MSI_COOKIE */
dma_addr_t msi_iova; dma_addr_t msi_iova;
}; };
...@@ -64,18 +78,205 @@ static int __init iommu_dma_forcedac_setup(char *str) ...@@ -64,18 +78,205 @@ static int __init iommu_dma_forcedac_setup(char *str)
} }
early_param("iommu.forcedac", iommu_dma_forcedac_setup); early_param("iommu.forcedac", iommu_dma_forcedac_setup);
static void iommu_dma_entry_dtor(unsigned long data) /* Number of entries per flush queue */
#define IOVA_FQ_SIZE 256
/* Timeout (in ms) after which entries are flushed from the queue */
#define IOVA_FQ_TIMEOUT 10
/* Flush queue entry for deferred flushing */
struct iova_fq_entry {
unsigned long iova_pfn;
unsigned long pages;
struct list_head freelist;
u64 counter; /* Flush counter when this entry was added */
};
/* Per-CPU flush queue structure */
struct iova_fq {
struct iova_fq_entry entries[IOVA_FQ_SIZE];
unsigned int head, tail;
spinlock_t lock;
};
#define fq_ring_for_each(i, fq) \
for ((i) = (fq)->head; (i) != (fq)->tail; (i) = ((i) + 1) % IOVA_FQ_SIZE)
static inline bool fq_full(struct iova_fq *fq)
{
assert_spin_locked(&fq->lock);
return (((fq->tail + 1) % IOVA_FQ_SIZE) == fq->head);
}
static inline unsigned int fq_ring_add(struct iova_fq *fq)
{ {
struct page *freelist = (struct page *)data; unsigned int idx = fq->tail;
while (freelist) { assert_spin_locked(&fq->lock);
unsigned long p = (unsigned long)page_address(freelist);
freelist = freelist->freelist; fq->tail = (idx + 1) % IOVA_FQ_SIZE;
free_page(p);
return idx;
}
static void fq_ring_free(struct iommu_dma_cookie *cookie, struct iova_fq *fq)
{
u64 counter = atomic64_read(&cookie->fq_flush_finish_cnt);
unsigned int idx;
assert_spin_locked(&fq->lock);
fq_ring_for_each(idx, fq) {
if (fq->entries[idx].counter >= counter)
break;
put_pages_list(&fq->entries[idx].freelist);
free_iova_fast(&cookie->iovad,
fq->entries[idx].iova_pfn,
fq->entries[idx].pages);
fq->head = (fq->head + 1) % IOVA_FQ_SIZE;
} }
} }
static void fq_flush_iotlb(struct iommu_dma_cookie *cookie)
{
atomic64_inc(&cookie->fq_flush_start_cnt);
cookie->fq_domain->ops->flush_iotlb_all(cookie->fq_domain);
atomic64_inc(&cookie->fq_flush_finish_cnt);
}
static void fq_flush_timeout(struct timer_list *t)
{
struct iommu_dma_cookie *cookie = from_timer(cookie, t, fq_timer);
int cpu;
atomic_set(&cookie->fq_timer_on, 0);
fq_flush_iotlb(cookie);
for_each_possible_cpu(cpu) {
unsigned long flags;
struct iova_fq *fq;
fq = per_cpu_ptr(cookie->fq, cpu);
spin_lock_irqsave(&fq->lock, flags);
fq_ring_free(cookie, fq);
spin_unlock_irqrestore(&fq->lock, flags);
}
}
static void queue_iova(struct iommu_dma_cookie *cookie,
unsigned long pfn, unsigned long pages,
struct list_head *freelist)
{
struct iova_fq *fq;
unsigned long flags;
unsigned int idx;
/*
* Order against the IOMMU driver's pagetable update from unmapping
* @pte, to guarantee that fq_flush_iotlb() observes that if called
* from a different CPU before we release the lock below. Full barrier
* so it also pairs with iommu_dma_init_fq() to avoid seeing partially
* written fq state here.
*/
smp_mb();
fq = raw_cpu_ptr(cookie->fq);
spin_lock_irqsave(&fq->lock, flags);
/*
* First remove all entries from the flush queue that have already been
* flushed out on another CPU. This makes the fq_full() check below less
* likely to be true.
*/
fq_ring_free(cookie, fq);
if (fq_full(fq)) {
fq_flush_iotlb(cookie);
fq_ring_free(cookie, fq);
}
idx = fq_ring_add(fq);
fq->entries[idx].iova_pfn = pfn;
fq->entries[idx].pages = pages;
fq->entries[idx].counter = atomic64_read(&cookie->fq_flush_start_cnt);
list_splice(freelist, &fq->entries[idx].freelist);
spin_unlock_irqrestore(&fq->lock, flags);
/* Avoid false sharing as much as possible. */
if (!atomic_read(&cookie->fq_timer_on) &&
!atomic_xchg(&cookie->fq_timer_on, 1))
mod_timer(&cookie->fq_timer,
jiffies + msecs_to_jiffies(IOVA_FQ_TIMEOUT));
}
static void iommu_dma_free_fq(struct iommu_dma_cookie *cookie)
{
int cpu, idx;
if (!cookie->fq)
return;
del_timer_sync(&cookie->fq_timer);
/* The IOVAs will be torn down separately, so just free our queued pages */
for_each_possible_cpu(cpu) {
struct iova_fq *fq = per_cpu_ptr(cookie->fq, cpu);
fq_ring_for_each(idx, fq)
put_pages_list(&fq->entries[idx].freelist);
}
free_percpu(cookie->fq);
}
/* sysfs updates are serialised by the mutex of the group owning @domain */
int iommu_dma_init_fq(struct iommu_domain *domain)
{
struct iommu_dma_cookie *cookie = domain->iova_cookie;
struct iova_fq __percpu *queue;
int i, cpu;
if (cookie->fq_domain)
return 0;
atomic64_set(&cookie->fq_flush_start_cnt, 0);
atomic64_set(&cookie->fq_flush_finish_cnt, 0);
queue = alloc_percpu(struct iova_fq);
if (!queue) {
pr_warn("iova flush queue initialization failed\n");
return -ENOMEM;
}
for_each_possible_cpu(cpu) {
struct iova_fq *fq = per_cpu_ptr(queue, cpu);
fq->head = 0;
fq->tail = 0;
spin_lock_init(&fq->lock);
for (i = 0; i < IOVA_FQ_SIZE; i++)
INIT_LIST_HEAD(&fq->entries[i].freelist);
}
cookie->fq = queue;
timer_setup(&cookie->fq_timer, fq_flush_timeout, 0);
atomic_set(&cookie->fq_timer_on, 0);
/*
* Prevent incomplete fq state being observable. Pairs with path from
* __iommu_dma_unmap() through iommu_dma_free_iova() to queue_iova()
*/
smp_wmb();
WRITE_ONCE(cookie->fq_domain, domain);
return 0;
}
static inline size_t cookie_msi_granule(struct iommu_dma_cookie *cookie) static inline size_t cookie_msi_granule(struct iommu_dma_cookie *cookie)
{ {
if (cookie->type == IOMMU_DMA_IOVA_COOKIE) if (cookie->type == IOMMU_DMA_IOVA_COOKIE)
...@@ -156,8 +357,10 @@ void iommu_put_dma_cookie(struct iommu_domain *domain) ...@@ -156,8 +357,10 @@ void iommu_put_dma_cookie(struct iommu_domain *domain)
if (!cookie) if (!cookie)
return; return;
if (cookie->type == IOMMU_DMA_IOVA_COOKIE && cookie->iovad.granule) if (cookie->type == IOMMU_DMA_IOVA_COOKIE && cookie->iovad.granule) {
iommu_dma_free_fq(cookie);
put_iova_domain(&cookie->iovad); put_iova_domain(&cookie->iovad);
}
list_for_each_entry_safe(msi, tmp, &cookie->msi_page_list, list) { list_for_each_entry_safe(msi, tmp, &cookie->msi_page_list, list) {
list_del(&msi->list); list_del(&msi->list);
...@@ -294,17 +497,6 @@ static int iova_reserve_iommu_regions(struct device *dev, ...@@ -294,17 +497,6 @@ static int iova_reserve_iommu_regions(struct device *dev,
return ret; return ret;
} }
static void iommu_dma_flush_iotlb_all(struct iova_domain *iovad)
{
struct iommu_dma_cookie *cookie;
struct iommu_domain *domain;
cookie = container_of(iovad, struct iommu_dma_cookie, iovad);
domain = cookie->fq_domain;
domain->ops->flush_iotlb_all(domain);
}
static bool dev_is_untrusted(struct device *dev) static bool dev_is_untrusted(struct device *dev)
{ {
return dev_is_pci(dev) && to_pci_dev(dev)->untrusted; return dev_is_pci(dev) && to_pci_dev(dev)->untrusted;
...@@ -315,30 +507,6 @@ static bool dev_use_swiotlb(struct device *dev) ...@@ -315,30 +507,6 @@ static bool dev_use_swiotlb(struct device *dev)
return IS_ENABLED(CONFIG_SWIOTLB) && dev_is_untrusted(dev); return IS_ENABLED(CONFIG_SWIOTLB) && dev_is_untrusted(dev);
} }
/* sysfs updates are serialised by the mutex of the group owning @domain */
int iommu_dma_init_fq(struct iommu_domain *domain)
{
struct iommu_dma_cookie *cookie = domain->iova_cookie;
int ret;
if (cookie->fq_domain)
return 0;
ret = init_iova_flush_queue(&cookie->iovad, iommu_dma_flush_iotlb_all,
iommu_dma_entry_dtor);
if (ret) {
pr_warn("iova flush queue initialization failed\n");
return ret;
}
/*
* Prevent incomplete iovad->fq being observable. Pairs with path from
* __iommu_dma_unmap() through iommu_dma_free_iova() to queue_iova()
*/
smp_wmb();
WRITE_ONCE(cookie->fq_domain, domain);
return 0;
}
/** /**
* iommu_dma_init_domain - Initialise a DMA mapping domain * iommu_dma_init_domain - Initialise a DMA mapping domain
* @domain: IOMMU domain previously prepared by iommu_get_dma_cookie() * @domain: IOMMU domain previously prepared by iommu_get_dma_cookie()
...@@ -442,14 +610,6 @@ static dma_addr_t iommu_dma_alloc_iova(struct iommu_domain *domain, ...@@ -442,14 +610,6 @@ static dma_addr_t iommu_dma_alloc_iova(struct iommu_domain *domain,
shift = iova_shift(iovad); shift = iova_shift(iovad);
iova_len = size >> shift; iova_len = size >> shift;
/*
* Freeing non-power-of-two-sized allocations back into the IOVA caches
* will come back to bite us badly, so we have to waste a bit of space
* rounding up anything cacheable to make sure that can't happen. The
* order of the unadjusted size will still match upon freeing.
*/
if (iova_len < (1 << (IOVA_RANGE_CACHE_MAX_SIZE - 1)))
iova_len = roundup_pow_of_two(iova_len);
dma_limit = min_not_zero(dma_limit, dev->bus_dma_limit); dma_limit = min_not_zero(dma_limit, dev->bus_dma_limit);
...@@ -477,9 +637,9 @@ static void iommu_dma_free_iova(struct iommu_dma_cookie *cookie, ...@@ -477,9 +637,9 @@ static void iommu_dma_free_iova(struct iommu_dma_cookie *cookie,
if (cookie->type == IOMMU_DMA_MSI_COOKIE) if (cookie->type == IOMMU_DMA_MSI_COOKIE)
cookie->msi_iova -= size; cookie->msi_iova -= size;
else if (gather && gather->queued) else if (gather && gather->queued)
queue_iova(iovad, iova_pfn(iovad, iova), queue_iova(cookie, iova_pfn(iovad, iova),
size >> iova_shift(iovad), size >> iova_shift(iovad),
(unsigned long)gather->freelist); &gather->freelist);
else else
free_iova_fast(iovad, iova_pfn(iovad, iova), free_iova_fast(iovad, iova_pfn(iovad, iova),
size >> iova_shift(iovad)); size >> iova_shift(iovad));
......
...@@ -133,11 +133,6 @@ static inline unsigned long lvl_to_nr_pages(unsigned int lvl) ...@@ -133,11 +133,6 @@ static inline unsigned long lvl_to_nr_pages(unsigned int lvl)
/* VT-d pages must always be _smaller_ than MM pages. Otherwise things /* VT-d pages must always be _smaller_ than MM pages. Otherwise things
are never going to work. */ are never going to work. */
static inline unsigned long dma_to_mm_pfn(unsigned long dma_pfn)
{
return dma_pfn >> (PAGE_SHIFT - VTD_PAGE_SHIFT);
}
static inline unsigned long mm_to_dma_pfn(unsigned long mm_pfn) static inline unsigned long mm_to_dma_pfn(unsigned long mm_pfn)
{ {
return mm_pfn << (PAGE_SHIFT - VTD_PAGE_SHIFT); return mm_pfn << (PAGE_SHIFT - VTD_PAGE_SHIFT);
...@@ -1280,10 +1275,6 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain, ...@@ -1280,10 +1275,6 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain,
unsigned long last_pfn, unsigned long last_pfn,
int retain_level) int retain_level)
{ {
BUG_ON(!domain_pfn_supported(domain, start_pfn));
BUG_ON(!domain_pfn_supported(domain, last_pfn));
BUG_ON(start_pfn > last_pfn);
dma_pte_clear_range(domain, start_pfn, last_pfn); dma_pte_clear_range(domain, start_pfn, last_pfn);
/* We don't need lock here; nobody else touches the iova range */ /* We don't need lock here; nobody else touches the iova range */
...@@ -1303,35 +1294,30 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain, ...@@ -1303,35 +1294,30 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain,
know the hardware page-walk will no longer touch them. know the hardware page-walk will no longer touch them.
The 'pte' argument is the *parent* PTE, pointing to the page that is to The 'pte' argument is the *parent* PTE, pointing to the page that is to
be freed. */ be freed. */
static struct page *dma_pte_list_pagetables(struct dmar_domain *domain, static void dma_pte_list_pagetables(struct dmar_domain *domain,
int level, struct dma_pte *pte, int level, struct dma_pte *pte,
struct page *freelist) struct list_head *freelist)
{ {
struct page *pg; struct page *pg;
pg = pfn_to_page(dma_pte_addr(pte) >> PAGE_SHIFT); pg = pfn_to_page(dma_pte_addr(pte) >> PAGE_SHIFT);
pg->freelist = freelist; list_add_tail(&pg->lru, freelist);
freelist = pg;
if (level == 1) if (level == 1)
return freelist; return;
pte = page_address(pg); pte = page_address(pg);
do { do {
if (dma_pte_present(pte) && !dma_pte_superpage(pte)) if (dma_pte_present(pte) && !dma_pte_superpage(pte))
freelist = dma_pte_list_pagetables(domain, level - 1, dma_pte_list_pagetables(domain, level - 1, pte, freelist);
pte, freelist);
pte++; pte++;
} while (!first_pte_in_page(pte)); } while (!first_pte_in_page(pte));
return freelist;
} }
static struct page *dma_pte_clear_level(struct dmar_domain *domain, int level, static void dma_pte_clear_level(struct dmar_domain *domain, int level,
struct dma_pte *pte, unsigned long pfn, struct dma_pte *pte, unsigned long pfn,
unsigned long start_pfn, unsigned long start_pfn, unsigned long last_pfn,
unsigned long last_pfn, struct list_head *freelist)
struct page *freelist)
{ {
struct dma_pte *first_pte = NULL, *last_pte = NULL; struct dma_pte *first_pte = NULL, *last_pte = NULL;
...@@ -1350,7 +1336,7 @@ static struct page *dma_pte_clear_level(struct dmar_domain *domain, int level, ...@@ -1350,7 +1336,7 @@ static struct page *dma_pte_clear_level(struct dmar_domain *domain, int level,
/* These suborbinate page tables are going away entirely. Don't /* These suborbinate page tables are going away entirely. Don't
bother to clear them; we're just going to *free* them. */ bother to clear them; we're just going to *free* them. */
if (level > 1 && !dma_pte_superpage(pte)) if (level > 1 && !dma_pte_superpage(pte))
freelist = dma_pte_list_pagetables(domain, level - 1, pte, freelist); dma_pte_list_pagetables(domain, level - 1, pte, freelist);
dma_clear_pte(pte); dma_clear_pte(pte);
if (!first_pte) if (!first_pte)
...@@ -1358,7 +1344,7 @@ static struct page *dma_pte_clear_level(struct dmar_domain *domain, int level, ...@@ -1358,7 +1344,7 @@ static struct page *dma_pte_clear_level(struct dmar_domain *domain, int level,
last_pte = pte; last_pte = pte;
} else if (level > 1) { } else if (level > 1) {
/* Recurse down into a level that isn't *entirely* obsolete */ /* Recurse down into a level that isn't *entirely* obsolete */
freelist = dma_pte_clear_level(domain, level - 1, dma_pte_clear_level(domain, level - 1,
phys_to_virt(dma_pte_addr(pte)), phys_to_virt(dma_pte_addr(pte)),
level_pfn, start_pfn, last_pfn, level_pfn, start_pfn, last_pfn,
freelist); freelist);
...@@ -1370,47 +1356,28 @@ static struct page *dma_pte_clear_level(struct dmar_domain *domain, int level, ...@@ -1370,47 +1356,28 @@ static struct page *dma_pte_clear_level(struct dmar_domain *domain, int level,
if (first_pte) if (first_pte)
domain_flush_cache(domain, first_pte, domain_flush_cache(domain, first_pte,
(void *)++last_pte - (void *)first_pte); (void *)++last_pte - (void *)first_pte);
return freelist;
} }
/* We can't just free the pages because the IOMMU may still be walking /* We can't just free the pages because the IOMMU may still be walking
the page tables, and may have cached the intermediate levels. The the page tables, and may have cached the intermediate levels. The
pages can only be freed after the IOTLB flush has been done. */ pages can only be freed after the IOTLB flush has been done. */
static struct page *domain_unmap(struct dmar_domain *domain, static void domain_unmap(struct dmar_domain *domain, unsigned long start_pfn,
unsigned long start_pfn, unsigned long last_pfn, struct list_head *freelist)
unsigned long last_pfn,
struct page *freelist)
{ {
BUG_ON(!domain_pfn_supported(domain, start_pfn)); BUG_ON(!domain_pfn_supported(domain, start_pfn));
BUG_ON(!domain_pfn_supported(domain, last_pfn)); BUG_ON(!domain_pfn_supported(domain, last_pfn));
BUG_ON(start_pfn > last_pfn); BUG_ON(start_pfn > last_pfn);
/* we don't need lock here; nobody else touches the iova range */ /* we don't need lock here; nobody else touches the iova range */
freelist = dma_pte_clear_level(domain, agaw_to_level(domain->agaw), dma_pte_clear_level(domain, agaw_to_level(domain->agaw),
domain->pgd, 0, start_pfn, last_pfn, domain->pgd, 0, start_pfn, last_pfn, freelist);
freelist);
/* free pgd */ /* free pgd */
if (start_pfn == 0 && last_pfn == DOMAIN_MAX_PFN(domain->gaw)) { if (start_pfn == 0 && last_pfn == DOMAIN_MAX_PFN(domain->gaw)) {
struct page *pgd_page = virt_to_page(domain->pgd); struct page *pgd_page = virt_to_page(domain->pgd);
pgd_page->freelist = freelist; list_add_tail(&pgd_page->lru, freelist);
freelist = pgd_page;
domain->pgd = NULL; domain->pgd = NULL;
} }
return freelist;
}
static void dma_free_pagelist(struct page *freelist)
{
struct page *pg;
while ((pg = freelist)) {
freelist = pg->freelist;
free_pgtable_page(page_address(pg));
}
} }
/* iommu handling */ /* iommu handling */
...@@ -1878,17 +1845,16 @@ static void iommu_disable_translation(struct intel_iommu *iommu) ...@@ -1878,17 +1845,16 @@ static void iommu_disable_translation(struct intel_iommu *iommu)
static int iommu_init_domains(struct intel_iommu *iommu) static int iommu_init_domains(struct intel_iommu *iommu)
{ {
u32 ndomains, nlongs; u32 ndomains;
size_t size; size_t size;
ndomains = cap_ndoms(iommu->cap); ndomains = cap_ndoms(iommu->cap);
pr_debug("%s: Number of Domains supported <%d>\n", pr_debug("%s: Number of Domains supported <%d>\n",
iommu->name, ndomains); iommu->name, ndomains);
nlongs = BITS_TO_LONGS(ndomains);
spin_lock_init(&iommu->lock); spin_lock_init(&iommu->lock);
iommu->domain_ids = kcalloc(nlongs, sizeof(unsigned long), GFP_KERNEL); iommu->domain_ids = bitmap_zalloc(ndomains, GFP_KERNEL);
if (!iommu->domain_ids) if (!iommu->domain_ids)
return -ENOMEM; return -ENOMEM;
...@@ -1903,7 +1869,7 @@ static int iommu_init_domains(struct intel_iommu *iommu) ...@@ -1903,7 +1869,7 @@ static int iommu_init_domains(struct intel_iommu *iommu)
if (!iommu->domains || !iommu->domains[0]) { if (!iommu->domains || !iommu->domains[0]) {
pr_err("%s: Allocating domain array failed\n", pr_err("%s: Allocating domain array failed\n",
iommu->name); iommu->name);
kfree(iommu->domain_ids); bitmap_free(iommu->domain_ids);
kfree(iommu->domains); kfree(iommu->domains);
iommu->domain_ids = NULL; iommu->domain_ids = NULL;
iommu->domains = NULL; iommu->domains = NULL;
...@@ -1964,7 +1930,7 @@ static void free_dmar_iommu(struct intel_iommu *iommu) ...@@ -1964,7 +1930,7 @@ static void free_dmar_iommu(struct intel_iommu *iommu)
for (i = 0; i < elems; i++) for (i = 0; i < elems; i++)
kfree(iommu->domains[i]); kfree(iommu->domains[i]);
kfree(iommu->domains); kfree(iommu->domains);
kfree(iommu->domain_ids); bitmap_free(iommu->domain_ids);
iommu->domains = NULL; iommu->domains = NULL;
iommu->domain_ids = NULL; iommu->domain_ids = NULL;
} }
...@@ -2095,11 +2061,10 @@ static void domain_exit(struct dmar_domain *domain) ...@@ -2095,11 +2061,10 @@ static void domain_exit(struct dmar_domain *domain)
domain_remove_dev_info(domain); domain_remove_dev_info(domain);
if (domain->pgd) { if (domain->pgd) {
struct page *freelist; LIST_HEAD(freelist);
freelist = domain_unmap(domain, 0, domain_unmap(domain, 0, DOMAIN_MAX_PFN(domain->gaw), &freelist);
DOMAIN_MAX_PFN(domain->gaw), NULL); put_pages_list(&freelist);
dma_free_pagelist(freelist);
} }
free_domain_mem(domain); free_domain_mem(domain);
...@@ -2112,10 +2077,10 @@ static void domain_exit(struct dmar_domain *domain) ...@@ -2112,10 +2077,10 @@ static void domain_exit(struct dmar_domain *domain)
*/ */
static inline unsigned long context_get_sm_pds(struct pasid_table *table) static inline unsigned long context_get_sm_pds(struct pasid_table *table)
{ {
int pds, max_pde; unsigned long pds, max_pde;
max_pde = table->max_pasid >> PASID_PDE_SHIFT; max_pde = table->max_pasid >> PASID_PDE_SHIFT;
pds = find_first_bit((unsigned long *)&max_pde, MAX_NR_PASID_BITS); pds = find_first_bit(&max_pde, MAX_NR_PASID_BITS);
if (pds < 7) if (pds < 7)
return 0; return 0;
...@@ -4192,19 +4157,17 @@ static int intel_iommu_memory_notifier(struct notifier_block *nb, ...@@ -4192,19 +4157,17 @@ static int intel_iommu_memory_notifier(struct notifier_block *nb,
{ {
struct dmar_drhd_unit *drhd; struct dmar_drhd_unit *drhd;
struct intel_iommu *iommu; struct intel_iommu *iommu;
struct page *freelist; LIST_HEAD(freelist);
freelist = domain_unmap(si_domain, domain_unmap(si_domain, start_vpfn, last_vpfn, &freelist);
start_vpfn, last_vpfn,
NULL);
rcu_read_lock(); rcu_read_lock();
for_each_active_iommu(iommu, drhd) for_each_active_iommu(iommu, drhd)
iommu_flush_iotlb_psi(iommu, si_domain, iommu_flush_iotlb_psi(iommu, si_domain,
start_vpfn, mhp->nr_pages, start_vpfn, mhp->nr_pages,
!freelist, 0); list_empty(&freelist), 0);
rcu_read_unlock(); rcu_read_unlock();
dma_free_pagelist(freelist); put_pages_list(&freelist);
} }
break; break;
} }
...@@ -5211,8 +5174,7 @@ static size_t intel_iommu_unmap(struct iommu_domain *domain, ...@@ -5211,8 +5174,7 @@ static size_t intel_iommu_unmap(struct iommu_domain *domain,
start_pfn = iova >> VTD_PAGE_SHIFT; start_pfn = iova >> VTD_PAGE_SHIFT;
last_pfn = (iova + size - 1) >> VTD_PAGE_SHIFT; last_pfn = (iova + size - 1) >> VTD_PAGE_SHIFT;
gather->freelist = domain_unmap(dmar_domain, start_pfn, domain_unmap(dmar_domain, start_pfn, last_pfn, &gather->freelist);
last_pfn, gather->freelist);
if (dmar_domain->max_addr == iova + size) if (dmar_domain->max_addr == iova + size)
dmar_domain->max_addr = iova; dmar_domain->max_addr = iova;
...@@ -5248,9 +5210,10 @@ static void intel_iommu_tlb_sync(struct iommu_domain *domain, ...@@ -5248,9 +5210,10 @@ static void intel_iommu_tlb_sync(struct iommu_domain *domain,
for_each_domain_iommu(iommu_id, dmar_domain) for_each_domain_iommu(iommu_id, dmar_domain)
iommu_flush_iotlb_psi(g_iommus[iommu_id], dmar_domain, iommu_flush_iotlb_psi(g_iommus[iommu_id], dmar_domain,
start_pfn, nrpages, !gather->freelist, 0); start_pfn, nrpages,
list_empty(&gather->freelist), 0);
dma_free_pagelist(gather->freelist); put_pages_list(&gather->freelist);
} }
static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain, static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain,
......
...@@ -246,13 +246,17 @@ static void *__arm_v7s_alloc_table(int lvl, gfp_t gfp, ...@@ -246,13 +246,17 @@ static void *__arm_v7s_alloc_table(int lvl, gfp_t gfp,
__GFP_ZERO | ARM_V7S_TABLE_GFP_DMA, get_order(size)); __GFP_ZERO | ARM_V7S_TABLE_GFP_DMA, get_order(size));
else if (lvl == 2) else if (lvl == 2)
table = kmem_cache_zalloc(data->l2_tables, gfp); table = kmem_cache_zalloc(data->l2_tables, gfp);
if (!table)
return NULL;
phys = virt_to_phys(table); phys = virt_to_phys(table);
if (phys != (arm_v7s_iopte)phys) { if (phys != (arm_v7s_iopte)phys) {
/* Doesn't fit in PTE */ /* Doesn't fit in PTE */
dev_err(dev, "Page table does not fit in PTE: %pa", &phys); dev_err(dev, "Page table does not fit in PTE: %pa", &phys);
goto out_free; goto out_free;
} }
if (table && !cfg->coherent_walk) { if (!cfg->coherent_walk) {
dma = dma_map_single(dev, table, size, DMA_TO_DEVICE); dma = dma_map_single(dev, table, size, DMA_TO_DEVICE);
if (dma_mapping_error(dev, dma)) if (dma_mapping_error(dev, dma))
goto out_free; goto out_free;
......
...@@ -315,11 +315,12 @@ static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data, ...@@ -315,11 +315,12 @@ static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data,
static arm_lpae_iopte arm_lpae_install_table(arm_lpae_iopte *table, static arm_lpae_iopte arm_lpae_install_table(arm_lpae_iopte *table,
arm_lpae_iopte *ptep, arm_lpae_iopte *ptep,
arm_lpae_iopte curr, arm_lpae_iopte curr,
struct io_pgtable_cfg *cfg) struct arm_lpae_io_pgtable *data)
{ {
arm_lpae_iopte old, new; arm_lpae_iopte old, new;
struct io_pgtable_cfg *cfg = &data->iop.cfg;
new = __pa(table) | ARM_LPAE_PTE_TYPE_TABLE; new = paddr_to_iopte(__pa(table), data) | ARM_LPAE_PTE_TYPE_TABLE;
if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_NS) if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_NS)
new |= ARM_LPAE_PTE_NSTABLE; new |= ARM_LPAE_PTE_NSTABLE;
...@@ -380,7 +381,7 @@ static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova, ...@@ -380,7 +381,7 @@ static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova,
if (!cptep) if (!cptep)
return -ENOMEM; return -ENOMEM;
pte = arm_lpae_install_table(cptep, ptep, 0, cfg); pte = arm_lpae_install_table(cptep, ptep, 0, data);
if (pte) if (pte)
__arm_lpae_free_pages(cptep, tblsz, cfg); __arm_lpae_free_pages(cptep, tblsz, cfg);
} else if (!cfg->coherent_walk && !(pte & ARM_LPAE_PTE_SW_SYNC)) { } else if (!cfg->coherent_walk && !(pte & ARM_LPAE_PTE_SW_SYNC)) {
...@@ -592,7 +593,7 @@ static size_t arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data, ...@@ -592,7 +593,7 @@ static size_t arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data,
__arm_lpae_init_pte(data, blk_paddr, pte, lvl, 1, &tablep[i]); __arm_lpae_init_pte(data, blk_paddr, pte, lvl, 1, &tablep[i]);
} }
pte = arm_lpae_install_table(tablep, ptep, blk_pte, cfg); pte = arm_lpae_install_table(tablep, ptep, blk_pte, data);
if (pte != blk_pte) { if (pte != blk_pte) {
__arm_lpae_free_pages(tablep, tablesz, cfg); __arm_lpae_free_pages(tablep, tablesz, cfg);
/* /*
......
...@@ -288,11 +288,11 @@ int iommu_probe_device(struct device *dev) ...@@ -288,11 +288,11 @@ int iommu_probe_device(struct device *dev)
*/ */
mutex_lock(&group->mutex); mutex_lock(&group->mutex);
iommu_alloc_default_domain(group, dev); iommu_alloc_default_domain(group, dev);
mutex_unlock(&group->mutex);
if (group->default_domain) { if (group->default_domain) {
ret = __iommu_attach_device(group->default_domain, dev); ret = __iommu_attach_device(group->default_domain, dev);
if (ret) { if (ret) {
mutex_unlock(&group->mutex);
iommu_group_put(group); iommu_group_put(group);
goto err_release; goto err_release;
} }
...@@ -300,6 +300,7 @@ int iommu_probe_device(struct device *dev) ...@@ -300,6 +300,7 @@ int iommu_probe_device(struct device *dev)
iommu_create_device_direct_mappings(group, dev); iommu_create_device_direct_mappings(group, dev);
mutex_unlock(&group->mutex);
iommu_group_put(group); iommu_group_put(group);
if (ops->probe_finalize) if (ops->probe_finalize)
......
...@@ -24,8 +24,6 @@ static unsigned long iova_rcache_get(struct iova_domain *iovad, ...@@ -24,8 +24,6 @@ static unsigned long iova_rcache_get(struct iova_domain *iovad,
static void init_iova_rcaches(struct iova_domain *iovad); static void init_iova_rcaches(struct iova_domain *iovad);
static void free_cpu_cached_iovas(unsigned int cpu, struct iova_domain *iovad); static void free_cpu_cached_iovas(unsigned int cpu, struct iova_domain *iovad);
static void free_iova_rcaches(struct iova_domain *iovad); static void free_iova_rcaches(struct iova_domain *iovad);
static void fq_destroy_all_entries(struct iova_domain *iovad);
static void fq_flush_timeout(struct timer_list *t);
static int iova_cpuhp_dead(unsigned int cpu, struct hlist_node *node) static int iova_cpuhp_dead(unsigned int cpu, struct hlist_node *node)
{ {
...@@ -63,8 +61,6 @@ init_iova_domain(struct iova_domain *iovad, unsigned long granule, ...@@ -63,8 +61,6 @@ init_iova_domain(struct iova_domain *iovad, unsigned long granule,
iovad->start_pfn = start_pfn; iovad->start_pfn = start_pfn;
iovad->dma_32bit_pfn = 1UL << (32 - iova_shift(iovad)); iovad->dma_32bit_pfn = 1UL << (32 - iova_shift(iovad));
iovad->max32_alloc_size = iovad->dma_32bit_pfn; iovad->max32_alloc_size = iovad->dma_32bit_pfn;
iovad->flush_cb = NULL;
iovad->fq = NULL;
iovad->anchor.pfn_lo = iovad->anchor.pfn_hi = IOVA_ANCHOR; iovad->anchor.pfn_lo = iovad->anchor.pfn_hi = IOVA_ANCHOR;
rb_link_node(&iovad->anchor.node, NULL, &iovad->rbroot.rb_node); rb_link_node(&iovad->anchor.node, NULL, &iovad->rbroot.rb_node);
rb_insert_color(&iovad->anchor.node, &iovad->rbroot); rb_insert_color(&iovad->anchor.node, &iovad->rbroot);
...@@ -73,62 +69,6 @@ init_iova_domain(struct iova_domain *iovad, unsigned long granule, ...@@ -73,62 +69,6 @@ init_iova_domain(struct iova_domain *iovad, unsigned long granule,
} }
EXPORT_SYMBOL_GPL(init_iova_domain); EXPORT_SYMBOL_GPL(init_iova_domain);
static bool has_iova_flush_queue(struct iova_domain *iovad)
{
return !!iovad->fq;
}
static void free_iova_flush_queue(struct iova_domain *iovad)
{
if (!has_iova_flush_queue(iovad))
return;
if (timer_pending(&iovad->fq_timer))
del_timer(&iovad->fq_timer);
fq_destroy_all_entries(iovad);
free_percpu(iovad->fq);
iovad->fq = NULL;
iovad->flush_cb = NULL;
iovad->entry_dtor = NULL;
}
int init_iova_flush_queue(struct iova_domain *iovad,
iova_flush_cb flush_cb, iova_entry_dtor entry_dtor)
{
struct iova_fq __percpu *queue;
int cpu;
atomic64_set(&iovad->fq_flush_start_cnt, 0);
atomic64_set(&iovad->fq_flush_finish_cnt, 0);
queue = alloc_percpu(struct iova_fq);
if (!queue)
return -ENOMEM;
iovad->flush_cb = flush_cb;
iovad->entry_dtor = entry_dtor;
for_each_possible_cpu(cpu) {
struct iova_fq *fq;
fq = per_cpu_ptr(queue, cpu);
fq->head = 0;
fq->tail = 0;
spin_lock_init(&fq->lock);
}
iovad->fq = queue;
timer_setup(&iovad->fq_timer, fq_flush_timeout, 0);
atomic_set(&iovad->fq_timer_on, 0);
return 0;
}
static struct rb_node * static struct rb_node *
__get_cached_rbnode(struct iova_domain *iovad, unsigned long limit_pfn) __get_cached_rbnode(struct iova_domain *iovad, unsigned long limit_pfn)
{ {
...@@ -497,6 +437,15 @@ alloc_iova_fast(struct iova_domain *iovad, unsigned long size, ...@@ -497,6 +437,15 @@ alloc_iova_fast(struct iova_domain *iovad, unsigned long size,
unsigned long iova_pfn; unsigned long iova_pfn;
struct iova *new_iova; struct iova *new_iova;
/*
* Freeing non-power-of-two-sized allocations back into the IOVA caches
* will come back to bite us badly, so we have to waste a bit of space
* rounding up anything cacheable to make sure that can't happen. The
* order of the unadjusted size will still match upon freeing.
*/
if (size < (1 << (IOVA_RANGE_CACHE_MAX_SIZE - 1)))
size = roundup_pow_of_two(size);
iova_pfn = iova_rcache_get(iovad, size, limit_pfn + 1); iova_pfn = iova_rcache_get(iovad, size, limit_pfn + 1);
if (iova_pfn) if (iova_pfn)
return iova_pfn; return iova_pfn;
...@@ -539,144 +488,6 @@ free_iova_fast(struct iova_domain *iovad, unsigned long pfn, unsigned long size) ...@@ -539,144 +488,6 @@ free_iova_fast(struct iova_domain *iovad, unsigned long pfn, unsigned long size)
} }
EXPORT_SYMBOL_GPL(free_iova_fast); EXPORT_SYMBOL_GPL(free_iova_fast);
#define fq_ring_for_each(i, fq) \
for ((i) = (fq)->head; (i) != (fq)->tail; (i) = ((i) + 1) % IOVA_FQ_SIZE)
static inline bool fq_full(struct iova_fq *fq)
{
assert_spin_locked(&fq->lock);
return (((fq->tail + 1) % IOVA_FQ_SIZE) == fq->head);
}
static inline unsigned fq_ring_add(struct iova_fq *fq)
{
unsigned idx = fq->tail;
assert_spin_locked(&fq->lock);
fq->tail = (idx + 1) % IOVA_FQ_SIZE;
return idx;
}
static void fq_ring_free(struct iova_domain *iovad, struct iova_fq *fq)
{
u64 counter = atomic64_read(&iovad->fq_flush_finish_cnt);
unsigned idx;
assert_spin_locked(&fq->lock);
fq_ring_for_each(idx, fq) {
if (fq->entries[idx].counter >= counter)
break;
if (iovad->entry_dtor)
iovad->entry_dtor(fq->entries[idx].data);
free_iova_fast(iovad,
fq->entries[idx].iova_pfn,
fq->entries[idx].pages);
fq->head = (fq->head + 1) % IOVA_FQ_SIZE;
}
}
static void iova_domain_flush(struct iova_domain *iovad)
{
atomic64_inc(&iovad->fq_flush_start_cnt);
iovad->flush_cb(iovad);
atomic64_inc(&iovad->fq_flush_finish_cnt);
}
static void fq_destroy_all_entries(struct iova_domain *iovad)
{
int cpu;
/*
* This code runs when the iova_domain is being detroyed, so don't
* bother to free iovas, just call the entry_dtor on all remaining
* entries.
*/
if (!iovad->entry_dtor)
return;
for_each_possible_cpu(cpu) {
struct iova_fq *fq = per_cpu_ptr(iovad->fq, cpu);
int idx;
fq_ring_for_each(idx, fq)
iovad->entry_dtor(fq->entries[idx].data);
}
}
static void fq_flush_timeout(struct timer_list *t)
{
struct iova_domain *iovad = from_timer(iovad, t, fq_timer);
int cpu;
atomic_set(&iovad->fq_timer_on, 0);
iova_domain_flush(iovad);
for_each_possible_cpu(cpu) {
unsigned long flags;
struct iova_fq *fq;
fq = per_cpu_ptr(iovad->fq, cpu);
spin_lock_irqsave(&fq->lock, flags);
fq_ring_free(iovad, fq);
spin_unlock_irqrestore(&fq->lock, flags);
}
}
void queue_iova(struct iova_domain *iovad,
unsigned long pfn, unsigned long pages,
unsigned long data)
{
struct iova_fq *fq;
unsigned long flags;
unsigned idx;
/*
* Order against the IOMMU driver's pagetable update from unmapping
* @pte, to guarantee that iova_domain_flush() observes that if called
* from a different CPU before we release the lock below. Full barrier
* so it also pairs with iommu_dma_init_fq() to avoid seeing partially
* written fq state here.
*/
smp_mb();
fq = raw_cpu_ptr(iovad->fq);
spin_lock_irqsave(&fq->lock, flags);
/*
* First remove all entries from the flush queue that have already been
* flushed out on another CPU. This makes the fq_full() check below less
* likely to be true.
*/
fq_ring_free(iovad, fq);
if (fq_full(fq)) {
iova_domain_flush(iovad);
fq_ring_free(iovad, fq);
}
idx = fq_ring_add(fq);
fq->entries[idx].iova_pfn = pfn;
fq->entries[idx].pages = pages;
fq->entries[idx].data = data;
fq->entries[idx].counter = atomic64_read(&iovad->fq_flush_start_cnt);
spin_unlock_irqrestore(&fq->lock, flags);
/* Avoid false sharing as much as possible. */
if (!atomic_read(&iovad->fq_timer_on) &&
!atomic_xchg(&iovad->fq_timer_on, 1))
mod_timer(&iovad->fq_timer,
jiffies + msecs_to_jiffies(IOVA_FQ_TIMEOUT));
}
/** /**
* put_iova_domain - destroys the iova domain * put_iova_domain - destroys the iova domain
* @iovad: - iova domain in question. * @iovad: - iova domain in question.
...@@ -688,8 +499,6 @@ void put_iova_domain(struct iova_domain *iovad) ...@@ -688,8 +499,6 @@ void put_iova_domain(struct iova_domain *iovad)
cpuhp_state_remove_instance_nocalls(CPUHP_IOMMU_IOVA_DEAD, cpuhp_state_remove_instance_nocalls(CPUHP_IOMMU_IOVA_DEAD,
&iovad->cpuhp_dead); &iovad->cpuhp_dead);
free_iova_flush_queue(iovad);
free_iova_rcaches(iovad); free_iova_rcaches(iovad);
rbtree_postorder_for_each_entry_safe(iova, tmp, &iovad->rbroot, node) rbtree_postorder_for_each_entry_safe(iova, tmp, &iovad->rbroot, node)
free_iova_mem(iova); free_iova_mem(iova);
......
...@@ -71,6 +71,7 @@ struct viommu_domain { ...@@ -71,6 +71,7 @@ struct viommu_domain {
struct rb_root_cached mappings; struct rb_root_cached mappings;
unsigned long nr_endpoints; unsigned long nr_endpoints;
bool bypass;
}; };
struct viommu_endpoint { struct viommu_endpoint {
...@@ -310,8 +311,8 @@ static int viommu_send_req_sync(struct viommu_dev *viommu, void *buf, ...@@ -310,8 +311,8 @@ static int viommu_send_req_sync(struct viommu_dev *viommu, void *buf,
* *
* On success, return the new mapping. Otherwise return NULL. * On success, return the new mapping. Otherwise return NULL.
*/ */
static int viommu_add_mapping(struct viommu_domain *vdomain, unsigned long iova, static int viommu_add_mapping(struct viommu_domain *vdomain, u64 iova, u64 end,
phys_addr_t paddr, size_t size, u32 flags) phys_addr_t paddr, u32 flags)
{ {
unsigned long irqflags; unsigned long irqflags;
struct viommu_mapping *mapping; struct viommu_mapping *mapping;
...@@ -322,7 +323,7 @@ static int viommu_add_mapping(struct viommu_domain *vdomain, unsigned long iova, ...@@ -322,7 +323,7 @@ static int viommu_add_mapping(struct viommu_domain *vdomain, unsigned long iova,
mapping->paddr = paddr; mapping->paddr = paddr;
mapping->iova.start = iova; mapping->iova.start = iova;
mapping->iova.last = iova + size - 1; mapping->iova.last = end;
mapping->flags = flags; mapping->flags = flags;
spin_lock_irqsave(&vdomain->mappings_lock, irqflags); spin_lock_irqsave(&vdomain->mappings_lock, irqflags);
...@@ -337,26 +338,24 @@ static int viommu_add_mapping(struct viommu_domain *vdomain, unsigned long iova, ...@@ -337,26 +338,24 @@ static int viommu_add_mapping(struct viommu_domain *vdomain, unsigned long iova,
* *
* @vdomain: the domain * @vdomain: the domain
* @iova: start of the range * @iova: start of the range
* @size: size of the range. A size of 0 corresponds to the entire address * @end: end of the range
* space.
* *
* On success, returns the number of unmapped bytes (>= size) * On success, returns the number of unmapped bytes
*/ */
static size_t viommu_del_mappings(struct viommu_domain *vdomain, static size_t viommu_del_mappings(struct viommu_domain *vdomain,
unsigned long iova, size_t size) u64 iova, u64 end)
{ {
size_t unmapped = 0; size_t unmapped = 0;
unsigned long flags; unsigned long flags;
unsigned long last = iova + size - 1;
struct viommu_mapping *mapping = NULL; struct viommu_mapping *mapping = NULL;
struct interval_tree_node *node, *next; struct interval_tree_node *node, *next;
spin_lock_irqsave(&vdomain->mappings_lock, flags); spin_lock_irqsave(&vdomain->mappings_lock, flags);
next = interval_tree_iter_first(&vdomain->mappings, iova, last); next = interval_tree_iter_first(&vdomain->mappings, iova, end);
while (next) { while (next) {
node = next; node = next;
mapping = container_of(node, struct viommu_mapping, iova); mapping = container_of(node, struct viommu_mapping, iova);
next = interval_tree_iter_next(node, iova, last); next = interval_tree_iter_next(node, iova, end);
/* Trying to split a mapping? */ /* Trying to split a mapping? */
if (mapping->iova.start < iova) if (mapping->iova.start < iova)
...@@ -376,6 +375,55 @@ static size_t viommu_del_mappings(struct viommu_domain *vdomain, ...@@ -376,6 +375,55 @@ static size_t viommu_del_mappings(struct viommu_domain *vdomain,
return unmapped; return unmapped;
} }
/*
* Fill the domain with identity mappings, skipping the device's reserved
* regions.
*/
static int viommu_domain_map_identity(struct viommu_endpoint *vdev,
struct viommu_domain *vdomain)
{
int ret;
struct iommu_resv_region *resv;
u64 iova = vdomain->domain.geometry.aperture_start;
u64 limit = vdomain->domain.geometry.aperture_end;
u32 flags = VIRTIO_IOMMU_MAP_F_READ | VIRTIO_IOMMU_MAP_F_WRITE;
unsigned long granule = 1UL << __ffs(vdomain->domain.pgsize_bitmap);
iova = ALIGN(iova, granule);
limit = ALIGN_DOWN(limit + 1, granule) - 1;
list_for_each_entry(resv, &vdev->resv_regions, list) {
u64 resv_start = ALIGN_DOWN(resv->start, granule);
u64 resv_end = ALIGN(resv->start + resv->length, granule) - 1;
if (resv_end < iova || resv_start > limit)
/* No overlap */
continue;
if (resv_start > iova) {
ret = viommu_add_mapping(vdomain, iova, resv_start - 1,
(phys_addr_t)iova, flags);
if (ret)
goto err_unmap;
}
if (resv_end >= limit)
return 0;
iova = resv_end + 1;
}
ret = viommu_add_mapping(vdomain, iova, limit, (phys_addr_t)iova,
flags);
if (ret)
goto err_unmap;
return 0;
err_unmap:
viommu_del_mappings(vdomain, 0, iova);
return ret;
}
/* /*
* viommu_replay_mappings - re-send MAP requests * viommu_replay_mappings - re-send MAP requests
* *
...@@ -422,7 +470,7 @@ static int viommu_add_resv_mem(struct viommu_endpoint *vdev, ...@@ -422,7 +470,7 @@ static int viommu_add_resv_mem(struct viommu_endpoint *vdev,
size_t size; size_t size;
u64 start64, end64; u64 start64, end64;
phys_addr_t start, end; phys_addr_t start, end;
struct iommu_resv_region *region = NULL; struct iommu_resv_region *region = NULL, *next;
unsigned long prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO; unsigned long prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
start = start64 = le64_to_cpu(mem->start); start = start64 = le64_to_cpu(mem->start);
...@@ -453,7 +501,12 @@ static int viommu_add_resv_mem(struct viommu_endpoint *vdev, ...@@ -453,7 +501,12 @@ static int viommu_add_resv_mem(struct viommu_endpoint *vdev,
if (!region) if (!region)
return -ENOMEM; return -ENOMEM;
list_add(&region->list, &vdev->resv_regions); /* Keep the list sorted */
list_for_each_entry(next, &vdev->resv_regions, list) {
if (next->start > region->start)
break;
}
list_add_tail(&region->list, &next->list);
return 0; return 0;
} }
...@@ -587,7 +640,9 @@ static struct iommu_domain *viommu_domain_alloc(unsigned type) ...@@ -587,7 +640,9 @@ static struct iommu_domain *viommu_domain_alloc(unsigned type)
{ {
struct viommu_domain *vdomain; struct viommu_domain *vdomain;
if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_DMA) if (type != IOMMU_DOMAIN_UNMANAGED &&
type != IOMMU_DOMAIN_DMA &&
type != IOMMU_DOMAIN_IDENTITY)
return NULL; return NULL;
vdomain = kzalloc(sizeof(*vdomain), GFP_KERNEL); vdomain = kzalloc(sizeof(*vdomain), GFP_KERNEL);
...@@ -630,6 +685,21 @@ static int viommu_domain_finalise(struct viommu_endpoint *vdev, ...@@ -630,6 +685,21 @@ static int viommu_domain_finalise(struct viommu_endpoint *vdev,
vdomain->map_flags = viommu->map_flags; vdomain->map_flags = viommu->map_flags;
vdomain->viommu = viommu; vdomain->viommu = viommu;
if (domain->type == IOMMU_DOMAIN_IDENTITY) {
if (virtio_has_feature(viommu->vdev,
VIRTIO_IOMMU_F_BYPASS_CONFIG)) {
vdomain->bypass = true;
return 0;
}
ret = viommu_domain_map_identity(vdev, vdomain);
if (ret) {
ida_free(&viommu->domain_ids, vdomain->id);
vdomain->viommu = NULL;
return -EOPNOTSUPP;
}
}
return 0; return 0;
} }
...@@ -637,8 +707,8 @@ static void viommu_domain_free(struct iommu_domain *domain) ...@@ -637,8 +707,8 @@ static void viommu_domain_free(struct iommu_domain *domain)
{ {
struct viommu_domain *vdomain = to_viommu_domain(domain); struct viommu_domain *vdomain = to_viommu_domain(domain);
/* Free all remaining mappings (size 2^64) */ /* Free all remaining mappings */
viommu_del_mappings(vdomain, 0, 0); viommu_del_mappings(vdomain, 0, ULLONG_MAX);
if (vdomain->viommu) if (vdomain->viommu)
ida_free(&vdomain->viommu->domain_ids, vdomain->id); ida_free(&vdomain->viommu->domain_ids, vdomain->id);
...@@ -673,7 +743,7 @@ static int viommu_attach_dev(struct iommu_domain *domain, struct device *dev) ...@@ -673,7 +743,7 @@ static int viommu_attach_dev(struct iommu_domain *domain, struct device *dev)
/* /*
* In the virtio-iommu device, when attaching the endpoint to a new * In the virtio-iommu device, when attaching the endpoint to a new
* domain, it is detached from the old one and, if as as a result the * domain, it is detached from the old one and, if as a result the
* old domain isn't attached to any endpoint, all mappings are removed * old domain isn't attached to any endpoint, all mappings are removed
* from the old domain and it is freed. * from the old domain and it is freed.
* *
...@@ -691,6 +761,9 @@ static int viommu_attach_dev(struct iommu_domain *domain, struct device *dev) ...@@ -691,6 +761,9 @@ static int viommu_attach_dev(struct iommu_domain *domain, struct device *dev)
.domain = cpu_to_le32(vdomain->id), .domain = cpu_to_le32(vdomain->id),
}; };
if (vdomain->bypass)
req.flags |= cpu_to_le32(VIRTIO_IOMMU_ATTACH_F_BYPASS);
for (i = 0; i < fwspec->num_ids; i++) { for (i = 0; i < fwspec->num_ids; i++) {
req.endpoint = cpu_to_le32(fwspec->ids[i]); req.endpoint = cpu_to_le32(fwspec->ids[i]);
...@@ -720,6 +793,7 @@ static int viommu_map(struct iommu_domain *domain, unsigned long iova, ...@@ -720,6 +793,7 @@ static int viommu_map(struct iommu_domain *domain, unsigned long iova,
{ {
int ret; int ret;
u32 flags; u32 flags;
u64 end = iova + size - 1;
struct virtio_iommu_req_map map; struct virtio_iommu_req_map map;
struct viommu_domain *vdomain = to_viommu_domain(domain); struct viommu_domain *vdomain = to_viommu_domain(domain);
...@@ -730,7 +804,7 @@ static int viommu_map(struct iommu_domain *domain, unsigned long iova, ...@@ -730,7 +804,7 @@ static int viommu_map(struct iommu_domain *domain, unsigned long iova,
if (flags & ~vdomain->map_flags) if (flags & ~vdomain->map_flags)
return -EINVAL; return -EINVAL;
ret = viommu_add_mapping(vdomain, iova, paddr, size, flags); ret = viommu_add_mapping(vdomain, iova, end, paddr, flags);
if (ret) if (ret)
return ret; return ret;
...@@ -739,7 +813,7 @@ static int viommu_map(struct iommu_domain *domain, unsigned long iova, ...@@ -739,7 +813,7 @@ static int viommu_map(struct iommu_domain *domain, unsigned long iova,
.domain = cpu_to_le32(vdomain->id), .domain = cpu_to_le32(vdomain->id),
.virt_start = cpu_to_le64(iova), .virt_start = cpu_to_le64(iova),
.phys_start = cpu_to_le64(paddr), .phys_start = cpu_to_le64(paddr),
.virt_end = cpu_to_le64(iova + size - 1), .virt_end = cpu_to_le64(end),
.flags = cpu_to_le32(flags), .flags = cpu_to_le32(flags),
}; };
...@@ -748,7 +822,7 @@ static int viommu_map(struct iommu_domain *domain, unsigned long iova, ...@@ -748,7 +822,7 @@ static int viommu_map(struct iommu_domain *domain, unsigned long iova,
ret = viommu_send_req_sync(vdomain->viommu, &map, sizeof(map)); ret = viommu_send_req_sync(vdomain->viommu, &map, sizeof(map));
if (ret) if (ret)
viommu_del_mappings(vdomain, iova, size); viommu_del_mappings(vdomain, iova, end);
return ret; return ret;
} }
...@@ -761,7 +835,7 @@ static size_t viommu_unmap(struct iommu_domain *domain, unsigned long iova, ...@@ -761,7 +835,7 @@ static size_t viommu_unmap(struct iommu_domain *domain, unsigned long iova,
struct virtio_iommu_req_unmap unmap; struct virtio_iommu_req_unmap unmap;
struct viommu_domain *vdomain = to_viommu_domain(domain); struct viommu_domain *vdomain = to_viommu_domain(domain);
unmapped = viommu_del_mappings(vdomain, iova, size); unmapped = viommu_del_mappings(vdomain, iova, iova + size - 1);
if (unmapped < size) if (unmapped < size)
return 0; return 0;
...@@ -1132,6 +1206,7 @@ static unsigned int features[] = { ...@@ -1132,6 +1206,7 @@ static unsigned int features[] = {
VIRTIO_IOMMU_F_DOMAIN_RANGE, VIRTIO_IOMMU_F_DOMAIN_RANGE,
VIRTIO_IOMMU_F_PROBE, VIRTIO_IOMMU_F_PROBE,
VIRTIO_IOMMU_F_MMIO, VIRTIO_IOMMU_F_MMIO,
VIRTIO_IOMMU_F_BYPASS_CONFIG,
}; };
static struct virtio_device_id id_table[] = { static struct virtio_device_id id_table[] = {
......
...@@ -292,14 +292,6 @@ vduse_domain_alloc_iova(struct iova_domain *iovad, ...@@ -292,14 +292,6 @@ vduse_domain_alloc_iova(struct iova_domain *iovad,
unsigned long iova_len = iova_align(iovad, size) >> shift; unsigned long iova_len = iova_align(iovad, size) >> shift;
unsigned long iova_pfn; unsigned long iova_pfn;
/*
* Freeing non-power-of-two-sized allocations back into the IOVA caches
* will come back to bite us badly, so we have to waste a bit of space
* rounding up anything cacheable to make sure that can't happen. The
* order of the unadjusted size will still match upon freeing.
*/
if (iova_len < (1 << (IOVA_RANGE_CACHE_MAX_SIZE - 1)))
iova_len = roundup_pow_of_two(iova_len);
iova_pfn = alloc_iova_fast(iovad, iova_len, limit >> shift, true); iova_pfn = alloc_iova_fast(iovad, iova_len, limit >> shift, true);
return iova_pfn << shift; return iova_pfn << shift;
......
...@@ -8,12 +8,6 @@ ...@@ -8,12 +8,6 @@
#ifndef __INTEL_SVM_H__ #ifndef __INTEL_SVM_H__
#define __INTEL_SVM_H__ #define __INTEL_SVM_H__
/* Values for rxwp in fault_cb callback */
#define SVM_REQ_READ (1<<3)
#define SVM_REQ_WRITE (1<<2)
#define SVM_REQ_EXEC (1<<1)
#define SVM_REQ_PRIV (1<<0)
/* Page Request Queue depth */ /* Page Request Queue depth */
#define PRQ_ORDER 2 #define PRQ_ORDER 2
#define PRQ_RING_MASK ((0x1000 << PRQ_ORDER) - 0x20) #define PRQ_RING_MASK ((0x1000 << PRQ_ORDER) - 0x20)
......
...@@ -186,7 +186,7 @@ struct iommu_iotlb_gather { ...@@ -186,7 +186,7 @@ struct iommu_iotlb_gather {
unsigned long start; unsigned long start;
unsigned long end; unsigned long end;
size_t pgsize; size_t pgsize;
struct page *freelist; struct list_head freelist;
bool queued; bool queued;
}; };
...@@ -399,6 +399,7 @@ static inline void iommu_iotlb_gather_init(struct iommu_iotlb_gather *gather) ...@@ -399,6 +399,7 @@ static inline void iommu_iotlb_gather_init(struct iommu_iotlb_gather *gather)
{ {
*gather = (struct iommu_iotlb_gather) { *gather = (struct iommu_iotlb_gather) {
.start = ULONG_MAX, .start = ULONG_MAX,
.freelist = LIST_HEAD_INIT(gather->freelist),
}; };
} }
......
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/rbtree.h> #include <linux/rbtree.h>
#include <linux/atomic.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
/* iova structure */ /* iova structure */
...@@ -35,35 +34,6 @@ struct iova_rcache { ...@@ -35,35 +34,6 @@ struct iova_rcache {
struct iova_cpu_rcache __percpu *cpu_rcaches; struct iova_cpu_rcache __percpu *cpu_rcaches;
}; };
struct iova_domain;
/* Call-Back from IOVA code into IOMMU drivers */
typedef void (* iova_flush_cb)(struct iova_domain *domain);
/* Destructor for per-entry data */
typedef void (* iova_entry_dtor)(unsigned long data);
/* Number of entries per Flush Queue */
#define IOVA_FQ_SIZE 256
/* Timeout (in ms) after which entries are flushed from the Flush-Queue */
#define IOVA_FQ_TIMEOUT 10
/* Flush Queue entry for defered flushing */
struct iova_fq_entry {
unsigned long iova_pfn;
unsigned long pages;
unsigned long data;
u64 counter; /* Flush counter when this entrie was added */
};
/* Per-CPU Flush Queue structure */
struct iova_fq {
struct iova_fq_entry entries[IOVA_FQ_SIZE];
unsigned head, tail;
spinlock_t lock;
};
/* holds all the iova translations for a domain */ /* holds all the iova translations for a domain */
struct iova_domain { struct iova_domain {
spinlock_t iova_rbtree_lock; /* Lock to protect update of rbtree */ spinlock_t iova_rbtree_lock; /* Lock to protect update of rbtree */
...@@ -74,27 +44,9 @@ struct iova_domain { ...@@ -74,27 +44,9 @@ struct iova_domain {
unsigned long start_pfn; /* Lower limit for this domain */ unsigned long start_pfn; /* Lower limit for this domain */
unsigned long dma_32bit_pfn; unsigned long dma_32bit_pfn;
unsigned long max32_alloc_size; /* Size of last failed allocation */ unsigned long max32_alloc_size; /* Size of last failed allocation */
struct iova_fq __percpu *fq; /* Flush Queue */
atomic64_t fq_flush_start_cnt; /* Number of TLB flushes that
have been started */
atomic64_t fq_flush_finish_cnt; /* Number of TLB flushes that
have been finished */
struct iova anchor; /* rbtree lookup anchor */ struct iova anchor; /* rbtree lookup anchor */
struct iova_rcache rcaches[IOVA_RANGE_CACHE_MAX_SIZE]; /* IOVA range caches */
iova_flush_cb flush_cb; /* Call-Back function to flush IOMMU
TLBs */
iova_entry_dtor entry_dtor; /* IOMMU driver specific destructor for struct iova_rcache rcaches[IOVA_RANGE_CACHE_MAX_SIZE]; /* IOVA range caches */
iova entry */
struct timer_list fq_timer; /* Timer to regularily empty the
flush-queues */
atomic_t fq_timer_on; /* 1 when timer is active, 0
when not */
struct hlist_node cpuhp_dead; struct hlist_node cpuhp_dead;
}; };
...@@ -144,17 +96,12 @@ struct iova *alloc_iova(struct iova_domain *iovad, unsigned long size, ...@@ -144,17 +96,12 @@ struct iova *alloc_iova(struct iova_domain *iovad, unsigned long size,
bool size_aligned); bool size_aligned);
void free_iova_fast(struct iova_domain *iovad, unsigned long pfn, void free_iova_fast(struct iova_domain *iovad, unsigned long pfn,
unsigned long size); unsigned long size);
void queue_iova(struct iova_domain *iovad,
unsigned long pfn, unsigned long pages,
unsigned long data);
unsigned long alloc_iova_fast(struct iova_domain *iovad, unsigned long size, unsigned long alloc_iova_fast(struct iova_domain *iovad, unsigned long size,
unsigned long limit_pfn, bool flush_rcache); unsigned long limit_pfn, bool flush_rcache);
struct iova *reserve_iova(struct iova_domain *iovad, unsigned long pfn_lo, struct iova *reserve_iova(struct iova_domain *iovad, unsigned long pfn_lo,
unsigned long pfn_hi); unsigned long pfn_hi);
void init_iova_domain(struct iova_domain *iovad, unsigned long granule, void init_iova_domain(struct iova_domain *iovad, unsigned long granule,
unsigned long start_pfn); unsigned long start_pfn);
int init_iova_flush_queue(struct iova_domain *iovad,
iova_flush_cb flush_cb, iova_entry_dtor entry_dtor);
struct iova *find_iova(struct iova_domain *iovad, unsigned long pfn); struct iova *find_iova(struct iova_domain *iovad, unsigned long pfn);
void put_iova_domain(struct iova_domain *iovad); void put_iova_domain(struct iova_domain *iovad);
#else #else
...@@ -189,12 +136,6 @@ static inline void free_iova_fast(struct iova_domain *iovad, ...@@ -189,12 +136,6 @@ static inline void free_iova_fast(struct iova_domain *iovad,
{ {
} }
static inline void queue_iova(struct iova_domain *iovad,
unsigned long pfn, unsigned long pages,
unsigned long data)
{
}
static inline unsigned long alloc_iova_fast(struct iova_domain *iovad, static inline unsigned long alloc_iova_fast(struct iova_domain *iovad,
unsigned long size, unsigned long size,
unsigned long limit_pfn, unsigned long limit_pfn,
...@@ -216,13 +157,6 @@ static inline void init_iova_domain(struct iova_domain *iovad, ...@@ -216,13 +157,6 @@ static inline void init_iova_domain(struct iova_domain *iovad,
{ {
} }
static inline int init_iova_flush_queue(struct iova_domain *iovad,
iova_flush_cb flush_cb,
iova_entry_dtor entry_dtor)
{
return -ENODEV;
}
static inline struct iova *find_iova(struct iova_domain *iovad, static inline struct iova *find_iova(struct iova_domain *iovad,
unsigned long pfn) unsigned long pfn)
{ {
......
...@@ -101,8 +101,9 @@ TRACE_EVENT(map, ...@@ -101,8 +101,9 @@ TRACE_EVENT(map,
__entry->size = size; __entry->size = size;
), ),
TP_printk("IOMMU: iova=0x%016llx paddr=0x%016llx size=%zu", TP_printk("IOMMU: iova=0x%016llx - 0x%016llx paddr=0x%016llx size=%zu",
__entry->iova, __entry->paddr, __entry->size __entry->iova, __entry->iova + __entry->size, __entry->paddr,
__entry->size
) )
); );
...@@ -124,8 +125,9 @@ TRACE_EVENT(unmap, ...@@ -124,8 +125,9 @@ TRACE_EVENT(unmap,
__entry->unmapped_size = unmapped_size; __entry->unmapped_size = unmapped_size;
), ),
TP_printk("IOMMU: iova=0x%016llx size=%zu unmapped_size=%zu", TP_printk("IOMMU: iova=0x%016llx - 0x%016llx size=%zu unmapped_size=%zu",
__entry->iova, __entry->size, __entry->unmapped_size __entry->iova, __entry->iova + __entry->size,
__entry->size, __entry->unmapped_size
) )
); );
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#define VIRTIO_IOMMU_F_BYPASS 3 #define VIRTIO_IOMMU_F_BYPASS 3
#define VIRTIO_IOMMU_F_PROBE 4 #define VIRTIO_IOMMU_F_PROBE 4
#define VIRTIO_IOMMU_F_MMIO 5 #define VIRTIO_IOMMU_F_MMIO 5
#define VIRTIO_IOMMU_F_BYPASS_CONFIG 6
struct virtio_iommu_range_64 { struct virtio_iommu_range_64 {
__le64 start; __le64 start;
...@@ -36,6 +37,8 @@ struct virtio_iommu_config { ...@@ -36,6 +37,8 @@ struct virtio_iommu_config {
struct virtio_iommu_range_32 domain_range; struct virtio_iommu_range_32 domain_range;
/* Probe buffer size */ /* Probe buffer size */
__le32 probe_size; __le32 probe_size;
__u8 bypass;
__u8 reserved[3];
}; };
/* Request types */ /* Request types */
...@@ -66,11 +69,14 @@ struct virtio_iommu_req_tail { ...@@ -66,11 +69,14 @@ struct virtio_iommu_req_tail {
__u8 reserved[3]; __u8 reserved[3];
}; };
#define VIRTIO_IOMMU_ATTACH_F_BYPASS (1 << 0)
struct virtio_iommu_req_attach { struct virtio_iommu_req_attach {
struct virtio_iommu_req_head head; struct virtio_iommu_req_head head;
__le32 domain; __le32 domain;
__le32 endpoint; __le32 endpoint;
__u8 reserved[8]; __le32 flags;
__u8 reserved[4];
struct virtio_iommu_req_tail tail; struct virtio_iommu_req_tail tail;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment