On Tue, Aug 23, 2016 at 08:05:20PM +0100, Robin Murphy wrote:
> In order to consider SMR masking, we really want to be able to validate
> ID/mask pairs against existing SMR contents to prevent stream match
> conflicts, which at best would cause transactions to fault unexpectedly,
> and at worst lead to silent unpredictable behaviour. With our SMMU
> instance data holding only an allocator bitmap, and the SMR values
> themselves scattered across master configs hanging off devices which we
> may have no way of finding, there's essentially no way short of digging
> everything back out of the hardware. Similarly, the thought of power
> management ops to support suspend/resume faces the exact same problem.
>
> By massaging the software state into a closer shape to the underlying
> hardware, everything comes together quite nicely; the allocator and the
> high-level view of the data become a single centralised state which we
> can easily keep track of, and to which any updates can be validated in
> full before being synchronised to the hardware itself.
>
> Signed-off-by: Robin Murphy <[email protected]>
> ---
> drivers/iommu/arm-smmu.c | 147
> +++++++++++++++++++++++++++--------------------
> 1 file changed, 86 insertions(+), 61 deletions(-)
[...]
> +static int arm_smmu_master_alloc_smes(struct arm_smmu_device *smmu,
> + struct arm_smmu_master_cfg *cfg)
> +{
> + struct arm_smmu_smr *smrs = smmu->smrs;
> + int i, idx;
>
> /* Allocate the SMRs on the SMMU */
> for (i = 0; i < cfg->num_streamids; ++i) {
> - int idx = __arm_smmu_alloc_bitmap(smmu->smr_map, 0,
> - smmu->num_mapping_groups);
> + if (cfg->smendx[i] >= 0)
> + return -EEXIST;
> +
> + /* ...except on stream indexing hardware, of course */
> + if (!smrs) {
> + cfg->smendx[i] = cfg->streamids[i];
> + continue;
> + }
> +
> + idx = arm_smmu_alloc_smr(smmu);
> if (idx < 0) {
> dev_err(smmu->dev, "failed to allocate free SMR\n");
> goto err_free_smrs;
> }
> + cfg->smendx[i] = idx;
>
> - smrs[i] = (struct arm_smmu_smr) {
> - .idx = idx,
> - .mask = 0, /* We don't currently share SMRs */
> - .id = cfg->streamids[i],
> - };
> + smrs[idx].id = cfg->streamids[i];
> + smrs[idx].mask = 0; /* We don't currently share SMRs */
> }
>
> + if (!smrs)
> + return 0;
> +
> /* It worked! Now, poke the actual hardware */
> - for (i = 0; i < cfg->num_streamids; ++i) {
> - u32 reg = SMR_VALID | smrs[i].id << SMR_ID_SHIFT |
> - smrs[i].mask << SMR_MASK_SHIFT;
> - writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_SMR(smrs[i].idx));
> - }
> + for (i = 0; i < cfg->num_streamids; ++i)
> + arm_smmu_write_smr(smmu, cfg->smendx[i]);
>
> - cfg->smrs = smrs;
> return 0;
>
> err_free_smrs:
> - while (--i >= 0)
> - __arm_smmu_free_bitmap(smmu->smr_map, smrs[i].idx);
> - kfree(smrs);
> + while (i--) {
> + arm_smmu_free_smr(smmu, cfg->smendx[i]);
> + cfg->smendx[i] = INVALID_SMENDX;
> + }
Hmm, don't you have an off-by-one here? At least, looking at the final
code, we branch to out_err from within the for_each_cfg_sme loop, but
before we've incremented smmu->s2crs[idx].count, so the arm_smmu_free_smr
will erroneously decrement that.
Will
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu