swiotlb_alloc_tlb() can allocate from the DMA atomic pool when a decrypted
pool is needed from atomic context. With CONFIG_DMA_DIRECT_REMAP, the
atomic pool is backed by remapped virtual addresses, which are not the same
as the direct-map addresses returned by phys_to_virt().

swiotlb_init_io_tlb_pool() currently reconstructs the pool virtual address
from the physical start address. For atomic-pool backed allocations this
stores the wrong address in pool->vaddr. Later, swiotlb_free_tlb() passes
that address to dma_free_from_pool(), which will fail to recognize the
chunk

Pass the virtual address returned by the allocation path into
swiotlb_init_io_tlb_pool(), and store that address in pool->vaddr. This
keeps the pool free path using the same virtual address as the allocator.

Signed-off-by: Aneesh Kumar K.V (Arm) <[email protected]>
---
 kernel/dma/swiotlb.c | 32 +++++++++++++++++++-------------
 1 file changed, 19 insertions(+), 13 deletions(-)

diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index 14d834ca298b..e4bd8c9eaeda 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -302,9 +302,9 @@ void __init swiotlb_update_mem_attributes(void)
 }
 
 static void swiotlb_init_io_tlb_pool(struct io_tlb_pool *mem, phys_addr_t 
start,
-               unsigned long nslabs, bool late_alloc, unsigned int nareas)
+               void *vaddr, unsigned long nslabs, bool late_alloc,
+               unsigned int nareas)
 {
-       void *vaddr = phys_to_virt(start);
        unsigned long bytes = nslabs << IO_TLB_SHIFT, i;
 
        mem->nslabs = nslabs;
@@ -445,7 +445,7 @@ void __init swiotlb_init_remap(bool addressing_limit, 
unsigned int flags,
                return;
        }
 
-       swiotlb_init_io_tlb_pool(mem, __pa(tlb), nslabs, false, nareas);
+       swiotlb_init_io_tlb_pool(mem, __pa(tlb), tlb, nslabs, false, nareas);
        add_mem_pool(&io_tlb_default_mem, mem);
 
        if (flags & SWIOTLB_VERBOSE)
@@ -553,7 +553,7 @@ int swiotlb_init_late(size_t size, gfp_t gfp_mask,
                }
        }
 
-       swiotlb_init_io_tlb_pool(mem, virt_to_phys(vstart), nslabs, true,
+       swiotlb_init_io_tlb_pool(mem, virt_to_phys(vstart), vstart, nslabs, 
true,
                                 nareas);
        add_mem_pool(&io_tlb_default_mem, mem);
 
@@ -664,25 +664,26 @@ static struct page *alloc_dma_pages(gfp_t gfp, size_t 
bytes,
  * @phys_limit:        Maximum allowed physical address of the buffer.
  * @attrs:     DMA attributes for the allocation.
  * @gfp:       GFP flags for the allocation.
+ * @vaddr:     Receives the virtual address for the allocated buffer.
  *
  * Return: Allocated pages, or %NULL on allocation failure.
  */
 static struct page *swiotlb_alloc_tlb(struct device *dev, size_t bytes,
-               u64 phys_limit, unsigned long attrs, gfp_t gfp)
+               u64 phys_limit, unsigned long attrs, gfp_t gfp, void **vaddr)
 {
        struct page *page;
 
+       *vaddr = NULL;
+
        /*
         * Allocate from the atomic pools if memory is encrypted and
         * the allocation is atomic, because decrypting may block.
         */
        if (!gfpflags_allow_blocking(gfp) && (attrs & DMA_ATTR_CC_SHARED)) {
-               void *vaddr;
-
                if (!IS_ENABLED(CONFIG_DMA_COHERENT_POOL))
                        return NULL;
 
-               return dma_alloc_from_pool(dev, bytes, &vaddr, gfp,
+               return dma_alloc_from_pool(dev, bytes, vaddr, gfp,
                                           attrs, dma_coherent_ok);
        }
 
@@ -705,6 +706,8 @@ static struct page *swiotlb_alloc_tlb(struct device *dev, 
size_t bytes,
                        return NULL;
        }
 
+       if (page)
+               *vaddr = phys_to_virt(page_to_phys(page));
        return page;
 }
 
@@ -750,6 +753,7 @@ static struct io_tlb_pool *swiotlb_alloc_pool(struct device 
*dev,
 {
        struct io_tlb_pool *pool;
        unsigned int slot_order;
+       void *tlb_vaddr;
        struct page *tlb;
        size_t pool_size;
        size_t tlb_size;
@@ -767,7 +771,8 @@ static struct io_tlb_pool *swiotlb_alloc_pool(struct device 
*dev,
        pool->unencrypted = !!(attrs & DMA_ATTR_CC_SHARED);
 
        tlb_size = nslabs << IO_TLB_SHIFT;
-       while (!(tlb = swiotlb_alloc_tlb(dev, tlb_size, phys_limit, attrs, 
gfp))) {
+       while (!(tlb = swiotlb_alloc_tlb(dev, tlb_size, phys_limit, attrs, gfp,
+                                        &tlb_vaddr))) {
                if (nslabs <= minslabs)
                        goto error_tlb;
                nslabs = ALIGN(nslabs >> 1, IO_TLB_SEGSIZE);
@@ -781,12 +786,12 @@ static struct io_tlb_pool *swiotlb_alloc_pool(struct 
device *dev,
        if (!pool->slots)
                goto error_slots;
 
-       swiotlb_init_io_tlb_pool(pool, page_to_phys(tlb), nslabs, true, nareas);
+       swiotlb_init_io_tlb_pool(pool, page_to_phys(tlb), tlb_vaddr, nslabs,
+                                true, nareas);
        return pool;
 
 error_slots:
-       swiotlb_free_tlb(page_address(tlb), tlb_size,
-                        !!(attrs & DMA_ATTR_CC_SHARED));
+       swiotlb_free_tlb(tlb_vaddr, tlb_size, !!(attrs & DMA_ATTR_CC_SHARED));
 error_tlb:
        kfree(pool);
 error:
@@ -1995,7 +2000,8 @@ static int rmem_swiotlb_device_init(struct reserved_mem 
*rmem,
                        mem->unencrypted = false;
                }
 
-               swiotlb_init_io_tlb_pool(pool, rmem->base, nslabs,
+               swiotlb_init_io_tlb_pool(pool, rmem->base, 
phys_to_virt(rmem->base),
+                                        nslabs,
                                         false, nareas);
                mem->force_bounce = true;
                mem->for_alloc = true;
-- 
2.43.0


Reply via email to