Commit 80187fd3 authored by Joerg Roedel's avatar Joerg Roedel

iommu/amd: Optimize map_sg and unmap_sg

Optimize these functions so that they need only one call
into the address alloctor. This also saves a couple of
io-tlb flushes in the unmap_sg path.
Signed-off-by: default avatarJoerg Roedel <jroedel@suse.de>
parent f37f7f33
...@@ -2396,50 +2396,110 @@ static void unmap_page(struct device *dev, dma_addr_t dma_addr, size_t size, ...@@ -2396,50 +2396,110 @@ static void unmap_page(struct device *dev, dma_addr_t dma_addr, size_t size,
__unmap_single(domain->priv, dma_addr, size, dir); __unmap_single(domain->priv, dma_addr, size, dir);
} }
static int sg_num_pages(struct device *dev,
struct scatterlist *sglist,
int nelems)
{
unsigned long mask, boundary_size;
struct scatterlist *s;
int i, npages = 0;
mask = dma_get_seg_boundary(dev);
boundary_size = mask + 1 ? ALIGN(mask + 1, PAGE_SIZE) >> PAGE_SHIFT :
1UL << (BITS_PER_LONG - PAGE_SHIFT);
for_each_sg(sglist, s, nelems, i) {
int p, n;
s->dma_address = npages << PAGE_SHIFT;
p = npages % boundary_size;
n = iommu_num_pages(sg_phys(s), s->length, PAGE_SIZE);
if (p + n > boundary_size)
npages += boundary_size - p;
npages += n;
}
return npages;
}
/* /*
* The exported map_sg function for dma_ops (handles scatter-gather * The exported map_sg function for dma_ops (handles scatter-gather
* lists). * lists).
*/ */
static int map_sg(struct device *dev, struct scatterlist *sglist, static int map_sg(struct device *dev, struct scatterlist *sglist,
int nelems, enum dma_data_direction dir, int nelems, enum dma_data_direction direction,
struct dma_attrs *attrs) struct dma_attrs *attrs)
{ {
int mapped_pages = 0, npages = 0, prot = 0, i;
struct protection_domain *domain; struct protection_domain *domain;
int i; struct dma_ops_domain *dma_dom;
struct scatterlist *s; struct scatterlist *s;
phys_addr_t paddr; unsigned long address;
int mapped_elems = 0;
u64 dma_mask; u64 dma_mask;
domain = get_domain(dev); domain = get_domain(dev);
if (IS_ERR(domain)) if (IS_ERR(domain))
return 0; return 0;
dma_dom = domain->priv;
dma_mask = *dev->dma_mask; dma_mask = *dev->dma_mask;
npages = sg_num_pages(dev, sglist, nelems);
address = dma_ops_alloc_iova(dev, dma_dom, npages, dma_mask);
if (address == DMA_ERROR_CODE)
goto out_err;
prot = dir2prot(direction);
/* Map all sg entries */
for_each_sg(sglist, s, nelems, i) { for_each_sg(sglist, s, nelems, i) {
paddr = sg_phys(s); int j, pages = iommu_num_pages(sg_phys(s), s->length, PAGE_SIZE);
for (j = 0; j < pages; ++j) {
unsigned long bus_addr, phys_addr;
int ret;
s->dma_address = __map_single(dev, domain->priv, bus_addr = address + s->dma_address + (j << PAGE_SHIFT);
paddr, s->length, dir, dma_mask); phys_addr = (sg_phys(s) & PAGE_MASK) + (j << PAGE_SHIFT);
ret = iommu_map_page(domain, bus_addr, phys_addr, PAGE_SIZE, prot, GFP_ATOMIC);
if (ret)
goto out_unmap;
if (s->dma_address) { mapped_pages += 1;
s->dma_length = s->length; }
mapped_elems++;
} else
goto unmap;
} }
return mapped_elems; /* Everything is mapped - write the right values into s->dma_address */
for_each_sg(sglist, s, nelems, i) {
s->dma_address += address + s->offset;
s->dma_length = s->length;
}
return nelems;
out_unmap:
pr_err("%s: IOMMU mapping error in map_sg (io-pages: %d)\n",
dev_name(dev), npages);
for_each_sg(sglist, s, nelems, i) {
int j, pages = iommu_num_pages(sg_phys(s), s->length, PAGE_SIZE);
for (j = 0; j < pages; ++j) {
unsigned long bus_addr;
unmap: bus_addr = address + s->dma_address + (j << PAGE_SHIFT);
for_each_sg(sglist, s, mapped_elems, i) { iommu_unmap_page(domain, bus_addr, PAGE_SIZE);
if (s->dma_address)
__unmap_single(domain->priv, s->dma_address, if (--mapped_pages)
s->dma_length, dir); goto out_free_iova;
s->dma_address = s->dma_length = 0; }
} }
out_free_iova:
free_iova_fast(&dma_dom->iovad, address, npages);
out_err:
return 0; return 0;
} }
...@@ -2452,18 +2512,17 @@ static void unmap_sg(struct device *dev, struct scatterlist *sglist, ...@@ -2452,18 +2512,17 @@ static void unmap_sg(struct device *dev, struct scatterlist *sglist,
struct dma_attrs *attrs) struct dma_attrs *attrs)
{ {
struct protection_domain *domain; struct protection_domain *domain;
struct scatterlist *s; unsigned long startaddr;
int i; int npages = 2;
domain = get_domain(dev); domain = get_domain(dev);
if (IS_ERR(domain)) if (IS_ERR(domain))
return; return;
for_each_sg(sglist, s, nelems, i) { startaddr = sg_dma_address(sglist) & PAGE_MASK;
__unmap_single(domain->priv, s->dma_address, npages = sg_num_pages(dev, sglist, nelems);
s->dma_length, dir);
s->dma_address = s->dma_length = 0; __unmap_single(domain->priv, startaddr, npages << PAGE_SHIFT, dir);
}
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment