Commit aa5cabc4 authored by Joerg Roedel's avatar Joerg Roedel

Merge tag 'arm-smmu-updates' of...

Merge tag 'arm-smmu-updates' of git://git.kernel.org/pub/scm/linux/kernel/git/will/linux into arm/smmu

Arm SMMU updates for 6.7

- Device-tree binding update:
  * Add qcom,sm7150-smmu-v2 for Adreno on SM7150 SoC

- SMMUv2:
  * Support for Qualcomm SDM670 (MDSS) and SM7150 SoCs

- SMMUv3:
  * Large refactoring of the context descriptor code to
    move the CD table into the master, paving the way
    for '->set_dev_pasid()' support on non-SVA domains

  * Minor cleanups to the SVA code
parents 6465e260 54865092
...@@ -110,6 +110,7 @@ properties: ...@@ -110,6 +110,7 @@ properties:
- qcom,sdm630-smmu-v2 - qcom,sdm630-smmu-v2
- qcom,sdm845-smmu-v2 - qcom,sdm845-smmu-v2
- qcom,sm6350-smmu-v2 - qcom,sm6350-smmu-v2
- qcom,sm7150-smmu-v2
- const: qcom,adreno-smmu - const: qcom,adreno-smmu
- const: qcom,smmu-v2 - const: qcom,smmu-v2
- description: Qcom Adreno GPUs on Google Cheza platform - description: Qcom Adreno GPUs on Google Cheza platform
...@@ -270,6 +271,7 @@ allOf: ...@@ -270,6 +271,7 @@ allOf:
contains: contains:
enum: enum:
- qcom,msm8998-smmu-v2 - qcom,msm8998-smmu-v2
- qcom,sdm630-smmu-v2
then: then:
anyOf: anyOf:
- properties: - properties:
...@@ -311,7 +313,6 @@ allOf: ...@@ -311,7 +313,6 @@ allOf:
compatible: compatible:
contains: contains:
enum: enum:
- qcom,sdm630-smmu-v2
- qcom,sm6375-smmu-v2 - qcom,sm6375-smmu-v2
then: then:
anyOf: anyOf:
...@@ -409,6 +410,7 @@ allOf: ...@@ -409,6 +410,7 @@ allOf:
contains: contains:
enum: enum:
- qcom,sm6350-smmu-v2 - qcom,sm6350-smmu-v2
- qcom,sm7150-smmu-v2
- qcom,sm8150-smmu-500 - qcom,sm8150-smmu-500
- qcom,sm8250-smmu-500 - qcom,sm8250-smmu-500
then: then:
......
...@@ -25,11 +25,9 @@ struct arm_smmu_mmu_notifier { ...@@ -25,11 +25,9 @@ struct arm_smmu_mmu_notifier {
#define mn_to_smmu(mn) container_of(mn, struct arm_smmu_mmu_notifier, mn) #define mn_to_smmu(mn) container_of(mn, struct arm_smmu_mmu_notifier, mn)
struct arm_smmu_bond { struct arm_smmu_bond {
struct iommu_sva sva;
struct mm_struct *mm; struct mm_struct *mm;
struct arm_smmu_mmu_notifier *smmu_mn; struct arm_smmu_mmu_notifier *smmu_mn;
struct list_head list; struct list_head list;
refcount_t refs;
}; };
#define sva_to_bond(handle) \ #define sva_to_bond(handle) \
...@@ -37,6 +35,25 @@ struct arm_smmu_bond { ...@@ -37,6 +35,25 @@ struct arm_smmu_bond {
static DEFINE_MUTEX(sva_lock); static DEFINE_MUTEX(sva_lock);
/*
* Write the CD to the CD tables for all masters that this domain is attached
* to. Note that this is only used to update existing CD entries in the target
* CD table, for which it's assumed that arm_smmu_write_ctx_desc can't fail.
*/
static void arm_smmu_update_ctx_desc_devices(struct arm_smmu_domain *smmu_domain,
int ssid,
struct arm_smmu_ctx_desc *cd)
{
struct arm_smmu_master *master;
unsigned long flags;
spin_lock_irqsave(&smmu_domain->devices_lock, flags);
list_for_each_entry(master, &smmu_domain->devices, domain_head) {
arm_smmu_write_ctx_desc(master, ssid, cd);
}
spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
}
/* /*
* Check if the CPU ASID is available on the SMMU side. If a private context * Check if the CPU ASID is available on the SMMU side. If a private context
* descriptor is using it, try to replace it. * descriptor is using it, try to replace it.
...@@ -62,7 +79,7 @@ arm_smmu_share_asid(struct mm_struct *mm, u16 asid) ...@@ -62,7 +79,7 @@ arm_smmu_share_asid(struct mm_struct *mm, u16 asid)
return cd; return cd;
} }
smmu_domain = container_of(cd, struct arm_smmu_domain, s1_cfg.cd); smmu_domain = container_of(cd, struct arm_smmu_domain, cd);
smmu = smmu_domain->smmu; smmu = smmu_domain->smmu;
ret = xa_alloc(&arm_smmu_asid_xa, &new_asid, cd, ret = xa_alloc(&arm_smmu_asid_xa, &new_asid, cd,
...@@ -80,7 +97,7 @@ arm_smmu_share_asid(struct mm_struct *mm, u16 asid) ...@@ -80,7 +97,7 @@ arm_smmu_share_asid(struct mm_struct *mm, u16 asid)
* be some overlap between use of both ASIDs, until we invalidate the * be some overlap between use of both ASIDs, until we invalidate the
* TLB. * TLB.
*/ */
arm_smmu_write_ctx_desc(smmu_domain, IOMMU_NO_PASID, cd); arm_smmu_update_ctx_desc_devices(smmu_domain, IOMMU_NO_PASID, cd);
/* Invalidate TLB entries previously associated with that context */ /* Invalidate TLB entries previously associated with that context */
arm_smmu_tlb_inv_asid(smmu, asid); arm_smmu_tlb_inv_asid(smmu, asid);
...@@ -186,6 +203,15 @@ static void arm_smmu_free_shared_cd(struct arm_smmu_ctx_desc *cd) ...@@ -186,6 +203,15 @@ static void arm_smmu_free_shared_cd(struct arm_smmu_ctx_desc *cd)
} }
} }
/*
* Cloned from the MAX_TLBI_OPS in arch/arm64/include/asm/tlbflush.h, this
* is used as a threshold to replace per-page TLBI commands to issue in the
* command queue with an address-space TLBI command, when SMMU w/o a range
* invalidation feature handles too many per-page TLBI commands, which will
* otherwise result in a soft lockup.
*/
#define CMDQ_MAX_TLBI_OPS (1 << (PAGE_SHIFT - 3))
static void arm_smmu_mm_arch_invalidate_secondary_tlbs(struct mmu_notifier *mn, static void arm_smmu_mm_arch_invalidate_secondary_tlbs(struct mmu_notifier *mn,
struct mm_struct *mm, struct mm_struct *mm,
unsigned long start, unsigned long start,
...@@ -201,8 +227,13 @@ static void arm_smmu_mm_arch_invalidate_secondary_tlbs(struct mmu_notifier *mn, ...@@ -201,8 +227,13 @@ static void arm_smmu_mm_arch_invalidate_secondary_tlbs(struct mmu_notifier *mn,
* range. So do a simple translation here by calculating size correctly. * range. So do a simple translation here by calculating size correctly.
*/ */
size = end - start; size = end - start;
if (size == ULONG_MAX) if (!(smmu_domain->smmu->features & ARM_SMMU_FEAT_RANGE_INV)) {
size = 0; if (size >= CMDQ_MAX_TLBI_OPS * PAGE_SIZE)
size = 0;
} else {
if (size == ULONG_MAX)
size = 0;
}
if (!(smmu_domain->smmu->features & ARM_SMMU_FEAT_BTM)) { if (!(smmu_domain->smmu->features & ARM_SMMU_FEAT_BTM)) {
if (!size) if (!size)
...@@ -233,7 +264,7 @@ static void arm_smmu_mm_release(struct mmu_notifier *mn, struct mm_struct *mm) ...@@ -233,7 +264,7 @@ static void arm_smmu_mm_release(struct mmu_notifier *mn, struct mm_struct *mm)
* DMA may still be running. Keep the cd valid to avoid C_BAD_CD events, * DMA may still be running. Keep the cd valid to avoid C_BAD_CD events,
* but disable translation. * but disable translation.
*/ */
arm_smmu_write_ctx_desc(smmu_domain, mm->pasid, &quiet_cd); arm_smmu_update_ctx_desc_devices(smmu_domain, mm->pasid, &quiet_cd);
arm_smmu_tlb_inv_asid(smmu_domain->smmu, smmu_mn->cd->asid); arm_smmu_tlb_inv_asid(smmu_domain->smmu, smmu_mn->cd->asid);
arm_smmu_atc_inv_domain(smmu_domain, mm->pasid, 0, 0); arm_smmu_atc_inv_domain(smmu_domain, mm->pasid, 0, 0);
...@@ -259,8 +290,10 @@ arm_smmu_mmu_notifier_get(struct arm_smmu_domain *smmu_domain, ...@@ -259,8 +290,10 @@ arm_smmu_mmu_notifier_get(struct arm_smmu_domain *smmu_domain,
struct mm_struct *mm) struct mm_struct *mm)
{ {
int ret; int ret;
unsigned long flags;
struct arm_smmu_ctx_desc *cd; struct arm_smmu_ctx_desc *cd;
struct arm_smmu_mmu_notifier *smmu_mn; struct arm_smmu_mmu_notifier *smmu_mn;
struct arm_smmu_master *master;
list_for_each_entry(smmu_mn, &smmu_domain->mmu_notifiers, list) { list_for_each_entry(smmu_mn, &smmu_domain->mmu_notifiers, list) {
if (smmu_mn->mn.mm == mm) { if (smmu_mn->mn.mm == mm) {
...@@ -290,7 +323,16 @@ arm_smmu_mmu_notifier_get(struct arm_smmu_domain *smmu_domain, ...@@ -290,7 +323,16 @@ arm_smmu_mmu_notifier_get(struct arm_smmu_domain *smmu_domain,
goto err_free_cd; goto err_free_cd;
} }
ret = arm_smmu_write_ctx_desc(smmu_domain, mm->pasid, cd); spin_lock_irqsave(&smmu_domain->devices_lock, flags);
list_for_each_entry(master, &smmu_domain->devices, domain_head) {
ret = arm_smmu_write_ctx_desc(master, mm->pasid, cd);
if (ret) {
list_for_each_entry_from_reverse(master, &smmu_domain->devices, domain_head)
arm_smmu_write_ctx_desc(master, mm->pasid, NULL);
break;
}
}
spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
if (ret) if (ret)
goto err_put_notifier; goto err_put_notifier;
...@@ -315,7 +357,8 @@ static void arm_smmu_mmu_notifier_put(struct arm_smmu_mmu_notifier *smmu_mn) ...@@ -315,7 +357,8 @@ static void arm_smmu_mmu_notifier_put(struct arm_smmu_mmu_notifier *smmu_mn)
return; return;
list_del(&smmu_mn->list); list_del(&smmu_mn->list);
arm_smmu_write_ctx_desc(smmu_domain, mm->pasid, NULL);
arm_smmu_update_ctx_desc_devices(smmu_domain, mm->pasid, NULL);
/* /*
* If we went through clear(), we've already invalidated, and no * If we went through clear(), we've already invalidated, and no
...@@ -331,8 +374,7 @@ static void arm_smmu_mmu_notifier_put(struct arm_smmu_mmu_notifier *smmu_mn) ...@@ -331,8 +374,7 @@ static void arm_smmu_mmu_notifier_put(struct arm_smmu_mmu_notifier *smmu_mn)
arm_smmu_free_shared_cd(cd); arm_smmu_free_shared_cd(cd);
} }
static struct iommu_sva * static int __arm_smmu_sva_bind(struct device *dev, struct mm_struct *mm)
__arm_smmu_sva_bind(struct device *dev, struct mm_struct *mm)
{ {
int ret; int ret;
struct arm_smmu_bond *bond; struct arm_smmu_bond *bond;
...@@ -341,23 +383,13 @@ __arm_smmu_sva_bind(struct device *dev, struct mm_struct *mm) ...@@ -341,23 +383,13 @@ __arm_smmu_sva_bind(struct device *dev, struct mm_struct *mm)
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
if (!master || !master->sva_enabled) if (!master || !master->sva_enabled)
return ERR_PTR(-ENODEV); return -ENODEV;
/* If bind() was already called for this {dev, mm} pair, reuse it. */
list_for_each_entry(bond, &master->bonds, list) {
if (bond->mm == mm) {
refcount_inc(&bond->refs);
return &bond->sva;
}
}
bond = kzalloc(sizeof(*bond), GFP_KERNEL); bond = kzalloc(sizeof(*bond), GFP_KERNEL);
if (!bond) if (!bond)
return ERR_PTR(-ENOMEM); return -ENOMEM;
bond->mm = mm; bond->mm = mm;
bond->sva.dev = dev;
refcount_set(&bond->refs, 1);
bond->smmu_mn = arm_smmu_mmu_notifier_get(smmu_domain, mm); bond->smmu_mn = arm_smmu_mmu_notifier_get(smmu_domain, mm);
if (IS_ERR(bond->smmu_mn)) { if (IS_ERR(bond->smmu_mn)) {
...@@ -366,11 +398,11 @@ __arm_smmu_sva_bind(struct device *dev, struct mm_struct *mm) ...@@ -366,11 +398,11 @@ __arm_smmu_sva_bind(struct device *dev, struct mm_struct *mm)
} }
list_add(&bond->list, &master->bonds); list_add(&bond->list, &master->bonds);
return &bond->sva; return 0;
err_free_bond: err_free_bond:
kfree(bond); kfree(bond);
return ERR_PTR(ret); return ret;
} }
bool arm_smmu_sva_supported(struct arm_smmu_device *smmu) bool arm_smmu_sva_supported(struct arm_smmu_device *smmu)
...@@ -536,7 +568,7 @@ void arm_smmu_sva_remove_dev_pasid(struct iommu_domain *domain, ...@@ -536,7 +568,7 @@ void arm_smmu_sva_remove_dev_pasid(struct iommu_domain *domain,
} }
} }
if (!WARN_ON(!bond) && refcount_dec_and_test(&bond->refs)) { if (!WARN_ON(!bond)) {
list_del(&bond->list); list_del(&bond->list);
arm_smmu_mmu_notifier_put(bond->smmu_mn); arm_smmu_mmu_notifier_put(bond->smmu_mn);
kfree(bond); kfree(bond);
...@@ -548,13 +580,10 @@ static int arm_smmu_sva_set_dev_pasid(struct iommu_domain *domain, ...@@ -548,13 +580,10 @@ static int arm_smmu_sva_set_dev_pasid(struct iommu_domain *domain,
struct device *dev, ioasid_t id) struct device *dev, ioasid_t id)
{ {
int ret = 0; int ret = 0;
struct iommu_sva *handle;
struct mm_struct *mm = domain->mm; struct mm_struct *mm = domain->mm;
mutex_lock(&sva_lock); mutex_lock(&sva_lock);
handle = __arm_smmu_sva_bind(dev, mm); ret = __arm_smmu_sva_bind(dev, mm);
if (IS_ERR(handle))
ret = PTR_ERR(handle);
mutex_unlock(&sva_lock); mutex_unlock(&sva_lock);
return ret; return ret;
......
This diff is collapsed.
...@@ -595,13 +595,11 @@ struct arm_smmu_ctx_desc_cfg { ...@@ -595,13 +595,11 @@ struct arm_smmu_ctx_desc_cfg {
dma_addr_t cdtab_dma; dma_addr_t cdtab_dma;
struct arm_smmu_l1_ctx_desc *l1_desc; struct arm_smmu_l1_ctx_desc *l1_desc;
unsigned int num_l1_ents; unsigned int num_l1_ents;
};
struct arm_smmu_s1_cfg {
struct arm_smmu_ctx_desc_cfg cdcfg;
struct arm_smmu_ctx_desc cd;
u8 s1fmt; u8 s1fmt;
/* log2 of the maximum number of CDs supported by this table */
u8 s1cdmax; u8 s1cdmax;
/* Whether CD entries in this table have the stall bit set. */
u8 stall_enabled:1;
}; };
struct arm_smmu_s2_cfg { struct arm_smmu_s2_cfg {
...@@ -697,6 +695,8 @@ struct arm_smmu_master { ...@@ -697,6 +695,8 @@ struct arm_smmu_master {
struct arm_smmu_domain *domain; struct arm_smmu_domain *domain;
struct list_head domain_head; struct list_head domain_head;
struct arm_smmu_stream *streams; struct arm_smmu_stream *streams;
/* Locked by the iommu core using the group mutex */
struct arm_smmu_ctx_desc_cfg cd_table;
unsigned int num_streams; unsigned int num_streams;
bool ats_enabled; bool ats_enabled;
bool stall_enabled; bool stall_enabled;
...@@ -719,13 +719,12 @@ struct arm_smmu_domain { ...@@ -719,13 +719,12 @@ struct arm_smmu_domain {
struct mutex init_mutex; /* Protects smmu pointer */ struct mutex init_mutex; /* Protects smmu pointer */
struct io_pgtable_ops *pgtbl_ops; struct io_pgtable_ops *pgtbl_ops;
bool stall_enabled;
atomic_t nr_ats_masters; atomic_t nr_ats_masters;
enum arm_smmu_domain_stage stage; enum arm_smmu_domain_stage stage;
union { union {
struct arm_smmu_s1_cfg s1_cfg; struct arm_smmu_ctx_desc cd;
struct arm_smmu_s2_cfg s2_cfg; struct arm_smmu_s2_cfg s2_cfg;
}; };
struct iommu_domain domain; struct iommu_domain domain;
...@@ -745,7 +744,7 @@ extern struct xarray arm_smmu_asid_xa; ...@@ -745,7 +744,7 @@ extern struct xarray arm_smmu_asid_xa;
extern struct mutex arm_smmu_asid_lock; extern struct mutex arm_smmu_asid_lock;
extern struct arm_smmu_ctx_desc quiet_cd; extern struct arm_smmu_ctx_desc quiet_cd;
int arm_smmu_write_ctx_desc(struct arm_smmu_domain *smmu_domain, int ssid, int arm_smmu_write_ctx_desc(struct arm_smmu_master *smmu_master, int ssid,
struct arm_smmu_ctx_desc *cd); struct arm_smmu_ctx_desc *cd);
void arm_smmu_tlb_inv_asid(struct arm_smmu_device *smmu, u16 asid); void arm_smmu_tlb_inv_asid(struct arm_smmu_device *smmu, u16 asid);
void arm_smmu_tlb_inv_range_asid(unsigned long iova, size_t size, int asid, void arm_smmu_tlb_inv_range_asid(unsigned long iova, size_t size, int asid,
......
...@@ -251,6 +251,7 @@ static const struct of_device_id qcom_smmu_client_of_match[] __maybe_unused = { ...@@ -251,6 +251,7 @@ static const struct of_device_id qcom_smmu_client_of_match[] __maybe_unused = {
{ .compatible = "qcom,sc7280-mss-pil" }, { .compatible = "qcom,sc7280-mss-pil" },
{ .compatible = "qcom,sc8180x-mdss" }, { .compatible = "qcom,sc8180x-mdss" },
{ .compatible = "qcom,sc8280xp-mdss" }, { .compatible = "qcom,sc8280xp-mdss" },
{ .compatible = "qcom,sdm670-mdss" },
{ .compatible = "qcom,sdm845-mdss" }, { .compatible = "qcom,sdm845-mdss" },
{ .compatible = "qcom,sdm845-mss-pil" }, { .compatible = "qcom,sdm845-mss-pil" },
{ .compatible = "qcom,sm6350-mdss" }, { .compatible = "qcom,sm6350-mdss" },
...@@ -532,6 +533,7 @@ static const struct of_device_id __maybe_unused qcom_smmu_impl_of_match[] = { ...@@ -532,6 +533,7 @@ static const struct of_device_id __maybe_unused qcom_smmu_impl_of_match[] = {
{ .compatible = "qcom,sm6350-smmu-500", .data = &qcom_smmu_500_impl0_data }, { .compatible = "qcom,sm6350-smmu-500", .data = &qcom_smmu_500_impl0_data },
{ .compatible = "qcom,sm6375-smmu-v2", .data = &qcom_smmu_v2_data }, { .compatible = "qcom,sm6375-smmu-v2", .data = &qcom_smmu_v2_data },
{ .compatible = "qcom,sm6375-smmu-500", .data = &qcom_smmu_500_impl0_data }, { .compatible = "qcom,sm6375-smmu-500", .data = &qcom_smmu_500_impl0_data },
{ .compatible = "qcom,sm7150-smmu-v2", .data = &qcom_smmu_v2_data },
{ .compatible = "qcom,sm8150-smmu-500", .data = &qcom_smmu_500_impl0_data }, { .compatible = "qcom,sm8150-smmu-500", .data = &qcom_smmu_500_impl0_data },
{ .compatible = "qcom,sm8250-smmu-500", .data = &qcom_smmu_500_impl0_data }, { .compatible = "qcom,sm8250-smmu-500", .data = &qcom_smmu_500_impl0_data },
{ .compatible = "qcom,sm8350-smmu-500", .data = &qcom_smmu_500_impl0_data }, { .compatible = "qcom,sm8350-smmu-500", .data = &qcom_smmu_500_impl0_data },
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment