Commit 395ad89d authored by Jean-Philippe Brucker's avatar Jean-Philippe Brucker Committed by Will Deacon

iommu/arm-smmu-v3: Add stall support for platform devices

The SMMU provides a Stall model for handling page faults in platform
devices. It is similar to PCIe PRI, but doesn't require devices to have
their own translation cache. Instead, faulting transactions are parked
and the OS is given a chance to fix the page tables and retry the
transaction.

Enable stall for devices that support it (opt-in by firmware). When an
event corresponds to a translation error, call the IOMMU fault handler.
If the fault is recoverable, it will call us back to terminate or
continue the stall.

To use stall device drivers need to enable IOMMU_DEV_FEAT_IOPF, which
initializes the fault queue for the device.
Tested-by: default avatarZhangfei Gao <zhangfei.gao@linaro.org>
Reviewed-by: default avatarEric Auger <eric.auger@redhat.com>
Reviewed-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: default avatarJean-Philippe Brucker <jean-philippe@linaro.org>
Link: https://lore.kernel.org/r/20210526161927.24268-4-jean-philippe@linaro.orgSigned-off-by: default avatarWill Deacon <will@kernel.org>
parent 6522b1e0
...@@ -435,9 +435,13 @@ bool arm_smmu_sva_supported(struct arm_smmu_device *smmu) ...@@ -435,9 +435,13 @@ bool arm_smmu_sva_supported(struct arm_smmu_device *smmu)
return true; return true;
} }
static bool arm_smmu_iopf_supported(struct arm_smmu_master *master) bool arm_smmu_master_iopf_supported(struct arm_smmu_master *master)
{ {
/* We're not keeping track of SIDs in fault events */
if (master->num_streams != 1)
return false; return false;
return master->stall_enabled;
} }
bool arm_smmu_master_sva_supported(struct arm_smmu_master *master) bool arm_smmu_master_sva_supported(struct arm_smmu_master *master)
...@@ -445,8 +449,8 @@ bool arm_smmu_master_sva_supported(struct arm_smmu_master *master) ...@@ -445,8 +449,8 @@ bool arm_smmu_master_sva_supported(struct arm_smmu_master *master)
if (!(master->smmu->features & ARM_SMMU_FEAT_SVA)) if (!(master->smmu->features & ARM_SMMU_FEAT_SVA))
return false; return false;
/* SSID and IOPF support are mandatory for the moment */ /* SSID support is mandatory for the moment */
return master->ssid_bits && arm_smmu_iopf_supported(master); return master->ssid_bits;
} }
bool arm_smmu_master_sva_enabled(struct arm_smmu_master *master) bool arm_smmu_master_sva_enabled(struct arm_smmu_master *master)
...@@ -459,13 +463,55 @@ bool arm_smmu_master_sva_enabled(struct arm_smmu_master *master) ...@@ -459,13 +463,55 @@ bool arm_smmu_master_sva_enabled(struct arm_smmu_master *master)
return enabled; return enabled;
} }
static int arm_smmu_master_sva_enable_iopf(struct arm_smmu_master *master)
{
int ret;
struct device *dev = master->dev;
/*
* Drivers for devices supporting PRI or stall should enable IOPF first.
* Others have device-specific fault handlers and don't need IOPF.
*/
if (!arm_smmu_master_iopf_supported(master))
return 0;
if (!master->iopf_enabled)
return -EINVAL;
ret = iopf_queue_add_device(master->smmu->evtq.iopf, dev);
if (ret)
return ret;
ret = iommu_register_device_fault_handler(dev, iommu_queue_iopf, dev);
if (ret) {
iopf_queue_remove_device(master->smmu->evtq.iopf, dev);
return ret;
}
return 0;
}
static void arm_smmu_master_sva_disable_iopf(struct arm_smmu_master *master)
{
struct device *dev = master->dev;
if (!master->iopf_enabled)
return;
iommu_unregister_device_fault_handler(dev);
iopf_queue_remove_device(master->smmu->evtq.iopf, dev);
}
int arm_smmu_master_enable_sva(struct arm_smmu_master *master) int arm_smmu_master_enable_sva(struct arm_smmu_master *master)
{ {
int ret;
mutex_lock(&sva_lock); mutex_lock(&sva_lock);
ret = arm_smmu_master_sva_enable_iopf(master);
if (!ret)
master->sva_enabled = true; master->sva_enabled = true;
mutex_unlock(&sva_lock); mutex_unlock(&sva_lock);
return 0; return ret;
} }
int arm_smmu_master_disable_sva(struct arm_smmu_master *master) int arm_smmu_master_disable_sva(struct arm_smmu_master *master)
...@@ -476,6 +522,7 @@ int arm_smmu_master_disable_sva(struct arm_smmu_master *master) ...@@ -476,6 +522,7 @@ int arm_smmu_master_disable_sva(struct arm_smmu_master *master)
mutex_unlock(&sva_lock); mutex_unlock(&sva_lock);
return -EBUSY; return -EBUSY;
} }
arm_smmu_master_sva_disable_iopf(master);
master->sva_enabled = false; master->sva_enabled = false;
mutex_unlock(&sva_lock); mutex_unlock(&sva_lock);
......
This diff is collapsed.
...@@ -354,6 +354,13 @@ ...@@ -354,6 +354,13 @@
#define CMDQ_PRI_1_GRPID GENMASK_ULL(8, 0) #define CMDQ_PRI_1_GRPID GENMASK_ULL(8, 0)
#define CMDQ_PRI_1_RESP GENMASK_ULL(13, 12) #define CMDQ_PRI_1_RESP GENMASK_ULL(13, 12)
#define CMDQ_RESUME_0_RESP_TERM 0UL
#define CMDQ_RESUME_0_RESP_RETRY 1UL
#define CMDQ_RESUME_0_RESP_ABORT 2UL
#define CMDQ_RESUME_0_RESP GENMASK_ULL(13, 12)
#define CMDQ_RESUME_0_SID GENMASK_ULL(63, 32)
#define CMDQ_RESUME_1_STAG GENMASK_ULL(15, 0)
#define CMDQ_SYNC_0_CS GENMASK_ULL(13, 12) #define CMDQ_SYNC_0_CS GENMASK_ULL(13, 12)
#define CMDQ_SYNC_0_CS_NONE 0 #define CMDQ_SYNC_0_CS_NONE 0
#define CMDQ_SYNC_0_CS_IRQ 1 #define CMDQ_SYNC_0_CS_IRQ 1
...@@ -370,6 +377,25 @@ ...@@ -370,6 +377,25 @@
#define EVTQ_0_ID GENMASK_ULL(7, 0) #define EVTQ_0_ID GENMASK_ULL(7, 0)
#define EVT_ID_TRANSLATION_FAULT 0x10
#define EVT_ID_ADDR_SIZE_FAULT 0x11
#define EVT_ID_ACCESS_FAULT 0x12
#define EVT_ID_PERMISSION_FAULT 0x13
#define EVTQ_0_SSV (1UL << 11)
#define EVTQ_0_SSID GENMASK_ULL(31, 12)
#define EVTQ_0_SID GENMASK_ULL(63, 32)
#define EVTQ_1_STAG GENMASK_ULL(15, 0)
#define EVTQ_1_STALL (1UL << 31)
#define EVTQ_1_PnU (1UL << 33)
#define EVTQ_1_InD (1UL << 34)
#define EVTQ_1_RnW (1UL << 35)
#define EVTQ_1_S2 (1UL << 39)
#define EVTQ_1_CLASS GENMASK_ULL(41, 40)
#define EVTQ_1_TT_READ (1UL << 44)
#define EVTQ_2_ADDR GENMASK_ULL(63, 0)
#define EVTQ_3_IPA GENMASK_ULL(51, 12)
/* PRI queue */ /* PRI queue */
#define PRIQ_ENT_SZ_SHIFT 4 #define PRIQ_ENT_SZ_SHIFT 4
#define PRIQ_ENT_DWORDS ((1 << PRIQ_ENT_SZ_SHIFT) >> 3) #define PRIQ_ENT_DWORDS ((1 << PRIQ_ENT_SZ_SHIFT) >> 3)
...@@ -462,6 +488,13 @@ struct arm_smmu_cmdq_ent { ...@@ -462,6 +488,13 @@ struct arm_smmu_cmdq_ent {
enum pri_resp resp; enum pri_resp resp;
} pri; } pri;
#define CMDQ_OP_RESUME 0x44
struct {
u32 sid;
u16 stag;
u8 resp;
} resume;
#define CMDQ_OP_CMD_SYNC 0x46 #define CMDQ_OP_CMD_SYNC 0x46
struct { struct {
u64 msiaddr; u64 msiaddr;
...@@ -520,6 +553,7 @@ struct arm_smmu_cmdq_batch { ...@@ -520,6 +553,7 @@ struct arm_smmu_cmdq_batch {
struct arm_smmu_evtq { struct arm_smmu_evtq {
struct arm_smmu_queue q; struct arm_smmu_queue q;
struct iopf_queue *iopf;
u32 max_stalls; u32 max_stalls;
}; };
...@@ -657,7 +691,9 @@ struct arm_smmu_master { ...@@ -657,7 +691,9 @@ struct arm_smmu_master {
struct arm_smmu_stream *streams; struct arm_smmu_stream *streams;
unsigned int num_streams; unsigned int num_streams;
bool ats_enabled; bool ats_enabled;
bool stall_enabled;
bool sva_enabled; bool sva_enabled;
bool iopf_enabled;
struct list_head bonds; struct list_head bonds;
unsigned int ssid_bits; unsigned int ssid_bits;
}; };
...@@ -675,6 +711,7 @@ struct arm_smmu_domain { ...@@ -675,6 +711,7 @@ struct arm_smmu_domain {
struct mutex init_mutex; /* Protects smmu pointer */ struct mutex init_mutex; /* Protects smmu pointer */
struct io_pgtable_ops *pgtbl_ops; struct io_pgtable_ops *pgtbl_ops;
bool stall_enabled;
atomic_t nr_ats_masters; atomic_t nr_ats_masters;
enum arm_smmu_domain_stage stage; enum arm_smmu_domain_stage stage;
...@@ -716,6 +753,7 @@ bool arm_smmu_master_sva_supported(struct arm_smmu_master *master); ...@@ -716,6 +753,7 @@ bool arm_smmu_master_sva_supported(struct arm_smmu_master *master);
bool arm_smmu_master_sva_enabled(struct arm_smmu_master *master); bool arm_smmu_master_sva_enabled(struct arm_smmu_master *master);
int arm_smmu_master_enable_sva(struct arm_smmu_master *master); int arm_smmu_master_enable_sva(struct arm_smmu_master *master);
int arm_smmu_master_disable_sva(struct arm_smmu_master *master); int arm_smmu_master_disable_sva(struct arm_smmu_master *master);
bool arm_smmu_master_iopf_supported(struct arm_smmu_master *master);
struct iommu_sva *arm_smmu_sva_bind(struct device *dev, struct mm_struct *mm, struct iommu_sva *arm_smmu_sva_bind(struct device *dev, struct mm_struct *mm,
void *drvdata); void *drvdata);
void arm_smmu_sva_unbind(struct iommu_sva *handle); void arm_smmu_sva_unbind(struct iommu_sva *handle);
...@@ -747,6 +785,11 @@ static inline int arm_smmu_master_disable_sva(struct arm_smmu_master *master) ...@@ -747,6 +785,11 @@ static inline int arm_smmu_master_disable_sva(struct arm_smmu_master *master)
return -ENODEV; return -ENODEV;
} }
static inline bool arm_smmu_master_iopf_supported(struct arm_smmu_master *master)
{
return false;
}
static inline struct iommu_sva * static inline struct iommu_sva *
arm_smmu_sva_bind(struct device *dev, struct mm_struct *mm, void *drvdata) arm_smmu_sva_bind(struct device *dev, struct mm_struct *mm, void *drvdata)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment