* [PATCH v7 12/24] iommu/arm-smmu-v3: Add support for VHE
From: Jean-Philippe Brucker @ 2020-05-19 17:54 UTC (permalink / raw)
To: iommu, devicetree, linux-arm-kernel, linux-pci, linux-mm
Cc: joro, catalin.marinas, will, robin.murphy, kevin.tian, baolu.lu,
Jonathan.Cameron, jacob.jun.pan, christian.koenig, felix.kuehling,
zhangfei.gao, jgg, xuzaibo, fenghua.yu, hch,
Jean-Philippe Brucker
In-Reply-To: <20200519175502.2504091-1-jean-philippe@linaro.org>
ARMv8.1 extensions added Virtualization Host Extensions (VHE), which allow
to run a host kernel at EL2. When using normal DMA, Device and CPU address
spaces are dissociated, and do not need to implement the same
capabilities, so VHE hasn't been used in the SMMU until now.
With shared address spaces however, ASIDs are shared between MMU and SMMU,
and broadcast TLB invalidations issued by a CPU are taken into account by
the SMMU. TLB entries on both sides need to have identical exception level
in order to be cleared with a single invalidation.
When the CPU is using VHE, enable VHE in the SMMU for all STEs. Normal DMA
mappings will need to use TLBI_EL2 commands instead of TLBI_NH, but
shouldn't be otherwise affected by this change.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
---
drivers/iommu/arm-smmu-v3.c | 31 ++++++++++++++++++++++++++-----
1 file changed, 26 insertions(+), 5 deletions(-)
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 403871d36438..7e1933e7e35f 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -13,6 +13,7 @@
#include <linux/acpi_iort.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
+#include <linux/cpufeature.h>
#include <linux/crash_dump.h>
#include <linux/delay.h>
#include <linux/dma-iommu.h>
@@ -482,6 +483,8 @@ struct arm_smmu_cmdq_ent {
#define CMDQ_OP_TLBI_NH_ASID 0x11
#define CMDQ_OP_TLBI_NH_VA 0x12
#define CMDQ_OP_TLBI_EL2_ALL 0x20
+ #define CMDQ_OP_TLBI_EL2_ASID 0x21
+ #define CMDQ_OP_TLBI_EL2_VA 0x22
#define CMDQ_OP_TLBI_S12_VMALL 0x28
#define CMDQ_OP_TLBI_S2_IPA 0x2a
#define CMDQ_OP_TLBI_NSNH_ALL 0x30
@@ -654,6 +657,7 @@ struct arm_smmu_device {
#define ARM_SMMU_FEAT_STALL_FORCE (1 << 13)
#define ARM_SMMU_FEAT_VAX (1 << 14)
#define ARM_SMMU_FEAT_RANGE_INV (1 << 15)
+#define ARM_SMMU_FEAT_E2H (1 << 16)
u32 features;
#define ARM_SMMU_OPT_SKIP_PREFETCH (1 << 0)
@@ -927,6 +931,8 @@ static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent)
cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_NUM, ent->tlbi.num);
cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_SCALE, ent->tlbi.scale);
cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_VMID, ent->tlbi.vmid);
+ /* Fallthrough */
+ case CMDQ_OP_TLBI_EL2_VA:
cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_ASID, ent->tlbi.asid);
cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_LEAF, ent->tlbi.leaf);
cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_TTL, ent->tlbi.ttl);
@@ -948,6 +954,9 @@ static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent)
case CMDQ_OP_TLBI_S12_VMALL:
cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_VMID, ent->tlbi.vmid);
break;
+ case CMDQ_OP_TLBI_EL2_ASID:
+ cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_ASID, ent->tlbi.asid);
+ break;
case CMDQ_OP_ATC_INV:
cmd[0] |= FIELD_PREP(CMDQ_0_SSV, ent->substream_valid);
cmd[0] |= FIELD_PREP(CMDQ_ATC_0_GLOBAL, ent->atc.global);
@@ -1541,7 +1550,8 @@ static int arm_smmu_cmdq_batch_submit(struct arm_smmu_device *smmu,
static void arm_smmu_tlb_inv_asid(struct arm_smmu_device *smmu, u16 asid)
{
struct arm_smmu_cmdq_ent cmd = {
- .opcode = CMDQ_OP_TLBI_NH_ASID,
+ .opcode = smmu->features & ARM_SMMU_FEAT_E2H ?
+ CMDQ_OP_TLBI_EL2_ASID : CMDQ_OP_TLBI_NH_ASID,
.tlbi.asid = asid,
};
@@ -2084,13 +2094,16 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid,
}
if (s1_cfg) {
+ int strw = smmu->features & ARM_SMMU_FEAT_E2H ?
+ STRTAB_STE_1_STRW_EL2 : STRTAB_STE_1_STRW_NSEL1;
+
BUG_ON(ste_live);
dst[1] = cpu_to_le64(
FIELD_PREP(STRTAB_STE_1_S1DSS, STRTAB_STE_1_S1DSS_SSID0) |
FIELD_PREP(STRTAB_STE_1_S1CIR, STRTAB_STE_1_S1C_CACHE_WBRA) |
FIELD_PREP(STRTAB_STE_1_S1COR, STRTAB_STE_1_S1C_CACHE_WBRA) |
FIELD_PREP(STRTAB_STE_1_S1CSH, ARM_SMMU_SH_ISH) |
- FIELD_PREP(STRTAB_STE_1_STRW, STRTAB_STE_1_STRW_NSEL1));
+ FIELD_PREP(STRTAB_STE_1_STRW, strw));
if (smmu->features & ARM_SMMU_FEAT_STALLS &&
!(smmu->features & ARM_SMMU_FEAT_STALL_FORCE))
@@ -2486,7 +2499,8 @@ static void arm_smmu_tlb_inv_range(unsigned long iova, size_t size,
return;
if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
- cmd.opcode = CMDQ_OP_TLBI_NH_VA;
+ cmd.opcode = smmu->features & ARM_SMMU_FEAT_E2H ?
+ CMDQ_OP_TLBI_EL2_VA : CMDQ_OP_TLBI_NH_VA;
cmd.tlbi.asid = smmu_domain->s1_cfg.cd.asid;
} else {
cmd.opcode = CMDQ_OP_TLBI_S2_IPA;
@@ -3805,7 +3819,11 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass)
writel_relaxed(reg, smmu->base + ARM_SMMU_CR1);
/* CR2 (random crap) */
- reg = CR2_PTM | CR2_RECINVSID | CR2_E2H;
+ reg = CR2_PTM | CR2_RECINVSID;
+
+ if (smmu->features & ARM_SMMU_FEAT_E2H)
+ reg |= CR2_E2H;
+
writel_relaxed(reg, smmu->base + ARM_SMMU_CR2);
/* Stream table */
@@ -3963,8 +3981,11 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
if (reg & IDR0_MSI)
smmu->features |= ARM_SMMU_FEAT_MSI;
- if (reg & IDR0_HYP)
+ if (reg & IDR0_HYP) {
smmu->features |= ARM_SMMU_FEAT_HYP;
+ if (cpus_have_cap(ARM64_HAS_VIRT_HOST_EXTN))
+ smmu->features |= ARM_SMMU_FEAT_E2H;
+ }
/*
* The coherency feature as set by FW is used in preference to the ID
--
2.26.2
^ permalink raw reply related
* [PATCH v7 11/24] iommu/arm-smmu-v3: Seize private ASID
From: Jean-Philippe Brucker @ 2020-05-19 17:54 UTC (permalink / raw)
To: iommu, devicetree, linux-arm-kernel, linux-pci, linux-mm
Cc: joro, catalin.marinas, will, robin.murphy, kevin.tian, baolu.lu,
Jonathan.Cameron, jacob.jun.pan, christian.koenig, felix.kuehling,
zhangfei.gao, jgg, xuzaibo, fenghua.yu, hch,
Jean-Philippe Brucker
In-Reply-To: <20200519175502.2504091-1-jean-philippe@linaro.org>
The SMMU has a single ASID space, the union of shared and private ASID
sets. This means that the SMMU driver competes with the arch allocator
for ASIDs. Shared ASIDs are those of Linux processes, allocated by the
arch, and contribute in broadcast TLB maintenance. Private ASIDs are
allocated by the SMMU driver and used for "classic" map/unmap DMA. They
require command-queue TLB invalidations.
When we pin down an mm_context and get an ASID that is already in use by
the SMMU, it belongs to a private context. We used to simply abort the
bind, but this is unfair to users that would be unable to bind a few
seemingly random processes. Try to allocate a new private ASID for the
context, and make the old ASID shared.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
---
v6->v7: Replace context_lock spinlock with asid_lock mutex, remove
GFP_ATOMIC changes, add comments about locking.
---
drivers/iommu/arm-smmu-v3.c | 100 ++++++++++++++++++++++++++++--------
1 file changed, 80 insertions(+), 20 deletions(-)
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 52cbdf08f5e2..403871d36438 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -733,6 +733,7 @@ struct arm_smmu_option_prop {
};
static DEFINE_XARRAY_ALLOC1(asid_xa);
+static DEFINE_MUTEX(asid_lock);
static DEFINE_MUTEX(sva_lock);
static struct arm_smmu_option_prop arm_smmu_options[] = {
@@ -1537,6 +1538,17 @@ static int arm_smmu_cmdq_batch_submit(struct arm_smmu_device *smmu,
}
/* Context descriptor manipulation functions */
+static void arm_smmu_tlb_inv_asid(struct arm_smmu_device *smmu, u16 asid)
+{
+ struct arm_smmu_cmdq_ent cmd = {
+ .opcode = CMDQ_OP_TLBI_NH_ASID,
+ .tlbi.asid = asid,
+ };
+
+ arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+ arm_smmu_cmdq_issue_sync(smmu);
+}
+
static void arm_smmu_sync_cd(struct arm_smmu_domain *smmu_domain,
int ssid, bool leaf)
{
@@ -1795,9 +1807,18 @@ static bool arm_smmu_free_asid(struct arm_smmu_ctx_desc *cd)
return free;
}
+/*
+ * Try to reserve this ASID in the SMMU. If it is in use, try to steal it from
+ * the private entry. Careful here, we may be modifying the context tables of
+ * another SMMU!
+ */
static struct arm_smmu_ctx_desc *arm_smmu_share_asid(u16 asid)
{
+ int ret;
+ u32 new_asid;
struct arm_smmu_ctx_desc *cd;
+ struct arm_smmu_device *smmu;
+ struct arm_smmu_domain *smmu_domain;
cd = xa_load(&asid_xa, asid);
if (!cd)
@@ -1809,11 +1830,31 @@ static struct arm_smmu_ctx_desc *arm_smmu_share_asid(u16 asid)
return cd;
}
+ smmu_domain = container_of(cd, struct arm_smmu_domain, s1_cfg.cd);
+ smmu = smmu_domain->smmu;
+
+ ret = xa_alloc(&asid_xa, &new_asid, cd,
+ XA_LIMIT(1, 1 << smmu->asid_bits), GFP_KERNEL);
+ if (ret)
+ return ERR_PTR(-ENOSPC);
+ /*
+ * Race with unmap: TLB invalidations will start targeting the new ASID,
+ * which isn't assigned yet. We'll do an invalidate-all on the old ASID
+ * later, so it doesn't matter.
+ */
+ cd->asid = new_asid;
+
/*
- * Ouch, ASID is already in use for a private cd.
- * TODO: seize it.
+ * Update ASID and invalidate CD in all associated masters. There will
+ * be some overlap between use of both ASIDs, until we invalidate the
+ * TLB.
*/
- return ERR_PTR(-EEXIST);
+ arm_smmu_write_ctx_desc(smmu_domain, 0, cd);
+
+ /* Invalidate TLB entries previously associated with that context */
+ arm_smmu_tlb_inv_asid(smmu, asid);
+
+ return NULL;
}
__maybe_unused
@@ -1839,7 +1880,20 @@ static struct arm_smmu_ctx_desc *arm_smmu_alloc_shared_cd(struct mm_struct *mm)
arm_smmu_init_cd(cd);
+ /*
+ * Serialize against arm_smmu_domain_finalise_s1() and
+ * arm_smmu_domain_free() as we might need to replace the private ASID
+ * from an existing CD.
+ */
+ mutex_lock(&asid_lock);
old_cd = arm_smmu_share_asid(asid);
+ if (!old_cd) {
+ ret = xa_insert(&asid_xa, asid, cd, GFP_KERNEL);
+ if (ret)
+ old_cd = ERR_PTR(ret);
+ }
+ mutex_unlock(&asid_lock);
+
if (IS_ERR(old_cd)) {
ret = PTR_ERR(old_cd);
goto err_free_cd;
@@ -1853,11 +1907,6 @@ static struct arm_smmu_ctx_desc *arm_smmu_alloc_shared_cd(struct mm_struct *mm)
return old_cd;
}
- /* Fails if a private ASID has been allocated since we last checked */
- ret = xa_insert(&asid_xa, asid, cd, GFP_KERNEL);
- if (ret)
- goto err_free_cd;
-
tcr = FIELD_PREP(CTXDESC_CD_0_TCR_T0SZ, 64ULL - VA_BITS) |
FIELD_PREP(CTXDESC_CD_0_TCR_IRGN0, ARM_LPAE_TCR_RGN_WBWA) |
FIELD_PREP(CTXDESC_CD_0_TCR_ORGN0, ARM_LPAE_TCR_RGN_WBWA) |
@@ -2401,15 +2450,6 @@ static void arm_smmu_tlb_inv_context(void *cookie)
struct arm_smmu_device *smmu = smmu_domain->smmu;
struct arm_smmu_cmdq_ent cmd;
- if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
- cmd.opcode = CMDQ_OP_TLBI_NH_ASID;
- cmd.tlbi.asid = smmu_domain->s1_cfg.cd.asid;
- cmd.tlbi.vmid = 0;
- } else {
- cmd.opcode = CMDQ_OP_TLBI_S12_VMALL;
- cmd.tlbi.vmid = smmu_domain->s2_cfg.vmid;
- }
-
/*
* NOTE: when io-pgtable is in non-strict mode, we may get here with
* PTEs previously cleared by unmaps on the current CPU not yet visible
@@ -2417,8 +2457,14 @@ static void arm_smmu_tlb_inv_context(void *cookie)
* insertion to guarantee those are observed before the TLBI. Do be
* careful, 007.
*/
- arm_smmu_cmdq_issue_cmd(smmu, &cmd);
- arm_smmu_cmdq_issue_sync(smmu);
+ if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
+ arm_smmu_tlb_inv_asid(smmu, smmu_domain->s1_cfg.cd.asid);
+ } else {
+ cmd.opcode = CMDQ_OP_TLBI_S12_VMALL;
+ cmd.tlbi.vmid = smmu_domain->s2_cfg.vmid;
+ arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+ arm_smmu_cmdq_issue_sync(smmu);
+ }
arm_smmu_atc_inv_domain(smmu_domain, 0, 0, 0);
}
@@ -2602,9 +2648,15 @@ static void arm_smmu_domain_free(struct iommu_domain *domain)
if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
+ /*
+ * Prevent arm_smmu_share_asid() from rewriting CD#0 while we're
+ * freeing it.
+ */
+ mutex_lock(&asid_lock);
if (cfg->cdcfg.cdtab)
arm_smmu_free_cd_tables(smmu_domain);
arm_smmu_free_asid(&cfg->cd);
+ mutex_unlock(&asid_lock);
} else {
struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
if (cfg->vmid)
@@ -2626,10 +2678,15 @@ static int arm_smmu_domain_finalise_s1(struct arm_smmu_domain *smmu_domain,
arm_smmu_init_cd(&cfg->cd);
+ /*
+ * Prevent arm_smmu_share_asid() from seizing the private ASID we're
+ * allocating here until it is written to the CD.
+ */
+ mutex_lock(&asid_lock);
ret = xa_alloc(&asid_xa, &asid, &cfg->cd,
XA_LIMIT(1, (1 << smmu->asid_bits) - 1), GFP_KERNEL);
if (ret)
- return ret;
+ goto out_unlock;
cfg->s1cdmax = master->ssid_bits;
@@ -2657,12 +2714,15 @@ static int arm_smmu_domain_finalise_s1(struct arm_smmu_domain *smmu_domain,
if (ret)
goto out_free_cd_tables;
+ mutex_unlock(&asid_lock);
return 0;
out_free_cd_tables:
arm_smmu_free_cd_tables(smmu_domain);
out_free_asid:
arm_smmu_free_asid(&cfg->cd);
+out_unlock:
+ mutex_unlock(&asid_lock);
return ret;
}
--
2.26.2
^ permalink raw reply related
* [PATCH v7 10/24] iommu/arm-smmu-v3: Share process page tables
From: Jean-Philippe Brucker @ 2020-05-19 17:54 UTC (permalink / raw)
To: iommu, devicetree, linux-arm-kernel, linux-pci, linux-mm
Cc: joro, catalin.marinas, will, robin.murphy, kevin.tian, baolu.lu,
Jonathan.Cameron, jacob.jun.pan, christian.koenig, felix.kuehling,
zhangfei.gao, jgg, xuzaibo, fenghua.yu, hch,
Jean-Philippe Brucker, Suzuki K Poulose
In-Reply-To: <20200519175502.2504091-1-jean-philippe@linaro.org>
With Shared Virtual Addressing (SVA), we need to mirror CPU TTBR, TCR,
MAIR and ASIDs in SMMU contexts. Each SMMU has a single ASID space split
into two sets, shared and private. Shared ASIDs correspond to those
obtained from the arch ASID allocator, and private ASIDs are used for
"classic" map/unmap DMA.
Each mm_struct shared with the SMMU will have a single context
descriptor. Add a refcount to keep track of this. It will be protected
by the global SVA lock.
Acked-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
---
v6->v7: Add lockdep annotations for sva_lock
---
drivers/iommu/arm-smmu-v3.c | 153 +++++++++++++++++++++++++++++++++++-
1 file changed, 149 insertions(+), 4 deletions(-)
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 847c7de0a93f..52cbdf08f5e2 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -22,6 +22,7 @@
#include <linux/iommu.h>
#include <linux/iopoll.h>
#include <linux/module.h>
+#include <linux/mmu_context.h>
#include <linux/msi.h>
#include <linux/of.h>
#include <linux/of_address.h>
@@ -33,6 +34,8 @@
#include <linux/amba/bus.h>
+#include "io-pgtable-arm.h"
+
/* MMIO registers */
#define ARM_SMMU_IDR0 0x0
#define IDR0_ST_LVL GENMASK(28, 27)
@@ -589,6 +592,9 @@ struct arm_smmu_ctx_desc {
u64 ttbr;
u64 tcr;
u64 mair;
+
+ refcount_t refs;
+ struct mm_struct *mm;
};
struct arm_smmu_l1_ctx_desc {
@@ -727,6 +733,7 @@ struct arm_smmu_option_prop {
};
static DEFINE_XARRAY_ALLOC1(asid_xa);
+static DEFINE_MUTEX(sva_lock);
static struct arm_smmu_option_prop arm_smmu_options[] = {
{ ARM_SMMU_OPT_SKIP_PREFETCH, "hisilicon,broken-prefetch-cmd" },
@@ -1662,7 +1669,8 @@ static int arm_smmu_write_ctx_desc(struct arm_smmu_domain *smmu_domain,
#ifdef __BIG_ENDIAN
CTXDESC_CD_0_ENDI |
#endif
- CTXDESC_CD_0_R | CTXDESC_CD_0_A | CTXDESC_CD_0_ASET |
+ CTXDESC_CD_0_R | CTXDESC_CD_0_A |
+ (cd->mm ? 0 : CTXDESC_CD_0_ASET) |
CTXDESC_CD_0_AA64 |
FIELD_PREP(CTXDESC_CD_0_ASID, cd->asid) |
CTXDESC_CD_0_V;
@@ -1766,12 +1774,147 @@ static void arm_smmu_free_cd_tables(struct arm_smmu_domain *smmu_domain)
cdcfg->cdtab = NULL;
}
-static void arm_smmu_free_asid(struct arm_smmu_ctx_desc *cd)
+static void arm_smmu_init_cd(struct arm_smmu_ctx_desc *cd)
{
+ refcount_set(&cd->refs, 1);
+}
+
+static bool arm_smmu_free_asid(struct arm_smmu_ctx_desc *cd)
+{
+ bool free;
+ struct arm_smmu_ctx_desc *old_cd;
+
if (!cd->asid)
- return;
+ return false;
+
+ free = refcount_dec_and_test(&cd->refs);
+ if (free) {
+ old_cd = xa_erase(&asid_xa, cd->asid);
+ WARN_ON(old_cd != cd);
+ }
+ return free;
+}
+
+static struct arm_smmu_ctx_desc *arm_smmu_share_asid(u16 asid)
+{
+ struct arm_smmu_ctx_desc *cd;
+
+ cd = xa_load(&asid_xa, asid);
+ if (!cd)
+ return NULL;
+
+ if (cd->mm) {
+ /* All devices bound to this mm use the same cd struct. */
+ refcount_inc(&cd->refs);
+ return cd;
+ }
+
+ /*
+ * Ouch, ASID is already in use for a private cd.
+ * TODO: seize it.
+ */
+ return ERR_PTR(-EEXIST);
+}
+
+__maybe_unused
+static struct arm_smmu_ctx_desc *arm_smmu_alloc_shared_cd(struct mm_struct *mm)
+{
+ u16 asid;
+ int ret = 0;
+ u64 tcr, par, reg;
+ struct arm_smmu_ctx_desc *cd;
+ struct arm_smmu_ctx_desc *old_cd = NULL;
+
+ lockdep_assert_held(&sva_lock);
+
+ asid = mm_context_get(mm);
+ if (!asid)
+ return ERR_PTR(-ESRCH);
- xa_erase(&asid_xa, cd->asid);
+ cd = kzalloc(sizeof(*cd), GFP_KERNEL);
+ if (!cd) {
+ ret = -ENOMEM;
+ goto err_put_context;
+ }
+
+ arm_smmu_init_cd(cd);
+
+ old_cd = arm_smmu_share_asid(asid);
+ if (IS_ERR(old_cd)) {
+ ret = PTR_ERR(old_cd);
+ goto err_free_cd;
+ } else if (old_cd) {
+ if (WARN_ON(old_cd->mm != mm)) {
+ ret = -EINVAL;
+ goto err_free_cd;
+ }
+ kfree(cd);
+ mm_context_put(mm);
+ return old_cd;
+ }
+
+ /* Fails if a private ASID has been allocated since we last checked */
+ ret = xa_insert(&asid_xa, asid, cd, GFP_KERNEL);
+ if (ret)
+ goto err_free_cd;
+
+ tcr = FIELD_PREP(CTXDESC_CD_0_TCR_T0SZ, 64ULL - VA_BITS) |
+ FIELD_PREP(CTXDESC_CD_0_TCR_IRGN0, ARM_LPAE_TCR_RGN_WBWA) |
+ FIELD_PREP(CTXDESC_CD_0_TCR_ORGN0, ARM_LPAE_TCR_RGN_WBWA) |
+ FIELD_PREP(CTXDESC_CD_0_TCR_SH0, ARM_LPAE_TCR_SH_IS) |
+ CTXDESC_CD_0_TCR_EPD1 | CTXDESC_CD_0_AA64;
+
+ switch (PAGE_SIZE) {
+ case SZ_4K:
+ tcr |= FIELD_PREP(CTXDESC_CD_0_TCR_TG0, ARM_LPAE_TCR_TG0_4K);
+ break;
+ case SZ_16K:
+ tcr |= FIELD_PREP(CTXDESC_CD_0_TCR_TG0, ARM_LPAE_TCR_TG0_16K);
+ break;
+ case SZ_64K:
+ tcr |= FIELD_PREP(CTXDESC_CD_0_TCR_TG0, ARM_LPAE_TCR_TG0_64K);
+ break;
+ default:
+ WARN_ON(1);
+ ret = -EINVAL;
+ goto err_free_asid;
+ }
+
+ reg = read_sanitised_ftr_reg(SYS_ID_AA64MMFR0_EL1);
+ par = cpuid_feature_extract_unsigned_field(reg, ID_AA64MMFR0_PARANGE_SHIFT);
+ tcr |= FIELD_PREP(CTXDESC_CD_0_TCR_IPS, par);
+
+ cd->ttbr = virt_to_phys(mm->pgd);
+ cd->tcr = tcr;
+ /*
+ * MAIR value is pretty much constant and global, so we can just get it
+ * from the current CPU register
+ */
+ cd->mair = read_sysreg(mair_el1);
+ cd->asid = asid;
+ cd->mm = mm;
+
+ return cd;
+
+err_free_asid:
+ arm_smmu_free_asid(cd);
+err_free_cd:
+ kfree(cd);
+err_put_context:
+ mm_context_put(mm);
+ return ERR_PTR(ret);
+}
+
+__maybe_unused
+static void arm_smmu_free_shared_cd(struct arm_smmu_ctx_desc *cd)
+{
+ lockdep_assert_held(&sva_lock);
+
+ if (arm_smmu_free_asid(cd)) {
+ /* Unpin ASID */
+ mm_context_put(cd->mm);
+ kfree(cd);
+ }
}
/* Stream table manipulation functions */
@@ -2481,6 +2624,8 @@ static int arm_smmu_domain_finalise_s1(struct arm_smmu_domain *smmu_domain,
struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
typeof(&pgtbl_cfg->arm_lpae_s1_cfg.tcr) tcr = &pgtbl_cfg->arm_lpae_s1_cfg.tcr;
+ arm_smmu_init_cd(&cfg->cd);
+
ret = xa_alloc(&asid_xa, &asid, &cfg->cd,
XA_LIMIT(1, (1 << smmu->asid_bits) - 1), GFP_KERNEL);
if (ret)
--
2.26.2
^ permalink raw reply related
* [PATCH v7 09/24] arm64: cpufeature: Export symbol read_sanitised_ftr_reg()
From: Jean-Philippe Brucker @ 2020-05-19 17:54 UTC (permalink / raw)
To: iommu, devicetree, linux-arm-kernel, linux-pci, linux-mm
Cc: joro, catalin.marinas, will, robin.murphy, kevin.tian, baolu.lu,
Jonathan.Cameron, jacob.jun.pan, christian.koenig, felix.kuehling,
zhangfei.gao, jgg, xuzaibo, fenghua.yu, hch,
Jean-Philippe Brucker, Suzuki K Poulose
In-Reply-To: <20200519175502.2504091-1-jean-philippe@linaro.org>
The SMMUv3 driver would like to read the MMFR0 PARANGE field in order to
share CPU page tables with devices. Allow the driver to be built as
module by exporting the read_sanitized_ftr_reg() cpufeature symbol.
Acked-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
---
arch/arm64/kernel/cpufeature.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 9fac745aa7bb..5f6adbf4ae89 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -841,6 +841,7 @@ u64 read_sanitised_ftr_reg(u32 id)
BUG_ON(!regp);
return regp->sys_val;
}
+EXPORT_SYMBOL_GPL(read_sanitised_ftr_reg);
#define read_sysreg_case(r) \
case r: return read_sysreg_s(r)
--
2.26.2
^ permalink raw reply related
* [PATCH v7 08/24] iommu/arm-smmu-v3: Manage ASIDs with xarray
From: Jean-Philippe Brucker @ 2020-05-19 17:54 UTC (permalink / raw)
To: iommu, devicetree, linux-arm-kernel, linux-pci, linux-mm
Cc: joro, catalin.marinas, will, robin.murphy, kevin.tian, baolu.lu,
Jonathan.Cameron, jacob.jun.pan, christian.koenig, felix.kuehling,
zhangfei.gao, jgg, xuzaibo, fenghua.yu, hch,
Jean-Philippe Brucker
In-Reply-To: <20200519175502.2504091-1-jean-philippe@linaro.org>
In preparation for sharing some ASIDs with the CPU, use a global xarray to
store ASIDs and their context. ASID#0 is now reserved, and the ASID
space is global.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
---
drivers/iommu/arm-smmu-v3.c | 27 ++++++++++++++++++---------
1 file changed, 18 insertions(+), 9 deletions(-)
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index b5467e3e9250..847c7de0a93f 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -667,7 +667,6 @@ struct arm_smmu_device {
#define ARM_SMMU_MAX_ASIDS (1 << 16)
unsigned int asid_bits;
- DECLARE_BITMAP(asid_map, ARM_SMMU_MAX_ASIDS);
#define ARM_SMMU_MAX_VMIDS (1 << 16)
unsigned int vmid_bits;
@@ -727,6 +726,8 @@ struct arm_smmu_option_prop {
const char *prop;
};
+static DEFINE_XARRAY_ALLOC1(asid_xa);
+
static struct arm_smmu_option_prop arm_smmu_options[] = {
{ ARM_SMMU_OPT_SKIP_PREFETCH, "hisilicon,broken-prefetch-cmd" },
{ ARM_SMMU_OPT_PAGE0_REGS_ONLY, "cavium,cn9900-broken-page1-regspace"},
@@ -1765,6 +1766,14 @@ static void arm_smmu_free_cd_tables(struct arm_smmu_domain *smmu_domain)
cdcfg->cdtab = NULL;
}
+static void arm_smmu_free_asid(struct arm_smmu_ctx_desc *cd)
+{
+ if (!cd->asid)
+ return;
+
+ xa_erase(&asid_xa, cd->asid);
+}
+
/* Stream table manipulation functions */
static void
arm_smmu_write_strtab_l1_desc(__le64 *dst, struct arm_smmu_strtab_l1_desc *desc)
@@ -2450,10 +2459,9 @@ static void arm_smmu_domain_free(struct iommu_domain *domain)
if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
- if (cfg->cdcfg.cdtab) {
+ if (cfg->cdcfg.cdtab)
arm_smmu_free_cd_tables(smmu_domain);
- arm_smmu_bitmap_free(smmu->asid_map, cfg->cd.asid);
- }
+ arm_smmu_free_asid(&cfg->cd);
} else {
struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
if (cfg->vmid)
@@ -2468,14 +2476,15 @@ static int arm_smmu_domain_finalise_s1(struct arm_smmu_domain *smmu_domain,
struct io_pgtable_cfg *pgtbl_cfg)
{
int ret;
- int asid;
+ u32 asid;
struct arm_smmu_device *smmu = smmu_domain->smmu;
struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
typeof(&pgtbl_cfg->arm_lpae_s1_cfg.tcr) tcr = &pgtbl_cfg->arm_lpae_s1_cfg.tcr;
- asid = arm_smmu_bitmap_alloc(smmu->asid_map, smmu->asid_bits);
- if (asid < 0)
- return asid;
+ ret = xa_alloc(&asid_xa, &asid, &cfg->cd,
+ XA_LIMIT(1, (1 << smmu->asid_bits) - 1), GFP_KERNEL);
+ if (ret)
+ return ret;
cfg->s1cdmax = master->ssid_bits;
@@ -2508,7 +2517,7 @@ static int arm_smmu_domain_finalise_s1(struct arm_smmu_domain *smmu_domain,
out_free_cd_tables:
arm_smmu_free_cd_tables(smmu_domain);
out_free_asid:
- arm_smmu_bitmap_free(smmu->asid_map, asid);
+ arm_smmu_free_asid(&cfg->cd);
return ret;
}
--
2.26.2
^ permalink raw reply related
* [PATCH v7 07/24] iommu/io-pgtable-arm: Move some definitions to a header
From: Jean-Philippe Brucker @ 2020-05-19 17:54 UTC (permalink / raw)
To: iommu, devicetree, linux-arm-kernel, linux-pci, linux-mm
Cc: joro, catalin.marinas, will, robin.murphy, kevin.tian, baolu.lu,
Jonathan.Cameron, jacob.jun.pan, christian.koenig, felix.kuehling,
zhangfei.gao, jgg, xuzaibo, fenghua.yu, hch,
Jean-Philippe Brucker
In-Reply-To: <20200519175502.2504091-1-jean-philippe@linaro.org>
Extract some of the most generic TCR defines, so they can be reused by
the page table sharing code.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
---
drivers/iommu/io-pgtable-arm.h | 30 ++++++++++++++++++++++++++++++
drivers/iommu/io-pgtable-arm.c | 27 ++-------------------------
MAINTAINERS | 3 +--
3 files changed, 33 insertions(+), 27 deletions(-)
create mode 100644 drivers/iommu/io-pgtable-arm.h
diff --git a/drivers/iommu/io-pgtable-arm.h b/drivers/iommu/io-pgtable-arm.h
new file mode 100644
index 000000000000..ba7cfdf7afa0
--- /dev/null
+++ b/drivers/iommu/io-pgtable-arm.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef IO_PGTABLE_ARM_H_
+#define IO_PGTABLE_ARM_H_
+
+#define ARM_LPAE_TCR_TG0_4K 0
+#define ARM_LPAE_TCR_TG0_64K 1
+#define ARM_LPAE_TCR_TG0_16K 2
+
+#define ARM_LPAE_TCR_TG1_16K 1
+#define ARM_LPAE_TCR_TG1_4K 2
+#define ARM_LPAE_TCR_TG1_64K 3
+
+#define ARM_LPAE_TCR_SH_NS 0
+#define ARM_LPAE_TCR_SH_OS 2
+#define ARM_LPAE_TCR_SH_IS 3
+
+#define ARM_LPAE_TCR_RGN_NC 0
+#define ARM_LPAE_TCR_RGN_WBWA 1
+#define ARM_LPAE_TCR_RGN_WT 2
+#define ARM_LPAE_TCR_RGN_WB 3
+
+#define ARM_LPAE_TCR_PS_32_BIT 0x0ULL
+#define ARM_LPAE_TCR_PS_36_BIT 0x1ULL
+#define ARM_LPAE_TCR_PS_40_BIT 0x2ULL
+#define ARM_LPAE_TCR_PS_42_BIT 0x3ULL
+#define ARM_LPAE_TCR_PS_44_BIT 0x4ULL
+#define ARM_LPAE_TCR_PS_48_BIT 0x5ULL
+#define ARM_LPAE_TCR_PS_52_BIT 0x6ULL
+
+#endif /* IO_PGTABLE_ARM_H_ */
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index 04fbd4bf0ff9..f71a2eade04a 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -20,6 +20,8 @@
#include <asm/barrier.h>
+#include "io-pgtable-arm.h"
+
#define ARM_LPAE_MAX_ADDR_BITS 52
#define ARM_LPAE_S2_MAX_CONCAT_PAGES 16
#define ARM_LPAE_MAX_LEVELS 4
@@ -100,23 +102,6 @@
#define ARM_LPAE_PTE_MEMATTR_DEV (((arm_lpae_iopte)0x1) << 2)
/* Register bits */
-#define ARM_LPAE_TCR_TG0_4K 0
-#define ARM_LPAE_TCR_TG0_64K 1
-#define ARM_LPAE_TCR_TG0_16K 2
-
-#define ARM_LPAE_TCR_TG1_16K 1
-#define ARM_LPAE_TCR_TG1_4K 2
-#define ARM_LPAE_TCR_TG1_64K 3
-
-#define ARM_LPAE_TCR_SH_NS 0
-#define ARM_LPAE_TCR_SH_OS 2
-#define ARM_LPAE_TCR_SH_IS 3
-
-#define ARM_LPAE_TCR_RGN_NC 0
-#define ARM_LPAE_TCR_RGN_WBWA 1
-#define ARM_LPAE_TCR_RGN_WT 2
-#define ARM_LPAE_TCR_RGN_WB 3
-
#define ARM_LPAE_VTCR_SL0_MASK 0x3
#define ARM_LPAE_TCR_T0SZ_SHIFT 0
@@ -124,14 +109,6 @@
#define ARM_LPAE_VTCR_PS_SHIFT 16
#define ARM_LPAE_VTCR_PS_MASK 0x7
-#define ARM_LPAE_TCR_PS_32_BIT 0x0ULL
-#define ARM_LPAE_TCR_PS_36_BIT 0x1ULL
-#define ARM_LPAE_TCR_PS_40_BIT 0x2ULL
-#define ARM_LPAE_TCR_PS_42_BIT 0x3ULL
-#define ARM_LPAE_TCR_PS_44_BIT 0x4ULL
-#define ARM_LPAE_TCR_PS_48_BIT 0x5ULL
-#define ARM_LPAE_TCR_PS_52_BIT 0x6ULL
-
#define ARM_LPAE_MAIR_ATTR_SHIFT(n) ((n) << 3)
#define ARM_LPAE_MAIR_ATTR_MASK 0xff
#define ARM_LPAE_MAIR_ATTR_DEVICE 0x04
diff --git a/MAINTAINERS b/MAINTAINERS
index ecc0749810b0..4ff7b9a5bb7d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1463,8 +1463,7 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: Documentation/devicetree/bindings/iommu/arm,smmu*
F: drivers/iommu/arm-smmu*
-F: drivers/iommu/io-pgtable-arm-v7s.c
-F: drivers/iommu/io-pgtable-arm.c
+F: drivers/iommu/io-pgtable-arm*
ARM SUB-ARCHITECTURES
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
--
2.26.2
^ permalink raw reply related
* [PATCH v7 06/24] arm64: mm: Pin down ASIDs for sharing mm with devices
From: Jean-Philippe Brucker @ 2020-05-19 17:54 UTC (permalink / raw)
To: iommu, devicetree, linux-arm-kernel, linux-pci, linux-mm
Cc: joro, catalin.marinas, will, robin.murphy, kevin.tian, baolu.lu,
Jonathan.Cameron, jacob.jun.pan, christian.koenig, felix.kuehling,
zhangfei.gao, jgg, xuzaibo, fenghua.yu, hch,
Jean-Philippe Brucker
In-Reply-To: <20200519175502.2504091-1-jean-philippe@linaro.org>
To enable address space sharing with the IOMMU, introduce mm_context_get()
and mm_context_put(), that pin down a context and ensure that it will keep
its ASID after a rollover. Export the symbols to let the modular SMMUv3
driver use them.
Pinning is necessary because a device constantly needs a valid ASID,
unlike tasks that only require one when running. Without pinning, we would
need to notify the IOMMU when we're about to use a new ASID for a task,
and it would get complicated when a new task is assigned a shared ASID.
Consider the following scenario with no ASID pinned:
1. Task t1 is running on CPUx with shared ASID (gen=1, asid=1)
2. Task t2 is scheduled on CPUx, gets ASID (1, 2)
3. Task tn is scheduled on CPUy, a rollover occurs, tn gets ASID (2, 1)
We would now have to immediately generate a new ASID for t1, notify
the IOMMU, and finally enable task tn. We are holding the lock during
all that time, since we can't afford having another CPU trigger a
rollover. The IOMMU issues invalidation commands that can take tens of
milliseconds.
It gets needlessly complicated. All we wanted to do was schedule task tn,
that has no business with the IOMMU. By letting the IOMMU pin tasks when
needed, we avoid stalling the slow path, and let the pinning fail when
we're out of shareable ASIDs.
After a rollover, the allocator expects at least one ASID to be available
in addition to the reserved ones (one per CPU). So (NR_ASIDS - NR_CPUS -
1) is the maximum number of ASIDs that can be shared with the IOMMU.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
---
arch/arm64/include/asm/mmu.h | 1 +
arch/arm64/include/asm/mmu_context.h | 11 +++-
arch/arm64/mm/context.c | 95 +++++++++++++++++++++++++++-
3 files changed, 104 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h
index 68140fdd89d6..bbdd291e31d5 100644
--- a/arch/arm64/include/asm/mmu.h
+++ b/arch/arm64/include/asm/mmu.h
@@ -19,6 +19,7 @@
typedef struct {
atomic64_t id;
+ unsigned long pinned;
void *vdso;
unsigned long flags;
} mm_context_t;
diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h
index ab46187c6300..69599a64945b 100644
--- a/arch/arm64/include/asm/mmu_context.h
+++ b/arch/arm64/include/asm/mmu_context.h
@@ -177,7 +177,13 @@ static inline void cpu_replace_ttbr1(pgd_t *pgdp)
#define destroy_context(mm) do { } while(0)
void check_and_switch_context(struct mm_struct *mm, unsigned int cpu);
-#define init_new_context(tsk,mm) ({ atomic64_set(&(mm)->context.id, 0); 0; })
+static inline int
+init_new_context(struct task_struct *tsk, struct mm_struct *mm)
+{
+ atomic64_set(&mm->context.id, 0);
+ mm->context.pinned = 0;
+ return 0;
+}
#ifdef CONFIG_ARM64_SW_TTBR0_PAN
static inline void update_saved_ttbr0(struct task_struct *tsk,
@@ -250,6 +256,9 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next,
void verify_cpu_asid_bits(void);
void post_ttbr_update_workaround(void);
+unsigned long mm_context_get(struct mm_struct *mm);
+void mm_context_put(struct mm_struct *mm);
+
#endif /* !__ASSEMBLY__ */
#endif /* !__ASM_MMU_CONTEXT_H */
diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c
index d702d60e64da..d0ddd413f564 100644
--- a/arch/arm64/mm/context.c
+++ b/arch/arm64/mm/context.c
@@ -27,6 +27,10 @@ static DEFINE_PER_CPU(atomic64_t, active_asids);
static DEFINE_PER_CPU(u64, reserved_asids);
static cpumask_t tlb_flush_pending;
+static unsigned long max_pinned_asids;
+static unsigned long nr_pinned_asids;
+static unsigned long *pinned_asid_map;
+
#define ASID_MASK (~GENMASK(asid_bits - 1, 0))
#define ASID_FIRST_VERSION (1UL << asid_bits)
@@ -74,6 +78,9 @@ void verify_cpu_asid_bits(void)
static void set_kpti_asid_bits(void)
{
+ unsigned int k;
+ u8 *dst = (u8 *)asid_map;
+ u8 *src = (u8 *)pinned_asid_map;
unsigned int len = BITS_TO_LONGS(NUM_USER_ASIDS) * sizeof(unsigned long);
/*
* In case of KPTI kernel/user ASIDs are allocated in
@@ -81,7 +88,8 @@ static void set_kpti_asid_bits(void)
* is set, then the ASID will map only userspace. Thus
* mark even as reserved for kernel.
*/
- memset(asid_map, 0xaa, len);
+ for (k = 0; k < len; k++)
+ dst[k] = src[k] | 0xaa;
}
static void set_reserved_asid_bits(void)
@@ -89,7 +97,7 @@ static void set_reserved_asid_bits(void)
if (arm64_kernel_unmapped_at_el0())
set_kpti_asid_bits();
else
- bitmap_clear(asid_map, 0, NUM_USER_ASIDS);
+ bitmap_copy(asid_map, pinned_asid_map, NUM_USER_ASIDS);
}
#define asid_gen_match(asid) \
@@ -165,6 +173,14 @@ static u64 new_context(struct mm_struct *mm)
if (check_update_reserved_asid(asid, newasid))
return newasid;
+ /*
+ * If it is pinned, we can keep using it. Note that reserved
+ * takes priority, because even if it is also pinned, we need to
+ * update the generation into the reserved_asids.
+ */
+ if (mm->context.pinned)
+ return newasid;
+
/*
* We had a valid ASID in a previous life, so try to re-use
* it if possible.
@@ -254,6 +270,68 @@ void check_and_switch_context(struct mm_struct *mm, unsigned int cpu)
cpu_switch_mm(mm->pgd, mm);
}
+unsigned long mm_context_get(struct mm_struct *mm)
+{
+ unsigned long flags;
+ u64 asid;
+
+ raw_spin_lock_irqsave(&cpu_asid_lock, flags);
+
+ asid = atomic64_read(&mm->context.id);
+
+ if (mm->context.pinned) {
+ mm->context.pinned++;
+ asid &= ~ASID_MASK;
+ goto out_unlock;
+ }
+
+ if (nr_pinned_asids >= max_pinned_asids) {
+ asid = 0;
+ goto out_unlock;
+ }
+
+ if (!asid_gen_match(asid)) {
+ /*
+ * We went through one or more rollover since that ASID was
+ * used. Ensure that it is still valid, or generate a new one.
+ */
+ asid = new_context(mm);
+ atomic64_set(&mm->context.id, asid);
+ }
+
+ asid &= ~ASID_MASK;
+
+ nr_pinned_asids++;
+ __set_bit(asid2idx(asid), pinned_asid_map);
+ mm->context.pinned++;
+
+out_unlock:
+ raw_spin_unlock_irqrestore(&cpu_asid_lock, flags);
+
+ /* Set the equivalent of USER_ASID_BIT */
+ if (asid && IS_ENABLED(CONFIG_UNMAP_KERNEL_AT_EL0))
+ asid |= 1;
+
+ return asid;
+}
+EXPORT_SYMBOL_GPL(mm_context_get);
+
+void mm_context_put(struct mm_struct *mm)
+{
+ unsigned long flags;
+ u64 asid = atomic64_read(&mm->context.id) & ~ASID_MASK;
+
+ raw_spin_lock_irqsave(&cpu_asid_lock, flags);
+
+ if (--mm->context.pinned == 0) {
+ __clear_bit(asid2idx(asid), pinned_asid_map);
+ nr_pinned_asids--;
+ }
+
+ raw_spin_unlock_irqrestore(&cpu_asid_lock, flags);
+}
+EXPORT_SYMBOL_GPL(mm_context_put);
+
/* Errata workaround post TTBRx_EL1 update. */
asmlinkage void post_ttbr_update_workaround(void)
{
@@ -303,6 +381,13 @@ static int asids_update_limit(void)
WARN_ON(num_available_asids - 1 <= num_possible_cpus());
pr_info("ASID allocator initialised with %lu entries\n",
num_available_asids);
+
+ /*
+ * We assume that an ASID is always available after a rollover. This
+ * means that even if all CPUs have a reserved ASID, there still is at
+ * least one slot available in the asid map.
+ */
+ max_pinned_asids = num_available_asids - num_possible_cpus() - 2;
return 0;
}
arch_initcall(asids_update_limit);
@@ -317,6 +402,12 @@ static int asids_init(void)
panic("Failed to allocate bitmap for %lu ASIDs\n",
NUM_USER_ASIDS);
+ pinned_asid_map = kcalloc(BITS_TO_LONGS(NUM_USER_ASIDS),
+ sizeof(*pinned_asid_map), GFP_KERNEL);
+ if (!pinned_asid_map)
+ panic("Failed to allocate pinned ASID bitmap\n");
+ nr_pinned_asids = 0;
+
/*
* We cannot call set_reserved_asid_bits() here because CPU
* caps are not finalized yet, so it is safer to assume KPTI
--
2.26.2
^ permalink raw reply related
* [PATCH v7 05/24] arm64: mm: Add asid_gen_match() helper
From: Jean-Philippe Brucker @ 2020-05-19 17:54 UTC (permalink / raw)
To: iommu, devicetree, linux-arm-kernel, linux-pci, linux-mm
Cc: joro, catalin.marinas, will, robin.murphy, kevin.tian, baolu.lu,
Jonathan.Cameron, jacob.jun.pan, christian.koenig, felix.kuehling,
zhangfei.gao, jgg, xuzaibo, fenghua.yu, hch,
Jean-Philippe Brucker
In-Reply-To: <20200519175502.2504091-1-jean-philippe@linaro.org>
Add a macro to check if an ASID is from the current generation, since a
subsequent patch will introduce a third user for this test.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
---
arch/arm64/mm/context.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c
index 9b26f9a88724..d702d60e64da 100644
--- a/arch/arm64/mm/context.c
+++ b/arch/arm64/mm/context.c
@@ -92,6 +92,9 @@ static void set_reserved_asid_bits(void)
bitmap_clear(asid_map, 0, NUM_USER_ASIDS);
}
+#define asid_gen_match(asid) \
+ (!(((asid) ^ atomic64_read(&asid_generation)) >> asid_bits))
+
static void flush_context(void)
{
int i;
@@ -220,8 +223,7 @@ void check_and_switch_context(struct mm_struct *mm, unsigned int cpu)
* because atomic RmWs are totally ordered for a given location.
*/
old_active_asid = atomic64_read(&per_cpu(active_asids, cpu));
- if (old_active_asid &&
- !((asid ^ atomic64_read(&asid_generation)) >> asid_bits) &&
+ if (old_active_asid && asid_gen_match(asid) &&
atomic64_cmpxchg_relaxed(&per_cpu(active_asids, cpu),
old_active_asid, asid))
goto switch_mm_fastpath;
@@ -229,7 +231,7 @@ void check_and_switch_context(struct mm_struct *mm, unsigned int cpu)
raw_spin_lock_irqsave(&cpu_asid_lock, flags);
/* Check that our ASID belongs to the current generation. */
asid = atomic64_read(&mm->context.id);
- if ((asid ^ atomic64_read(&asid_generation)) >> asid_bits) {
+ if (!asid_gen_match(asid)) {
asid = new_context(mm);
atomic64_set(&mm->context.id, asid);
}
--
2.26.2
^ permalink raw reply related
* [PATCH v7 04/24] iommu: Add a page fault handler
From: Jean-Philippe Brucker @ 2020-05-19 17:54 UTC (permalink / raw)
To: iommu, devicetree, linux-arm-kernel, linux-pci, linux-mm
Cc: joro, catalin.marinas, will, robin.murphy, kevin.tian, baolu.lu,
Jonathan.Cameron, jacob.jun.pan, christian.koenig, felix.kuehling,
zhangfei.gao, jgg, xuzaibo, fenghua.yu, hch,
Jean-Philippe Brucker
In-Reply-To: <20200519175502.2504091-1-jean-philippe@linaro.org>
Some systems allow devices to handle I/O Page Faults in the core mm. For
example systems implementing the PCIe PRI extension or Arm SMMU stall
model. Infrastructure for reporting these recoverable page faults was
added to the IOMMU core by commit 0c830e6b3282 ("iommu: Introduce device
fault report API"). Add a page fault handler for host SVA.
IOMMU driver can now instantiate several fault workqueues and link them
to IOPF-capable devices. Drivers can choose between a single global
workqueue, one per IOMMU device, one per low-level fault queue, one per
domain, etc.
When it receives a fault event, supposedly in an IRQ handler, the IOMMU
driver reports the fault using iommu_report_device_fault(), which calls
the registered handler. The page fault handler then calls the mm fault
handler, and reports either success or failure with iommu_page_response().
When the handler succeeded, the IOMMU retries the access.
The iopf_param pointer could be embedded into iommu_fault_param. But
putting iopf_param into the iommu_param structure allows us not to care
about ordering between calls to iopf_queue_add_device() and
iommu_register_device_fault_handler().
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
---
v6->v7: Fix leak in iopf_queue_discard_partial()
---
drivers/iommu/Kconfig | 4 +
drivers/iommu/Makefile | 1 +
include/linux/iommu.h | 51 +++++
drivers/iommu/io-pgfault.c | 459 +++++++++++++++++++++++++++++++++++++
4 files changed, 515 insertions(+)
create mode 100644 drivers/iommu/io-pgfault.c
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index d9fa5b410015..15e9dc4e503c 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -107,6 +107,10 @@ config IOMMU_SVA
bool
select IOASID
+config IOMMU_PAGE_FAULT
+ bool
+ select IOMMU_SVA
+
config FSL_PAMU
bool "Freescale IOMMU support"
depends on PCI
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 40c800dd4e3e..bf5cb4ee8409 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_IOMMU_API) += iommu-traces.o
obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o
obj-$(CONFIG_IOMMU_DEBUGFS) += iommu-debugfs.o
obj-$(CONFIG_IOMMU_DMA) += dma-iommu.o
+obj-$(CONFIG_IOMMU_PAGE_FAULT) += io-pgfault.o
obj-$(CONFIG_IOMMU_IO_PGTABLE) += io-pgtable.o
obj-$(CONFIG_IOMMU_IO_PGTABLE_ARMV7S) += io-pgtable-arm-v7s.o
obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index b62525747bd9..a462157c855b 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -46,6 +46,7 @@ struct iommu_domain;
struct notifier_block;
struct iommu_sva;
struct iommu_fault_event;
+struct iopf_queue;
/* iommu fault flags */
#define IOMMU_FAULT_READ 0x0
@@ -347,6 +348,7 @@ struct iommu_fault_param {
* struct dev_iommu - Collection of per-device IOMMU data
*
* @fault_param: IOMMU detected device fault reporting data
+ * @iopf_param: I/O Page Fault queue and data
* @fwspec: IOMMU fwspec data
* @priv: IOMMU Driver private data
*
@@ -356,6 +358,7 @@ struct iommu_fault_param {
struct dev_iommu {
struct mutex lock;
struct iommu_fault_param *fault_param;
+ struct iopf_device_param *iopf_param;
struct iommu_fwspec *fwspec;
void *priv;
};
@@ -1067,4 +1070,52 @@ void iommu_debugfs_setup(void);
static inline void iommu_debugfs_setup(void) {}
#endif
+#ifdef CONFIG_IOMMU_PAGE_FAULT
+extern int iommu_queue_iopf(struct iommu_fault *fault, void *cookie);
+
+extern int iopf_queue_add_device(struct iopf_queue *queue, struct device *dev);
+extern int iopf_queue_remove_device(struct iopf_queue *queue,
+ struct device *dev);
+extern int iopf_queue_flush_dev(struct device *dev);
+extern struct iopf_queue *iopf_queue_alloc(const char *name);
+extern void iopf_queue_free(struct iopf_queue *queue);
+extern int iopf_queue_discard_partial(struct iopf_queue *queue);
+#else /* CONFIG_IOMMU_PAGE_FAULT */
+static inline int iommu_queue_iopf(struct iommu_fault *fault, void *cookie)
+{
+ return -ENODEV;
+}
+
+static inline int iopf_queue_add_device(struct iopf_queue *queue,
+ struct device *dev)
+{
+ return -ENODEV;
+}
+
+static inline int iopf_queue_remove_device(struct iopf_queue *queue,
+ struct device *dev)
+{
+ return -ENODEV;
+}
+
+static inline int iopf_queue_flush_dev(struct device *dev)
+{
+ return -ENODEV;
+}
+
+static inline struct iopf_queue *iopf_queue_alloc(const char *name)
+{
+ return NULL;
+}
+
+static inline void iopf_queue_free(struct iopf_queue *queue)
+{
+}
+
+static inline int iopf_queue_discard_partial(struct iopf_queue *queue)
+{
+ return -ENODEV;
+}
+#endif /* CONFIG_IOMMU_PAGE_FAULT */
+
#endif /* __LINUX_IOMMU_H */
diff --git a/drivers/iommu/io-pgfault.c b/drivers/iommu/io-pgfault.c
new file mode 100644
index 000000000000..1f61c1bc05da
--- /dev/null
+++ b/drivers/iommu/io-pgfault.c
@@ -0,0 +1,459 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Handle device page faults
+ *
+ * Copyright (C) 2020 ARM Ltd.
+ */
+
+#include <linux/iommu.h>
+#include <linux/list.h>
+#include <linux/sched/mm.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include "iommu-sva.h"
+
+/**
+ * struct iopf_queue - IO Page Fault queue
+ * @wq: the fault workqueue
+ * @devices: devices attached to this queue
+ * @lock: protects the device list
+ */
+struct iopf_queue {
+ struct workqueue_struct *wq;
+ struct list_head devices;
+ struct mutex lock;
+};
+
+/**
+ * struct iopf_device_param - IO Page Fault data attached to a device
+ * @dev: the device that owns this param
+ * @queue: IOPF queue
+ * @queue_list: index into queue->devices
+ * @partial: faults that are part of a Page Request Group for which the last
+ * request hasn't been submitted yet.
+ */
+struct iopf_device_param {
+ struct device *dev;
+ struct iopf_queue *queue;
+ struct list_head queue_list;
+ struct list_head partial;
+};
+
+struct iopf_fault {
+ struct iommu_fault fault;
+ struct list_head list;
+};
+
+struct iopf_group {
+ struct iopf_fault last_fault;
+ struct list_head faults;
+ struct work_struct work;
+ struct device *dev;
+};
+
+static int iopf_complete_group(struct device *dev, struct iopf_fault *iopf,
+ enum iommu_page_response_code status)
+{
+ struct iommu_page_response resp = {
+ .version = IOMMU_PAGE_RESP_VERSION_1,
+ .pasid = iopf->fault.prm.pasid,
+ .grpid = iopf->fault.prm.grpid,
+ .code = status,
+ };
+
+ if (iopf->fault.prm.flags & IOMMU_FAULT_PAGE_REQUEST_PASID_VALID)
+ resp.flags = IOMMU_PAGE_RESP_PASID_VALID;
+
+ return iommu_page_response(dev, &resp);
+}
+
+static enum iommu_page_response_code
+iopf_handle_single(struct iopf_fault *iopf)
+{
+ vm_fault_t ret;
+ struct mm_struct *mm;
+ struct vm_area_struct *vma;
+ unsigned int access_flags = 0;
+ unsigned int fault_flags = FAULT_FLAG_REMOTE;
+ struct iommu_fault_page_request *prm = &iopf->fault.prm;
+ enum iommu_page_response_code status = IOMMU_PAGE_RESP_INVALID;
+
+ if (!(prm->flags & IOMMU_FAULT_PAGE_REQUEST_PASID_VALID))
+ return status;
+
+ mm = iommu_sva_find(prm->pasid);
+ if (IS_ERR_OR_NULL(mm))
+ return status;
+
+ down_read(&mm->mmap_sem);
+
+ vma = find_extend_vma(mm, prm->addr);
+ if (!vma)
+ /* Unmapped area */
+ goto out_put_mm;
+
+ if (prm->perm & IOMMU_FAULT_PERM_READ)
+ access_flags |= VM_READ;
+
+ if (prm->perm & IOMMU_FAULT_PERM_WRITE) {
+ access_flags |= VM_WRITE;
+ fault_flags |= FAULT_FLAG_WRITE;
+ }
+
+ if (prm->perm & IOMMU_FAULT_PERM_EXEC) {
+ access_flags |= VM_EXEC;
+ fault_flags |= FAULT_FLAG_INSTRUCTION;
+ }
+
+ if (!(prm->perm & IOMMU_FAULT_PERM_PRIV))
+ fault_flags |= FAULT_FLAG_USER;
+
+ if (access_flags & ~vma->vm_flags)
+ /* Access fault */
+ goto out_put_mm;
+
+ ret = handle_mm_fault(vma, prm->addr, fault_flags);
+ status = ret & VM_FAULT_ERROR ? IOMMU_PAGE_RESP_INVALID :
+ IOMMU_PAGE_RESP_SUCCESS;
+
+out_put_mm:
+ up_read(&mm->mmap_sem);
+ mmput(mm);
+
+ return status;
+}
+
+static void iopf_handle_group(struct work_struct *work)
+{
+ struct iopf_group *group;
+ struct iopf_fault *iopf, *next;
+ enum iommu_page_response_code status = IOMMU_PAGE_RESP_SUCCESS;
+
+ group = container_of(work, struct iopf_group, work);
+
+ list_for_each_entry_safe(iopf, next, &group->faults, list) {
+ /*
+ * For the moment, errors are sticky: don't handle subsequent
+ * faults in the group if there is an error.
+ */
+ if (status == IOMMU_PAGE_RESP_SUCCESS)
+ status = iopf_handle_single(iopf);
+
+ if (!(iopf->fault.prm.flags &
+ IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE))
+ kfree(iopf);
+ }
+
+ iopf_complete_group(group->dev, &group->last_fault, status);
+ kfree(group);
+}
+
+/**
+ * iommu_queue_iopf - IO Page Fault handler
+ * @evt: fault event
+ * @cookie: struct device, passed to iommu_register_device_fault_handler.
+ *
+ * Add a fault to the device workqueue, to be handled by mm.
+ *
+ * This module doesn't handle PCI PASID Stop Marker; IOMMU drivers must discard
+ * them before reporting faults. A PASID Stop Marker (LRW = 0b100) doesn't
+ * expect a response. It may be generated when disabling a PASID (issuing a
+ * PASID stop request) by some PCI devices.
+ *
+ * The PASID stop request is issued by the device driver before unbind(). Once
+ * it completes, no page request is generated for this PASID anymore and
+ * outstanding ones have been pushed to the IOMMU (as per PCIe 4.0r1.0 - 6.20.1
+ * and 10.4.1.2 - Managing PASID TLP Prefix Usage). Some PCI devices will wait
+ * for all outstanding page requests to come back with a response before
+ * completing the PASID stop request. Others do not wait for page responses, and
+ * instead issue this Stop Marker that tells us when the PASID can be
+ * reallocated.
+ *
+ * It is safe to discard the Stop Marker because it is an optimization.
+ * a. Page requests, which are posted requests, have been flushed to the IOMMU
+ * when the stop request completes.
+ * b. We flush all fault queues on unbind() before freeing the PASID.
+ *
+ * So even though the Stop Marker might be issued by the device *after* the stop
+ * request completes, outstanding faults will have been dealt with by the time
+ * we free the PASID.
+ *
+ * Return: 0 on success and <0 on error.
+ */
+int iommu_queue_iopf(struct iommu_fault *fault, void *cookie)
+{
+ int ret;
+ struct iopf_group *group;
+ struct iopf_fault *iopf, *next;
+ struct iopf_device_param *iopf_param;
+
+ struct device *dev = cookie;
+ struct dev_iommu *param = dev->iommu;
+
+ lockdep_assert_held(¶m->lock);
+
+ if (fault->type != IOMMU_FAULT_PAGE_REQ)
+ /* Not a recoverable page fault */
+ return -EOPNOTSUPP;
+
+ /*
+ * As long as we're holding param->lock, the queue can't be unlinked
+ * from the device and therefore cannot disappear.
+ */
+ iopf_param = param->iopf_param;
+ if (!iopf_param)
+ return -ENODEV;
+
+ if (!(fault->prm.flags & IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE)) {
+ iopf = kzalloc(sizeof(*iopf), GFP_KERNEL);
+ if (!iopf)
+ return -ENOMEM;
+
+ iopf->fault = *fault;
+
+ /* Non-last request of a group. Postpone until the last one */
+ list_add(&iopf->list, &iopf_param->partial);
+
+ return 0;
+ }
+
+ group = kzalloc(sizeof(*group), GFP_KERNEL);
+ if (!group) {
+ /*
+ * The caller will send a response to the hardware. But we do
+ * need to clean up before leaving, otherwise partial faults
+ * will be stuck.
+ */
+ ret = -ENOMEM;
+ goto cleanup_partial;
+ }
+
+ group->dev = dev;
+ group->last_fault.fault = *fault;
+ INIT_LIST_HEAD(&group->faults);
+ list_add(&group->last_fault.list, &group->faults);
+ INIT_WORK(&group->work, iopf_handle_group);
+
+ /* See if we have partial faults for this group */
+ list_for_each_entry_safe(iopf, next, &iopf_param->partial, list) {
+ if (iopf->fault.prm.grpid == fault->prm.grpid)
+ /* Insert *before* the last fault */
+ list_move(&iopf->list, &group->faults);
+ }
+
+ queue_work(iopf_param->queue->wq, &group->work);
+ return 0;
+
+cleanup_partial:
+ list_for_each_entry_safe(iopf, next, &iopf_param->partial, list) {
+ if (iopf->fault.prm.grpid == fault->prm.grpid) {
+ list_del(&iopf->list);
+ kfree(iopf);
+ }
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iommu_queue_iopf);
+
+/**
+ * iopf_queue_flush_dev - Ensure that all queued faults have been processed
+ * @dev: the endpoint whose faults need to be flushed.
+ *
+ * The IOMMU driver calls this before releasing a PASID, to ensure that all
+ * pending faults for this PASID have been handled, and won't hit the address
+ * space of the next process that uses this PASID. The driver must make sure
+ * that no new fault is added to the queue. In particular it must flush its
+ * low-level queue before calling this function.
+ *
+ * Return: 0 on success and <0 on error.
+ */
+int iopf_queue_flush_dev(struct device *dev)
+{
+ int ret = 0;
+ struct iopf_device_param *iopf_param;
+ struct dev_iommu *param = dev->iommu;
+
+ if (!param)
+ return -ENODEV;
+
+ mutex_lock(¶m->lock);
+ iopf_param = param->iopf_param;
+ if (iopf_param)
+ flush_workqueue(iopf_param->queue->wq);
+ else
+ ret = -ENODEV;
+ mutex_unlock(¶m->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iopf_queue_flush_dev);
+
+/**
+ * iopf_queue_discard_partial - Remove all pending partial fault
+ * @queue: the queue whose partial faults need to be discarded
+ *
+ * When the hardware queue overflows, last page faults in a group may have been
+ * lost and the IOMMU driver calls this to discard all partial faults. The
+ * driver shouldn't be adding new faults to this queue concurrently.
+ *
+ * Return: 0 on success and <0 on error.
+ */
+int iopf_queue_discard_partial(struct iopf_queue *queue)
+{
+ struct iopf_fault *iopf, *next;
+ struct iopf_device_param *iopf_param;
+
+ if (!queue)
+ return -EINVAL;
+
+ mutex_lock(&queue->lock);
+ list_for_each_entry(iopf_param, &queue->devices, queue_list) {
+ list_for_each_entry_safe(iopf, next, &iopf_param->partial,
+ list) {
+ list_del(&iopf->list);
+ kfree(iopf);
+ }
+ }
+ mutex_unlock(&queue->lock);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iopf_queue_discard_partial);
+
+/**
+ * iopf_queue_add_device - Add producer to the fault queue
+ * @queue: IOPF queue
+ * @dev: device to add
+ *
+ * Return: 0 on success and <0 on error.
+ */
+int iopf_queue_add_device(struct iopf_queue *queue, struct device *dev)
+{
+ int ret = -EBUSY;
+ struct iopf_device_param *iopf_param;
+ struct dev_iommu *param = dev->iommu;
+
+ if (!param)
+ return -ENODEV;
+
+ iopf_param = kzalloc(sizeof(*iopf_param), GFP_KERNEL);
+ if (!iopf_param)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&iopf_param->partial);
+ iopf_param->queue = queue; iopf_param->dev = dev;
+
+ mutex_lock(&queue->lock);
+ mutex_lock(¶m->lock);
+ if (!param->iopf_param) {
+ list_add(&iopf_param->queue_list, &queue->devices);
+ param->iopf_param = iopf_param;
+ ret = 0;
+ }
+ mutex_unlock(¶m->lock);
+ mutex_unlock(&queue->lock);
+
+ if (ret)
+ kfree(iopf_param);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iopf_queue_add_device);
+
+/**
+ * iopf_queue_remove_device - Remove producer from fault queue
+ * @queue: IOPF queue
+ * @dev: device to remove
+ *
+ * Caller makes sure that no more faults are reported for this device.
+ *
+ * Return: 0 on success and <0 on error.
+ */
+int iopf_queue_remove_device(struct iopf_queue *queue, struct device *dev)
+{
+ int ret = 0;
+ struct iopf_fault *iopf, *next;
+ struct iopf_device_param *iopf_param;
+ struct dev_iommu *param = dev->iommu;
+
+ if (!param || !queue)
+ return -EINVAL;
+
+ mutex_lock(&queue->lock);
+ mutex_lock(¶m->lock);
+ iopf_param = param->iopf_param;
+ if (iopf_param && iopf_param->queue == queue) {
+ list_del(&iopf_param->queue_list);
+ param->iopf_param = NULL;
+ } else {
+ ret = -EINVAL;
+ }
+ mutex_unlock(¶m->lock);
+ mutex_unlock(&queue->lock);
+ if (ret)
+ return ret;
+
+ /* Just in case some faults are still stuck */
+ list_for_each_entry_safe(iopf, next, &iopf_param->partial, list)
+ kfree(iopf);
+
+ kfree(iopf_param);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iopf_queue_remove_device);
+
+/**
+ * iopf_queue_alloc - Allocate and initialize a fault queue
+ * @name: a unique string identifying the queue (for workqueue)
+ *
+ * Return: the queue on success and NULL on error.
+ */
+struct iopf_queue *iopf_queue_alloc(const char *name)
+{
+ struct iopf_queue *queue;
+
+ queue = kzalloc(sizeof(*queue), GFP_KERNEL);
+ if (!queue)
+ return NULL;
+
+ /*
+ * The WQ is unordered because the low-level handler enqueues faults by
+ * group. PRI requests within a group have to be ordered, but once
+ * that's dealt with, the high-level function can handle groups out of
+ * order.
+ */
+ queue->wq = alloc_workqueue("iopf_queue/%s", WQ_UNBOUND, 0, name);
+ if (!queue->wq) {
+ kfree(queue);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&queue->devices);
+ mutex_init(&queue->lock);
+
+ return queue;
+}
+EXPORT_SYMBOL_GPL(iopf_queue_alloc);
+
+/**
+ * iopf_queue_free - Free IOPF queue
+ * @queue: queue to free
+ *
+ * Counterpart to iopf_queue_alloc(). The driver must not be queuing faults or
+ * adding/removing devices on this queue anymore.
+ */
+void iopf_queue_free(struct iopf_queue *queue)
+{
+ struct iopf_device_param *iopf_param, *next;
+
+ if (!queue)
+ return;
+
+ list_for_each_entry_safe(iopf_param, next, &queue->devices, queue_list)
+ iopf_queue_remove_device(queue, iopf_param->dev);
+
+ destroy_workqueue(queue->wq);
+ kfree(queue);
+}
+EXPORT_SYMBOL_GPL(iopf_queue_free);
--
2.26.2
^ permalink raw reply related
* [PATCH v7 02/24] iommu/ioasid: Add ioasid references
From: Jean-Philippe Brucker @ 2020-05-19 17:54 UTC (permalink / raw)
To: iommu, devicetree, linux-arm-kernel, linux-pci, linux-mm
Cc: joro, catalin.marinas, will, robin.murphy, kevin.tian, baolu.lu,
Jonathan.Cameron, jacob.jun.pan, christian.koenig, felix.kuehling,
zhangfei.gao, jgg, xuzaibo, fenghua.yu, hch,
Jean-Philippe Brucker
In-Reply-To: <20200519175502.2504091-1-jean-philippe@linaro.org>
Let IOASID users take references to existing ioasids with ioasid_get().
ioasid_put() drops a reference and only frees the ioasid when its
reference number is zero. It returns true if the ioasid was freed.
For drivers that don't call ioasid_get(), ioasid_put() is the same as
ioasid_free().
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
---
v6->v7: rename ioasid_free() to ioasid_put(), add WARN in ioasid_get()
---
include/linux/ioasid.h | 10 ++++++++--
drivers/iommu/intel-iommu.c | 4 ++--
drivers/iommu/intel-svm.c | 6 +++---
drivers/iommu/ioasid.c | 38 +++++++++++++++++++++++++++++++++----
4 files changed, 47 insertions(+), 11 deletions(-)
diff --git a/include/linux/ioasid.h b/include/linux/ioasid.h
index 6f000d7a0ddc..e9dacd4b9f6b 100644
--- a/include/linux/ioasid.h
+++ b/include/linux/ioasid.h
@@ -34,7 +34,8 @@ struct ioasid_allocator_ops {
#if IS_ENABLED(CONFIG_IOASID)
ioasid_t ioasid_alloc(struct ioasid_set *set, ioasid_t min, ioasid_t max,
void *private);
-void ioasid_free(ioasid_t ioasid);
+void ioasid_get(ioasid_t ioasid);
+bool ioasid_put(ioasid_t ioasid);
void *ioasid_find(struct ioasid_set *set, ioasid_t ioasid,
bool (*getter)(void *));
int ioasid_register_allocator(struct ioasid_allocator_ops *allocator);
@@ -48,10 +49,15 @@ static inline ioasid_t ioasid_alloc(struct ioasid_set *set, ioasid_t min,
return INVALID_IOASID;
}
-static inline void ioasid_free(ioasid_t ioasid)
+static inline void ioasid_get(ioasid_t ioasid)
{
}
+static inline bool ioasid_put(ioasid_t ioasid)
+{
+ return false;
+}
+
static inline void *ioasid_find(struct ioasid_set *set, ioasid_t ioasid,
bool (*getter)(void *))
{
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index ed21ce6d1238..0230f35480ee 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -5432,7 +5432,7 @@ static void auxiliary_unlink_device(struct dmar_domain *domain,
domain->auxd_refcnt--;
if (!domain->auxd_refcnt && domain->default_pasid > 0)
- ioasid_free(domain->default_pasid);
+ ioasid_put(domain->default_pasid);
}
static int aux_domain_add_dev(struct dmar_domain *domain,
@@ -5494,7 +5494,7 @@ static int aux_domain_add_dev(struct dmar_domain *domain,
spin_unlock(&iommu->lock);
spin_unlock_irqrestore(&device_domain_lock, flags);
if (!domain->auxd_refcnt && domain->default_pasid > 0)
- ioasid_free(domain->default_pasid);
+ ioasid_put(domain->default_pasid);
return ret;
}
diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c
index 2998418f0a38..86f1264bd07c 100644
--- a/drivers/iommu/intel-svm.c
+++ b/drivers/iommu/intel-svm.c
@@ -353,7 +353,7 @@ int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, struct svm_dev_
if (mm) {
ret = mmu_notifier_register(&svm->notifier, mm);
if (ret) {
- ioasid_free(svm->pasid);
+ ioasid_put(svm->pasid);
kfree(svm);
kfree(sdev);
goto out;
@@ -371,7 +371,7 @@ int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, struct svm_dev_
if (ret) {
if (mm)
mmu_notifier_unregister(&svm->notifier, mm);
- ioasid_free(svm->pasid);
+ ioasid_put(svm->pasid);
kfree(svm);
kfree(sdev);
goto out;
@@ -447,7 +447,7 @@ int intel_svm_unbind_mm(struct device *dev, int pasid)
kfree_rcu(sdev, rcu);
if (list_empty(&svm->devs)) {
- ioasid_free(svm->pasid);
+ ioasid_put(svm->pasid);
if (svm->mm)
mmu_notifier_unregister(&svm->notifier, svm->mm);
list_del(&svm->list);
diff --git a/drivers/iommu/ioasid.c b/drivers/iommu/ioasid.c
index 0f8dd377aada..50ee27bbd04e 100644
--- a/drivers/iommu/ioasid.c
+++ b/drivers/iommu/ioasid.c
@@ -2,7 +2,7 @@
/*
* I/O Address Space ID allocator. There is one global IOASID space, split into
* subsets. Users create a subset with DECLARE_IOASID_SET, then allocate and
- * free IOASIDs with ioasid_alloc and ioasid_free.
+ * free IOASIDs with ioasid_alloc and ioasid_put.
*/
#include <linux/ioasid.h>
#include <linux/module.h>
@@ -15,6 +15,7 @@ struct ioasid_data {
struct ioasid_set *set;
void *private;
struct rcu_head rcu;
+ refcount_t refs;
};
/*
@@ -314,6 +315,7 @@ ioasid_t ioasid_alloc(struct ioasid_set *set, ioasid_t min, ioasid_t max,
data->set = set;
data->private = private;
+ refcount_set(&data->refs, 1);
/*
* Custom allocator needs allocator data to perform platform specific
@@ -346,11 +348,34 @@ ioasid_t ioasid_alloc(struct ioasid_set *set, ioasid_t min, ioasid_t max,
EXPORT_SYMBOL_GPL(ioasid_alloc);
/**
- * ioasid_free - Free an IOASID
+ * ioasid_get - obtain a reference to the IOASID
+ */
+void ioasid_get(ioasid_t ioasid)
+{
+ struct ioasid_data *ioasid_data;
+
+ spin_lock(&ioasid_allocator_lock);
+ ioasid_data = xa_load(&active_allocator->xa, ioasid);
+ if (ioasid_data)
+ refcount_inc(&ioasid_data->refs);
+ else
+ WARN_ON(1);
+ spin_unlock(&ioasid_allocator_lock);
+}
+EXPORT_SYMBOL_GPL(ioasid_get);
+
+/**
+ * ioasid_put - Release a reference to an ioasid
* @ioasid: the ID to remove
+ *
+ * Put a reference to the IOASID, free it when the number of references drops to
+ * zero.
+ *
+ * Return: %true if the IOASID was freed, %false otherwise.
*/
-void ioasid_free(ioasid_t ioasid)
+bool ioasid_put(ioasid_t ioasid)
{
+ bool free = false;
struct ioasid_data *ioasid_data;
spin_lock(&ioasid_allocator_lock);
@@ -360,6 +385,10 @@ void ioasid_free(ioasid_t ioasid)
goto exit_unlock;
}
+ free = refcount_dec_and_test(&ioasid_data->refs);
+ if (!free)
+ goto exit_unlock;
+
active_allocator->ops->free(ioasid, active_allocator->ops->pdata);
/* Custom allocator needs additional steps to free the xa element */
if (active_allocator->flags & IOASID_ALLOCATOR_CUSTOM) {
@@ -369,8 +398,9 @@ void ioasid_free(ioasid_t ioasid)
exit_unlock:
spin_unlock(&ioasid_allocator_lock);
+ return free;
}
-EXPORT_SYMBOL_GPL(ioasid_free);
+EXPORT_SYMBOL_GPL(ioasid_put);
/**
* ioasid_find - Find IOASID data
--
2.26.2
^ permalink raw reply related
* [PATCH v7 03/24] iommu/sva: Add PASID helpers
From: Jean-Philippe Brucker @ 2020-05-19 17:54 UTC (permalink / raw)
To: iommu, devicetree, linux-arm-kernel, linux-pci, linux-mm
Cc: joro, catalin.marinas, will, robin.murphy, kevin.tian, baolu.lu,
Jonathan.Cameron, jacob.jun.pan, christian.koenig, felix.kuehling,
zhangfei.gao, jgg, xuzaibo, fenghua.yu, hch,
Jean-Philippe Brucker
In-Reply-To: <20200519175502.2504091-1-jean-philippe@linaro.org>
Let IOMMU drivers allocate a single PASID per mm. Store the mm in the
IOASID set to allow refcounting and searching mm by PASID, when handling
an I/O page fault.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
---
drivers/iommu/Kconfig | 5 +++
drivers/iommu/Makefile | 1 +
drivers/iommu/iommu-sva.h | 15 +++++++
drivers/iommu/iommu-sva.c | 85 +++++++++++++++++++++++++++++++++++++++
4 files changed, 106 insertions(+)
create mode 100644 drivers/iommu/iommu-sva.h
create mode 100644 drivers/iommu/iommu-sva.c
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 2ab07ce17abb..d9fa5b410015 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -102,6 +102,11 @@ config IOMMU_DMA
select IRQ_MSI_IOMMU
select NEED_SG_DMA_LENGTH
+# Shared Virtual Addressing library
+config IOMMU_SVA
+ bool
+ select IOASID
+
config FSL_PAMU
bool "Freescale IOMMU support"
depends on PCI
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 9f33fdb3bb05..40c800dd4e3e 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -37,3 +37,4 @@ obj-$(CONFIG_S390_IOMMU) += s390-iommu.o
obj-$(CONFIG_QCOM_IOMMU) += qcom_iommu.o
obj-$(CONFIG_HYPERV_IOMMU) += hyperv-iommu.o
obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o
+obj-$(CONFIG_IOMMU_SVA) += iommu-sva.o
diff --git a/drivers/iommu/iommu-sva.h b/drivers/iommu/iommu-sva.h
new file mode 100644
index 000000000000..78f806fcacbe
--- /dev/null
+++ b/drivers/iommu/iommu-sva.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * SVA library for IOMMU drivers
+ */
+#ifndef _IOMMU_SVA_H
+#define _IOMMU_SVA_H
+
+#include <linux/ioasid.h>
+#include <linux/mm_types.h>
+
+int iommu_sva_alloc_pasid(struct mm_struct *mm, ioasid_t min, ioasid_t max);
+void iommu_sva_free_pasid(struct mm_struct *mm);
+struct mm_struct *iommu_sva_find(ioasid_t pasid);
+
+#endif /* _IOMMU_SVA_H */
diff --git a/drivers/iommu/iommu-sva.c b/drivers/iommu/iommu-sva.c
new file mode 100644
index 000000000000..442644a1ade0
--- /dev/null
+++ b/drivers/iommu/iommu-sva.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Helpers for IOMMU drivers implementing SVA
+ */
+#include <linux/mutex.h>
+#include <linux/sched/mm.h>
+
+#include "iommu-sva.h"
+
+static DEFINE_MUTEX(iommu_sva_lock);
+static DECLARE_IOASID_SET(shared_pasid);
+
+/**
+ * iommu_sva_alloc_pasid - Allocate a PASID for the mm
+ * @mm: the mm
+ * @min: minimum PASID value (inclusive)
+ * @max: maximum PASID value (inclusive)
+ *
+ * Try to allocate a PASID for this mm, or take a reference to the existing one
+ * provided it fits within the [min, max] range. On success the PASID is
+ * available in mm->pasid, and must be released with iommu_sva_free_pasid().
+ *
+ * Returns 0 on success and < 0 on error.
+ */
+int iommu_sva_alloc_pasid(struct mm_struct *mm, ioasid_t min, ioasid_t max)
+{
+ int ret = 0;
+ ioasid_t pasid;
+
+ if (min == INVALID_IOASID || max == INVALID_IOASID ||
+ min == 0 || max < min)
+ return -EINVAL;
+
+ mutex_lock(&iommu_sva_lock);
+ if (mm->pasid) {
+ if (mm->pasid >= min && mm->pasid <= max)
+ ioasid_get(mm->pasid);
+ else
+ ret = -EOVERFLOW;
+ } else {
+ pasid = ioasid_alloc(&shared_pasid, min, max, mm);
+ if (pasid == INVALID_IOASID)
+ ret = -ENOMEM;
+ else
+ mm->pasid = pasid;
+ }
+ mutex_unlock(&iommu_sva_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iommu_sva_alloc_pasid);
+
+/**
+ * iommu_sva_free_pasid - Release the mm's PASID
+ * @mm: the mm.
+ *
+ * Drop one reference to a PASID allocated with iommu_sva_alloc_pasid()
+ */
+void iommu_sva_free_pasid(struct mm_struct *mm)
+{
+ mutex_lock(&iommu_sva_lock);
+ if (ioasid_put(mm->pasid))
+ mm->pasid = 0;
+ mutex_unlock(&iommu_sva_lock);
+}
+EXPORT_SYMBOL_GPL(iommu_sva_free_pasid);
+
+/* ioasid wants a void * argument */
+static bool __mmget_not_zero(void *mm)
+{
+ return mmget_not_zero(mm);
+}
+
+/**
+ * iommu_sva_find() - Find mm associated to the given PASID
+ * @pasid: Process Address Space ID assigned to the mm
+ *
+ * On success a reference to the mm is taken, and must be released with mmput().
+ *
+ * Returns the mm corresponding to this PASID, or an error if not found.
+ */
+struct mm_struct *iommu_sva_find(ioasid_t pasid)
+{
+ return ioasid_find(&shared_pasid, pasid, __mmget_not_zero);
+}
+EXPORT_SYMBOL_GPL(iommu_sva_find);
--
2.26.2
^ permalink raw reply related
* [PATCH v7 01/24] mm: Add a PASID field to mm_struct
From: Jean-Philippe Brucker @ 2020-05-19 17:54 UTC (permalink / raw)
To: iommu, devicetree, linux-arm-kernel, linux-pci, linux-mm
Cc: joro, catalin.marinas, will, robin.murphy, kevin.tian, baolu.lu,
Jonathan.Cameron, jacob.jun.pan, christian.koenig, felix.kuehling,
zhangfei.gao, jgg, xuzaibo, fenghua.yu, hch,
Jean-Philippe Brucker
In-Reply-To: <20200519175502.2504091-1-jean-philippe@linaro.org>
Some devices can tag their DMA requests with a 20-bit Process Address
Space ID (PASID), allowing them to access multiple address spaces. In
combination with recoverable I/O page faults (for example PCIe PRI),
PASID allows the IOMMU to share page tables with the MMU.
To make sure that a single PASID is allocated for each address space, as
required by Intel ENQCMD, store the PASID in the mm_struct. The IOMMU
driver is in charge of serializing modifications to the PASID field.
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
---
include/linux/mm_types.h | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 4aba6c0c2ba8..8db647275817 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -534,6 +534,10 @@ struct mm_struct {
atomic_long_t hugetlb_usage;
#endif
struct work_struct async_put_work;
+#ifdef CONFIG_IOMMU_SUPPORT
+ /* Address space ID used by device DMA */
+ unsigned int pasid;
+#endif
} __randomize_layout;
/*
--
2.26.2
^ permalink raw reply related
* [PATCH v7 00/24] iommu: Shared Virtual Addressing for SMMUv3
From: Jean-Philippe Brucker @ 2020-05-19 17:54 UTC (permalink / raw)
To: iommu, devicetree, linux-arm-kernel, linux-pci, linux-mm
Cc: joro, catalin.marinas, will, robin.murphy, kevin.tian, baolu.lu,
Jonathan.Cameron, jacob.jun.pan, christian.koenig, felix.kuehling,
zhangfei.gao, jgg, xuzaibo, fenghua.yu, hch,
Jean-Philippe Brucker
Shared Virtual Addressing (SVA) allows to share process page tables with
devices using the IOMMU, PASIDs and I/O page faults. Add SVA support to
the Arm SMMUv3 driver.
Since v6 [1]:
* Rename ioasid_free() to ioasid_put() in patch 02, requiring changes to
the Intel drivers.
* Use mmu_notifier_register() in patch 16 to avoid copying the ops and
simplify the invalidate() notifier in patch 17.
* As a result, replace context spinlock with a mutex. Simplified locking in
patch 11 (That patch still looks awful, but I think the series is more
readable overall). And I've finally been able to remove the GFP_ATOMIC
allocations.
* Use a single patch (04) for io-pgfault.c, since the code was simplified
in v6. Fixed partial list in patch 04.
[1] https://lore.kernel.org/linux-iommu/20200430143424.2787566-1-jean-philippe@linaro.org/
Jean-Philippe Brucker (24):
mm: Add a PASID field to mm_struct
iommu/ioasid: Add ioasid references
iommu/sva: Add PASID helpers
iommu: Add a page fault handler
arm64: mm: Add asid_gen_match() helper
arm64: mm: Pin down ASIDs for sharing mm with devices
iommu/io-pgtable-arm: Move some definitions to a header
iommu/arm-smmu-v3: Manage ASIDs with xarray
arm64: cpufeature: Export symbol read_sanitised_ftr_reg()
iommu/arm-smmu-v3: Share process page tables
iommu/arm-smmu-v3: Seize private ASID
iommu/arm-smmu-v3: Add support for VHE
iommu/arm-smmu-v3: Enable broadcast TLB maintenance
iommu/arm-smmu-v3: Add SVA feature checking
iommu/arm-smmu-v3: Add SVA device feature
iommu/arm-smmu-v3: Implement iommu_sva_bind/unbind()
iommu/arm-smmu-v3: Hook up ATC invalidation to mm ops
iommu/arm-smmu-v3: Add support for Hardware Translation Table Update
iommu/arm-smmu-v3: Maintain a SID->device structure
dt-bindings: document stall property for IOMMU masters
iommu/arm-smmu-v3: Add stall support for platform devices
PCI/ATS: Add PRI stubs
PCI/ATS: Export PRI functions
iommu/arm-smmu-v3: Add support for PRI
drivers/iommu/Kconfig | 12 +
drivers/iommu/Makefile | 2 +
.../devicetree/bindings/iommu/iommu.txt | 18 +
arch/arm64/include/asm/mmu.h | 1 +
arch/arm64/include/asm/mmu_context.h | 11 +-
drivers/iommu/io-pgtable-arm.h | 30 +
drivers/iommu/iommu-sva.h | 15 +
include/linux/ioasid.h | 10 +-
include/linux/iommu.h | 53 +
include/linux/mm_types.h | 4 +
include/linux/pci-ats.h | 8 +
arch/arm64/kernel/cpufeature.c | 1 +
arch/arm64/mm/context.c | 103 +-
drivers/iommu/arm-smmu-v3.c | 1552 +++++++++++++++--
drivers/iommu/intel-iommu.c | 4 +-
drivers/iommu/intel-svm.c | 6 +-
drivers/iommu/io-pgfault.c | 459 +++++
drivers/iommu/io-pgtable-arm.c | 27 +-
drivers/iommu/ioasid.c | 38 +-
drivers/iommu/iommu-sva.c | 85 +
drivers/iommu/of_iommu.c | 5 +-
drivers/pci/ats.c | 4 +
MAINTAINERS | 3 +-
23 files changed, 2286 insertions(+), 165 deletions(-)
create mode 100644 drivers/iommu/io-pgtable-arm.h
create mode 100644 drivers/iommu/iommu-sva.h
create mode 100644 drivers/iommu/io-pgfault.c
create mode 100644 drivers/iommu/iommu-sva.c
--
2.26.2
^ permalink raw reply
* Re: [PATCH v2 4/4] iio: imu: bmi160: added mount-matrix support
From: Jonathan Cameron @ 2020-05-19 17:57 UTC (permalink / raw)
To: Jonathan Albrieux
Cc: linux-kernel,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Hartmut Knaack, Lars-Peter Clausen,
open list:IIO SUBSYSTEM AND DRIVERS, Peter Meerwald-Stadler,
Jonathan Cameron
In-Reply-To: <20200519075111.6356-5-jonathan.albrieux@gmail.com>
On Tue, 19 May 2020 09:51:00 +0200
Jonathan Albrieux <jonathan.albrieux@gmail.com> wrote:
> Add mount-matrix binding support. As chip could have different orientations
> a mount matrix support is needed to correctly translate these differences
>
> Signed-off-by: Jonathan Albrieux <jonathan.albrieux@gmail.com>
Hi Jonathan,
Looks good to me. I'll pick this up once 1-3 are tidied up and
we have a device tree review in for the binding doc. I'm rubbish
at reviewing those as Rob will certify so may well have missed something!
Jonathan
> ---
> drivers/iio/imu/bmi160/bmi160.h | 1 +
> drivers/iio/imu/bmi160/bmi160_core.c | 20 ++++++++++++++++++++
> 2 files changed, 21 insertions(+)
>
> diff --git a/drivers/iio/imu/bmi160/bmi160.h b/drivers/iio/imu/bmi160/bmi160.h
> index 923c3b274fde..a82e040bd109 100644
> --- a/drivers/iio/imu/bmi160/bmi160.h
> +++ b/drivers/iio/imu/bmi160/bmi160.h
> @@ -9,6 +9,7 @@ struct bmi160_data {
> struct regmap *regmap;
> struct iio_trigger *trig;
> struct regulator_bulk_data supplies[2];
> + struct iio_mount_matrix orientation;
> };
>
> extern const struct regmap_config bmi160_regmap_config;
> diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c
> index 9bbe0d8e6720..78c8ca962359 100644
> --- a/drivers/iio/imu/bmi160/bmi160_core.c
> +++ b/drivers/iio/imu/bmi160/bmi160_core.c
> @@ -110,6 +110,7 @@
> .storagebits = 16, \
> .endianness = IIO_LE, \
> }, \
> + .ext_info = bmi160_ext_info, \
> }
>
> /* scan indexes follow DATA register order */
> @@ -265,6 +266,20 @@ static const struct bmi160_odr_item bmi160_odr_table[] = {
> },
> };
>
> +static const struct iio_mount_matrix *
> +bmi160_get_mount_matrix(const struct iio_dev *indio_dev,
> + const struct iio_chan_spec *chan)
> +{
> + struct bmi160_data *data = iio_priv(indio_dev);
> +
> + return &data->orientation;
> +}
> +
> +static const struct iio_chan_spec_ext_info bmi160_ext_info[] = {
> + IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, bmi160_get_mount_matrix),
> + { }
> +};
> +
> static const struct iio_chan_spec bmi160_channels[] = {
> BMI160_CHANNEL(IIO_ACCEL, X, BMI160_SCAN_ACCEL_X),
> BMI160_CHANNEL(IIO_ACCEL, Y, BMI160_SCAN_ACCEL_Y),
> @@ -840,6 +855,11 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap,
> return ret;
> }
>
> + ret = iio_read_mount_matrix(dev, "mount-matrix",
> + &data->orientation);
> + if (ret)
> + return ret;
> +
> ret = bmi160_chip_init(data, use_spi);
> if (ret)
> return ret;
^ permalink raw reply
* Re: [PATCH net-next 2/4] net: phy: dp83869: Set opmode from straps
From: Dan Murphy @ 2020-05-19 17:56 UTC (permalink / raw)
To: Jakub Kicinski
Cc: andrew, f.fainelli, hkallweit1, davem, netdev, linux-kernel,
devicetree
In-Reply-To: <65e1b5ca-680e-f82d-cde4-3d5a3eb40884@ti.com>
Jakub
On 5/19/20 12:40 PM, Dan Murphy wrote:
> Jakub
>
> On 5/19/20 11:58 AM, Jakub Kicinski wrote:
>> On Tue, 19 May 2020 09:18:11 -0500 Dan Murphy wrote:
>>> If the op-mode for the device is not set in the device tree then set
>>> the strapped op-mode and store it for later configuration.
>>>
>>> Signed-off-by: Dan Murphy <dmurphy@ti.com>
>> ../drivers/net/phy/dp83869.c: In function0 dp83869_set_strapped_mode:
>> ../drivers/net/phy/dp83869.c:171:10: warning: comparison is always
>> false due to limited range of data type [-Wtype-limits]
>> 171 | if (val < 0)
>> | ^
>
> This looks to be a false positive.
>
> phy_read_mmd will return an errno or a value from 0->15
>
> So if errno is returned then this will be true.
>
> Unless I have to do IS_ERR.
>
> I did not get that warning. But I am using 9.2-gcc.
>
> What compiler are you using?
>
I see what the issue is val needs to be an int not a u16
I will fix it
Dan
> Dan
>
^ permalink raw reply
* Re: [PATCH v2 3/4] iio: imu: bmi160: added regulator support
From: Jonathan Cameron @ 2020-05-19 17:55 UTC (permalink / raw)
To: Jonathan Albrieux
Cc: linux-kernel,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Hartmut Knaack, Lars-Peter Clausen,
open list:IIO SUBSYSTEM AND DRIVERS, Peter Meerwald-Stadler,
Jonathan Cameron
In-Reply-To: <20200519075111.6356-4-jonathan.albrieux@gmail.com>
On Tue, 19 May 2020 09:50:59 +0200
Jonathan Albrieux <jonathan.albrieux@gmail.com> wrote:
> v2: fixed missing description
Don't put change log here....
>
> Add vdd-supply and vddio-supply support. Without this support vdd and vddio
> should be set to always-on in device tree
Kind of the opposite. If they are always on we don't have to provide them
in the device tree.
A few trivial things inline.
>
> Signed-off-by: Jonathan Albrieux <jonathan.albrieux@gmail.com>
> ---
Change log goes here so we don't end up keeping it in the git log.
> drivers/iio/imu/bmi160/bmi160.h | 2 ++
> drivers/iio/imu/bmi160/bmi160_core.c | 27 ++++++++++++++++++++++++++-
> 2 files changed, 28 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/iio/imu/bmi160/bmi160.h b/drivers/iio/imu/bmi160/bmi160.h
> index 621f5309d735..923c3b274fde 100644
> --- a/drivers/iio/imu/bmi160/bmi160.h
> +++ b/drivers/iio/imu/bmi160/bmi160.h
> @@ -3,10 +3,12 @@
> #define BMI160_H_
>
> #include <linux/iio/iio.h>
> +#include <linux/regulator/consumer.h>
>
> struct bmi160_data {
> struct regmap *regmap;
> struct iio_trigger *trig;
> + struct regulator_bulk_data supplies[2];
> };
>
> extern const struct regmap_config bmi160_regmap_config;
> diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c
> index 6af65d6f1d28..9bbe0d8e6720 100644
> --- a/drivers/iio/imu/bmi160/bmi160_core.c
> +++ b/drivers/iio/imu/bmi160/bmi160_core.c
> @@ -15,6 +15,7 @@
> #include <linux/delay.h>
> #include <linux/irq.h>
> #include <linux/of_irq.h>
> +#include <linux/regulator/consumer.h>
>
> #include <linux/iio/iio.h>
> #include <linux/iio/triggered_buffer.h>
> @@ -709,6 +710,12 @@ static int bmi160_chip_init(struct bmi160_data *data, bool use_spi)
> unsigned int val;
> struct device *dev = regmap_get_device(data->regmap);
>
> + ret = regulator_bulk_enable(ARRAY_SIZE(data->supplies), data->supplies);
> + if (ret) {
> + dev_err(dev, "Failed to enable regulators: %d\n", ret);
> + return ret;
> + }
> +
> ret = regmap_write(data->regmap, BMI160_REG_CMD, BMI160_CMD_SOFTRESET);
> if (ret)
> return ret;
> @@ -793,9 +800,17 @@ int bmi160_probe_trigger(struct iio_dev *indio_dev, int irq, u32 irq_type)
> static void bmi160_chip_uninit(void *data)
> {
> struct bmi160_data *bmi_data = data;
> + struct device *dev = regmap_get_device(bmi_data->regmap);
> + int ret;
>
> bmi160_set_mode(bmi_data, BMI160_GYRO, false);
> bmi160_set_mode(bmi_data, BMI160_ACCEL, false);
> +
> + ret = regulator_bulk_disable(ARRAY_SIZE(bmi_data->supplies),
> + bmi_data->supplies);
> + if (ret) {
> + dev_err(dev, "Failed to disable regulators: %d\n", ret);
> + }
No need for brackets around a 1 line if block
if (ret)
dev_err(dev, "failed to disable regulators: %d\n", ret);
> }
>
> int bmi160_core_probe(struct device *dev, struct regmap *regmap,
> @@ -815,6 +830,16 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap,
> dev_set_drvdata(dev, indio_dev);
> data->regmap = regmap;
>
> + data->supplies[0].supply = "vdd";
> + data->supplies[1].supply = "vddio";
> + ret = devm_regulator_bulk_get(dev,
> + ARRAY_SIZE(data->supplies),
> + data->supplies);
> + if (ret) {
> + dev_err(dev, "Failed to get regulators: %d\n", ret);
> + return ret;
> + }
> +
> ret = bmi160_chip_init(data, use_spi);
> if (ret)
> return ret;
> @@ -853,6 +878,6 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap,
> }
> EXPORT_SYMBOL_GPL(bmi160_core_probe);
>
> -MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com");
> +MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
Good fix but shouldn't be in this patch. Put it a separate patch on it's own.
> MODULE_DESCRIPTION("Bosch BMI160 driver");
> MODULE_LICENSE("GPL v2");
^ permalink raw reply
* Re: [PATCH v2 2/4] dt-bindings: iio: imu: bmi160: add regulators and mount-matrix
From: Jonathan Cameron @ 2020-05-19 17:51 UTC (permalink / raw)
To: Jonathan Albrieux
Cc: linux-kernel,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Hartmut Knaack, Lars-Peter Clausen,
open list:IIO SUBSYSTEM AND DRIVERS, Peter Meerwald-Stadler,
Jonathan Cameron, Rob Herring
In-Reply-To: <20200519075111.6356-3-jonathan.albrieux@gmail.com>
On Tue, 19 May 2020 09:50:58 +0200
Jonathan Albrieux <jonathan.albrieux@gmail.com> wrote:
> Add vdd-supply and vddio-supply support.
> Add mount-matrix support.
>
> Signed-off-by: Jonathan Albrieux <jonathan.albrieux@gmail.com>
A few minor comments inline.
> ---
> .../devicetree/bindings/iio/imu/bmi160.yaml | 21 +++++++++++++++++++
> 1 file changed, 21 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/iio/imu/bmi160.yaml b/Documentation/devicetree/bindings/iio/imu/bmi160.yaml
> index 6b464ce5ed0b..5b13af7a209f 100644
> --- a/Documentation/devicetree/bindings/iio/imu/bmi160.yaml
> +++ b/Documentation/devicetree/bindings/iio/imu/bmi160.yaml
> @@ -46,6 +46,21 @@ properties:
> set if the specified interrupt pin should be configured as
> open drain. If not set, defaults to push-pull.
>
> + vdd-supply:
> + maxItems: 1
> + description: |
> + an optional regulator that needs to be on to provide VDD power to
> + the sensor.
They aren't optional. Whether we specify them or rely on stub regulators
being provided because they aren't controllable is the optional bit.
That's clearly defined by them not being in the required list below.
So say something li.e
description: |
provide VDD power to the sensor.
> +
> + vddio-supply:
> + maxItems: 1
> + description: |
> + an optional regulator that needs to be on to provide the VDD IO power to
> + the sensor.
> +
> + mount-matrix:
> + description: an optional 3x3 mounting rotation matrix
> +
> required:
> - compatible
> - reg
> @@ -61,9 +76,15 @@ examples:
> bmi160@68 {
> compatible = "bosch,bmi160";
> reg = <0x68>;
> + vdd-supply = <&pm8916_l17>;
> + vddio-supply = <&pm8916_l6>;
> interrupt-parent = <&gpio4>;
> interrupts = <12 1>;
> interrupt-names = "INT1";
> + mount-matrix = "0", "1", "0",
> + "-1", "0", "0",
> + "0", "0", "1";
> + };
> };
> - |
> // Example for SPI
^ permalink raw reply
* Re: [PATCH v2 1/4] dt-bindings: iio: imu: bmi160: convert txt format to yaml
From: Jonathan Cameron @ 2020-05-19 17:49 UTC (permalink / raw)
To: Jonathan Albrieux
Cc: linux-kernel,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Hartmut Knaack, Lars-Peter Clausen,
open list:IIO SUBSYSTEM AND DRIVERS, Peter Meerwald-Stadler,
Jonathan Cameron, Rob Herring, Daniel Baluta
In-Reply-To: <20200519075111.6356-2-jonathan.albrieux@gmail.com>
On Tue, 19 May 2020 09:50:57 +0200
Jonathan Albrieux <jonathan.albrieux@gmail.com> wrote:
> Converts documentation from txt format to yaml
>
> Signed-off-by: Jonathan Albrieux <jonathan.albrieux@gmail.com>
> ---
> .../devicetree/bindings/iio/imu/bmi160.txt | 37 --------
> .../devicetree/bindings/iio/imu/bmi160.yaml | 84 +++++++++++++++++++
> 2 files changed, 84 insertions(+), 37 deletions(-)
> delete mode 100644 Documentation/devicetree/bindings/iio/imu/bmi160.txt
> create mode 100644 Documentation/devicetree/bindings/iio/imu/bmi160.yaml
>
> diff --git a/Documentation/devicetree/bindings/iio/imu/bmi160.txt b/Documentation/devicetree/bindings/iio/imu/bmi160.txt
> deleted file mode 100644
> index 900c169de00f..000000000000
> --- a/Documentation/devicetree/bindings/iio/imu/bmi160.txt
> +++ /dev/null
> @@ -1,37 +0,0 @@
> -Bosch BMI160 - Inertial Measurement Unit with Accelerometer, Gyroscope
> -and externally connectable Magnetometer
> -
> -https://www.bosch-sensortec.com/bst/products/all_products/bmi160
> -
> -Required properties:
> - - compatible : should be "bosch,bmi160"
> - - reg : the I2C address or SPI chip select number of the sensor
> - - spi-max-frequency : set maximum clock frequency (only for SPI)
> -
> -Optional properties:
> - - interrupts : interrupt mapping for IRQ
> - - interrupt-names : set to "INT1" if INT1 pin should be used as interrupt
> - input, set to "INT2" if INT2 pin should be used instead
> - - drive-open-drain : set if the specified interrupt pin should be configured as
> - open drain. If not set, defaults to push-pull.
> -
> -Examples:
> -
> -bmi160@68 {
> - compatible = "bosch,bmi160";
> - reg = <0x68>;
> -
> - interrupt-parent = <&gpio4>;
> - interrupts = <12 IRQ_TYPE_EDGE_RISING>;
> - interrupt-names = "INT1";
> -};
> -
> -bmi160@0 {
> - compatible = "bosch,bmi160";
> - reg = <0>;
> - spi-max-frequency = <10000000>;
> -
> - interrupt-parent = <&gpio2>;
> - interrupts = <12 IRQ_TYPE_LEVEL_LOW>;
> - interrupt-names = "INT2";
> -};
> diff --git a/Documentation/devicetree/bindings/iio/imu/bmi160.yaml b/Documentation/devicetree/bindings/iio/imu/bmi160.yaml
> new file mode 100644
> index 000000000000..6b464ce5ed0b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/imu/bmi160.yaml
> @@ -0,0 +1,84 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/imu/bmi160.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Bosch BMI160
> +
> +maintainers:
> + - can't find a mantainer, author is Daniel Baluta <daniel.baluta@intel.com>
Daniel is still active in the kernel, just not at Intel any more. +CC
> +
> +description: |
> + Inertial Measurement Unit with Accelerometer, Gyroscope and externally
> + connectable Magnetometer
> + https://www.bosch-sensortec.com/bst/products/all_products/bmi160
> +
> +properties:
> + compatible:
> + const: bosch,bmi160
> +
> + reg:
> + maxItems: 1
> + description: the I2C address or SPI chip select number of the sensor
As standard for i2c and spi, usually no need to have a description line for
this element.
> +
> + spi-max-frequency:
> + maxItems: 1
> + description: set maximum clock frequency (required only for SPI)
Standard spi binding. Probably doesn't need to be included here.
> +
> + interrupts:
> + maxItems: 1
> + description: interrupt mapping for IRQ
> +
> + interrupt-names:
> + minItems: 1
> + maxItems: 1
> + items:
> + enum:
> + - INT1
> + - INT2
> + description: |
> + set to "INT1" if INT1 pin should be used as interrupt input, set
> + to "INT2" if INT2 pin should be used instead
> +
> + drive-open-drain:
> + description: |
> + set if the specified interrupt pin should be configured as
> + open drain. If not set, defaults to push-pull.
> +
> +required:
> + - compatible
> + - reg
> +
> +examples:
> + - |
> + // Example for I2C
> + i2c@78b7000 {
> + reg = <0x78b6000 0x600>;
> + #address-cells = <1>;
> + #size-cells = <0>;
Take a look at some of the other binding examples. We normally
just focus on the driver so don't supply details for the bus.
e.g. https://elixir.bootlin.com/linux/v5.7-rc6/source/Documentation/devicetree/bindings/iio/adc/maxim,max1363.yaml#L39
> +
> + bmi160@68 {
> + compatible = "bosch,bmi160";
> + reg = <0x68>;
> + interrupt-parent = <&gpio4>;
> + interrupts = <12 1>;
> + interrupt-names = "INT1";
> + };
> + - |
> + // Example for SPI
> + spi@78b7000 {
> + reg = <0x78b7000 0x600>,
> + <0x7884000 0x23000>;
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + bmi160@0 {
> + compatible = "bosch,bmi160";
> + reg = <0>;
> + spi-max-frequency = <10000000>;
> + interrupt-parent = <&gpio2>;
> + interrupts = <12 1>;
> + interrupt-names = "INT2";
> + };
> + };
^ permalink raw reply
* Re: [PATCH v8 3/6] clocksource: Ingenic: Add high resolution timer support for SMP.
From: Paul Cercueil @ 2020-05-19 17:42 UTC (permalink / raw)
To: 周琰杰
Cc: linux-mips, linux-kernel, devicetree, tsbogend, paulburton,
jiaxun.yang, chenhc, tglx, robh+dt, daniel.lezcano, keescook,
krzk, hns, ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu,
sernia.zhou, zhenwenjin
In-Reply-To: <1589898923-60048-5-git-send-email-zhouyanjie@wanyeetech.com>
Hi Zhou,
Le mar. 19 mai 2020 à 22:35, 周琰杰 (Zhou Yanjie)
<zhouyanjie@wanyeetech.com> a écrit :
> Enable clock event handling on per CPU core basis.
> Make sure that interrupts raised on the first core execute
> event handlers on the correct CPU core.
>
> Tested-by: H. Nikolaus Schaller <hns@goldelico.com>
> Tested-by: Paul Boddie <paul@boddie.org.uk>
> Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
> ---
>
> Notes:
> v1->v2:
> 1.Adjust function naming to make it more reasonable.
> 2.Replace function smp_call_function_single() with
> smp_call_function_single_async() in order to resolve
> the warning below:
>
> [ 0.350942] smp: Brought up 1 node, 2 CPUs
> [ 0.365497] ------------[ cut here ]------------
> [ 0.365522] WARNING: CPU: 0 PID: 1 at kernel/smp.c:300
> smp_call_function_single+0x110/0x200
> [ 0.365533] CPU: 0 PID: 1 Comm: swapper/0 Not tainted
> 5.5.0-rc1+ #5
> [ 0.365537] Stack : 00000000 59c73bcd 00000037 80074e80
> 80000000 80670000 805a0000 80620590
> [ 0.365557] 8065ce38 8fc0dc8c 806d0000 00000000
> 80670000 00000001 8fc0dc20 59c73bcd
> [ 0.365574] 00000000 00000000 806f0000 80670000
> 00000000 806dab00 00000000 2d302e35
> [ 0.365591] 203a6d6d 806e0000 806e0000 70617773
> 80670000 00000000 00000000 00000009
> [ 0.365610] 00000000 8fc94e20 8fc0de30 80690000
> 00000018 803592dc 00000000 806d0000
> [ 0.365627] ...
> [ 0.365634] Call Trace:
> [ 0.365647] [<8001b9a0>] show_stack+0x6c/0x12c
> [ 0.365663] [<804aed20>] dump_stack+0x98/0xc8
> [ 0.365673] [<8003044c>] __warn+0xc4/0xe8
> [ 0.365682] [<800304f4>] warn_slowpath_fmt+0x84/0xb8
> [ 0.365690] [<800a886c>] smp_call_function_single+0x110/0x200
> [ 0.365703] ---[ end trace 5785856ca39c79d5 ]---
> [ 0.365557] 8065ce38 8fc0dc8c 806d0000 00000000
> 80670000 00000001 8fc0dc20 59c73bcd
> [ 0.365574] 00000000 00000000 806f0000 80670000
> 00000000 806dab00 00000000 2d302e35
> [ 0.365591] 203a6d6d 806e0000 806e0000 70617773
> 80670000 00000000 00000000 00000009
> [ 0.365610] 00000000 8fc94e20 8fc0de30 80690000
> 00000018 803592dc 00000000 806d0000
> [ 0.365627] ...
> [ 0.365634] Call Trace:
> [ 0.365647] [<8001b9a0>] show_stack+0x6c/0x12c
> [ 0.365663] [<804aed20>] dump_stack+0x98/0xc8
> [ 0.365673] [<8003044c>] __warn+0xc4/0xe8
> [ 0.365682] [<800304f4>] warn_slowpath_fmt+0x84/0xb8
> [ 0.365690] [<800a886c>] smp_call_function_single+0x110/0x200
> [ 0.365703] ---[ end trace 5785856ca39c79d5 ]---
>
> v2->v3:
> No Change.
>
> v3->v4:
> Rebase on top of kernel 5.6-rc1.
>
> v4->v5:
> Move the check for (evt->event_handler) from
> "ingenic_per_cpu_event_handler"
> to "ingenic_tcu_cevt_cb".
>
> v5->v6:
> No change.
>
> v6->v7:
> Remove unnecessary check for "NR_CPUS > 1".
>
> v7->v8:
> Use "num_possible_cpus()" instead "NR_CPUS".
> Reported-by: kbuild test robot <lkp@intel.com>
>
> drivers/clocksource/ingenic-timer.c | 103
> ++++++++++++++++++++++++++++--------
> 1 file changed, 82 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/clocksource/ingenic-timer.c
> b/drivers/clocksource/ingenic-timer.c
> index 4963336..230e996 100644
> --- a/drivers/clocksource/ingenic-timer.c
> +++ b/drivers/clocksource/ingenic-timer.c
> @@ -1,7 +1,8 @@
> // SPDX-License-Identifier: GPL-2.0
> /*
> - * JZ47xx SoCs TCU IRQ driver
> + * XBurst SoCs TCU IRQ driver
If you want to get rid of the JZ47xx, then just write 'Ingenic SoCs TCU
IRQ driver', since XBurst is only the name of the CPU. Also, this
belongs to a separate patch.
> * Copyright (C) 2019 Paul Cercueil <paul@crapouillou.net>
> + * Copyright (C) 2020 周琰杰 (Zhou Yanjie)
> <zhouyanjie@wanyeetech.com>
> */
>
> #include <linux/bitops.h>
> @@ -21,18 +22,23 @@
>
> #include <dt-bindings/clock/ingenic,tcu.h>
>
> +static DEFINE_PER_CPU(call_single_data_t, ingenic_cevt_csd);
> +
> struct ingenic_soc_info {
> unsigned int num_channels;
> };
>
> struct ingenic_tcu {
> struct regmap *map;
> + struct device_node *np;
> struct clk *timer_clk, *cs_clk;
> + unsigned int timer_local[NR_CPUS];
NR_CPUS can be very big, so this would make the struct ingenic_tcu very
large. What you could do, is have a variable array at the end of the
struct:
unsigned int timer_local[];
Then you can create the ingenic_tcu struct using struct_size() from
<linux/overflow.h>:
tcu = kzalloc(struct_size(tcu, timer_local, num_possible_cpus()),
GFP_KERNEL);
-Paul
> unsigned int timer_channel, cs_channel;
> struct clock_event_device cevt;
> struct clocksource cs;
> - char name[4];
> + char name[8];
> unsigned long pwm_channels_mask;
> + int cpu;
> };
>
> static struct ingenic_tcu *ingenic_tcu;
> @@ -81,6 +87,24 @@ static int ingenic_tcu_cevt_set_next(unsigned long
> next,
> return 0;
> }
>
> +static void ingenic_per_cpu_event_handler(void *info)
> +{
> + struct clock_event_device *cevt = (struct clock_event_device *)
> info;
> +
> + cevt->event_handler(cevt);
> +}
> +
> +static void ingenic_tcu_per_cpu_cb(struct clock_event_device *evt)
> +{
> + struct ingenic_tcu *tcu = to_ingenic_tcu(evt);
> + call_single_data_t *csd;
> +
> + csd = &per_cpu(ingenic_cevt_csd, tcu->cpu);
> + csd->info = (void *) evt;
> + csd->func = ingenic_per_cpu_event_handler;
> + smp_call_function_single_async(tcu->cpu, csd);
> +}
> +
> static irqreturn_t ingenic_tcu_cevt_cb(int irq, void *dev_id)
> {
> struct clock_event_device *evt = dev_id;
> @@ -89,7 +113,7 @@ static irqreturn_t ingenic_tcu_cevt_cb(int irq,
> void *dev_id)
> regmap_write(tcu->map, TCU_REG_TECR, BIT(tcu->timer_channel));
>
> if (evt->event_handler)
> - evt->event_handler(evt);
> + ingenic_tcu_per_cpu_cb(evt);
>
> return IRQ_HANDLED;
> }
> @@ -105,14 +129,21 @@ static struct clk * __init
> ingenic_tcu_get_clock(struct device_node *np, int id)
> return of_clk_get_from_provider(&args);
> }
>
> -static int __init ingenic_tcu_timer_init(struct device_node *np,
> - struct ingenic_tcu *tcu)
> +static int ingenic_tcu_setup_per_cpu_cevt(struct device_node *np,
> + unsigned int channel)
> {
> - unsigned int timer_virq, channel = tcu->timer_channel;
> + unsigned int timer_virq;
> struct irq_domain *domain;
> + struct ingenic_tcu *tcu;
> unsigned long rate;
> int err;
>
> + tcu = kzalloc(sizeof(*tcu), GFP_KERNEL);
> + if (!tcu)
> + return -ENOMEM;
I have no idea why you're doing that, but this is really wrong and
highly confusing. The ingenic_tcu instance is already created in the
probe. If you need per-timer data, then create another structure.
-Paul
> +
> + tcu->map = ingenic_tcu->map;
> +
> tcu->timer_clk = ingenic_tcu_get_clock(np, channel);
> if (IS_ERR(tcu->timer_clk))
> return PTR_ERR(tcu->timer_clk);
> @@ -139,13 +170,15 @@ static int __init ingenic_tcu_timer_init(struct
> device_node *np,
> goto err_clk_disable;
> }
>
> - snprintf(tcu->name, sizeof(tcu->name), "TCU");
> + snprintf(tcu->name, sizeof(tcu->name), "TCU%u", channel);
>
> err = request_irq(timer_virq, ingenic_tcu_cevt_cb, IRQF_TIMER,
> tcu->name, &tcu->cevt);
> if (err)
> goto err_irq_dispose_mapping;
>
> + tcu->cpu = smp_processor_id();
> + tcu->timer_channel = channel;
> tcu->cevt.cpumask = cpumask_of(smp_processor_id());
> tcu->cevt.features = CLOCK_EVT_FEAT_ONESHOT;
> tcu->cevt.name = tcu->name;
> @@ -166,6 +199,25 @@ static int __init ingenic_tcu_timer_init(struct
> device_node *np,
> return err;
> }
>
> +static int ingenic_tcu_setup_cevt(unsigned int cpu)
> +{
> + int ret;
> +
> + ret = ingenic_tcu_setup_per_cpu_cevt(ingenic_tcu->np,
> + ingenic_tcu->timer_local[cpu]);
> + if (ret)
> + goto err_tcu_clocksource_cleanup;
> +
> + return 0;
> +
> +err_tcu_clocksource_cleanup:
> + clocksource_unregister(&ingenic_tcu->cs);
> + clk_disable_unprepare(ingenic_tcu->cs_clk);
> + clk_put(ingenic_tcu->cs_clk);
> + kfree(ingenic_tcu);
> + return ret;
> +}
> +
> static int __init ingenic_tcu_clocksource_init(struct device_node
> *np,
> struct ingenic_tcu *tcu)
> {
> @@ -240,6 +292,7 @@ static int __init ingenic_tcu_init(struct
> device_node *np)
> const struct ingenic_soc_info *soc_info = id->data;
> struct ingenic_tcu *tcu;
> struct regmap *map;
> + unsigned cpu = 0;
> long rate;
> int ret;
>
> @@ -253,13 +306,18 @@ static int __init ingenic_tcu_init(struct
> device_node *np)
> if (!tcu)
> return -ENOMEM;
>
> - /* Enable all TCU channels for PWM use by default except channels
> 0/1 */
> - tcu->pwm_channels_mask = GENMASK(soc_info->num_channels - 1, 2);
> + /*
> + * Enable all TCU channels for PWM use by default except channels
> 0/1,
> + * and channel 2 if target CPU is JZ4780 and SMP is selected.
> + */
> + tcu->pwm_channels_mask = GENMASK(soc_info->num_channels - 1,
> + num_possible_cpus() + 1);
> of_property_read_u32(np, "ingenic,pwm-channels-mask",
> (u32 *)&tcu->pwm_channels_mask);
>
> - /* Verify that we have at least two free channels */
> - if (hweight8(tcu->pwm_channels_mask) > soc_info->num_channels - 2) {
> + /* Verify that we have at least num_possible_cpus() + 1 free
> channels */
> + if (hweight8(tcu->pwm_channels_mask) >
> + soc_info->num_channels - num_possible_cpus() + 1) {
> pr_crit("%s: Invalid PWM channel mask: 0x%02lx\n", __func__,
> tcu->pwm_channels_mask);
> ret = -EINVAL;
> @@ -267,13 +325,19 @@ static int __init ingenic_tcu_init(struct
> device_node *np)
> }
>
> tcu->map = map;
> + tcu->np = np;
> ingenic_tcu = tcu;
>
> - tcu->timer_channel = find_first_zero_bit(&tcu->pwm_channels_mask,
> + tcu->timer_local[cpu] = find_first_zero_bit(&tcu->pwm_channels_mask,
> soc_info->num_channels);
> +
> + for (cpu = 1; cpu < num_possible_cpus(); cpu++)
> + tcu->timer_local[cpu] = find_next_zero_bit(
> + &tcu->pwm_channels_mask, soc_info->num_channels,
> + tcu->timer_local[cpu - 1] + 1);
> +
> tcu->cs_channel = find_next_zero_bit(&tcu->pwm_channels_mask,
> - soc_info->num_channels,
> - tcu->timer_channel + 1);
> + soc_info->num_channels, tcu->timer_local[cpu - 1] + 1);
>
> ret = ingenic_tcu_clocksource_init(np, tcu);
> if (ret) {
> @@ -281,9 +345,10 @@ static int __init ingenic_tcu_init(struct
> device_node *np)
> goto err_free_ingenic_tcu;
> }
>
> - ret = ingenic_tcu_timer_init(np, tcu);
> - if (ret)
> - goto err_tcu_clocksource_cleanup;
> + /* Setup clock events on each CPU core */
> + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "Ingenic XBurst:
> online",
> + ingenic_tcu_setup_cevt, NULL);
> + WARN_ON(ret < 0);
>
> /* Register the sched_clock at the end as there's no way to undo it
> */
> rate = clk_get_rate(tcu->cs_clk);
> @@ -291,10 +356,6 @@ static int __init ingenic_tcu_init(struct
> device_node *np)
>
> return 0;
>
> -err_tcu_clocksource_cleanup:
> - clocksource_unregister(&tcu->cs);
> - clk_disable_unprepare(tcu->cs_clk);
> - clk_put(tcu->cs_clk);
> err_free_ingenic_tcu:
> kfree(tcu);
> return ret;
> --
> 2.7.4
>
^ permalink raw reply
* Re: [PATCH net-next 2/4] net: phy: dp83869: Set opmode from straps
From: Dan Murphy @ 2020-05-19 17:40 UTC (permalink / raw)
To: kbuild test robot, andrew, f.fainelli, hkallweit1, davem
Cc: kbuild-all, netdev, linux-kernel, devicetree
In-Reply-To: <202005200117.iOd1QuA3%lkp@intel.com>
kbuild
On 5/19/20 12:19 PM, kbuild test robot wrote:
> Hi Dan,
>
> I love your patch! Perhaps something to improve:
>
> [auto build test WARNING on net-next/master]
> [also build test WARNING on robh/for-next sparc-next/master net/master linus/master v5.7-rc6 next-20200518]
> [if your patch is applied to the wrong git tree, please drop us a note to help
> improve the system. BTW, we also suggest to use '--base' option to specify the
> base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
>
> url: https://github.com/0day-ci/linux/commits/Dan-Murphy/DP83869-Enhancements/20200519-222047
> base: https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git 5cdfe8306631b2224e3f81fc5a1e2721c7a1948b
> config: sh-allmodconfig (attached as .config)
> compiler: sh4-linux-gcc (GCC) 9.3.0
> reproduce:
> wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
> chmod +x ~/bin/make.cross
> # save the attached .config to linux build tree
> COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=sh
>
> If you fix the issue, kindly add following tag as appropriate
> Reported-by: kbuild test robot <lkp@intel.com>
>
> All warnings (new ones prefixed by >>, old ones prefixed by <<):
>
> drivers/net/phy/dp83869.c: In function 'dp83869_set_strapped_mode':
>>> drivers/net/phy/dp83869.c:171:10: warning: comparison is always false due to limited range of data type [-Wtype-limits]
> 171 | if (val < 0)
This looks to be a false positive.
phy_read_mmd will return an errno or a value from 0->15
So if errno is returned then this will be true.
Unless I have to do IS_ERR.
Dan
^ permalink raw reply
* Re: [PATCH net-next 2/4] net: phy: dp83869: Set opmode from straps
From: Dan Murphy @ 2020-05-19 17:40 UTC (permalink / raw)
To: Jakub Kicinski
Cc: andrew, f.fainelli, hkallweit1, davem, netdev, linux-kernel,
devicetree
In-Reply-To: <20200519095818.425d227b@kicinski-fedora-pc1c0hjn.dhcp.thefacebook.com>
Jakub
On 5/19/20 11:58 AM, Jakub Kicinski wrote:
> On Tue, 19 May 2020 09:18:11 -0500 Dan Murphy wrote:
>> If the op-mode for the device is not set in the device tree then set
>> the strapped op-mode and store it for later configuration.
>>
>> Signed-off-by: Dan Murphy <dmurphy@ti.com>
> ../drivers/net/phy/dp83869.c: In function0 dp83869_set_strapped_mode:
> ../drivers/net/phy/dp83869.c:171:10: warning: comparison is always false due to limited range of data type [-Wtype-limits]
> 171 | if (val < 0)
> | ^
This looks to be a false positive.
phy_read_mmd will return an errno or a value from 0->15
So if errno is returned then this will be true.
Unless I have to do IS_ERR.
I did not get that warning. But I am using 9.2-gcc.
What compiler are you using?
Dan
^ permalink raw reply
* Re: [PATCH 4/4] sc16is7xx: Use threaded IRQ
From: Daniel Mack @ 2020-05-19 17:37 UTC (permalink / raw)
To: Maarten Brock
Cc: devicetree, linux-serial, gregkh, robh+dt, jslaby, pascal.huerst,
linux-serial-owner
In-Reply-To: <b5d56abc8109fb3a7ef057c89a649f06@vanmierlo.com>
On 5/19/20 6:32 PM, Maarten Brock wrote:
> On 2020-05-18 18:57, Daniel Mack wrote:
>> It's a controller that sits behind another hardware bus itself, so
>> polling is expensive. If the controller would need to check for level
>> IRQs it would need to poll, and then we could as well just poll the UART
>> directly, that's just as good :)
>
> That depends on the IRQ coming out of the interrupt controller. If that is
> a level interrupt itself, then it is easy to see if all interrupts are
> handled. Further polling zooms in on the devices that require attention.
Yeah, I'm familiar with the concept, but it's not like that here,
unfortunately.
>> But again - the UART driver works perfectly fine with edge IRQs as long
>> as the interrupt is not shared.
>
> If you would require multiple sc16is7xx devices on I2C would you like to
> connect multiple interrupt lines? Or just SCL,SDA and *one* IRQ?
>
> OTOH for SPI you would require multiple CS already.
Right. Nevertheless, we can allow sharing the IRQ line for level-trigger
capable IRQ controllers, you're right.
>> What many drivers do is try with one setting, and if that fails because
>> the interrupt controller returns an error, they fall back to something
>> else. We could do the same here of course, but it'd be another patch on
>> top, as it's unrelated to the concrete change the patch we're commenting
>> on is bringing in.
>>
>> So what I can add is logic that first tries with IRQF_LOW|IRQF_SHARED,
>> and if that fails, we fall back to IRQF_FALLING and retry. WDYT?
>
> That sounds like a decent plan.
Okay, I'll add a patch to the series then and resend.
Thanks for your feedback!
Daniel
^ permalink raw reply
* Re: [PATCH v2 1/4] dt-bindings: iio: imu: bmi160: convert txt format to yaml
From: Rob Herring @ 2020-05-19 17:37 UTC (permalink / raw)
To: Jonathan Albrieux
Cc: Rob Herring, open list:IIO SUBSYSTEM AND DRIVERS,
Peter Meerwald-Stadler,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
linux-kernel, Lars-Peter Clausen, Jonathan Cameron,
Hartmut Knaack
In-Reply-To: <20200519075111.6356-2-jonathan.albrieux@gmail.com>
On Tue, 19 May 2020 09:50:57 +0200, Jonathan Albrieux wrote:
> Converts documentation from txt format to yaml
>
> Signed-off-by: Jonathan Albrieux <jonathan.albrieux@gmail.com>
> ---
> .../devicetree/bindings/iio/imu/bmi160.txt | 37 --------
> .../devicetree/bindings/iio/imu/bmi160.yaml | 84 +++++++++++++++++++
> 2 files changed, 84 insertions(+), 37 deletions(-)
> delete mode 100644 Documentation/devicetree/bindings/iio/imu/bmi160.txt
> create mode 100644 Documentation/devicetree/bindings/iio/imu/bmi160.yaml
>
My bot found errors running 'make dt_binding_check' on your patch:
Error: Documentation/devicetree/bindings/iio/imu/bmi160.example.dts:37.1-2 syntax error
FATAL ERROR: Unable to parse input tree
scripts/Makefile.lib:312: recipe for target 'Documentation/devicetree/bindings/iio/imu/bmi160.example.dt.yaml' failed
make[1]: *** [Documentation/devicetree/bindings/iio/imu/bmi160.example.dt.yaml] Error 1
Makefile:1300: recipe for target 'dt_binding_check' failed
make: *** [dt_binding_check] Error 2
See https://patchwork.ozlabs.org/patch/1293085
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure dt-schema is up to date:
pip3 install git+https://github.com/devicetree-org/dt-schema.git@master --upgrade
Please check and re-submit.
^ permalink raw reply
* Re: [PATCH v3 1/4] dt-bindings: iio: magnetometer: ak8975: convert txt format to yaml
From: Jonathan Cameron @ 2020-05-19 17:37 UTC (permalink / raw)
To: Jonathan Albrieux
Cc: Andy Shevchenko, Stephan Gerhold, linux-kernel,
~postmarketos/upstreaming,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Greg Kroah-Hartman, Hartmut Knaack, Jilayne Lovejoy, Kate Stewart,
Lars-Peter Clausen, Linus Walleij,
open list:IIO SUBSYSTEM AND DRIVERS, Peter Meerwald-Stadler,
Steve Winslow, Thomas Gleixner, Jonathan Cameron, Rob Herring
In-Reply-To: <20200519164433.GA8726@ict14-OptiPlex-980>
On Tue, 19 May 2020 18:44:33 +0200
Jonathan Albrieux <jonathan.albrieux@gmail.com> wrote:
> On Tue, May 19, 2020 at 07:01:37PM +0300, Andy Shevchenko wrote:
> > On Tue, May 19, 2020 at 04:03:54PM +0200, Jonathan Albrieux wrote:
> > > On Tue, May 19, 2020 at 03:22:07PM +0200, Stephan Gerhold wrote:
> > > > On Tue, May 19, 2020 at 02:43:51PM +0200, Jonathan Albrieux wrote:
> >
> > ...
> >
> > > > > +maintainers:
> > > > > + - can't find a mantainer, author is Laxman Dewangan <ldewangan@nvidia.com>
> > > >
> > > > Should probably add someone here, although I'm not sure who either.
> > > >
> > >
> > > Yep I couldn't find a maintainer for that driver..what to do in this case?
> >
> > Volunteer yourself!
> >
>
> While I'd really like to, I have to decline the offer as I currently don't have
> enought knowledge to become a maintainer :-) but thank you! (Who knows, maybe in
> a couple of year!) Now I'll make the final edits and will submit a new
> patchset soon with all the changes
Don't be so hard on yourself. We all get thrown in at the deep end :)
Note that being a driver maintainer (or even just the binding) really
just means you get cc'd on the patches and I'll make sure you've had time
to review them if you wish. Best of all, if you have hardware (and time)
being able to test them, that is extremely useful (whether you are
maintaining the driver or not!)
I closely review the majority of stuff that comes through IIO and in
the case of bindings we also have Rob and co. doing an amazing job.
We have some excellent additional reviewers who review IIO stuff all the
time, some of which have reviewed your patch I see. Without them I'd
never survive the deluge.
Of course it's entirely your decision, but I'd definitely encourage you
to give it a go.
Thanks,
Jonathan
>
> > --
> > With Best Regards,
> > Andy Shevchenko
> >
> >
>
> Best regards,
> Jonathan Albrieux
^ permalink raw reply
* Re: [PATCH v3 1/2] dt-bindings: memory: document Renesas RPC-IF bindings
From: Rob Herring @ 2020-05-19 17:36 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: Mason Yang, Philipp Zabel, Chris Brandt, linux-spi, devicetree,
linux-mtd@lists.infradead.org, Rob Herring
In-Reply-To: <f998fe6c-c586-17cb-9343-30460ce0bf9d@cogentembedded.com>
On Mon, 18 May 2020 23:33:15 +0300, Sergei Shtylyov wrote:
> Renesas Reduced Pin Count Interface (RPC-IF) allows a SPI flash or
> HyperFlash connected to the SoC to be accessed via the external address
> space read mode or the manual mode.
>
> Document the device tree bindings for the Renesas RPC-IF found in the R-Car
> gen3 SoCs.
>
> Based on the original patch by Mason Yang <masonccyang@mxic.com.tw>.
>
> Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
>
> ---
> Changes in version 3:
> - fixed up the whitespace and added hyphens in the "compatible" prop text, also
> removed the comments;
> - specified the subnode name as "flash", and used "enum" in the subnode text;
> - fixed up the SPDX-License-Identifier: tag.
>
> Changes in version 2:
> - rewrote the bindings in YAML.
>
> Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml | 88 ++++++++++
> 1 file changed, 88 insertions(+)
>
My bot found errors running 'make dt_binding_check' on your patch:
/builds/robherring/linux-dt-review/Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml: patternProperties:flash@[0-9a-f]+$:properties:compatible: [{'enum': ['cfi-flash', 'jedec,spi-nor']}] is not of type 'object', 'boolean'
Documentation/devicetree/bindings/Makefile:12: recipe for target 'Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.example.dts' failed
make[1]: *** [Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.example.dts] Error 1
make[1]: *** Waiting for unfinished jobs....
Makefile:1300: recipe for target 'dt_binding_check' failed
make: *** [dt_binding_check] Error 2
See https://patchwork.ozlabs.org/patch/1292811
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure dt-schema is up to date:
pip3 install git+https://github.com/devicetree-org/dt-schema.git@master --upgrade
Please check and re-submit.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox