Message ID | 2-v4-c93b774edcc4+42d2b-smmuv3_newapi_p1_jgg@nvidia.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Update SMMUv3 to the modern iommu API (part 1/3) | expand |
Hi Jason, On Thu, Jan 25, 2024 at 07:57:12PM -0400, Jason Gunthorpe wrote: > This allows writing the flow of arm_smmu_write_strtab_ent() around abort > and bypass domains more naturally. > > Note that the core code no longer supplies NULL domains, though there is > still a flow in the driver that end up in arm_smmu_write_strtab_ent() with > NULL. A later patch will remove it. > > Remove the duplicate calculation of the STE in arm_smmu_init_bypass_stes() > and remove the force parameter. arm_smmu_rmr_install_bypass_ste() can now > simply invoke arm_smmu_make_bypass_ste() directly. > > Reviewed-by: Michael Shavit <mshavit@google.com> > Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> > Tested-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com> > Tested-by: Nicolin Chen <nicolinc@nvidia.com> > Tested-by: Moritz Fischer <moritzf@google.com> > Signed-off-by: Jason Gunthorpe <jgg@nvidia.com> > --- > drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 89 +++++++++++---------- > 1 file changed, 47 insertions(+), 42 deletions(-) > > diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c > index 690742e8f173eb..38bcb4ed1fccc1 100644 > --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c > +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c > @@ -1496,6 +1496,24 @@ static void arm_smmu_write_ste(struct arm_smmu_master *master, u32 sid, > } > } > > +static void arm_smmu_make_abort_ste(struct arm_smmu_ste *target) > +{ > + memset(target, 0, sizeof(*target)); > + target->data[0] = cpu_to_le64( > + STRTAB_STE_0_V | > + FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_ABORT)); > +} > + > +static void arm_smmu_make_bypass_ste(struct arm_smmu_ste *target) > +{ > + memset(target, 0, sizeof(*target)); I see this can be used with the actual STE. Although this is done at init, but briefly making the STE abort from “arm_smmu_make_bypass_ste”, seems a bit fragile to me, in case we use this in the future in different scenarios, it might break the hitless assumption. But no strong opinion though. > + target->data[0] = cpu_to_le64( > + STRTAB_STE_0_V | > + FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_BYPASS)); > + target->data[1] = cpu_to_le64( > + FIELD_PREP(STRTAB_STE_1_SHCFG, STRTAB_STE_1_SHCFG_INCOMING)); > +} > + > static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid, > struct arm_smmu_ste *dst) > { > @@ -1506,37 +1524,31 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid, > struct arm_smmu_domain *smmu_domain = master->domain; > struct arm_smmu_ste target = {}; > > - if (smmu_domain) { > - switch (smmu_domain->stage) { > - case ARM_SMMU_DOMAIN_S1: > - cd_table = &master->cd_table; > - break; > - case ARM_SMMU_DOMAIN_S2: > - s2_cfg = &smmu_domain->s2_cfg; > - break; > - default: > - break; > - } > + if (!smmu_domain) { > + if (disable_bypass) > + arm_smmu_make_abort_ste(&target); > + else > + arm_smmu_make_bypass_ste(&target); > + arm_smmu_write_ste(master, sid, dst, &target); > + return; > + } > + > + switch (smmu_domain->stage) { > + case ARM_SMMU_DOMAIN_S1: > + cd_table = &master->cd_table; > + break; > + case ARM_SMMU_DOMAIN_S2: > + s2_cfg = &smmu_domain->s2_cfg; > + break; > + case ARM_SMMU_DOMAIN_BYPASS: > + arm_smmu_make_bypass_ste(&target); > + arm_smmu_write_ste(master, sid, dst, &target); > + return; > } > > /* Nuke the existing STE_0 value, as we're going to rewrite it */ > val = STRTAB_STE_0_V; > > - /* Bypass/fault */ > - if (!smmu_domain || !(cd_table || s2_cfg)) { > - if (!smmu_domain && disable_bypass) > - val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_ABORT); > - else > - val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_BYPASS); > - > - target.data[0] = cpu_to_le64(val); > - target.data[1] = cpu_to_le64(FIELD_PREP(STRTAB_STE_1_SHCFG, > - STRTAB_STE_1_SHCFG_INCOMING)); > - target.data[2] = 0; /* Nuke the VMID */ > - arm_smmu_write_ste(master, sid, dst, &target); > - return; > - } > - > if (cd_table) { > u64 strw = smmu->features & ARM_SMMU_FEAT_E2H ? > STRTAB_STE_1_STRW_EL2 : STRTAB_STE_1_STRW_NSEL1; > @@ -1582,21 +1594,15 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid, > } > > static void arm_smmu_init_bypass_stes(struct arm_smmu_ste *strtab, > - unsigned int nent, bool force) > + unsigned int nent) > { > unsigned int i; > - u64 val = STRTAB_STE_0_V; > - > - if (disable_bypass && !force) > - val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_ABORT); > - else > - val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_BYPASS); > > for (i = 0; i < nent; ++i) { > - strtab->data[0] = cpu_to_le64(val); > - strtab->data[1] = cpu_to_le64(FIELD_PREP( > - STRTAB_STE_1_SHCFG, STRTAB_STE_1_SHCFG_INCOMING)); > - strtab->data[2] = 0; > + if (disable_bypass) > + arm_smmu_make_abort_ste(strtab); > + else > + arm_smmu_make_bypass_ste(strtab); > strtab++; > } > } > @@ -1624,7 +1630,7 @@ static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid) > return -ENOMEM; > } > > - arm_smmu_init_bypass_stes(desc->l2ptr, 1 << STRTAB_SPLIT, false); > + arm_smmu_init_bypass_stes(desc->l2ptr, 1 << STRTAB_SPLIT); > arm_smmu_write_strtab_l1_desc(strtab, desc); > return 0; > } > @@ -3243,7 +3249,7 @@ static int arm_smmu_init_strtab_linear(struct arm_smmu_device *smmu) > reg |= FIELD_PREP(STRTAB_BASE_CFG_LOG2SIZE, smmu->sid_bits); > cfg->strtab_base_cfg = reg; > > - arm_smmu_init_bypass_stes(strtab, cfg->num_l1_ents, false); > + arm_smmu_init_bypass_stes(strtab, cfg->num_l1_ents); > return 0; > } > > @@ -3954,7 +3960,6 @@ static void arm_smmu_rmr_install_bypass_ste(struct arm_smmu_device *smmu) > iort_get_rmr_sids(dev_fwnode(smmu->dev), &rmr_list); > > list_for_each_entry(e, &rmr_list, list) { > - struct arm_smmu_ste *step; > struct iommu_iort_rmr_data *rmr; > int ret, i; > > @@ -3967,8 +3972,8 @@ static void arm_smmu_rmr_install_bypass_ste(struct arm_smmu_device *smmu) > continue; > } > > - step = arm_smmu_get_step_for_sid(smmu, rmr->sids[i]); > - arm_smmu_init_bypass_stes(step, 1, true); > + arm_smmu_make_bypass_ste( > + arm_smmu_get_step_for_sid(smmu, rmr->sids[i])); > } > } > > -- > 2.43.0 > Reviewed-by: Mostafa Saleh <smostafa@google.com> Thanks, Mostafa
On Wed, Jan 31, 2024 at 02:40:24PM +0000, Mostafa Saleh wrote: > > +static void arm_smmu_make_bypass_ste(struct arm_smmu_ste *target) > > +{ > > + memset(target, 0, sizeof(*target)); > > I see this can be used with the actual STE. Although this is done at init, but > briefly making the STE abort from “arm_smmu_make_bypass_ste”, seems a bit > fragile to me, in case we use this in the future in different scenarios, it > might break the hitless assumption. But no strong opinion though. At init time, when that case happens, the STE table hasn't been installed in the HW yet. This is why that specific code path has been directly manipulating the STE and does not call the normal update path with the sync'ing. It is perhaps subtle that is why it is a different flow. (this is also why I moved the function order as it was a bit obscure to see that indeed all this stuff was sequenced right and we were not updating a live STE improperly) Thanks, Jason
On Wed, Jan 31, 2024 at 10:47:02AM -0400, Jason Gunthorpe wrote: > On Wed, Jan 31, 2024 at 02:40:24PM +0000, Mostafa Saleh wrote: > > > +static void arm_smmu_make_bypass_ste(struct arm_smmu_ste *target) > > > +{ > > > + memset(target, 0, sizeof(*target)); > > > > I see this can be used with the actual STE. Although this is done at init, but > > briefly making the STE abort from “arm_smmu_make_bypass_ste”, seems a bit > > fragile to me, in case we use this in the future in different scenarios, it > > might break the hitless assumption. But no strong opinion though. > > At init time, when that case happens, the STE table hasn't been > installed in the HW yet. This is why that specific code path has been > directly manipulating the STE and does not call the normal update path > with the sync'ing. > > It is perhaps subtle that is why it is a different flow. > > (this is also why I moved the function order as it was a bit obscure > to see that indeed all this stuff was sequenced right and we were not > updating a live STE improperly) I agree, this is not an issue, but it was just confusing when I first read it as “arm_smmu_make_bypass_ste” would make the STE transiently unavailable, and I had to go and check the usage. Maybe we can add a comment to clarify how this function is expected to be used. Thanks, Mostafa
On Thu, Feb 01, 2024 at 11:32:41AM +0000, Mostafa Saleh wrote: > On Wed, Jan 31, 2024 at 10:47:02AM -0400, Jason Gunthorpe wrote: > > On Wed, Jan 31, 2024 at 02:40:24PM +0000, Mostafa Saleh wrote: > > > > +static void arm_smmu_make_bypass_ste(struct arm_smmu_ste *target) > > > > +{ > > > > + memset(target, 0, sizeof(*target)); > > > > > > I see this can be used with the actual STE. Although this is done at init, but > > > briefly making the STE abort from “arm_smmu_make_bypass_ste”, seems a bit > > > fragile to me, in case we use this in the future in different scenarios, it > > > might break the hitless assumption. But no strong opinion though. > > > > At init time, when that case happens, the STE table hasn't been > > installed in the HW yet. This is why that specific code path has been > > directly manipulating the STE and does not call the normal update path > > with the sync'ing. > > > > It is perhaps subtle that is why it is a different flow. > > > > (this is also why I moved the function order as it was a bit obscure > > to see that indeed all this stuff was sequenced right and we were not > > updating a live STE improperly) > > I agree, this is not an issue, but it was just confusing when I first read it > as “arm_smmu_make_bypass_ste” would make the STE transiently unavailable, and > I had to go and check the usage. Maybe we can add a comment to clarify how this > function is expected to be used. I added this remark: @@ -1592,6 +1592,10 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid, arm_smmu_write_ste(master, sid, dst, &target); } +/* + * This can safely directly manipulate the STE memory without a sync sequence + * because the STE table has not been installed in the SMMU yet. + */ static void arm_smmu_init_bypass_stes(struct arm_smmu_ste *strtab, unsigned int nent) { @@ -3971,6 +3975,10 @@ static void arm_smmu_rmr_install_bypass_ste(struct arm_smmu_device *smmu) continue; } + /* + * STE table is not programmed to HW, see + * arm_smmu_init_bypass_stes() + */ arm_smmu_make_bypass_ste( arm_smmu_get_step_for_sid(smmu, rmr->sids[i])); } Jason
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c index 690742e8f173eb..38bcb4ed1fccc1 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -1496,6 +1496,24 @@ static void arm_smmu_write_ste(struct arm_smmu_master *master, u32 sid, } } +static void arm_smmu_make_abort_ste(struct arm_smmu_ste *target) +{ + memset(target, 0, sizeof(*target)); + target->data[0] = cpu_to_le64( + STRTAB_STE_0_V | + FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_ABORT)); +} + +static void arm_smmu_make_bypass_ste(struct arm_smmu_ste *target) +{ + memset(target, 0, sizeof(*target)); + target->data[0] = cpu_to_le64( + STRTAB_STE_0_V | + FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_BYPASS)); + target->data[1] = cpu_to_le64( + FIELD_PREP(STRTAB_STE_1_SHCFG, STRTAB_STE_1_SHCFG_INCOMING)); +} + static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid, struct arm_smmu_ste *dst) { @@ -1506,37 +1524,31 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid, struct arm_smmu_domain *smmu_domain = master->domain; struct arm_smmu_ste target = {}; - if (smmu_domain) { - switch (smmu_domain->stage) { - case ARM_SMMU_DOMAIN_S1: - cd_table = &master->cd_table; - break; - case ARM_SMMU_DOMAIN_S2: - s2_cfg = &smmu_domain->s2_cfg; - break; - default: - break; - } + if (!smmu_domain) { + if (disable_bypass) + arm_smmu_make_abort_ste(&target); + else + arm_smmu_make_bypass_ste(&target); + arm_smmu_write_ste(master, sid, dst, &target); + return; + } + + switch (smmu_domain->stage) { + case ARM_SMMU_DOMAIN_S1: + cd_table = &master->cd_table; + break; + case ARM_SMMU_DOMAIN_S2: + s2_cfg = &smmu_domain->s2_cfg; + break; + case ARM_SMMU_DOMAIN_BYPASS: + arm_smmu_make_bypass_ste(&target); + arm_smmu_write_ste(master, sid, dst, &target); + return; } /* Nuke the existing STE_0 value, as we're going to rewrite it */ val = STRTAB_STE_0_V; - /* Bypass/fault */ - if (!smmu_domain || !(cd_table || s2_cfg)) { - if (!smmu_domain && disable_bypass) - val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_ABORT); - else - val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_BYPASS); - - target.data[0] = cpu_to_le64(val); - target.data[1] = cpu_to_le64(FIELD_PREP(STRTAB_STE_1_SHCFG, - STRTAB_STE_1_SHCFG_INCOMING)); - target.data[2] = 0; /* Nuke the VMID */ - arm_smmu_write_ste(master, sid, dst, &target); - return; - } - if (cd_table) { u64 strw = smmu->features & ARM_SMMU_FEAT_E2H ? STRTAB_STE_1_STRW_EL2 : STRTAB_STE_1_STRW_NSEL1; @@ -1582,21 +1594,15 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid, } static void arm_smmu_init_bypass_stes(struct arm_smmu_ste *strtab, - unsigned int nent, bool force) + unsigned int nent) { unsigned int i; - u64 val = STRTAB_STE_0_V; - - if (disable_bypass && !force) - val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_ABORT); - else - val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_BYPASS); for (i = 0; i < nent; ++i) { - strtab->data[0] = cpu_to_le64(val); - strtab->data[1] = cpu_to_le64(FIELD_PREP( - STRTAB_STE_1_SHCFG, STRTAB_STE_1_SHCFG_INCOMING)); - strtab->data[2] = 0; + if (disable_bypass) + arm_smmu_make_abort_ste(strtab); + else + arm_smmu_make_bypass_ste(strtab); strtab++; } } @@ -1624,7 +1630,7 @@ static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid) return -ENOMEM; } - arm_smmu_init_bypass_stes(desc->l2ptr, 1 << STRTAB_SPLIT, false); + arm_smmu_init_bypass_stes(desc->l2ptr, 1 << STRTAB_SPLIT); arm_smmu_write_strtab_l1_desc(strtab, desc); return 0; } @@ -3243,7 +3249,7 @@ static int arm_smmu_init_strtab_linear(struct arm_smmu_device *smmu) reg |= FIELD_PREP(STRTAB_BASE_CFG_LOG2SIZE, smmu->sid_bits); cfg->strtab_base_cfg = reg; - arm_smmu_init_bypass_stes(strtab, cfg->num_l1_ents, false); + arm_smmu_init_bypass_stes(strtab, cfg->num_l1_ents); return 0; } @@ -3954,7 +3960,6 @@ static void arm_smmu_rmr_install_bypass_ste(struct arm_smmu_device *smmu) iort_get_rmr_sids(dev_fwnode(smmu->dev), &rmr_list); list_for_each_entry(e, &rmr_list, list) { - struct arm_smmu_ste *step; struct iommu_iort_rmr_data *rmr; int ret, i; @@ -3967,8 +3972,8 @@ static void arm_smmu_rmr_install_bypass_ste(struct arm_smmu_device *smmu) continue; } - step = arm_smmu_get_step_for_sid(smmu, rmr->sids[i]); - arm_smmu_init_bypass_stes(step, 1, true); + arm_smmu_make_bypass_ste( + arm_smmu_get_step_for_sid(smmu, rmr->sids[i])); } }