Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 634242b8 authored by Mitchel Humpherys's avatar Mitchel Humpherys Committed by Matt Wagantall
Browse files

iommu: io-pgtable-arm: implement .map_sg



Mapping an entire scatterlist at once is faster than calling iommu_map
on each link individually.  Implement .map_sg in the ARM LPAE page table
allocator so that drivers using the allocator can leverage this
performance boost.

Change-Id: I77f62a2566058693c3f58fc0b05d715a780ae5d8
Signed-off-by: default avatarMitchel Humpherys <mitchelh@codeaurora.org>
parent 9916c5dc
Loading
Loading
Loading
Loading
+61 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@

#include <linux/iommu.h>
#include <linux/kernel.h>
#include <linux/scatterlist.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/types.h>
@@ -317,6 +318,65 @@ static int arm_lpae_map(struct io_pgtable_ops *ops, unsigned long iova,
	return __arm_lpae_map(data, iova, paddr, size, prot, lvl, ptep);
}

static int arm_lpae_map_sg(struct io_pgtable_ops *ops, unsigned long iova,
			   struct scatterlist *sg, unsigned int nents,
			   int iommu_prot)
{
	struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);
	arm_lpae_iopte *ptep = data->pgd;
	int lvl = ARM_LPAE_START_LVL(data);
	arm_lpae_iopte prot;
	struct scatterlist *s;
	size_t mapped = 0;
	int i, ret;
	unsigned int min_pagesz;

	/* If no access, then nothing to do */
	if (!(iommu_prot & (IOMMU_READ | IOMMU_WRITE)))
		return 0;

	prot = arm_lpae_prot_to_pte(data, iommu_prot);

	min_pagesz = 1 << __ffs(data->iop.cfg.pgsize_bitmap);

	for_each_sg(sg, s, nents, i) {
		phys_addr_t phys = page_to_phys(sg_page(s)) + s->offset;
		size_t size = s->length;

		/*
		 * We are mapping on IOMMU page boundaries, so offset within
		 * the page must be 0. However, the IOMMU may support pages
		 * smaller than PAGE_SIZE, so s->offset may still represent
		 * an offset of that boundary within the CPU page.
		 */
		if (!IS_ALIGNED(s->offset, min_pagesz))
			goto out_err;

		while (size) {
			size_t pgsize = iommu_pgsize(
				data->iop.cfg.pgsize_bitmap, iova | phys, size);
			ret = __arm_lpae_map(data, iova, phys, pgsize, prot,
					     lvl, ptep);
			if (ret)
				goto out_err;

			iova += pgsize;
			mapped += pgsize;
			phys += pgsize;
			size -= pgsize;
		}
	}

	return mapped;

out_err:
	/* undo mappings already done */
	if (mapped)
		ops->unmap(ops, iova, mapped);

	return 0;
}

static void __arm_lpae_free_pgtable(struct arm_lpae_io_pgtable *data, int lvl,
				    arm_lpae_iopte *ptep)
{
@@ -563,6 +623,7 @@ arm_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg)

	data->iop.ops = (struct io_pgtable_ops) {
		.map		= arm_lpae_map,
		.map_sg		= arm_lpae_map_sg,
		.unmap		= arm_lpae_unmap,
		.iova_to_phys	= arm_lpae_iova_to_phys,
	};