Skip to content

Commit eb054d6

Browse files
jpemartinswilldeacon
authored andcommitted
iommu/arm-smmu-v3: Add support for dirty tracking in domain alloc
This provides all the infrastructure to enable dirty tracking if the hardware has the capability and domain alloc request for it. Also, add a device_iommu_capable() check in iommufd core for IOMMU_CAP_DIRTY_TRACKING before we request a user domain with dirty tracking support. Please note, we still report no support for IOMMU_CAP_DIRTY_TRACKING as it will finally be enabled in a subsequent patch. Signed-off-by: Joao Martins <[email protected]> Reviewed-by: Ryan Roberts <[email protected]> Reviewed-by: Jason Gunthorpe <[email protected]> Reviewed-by: Nicolin Chen <[email protected]> Reviewed-by: Kevin Tian <[email protected]> Signed-off-by: Shameer Kolothum <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Will Deacon <[email protected]>
1 parent 4fe88fd commit eb054d6

File tree

3 files changed

+67
-23
lines changed

3 files changed

+67
-23
lines changed

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c

Lines changed: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <linux/pci-ats.h>
2828
#include <linux/platform_device.h>
2929
#include <kunit/visibility.h>
30+
#include <uapi/linux/iommufd.h>
3031

3132
#include "arm-smmu-v3.h"
3233
#include "../../dma-iommu.h"
@@ -37,6 +38,7 @@ MODULE_PARM_DESC(disable_msipolling,
3738
"Disable MSI-based polling for CMD_SYNC completion.");
3839

3940
static struct iommu_ops arm_smmu_ops;
41+
static struct iommu_dirty_ops arm_smmu_dirty_ops;
4042

4143
enum arm_smmu_msi_index {
4244
EVTQ_MSI_INDEX,
@@ -82,7 +84,7 @@ static struct arm_smmu_option_prop arm_smmu_options[] = {
8284
};
8385

8486
static int arm_smmu_domain_finalise(struct arm_smmu_domain *smmu_domain,
85-
struct arm_smmu_device *smmu);
87+
struct arm_smmu_device *smmu, u32 flags);
8688
static int arm_smmu_alloc_cd_tables(struct arm_smmu_master *master);
8789

8890
static void parse_driver_options(struct arm_smmu_device *smmu)
@@ -2282,7 +2284,7 @@ static struct iommu_domain *arm_smmu_domain_alloc_paging(struct device *dev)
22822284
struct arm_smmu_master *master = dev_iommu_priv_get(dev);
22832285
int ret;
22842286

2285-
ret = arm_smmu_domain_finalise(smmu_domain, master->smmu);
2287+
ret = arm_smmu_domain_finalise(smmu_domain, master->smmu, 0);
22862288
if (ret) {
22872289
kfree(smmu_domain);
22882290
return ERR_PTR(ret);
@@ -2346,56 +2348,63 @@ static int arm_smmu_domain_finalise_s2(struct arm_smmu_device *smmu,
23462348
}
23472349

23482350
static int arm_smmu_domain_finalise(struct arm_smmu_domain *smmu_domain,
2349-
struct arm_smmu_device *smmu)
2351+
struct arm_smmu_device *smmu, u32 flags)
23502352
{
23512353
int ret;
2352-
unsigned long ias, oas;
23532354
enum io_pgtable_fmt fmt;
23542355
struct io_pgtable_cfg pgtbl_cfg;
23552356
struct io_pgtable_ops *pgtbl_ops;
23562357
int (*finalise_stage_fn)(struct arm_smmu_device *smmu,
23572358
struct arm_smmu_domain *smmu_domain);
2359+
bool enable_dirty = flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING;
23582360

23592361
/* Restrict the stage to what we can actually support */
23602362
if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1))
23612363
smmu_domain->stage = ARM_SMMU_DOMAIN_S2;
23622364
if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S2))
23632365
smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
23642366

2367+
pgtbl_cfg = (struct io_pgtable_cfg) {
2368+
.pgsize_bitmap = smmu->pgsize_bitmap,
2369+
.coherent_walk = smmu->features & ARM_SMMU_FEAT_COHERENCY,
2370+
.tlb = &arm_smmu_flush_ops,
2371+
.iommu_dev = smmu->dev,
2372+
};
2373+
23652374
switch (smmu_domain->stage) {
2366-
case ARM_SMMU_DOMAIN_S1:
2367-
ias = (smmu->features & ARM_SMMU_FEAT_VAX) ? 52 : 48;
2368-
ias = min_t(unsigned long, ias, VA_BITS);
2369-
oas = smmu->ias;
2375+
case ARM_SMMU_DOMAIN_S1: {
2376+
unsigned long ias = (smmu->features &
2377+
ARM_SMMU_FEAT_VAX) ? 52 : 48;
2378+
2379+
pgtbl_cfg.ias = min_t(unsigned long, ias, VA_BITS);
2380+
pgtbl_cfg.oas = smmu->ias;
2381+
if (enable_dirty)
2382+
pgtbl_cfg.quirks |= IO_PGTABLE_QUIRK_ARM_HD;
23702383
fmt = ARM_64_LPAE_S1;
23712384
finalise_stage_fn = arm_smmu_domain_finalise_s1;
23722385
break;
2386+
}
23732387
case ARM_SMMU_DOMAIN_S2:
2374-
ias = smmu->ias;
2375-
oas = smmu->oas;
2388+
if (enable_dirty)
2389+
return -EOPNOTSUPP;
2390+
pgtbl_cfg.ias = smmu->ias;
2391+
pgtbl_cfg.oas = smmu->oas;
23762392
fmt = ARM_64_LPAE_S2;
23772393
finalise_stage_fn = arm_smmu_domain_finalise_s2;
23782394
break;
23792395
default:
23802396
return -EINVAL;
23812397
}
23822398

2383-
pgtbl_cfg = (struct io_pgtable_cfg) {
2384-
.pgsize_bitmap = smmu->pgsize_bitmap,
2385-
.ias = ias,
2386-
.oas = oas,
2387-
.coherent_walk = smmu->features & ARM_SMMU_FEAT_COHERENCY,
2388-
.tlb = &arm_smmu_flush_ops,
2389-
.iommu_dev = smmu->dev,
2390-
};
2391-
23922399
pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain);
23932400
if (!pgtbl_ops)
23942401
return -ENOMEM;
23952402

23962403
smmu_domain->domain.pgsize_bitmap = pgtbl_cfg.pgsize_bitmap;
23972404
smmu_domain->domain.geometry.aperture_end = (1UL << pgtbl_cfg.ias) - 1;
23982405
smmu_domain->domain.geometry.force_aperture = true;
2406+
if (enable_dirty && smmu_domain->stage == ARM_SMMU_DOMAIN_S1)
2407+
smmu_domain->domain.dirty_ops = &arm_smmu_dirty_ops;
23992408

24002409
ret = finalise_stage_fn(smmu, smmu_domain);
24012410
if (ret < 0) {
@@ -2745,7 +2754,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
27452754
mutex_lock(&smmu_domain->init_mutex);
27462755

27472756
if (!smmu_domain->smmu) {
2748-
ret = arm_smmu_domain_finalise(smmu_domain, smmu);
2757+
ret = arm_smmu_domain_finalise(smmu_domain, smmu, 0);
27492758
} else if (smmu_domain->smmu != smmu)
27502759
ret = -EINVAL;
27512760

@@ -2810,7 +2819,7 @@ static int arm_smmu_s1_set_dev_pasid(struct iommu_domain *domain,
28102819

28112820
mutex_lock(&smmu_domain->init_mutex);
28122821
if (!smmu_domain->smmu)
2813-
ret = arm_smmu_domain_finalise(smmu_domain, smmu);
2822+
ret = arm_smmu_domain_finalise(smmu_domain, smmu, 0);
28142823
else if (smmu_domain->smmu != smmu)
28152824
ret = -EINVAL;
28162825
mutex_unlock(&smmu_domain->init_mutex);
@@ -3028,10 +3037,13 @@ arm_smmu_domain_alloc_user(struct device *dev, u32 flags,
30283037
const struct iommu_user_data *user_data)
30293038
{
30303039
struct arm_smmu_master *master = dev_iommu_priv_get(dev);
3040+
const u32 PAGING_FLAGS = IOMMU_HWPT_ALLOC_DIRTY_TRACKING;
30313041
struct arm_smmu_domain *smmu_domain;
30323042
int ret;
30333043

3034-
if (flags || parent || user_data)
3044+
if (flags & ~PAGING_FLAGS)
3045+
return ERR_PTR(-EOPNOTSUPP);
3046+
if (parent || user_data)
30353047
return ERR_PTR(-EOPNOTSUPP);
30363048

30373049
smmu_domain = arm_smmu_domain_alloc();
@@ -3040,7 +3052,7 @@ arm_smmu_domain_alloc_user(struct device *dev, u32 flags,
30403052

30413053
smmu_domain->domain.type = IOMMU_DOMAIN_UNMANAGED;
30423054
smmu_domain->domain.ops = arm_smmu_ops.default_domain_ops;
3043-
ret = arm_smmu_domain_finalise(smmu_domain, master->smmu);
3055+
ret = arm_smmu_domain_finalise(smmu_domain, master->smmu, flags);
30443056
if (ret)
30453057
goto err_free;
30463058
return &smmu_domain->domain;
@@ -3295,6 +3307,27 @@ static void arm_smmu_release_device(struct device *dev)
32953307
kfree(master);
32963308
}
32973309

3310+
static int arm_smmu_read_and_clear_dirty(struct iommu_domain *domain,
3311+
unsigned long iova, size_t size,
3312+
unsigned long flags,
3313+
struct iommu_dirty_bitmap *dirty)
3314+
{
3315+
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
3316+
struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
3317+
3318+
return ops->read_and_clear_dirty(ops, iova, size, flags, dirty);
3319+
}
3320+
3321+
static int arm_smmu_set_dirty_tracking(struct iommu_domain *domain,
3322+
bool enabled)
3323+
{
3324+
/*
3325+
* Always enabled and the dirty bitmap is cleared prior to
3326+
* set_dirty_tracking().
3327+
*/
3328+
return 0;
3329+
}
3330+
32983331
static struct iommu_group *arm_smmu_device_group(struct device *dev)
32993332
{
33003333
struct iommu_group *group;
@@ -3453,6 +3486,11 @@ static struct iommu_ops arm_smmu_ops = {
34533486
}
34543487
};
34553488

3489+
static struct iommu_dirty_ops arm_smmu_dirty_ops = {
3490+
.read_and_clear_dirty = arm_smmu_read_and_clear_dirty,
3491+
.set_dirty_tracking = arm_smmu_set_dirty_tracking,
3492+
};
3493+
34563494
/* Probing and initialisation functions */
34573495
static int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
34583496
struct arm_smmu_queue *q,

drivers/iommu/iommufd/hw_pagetable.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ iommufd_hwpt_paging_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas,
114114
return ERR_PTR(-EOPNOTSUPP);
115115
if (flags & ~valid_flags)
116116
return ERR_PTR(-EOPNOTSUPP);
117+
if ((flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING) &&
118+
!device_iommu_capable(idev->dev, IOMMU_CAP_DIRTY_TRACKING))
119+
return ERR_PTR(-EOPNOTSUPP);
117120

118121
hwpt_paging = __iommufd_object_alloc(
119122
ictx, hwpt_paging, IOMMUFD_OBJ_HWPT_PAGING, common.obj);

include/linux/io-pgtable.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,16 @@ struct io_pgtable_cfg {
8585
*
8686
* IO_PGTABLE_QUIRK_ARM_OUTER_WBWA: Override the outer-cacheability
8787
* attributes set in the TCR for a non-coherent page-table walker.
88+
*
89+
* IO_PGTABLE_QUIRK_ARM_HD: Enables dirty tracking in stage 1 pagetable.
8890
*/
8991
#define IO_PGTABLE_QUIRK_ARM_NS BIT(0)
9092
#define IO_PGTABLE_QUIRK_NO_PERMS BIT(1)
9193
#define IO_PGTABLE_QUIRK_ARM_MTK_EXT BIT(3)
9294
#define IO_PGTABLE_QUIRK_ARM_MTK_TTBR_EXT BIT(4)
9395
#define IO_PGTABLE_QUIRK_ARM_TTBR1 BIT(5)
9496
#define IO_PGTABLE_QUIRK_ARM_OUTER_WBWA BIT(6)
97+
#define IO_PGTABLE_QUIRK_ARM_HD BIT(7)
9598
unsigned long quirks;
9699
unsigned long pgsize_bitmap;
97100
unsigned int ias;

0 commit comments

Comments
 (0)