From 2f119c7895af56ca26b8e94f4650145a4b1b0d12 Mon Sep 17 00:00:00 2001 From: Quentin Lambert Date: Fri, 6 Feb 2015 10:59:53 +0100 Subject: iommu/vt-d: Convert non-returned local variable to boolean when relevant This patch was produced using Coccinelle. A simplified version of the semantic patch is: @r exists@ identifier f; local idexpression u8 x; identifier xname; @@ f(...) { ...when any ( x@xname = 1; | x@xname = 0; ) ...when any } @bad exists@ identifier r.f; local idexpression u8 r.x expression e1 != {0, 1}, e2; @@ f(...) { ...when any ( x = e1; | x + e2 ) ...when any } @depends on !bad@ identifier r.f; local idexpression u8 r.x; identifier r.xname; @@ f(...) { ... ++ bool xname; - int xname; <... ( x = - 1 + true | x = - -1 + false ) ...> } Signed-off-by: Quentin Lambert Signed-off-by: Joerg Roedel --- drivers/iommu/intel-iommu.c | 13 +++++++------ drivers/iommu/intel_irq_remapping.c | 12 ++++++------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index ae4c1a854e57..d25cc6219a32 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -595,12 +595,13 @@ static void domain_update_iommu_coherency(struct dmar_domain *domain) { struct dmar_drhd_unit *drhd; struct intel_iommu *iommu; - int i, found = 0; + bool found = false; + int i; domain->iommu_coherency = 1; for_each_set_bit(i, domain->iommu_bmp, g_num_of_iommus) { - found = 1; + found = true; if (!ecap_coherent(g_iommus[i]->ecap)) { domain->iommu_coherency = 0; break; @@ -1267,7 +1268,7 @@ static struct device_domain_info * iommu_support_dev_iotlb (struct dmar_domain *domain, struct intel_iommu *iommu, u8 bus, u8 devfn) { - int found = 0; + bool found = false; unsigned long flags; struct device_domain_info *info; struct pci_dev *pdev; @@ -1282,7 +1283,7 @@ iommu_support_dev_iotlb (struct dmar_domain *domain, struct intel_iommu *iommu, list_for_each_entry(info, &domain->devices, link) if (info->iommu == iommu && info->bus == bus && info->devfn == devfn) { - found = 1; + found = true; break; } spin_unlock_irqrestore(&device_domain_lock, flags); @@ -4270,7 +4271,7 @@ static void domain_remove_one_dev_info(struct dmar_domain *domain, struct device_domain_info *info, *tmp; struct intel_iommu *iommu; unsigned long flags; - int found = 0; + bool found = false; u8 bus, devfn; iommu = device_to_iommu(dev, &bus, &devfn); @@ -4302,7 +4303,7 @@ static void domain_remove_one_dev_info(struct dmar_domain *domain, * update iommu count and coherency */ if (info->iommu == iommu) - found = 1; + found = true; } spin_unlock_irqrestore(&device_domain_lock, flags); diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c index 14de1ab223c8..6c25b3c5b729 100644 --- a/drivers/iommu/intel_irq_remapping.c +++ b/drivers/iommu/intel_irq_remapping.c @@ -631,7 +631,7 @@ static int __init intel_enable_irq_remapping(void) { struct dmar_drhd_unit *drhd; struct intel_iommu *iommu; - int setup = 0; + bool setup = false; int eim = 0; if (x2apic_supported()) { @@ -697,7 +697,7 @@ static int __init intel_enable_irq_remapping(void) */ for_each_iommu(iommu, drhd) { iommu_set_irq_remapping(iommu, eim); - setup = 1; + setup = true; } if (!setup) @@ -856,7 +856,7 @@ static int __init parse_ioapics_under_ir(void) { struct dmar_drhd_unit *drhd; struct intel_iommu *iommu; - int ir_supported = 0; + bool ir_supported = false; int ioapic_idx; for_each_iommu(iommu, drhd) @@ -864,7 +864,7 @@ static int __init parse_ioapics_under_ir(void) if (ir_parse_ioapic_hpet_scope(drhd->hdr, iommu)) return -1; - ir_supported = 1; + ir_supported = true; } if (!ir_supported) @@ -917,7 +917,7 @@ static void disable_irq_remapping(void) static int reenable_irq_remapping(int eim) { struct dmar_drhd_unit *drhd; - int setup = 0; + bool setup = false; struct intel_iommu *iommu = NULL; for_each_iommu(iommu, drhd) @@ -933,7 +933,7 @@ static int reenable_irq_remapping(int eim) /* Set up interrupt remapping for iommu.*/ iommu_set_irq_remapping(iommu, eim); - setup = 1; + setup = true; } if (!setup) -- cgit v1.2.3 From 940f700dfbe91d37a56ebd26e0a7a681d387326a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 20 Feb 2015 13:52:01 +0300 Subject: iommu/amd: Small cleanup in mn_release() "pasid_state->device_state" and "dev_state" are the same, but it's nicer to use dev_state consistently. Signed-off-by: Dan Carpenter Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu_v2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/amd_iommu_v2.c b/drivers/iommu/amd_iommu_v2.c index 6d5a5c44453b..a1cbba9056fd 100644 --- a/drivers/iommu/amd_iommu_v2.c +++ b/drivers/iommu/amd_iommu_v2.c @@ -417,7 +417,7 @@ static void mn_release(struct mmu_notifier *mn, struct mm_struct *mm) dev_state = pasid_state->device_state; run_inv_ctx_cb = !pasid_state->invalid; - if (run_inv_ctx_cb && pasid_state->device_state->inv_ctx_cb) + if (run_inv_ctx_cb && dev_state->inv_ctx_cb) dev_state->inv_ctx_cb(dev_state->pdev, pasid_state->pasid); unbind_pasid(pasid_state); -- cgit v1.2.3 From 4a1c93cbe9b2cf559fee3316ee287bb042eb1aba Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Wed, 4 Mar 2015 12:21:03 +0000 Subject: iommu/arm-smmu: ensure CBA2R is initialised before CBAR on SMMUv2 The VMID16 (8.1) extension to SMMUv2 added a 16-bit VMID16 field to the CBA2R registers. Unfortunately, if software writes this field as zero after setting an 8-bit VMID in a stage-2 CBAR, then the VMID may also be overwritten with zero on some early implementations (the architecture was later updated to fix this issue). This patch ensures that we initialise CBA2R before CBAR, therefore ensuring that the VMID is set correctly. Tested-by: Manish Jaggi Signed-off-by: Will Deacon --- drivers/iommu/arm-smmu.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index fc13dd56953e..4abb831e24f0 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -730,6 +730,20 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain, stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS; cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx); + if (smmu->version > ARM_SMMU_V1) { + /* + * CBA2R. + * *Must* be initialised before CBAR thanks to VMID16 + * architectural oversight affected some implementations. + */ +#ifdef CONFIG_64BIT + reg = CBA2R_RW64_64BIT; +#else + reg = CBA2R_RW64_32BIT; +#endif + writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBA2R(cfg->cbndx)); + } + /* CBAR */ reg = cfg->cbar; if (smmu->version == ARM_SMMU_V1) @@ -747,16 +761,6 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain, } writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBAR(cfg->cbndx)); - if (smmu->version > ARM_SMMU_V1) { - /* CBA2R */ -#ifdef CONFIG_64BIT - reg = CBA2R_RW64_64BIT; -#else - reg = CBA2R_RW64_32BIT; -#endif - writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBA2R(cfg->cbndx)); - } - /* TTBRs */ if (stage1) { reg = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0]; -- cgit v1.2.3 From f1d84548694fe45f3348c0379d7277fed05bbcf0 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Wed, 4 Mar 2015 16:41:05 +0000 Subject: iommu/arm-smmu: set a more appropriate DMA mask Since we use dma_map_page() as an architecture-independent means of making page table updates visible to non-coherent SMMUs, we need to have a suitable DMA mask set to discourage the DMA mapping layer from creating bounce buffers and flushing those instead, if said page tables happen to lie outside the default 32-bit mask. Tested-by: Mitchel Humpherys Signed-off-by: Robin Murphy [will: added error checking] Signed-off-by: Will Deacon --- drivers/iommu/arm-smmu.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 4abb831e24f0..161dd46999e2 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -1634,6 +1634,15 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) size = arm_smmu_id_size_to_bits((id >> ID2_OAS_SHIFT) & ID2_OAS_MASK); smmu->pa_size = size; + /* + * What the page table walker can address actually depends on which + * descriptor format is in use, but since a) we don't know that yet, + * and b) it can vary per context bank, this will have to do... + */ + if (dma_set_mask_and_coherent(smmu->dev, DMA_BIT_MASK(size))) + dev_warn(smmu->dev, + "failed to set DMA mask for table walker\n"); + if (smmu->version == ARM_SMMU_V1) { smmu->va_size = smmu->ipa_size; size = SZ_4K | SZ_2M | SZ_1G; -- cgit v1.2.3 From 03edb2264cadddc38ca9030887c2902affbfca3f Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 19 Jan 2015 14:27:33 +0000 Subject: iommu/arm-smmu: handle multi-alias IOMMU groups for PCI devices IOMMU groups for PCI devices can correspond to multiple DMA aliases due to things like ACS and PCI quirks. This patch extends the ARM SMMU ->add_device callback so that we consider all of the DMA aliases for a PCI IOMMU group, rather than creating a separate group for each Requester ID. Signed-off-by: Will Deacon --- drivers/iommu/arm-smmu.c | 92 ++++++++++++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 35 deletions(-) diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 161dd46999e2..6ac184669295 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -1330,61 +1330,83 @@ static void __arm_smmu_release_pci_iommudata(void *data) kfree(data); } -static int arm_smmu_add_device(struct device *dev) +static int arm_smmu_add_pci_device(struct pci_dev *pdev) { - struct arm_smmu_device *smmu; - struct arm_smmu_master_cfg *cfg; + int i, ret; + u16 sid; struct iommu_group *group; - void (*releasefn)(void *) = NULL; - int ret; - - smmu = find_smmu_for_device(dev); - if (!smmu) - return -ENODEV; + struct arm_smmu_master_cfg *cfg; - group = iommu_group_alloc(); - if (IS_ERR(group)) { - dev_err(dev, "Failed to allocate IOMMU group\n"); + group = iommu_group_get_for_dev(&pdev->dev); + if (IS_ERR(group)) return PTR_ERR(group); - } - - if (dev_is_pci(dev)) { - struct pci_dev *pdev = to_pci_dev(dev); + cfg = iommu_group_get_iommudata(group); + if (!cfg) { cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); if (!cfg) { ret = -ENOMEM; goto out_put_group; } - cfg->num_streamids = 1; - /* - * Assume Stream ID == Requester ID for now. - * We need a way to describe the ID mappings in FDT. - */ - pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, - &cfg->streamids[0]); - releasefn = __arm_smmu_release_pci_iommudata; - } else { - struct arm_smmu_master *master; - - master = find_smmu_master(smmu, dev->of_node); - if (!master) { - ret = -ENODEV; - goto out_put_group; - } + iommu_group_set_iommudata(group, cfg, + __arm_smmu_release_pci_iommudata); + } - cfg = &master->cfg; + if (cfg->num_streamids >= MAX_MASTER_STREAMIDS) { + ret = -ENOSPC; + goto out_put_group; } - iommu_group_set_iommudata(group, cfg, releasefn); - ret = iommu_group_add_device(group, dev); + /* + * Assume Stream ID == Requester ID for now. + * We need a way to describe the ID mappings in FDT. + */ + pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, &sid); + for (i = 0; i < cfg->num_streamids; ++i) + if (cfg->streamids[i] == sid) + break; + + /* Avoid duplicate SIDs, as this can lead to SMR conflicts */ + if (i == cfg->num_streamids) + cfg->streamids[cfg->num_streamids++] = sid; + return 0; out_put_group: iommu_group_put(group); return ret; } +static int arm_smmu_add_platform_device(struct device *dev) +{ + struct iommu_group *group; + struct arm_smmu_master *master; + struct arm_smmu_device *smmu = find_smmu_for_device(dev); + + if (!smmu) + return -ENODEV; + + master = find_smmu_master(smmu, dev->of_node); + if (!master) + return -ENODEV; + + /* No automatic group creation for platform devices */ + group = iommu_group_alloc(); + if (IS_ERR(group)) + return PTR_ERR(group); + + iommu_group_set_iommudata(group, &master->cfg, NULL); + return iommu_group_add_device(group, dev); +} + +static int arm_smmu_add_device(struct device *dev) +{ + if (dev_is_pci(dev)) + return arm_smmu_add_pci_device(to_pci_dev(dev)); + + return arm_smmu_add_platform_device(dev); +} + static void arm_smmu_remove_device(struct device *dev) { iommu_group_remove_device(dev); -- cgit v1.2.3 From 63979b8da30013ce58d8447f34dde60802d1ccba Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Wed, 18 Mar 2015 10:22:18 +0000 Subject: iommu/io-pgtable-arm: avoid speculative walks through TTBR1 Although we set TCR.T1SZ to 0, the input address range covered by TTBR1 is actually calculated using T0SZ in this case on the ARM SMMU. This could theoretically lead to speculative table walks through physical address zero, leading to all sorts of fun and games if we have MMIO regions down there. This patch avoids the issue by setting EPD1 to disable walks through the unused TTBR1 register. Signed-off-by: Will Deacon --- drivers/iommu/io-pgtable-arm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c index b610a8dee238..4e460216bd16 100644 --- a/drivers/iommu/io-pgtable-arm.c +++ b/drivers/iommu/io-pgtable-arm.c @@ -116,6 +116,8 @@ #define ARM_32_LPAE_TCR_EAE (1 << 31) #define ARM_64_LPAE_S2_TCR_RES1 (1 << 31) +#define ARM_LPAE_TCR_EPD1 (1 << 23) + #define ARM_LPAE_TCR_TG0_4K (0 << 14) #define ARM_LPAE_TCR_TG0_64K (1 << 14) #define ARM_LPAE_TCR_TG0_16K (2 << 14) @@ -621,6 +623,9 @@ arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie) } reg |= (64ULL - cfg->ias) << ARM_LPAE_TCR_T0SZ_SHIFT; + + /* Disable speculative walks through TTBR1 */ + reg |= ARM_LPAE_TCR_EPD1; cfg->arm_lpae_s1_cfg.tcr = reg; /* MAIRs */ -- cgit v1.2.3 From 938c470976192590b4adc921b2e10fa31237eddc Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Mar 2015 13:43:04 +0100 Subject: iommu: Introduce domain_alloc and domain_free iommu_ops These new call-backs defer the allocation and destruction of 'struct iommu_domain' to the iommu driver. This allows drivers to embed this struct into their private domain structures and to get rid of the domain_init and domain_destroy call-backs when all drivers have been converted. Tested-by: Thierry Reding Tested-by: Heiko Stuebner Reviewed-by: Alex Williamson Acked-by: Will Deacon Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 29 +++++++++++++++++++++-------- include/linux/iommu.h | 5 +++++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 72e683df0731..11de2620bbf4 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -900,26 +900,34 @@ EXPORT_SYMBOL_GPL(iommu_set_fault_handler); struct iommu_domain *iommu_domain_alloc(struct bus_type *bus) { + const struct iommu_ops *ops; struct iommu_domain *domain; - int ret; if (bus == NULL || bus->iommu_ops == NULL) return NULL; - domain = kzalloc(sizeof(*domain), GFP_KERNEL); + ops = bus->iommu_ops; + + if (ops->domain_alloc) + domain = ops->domain_alloc(); + else + domain = kzalloc(sizeof(*domain), GFP_KERNEL); + if (!domain) return NULL; domain->ops = bus->iommu_ops; - ret = domain->ops->domain_init(domain); - if (ret) + if (ops->domain_init && domain->ops->domain_init(domain)) goto out_free; return domain; out_free: - kfree(domain); + if (ops->domain_free) + ops->domain_free(domain); + else + kfree(domain); return NULL; } @@ -927,10 +935,15 @@ EXPORT_SYMBOL_GPL(iommu_domain_alloc); void iommu_domain_free(struct iommu_domain *domain) { - if (likely(domain->ops->domain_destroy != NULL)) - domain->ops->domain_destroy(domain); + const struct iommu_ops *ops = domain->ops; - kfree(domain); + if (likely(ops->domain_destroy != NULL)) + ops->domain_destroy(domain); + + if (ops->domain_free) + ops->domain_free(domain); + else + kfree(domain); } EXPORT_SYMBOL_GPL(iommu_domain_free); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 38daa453f2e5..69d1d120c0a3 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -115,6 +115,11 @@ struct iommu_ops { bool (*capable)(enum iommu_cap); int (*domain_init)(struct iommu_domain *domain); void (*domain_destroy)(struct iommu_domain *domain); + + /* Domain allocation and freeing by the iommu driver */ + struct iommu_domain *(*domain_alloc)(void); + void (*domain_free)(struct iommu_domain *); + int (*attach_dev)(struct iommu_domain *domain, struct device *dev); void (*detach_dev)(struct iommu_domain *domain, struct device *dev); int (*map)(struct iommu_domain *domain, unsigned long iova, -- cgit v1.2.3 From 8539c7c16b970258e14761d8a1f7d10fe798031a Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Mar 2015 13:43:05 +0100 Subject: iommu: Introduce iommu domain types This allows to handle domains differently based on their type in the future. An IOMMU driver can implement certain optimizations for DMA-API domains for example. The domain types can be extended later and some of the existing domain attributes can be migrated to become domain flags. Tested-by: Thierry Reding Tested-by: Heiko Stuebner Reviewed-by: Alex Williamson Acked-by: Will Deacon Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 5 +++-- include/linux/iommu.h | 27 ++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 11de2620bbf4..4920605892a3 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -909,14 +909,15 @@ struct iommu_domain *iommu_domain_alloc(struct bus_type *bus) ops = bus->iommu_ops; if (ops->domain_alloc) - domain = ops->domain_alloc(); + domain = ops->domain_alloc(IOMMU_DOMAIN_UNMANAGED); else domain = kzalloc(sizeof(*domain), GFP_KERNEL); if (!domain) return NULL; - domain->ops = bus->iommu_ops; + domain->ops = bus->iommu_ops; + domain->type = IOMMU_DOMAIN_UNMANAGED; if (ops->domain_init && domain->ops->domain_init(domain)) goto out_free; diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 69d1d120c0a3..72d03fe78cf6 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -51,7 +51,32 @@ struct iommu_domain_geometry { bool force_aperture; /* DMA only allowed in mappable range? */ }; +/* Domain feature flags */ +#define __IOMMU_DOMAIN_PAGING (1U << 0) /* Support for iommu_map/unmap */ +#define __IOMMU_DOMAIN_DMA_API (1U << 1) /* Domain for use in DMA-API + implementation */ +#define __IOMMU_DOMAIN_PT (1U << 2) /* Domain is identity mapped */ + +/* + * This are the possible domain-types + * + * IOMMU_DOMAIN_BLOCKED - All DMA is blocked, can be used to isolate + * devices + * IOMMU_DOMAIN_IDENTITY - DMA addresses are system physical addresses + * IOMMU_DOMAIN_UNMANAGED - DMA mappings managed by IOMMU-API user, used + * for VMs + * IOMMU_DOMAIN_DMA - Internally used for DMA-API implementations. + * This flag allows IOMMU drivers to implement + * certain optimizations for these domains + */ +#define IOMMU_DOMAIN_BLOCKED (0U) +#define IOMMU_DOMAIN_IDENTITY (__IOMMU_DOMAIN_PT) +#define IOMMU_DOMAIN_UNMANAGED (__IOMMU_DOMAIN_PAGING) +#define IOMMU_DOMAIN_DMA (__IOMMU_DOMAIN_PAGING | \ + __IOMMU_DOMAIN_DMA_API) + struct iommu_domain { + unsigned type; const struct iommu_ops *ops; void *priv; iommu_fault_handler_t handler; @@ -117,7 +142,7 @@ struct iommu_ops { void (*domain_destroy)(struct iommu_domain *domain); /* Domain allocation and freeing by the iommu driver */ - struct iommu_domain *(*domain_alloc)(void); + struct iommu_domain *(*domain_alloc)(unsigned iommu_domain_type); void (*domain_free)(struct iommu_domain *); int (*attach_dev)(struct iommu_domain *domain, struct device *dev); -- cgit v1.2.3 From a10315e5efb86c689142a7e7927125889f3682e6 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Mar 2015 13:43:06 +0100 Subject: iommu: Only allow iommu_map/unmap for paging domains Check for the new __IOMMU_DOMAIN_PAGING flag before calling into the iommu drivers ->map and ->unmap call-backs. Tested-by: Thierry Reding Tested-by: Heiko Stuebner Reviewed-by: Alex Williamson Acked-by: Will Deacon Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 4920605892a3..656b9499e748 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -1063,6 +1063,9 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova, domain->ops->pgsize_bitmap == 0UL)) return -ENODEV; + if (unlikely(!(domain->type & __IOMMU_DOMAIN_PAGING))) + return -EINVAL; + /* find out the minimum page size supported */ min_pagesz = 1 << __ffs(domain->ops->pgsize_bitmap); @@ -1114,6 +1117,9 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) domain->ops->pgsize_bitmap == 0UL)) return -ENODEV; + if (unlikely(!(domain->type & __IOMMU_DOMAIN_PAGING))) + return -EINVAL; + /* find out the minimum page size supported */ min_pagesz = 1 << __ffs(domain->ops->pgsize_bitmap); -- cgit v1.2.3 From 3f4b87b959eab362b89fce6ceb9d1badd102e5ea Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Mar 2015 13:43:07 +0100 Subject: iommu/amd: Make use of domain_alloc and domain_free Implement the new iommu-ops function pointers and remove the obsolete domain_init and domain_destroy functions. Reviewed-by: Alex Williamson Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 84 ++++++++++++++++++++++------------------- drivers/iommu/amd_iommu_types.h | 7 ++-- 2 files changed, 48 insertions(+), 43 deletions(-) diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 48882c126245..e8c412dcac21 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -126,6 +126,11 @@ static int __init alloc_passthrough_domain(void); * ****************************************************************************/ +static struct protection_domain *to_pdomain(struct iommu_domain *dom) +{ + return container_of(dom, struct protection_domain, domain); +} + static struct iommu_dev_data *alloc_dev_data(u16 devid) { struct iommu_dev_data *dev_data; @@ -3236,42 +3241,45 @@ static int __init alloc_passthrough_domain(void) return 0; } -static int amd_iommu_domain_init(struct iommu_domain *dom) + +static struct iommu_domain *amd_iommu_domain_alloc(unsigned type) { - struct protection_domain *domain; + struct protection_domain *pdomain; - domain = protection_domain_alloc(); - if (!domain) - goto out_free; + /* We only support unmanaged domains for now */ + if (type != IOMMU_DOMAIN_UNMANAGED) + return NULL; - domain->mode = PAGE_MODE_3_LEVEL; - domain->pt_root = (void *)get_zeroed_page(GFP_KERNEL); - if (!domain->pt_root) + pdomain = protection_domain_alloc(); + if (!pdomain) goto out_free; - domain->iommu_domain = dom; - - dom->priv = domain; + pdomain->mode = PAGE_MODE_3_LEVEL; + pdomain->pt_root = (void *)get_zeroed_page(GFP_KERNEL); + if (!pdomain->pt_root) + goto out_free; - dom->geometry.aperture_start = 0; - dom->geometry.aperture_end = ~0ULL; - dom->geometry.force_aperture = true; + pdomain->domain.geometry.aperture_start = 0; + pdomain->domain.geometry.aperture_end = ~0ULL; + pdomain->domain.geometry.force_aperture = true; - return 0; + return &pdomain->domain; out_free: - protection_domain_free(domain); + protection_domain_free(pdomain); - return -ENOMEM; + return NULL; } -static void amd_iommu_domain_destroy(struct iommu_domain *dom) +static void amd_iommu_domain_free(struct iommu_domain *dom) { - struct protection_domain *domain = dom->priv; + struct protection_domain *domain; - if (!domain) + if (!dom) return; + domain = to_pdomain(dom); + if (domain->dev_cnt > 0) cleanup_domain(domain); @@ -3284,8 +3292,6 @@ static void amd_iommu_domain_destroy(struct iommu_domain *dom) free_gcr3_table(domain); protection_domain_free(domain); - - dom->priv = NULL; } static void amd_iommu_detach_device(struct iommu_domain *dom, @@ -3313,7 +3319,7 @@ static void amd_iommu_detach_device(struct iommu_domain *dom, static int amd_iommu_attach_device(struct iommu_domain *dom, struct device *dev) { - struct protection_domain *domain = dom->priv; + struct protection_domain *domain = to_pdomain(dom); struct iommu_dev_data *dev_data; struct amd_iommu *iommu; int ret; @@ -3340,7 +3346,7 @@ static int amd_iommu_attach_device(struct iommu_domain *dom, static int amd_iommu_map(struct iommu_domain *dom, unsigned long iova, phys_addr_t paddr, size_t page_size, int iommu_prot) { - struct protection_domain *domain = dom->priv; + struct protection_domain *domain = to_pdomain(dom); int prot = 0; int ret; @@ -3362,7 +3368,7 @@ static int amd_iommu_map(struct iommu_domain *dom, unsigned long iova, static size_t amd_iommu_unmap(struct iommu_domain *dom, unsigned long iova, size_t page_size) { - struct protection_domain *domain = dom->priv; + struct protection_domain *domain = to_pdomain(dom); size_t unmap_size; if (domain->mode == PAGE_MODE_NONE) @@ -3380,7 +3386,7 @@ static size_t amd_iommu_unmap(struct iommu_domain *dom, unsigned long iova, static phys_addr_t amd_iommu_iova_to_phys(struct iommu_domain *dom, dma_addr_t iova) { - struct protection_domain *domain = dom->priv; + struct protection_domain *domain = to_pdomain(dom); unsigned long offset_mask; phys_addr_t paddr; u64 *pte, __pte; @@ -3420,8 +3426,8 @@ static bool amd_iommu_capable(enum iommu_cap cap) static const struct iommu_ops amd_iommu_ops = { .capable = amd_iommu_capable, - .domain_init = amd_iommu_domain_init, - .domain_destroy = amd_iommu_domain_destroy, + .domain_alloc = amd_iommu_domain_alloc, + .domain_free = amd_iommu_domain_free, .attach_dev = amd_iommu_attach_device, .detach_dev = amd_iommu_detach_device, .map = amd_iommu_map, @@ -3483,7 +3489,7 @@ EXPORT_SYMBOL(amd_iommu_unregister_ppr_notifier); void amd_iommu_domain_direct_map(struct iommu_domain *dom) { - struct protection_domain *domain = dom->priv; + struct protection_domain *domain = to_pdomain(dom); unsigned long flags; spin_lock_irqsave(&domain->lock, flags); @@ -3504,7 +3510,7 @@ EXPORT_SYMBOL(amd_iommu_domain_direct_map); int amd_iommu_domain_enable_v2(struct iommu_domain *dom, int pasids) { - struct protection_domain *domain = dom->priv; + struct protection_domain *domain = to_pdomain(dom); unsigned long flags; int levels, ret; @@ -3616,7 +3622,7 @@ static int __amd_iommu_flush_page(struct protection_domain *domain, int pasid, int amd_iommu_flush_page(struct iommu_domain *dom, int pasid, u64 address) { - struct protection_domain *domain = dom->priv; + struct protection_domain *domain = to_pdomain(dom); unsigned long flags; int ret; @@ -3638,7 +3644,7 @@ static int __amd_iommu_flush_tlb(struct protection_domain *domain, int pasid) int amd_iommu_flush_tlb(struct iommu_domain *dom, int pasid) { - struct protection_domain *domain = dom->priv; + struct protection_domain *domain = to_pdomain(dom); unsigned long flags; int ret; @@ -3718,7 +3724,7 @@ static int __clear_gcr3(struct protection_domain *domain, int pasid) int amd_iommu_domain_set_gcr3(struct iommu_domain *dom, int pasid, unsigned long cr3) { - struct protection_domain *domain = dom->priv; + struct protection_domain *domain = to_pdomain(dom); unsigned long flags; int ret; @@ -3732,7 +3738,7 @@ EXPORT_SYMBOL(amd_iommu_domain_set_gcr3); int amd_iommu_domain_clear_gcr3(struct iommu_domain *dom, int pasid) { - struct protection_domain *domain = dom->priv; + struct protection_domain *domain = to_pdomain(dom); unsigned long flags; int ret; @@ -3765,17 +3771,17 @@ EXPORT_SYMBOL(amd_iommu_complete_ppr); struct iommu_domain *amd_iommu_get_v2_domain(struct pci_dev *pdev) { - struct protection_domain *domain; + struct protection_domain *pdomain; - domain = get_domain(&pdev->dev); - if (IS_ERR(domain)) + pdomain = get_domain(&pdev->dev); + if (IS_ERR(pdomain)) return NULL; /* Only return IOMMUv2 domains */ - if (!(domain->flags & PD_IOMMUV2_MASK)) + if (!(pdomain->flags & PD_IOMMUV2_MASK)) return NULL; - return domain->iommu_domain; + return &pdomain->domain; } EXPORT_SYMBOL(amd_iommu_get_v2_domain); diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h index c4fffb710c58..2b1276b2ec80 100644 --- a/drivers/iommu/amd_iommu_types.h +++ b/drivers/iommu/amd_iommu_types.h @@ -400,6 +400,8 @@ struct iommu_domain; struct protection_domain { struct list_head list; /* for list of all protection domains */ struct list_head dev_list; /* List of all devices in this domain */ + struct iommu_domain domain; /* generic domain handle used by + iommu core code */ spinlock_t lock; /* mostly used to lock the page table*/ struct mutex api_lock; /* protect page tables in the iommu-api path */ u16 id; /* the domain id written to the device table */ @@ -411,10 +413,7 @@ struct protection_domain { bool updated; /* complete domain flush required */ unsigned dev_cnt; /* devices assigned to this domain */ unsigned dev_iommu[MAX_IOMMUS]; /* per-IOMMU reference count */ - void *priv; /* private data */ - struct iommu_domain *iommu_domain; /* Pointer to generic - domain structure */ - + void *priv; /* private data */ }; /* -- cgit v1.2.3 From 00a77deb0f2f2c35cfeba183df98f92487d2e88b Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Mar 2015 13:43:08 +0100 Subject: iommu/vt-d: Make use of domain_alloc and domain_free Get rid of domain_init and domain_destroy and implement domain_alloc/domain_free instead. Reviewed-by: Alex Williamson Signed-off-by: Joerg Roedel --- drivers/iommu/intel-iommu.c | 48 ++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index ae4c1a854e57..a312e4966686 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -339,7 +339,7 @@ struct dmar_domain { DECLARE_BITMAP(iommu_bmp, DMAR_UNITS_SUPPORTED); /* bitmap of iommus this domain uses*/ - struct list_head devices; /* all devices' list */ + struct list_head devices; /* all devices' list */ struct iova_domain iovad; /* iova's that belong to this domain */ struct dma_pte *pgd; /* virtual address */ @@ -358,6 +358,9 @@ struct dmar_domain { 2 == 1GiB, 3 == 512GiB, 4 == 1TiB */ spinlock_t iommu_lock; /* protect iommu set in domain */ u64 max_addr; /* maximum mapped address */ + + struct iommu_domain domain; /* generic domain data structure for + iommu core */ }; /* PCI domain-device relationship */ @@ -449,6 +452,12 @@ static LIST_HEAD(device_domain_list); static const struct iommu_ops intel_iommu_ops; +/* Convert generic 'struct iommu_domain to private struct dmar_domain */ +static struct dmar_domain *to_dmar_domain(struct iommu_domain *dom) +{ + return container_of(dom, struct dmar_domain, domain); +} + static int __init intel_iommu_setup(char *str) { if (!str) @@ -4340,44 +4349,45 @@ static int md_domain_init(struct dmar_domain *domain, int guest_width) return 0; } -static int intel_iommu_domain_init(struct iommu_domain *domain) +static struct iommu_domain *intel_iommu_domain_alloc(unsigned type) { struct dmar_domain *dmar_domain; + struct iommu_domain *domain; + + if (type != IOMMU_DOMAIN_UNMANAGED) + return NULL; dmar_domain = alloc_domain(DOMAIN_FLAG_VIRTUAL_MACHINE); if (!dmar_domain) { printk(KERN_ERR "intel_iommu_domain_init: dmar_domain == NULL\n"); - return -ENOMEM; + return NULL; } if (md_domain_init(dmar_domain, DEFAULT_DOMAIN_ADDRESS_WIDTH)) { printk(KERN_ERR "intel_iommu_domain_init() failed\n"); domain_exit(dmar_domain); - return -ENOMEM; + return NULL; } domain_update_iommu_cap(dmar_domain); - domain->priv = dmar_domain; + domain = &dmar_domain->domain; domain->geometry.aperture_start = 0; domain->geometry.aperture_end = __DOMAIN_MAX_ADDR(dmar_domain->gaw); domain->geometry.force_aperture = true; - return 0; + return domain; } -static void intel_iommu_domain_destroy(struct iommu_domain *domain) +static void intel_iommu_domain_free(struct iommu_domain *domain) { - struct dmar_domain *dmar_domain = domain->priv; - - domain->priv = NULL; - domain_exit(dmar_domain); + domain_exit(to_dmar_domain(domain)); } static int intel_iommu_attach_device(struct iommu_domain *domain, struct device *dev) { - struct dmar_domain *dmar_domain = domain->priv; + struct dmar_domain *dmar_domain = to_dmar_domain(domain); struct intel_iommu *iommu; int addr_width; u8 bus, devfn; @@ -4442,16 +4452,14 @@ static int intel_iommu_attach_device(struct iommu_domain *domain, static void intel_iommu_detach_device(struct iommu_domain *domain, struct device *dev) { - struct dmar_domain *dmar_domain = domain->priv; - - domain_remove_one_dev_info(dmar_domain, dev); + domain_remove_one_dev_info(to_dmar_domain(domain), dev); } static int intel_iommu_map(struct iommu_domain *domain, unsigned long iova, phys_addr_t hpa, size_t size, int iommu_prot) { - struct dmar_domain *dmar_domain = domain->priv; + struct dmar_domain *dmar_domain = to_dmar_domain(domain); u64 max_addr; int prot = 0; int ret; @@ -4488,7 +4496,7 @@ static int intel_iommu_map(struct iommu_domain *domain, static size_t intel_iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) { - struct dmar_domain *dmar_domain = domain->priv; + struct dmar_domain *dmar_domain = to_dmar_domain(domain); struct page *freelist = NULL; struct intel_iommu *iommu; unsigned long start_pfn, last_pfn; @@ -4536,7 +4544,7 @@ static size_t intel_iommu_unmap(struct iommu_domain *domain, static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova) { - struct dmar_domain *dmar_domain = domain->priv; + struct dmar_domain *dmar_domain = to_dmar_domain(domain); struct dma_pte *pte; int level = 0; u64 phys = 0; @@ -4595,8 +4603,8 @@ static void intel_iommu_remove_device(struct device *dev) static const struct iommu_ops intel_iommu_ops = { .capable = intel_iommu_capable, - .domain_init = intel_iommu_domain_init, - .domain_destroy = intel_iommu_domain_destroy, + .domain_alloc = intel_iommu_domain_alloc, + .domain_free = intel_iommu_domain_free, .attach_dev = intel_iommu_attach_device, .detach_dev = intel_iommu_detach_device, .map = intel_iommu_map, -- cgit v1.2.3 From 8cf851e0945251766e8bf1f966a9aef80663ed63 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Mar 2015 13:43:09 +0100 Subject: iommu/omap: Make use of domain_alloc and domain_free Implement the new domain_alloc and domain_free call-backs and remove the old domain_init/destroy ones. Signed-off-by: Joerg Roedel --- drivers/iommu/omap-iommu.c | 49 +++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c index a4ba851825c2..a22c33d6a486 100644 --- a/drivers/iommu/omap-iommu.c +++ b/drivers/iommu/omap-iommu.c @@ -59,6 +59,7 @@ struct omap_iommu_domain { struct omap_iommu *iommu_dev; struct device *dev; spinlock_t lock; + struct iommu_domain domain; }; #define MMU_LOCK_BASE_SHIFT 10 @@ -79,6 +80,15 @@ struct iotlb_lock { static struct platform_driver omap_iommu_driver; static struct kmem_cache *iopte_cachep; +/** + * to_omap_domain - Get struct omap_iommu_domain from generic iommu_domain + * @dom: generic iommu domain handle + **/ +static struct omap_iommu_domain *to_omap_domain(struct iommu_domain *dom) +{ + return container_of(dom, struct omap_iommu_domain, domain); +} + /** * omap_iommu_save_ctx - Save registers for pm off-mode support * @dev: client device @@ -901,7 +911,7 @@ static irqreturn_t iommu_fault_handler(int irq, void *data) u32 *iopgd, *iopte; struct omap_iommu *obj = data; struct iommu_domain *domain = obj->domain; - struct omap_iommu_domain *omap_domain = domain->priv; + struct omap_iommu_domain *omap_domain = to_omap_domain(domain); if (!omap_domain->iommu_dev) return IRQ_NONE; @@ -1113,7 +1123,7 @@ static u32 iotlb_init_entry(struct iotlb_entry *e, u32 da, u32 pa, int pgsz) static int omap_iommu_map(struct iommu_domain *domain, unsigned long da, phys_addr_t pa, size_t bytes, int prot) { - struct omap_iommu_domain *omap_domain = domain->priv; + struct omap_iommu_domain *omap_domain = to_omap_domain(domain); struct omap_iommu *oiommu = omap_domain->iommu_dev; struct device *dev = oiommu->dev; struct iotlb_entry e; @@ -1140,7 +1150,7 @@ static int omap_iommu_map(struct iommu_domain *domain, unsigned long da, static size_t omap_iommu_unmap(struct iommu_domain *domain, unsigned long da, size_t size) { - struct omap_iommu_domain *omap_domain = domain->priv; + struct omap_iommu_domain *omap_domain = to_omap_domain(domain); struct omap_iommu *oiommu = omap_domain->iommu_dev; struct device *dev = oiommu->dev; @@ -1152,7 +1162,7 @@ static size_t omap_iommu_unmap(struct iommu_domain *domain, unsigned long da, static int omap_iommu_attach_dev(struct iommu_domain *domain, struct device *dev) { - struct omap_iommu_domain *omap_domain = domain->priv; + struct omap_iommu_domain *omap_domain = to_omap_domain(domain); struct omap_iommu *oiommu; struct omap_iommu_arch_data *arch_data = dev->archdata.iommu; int ret = 0; @@ -1212,17 +1222,20 @@ static void _omap_iommu_detach_dev(struct omap_iommu_domain *omap_domain, static void omap_iommu_detach_dev(struct iommu_domain *domain, struct device *dev) { - struct omap_iommu_domain *omap_domain = domain->priv; + struct omap_iommu_domain *omap_domain = to_omap_domain(domain); spin_lock(&omap_domain->lock); _omap_iommu_detach_dev(omap_domain, dev); spin_unlock(&omap_domain->lock); } -static int omap_iommu_domain_init(struct iommu_domain *domain) +static struct iommu_domain *omap_iommu_domain_alloc(unsigned type) { struct omap_iommu_domain *omap_domain; + if (type != IOMMU_DOMAIN_UNMANAGED) + return NULL; + omap_domain = kzalloc(sizeof(*omap_domain), GFP_KERNEL); if (!omap_domain) { pr_err("kzalloc failed\n"); @@ -1244,25 +1257,21 @@ static int omap_iommu_domain_init(struct iommu_domain *domain) clean_dcache_area(omap_domain->pgtable, IOPGD_TABLE_SIZE); spin_lock_init(&omap_domain->lock); - domain->priv = omap_domain; + omap_domain->domain.geometry.aperture_start = 0; + omap_domain->domain.geometry.aperture_end = (1ULL << 32) - 1; + omap_domain->domain.geometry.force_aperture = true; - domain->geometry.aperture_start = 0; - domain->geometry.aperture_end = (1ULL << 32) - 1; - domain->geometry.force_aperture = true; - - return 0; + return &omap_domain->domain; fail_nomem: kfree(omap_domain); out: - return -ENOMEM; + return NULL; } -static void omap_iommu_domain_destroy(struct iommu_domain *domain) +static void omap_iommu_domain_free(struct iommu_domain *domain) { - struct omap_iommu_domain *omap_domain = domain->priv; - - domain->priv = NULL; + struct omap_iommu_domain *omap_domain = to_omap_domain(domain); /* * An iommu device is still attached @@ -1278,7 +1287,7 @@ static void omap_iommu_domain_destroy(struct iommu_domain *domain) static phys_addr_t omap_iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t da) { - struct omap_iommu_domain *omap_domain = domain->priv; + struct omap_iommu_domain *omap_domain = to_omap_domain(domain); struct omap_iommu *oiommu = omap_domain->iommu_dev; struct device *dev = oiommu->dev; u32 *pgd, *pte; @@ -1358,8 +1367,8 @@ static void omap_iommu_remove_device(struct device *dev) } static const struct iommu_ops omap_iommu_ops = { - .domain_init = omap_iommu_domain_init, - .domain_destroy = omap_iommu_domain_destroy, + .domain_alloc = omap_iommu_domain_alloc, + .domain_free = omap_iommu_domain_free, .attach_dev = omap_iommu_attach_dev, .detach_dev = omap_iommu_detach_dev, .map = omap_iommu_map, -- cgit v1.2.3 From 1d672638fca24db2a66101c2109d94dbee084e96 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Mar 2015 13:43:10 +0100 Subject: iommu/arm-smmu: Make use of domain_alloc and domain_free Implement domain_alloc and domain_free iommu-ops as a replacement for domain_init/domain_destroy. Acked-by: Will Deacon Signed-off-by: Joerg Roedel --- drivers/iommu/arm-smmu.c | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index fc13dd56953e..5575c3d485d0 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -343,6 +343,7 @@ struct arm_smmu_domain { struct arm_smmu_cfg cfg; enum arm_smmu_domain_stage stage; struct mutex init_mutex; /* Protects smmu pointer */ + struct iommu_domain domain; }; static struct iommu_ops arm_smmu_ops; @@ -360,6 +361,11 @@ static struct arm_smmu_option_prop arm_smmu_options[] = { { 0, NULL}, }; +static struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom) +{ + return container_of(dom, struct arm_smmu_domain, domain); +} + static void parse_driver_options(struct arm_smmu_device *smmu) { int i = 0; @@ -645,7 +651,7 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev) u32 fsr, far, fsynr, resume; unsigned long iova; struct iommu_domain *domain = dev; - struct arm_smmu_domain *smmu_domain = domain->priv; + struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); struct arm_smmu_cfg *cfg = &smmu_domain->cfg; struct arm_smmu_device *smmu = smmu_domain->smmu; void __iomem *cb_base; @@ -836,7 +842,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, struct io_pgtable_ops *pgtbl_ops; struct io_pgtable_cfg pgtbl_cfg; enum io_pgtable_fmt fmt; - struct arm_smmu_domain *smmu_domain = domain->priv; + struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); struct arm_smmu_cfg *cfg = &smmu_domain->cfg; mutex_lock(&smmu_domain->init_mutex); @@ -958,7 +964,7 @@ out_unlock: static void arm_smmu_destroy_domain_context(struct iommu_domain *domain) { - struct arm_smmu_domain *smmu_domain = domain->priv; + struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); struct arm_smmu_device *smmu = smmu_domain->smmu; struct arm_smmu_cfg *cfg = &smmu_domain->cfg; void __iomem *cb_base; @@ -985,10 +991,12 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain) __arm_smmu_free_bitmap(smmu->context_map, cfg->cbndx); } -static int arm_smmu_domain_init(struct iommu_domain *domain) +static struct iommu_domain *arm_smmu_domain_alloc(unsigned type) { struct arm_smmu_domain *smmu_domain; + if (type != IOMMU_DOMAIN_UNMANAGED) + return NULL; /* * Allocate the domain and initialise some of its data structures. * We can't really do anything meaningful until we've added a @@ -996,17 +1004,17 @@ static int arm_smmu_domain_init(struct iommu_domain *domain) */ smmu_domain = kzalloc(sizeof(*smmu_domain), GFP_KERNEL); if (!smmu_domain) - return -ENOMEM; + return NULL; mutex_init(&smmu_domain->init_mutex); spin_lock_init(&smmu_domain->pgtbl_lock); - domain->priv = smmu_domain; - return 0; + + return &smmu_domain->domain; } -static void arm_smmu_domain_destroy(struct iommu_domain *domain) +static void arm_smmu_domain_free(struct iommu_domain *domain) { - struct arm_smmu_domain *smmu_domain = domain->priv; + struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); /* * Free the domain resources. We assume that all devices have @@ -1143,7 +1151,7 @@ static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain, static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) { int ret; - struct arm_smmu_domain *smmu_domain = domain->priv; + struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); struct arm_smmu_device *smmu; struct arm_smmu_master_cfg *cfg; @@ -1187,7 +1195,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev) { - struct arm_smmu_domain *smmu_domain = domain->priv; + struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); struct arm_smmu_master_cfg *cfg; cfg = find_smmu_master_cfg(dev); @@ -1203,7 +1211,7 @@ static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova, { int ret; unsigned long flags; - struct arm_smmu_domain *smmu_domain = domain->priv; + struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); struct io_pgtable_ops *ops= smmu_domain->pgtbl_ops; if (!ops) @@ -1220,7 +1228,7 @@ static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova, { size_t ret; unsigned long flags; - struct arm_smmu_domain *smmu_domain = domain->priv; + struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); struct io_pgtable_ops *ops= smmu_domain->pgtbl_ops; if (!ops) @@ -1235,7 +1243,7 @@ static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova, static phys_addr_t arm_smmu_iova_to_phys_hard(struct iommu_domain *domain, dma_addr_t iova) { - struct arm_smmu_domain *smmu_domain = domain->priv; + struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); struct arm_smmu_device *smmu = smmu_domain->smmu; struct arm_smmu_cfg *cfg = &smmu_domain->cfg; struct io_pgtable_ops *ops= smmu_domain->pgtbl_ops; @@ -1281,7 +1289,7 @@ static phys_addr_t arm_smmu_iova_to_phys(struct iommu_domain *domain, { phys_addr_t ret; unsigned long flags; - struct arm_smmu_domain *smmu_domain = domain->priv; + struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); struct io_pgtable_ops *ops= smmu_domain->pgtbl_ops; if (!ops) @@ -1389,7 +1397,7 @@ static void arm_smmu_remove_device(struct device *dev) static int arm_smmu_domain_get_attr(struct iommu_domain *domain, enum iommu_attr attr, void *data) { - struct arm_smmu_domain *smmu_domain = domain->priv; + struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); switch (attr) { case DOMAIN_ATTR_NESTING: @@ -1404,7 +1412,7 @@ static int arm_smmu_domain_set_attr(struct iommu_domain *domain, enum iommu_attr attr, void *data) { int ret = 0; - struct arm_smmu_domain *smmu_domain = domain->priv; + struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); mutex_lock(&smmu_domain->init_mutex); @@ -1432,8 +1440,8 @@ out_unlock: static struct iommu_ops arm_smmu_ops = { .capable = arm_smmu_capable, - .domain_init = arm_smmu_domain_init, - .domain_destroy = arm_smmu_domain_destroy, + .domain_alloc = arm_smmu_domain_alloc, + .domain_free = arm_smmu_domain_free, .attach_dev = arm_smmu_attach_dev, .detach_dev = arm_smmu_detach_dev, .map = arm_smmu_map, -- cgit v1.2.3 From e1fd1eaa3323b3ea68f2ad3116b8cbe1f34cb2a2 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Mar 2015 13:43:11 +0100 Subject: iommu/exynos: Make use of domain_alloc and domain_free Implement domain_alloc and domain_free iommu-ops as a replacement for domain_init/domain_destroy. Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 87 ++++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 40 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index dc14fec4ede1..3e898504a7c4 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -200,6 +200,7 @@ struct exynos_iommu_domain { short *lv2entcnt; /* free lv2 entry counter for each section */ spinlock_t lock; /* lock for this structure */ spinlock_t pgtablelock; /* lock for modifying page table @ pgtable */ + struct iommu_domain domain; /* generic domain data structure */ }; struct sysmmu_drvdata { @@ -214,6 +215,11 @@ struct sysmmu_drvdata { phys_addr_t pgtable; }; +static struct exynos_iommu_domain *to_exynos_domain(struct iommu_domain *dom) +{ + return container_of(dom, struct exynos_iommu_domain, domain); +} + static bool set_sysmmu_active(struct sysmmu_drvdata *data) { /* return true if the System MMU was not active previously @@ -696,58 +702,60 @@ static inline void pgtable_flush(void *vastart, void *vaend) virt_to_phys(vaend)); } -static int exynos_iommu_domain_init(struct iommu_domain *domain) +static struct iommu_domain *exynos_iommu_domain_alloc(unsigned type) { - struct exynos_iommu_domain *priv; + struct exynos_iommu_domain *exynos_domain; int i; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + if (type != IOMMU_DOMAIN_UNMANAGED) + return NULL; + + exynos_domain = kzalloc(sizeof(*exynos_domain), GFP_KERNEL); + if (!exynos_domain) + return NULL; - priv->pgtable = (sysmmu_pte_t *)__get_free_pages(GFP_KERNEL, 2); - if (!priv->pgtable) + exynos_domain->pgtable = (sysmmu_pte_t *)__get_free_pages(GFP_KERNEL, 2); + if (!exynos_domain->pgtable) goto err_pgtable; - priv->lv2entcnt = (short *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); - if (!priv->lv2entcnt) + exynos_domain->lv2entcnt = (short *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); + if (!exynos_domain->lv2entcnt) goto err_counter; /* Workaround for System MMU v3.3 to prevent caching 1MiB mapping */ for (i = 0; i < NUM_LV1ENTRIES; i += 8) { - priv->pgtable[i + 0] = ZERO_LV2LINK; - priv->pgtable[i + 1] = ZERO_LV2LINK; - priv->pgtable[i + 2] = ZERO_LV2LINK; - priv->pgtable[i + 3] = ZERO_LV2LINK; - priv->pgtable[i + 4] = ZERO_LV2LINK; - priv->pgtable[i + 5] = ZERO_LV2LINK; - priv->pgtable[i + 6] = ZERO_LV2LINK; - priv->pgtable[i + 7] = ZERO_LV2LINK; + exynos_domain->pgtable[i + 0] = ZERO_LV2LINK; + exynos_domain->pgtable[i + 1] = ZERO_LV2LINK; + exynos_domain->pgtable[i + 2] = ZERO_LV2LINK; + exynos_domain->pgtable[i + 3] = ZERO_LV2LINK; + exynos_domain->pgtable[i + 4] = ZERO_LV2LINK; + exynos_domain->pgtable[i + 5] = ZERO_LV2LINK; + exynos_domain->pgtable[i + 6] = ZERO_LV2LINK; + exynos_domain->pgtable[i + 7] = ZERO_LV2LINK; } - pgtable_flush(priv->pgtable, priv->pgtable + NUM_LV1ENTRIES); + pgtable_flush(exynos_domain->pgtable, exynos_domain->pgtable + NUM_LV1ENTRIES); - spin_lock_init(&priv->lock); - spin_lock_init(&priv->pgtablelock); - INIT_LIST_HEAD(&priv->clients); + spin_lock_init(&exynos_domain->lock); + spin_lock_init(&exynos_domain->pgtablelock); + INIT_LIST_HEAD(&exynos_domain->clients); - domain->geometry.aperture_start = 0; - domain->geometry.aperture_end = ~0UL; - domain->geometry.force_aperture = true; + exynos_domain->domain.geometry.aperture_start = 0; + exynos_domain->domain.geometry.aperture_end = ~0UL; + exynos_domain->domain.geometry.force_aperture = true; - domain->priv = priv; - return 0; + return &exynos_domain->domain; err_counter: - free_pages((unsigned long)priv->pgtable, 2); + free_pages((unsigned long)exynos_domain->pgtable, 2); err_pgtable: - kfree(priv); - return -ENOMEM; + kfree(exynos_domain); + return NULL; } -static void exynos_iommu_domain_destroy(struct iommu_domain *domain) +static void exynos_iommu_domain_free(struct iommu_domain *domain) { - struct exynos_iommu_domain *priv = domain->priv; + struct exynos_iommu_domain *priv = to_exynos_domain(domain); struct exynos_iommu_owner *owner; unsigned long flags; int i; @@ -773,15 +781,14 @@ static void exynos_iommu_domain_destroy(struct iommu_domain *domain) free_pages((unsigned long)priv->pgtable, 2); free_pages((unsigned long)priv->lv2entcnt, 1); - kfree(domain->priv); - domain->priv = NULL; + kfree(priv); } static int exynos_iommu_attach_device(struct iommu_domain *domain, struct device *dev) { struct exynos_iommu_owner *owner = dev->archdata.iommu; - struct exynos_iommu_domain *priv = domain->priv; + struct exynos_iommu_domain *priv = to_exynos_domain(domain); phys_addr_t pagetable = virt_to_phys(priv->pgtable); unsigned long flags; int ret; @@ -812,7 +819,7 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, struct device *dev) { struct exynos_iommu_owner *owner; - struct exynos_iommu_domain *priv = domain->priv; + struct exynos_iommu_domain *priv = to_exynos_domain(domain); phys_addr_t pagetable = virt_to_phys(priv->pgtable); unsigned long flags; @@ -988,7 +995,7 @@ static int lv2set_page(sysmmu_pte_t *pent, phys_addr_t paddr, size_t size, static int exynos_iommu_map(struct iommu_domain *domain, unsigned long l_iova, phys_addr_t paddr, size_t size, int prot) { - struct exynos_iommu_domain *priv = domain->priv; + struct exynos_iommu_domain *priv = to_exynos_domain(domain); sysmmu_pte_t *entry; sysmmu_iova_t iova = (sysmmu_iova_t)l_iova; unsigned long flags; @@ -1042,7 +1049,7 @@ static void exynos_iommu_tlb_invalidate_entry(struct exynos_iommu_domain *priv, static size_t exynos_iommu_unmap(struct iommu_domain *domain, unsigned long l_iova, size_t size) { - struct exynos_iommu_domain *priv = domain->priv; + struct exynos_iommu_domain *priv = to_exynos_domain(domain); sysmmu_iova_t iova = (sysmmu_iova_t)l_iova; sysmmu_pte_t *ent; size_t err_pgsize; @@ -1119,7 +1126,7 @@ err: static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova) { - struct exynos_iommu_domain *priv = domain->priv; + struct exynos_iommu_domain *priv = to_exynos_domain(domain); sysmmu_pte_t *entry; unsigned long flags; phys_addr_t phys = 0; @@ -1171,8 +1178,8 @@ static void exynos_iommu_remove_device(struct device *dev) } static const struct iommu_ops exynos_iommu_ops = { - .domain_init = exynos_iommu_domain_init, - .domain_destroy = exynos_iommu_domain_destroy, + .domain_alloc = exynos_iommu_domain_alloc, + .domain_free = exynos_iommu_domain_free, .attach_dev = exynos_iommu_attach_device, .detach_dev = exynos_iommu_detach_device, .map = exynos_iommu_map, -- cgit v1.2.3 From d5f1a81ccb8947828f60b50d8e9ed7617259a9ec Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Mar 2015 13:43:12 +0100 Subject: iommu/tegra-smmu: Make use of domain_alloc and domain_free Implement domain_alloc and domain_free iommu-ops as a replacement for domain_init/domain_destroy. Tested-by: Thierry Reding Acked-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-smmu.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 6e134c7c227f..720829724d86 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -31,7 +31,7 @@ struct tegra_smmu { }; struct tegra_smmu_as { - struct iommu_domain *domain; + struct iommu_domain domain; struct tegra_smmu *smmu; unsigned int use_count; struct page *count; @@ -40,6 +40,11 @@ struct tegra_smmu_as { u32 attr; }; +static struct tegra_smmu_as *to_smmu_as(struct iommu_domain *dom) +{ + return container_of(dom, struct tegra_smmu_as, domain); +} + static inline void smmu_writel(struct tegra_smmu *smmu, u32 value, unsigned long offset) { @@ -224,30 +229,32 @@ static bool tegra_smmu_capable(enum iommu_cap cap) return false; } -static int tegra_smmu_domain_init(struct iommu_domain *domain) +static struct iommu_domain *tegra_smmu_domain_alloc(unsigned type) { struct tegra_smmu_as *as; unsigned int i; uint32_t *pd; + if (type != IOMMU_DOMAIN_UNMANAGED) + return NULL; + as = kzalloc(sizeof(*as), GFP_KERNEL); if (!as) - return -ENOMEM; + return NULL; as->attr = SMMU_PD_READABLE | SMMU_PD_WRITABLE | SMMU_PD_NONSECURE; - as->domain = domain; as->pd = alloc_page(GFP_KERNEL | __GFP_DMA); if (!as->pd) { kfree(as); - return -ENOMEM; + return NULL; } as->count = alloc_page(GFP_KERNEL); if (!as->count) { __free_page(as->pd); kfree(as); - return -ENOMEM; + return NULL; } /* clear PDEs */ @@ -264,14 +271,12 @@ static int tegra_smmu_domain_init(struct iommu_domain *domain) for (i = 0; i < SMMU_NUM_PDE; i++) pd[i] = 0; - domain->priv = as; - - return 0; + return &as->domain; } -static void tegra_smmu_domain_destroy(struct iommu_domain *domain) +static void tegra_smmu_domain_free(struct iommu_domain *domain) { - struct tegra_smmu_as *as = domain->priv; + struct tegra_smmu_as *as = to_smmu_as(domain); /* TODO: free page directory and page tables */ ClearPageReserved(as->pd); @@ -395,7 +400,7 @@ static int tegra_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) { struct tegra_smmu *smmu = dev->archdata.iommu; - struct tegra_smmu_as *as = domain->priv; + struct tegra_smmu_as *as = to_smmu_as(domain); struct device_node *np = dev->of_node; struct of_phandle_args args; unsigned int index = 0; @@ -428,7 +433,7 @@ static int tegra_smmu_attach_dev(struct iommu_domain *domain, static void tegra_smmu_detach_dev(struct iommu_domain *domain, struct device *dev) { - struct tegra_smmu_as *as = domain->priv; + struct tegra_smmu_as *as = to_smmu_as(domain); struct device_node *np = dev->of_node; struct tegra_smmu *smmu = as->smmu; struct of_phandle_args args; @@ -524,7 +529,7 @@ static void as_put_pte(struct tegra_smmu_as *as, dma_addr_t iova) static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova, phys_addr_t paddr, size_t size, int prot) { - struct tegra_smmu_as *as = domain->priv; + struct tegra_smmu_as *as = to_smmu_as(domain); struct tegra_smmu *smmu = as->smmu; unsigned long offset; struct page *page; @@ -548,7 +553,7 @@ static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova, static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) { - struct tegra_smmu_as *as = domain->priv; + struct tegra_smmu_as *as = to_smmu_as(domain); struct tegra_smmu *smmu = as->smmu; unsigned long offset; struct page *page; @@ -572,7 +577,7 @@ static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova, static phys_addr_t tegra_smmu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova) { - struct tegra_smmu_as *as = domain->priv; + struct tegra_smmu_as *as = to_smmu_as(domain); struct page *page; unsigned long pfn; u32 *pte; @@ -633,8 +638,8 @@ static void tegra_smmu_remove_device(struct device *dev) static const struct iommu_ops tegra_smmu_ops = { .capable = tegra_smmu_capable, - .domain_init = tegra_smmu_domain_init, - .domain_destroy = tegra_smmu_domain_destroy, + .domain_alloc = tegra_smmu_domain_alloc, + .domain_free = tegra_smmu_domain_free, .attach_dev = tegra_smmu_attach_dev, .detach_dev = tegra_smmu_detach_dev, .add_device = tegra_smmu_add_device, -- cgit v1.2.3 From b5cbb386ae312b2028cee61c96f65581ac0d44ec Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Mar 2015 13:43:13 +0100 Subject: iommu/tegra-gart: Make use of domain_alloc and domain_free Implement domain_alloc and domain_free iommu-ops as a replacement for domain_init/domain_destroy. Tested-by: Thierry Reding Acked-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-gart.c | 67 +++++++++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index c48da057dbb1..fc588a1ffeef 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c @@ -63,11 +63,21 @@ struct gart_device { struct device *dev; }; +struct gart_domain { + struct iommu_domain domain; /* generic domain handle */ + struct gart_device *gart; /* link to gart device */ +}; + static struct gart_device *gart_handle; /* unique for a system */ #define GART_PTE(_pfn) \ (GART_ENTRY_PHYS_ADDR_VALID | ((_pfn) << PAGE_SHIFT)) +static struct gart_domain *to_gart_domain(struct iommu_domain *dom) +{ + return container_of(dom, struct gart_domain, domain); +} + /* * Any interaction between any block on PPSB and a block on APB or AHB * must have these read-back to ensure the APB/AHB bus transaction is @@ -156,6 +166,7 @@ static inline bool gart_iova_range_valid(struct gart_device *gart, static int gart_iommu_attach_dev(struct iommu_domain *domain, struct device *dev) { + struct gart_domain *gart_domain = to_gart_domain(domain); struct gart_device *gart; struct gart_client *client, *c; int err = 0; @@ -163,7 +174,7 @@ static int gart_iommu_attach_dev(struct iommu_domain *domain, gart = gart_handle; if (!gart) return -EINVAL; - domain->priv = gart; + gart_domain->gart = gart; domain->geometry.aperture_start = gart->iovmm_base; domain->geometry.aperture_end = gart->iovmm_base + @@ -198,7 +209,8 @@ fail: static void gart_iommu_detach_dev(struct iommu_domain *domain, struct device *dev) { - struct gart_device *gart = domain->priv; + struct gart_domain *gart_domain = to_gart_domain(domain); + struct gart_device *gart = gart_domain->gart; struct gart_client *c; spin_lock(&gart->client_lock); @@ -216,33 +228,44 @@ out: spin_unlock(&gart->client_lock); } -static int gart_iommu_domain_init(struct iommu_domain *domain) +static struct iommu_domain *gart_iommu_domain_alloc(unsigned type) { - return 0; + struct gart_domain *gart_domain; + + if (type != IOMMU_DOMAIN_UNMANAGED) + return NULL; + + gart_domain = kzalloc(sizeof(*gart_domain), GFP_KERNEL); + if (!gart_domain) + return NULL; + + return &gart_domain->domain; } -static void gart_iommu_domain_destroy(struct iommu_domain *domain) +static void gart_iommu_domain_free(struct iommu_domain *domain) { - struct gart_device *gart = domain->priv; - - if (!gart) - return; + struct gart_domain *gart_domain = to_gart_domain(domain); + struct gart_device *gart = gart_domain->gart; - spin_lock(&gart->client_lock); - if (!list_empty(&gart->client)) { - struct gart_client *c; + if (gart) { + spin_lock(&gart->client_lock); + if (!list_empty(&gart->client)) { + struct gart_client *c; - list_for_each_entry(c, &gart->client, list) - gart_iommu_detach_dev(domain, c->dev); + list_for_each_entry(c, &gart->client, list) + gart_iommu_detach_dev(domain, c->dev); + } + spin_unlock(&gart->client_lock); } - spin_unlock(&gart->client_lock); - domain->priv = NULL; + + kfree(gart_domain); } static int gart_iommu_map(struct iommu_domain *domain, unsigned long iova, phys_addr_t pa, size_t bytes, int prot) { - struct gart_device *gart = domain->priv; + struct gart_domain *gart_domain = to_gart_domain(domain); + struct gart_device *gart = gart_domain->gart; unsigned long flags; unsigned long pfn; @@ -265,7 +288,8 @@ static int gart_iommu_map(struct iommu_domain *domain, unsigned long iova, static size_t gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t bytes) { - struct gart_device *gart = domain->priv; + struct gart_domain *gart_domain = to_gart_domain(domain); + struct gart_device *gart = gart_domain->gart; unsigned long flags; if (!gart_iova_range_valid(gart, iova, bytes)) @@ -281,7 +305,8 @@ static size_t gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova, static phys_addr_t gart_iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova) { - struct gart_device *gart = domain->priv; + struct gart_domain *gart_domain = to_gart_domain(domain); + struct gart_device *gart = gart_domain->gart; unsigned long pte; phys_addr_t pa; unsigned long flags; @@ -310,8 +335,8 @@ static bool gart_iommu_capable(enum iommu_cap cap) static const struct iommu_ops gart_iommu_ops = { .capable = gart_iommu_capable, - .domain_init = gart_iommu_domain_init, - .domain_destroy = gart_iommu_domain_destroy, + .domain_alloc = gart_iommu_domain_alloc, + .domain_free = gart_iommu_domain_free, .attach_dev = gart_iommu_attach_dev, .detach_dev = gart_iommu_detach_dev, .map = gart_iommu_map, -- cgit v1.2.3 From 3e116c3cd2a99fb9628fe3169b454482a7f07047 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Mar 2015 13:43:14 +0100 Subject: iommu/msm: Make use of domain_alloc and domain_free Implement domain_alloc and domain_free iommu-ops as a replacement for domain_init/domain_destroy. Signed-off-by: Joerg Roedel --- drivers/iommu/msm_iommu.c | 73 +++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c index e1b05379ca0e..15a2063812fa 100644 --- a/drivers/iommu/msm_iommu.c +++ b/drivers/iommu/msm_iommu.c @@ -52,8 +52,14 @@ DEFINE_SPINLOCK(msm_iommu_lock); struct msm_priv { unsigned long *pgtable; struct list_head list_attached; + struct iommu_domain domain; }; +static struct msm_priv *to_msm_priv(struct iommu_domain *dom) +{ + return container_of(dom, struct msm_priv, domain); +} + static int __enable_clocks(struct msm_iommu_drvdata *drvdata) { int ret; @@ -79,7 +85,7 @@ static void __disable_clocks(struct msm_iommu_drvdata *drvdata) static int __flush_iotlb(struct iommu_domain *domain) { - struct msm_priv *priv = domain->priv; + struct msm_priv *priv = to_msm_priv(domain); struct msm_iommu_drvdata *iommu_drvdata; struct msm_iommu_ctx_drvdata *ctx_drvdata; int ret = 0; @@ -209,10 +215,14 @@ static void __program_context(void __iomem *base, int ctx, phys_addr_t pgtable) SET_M(base, ctx, 1); } -static int msm_iommu_domain_init(struct iommu_domain *domain) +static struct iommu_domain *msm_iommu_domain_alloc(unsigned type) { - struct msm_priv *priv = kzalloc(sizeof(*priv), GFP_KERNEL); + struct msm_priv *priv; + if (type != IOMMU_DOMAIN_UNMANAGED) + return NULL; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) goto fail_nomem; @@ -224,20 +234,19 @@ static int msm_iommu_domain_init(struct iommu_domain *domain) goto fail_nomem; memset(priv->pgtable, 0, SZ_16K); - domain->priv = priv; - domain->geometry.aperture_start = 0; - domain->geometry.aperture_end = (1ULL << 32) - 1; - domain->geometry.force_aperture = true; + priv->domain.geometry.aperture_start = 0; + priv->domain.geometry.aperture_end = (1ULL << 32) - 1; + priv->domain.geometry.force_aperture = true; - return 0; + return &priv->domain; fail_nomem: kfree(priv); - return -ENOMEM; + return NULL; } -static void msm_iommu_domain_destroy(struct iommu_domain *domain) +static void msm_iommu_domain_free(struct iommu_domain *domain) { struct msm_priv *priv; unsigned long flags; @@ -245,20 +254,17 @@ static void msm_iommu_domain_destroy(struct iommu_domain *domain) int i; spin_lock_irqsave(&msm_iommu_lock, flags); - priv = domain->priv; - domain->priv = NULL; + priv = to_msm_priv(domain); - if (priv) { - fl_table = priv->pgtable; + fl_table = priv->pgtable; - for (i = 0; i < NUM_FL_PTE; i++) - if ((fl_table[i] & 0x03) == FL_TYPE_TABLE) - free_page((unsigned long) __va(((fl_table[i]) & - FL_BASE_MASK))); + for (i = 0; i < NUM_FL_PTE; i++) + if ((fl_table[i] & 0x03) == FL_TYPE_TABLE) + free_page((unsigned long) __va(((fl_table[i]) & + FL_BASE_MASK))); - free_pages((unsigned long)priv->pgtable, get_order(SZ_16K)); - priv->pgtable = NULL; - } + free_pages((unsigned long)priv->pgtable, get_order(SZ_16K)); + priv->pgtable = NULL; kfree(priv); spin_unlock_irqrestore(&msm_iommu_lock, flags); @@ -276,9 +282,9 @@ static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev) spin_lock_irqsave(&msm_iommu_lock, flags); - priv = domain->priv; + priv = to_msm_priv(domain); - if (!priv || !dev) { + if (!dev) { ret = -EINVAL; goto fail; } @@ -330,9 +336,9 @@ static void msm_iommu_detach_dev(struct iommu_domain *domain, int ret; spin_lock_irqsave(&msm_iommu_lock, flags); - priv = domain->priv; + priv = to_msm_priv(domain); - if (!priv || !dev) + if (!dev) goto fail; iommu_drvdata = dev_get_drvdata(dev->parent); @@ -382,11 +388,7 @@ static int msm_iommu_map(struct iommu_domain *domain, unsigned long va, goto fail; } - priv = domain->priv; - if (!priv) { - ret = -EINVAL; - goto fail; - } + priv = to_msm_priv(domain); fl_table = priv->pgtable; @@ -484,10 +486,7 @@ static size_t msm_iommu_unmap(struct iommu_domain *domain, unsigned long va, spin_lock_irqsave(&msm_iommu_lock, flags); - priv = domain->priv; - - if (!priv) - goto fail; + priv = to_msm_priv(domain); fl_table = priv->pgtable; @@ -566,7 +565,7 @@ static phys_addr_t msm_iommu_iova_to_phys(struct iommu_domain *domain, spin_lock_irqsave(&msm_iommu_lock, flags); - priv = domain->priv; + priv = to_msm_priv(domain); if (list_empty(&priv->list_attached)) goto fail; @@ -674,8 +673,8 @@ fail: static const struct iommu_ops msm_iommu_ops = { .capable = msm_iommu_capable, - .domain_init = msm_iommu_domain_init, - .domain_destroy = msm_iommu_domain_destroy, + .domain_alloc = msm_iommu_domain_alloc, + .domain_free = msm_iommu_domain_free, .attach_dev = msm_iommu_attach_dev, .detach_dev = msm_iommu_detach_dev, .map = msm_iommu_map, -- cgit v1.2.3 From dc8fb227ad9029f071c7de0a97b12a810a376364 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Mar 2015 13:43:15 +0100 Subject: iommu/shmobile: Make use of domain_alloc and domain_free Implement domain_alloc and domain_free iommu-ops as a replacement for domain_init/domain_destroy. Signed-off-by: Joerg Roedel --- drivers/iommu/shmobile-iommu.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/drivers/iommu/shmobile-iommu.c b/drivers/iommu/shmobile-iommu.c index f1b00774e4de..a0287519a1d4 100644 --- a/drivers/iommu/shmobile-iommu.c +++ b/drivers/iommu/shmobile-iommu.c @@ -42,11 +42,17 @@ struct shmobile_iommu_domain { spinlock_t map_lock; spinlock_t attached_list_lock; struct list_head attached_list; + struct iommu_domain domain; }; static struct shmobile_iommu_archdata *ipmmu_archdata; static struct kmem_cache *l1cache, *l2cache; +static struct shmobile_iommu_domain *to_sh_domain(struct iommu_domain *dom) +{ + return container_of(dom, struct shmobile_iommu_domain, domain); +} + static int pgtable_alloc(struct shmobile_iommu_domain_pgtable *pgtable, struct kmem_cache *cache, size_t size) { @@ -82,31 +88,33 @@ static void pgtable_write(struct shmobile_iommu_domain_pgtable *pgtable, sizeof(val) * count, DMA_TO_DEVICE); } -static int shmobile_iommu_domain_init(struct iommu_domain *domain) +static struct iommu_domain *shmobile_iommu_domain_alloc(unsigned type) { struct shmobile_iommu_domain *sh_domain; int i, ret; - sh_domain = kmalloc(sizeof(*sh_domain), GFP_KERNEL); + if (type != IOMMU_DOMAIN_UNMANAGED) + return NULL; + + sh_domain = kzalloc(sizeof(*sh_domain), GFP_KERNEL); if (!sh_domain) - return -ENOMEM; + return NULL; ret = pgtable_alloc(&sh_domain->l1, l1cache, L1_SIZE); if (ret < 0) { kfree(sh_domain); - return ret; + return NULL; } for (i = 0; i < L1_LEN; i++) sh_domain->l2[i].pgtable = NULL; spin_lock_init(&sh_domain->map_lock); spin_lock_init(&sh_domain->attached_list_lock); INIT_LIST_HEAD(&sh_domain->attached_list); - domain->priv = sh_domain; - return 0; + return &sh_domain->domain; } -static void shmobile_iommu_domain_destroy(struct iommu_domain *domain) +static void shmobile_iommu_domain_free(struct iommu_domain *domain) { - struct shmobile_iommu_domain *sh_domain = domain->priv; + struct shmobile_iommu_domain *sh_domain = to_sh_domain(domain); int i; for (i = 0; i < L1_LEN; i++) { @@ -115,14 +123,13 @@ static void shmobile_iommu_domain_destroy(struct iommu_domain *domain) } pgtable_free(&sh_domain->l1, l1cache, L1_SIZE); kfree(sh_domain); - domain->priv = NULL; } static int shmobile_iommu_attach_device(struct iommu_domain *domain, struct device *dev) { struct shmobile_iommu_archdata *archdata = dev->archdata.iommu; - struct shmobile_iommu_domain *sh_domain = domain->priv; + struct shmobile_iommu_domain *sh_domain = to_sh_domain(domain); int ret = -EBUSY; if (!archdata) @@ -151,7 +158,7 @@ static void shmobile_iommu_detach_device(struct iommu_domain *domain, struct device *dev) { struct shmobile_iommu_archdata *archdata = dev->archdata.iommu; - struct shmobile_iommu_domain *sh_domain = domain->priv; + struct shmobile_iommu_domain *sh_domain = to_sh_domain(domain); if (!archdata) return; @@ -214,7 +221,7 @@ static int shmobile_iommu_map(struct iommu_domain *domain, unsigned long iova, phys_addr_t paddr, size_t size, int prot) { struct shmobile_iommu_domain_pgtable l2 = { .pgtable = NULL }; - struct shmobile_iommu_domain *sh_domain = domain->priv; + struct shmobile_iommu_domain *sh_domain = to_sh_domain(domain); unsigned int l1index, l2index; int ret; @@ -258,7 +265,7 @@ static size_t shmobile_iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) { struct shmobile_iommu_domain_pgtable l2 = { .pgtable = NULL }; - struct shmobile_iommu_domain *sh_domain = domain->priv; + struct shmobile_iommu_domain *sh_domain = to_sh_domain(domain); unsigned int l1index, l2index; uint32_t l2entry = 0; size_t ret = 0; @@ -298,7 +305,7 @@ done: static phys_addr_t shmobile_iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova) { - struct shmobile_iommu_domain *sh_domain = domain->priv; + struct shmobile_iommu_domain *sh_domain = to_sh_domain(domain); uint32_t l1entry = 0, l2entry = 0; unsigned int l1index, l2index; @@ -355,8 +362,8 @@ static int shmobile_iommu_add_device(struct device *dev) } static const struct iommu_ops shmobile_iommu_ops = { - .domain_init = shmobile_iommu_domain_init, - .domain_destroy = shmobile_iommu_domain_destroy, + .domain_alloc = shmobile_iommu_domain_alloc, + .domain_free = shmobile_iommu_domain_free, .attach_dev = shmobile_iommu_attach_device, .detach_dev = shmobile_iommu_detach_device, .map = shmobile_iommu_map, -- cgit v1.2.3 From 5914c5fdde926604f65b249b662b26e3bc2c5923 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Mar 2015 13:43:16 +0100 Subject: iommu/ipmmu-vmsa: Make use of domain_alloc and domain_free Implement domain_alloc and domain_free iommu-ops as a replacement for domain_init/domain_destroy. Signed-off-by: Joerg Roedel --- drivers/iommu/ipmmu-vmsa.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c index 10186cac7716..274c08d76c08 100644 --- a/drivers/iommu/ipmmu-vmsa.c +++ b/drivers/iommu/ipmmu-vmsa.c @@ -38,7 +38,7 @@ struct ipmmu_vmsa_device { struct ipmmu_vmsa_domain { struct ipmmu_vmsa_device *mmu; - struct iommu_domain *io_domain; + struct iommu_domain io_domain; struct io_pgtable_cfg cfg; struct io_pgtable_ops *iop; @@ -56,6 +56,11 @@ struct ipmmu_vmsa_archdata { static DEFINE_SPINLOCK(ipmmu_devices_lock); static LIST_HEAD(ipmmu_devices); +static struct ipmmu_vmsa_domain *to_vmsa_domain(struct iommu_domain *dom) +{ + return container_of(dom, struct ipmmu_vmsa_domain, io_domain); +} + #define TLB_LOOP_TIMEOUT 100 /* 100us */ /* ----------------------------------------------------------------------------- @@ -428,7 +433,7 @@ static irqreturn_t ipmmu_domain_irq(struct ipmmu_vmsa_domain *domain) * TODO: We need to look up the faulty device based on the I/O VA. Use * the IOMMU device for now. */ - if (!report_iommu_fault(domain->io_domain, mmu->dev, iova, 0)) + if (!report_iommu_fault(&domain->io_domain, mmu->dev, iova, 0)) return IRQ_HANDLED; dev_err_ratelimited(mmu->dev, @@ -448,7 +453,7 @@ static irqreturn_t ipmmu_irq(int irq, void *dev) return IRQ_NONE; io_domain = mmu->mapping->domain; - domain = io_domain->priv; + domain = to_vmsa_domain(io_domain); return ipmmu_domain_irq(domain); } @@ -457,25 +462,25 @@ static irqreturn_t ipmmu_irq(int irq, void *dev) * IOMMU Operations */ -static int ipmmu_domain_init(struct iommu_domain *io_domain) +static struct iommu_domain *ipmmu_domain_alloc(unsigned type) { struct ipmmu_vmsa_domain *domain; + if (type != IOMMU_DOMAIN_UNMANAGED) + return NULL; + domain = kzalloc(sizeof(*domain), GFP_KERNEL); if (!domain) - return -ENOMEM; + return NULL; spin_lock_init(&domain->lock); - io_domain->priv = domain; - domain->io_domain = io_domain; - - return 0; + return &domain->io_domain; } -static void ipmmu_domain_destroy(struct iommu_domain *io_domain) +static void ipmmu_domain_free(struct iommu_domain *io_domain) { - struct ipmmu_vmsa_domain *domain = io_domain->priv; + struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain); /* * Free the domain resources. We assume that all devices have already @@ -491,7 +496,7 @@ static int ipmmu_attach_device(struct iommu_domain *io_domain, { struct ipmmu_vmsa_archdata *archdata = dev->archdata.iommu; struct ipmmu_vmsa_device *mmu = archdata->mmu; - struct ipmmu_vmsa_domain *domain = io_domain->priv; + struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain); unsigned long flags; unsigned int i; int ret = 0; @@ -532,7 +537,7 @@ static void ipmmu_detach_device(struct iommu_domain *io_domain, struct device *dev) { struct ipmmu_vmsa_archdata *archdata = dev->archdata.iommu; - struct ipmmu_vmsa_domain *domain = io_domain->priv; + struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain); unsigned int i; for (i = 0; i < archdata->num_utlbs; ++i) @@ -546,7 +551,7 @@ static void ipmmu_detach_device(struct iommu_domain *io_domain, static int ipmmu_map(struct iommu_domain *io_domain, unsigned long iova, phys_addr_t paddr, size_t size, int prot) { - struct ipmmu_vmsa_domain *domain = io_domain->priv; + struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain); if (!domain) return -ENODEV; @@ -557,7 +562,7 @@ static int ipmmu_map(struct iommu_domain *io_domain, unsigned long iova, static size_t ipmmu_unmap(struct iommu_domain *io_domain, unsigned long iova, size_t size) { - struct ipmmu_vmsa_domain *domain = io_domain->priv; + struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain); return domain->iop->unmap(domain->iop, iova, size); } @@ -565,7 +570,7 @@ static size_t ipmmu_unmap(struct iommu_domain *io_domain, unsigned long iova, static phys_addr_t ipmmu_iova_to_phys(struct iommu_domain *io_domain, dma_addr_t iova) { - struct ipmmu_vmsa_domain *domain = io_domain->priv; + struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain); /* TODO: Is locking needed ? */ @@ -737,8 +742,8 @@ static void ipmmu_remove_device(struct device *dev) } static const struct iommu_ops ipmmu_ops = { - .domain_init = ipmmu_domain_init, - .domain_destroy = ipmmu_domain_destroy, + .domain_alloc = ipmmu_domain_alloc, + .domain_free = ipmmu_domain_free, .attach_dev = ipmmu_attach_device, .detach_dev = ipmmu_detach_device, .map = ipmmu_map, -- cgit v1.2.3 From bcd516a32416aadd4f1ac40540407aa3b4ffd222 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Mar 2015 13:43:17 +0100 Subject: iommu/rockchip: Make use of domain_alloc and domain_free Implement domain_alloc and domain_free iommu-ops as a replacement for domain_init/domain_destroy. Tested-by: Heiko Stuebner Signed-off-by: Joerg Roedel --- drivers/iommu/rockchip-iommu.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c index 9f74fddcd304..4015560bf486 100644 --- a/drivers/iommu/rockchip-iommu.c +++ b/drivers/iommu/rockchip-iommu.c @@ -80,6 +80,8 @@ struct rk_iommu_domain { u32 *dt; /* page directory table */ spinlock_t iommus_lock; /* lock for iommus list */ spinlock_t dt_lock; /* lock for modifying page directory table */ + + struct iommu_domain domain; }; struct rk_iommu { @@ -100,6 +102,11 @@ static inline void rk_table_flush(u32 *va, unsigned int count) outer_flush_range(pa_start, pa_end); } +static struct rk_iommu_domain *to_rk_domain(struct iommu_domain *dom) +{ + return container_of(dom, struct rk_iommu_domain, domain); +} + /** * Inspired by _wait_for in intel_drv.h * This is NOT safe for use in interrupt context. @@ -503,7 +510,7 @@ static irqreturn_t rk_iommu_irq(int irq, void *dev_id) static phys_addr_t rk_iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova) { - struct rk_iommu_domain *rk_domain = domain->priv; + struct rk_iommu_domain *rk_domain = to_rk_domain(domain); unsigned long flags; phys_addr_t pt_phys, phys = 0; u32 dte, pte; @@ -639,7 +646,7 @@ unwind: static int rk_iommu_map(struct iommu_domain *domain, unsigned long _iova, phys_addr_t paddr, size_t size, int prot) { - struct rk_iommu_domain *rk_domain = domain->priv; + struct rk_iommu_domain *rk_domain = to_rk_domain(domain); unsigned long flags; dma_addr_t iova = (dma_addr_t)_iova; u32 *page_table, *pte_addr; @@ -670,7 +677,7 @@ static int rk_iommu_map(struct iommu_domain *domain, unsigned long _iova, static size_t rk_iommu_unmap(struct iommu_domain *domain, unsigned long _iova, size_t size) { - struct rk_iommu_domain *rk_domain = domain->priv; + struct rk_iommu_domain *rk_domain = to_rk_domain(domain); unsigned long flags; dma_addr_t iova = (dma_addr_t)_iova; phys_addr_t pt_phys; @@ -726,7 +733,7 @@ static int rk_iommu_attach_device(struct iommu_domain *domain, struct device *dev) { struct rk_iommu *iommu; - struct rk_iommu_domain *rk_domain = domain->priv; + struct rk_iommu_domain *rk_domain = to_rk_domain(domain); unsigned long flags; int ret; phys_addr_t dte_addr; @@ -778,7 +785,7 @@ static void rk_iommu_detach_device(struct iommu_domain *domain, struct device *dev) { struct rk_iommu *iommu; - struct rk_iommu_domain *rk_domain = domain->priv; + struct rk_iommu_domain *rk_domain = to_rk_domain(domain); unsigned long flags; /* Allow 'virtual devices' (eg drm) to detach from domain */ @@ -804,13 +811,16 @@ static void rk_iommu_detach_device(struct iommu_domain *domain, dev_info(dev, "Detached from iommu domain\n"); } -static int rk_iommu_domain_init(struct iommu_domain *domain) +static struct iommu_domain *rk_iommu_domain_alloc(unsigned type) { struct rk_iommu_domain *rk_domain; + if (type != IOMMU_DOMAIN_UNMANAGED) + return NULL; + rk_domain = kzalloc(sizeof(*rk_domain), GFP_KERNEL); if (!rk_domain) - return -ENOMEM; + return NULL; /* * rk32xx iommus use a 2 level pagetable. @@ -827,17 +837,16 @@ static int rk_iommu_domain_init(struct iommu_domain *domain) spin_lock_init(&rk_domain->dt_lock); INIT_LIST_HEAD(&rk_domain->iommus); - domain->priv = rk_domain; + return &rk_domain->domain; - return 0; err_dt: kfree(rk_domain); - return -ENOMEM; + return NULL; } -static void rk_iommu_domain_destroy(struct iommu_domain *domain) +static void rk_iommu_domain_free(struct iommu_domain *domain) { - struct rk_iommu_domain *rk_domain = domain->priv; + struct rk_iommu_domain *rk_domain = to_rk_domain(domain); int i; WARN_ON(!list_empty(&rk_domain->iommus)); @@ -852,8 +861,7 @@ static void rk_iommu_domain_destroy(struct iommu_domain *domain) } free_page((unsigned long)rk_domain->dt); - kfree(domain->priv); - domain->priv = NULL; + kfree(rk_domain); } static bool rk_iommu_is_dev_iommu_master(struct device *dev) @@ -952,8 +960,8 @@ static void rk_iommu_remove_device(struct device *dev) } static const struct iommu_ops rk_iommu_ops = { - .domain_init = rk_iommu_domain_init, - .domain_destroy = rk_iommu_domain_destroy, + .domain_alloc = rk_iommu_domain_alloc, + .domain_free = rk_iommu_domain_free, .attach_dev = rk_iommu_attach_device, .detach_dev = rk_iommu_detach_device, .map = rk_iommu_map, -- cgit v1.2.3 From 8d4bfe40bd001c49caa9079541ff25522e7ed55d Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Mar 2015 13:43:18 +0100 Subject: iommu/fsl: Make use of domain_alloc and domain_free Implement domain_alloc and domain_free iommu-ops as a replacement for domain_init/domain_destroy. Signed-off-by: Joerg Roedel --- drivers/iommu/fsl_pamu_domain.c | 60 ++++++++++++++++++++++------------------- drivers/iommu/fsl_pamu_domain.h | 2 +- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/drivers/iommu/fsl_pamu_domain.c b/drivers/iommu/fsl_pamu_domain.c index ceebd287b660..1d452930c890 100644 --- a/drivers/iommu/fsl_pamu_domain.c +++ b/drivers/iommu/fsl_pamu_domain.c @@ -33,6 +33,11 @@ static struct kmem_cache *fsl_pamu_domain_cache; static struct kmem_cache *iommu_devinfo_cache; static DEFINE_SPINLOCK(device_domain_lock); +static struct fsl_dma_domain *to_fsl_dma_domain(struct iommu_domain *dom) +{ + return container_of(dom, struct fsl_dma_domain, iommu_domain); +} + static int __init iommu_init_mempool(void) { fsl_pamu_domain_cache = kmem_cache_create("fsl_pamu_domain", @@ -65,7 +70,7 @@ static phys_addr_t get_phys_addr(struct fsl_dma_domain *dma_domain, dma_addr_t i struct dma_window *win_ptr = &dma_domain->win_arr[0]; struct iommu_domain_geometry *geom; - geom = &dma_domain->iommu_domain->geometry; + geom = &dma_domain->iommu_domain.geometry; if (!win_cnt || !dma_domain->geom_size) { pr_debug("Number of windows/geometry not configured for the domain\n"); @@ -123,7 +128,7 @@ static int map_win(int liodn, struct fsl_dma_domain *dma_domain) { int ret; struct dma_window *wnd = &dma_domain->win_arr[0]; - phys_addr_t wnd_addr = dma_domain->iommu_domain->geometry.aperture_start; + phys_addr_t wnd_addr = dma_domain->iommu_domain.geometry.aperture_start; unsigned long flags; spin_lock_irqsave(&iommu_lock, flags); @@ -172,7 +177,7 @@ static int update_liodn(int liodn, struct fsl_dma_domain *dma_domain, u32 wnd_nr } else { phys_addr_t wnd_addr; - wnd_addr = dma_domain->iommu_domain->geometry.aperture_start; + wnd_addr = dma_domain->iommu_domain.geometry.aperture_start; ret = pamu_config_ppaace(liodn, wnd_addr, wnd->size, @@ -384,7 +389,7 @@ static void attach_device(struct fsl_dma_domain *dma_domain, int liodn, struct d static phys_addr_t fsl_pamu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova) { - struct fsl_dma_domain *dma_domain = domain->priv; + struct fsl_dma_domain *dma_domain = to_fsl_dma_domain(domain); if (iova < domain->geometry.aperture_start || iova > domain->geometry.aperture_end) @@ -398,11 +403,9 @@ static bool fsl_pamu_capable(enum iommu_cap cap) return cap == IOMMU_CAP_CACHE_COHERENCY; } -static void fsl_pamu_domain_destroy(struct iommu_domain *domain) +static void fsl_pamu_domain_free(struct iommu_domain *domain) { - struct fsl_dma_domain *dma_domain = domain->priv; - - domain->priv = NULL; + struct fsl_dma_domain *dma_domain = to_fsl_dma_domain(domain); /* remove all the devices from the device list */ detach_device(NULL, dma_domain); @@ -413,23 +416,24 @@ static void fsl_pamu_domain_destroy(struct iommu_domain *domain) kmem_cache_free(fsl_pamu_domain_cache, dma_domain); } -static int fsl_pamu_domain_init(struct iommu_domain *domain) +static struct iommu_domain *fsl_pamu_domain_alloc(unsigned type) { struct fsl_dma_domain *dma_domain; + if (type != IOMMU_DOMAIN_UNMANAGED) + return NULL; + dma_domain = iommu_alloc_dma_domain(); if (!dma_domain) { pr_debug("dma_domain allocation failed\n"); - return -ENOMEM; + return NULL; } - domain->priv = dma_domain; - dma_domain->iommu_domain = domain; /* defaul geometry 64 GB i.e. maximum system address */ - domain->geometry.aperture_start = 0; - domain->geometry.aperture_end = (1ULL << 36) - 1; - domain->geometry.force_aperture = true; + dma_domain->iommu_domain. geometry.aperture_start = 0; + dma_domain->iommu_domain.geometry.aperture_end = (1ULL << 36) - 1; + dma_domain->iommu_domain.geometry.force_aperture = true; - return 0; + return &dma_domain->iommu_domain; } /* Configure geometry settings for all LIODNs associated with domain */ @@ -499,7 +503,7 @@ static int disable_domain_win(struct fsl_dma_domain *dma_domain, u32 wnd_nr) static void fsl_pamu_window_disable(struct iommu_domain *domain, u32 wnd_nr) { - struct fsl_dma_domain *dma_domain = domain->priv; + struct fsl_dma_domain *dma_domain = to_fsl_dma_domain(domain); unsigned long flags; int ret; @@ -530,7 +534,7 @@ static void fsl_pamu_window_disable(struct iommu_domain *domain, u32 wnd_nr) static int fsl_pamu_window_enable(struct iommu_domain *domain, u32 wnd_nr, phys_addr_t paddr, u64 size, int prot) { - struct fsl_dma_domain *dma_domain = domain->priv; + struct fsl_dma_domain *dma_domain = to_fsl_dma_domain(domain); struct dma_window *wnd; int pamu_prot = 0; int ret; @@ -607,7 +611,7 @@ static int handle_attach_device(struct fsl_dma_domain *dma_domain, int num) { unsigned long flags; - struct iommu_domain *domain = dma_domain->iommu_domain; + struct iommu_domain *domain = &dma_domain->iommu_domain; int ret = 0; int i; @@ -653,7 +657,7 @@ static int handle_attach_device(struct fsl_dma_domain *dma_domain, static int fsl_pamu_attach_device(struct iommu_domain *domain, struct device *dev) { - struct fsl_dma_domain *dma_domain = domain->priv; + struct fsl_dma_domain *dma_domain = to_fsl_dma_domain(domain); const u32 *liodn; u32 liodn_cnt; int len, ret = 0; @@ -691,7 +695,7 @@ static int fsl_pamu_attach_device(struct iommu_domain *domain, static void fsl_pamu_detach_device(struct iommu_domain *domain, struct device *dev) { - struct fsl_dma_domain *dma_domain = domain->priv; + struct fsl_dma_domain *dma_domain = to_fsl_dma_domain(domain); const u32 *prop; int len; struct pci_dev *pdev = NULL; @@ -723,7 +727,7 @@ static void fsl_pamu_detach_device(struct iommu_domain *domain, static int configure_domain_geometry(struct iommu_domain *domain, void *data) { struct iommu_domain_geometry *geom_attr = data; - struct fsl_dma_domain *dma_domain = domain->priv; + struct fsl_dma_domain *dma_domain = to_fsl_dma_domain(domain); dma_addr_t geom_size; unsigned long flags; @@ -813,7 +817,7 @@ static int configure_domain_dma_state(struct fsl_dma_domain *dma_domain, bool en static int fsl_pamu_set_domain_attr(struct iommu_domain *domain, enum iommu_attr attr_type, void *data) { - struct fsl_dma_domain *dma_domain = domain->priv; + struct fsl_dma_domain *dma_domain = to_fsl_dma_domain(domain); int ret = 0; switch (attr_type) { @@ -838,7 +842,7 @@ static int fsl_pamu_set_domain_attr(struct iommu_domain *domain, static int fsl_pamu_get_domain_attr(struct iommu_domain *domain, enum iommu_attr attr_type, void *data) { - struct fsl_dma_domain *dma_domain = domain->priv; + struct fsl_dma_domain *dma_domain = to_fsl_dma_domain(domain); int ret = 0; switch (attr_type) { @@ -999,7 +1003,7 @@ static void fsl_pamu_remove_device(struct device *dev) static int fsl_pamu_set_windows(struct iommu_domain *domain, u32 w_count) { - struct fsl_dma_domain *dma_domain = domain->priv; + struct fsl_dma_domain *dma_domain = to_fsl_dma_domain(domain); unsigned long flags; int ret; @@ -1048,15 +1052,15 @@ static int fsl_pamu_set_windows(struct iommu_domain *domain, u32 w_count) static u32 fsl_pamu_get_windows(struct iommu_domain *domain) { - struct fsl_dma_domain *dma_domain = domain->priv; + struct fsl_dma_domain *dma_domain = to_fsl_dma_domain(domain); return dma_domain->win_cnt; } static const struct iommu_ops fsl_pamu_ops = { .capable = fsl_pamu_capable, - .domain_init = fsl_pamu_domain_init, - .domain_destroy = fsl_pamu_domain_destroy, + .domain_alloc = fsl_pamu_domain_alloc, + .domain_free = fsl_pamu_domain_free, .attach_dev = fsl_pamu_attach_device, .detach_dev = fsl_pamu_detach_device, .domain_window_enable = fsl_pamu_window_enable, diff --git a/drivers/iommu/fsl_pamu_domain.h b/drivers/iommu/fsl_pamu_domain.h index c90293f99709..f2b0f741d3de 100644 --- a/drivers/iommu/fsl_pamu_domain.h +++ b/drivers/iommu/fsl_pamu_domain.h @@ -71,7 +71,7 @@ struct fsl_dma_domain { u32 stash_id; struct pamu_stash_attribute dma_stash; u32 snoop_id; - struct iommu_domain *iommu_domain; + struct iommu_domain iommu_domain; spinlock_t domain_lock; }; -- cgit v1.2.3 From 89be34a1ced886880a3219f9d2ba2192dc738ef2 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Mar 2015 13:43:19 +0100 Subject: iommu: Remove domain_init and domain_free iommu_ops All drivers have been converted to the new domain_alloc and domain_free iommu-ops. So remove the old ones and get rid of iommu_domain->priv too, as this is no longer needed when the struct iommu_domain is embedded in the private structures of the iommu drivers. Tested-by: Thierry Reding Tested-by: Heiko Stuebner Reviewed-by: Alex Williamson Acked-by: Will Deacon Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 30 ++---------------------------- include/linux/iommu.h | 3 --- 2 files changed, 2 insertions(+), 31 deletions(-) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 656b9499e748..d4f527e56679 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -900,51 +900,25 @@ EXPORT_SYMBOL_GPL(iommu_set_fault_handler); struct iommu_domain *iommu_domain_alloc(struct bus_type *bus) { - const struct iommu_ops *ops; struct iommu_domain *domain; if (bus == NULL || bus->iommu_ops == NULL) return NULL; - ops = bus->iommu_ops; - - if (ops->domain_alloc) - domain = ops->domain_alloc(IOMMU_DOMAIN_UNMANAGED); - else - domain = kzalloc(sizeof(*domain), GFP_KERNEL); - + domain = bus->iommu_ops->domain_alloc(IOMMU_DOMAIN_UNMANAGED); if (!domain) return NULL; domain->ops = bus->iommu_ops; domain->type = IOMMU_DOMAIN_UNMANAGED; - if (ops->domain_init && domain->ops->domain_init(domain)) - goto out_free; - return domain; - -out_free: - if (ops->domain_free) - ops->domain_free(domain); - else - kfree(domain); - - return NULL; } EXPORT_SYMBOL_GPL(iommu_domain_alloc); void iommu_domain_free(struct iommu_domain *domain) { - const struct iommu_ops *ops = domain->ops; - - if (likely(ops->domain_destroy != NULL)) - ops->domain_destroy(domain); - - if (ops->domain_free) - ops->domain_free(domain); - else - kfree(domain); + domain->ops->domain_free(domain); } EXPORT_SYMBOL_GPL(iommu_domain_free); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 72d03fe78cf6..0546b8710ce3 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -78,7 +78,6 @@ struct iommu_domain_geometry { struct iommu_domain { unsigned type; const struct iommu_ops *ops; - void *priv; iommu_fault_handler_t handler; void *handler_token; struct iommu_domain_geometry geometry; @@ -138,8 +137,6 @@ enum iommu_attr { */ struct iommu_ops { bool (*capable)(enum iommu_cap); - int (*domain_init)(struct iommu_domain *domain); - void (*domain_destroy)(struct iommu_domain *domain); /* Domain allocation and freeing by the iommu driver */ struct iommu_domain *(*domain_alloc)(unsigned iommu_domain_type); -- cgit v1.2.3 From 471d9144b4ec2a2cbf497af7e186c1fe89b5150e Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 27 Mar 2015 11:07:25 +0100 Subject: iommu/tegra: Setup aperture Each address space in the Tegra SMMU provides 4 GiB worth of addresses. Cc: Hiroshi Doyu Signed-off-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-smmu.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 6e134c7c227f..d8418b54619e 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -266,6 +266,11 @@ static int tegra_smmu_domain_init(struct iommu_domain *domain) domain->priv = as; + /* setup aperture */ + domain->geometry.aperture_start = 0; + domain->geometry.aperture_end = 0xffffffff; + domain->geometry.force_aperture = true; + return 0; } -- cgit v1.2.3 From 836a8ac9fe60fb112e830464868791bf7470e7b6 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 27 Mar 2015 11:07:26 +0100 Subject: iommu/tegra: gart: Set aperture at domain initialization time The aperture of the domain should always be available, otherwise drivers need to attach first before they can use the aperture geometry. Cc: Hiroshi Doyu Signed-off-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-gart.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index c48da057dbb1..5f3b68c26b83 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c @@ -156,20 +156,10 @@ static inline bool gart_iova_range_valid(struct gart_device *gart, static int gart_iommu_attach_dev(struct iommu_domain *domain, struct device *dev) { - struct gart_device *gart; + struct gart_device *gart = domain->priv; struct gart_client *client, *c; int err = 0; - gart = gart_handle; - if (!gart) - return -EINVAL; - domain->priv = gart; - - domain->geometry.aperture_start = gart->iovmm_base; - domain->geometry.aperture_end = gart->iovmm_base + - gart->page_count * GART_PAGE_SIZE - 1; - domain->geometry.force_aperture = true; - client = devm_kzalloc(gart->dev, sizeof(*c), GFP_KERNEL); if (!client) return -ENOMEM; @@ -218,6 +208,19 @@ out: static int gart_iommu_domain_init(struct iommu_domain *domain) { + struct gart_device *gart; + + gart = gart_handle; + if (!gart) + return -EINVAL; + + domain->priv = gart; + + domain->geometry.aperture_start = gart->iovmm_base; + domain->geometry.aperture_end = gart->iovmm_base + + gart->page_count * GART_PAGE_SIZE - 1; + domain->geometry.force_aperture = true; + return 0; } -- cgit v1.2.3 From 804cb54cbb3ddac58218e830d64816617bdb6da8 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 27 Mar 2015 11:07:27 +0100 Subject: iommu/tegra: smmu: Compute PFN mask at runtime The SMMU on Tegra30 and Tegra114 supports addressing up to 4 GiB of physical memory. On Tegra124 the addressable physical memory was extended to 16 GiB. The page frame number stored in PTEs therefore requires 20 or 22 bits, depending on SoC generation. In order to cope with this, compute the proper value at runtime. Reported-by: Joseph Lo Cc: Hiroshi Doyu Signed-off-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-smmu.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index d8418b54619e..76887a73b47a 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -6,6 +6,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include @@ -24,6 +25,8 @@ struct tegra_smmu { struct tegra_mc *mc; const struct tegra_smmu_soc *soc; + unsigned long pfn_mask; + unsigned long *asids; struct mutex lock; @@ -105,8 +108,6 @@ static inline u32 smmu_readl(struct tegra_smmu *smmu, unsigned long offset) #define SMMU_PDE_SHIFT 22 #define SMMU_PTE_SHIFT 12 -#define SMMU_PFN_MASK 0x000fffff - #define SMMU_PD_READABLE (1 << 31) #define SMMU_PD_WRITABLE (1 << 30) #define SMMU_PD_NONSECURE (1 << 29) @@ -486,7 +487,7 @@ static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova, smmu_flush_tlb_section(smmu, as->id, iova); smmu_flush(smmu); } else { - page = pfn_to_page(pd[pde] & SMMU_PFN_MASK); + page = pfn_to_page(pd[pde] & smmu->pfn_mask); pt = page_address(page); } @@ -508,7 +509,7 @@ static void as_put_pte(struct tegra_smmu_as *as, dma_addr_t iova) u32 *pd = page_address(as->pd), *pt; struct page *page; - page = pfn_to_page(pd[pde] & SMMU_PFN_MASK); + page = pfn_to_page(pd[pde] & as->smmu->pfn_mask); pt = page_address(page); /* @@ -583,7 +584,7 @@ static phys_addr_t tegra_smmu_iova_to_phys(struct iommu_domain *domain, u32 *pte; pte = as_get_pte(as, iova, &page); - pfn = *pte & SMMU_PFN_MASK; + pfn = *pte & as->smmu->pfn_mask; return PFN_PHYS(pfn); } @@ -707,6 +708,10 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev, smmu->dev = dev; smmu->mc = mc; + smmu->pfn_mask = BIT_MASK(mc->soc->num_address_bits - PAGE_SHIFT) - 1; + dev_dbg(dev, "address bits: %u, PFN mask: %#lx\n", + mc->soc->num_address_bits, smmu->pfn_mask); + value = SMMU_PTC_CONFIG_ENABLE | SMMU_PTC_CONFIG_INDEX_MAP(0x3f); if (soc->supports_request_limit) -- cgit v1.2.3 From 6c5cc8015793cdfd56d9997bcfddf2740ff716c5 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 1 Apr 2015 14:58:44 +0200 Subject: iommu/amd: Use BUS_NOTIFY_REMOVED_DEVICE Use the new device-notifier event instead of the old BUS_NOTIFY_DEL_DEVICE to make sure the device driver had a chance to uninit the device before all its mappings are teared down. Tested-by: Suravee Suthikulpanit Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 48882c126245..8a1dea4d0d26 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -2467,7 +2467,7 @@ static int device_change_notifier(struct notifier_block *nb, dev->archdata.dma_ops = &amd_iommu_dma_ops; break; - case BUS_NOTIFY_DEL_DEVICE: + case BUS_NOTIFY_REMOVED_DEVICE: iommu_uninit_device(dev); -- cgit v1.2.3 From 7139a2e929b9e3ca8d5c217cb8aec560be1faf00 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 1 Apr 2015 14:58:45 +0200 Subject: iommu/amd: Ignore BUS_NOTIFY_UNBOUND_DRIVER event Detaching a device from its domain at this event is problematic for several reasons: * The device might me in an alias group and detaching it will also detach all other devices in the group. This removes valid DMA mappings from the other devices causing io-page-faults and lets these devices fail. * Devices might have unity mappings specified by the IVRS table. These mappings are required for the device even when no device driver is attached. Detaching the device from its domain in driver unbind will also remove these unity mappings. This patch removes the handling of the BUS_NOTIFY_UNBOUND_DRIVER event to prevent these issues and align it better with the behavior of the VT-d driver. Tested-by: Suravee Suthikulpanit Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 8a1dea4d0d26..994cc7dbb833 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -2422,16 +2422,6 @@ static int device_change_notifier(struct notifier_block *nb, dev_data = get_dev_data(dev); switch (action) { - case BUS_NOTIFY_UNBOUND_DRIVER: - - domain = domain_for_device(dev); - - if (!domain) - goto out; - if (dev_data->passthrough) - break; - detach_device(dev); - break; case BUS_NOTIFY_ADD_DEVICE: iommu_init_device(dev); -- cgit v1.2.3 From 5fc872c7323534e8f7dc21bab635e7a9b9659e07 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 1 Apr 2015 14:58:46 +0200 Subject: iommu/amd: Don't allocate with __GFP_ZERO in alloc_coherent Don't explicitly add __GFP_ZERO to the allocator flags. Leave this up to the caller. Tested-by: Suravee Suthikulpanit Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 994cc7dbb833..c2e6f13d148f 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -2931,7 +2931,6 @@ static void *alloc_coherent(struct device *dev, size_t size, dma_mask = dev->coherent_dma_mask; flag &= ~(__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32); - flag |= __GFP_ZERO; virt_addr = (void *)__get_free_pages(flag, get_order(size)); if (!virt_addr) -- cgit v1.2.3 From 3b839a57998515bb44c091bbcb8ea0da9d2adef4 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 1 Apr 2015 14:58:47 +0200 Subject: iommu/amd: Add support for contiguous dma allocator Add code to allocate memory from the contiguous memory allocator to support coherent allocations larger than 8MB. Tested-by: Suravee Suthikulpanit Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index c2e6f13d148f..49ecf003f7ca 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -2913,37 +2914,42 @@ static void *alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_addr, gfp_t flag, struct dma_attrs *attrs) { - unsigned long flags; - void *virt_addr; - struct protection_domain *domain; - phys_addr_t paddr; u64 dma_mask = dev->coherent_dma_mask; + struct protection_domain *domain; + unsigned long flags; + struct page *page; INC_STATS_COUNTER(cnt_alloc_coherent); domain = get_domain(dev); if (PTR_ERR(domain) == -EINVAL) { - virt_addr = (void *)__get_free_pages(flag, get_order(size)); - *dma_addr = __pa(virt_addr); - return virt_addr; + page = alloc_pages(flag, get_order(size)); + *dma_addr = page_to_phys(page); + return page_address(page); } else if (IS_ERR(domain)) return NULL; + size = PAGE_ALIGN(size); dma_mask = dev->coherent_dma_mask; flag &= ~(__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32); - virt_addr = (void *)__get_free_pages(flag, get_order(size)); - if (!virt_addr) - return NULL; + page = alloc_pages(flag | __GFP_NOWARN, get_order(size)); + if (!page) { + if (!(flag & __GFP_WAIT)) + return NULL; - paddr = virt_to_phys(virt_addr); + page = dma_alloc_from_contiguous(dev, size >> PAGE_SHIFT, + get_order(size)); + if (!page) + return NULL; + } if (!dma_mask) dma_mask = *dev->dma_mask; spin_lock_irqsave(&domain->lock, flags); - *dma_addr = __map_single(dev, domain->priv, paddr, + *dma_addr = __map_single(dev, domain->priv, page_to_phys(page), size, DMA_BIDIRECTIONAL, true, dma_mask); if (*dma_addr == DMA_ERROR_CODE) { @@ -2955,11 +2961,12 @@ static void *alloc_coherent(struct device *dev, size_t size, spin_unlock_irqrestore(&domain->lock, flags); - return virt_addr; + return page_address(page); out_free: - free_pages((unsigned long)virt_addr, get_order(size)); + if (!dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT)) + __free_pages(page, get_order(size)); return NULL; } @@ -2971,11 +2978,15 @@ static void free_coherent(struct device *dev, size_t size, void *virt_addr, dma_addr_t dma_addr, struct dma_attrs *attrs) { - unsigned long flags; struct protection_domain *domain; + unsigned long flags; + struct page *page; INC_STATS_COUNTER(cnt_free_coherent); + page = virt_to_page(virt_addr); + size = PAGE_ALIGN(size); + domain = get_domain(dev); if (IS_ERR(domain)) goto free_mem; @@ -2989,7 +3000,8 @@ static void free_coherent(struct device *dev, size_t size, spin_unlock_irqrestore(&domain->lock, flags); free_mem: - free_pages((unsigned long)virt_addr, get_order(size)); + if (!dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT)) + __free_pages(page, get_order(size)); } /* -- cgit v1.2.3 From 3039ca1b1c37e61cc9239dbb3903db55141ecabd Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 1 Apr 2015 14:58:48 +0200 Subject: iommu/amd: Return the pte page-size in fetch_pte Extend the fetch_pte function to also return the page-size that is mapped by the returned pte. Tested-by: Suravee Suthikulpanit Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 52 ++++++++++++++++++++++++----------------- drivers/iommu/amd_iommu_types.h | 6 +++++ 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 49ecf003f7ca..24ef9e600289 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -1322,7 +1322,9 @@ static u64 *alloc_pte(struct protection_domain *domain, * This function checks if there is a PTE for a given dma address. If * there is one, it returns the pointer to it. */ -static u64 *fetch_pte(struct protection_domain *domain, unsigned long address) +static u64 *fetch_pte(struct protection_domain *domain, + unsigned long address, + unsigned long *page_size) { int level; u64 *pte; @@ -1330,8 +1332,9 @@ static u64 *fetch_pte(struct protection_domain *domain, unsigned long address) if (address > PM_LEVEL_SIZE(domain->mode)) return NULL; - level = domain->mode - 1; - pte = &domain->pt_root[PM_LEVEL_INDEX(level, address)]; + level = domain->mode - 1; + pte = &domain->pt_root[PM_LEVEL_INDEX(level, address)]; + *page_size = PTE_LEVEL_PAGE_SIZE(level); while (level > 0) { @@ -1340,19 +1343,9 @@ static u64 *fetch_pte(struct protection_domain *domain, unsigned long address) return NULL; /* Large PTE */ - if (PM_PTE_LEVEL(*pte) == 0x07) { - unsigned long pte_mask, __pte; - - /* - * If we have a series of large PTEs, make - * sure to return a pointer to the first one. - */ - pte_mask = PTE_PAGE_SIZE(*pte); - pte_mask = ~((PAGE_SIZE_PTE_COUNT(pte_mask) << 3) - 1); - __pte = ((unsigned long)pte) & pte_mask; - - return (u64 *)__pte; - } + if (PM_PTE_LEVEL(*pte) == 7 || + PM_PTE_LEVEL(*pte) == 0) + break; /* No level skipping support yet */ if (PM_PTE_LEVEL(*pte) != level) @@ -1361,8 +1354,21 @@ static u64 *fetch_pte(struct protection_domain *domain, unsigned long address) level -= 1; /* Walk to the next level */ - pte = IOMMU_PTE_PAGE(*pte); - pte = &pte[PM_LEVEL_INDEX(level, address)]; + pte = IOMMU_PTE_PAGE(*pte); + pte = &pte[PM_LEVEL_INDEX(level, address)]; + *page_size = PTE_LEVEL_PAGE_SIZE(level); + } + + if (PM_PTE_LEVEL(*pte) == 0x07) { + unsigned long pte_mask; + + /* + * If we have a series of large PTEs, make + * sure to return a pointer to the first one. + */ + *page_size = pte_mask = PTE_PAGE_SIZE(*pte); + pte_mask = ~((PAGE_SIZE_PTE_COUNT(pte_mask) << 3) - 1); + pte = (u64 *)(((unsigned long)pte) & pte_mask); } return pte; @@ -1423,6 +1429,7 @@ static unsigned long iommu_unmap_page(struct protection_domain *dom, unsigned long page_size) { unsigned long long unmap_size, unmapped; + unsigned long pte_pgsize; u64 *pte; BUG_ON(!is_power_of_2(page_size)); @@ -1431,7 +1438,7 @@ static unsigned long iommu_unmap_page(struct protection_domain *dom, while (unmapped < page_size) { - pte = fetch_pte(dom, bus_addr); + pte = fetch_pte(dom, bus_addr, &pte_pgsize); if (!pte) { /* @@ -1674,7 +1681,8 @@ static int alloc_new_range(struct dma_ops_domain *dma_dom, for (i = dma_dom->aperture[index]->offset; i < dma_dom->aperture_size; i += PAGE_SIZE) { - u64 *pte = fetch_pte(&dma_dom->domain, i); + unsigned long pte_pgsize; + u64 *pte = fetch_pte(&dma_dom->domain, i, &pte_pgsize); if (!pte || !IOMMU_PTE_PRESENT(*pte)) continue; @@ -3382,14 +3390,14 @@ static phys_addr_t amd_iommu_iova_to_phys(struct iommu_domain *dom, dma_addr_t iova) { struct protection_domain *domain = dom->priv; - unsigned long offset_mask; + unsigned long offset_mask, pte_pgsize; phys_addr_t paddr; u64 *pte, __pte; if (domain->mode == PAGE_MODE_NONE) return iova; - pte = fetch_pte(domain, iova); + pte = fetch_pte(domain, iova, &pte_pgsize); if (!pte || !IOMMU_PTE_PRESENT(*pte)) return 0; diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h index c4fffb710c58..60e87d2e140a 100644 --- a/drivers/iommu/amd_iommu_types.h +++ b/drivers/iommu/amd_iommu_types.h @@ -282,6 +282,12 @@ #define PTE_PAGE_SIZE(pte) \ (1ULL << (1 + ffz(((pte) | 0xfffULL)))) +/* + * Takes a page-table level and returns the default page-size for this level + */ +#define PTE_LEVEL_PAGE_SIZE(level) \ + (1ULL << (12 + (9 * (level)))) + #define IOMMU_PTE_P (1ULL << 0) #define IOMMU_PTE_TV (1ULL << 1) #define IOMMU_PTE_U (1ULL << 59) -- cgit v1.2.3 From 71b390e9bec5121d25c45326ff0b0b96a143f9a8 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 1 Apr 2015 14:58:49 +0200 Subject: iommu/amd: Optimize iommu_unmap_page for new fetch_pte interface Now that fetch_pte returns the page-size of the pte, this function can be optimized a lot. Tested-by: Suravee Suthikulpanit Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 24ef9e600289..c9ee444d8209 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -1428,8 +1428,8 @@ static unsigned long iommu_unmap_page(struct protection_domain *dom, unsigned long bus_addr, unsigned long page_size) { - unsigned long long unmap_size, unmapped; - unsigned long pte_pgsize; + unsigned long long unmapped; + unsigned long unmap_size; u64 *pte; BUG_ON(!is_power_of_2(page_size)); @@ -1438,28 +1438,12 @@ static unsigned long iommu_unmap_page(struct protection_domain *dom, while (unmapped < page_size) { - pte = fetch_pte(dom, bus_addr, &pte_pgsize); - - if (!pte) { - /* - * No PTE for this address - * move forward in 4kb steps - */ - unmap_size = PAGE_SIZE; - } else if (PM_PTE_LEVEL(*pte) == 0) { - /* 4kb PTE found for this address */ - unmap_size = PAGE_SIZE; - *pte = 0ULL; - } else { - int count, i; - - /* Large PTE found which maps this address */ - unmap_size = PTE_PAGE_SIZE(*pte); - - /* Only unmap from the first pte in the page */ - if ((unmap_size - 1) & bus_addr) - break; - count = PAGE_SIZE_PTE_COUNT(unmap_size); + pte = fetch_pte(dom, bus_addr, &unmap_size); + + if (pte) { + int i, count; + + count = PAGE_SIZE_PTE_COUNT(unmap_size); for (i = 0; i < count; i++) pte[i] = 0ULL; } -- cgit v1.2.3 From 5d7c94c3f4f20964b217d64ee44a9a08320c315a Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 1 Apr 2015 14:58:50 +0200 Subject: iommu/amd: Optimize alloc_new_range for new fetch_pte interface Now that fetch_pte returns the page-size of the pte, the call in this function can also be optimized a little bit. Tested-by: Suravee Suthikulpanit Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index c9ee444d8209..f97441b6aaad 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -1591,7 +1591,7 @@ static int alloc_new_range(struct dma_ops_domain *dma_dom, { int index = dma_dom->aperture_size >> APERTURE_RANGE_SHIFT; struct amd_iommu *iommu; - unsigned long i, old_size; + unsigned long i, old_size, pte_pgsize; #ifdef CONFIG_IOMMU_STRESS populate = false; @@ -1664,13 +1664,13 @@ static int alloc_new_range(struct dma_ops_domain *dma_dom, */ for (i = dma_dom->aperture[index]->offset; i < dma_dom->aperture_size; - i += PAGE_SIZE) { - unsigned long pte_pgsize; + i += pte_pgsize) { u64 *pte = fetch_pte(&dma_dom->domain, i, &pte_pgsize); if (!pte || !IOMMU_PTE_PRESENT(*pte)) continue; - dma_ops_reserve_addresses(dma_dom, i >> PAGE_SHIFT, 1); + dma_ops_reserve_addresses(dma_dom, i >> PAGE_SHIFT, + pte_pgsize >> 12); } update_domain(&dma_dom->domain); -- cgit v1.2.3 From b24b1b63a37d05d61601d643ef30f95dd2452048 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 1 Apr 2015 14:58:51 +0200 Subject: iommu/amd: Optimize amd_iommu_iova_to_phys for new fetch_pte interface Now that fetch_pte returns the page-size of the pte, this function can be optimized too. Tested-by: Suravee Suthikulpanit Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index f97441b6aaad..7a00e5d40eb6 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -3375,7 +3375,6 @@ static phys_addr_t amd_iommu_iova_to_phys(struct iommu_domain *dom, { struct protection_domain *domain = dom->priv; unsigned long offset_mask, pte_pgsize; - phys_addr_t paddr; u64 *pte, __pte; if (domain->mode == PAGE_MODE_NONE) @@ -3386,15 +3385,10 @@ static phys_addr_t amd_iommu_iova_to_phys(struct iommu_domain *dom, if (!pte || !IOMMU_PTE_PRESENT(*pte)) return 0; - if (PM_PTE_LEVEL(*pte) == 0) - offset_mask = PAGE_SIZE - 1; - else - offset_mask = PTE_PAGE_SIZE(*pte) - 1; - - __pte = *pte & PM_ADDR_MASK; - paddr = (__pte & ~offset_mask) | (iova & offset_mask); + offset_mask = pte_pgsize - 1; + __pte = *pte & PM_ADDR_MASK; - return paddr; + return (__pte & ~offset_mask) | (iova & offset_mask); } static bool amd_iommu_capable(enum iommu_cap cap) -- cgit v1.2.3 From d4b036648402bb4ef6d4a0df51375a2fb705b6cc Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 1 Apr 2015 14:58:52 +0200 Subject: iommu/amd: Correctly encode huge pages in iommu page tables When a default page-size for given level should be mapped, the level encoding must be 0 rather than 7. This fixes an issue seen on IOMMUv2 hardware, where this encoding is enforced. Tested-by: Suravee Suthikulpanit Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 7a00e5d40eb6..aa710b095f1a 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -1390,13 +1390,14 @@ static int iommu_map_page(struct protection_domain *dom, u64 __pte, *pte; int i, count; + BUG_ON(!IS_ALIGNED(bus_addr, page_size)); + BUG_ON(!IS_ALIGNED(phys_addr, page_size)); + if (!(prot & IOMMU_PROT_MASK)) return -EINVAL; - bus_addr = PAGE_ALIGN(bus_addr); - phys_addr = PAGE_ALIGN(phys_addr); - count = PAGE_SIZE_PTE_COUNT(page_size); - pte = alloc_pte(dom, bus_addr, page_size, NULL, GFP_KERNEL); + count = PAGE_SIZE_PTE_COUNT(page_size); + pte = alloc_pte(dom, bus_addr, page_size, NULL, GFP_KERNEL); if (!pte) return -ENOMEM; @@ -1405,7 +1406,7 @@ static int iommu_map_page(struct protection_domain *dom, if (IOMMU_PTE_PRESENT(pte[i])) return -EBUSY; - if (page_size > PAGE_SIZE) { + if (count > 1) { __pte = PAGE_SIZE_PTE(phys_addr, page_size); __pte |= PM_LEVEL_ENC(7) | IOMMU_PTE_P | IOMMU_PTE_FC; } else -- cgit v1.2.3