Commit dbbde63c authored by Rob Clark's avatar Rob Clark
Browse files

drm/msm: Add PRR support



Add PRR (Partial Resident Region) is a bypass address which make GPU
writes go to /dev/null and reads return zero.  This is used to implement
vulkan sparse residency.

To support PRR/NULL mappings, we allocate a page to reserve a physical
address which we know will not be used as part of a GEM object, and
configure the SMMU to use this address for PRR/NULL mappings.

Signed-off-by: default avatarRob Clark <robdclark@chromium.org>
Signed-off-by: default avatarRob Clark <robin.clark@oss.qualcomm.com>
Tested-by: default avatarAntonino Maniscalco <antomani103@gmail.com>
Reviewed-by: default avatarAntonino Maniscalco <antomani103@gmail.com>
Patchwork: https://patchwork.freedesktop.org/patch/661486/
parent 2c7ad992
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -357,6 +357,13 @@ int adreno_fault_handler(struct msm_gpu *gpu, unsigned long iova, int flags,
	return 0;
}

static bool
adreno_smmu_has_prr(struct msm_gpu *gpu)
{
	struct adreno_smmu_priv *adreno_smmu = dev_get_drvdata(&gpu->pdev->dev);
	return adreno_smmu && adreno_smmu->set_prr_addr;
}

int adreno_get_param(struct msm_gpu *gpu, struct msm_context *ctx,
		     uint32_t param, uint64_t *value, uint32_t *len)
{
@@ -440,6 +447,9 @@ int adreno_get_param(struct msm_gpu *gpu, struct msm_context *ctx,
	case MSM_PARAM_UCHE_TRAP_BASE:
		*value = adreno_gpu->uche_trap_base;
		return 0;
	case MSM_PARAM_HAS_PRR:
		*value = adreno_smmu_has_prr(gpu);
		return 0;
	default:
		return UERR(EINVAL, drm, "%s: invalid param: %u", gpu->name, param);
	}
+61 −1
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ struct msm_iommu {
	struct msm_mmu base;
	struct iommu_domain *domain;
	atomic_t pagetables;
	struct page *prr_page;
};

#define to_msm_iommu(x) container_of(x, struct msm_iommu, base)
@@ -112,6 +113,36 @@ static int msm_iommu_pagetable_unmap(struct msm_mmu *mmu, u64 iova,
	return (size == 0) ? 0 : -EINVAL;
}

static int msm_iommu_pagetable_map_prr(struct msm_mmu *mmu, u64 iova, size_t len, int prot)
{
	struct msm_iommu_pagetable *pagetable = to_pagetable(mmu);
	struct io_pgtable_ops *ops = pagetable->pgtbl_ops;
	struct msm_iommu *iommu = to_msm_iommu(pagetable->parent);
	phys_addr_t phys = page_to_phys(iommu->prr_page);
	u64 addr = iova;

	while (len) {
		size_t mapped = 0;
		size_t size = PAGE_SIZE;
		int ret;

		ret = ops->map_pages(ops, addr, phys, size, 1, prot, GFP_KERNEL, &mapped);

		/* map_pages could fail after mapping some of the pages,
		 * so update the counters before error handling.
		 */
		addr += mapped;
		len  -= mapped;

		if (ret) {
			msm_iommu_pagetable_unmap(mmu, iova, addr - iova);
			return -EINVAL;
		}
	}

	return 0;
}

static int msm_iommu_pagetable_map(struct msm_mmu *mmu, u64 iova,
				   struct sg_table *sgt, size_t off, size_t len,
				   int prot)
@@ -122,6 +153,9 @@ static int msm_iommu_pagetable_map(struct msm_mmu *mmu, u64 iova,
	u64 addr = iova;
	unsigned int i;

	if (!sgt)
		return msm_iommu_pagetable_map_prr(mmu, iova, len, prot);

	for_each_sgtable_sg(sgt, sg, i) {
		size_t size = sg->length;
		phys_addr_t phys = sg_phys(sg);
@@ -177,9 +211,16 @@ static void msm_iommu_pagetable_destroy(struct msm_mmu *mmu)
	 * If this is the last attached pagetable for the parent,
	 * disable TTBR0 in the arm-smmu driver
	 */
	if (atomic_dec_return(&iommu->pagetables) == 0)
	if (atomic_dec_return(&iommu->pagetables) == 0) {
		adreno_smmu->set_ttbr0_cfg(adreno_smmu->cookie, NULL);

		if (adreno_smmu->set_prr_bit) {
			adreno_smmu->set_prr_bit(adreno_smmu->cookie, false);
			__free_page(iommu->prr_page);
			iommu->prr_page = NULL;
		}
	}

	free_io_pgtable_ops(pagetable->pgtbl_ops);
	kfree(pagetable);
}
@@ -336,6 +377,25 @@ struct msm_mmu *msm_iommu_pagetable_create(struct msm_mmu *parent)
			kfree(pagetable);
			return ERR_PTR(ret);
		}

		BUG_ON(iommu->prr_page);
		if (adreno_smmu->set_prr_bit) {
			/*
			 * We need a zero'd page for two reasons:
			 *
			 * 1) Reserve a known physical address to use when
			 *    mapping NULL / sparsely resident regions
			 * 2) Read back zero
			 *
			 * It appears the hw drops writes to the PRR region
			 * on the floor, but reads actually return whatever
			 * is in the PRR page.
			 */
			iommu->prr_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
			adreno_smmu->set_prr_addr(adreno_smmu->cookie,
						  page_to_phys(iommu->prr_page));
			adreno_smmu->set_prr_bit(adreno_smmu->cookie, true);
		}
	}

	/* Needed later for TLB flush */
+2 −0
Original line number Diff line number Diff line
@@ -91,6 +91,8 @@ struct drm_msm_timespec {
#define MSM_PARAM_UBWC_SWIZZLE 0x12 /* RO */
#define MSM_PARAM_MACROTILE_MODE 0x13 /* RO */
#define MSM_PARAM_UCHE_TRAP_BASE 0x14 /* RO */
/* PRR (Partially Resident Region) is required for sparse residency: */
#define MSM_PARAM_HAS_PRR    0x15  /* RO */

/* For backwards compat.  The original support for preemption was based on
 * a single ring per priority level so # of priority levels equals the #