Skip to content

Commit

Permalink
iommu/arm-smmu-v3: Make arm_smmu_alloc_cd_ptr()
Browse files Browse the repository at this point in the history
Only the attach callers can perform an allocation for the CD table entry,
the other callers must not do so, they do not have the correct locking and
they cannot sleep. Split up the functions so this is clear.

arm_smmu_get_cd_ptr() will return pointer to a CD table entry without
doing any kind of allocation.

arm_smmu_alloc_cd_ptr() will allocate the table and any required
leaf.

A following patch will add lockdep assertions to arm_smmu_alloc_cd_ptr()
once the restructuring is completed and arm_smmu_alloc_cd_ptr() is never
called in the wrong context.

Tested-by: Nicolin Chen <nicolinc@nvidia.com>
Reviewed-by: Nicolin Chen <nicolinc@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
Link: https://lore.kernel.org/r/5-v9-5040dc602008+177d7-smmuv3_newapi_p2_jgg@nvidia.com
Signed-off-by: Will Deacon <will@kernel.org>
  • Loading branch information
Jason Gunthorpe authored and Will Deacon committed May 1, 2024
1 parent af8f0b8 commit b2f4c0f
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 23 deletions.
59 changes: 38 additions & 21 deletions drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ static struct arm_smmu_option_prop arm_smmu_options[] = {

static int arm_smmu_domain_finalise(struct arm_smmu_domain *smmu_domain,
struct arm_smmu_device *smmu);
static int arm_smmu_alloc_cd_tables(struct arm_smmu_master *master);

static void parse_driver_options(struct arm_smmu_device *smmu)
{
Expand Down Expand Up @@ -1206,29 +1207,51 @@ static void arm_smmu_write_cd_l1_desc(__le64 *dst,
struct arm_smmu_cd *arm_smmu_get_cd_ptr(struct arm_smmu_master *master,
u32 ssid)
{
__le64 *l1ptr;
unsigned int idx;
struct arm_smmu_l1_ctx_desc *l1_desc;
struct arm_smmu_device *smmu = master->smmu;
struct arm_smmu_ctx_desc_cfg *cd_table = &master->cd_table;

if (!cd_table->cdtab)
return NULL;

if (cd_table->s1fmt == STRTAB_STE_0_S1FMT_LINEAR)
return (struct arm_smmu_cd *)(cd_table->cdtab +
ssid * CTXDESC_CD_DWORDS);

idx = ssid >> CTXDESC_SPLIT;
l1_desc = &cd_table->l1_desc[idx];
if (!l1_desc->l2ptr) {
if (arm_smmu_alloc_cd_leaf_table(smmu, l1_desc))
l1_desc = &cd_table->l1_desc[ssid / CTXDESC_L2_ENTRIES];
if (!l1_desc->l2ptr)
return NULL;
return &l1_desc->l2ptr[ssid % CTXDESC_L2_ENTRIES];
}

static struct arm_smmu_cd *arm_smmu_alloc_cd_ptr(struct arm_smmu_master *master,
u32 ssid)
{
struct arm_smmu_ctx_desc_cfg *cd_table = &master->cd_table;
struct arm_smmu_device *smmu = master->smmu;

if (!cd_table->cdtab) {
if (arm_smmu_alloc_cd_tables(master))
return NULL;
}

if (cd_table->s1fmt == STRTAB_STE_0_S1FMT_64K_L2) {
unsigned int idx = ssid / CTXDESC_L2_ENTRIES;
struct arm_smmu_l1_ctx_desc *l1_desc;

l1_desc = &cd_table->l1_desc[idx];
if (!l1_desc->l2ptr) {
__le64 *l1ptr;

l1ptr = cd_table->cdtab + idx * CTXDESC_L1_DESC_DWORDS;
arm_smmu_write_cd_l1_desc(l1ptr, l1_desc);
/* An invalid L1CD can be cached */
arm_smmu_sync_cd(master, ssid, false);
if (arm_smmu_alloc_cd_leaf_table(smmu, l1_desc))
return NULL;

l1ptr = cd_table->cdtab + idx * CTXDESC_L1_DESC_DWORDS;
arm_smmu_write_cd_l1_desc(l1ptr, l1_desc);
/* An invalid L1CD can be cached */
arm_smmu_sync_cd(master, ssid, false);
}
}
idx = ssid & (CTXDESC_L2_ENTRIES - 1);
return &l1_desc->l2ptr[idx];
return arm_smmu_get_cd_ptr(master, ssid);
}

struct arm_smmu_cd_writer {
Expand Down Expand Up @@ -1344,7 +1367,7 @@ int arm_smmu_write_ctx_desc(struct arm_smmu_master *master, int ssid,
if (WARN_ON(ssid >= (1 << cd_table->s1cdmax)))
return -E2BIG;

cd_table_entry = arm_smmu_get_cd_ptr(master, ssid);
cd_table_entry = arm_smmu_alloc_cd_ptr(master, ssid);
if (!cd_table_entry)
return -ENOMEM;

Expand Down Expand Up @@ -2661,13 +2684,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
struct arm_smmu_cd target_cd;
struct arm_smmu_cd *cdptr;

if (!master->cd_table.cdtab) {
ret = arm_smmu_alloc_cd_tables(master);
if (ret)
goto out_list_del;
}

cdptr = arm_smmu_get_cd_ptr(master, IOMMU_NO_PASID);
cdptr = arm_smmu_alloc_cd_ptr(master, IOMMU_NO_PASID);
if (!cdptr) {
ret = -ENOMEM;
goto out_list_del;
Expand Down
3 changes: 1 addition & 2 deletions drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,8 +275,7 @@ struct arm_smmu_ste {
* 2lvl: at most 1024 L1 entries,
* 1024 lazy entries per table.
*/
#define CTXDESC_SPLIT 10
#define CTXDESC_L2_ENTRIES (1 << CTXDESC_SPLIT)
#define CTXDESC_L2_ENTRIES 1024

#define CTXDESC_L1_DESC_DWORDS 1
#define CTXDESC_L1_DESC_V (1UL << 0)
Expand Down

0 comments on commit b2f4c0f

Please sign in to comment.