Skip to content

Commit b774297

Browse files
nicolincwilldeacon
authored andcommitted
iommu/arm-smmu-v3: Populate smmu_domain->invs when attaching masters
Update the invs array with the invalidations required by each domain type during attachment operations. Only an SVA domain or a paging domain will have an invs array: a. SVA domain will add an INV_TYPE_S1_ASID per SMMU and an INV_TYPE_ATS per SID b. Non-nesting-parent paging domain with no ATS-enabled master will add a single INV_TYPE_S1_ASID or INV_TYPE_S2_VMID per SMMU c. Non-nesting-parent paging domain with ATS-enabled master(s) will do (b) and add an INV_TYPE_ATS per SID d. Nesting-parent paging domain will add an INV_TYPE_S2_VMID followed by an INV_TYPE_S2_VMID_S1_CLEAR per vSMMU. For an ATS-enabled master, it will add an INV_TYPE_ATS_FULL per SID Note that case #d prepares for a future implementation of VMID allocation which requires a followup series for S2 domain sharing. So when a nesting parent domain is attached through a vSMMU instance using a nested domain. VMID will be allocated per vSMMU instance v.s. currectly per S2 domain. The per-domain invalidation is not needed until the domain is attached to a master (when it starts to possibly use TLB). This will make it possible to attach the domain to multiple SMMUs and avoid unnecessary invalidation overhead during teardown if no STEs/CDs refer to the domain. It also means that when the last device is detached, the old domain must flush its ASID or VMID, since any new iommu_unmap() call would not trigger invalidations given an empty domain->invs array. Introduce some arm_smmu_invs helper functions for building scratch arrays, preparing and installing old/new domain's invalidation arrays. Co-developed-by: Jason Gunthorpe <jgg@nvidia.com> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com> Reviewed-by: Jason Gunthorpe <jgg@nvidia.com> Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Will Deacon <will@kernel.org>
1 parent e3a56b3 commit b774297

2 files changed

Lines changed: 278 additions & 1 deletion

File tree

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

Lines changed: 261 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3147,6 +3147,121 @@ static void arm_smmu_disable_iopf(struct arm_smmu_master *master,
31473147
iopf_queue_remove_device(master->smmu->evtq.iopf, master->dev);
31483148
}
31493149

3150+
static struct arm_smmu_inv *
3151+
arm_smmu_master_build_inv(struct arm_smmu_master *master,
3152+
enum arm_smmu_inv_type type, u32 id, ioasid_t ssid,
3153+
size_t pgsize)
3154+
{
3155+
struct arm_smmu_invs *build_invs = master->build_invs;
3156+
struct arm_smmu_inv *cur, inv = {
3157+
.smmu = master->smmu,
3158+
.type = type,
3159+
.id = id,
3160+
.pgsize = pgsize,
3161+
};
3162+
3163+
if (WARN_ON(build_invs->num_invs >= build_invs->max_invs))
3164+
return NULL;
3165+
cur = &build_invs->inv[build_invs->num_invs];
3166+
build_invs->num_invs++;
3167+
3168+
*cur = inv;
3169+
switch (type) {
3170+
case INV_TYPE_S1_ASID:
3171+
/*
3172+
* For S1 page tables the driver always uses VMID=0, and the
3173+
* invalidation logic for this type will set it as well.
3174+
*/
3175+
if (master->smmu->features & ARM_SMMU_FEAT_E2H) {
3176+
cur->size_opcode = CMDQ_OP_TLBI_EL2_VA;
3177+
cur->nsize_opcode = CMDQ_OP_TLBI_EL2_ASID;
3178+
} else {
3179+
cur->size_opcode = CMDQ_OP_TLBI_NH_VA;
3180+
cur->nsize_opcode = CMDQ_OP_TLBI_NH_ASID;
3181+
}
3182+
break;
3183+
case INV_TYPE_S2_VMID:
3184+
cur->size_opcode = CMDQ_OP_TLBI_S2_IPA;
3185+
cur->nsize_opcode = CMDQ_OP_TLBI_S12_VMALL;
3186+
break;
3187+
case INV_TYPE_S2_VMID_S1_CLEAR:
3188+
cur->size_opcode = cur->nsize_opcode = CMDQ_OP_TLBI_NH_ALL;
3189+
break;
3190+
case INV_TYPE_ATS:
3191+
case INV_TYPE_ATS_FULL:
3192+
cur->size_opcode = cur->nsize_opcode = CMDQ_OP_ATC_INV;
3193+
cur->ssid = ssid;
3194+
break;
3195+
}
3196+
3197+
return cur;
3198+
}
3199+
3200+
/*
3201+
* Use the preallocated scratch array at master->build_invs, to build a to_merge
3202+
* or to_unref array, to pass into a following arm_smmu_invs_merge/unref() call.
3203+
*
3204+
* Do not free the returned invs array. It is reused, and will be overwritten by
3205+
* the next arm_smmu_master_build_invs() call.
3206+
*/
3207+
static struct arm_smmu_invs *
3208+
arm_smmu_master_build_invs(struct arm_smmu_master *master, bool ats_enabled,
3209+
ioasid_t ssid, struct arm_smmu_domain *smmu_domain)
3210+
{
3211+
const bool nesting = smmu_domain->nest_parent;
3212+
size_t pgsize = 0, i;
3213+
3214+
iommu_group_mutex_assert(master->dev);
3215+
3216+
master->build_invs->num_invs = 0;
3217+
3218+
/* Range-based invalidation requires the leaf pgsize for calculation */
3219+
if (master->smmu->features & ARM_SMMU_FEAT_RANGE_INV)
3220+
pgsize = __ffs(smmu_domain->domain.pgsize_bitmap);
3221+
3222+
switch (smmu_domain->stage) {
3223+
case ARM_SMMU_DOMAIN_SVA:
3224+
case ARM_SMMU_DOMAIN_S1:
3225+
if (!arm_smmu_master_build_inv(master, INV_TYPE_S1_ASID,
3226+
smmu_domain->cd.asid,
3227+
IOMMU_NO_PASID, pgsize))
3228+
return NULL;
3229+
break;
3230+
case ARM_SMMU_DOMAIN_S2:
3231+
if (!arm_smmu_master_build_inv(master, INV_TYPE_S2_VMID,
3232+
smmu_domain->s2_cfg.vmid,
3233+
IOMMU_NO_PASID, pgsize))
3234+
return NULL;
3235+
break;
3236+
default:
3237+
WARN_ON(true);
3238+
return NULL;
3239+
}
3240+
3241+
/* All the nested S1 ASIDs have to be flushed when S2 parent changes */
3242+
if (nesting) {
3243+
if (!arm_smmu_master_build_inv(
3244+
master, INV_TYPE_S2_VMID_S1_CLEAR,
3245+
smmu_domain->s2_cfg.vmid, IOMMU_NO_PASID, 0))
3246+
return NULL;
3247+
}
3248+
3249+
for (i = 0; ats_enabled && i < master->num_streams; i++) {
3250+
/*
3251+
* If an S2 used as a nesting parent is changed we have no
3252+
* option but to completely flush the ATC.
3253+
*/
3254+
if (!arm_smmu_master_build_inv(
3255+
master, nesting ? INV_TYPE_ATS_FULL : INV_TYPE_ATS,
3256+
master->streams[i].id, ssid, 0))
3257+
return NULL;
3258+
}
3259+
3260+
/* Note this build_invs must have been sorted */
3261+
3262+
return master->build_invs;
3263+
}
3264+
31503265
static void arm_smmu_remove_master_domain(struct arm_smmu_master *master,
31513266
struct iommu_domain *domain,
31523267
ioasid_t ssid)
@@ -3176,6 +3291,135 @@ static void arm_smmu_remove_master_domain(struct arm_smmu_master *master,
31763291
kfree(master_domain);
31773292
}
31783293

3294+
/*
3295+
* During attachment, the updates of the two domain->invs arrays are sequenced:
3296+
* 1. new domain updates its invs array, merging master->build_invs
3297+
* 2. new domain starts to include the master during its invalidation
3298+
* 3. master updates its STE switching from the old domain to the new domain
3299+
* 4. old domain still includes the master during its invalidation
3300+
* 5. old domain updates its invs array, unreferencing master->build_invs
3301+
*
3302+
* For 1 and 5, prepare the two updated arrays in advance, handling any changes
3303+
* that can possibly failure. So the actual update of either 1 or 5 won't fail.
3304+
* arm_smmu_asid_lock ensures that the old invs in the domains are intact while
3305+
* we are sequencing to update them.
3306+
*/
3307+
static int arm_smmu_attach_prepare_invs(struct arm_smmu_attach_state *state,
3308+
struct iommu_domain *new_domain)
3309+
{
3310+
struct arm_smmu_domain *old_smmu_domain =
3311+
to_smmu_domain_devices(state->old_domain);
3312+
struct arm_smmu_domain *new_smmu_domain =
3313+
to_smmu_domain_devices(new_domain);
3314+
struct arm_smmu_master *master = state->master;
3315+
ioasid_t ssid = state->ssid;
3316+
3317+
/*
3318+
* At this point a NULL domain indicates the domain doesn't use the
3319+
* IOTLB, see to_smmu_domain_devices().
3320+
*/
3321+
if (new_smmu_domain) {
3322+
struct arm_smmu_inv_state *invst = &state->new_domain_invst;
3323+
struct arm_smmu_invs *build_invs;
3324+
3325+
invst->invs_ptr = &new_smmu_domain->invs;
3326+
invst->old_invs = rcu_dereference_protected(
3327+
new_smmu_domain->invs,
3328+
lockdep_is_held(&arm_smmu_asid_lock));
3329+
build_invs = arm_smmu_master_build_invs(
3330+
master, state->ats_enabled, ssid, new_smmu_domain);
3331+
if (!build_invs)
3332+
return -EINVAL;
3333+
3334+
invst->new_invs =
3335+
arm_smmu_invs_merge(invst->old_invs, build_invs);
3336+
if (IS_ERR(invst->new_invs))
3337+
return PTR_ERR(invst->new_invs);
3338+
}
3339+
3340+
if (old_smmu_domain) {
3341+
struct arm_smmu_inv_state *invst = &state->old_domain_invst;
3342+
3343+
invst->invs_ptr = &old_smmu_domain->invs;
3344+
/* A re-attach case might have a different ats_enabled state */
3345+
if (new_smmu_domain == old_smmu_domain)
3346+
invst->old_invs = state->new_domain_invst.new_invs;
3347+
else
3348+
invst->old_invs = rcu_dereference_protected(
3349+
old_smmu_domain->invs,
3350+
lockdep_is_held(&arm_smmu_asid_lock));
3351+
/* For old_smmu_domain, new_invs points to master->build_invs */
3352+
invst->new_invs = arm_smmu_master_build_invs(
3353+
master, master->ats_enabled, ssid, old_smmu_domain);
3354+
}
3355+
3356+
return 0;
3357+
}
3358+
3359+
/* Must be installed before arm_smmu_install_ste_for_dev() */
3360+
static void
3361+
arm_smmu_install_new_domain_invs(struct arm_smmu_attach_state *state)
3362+
{
3363+
struct arm_smmu_inv_state *invst = &state->new_domain_invst;
3364+
3365+
if (!invst->invs_ptr)
3366+
return;
3367+
3368+
rcu_assign_pointer(*invst->invs_ptr, invst->new_invs);
3369+
kfree_rcu(invst->old_invs, rcu);
3370+
}
3371+
3372+
static void arm_smmu_inv_flush_iotlb_tag(struct arm_smmu_inv *inv)
3373+
{
3374+
struct arm_smmu_cmdq_ent cmd = {};
3375+
3376+
switch (inv->type) {
3377+
case INV_TYPE_S1_ASID:
3378+
cmd.tlbi.asid = inv->id;
3379+
break;
3380+
case INV_TYPE_S2_VMID:
3381+
/* S2_VMID using nsize_opcode covers S2_VMID_S1_CLEAR */
3382+
cmd.tlbi.vmid = inv->id;
3383+
break;
3384+
default:
3385+
return;
3386+
}
3387+
3388+
cmd.opcode = inv->nsize_opcode;
3389+
arm_smmu_cmdq_issue_cmd_with_sync(inv->smmu, &cmd);
3390+
}
3391+
3392+
/* Should be installed after arm_smmu_install_ste_for_dev() */
3393+
static void
3394+
arm_smmu_install_old_domain_invs(struct arm_smmu_attach_state *state)
3395+
{
3396+
struct arm_smmu_inv_state *invst = &state->old_domain_invst;
3397+
struct arm_smmu_invs *old_invs = invst->old_invs;
3398+
struct arm_smmu_invs *new_invs;
3399+
3400+
lockdep_assert_held(&arm_smmu_asid_lock);
3401+
3402+
if (!invst->invs_ptr)
3403+
return;
3404+
3405+
arm_smmu_invs_unref(old_invs, invst->new_invs);
3406+
/*
3407+
* When an IOTLB tag (the first entry in invs->new_invs) is no longer used,
3408+
* it means the ASID or VMID will no longer be invalidated by map/unmap and
3409+
* must be cleaned right now. The rule is that any ASID/VMID not in an invs
3410+
* array must be left cleared in the IOTLB.
3411+
*/
3412+
if (!READ_ONCE(invst->new_invs->inv[0].users))
3413+
arm_smmu_inv_flush_iotlb_tag(&invst->new_invs->inv[0]);
3414+
3415+
new_invs = arm_smmu_invs_purge(old_invs);
3416+
if (!new_invs)
3417+
return;
3418+
3419+
rcu_assign_pointer(*invst->invs_ptr, new_invs);
3420+
kfree_rcu(old_invs, rcu);
3421+
}
3422+
31793423
/*
31803424
* Start the sequence to attach a domain to a master. The sequence contains three
31813425
* steps:
@@ -3233,12 +3477,16 @@ int arm_smmu_attach_prepare(struct arm_smmu_attach_state *state,
32333477
arm_smmu_ats_supported(master);
32343478
}
32353479

3480+
ret = arm_smmu_attach_prepare_invs(state, new_domain);
3481+
if (ret)
3482+
return ret;
3483+
32363484
if (smmu_domain) {
32373485
if (new_domain->type == IOMMU_DOMAIN_NESTED) {
32383486
ret = arm_smmu_attach_prepare_vmaster(
32393487
state, to_smmu_nested_domain(new_domain));
32403488
if (ret)
3241-
return ret;
3489+
goto err_unprepare_invs;
32423490
}
32433491

32443492
master_domain = kzalloc_obj(*master_domain);
@@ -3286,6 +3534,8 @@ int arm_smmu_attach_prepare(struct arm_smmu_attach_state *state,
32863534
atomic_inc(&smmu_domain->nr_ats_masters);
32873535
list_add(&master_domain->devices_elm, &smmu_domain->devices);
32883536
spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
3537+
3538+
arm_smmu_install_new_domain_invs(state);
32893539
}
32903540

32913541
if (!state->ats_enabled && master->ats_enabled) {
@@ -3305,6 +3555,8 @@ int arm_smmu_attach_prepare(struct arm_smmu_attach_state *state,
33053555
kfree(master_domain);
33063556
err_free_vmaster:
33073557
kfree(state->vmaster);
3558+
err_unprepare_invs:
3559+
kfree(state->new_domain_invst.new_invs);
33083560
return ret;
33093561
}
33103562

@@ -3336,6 +3588,7 @@ void arm_smmu_attach_commit(struct arm_smmu_attach_state *state)
33363588
}
33373589

33383590
arm_smmu_remove_master_domain(master, state->old_domain, state->ssid);
3591+
arm_smmu_install_old_domain_invs(state);
33393592
master->ats_enabled = state->ats_enabled;
33403593
}
33413594

@@ -3518,12 +3771,19 @@ static int arm_smmu_blocking_set_dev_pasid(struct iommu_domain *new_domain,
35183771
{
35193772
struct arm_smmu_domain *smmu_domain = to_smmu_domain(old_domain);
35203773
struct arm_smmu_master *master = dev_iommu_priv_get(dev);
3774+
struct arm_smmu_attach_state state = {
3775+
.master = master,
3776+
.old_domain = old_domain,
3777+
.ssid = pasid,
3778+
};
35213779

35223780
mutex_lock(&arm_smmu_asid_lock);
3781+
arm_smmu_attach_prepare_invs(&state, NULL);
35233782
arm_smmu_clear_cd(master, pasid);
35243783
if (master->ats_enabled)
35253784
arm_smmu_atc_inv_master(master, pasid);
35263785
arm_smmu_remove_master_domain(master, &smmu_domain->domain, pasid);
3786+
arm_smmu_install_old_domain_invs(&state);
35273787
mutex_unlock(&arm_smmu_asid_lock);
35283788

35293789
/*

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,21 @@ static inline bool arm_smmu_master_canwbs(struct arm_smmu_master *master)
11021102
IOMMU_FWSPEC_PCI_RC_CANWBS;
11031103
}
11041104

1105+
/**
1106+
* struct arm_smmu_inv_state - Per-domain invalidation array state
1107+
* @invs_ptr: points to the domain->invs (unwinding nesting/etc.) or is NULL if
1108+
* no change should be made
1109+
* @old_invs: the original invs array
1110+
* @new_invs: for new domain, this is the new invs array to update domain->invs;
1111+
* for old domain, this is the master->build_invs to pass in as the
1112+
* to_unref argument to an arm_smmu_invs_unref() call
1113+
*/
1114+
struct arm_smmu_inv_state {
1115+
struct arm_smmu_invs __rcu **invs_ptr;
1116+
struct arm_smmu_invs *old_invs;
1117+
struct arm_smmu_invs *new_invs;
1118+
};
1119+
11051120
struct arm_smmu_attach_state {
11061121
/* Inputs */
11071122
struct iommu_domain *old_domain;
@@ -1111,6 +1126,8 @@ struct arm_smmu_attach_state {
11111126
ioasid_t ssid;
11121127
/* Resulting state */
11131128
struct arm_smmu_vmaster *vmaster;
1129+
struct arm_smmu_inv_state old_domain_invst;
1130+
struct arm_smmu_inv_state new_domain_invst;
11141131
bool ats_enabled;
11151132
};
11161133

0 commit comments

Comments
 (0)