aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/iommu/amd_iommu.c111
1 files changed, 85 insertions, 26 deletions
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 281cacbd5816..acad37c3f0a1 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -2396,50 +2396,110 @@ static void unmap_page(struct device *dev, dma_addr_t dma_addr, size_t size,
2396 __unmap_single(domain->priv, dma_addr, size, dir); 2396 __unmap_single(domain->priv, dma_addr, size, dir);
2397} 2397}
2398 2398
2399static int sg_num_pages(struct device *dev,
2400 struct scatterlist *sglist,
2401 int nelems)
2402{
2403 unsigned long mask, boundary_size;
2404 struct scatterlist *s;
2405 int i, npages = 0;
2406
2407 mask = dma_get_seg_boundary(dev);
2408 boundary_size = mask + 1 ? ALIGN(mask + 1, PAGE_SIZE) >> PAGE_SHIFT :
2409 1UL << (BITS_PER_LONG - PAGE_SHIFT);
2410
2411 for_each_sg(sglist, s, nelems, i) {
2412 int p, n;
2413
2414 s->dma_address = npages << PAGE_SHIFT;
2415 p = npages % boundary_size;
2416 n = iommu_num_pages(sg_phys(s), s->length, PAGE_SIZE);
2417 if (p + n > boundary_size)
2418 npages += boundary_size - p;
2419 npages += n;
2420 }
2421
2422 return npages;
2423}
2424
2399/* 2425/*
2400 * The exported map_sg function for dma_ops (handles scatter-gather 2426 * The exported map_sg function for dma_ops (handles scatter-gather
2401 * lists). 2427 * lists).
2402 */ 2428 */
2403static int map_sg(struct device *dev, struct scatterlist *sglist, 2429static int map_sg(struct device *dev, struct scatterlist *sglist,
2404 int nelems, enum dma_data_direction dir, 2430 int nelems, enum dma_data_direction direction,
2405 struct dma_attrs *attrs) 2431 struct dma_attrs *attrs)
2406{ 2432{
2433 int mapped_pages = 0, npages = 0, prot = 0, i;
2407 struct protection_domain *domain; 2434 struct protection_domain *domain;
2408 int i; 2435 struct dma_ops_domain *dma_dom;
2409 struct scatterlist *s; 2436 struct scatterlist *s;
2410 phys_addr_t paddr; 2437 unsigned long address;
2411 int mapped_elems = 0;
2412 u64 dma_mask; 2438 u64 dma_mask;
2413 2439
2414 domain = get_domain(dev); 2440 domain = get_domain(dev);
2415 if (IS_ERR(domain)) 2441 if (IS_ERR(domain))
2416 return 0; 2442 return 0;
2417 2443
2444 dma_dom = domain->priv;
2418 dma_mask = *dev->dma_mask; 2445 dma_mask = *dev->dma_mask;
2419 2446
2447 npages = sg_num_pages(dev, sglist, nelems);
2448
2449 address = dma_ops_alloc_iova(dev, dma_dom, npages, dma_mask);
2450 if (address == DMA_ERROR_CODE)
2451 goto out_err;
2452
2453 prot = dir2prot(direction);
2454
2455 /* Map all sg entries */
2420 for_each_sg(sglist, s, nelems, i) { 2456 for_each_sg(sglist, s, nelems, i) {
2421 paddr = sg_phys(s); 2457 int j, pages = iommu_num_pages(sg_phys(s), s->length, PAGE_SIZE);
2458
2459 for (j = 0; j < pages; ++j) {
2460 unsigned long bus_addr, phys_addr;
2461 int ret;
2422 2462
2423 s->dma_address = __map_single(dev, domain->priv, 2463 bus_addr = address + s->dma_address + (j << PAGE_SHIFT);
2424 paddr, s->length, dir, dma_mask); 2464 phys_addr = (sg_phys(s) & PAGE_MASK) + (j << PAGE_SHIFT);
2465 ret = iommu_map_page(domain, bus_addr, phys_addr, PAGE_SIZE, prot, GFP_ATOMIC);
2466 if (ret)
2467 goto out_unmap;
2425 2468
2426 if (s->dma_address) { 2469 mapped_pages += 1;
2427 s->dma_length = s->length; 2470 }
2428 mapped_elems++;
2429 } else
2430 goto unmap;
2431 } 2471 }
2432 2472
2433 return mapped_elems; 2473 /* Everything is mapped - write the right values into s->dma_address */
2474 for_each_sg(sglist, s, nelems, i) {
2475 s->dma_address += address + s->offset;
2476 s->dma_length = s->length;
2477 }
2478
2479 return nelems;
2480
2481out_unmap:
2482 pr_err("%s: IOMMU mapping error in map_sg (io-pages: %d)\n",
2483 dev_name(dev), npages);
2484
2485 for_each_sg(sglist, s, nelems, i) {
2486 int j, pages = iommu_num_pages(sg_phys(s), s->length, PAGE_SIZE);
2487
2488 for (j = 0; j < pages; ++j) {
2489 unsigned long bus_addr;
2434 2490
2435unmap: 2491 bus_addr = address + s->dma_address + (j << PAGE_SHIFT);
2436 for_each_sg(sglist, s, mapped_elems, i) { 2492 iommu_unmap_page(domain, bus_addr, PAGE_SIZE);
2437 if (s->dma_address) 2493
2438 __unmap_single(domain->priv, s->dma_address, 2494 if (--mapped_pages)
2439 s->dma_length, dir); 2495 goto out_free_iova;
2440 s->dma_address = s->dma_length = 0; 2496 }
2441 } 2497 }
2442 2498
2499out_free_iova:
2500 free_iova_fast(&dma_dom->iovad, address, npages);
2501
2502out_err:
2443 return 0; 2503 return 0;
2444} 2504}
2445 2505
@@ -2452,18 +2512,17 @@ static void unmap_sg(struct device *dev, struct scatterlist *sglist,
2452 struct dma_attrs *attrs) 2512 struct dma_attrs *attrs)
2453{ 2513{
2454 struct protection_domain *domain; 2514 struct protection_domain *domain;
2455 struct scatterlist *s; 2515 unsigned long startaddr;
2456 int i; 2516 int npages = 2;
2457 2517
2458 domain = get_domain(dev); 2518 domain = get_domain(dev);
2459 if (IS_ERR(domain)) 2519 if (IS_ERR(domain))
2460 return; 2520 return;
2461 2521
2462 for_each_sg(sglist, s, nelems, i) { 2522 startaddr = sg_dma_address(sglist) & PAGE_MASK;
2463 __unmap_single(domain->priv, s->dma_address, 2523 npages = sg_num_pages(dev, sglist, nelems);
2464 s->dma_length, dir); 2524
2465 s->dma_address = s->dma_length = 0; 2525 __unmap_single(domain->priv, startaddr, npages << PAGE_SHIFT, dir);
2466 }
2467} 2526}
2468 2527
2469/* 2528/*