Skip to content

Commit 79636ca

Browse files
Petr TesarikChristoph Hellwig
authored andcommitted
swiotlb: if swiotlb is full, fall back to a transient memory pool
Try to allocate a transient memory pool if no suitable slots can be found and the respective SWIOTLB is allowed to grow. The transient pool is just enough big for this one bounce buffer. It is inserted into a per-device list of transient memory pools, and it is freed again when the bounce buffer is unmapped. Transient memory pools are kept in an RCU list. A memory barrier is required after adding a new entry, because any address within a transient buffer must be immediately recognized as belonging to the SWIOTLB, even if it is passed to another CPU. Deletion does not require any synchronization beyond RCU ordering guarantees. After a buffer is unmapped, its physical addresses may no longer be passed to the DMA API, so the memory range of the corresponding stale entry in the RCU list never matches. If the memory range gets allocated again, then it happens only after a RCU quiescent state. Since bounce buffers can now be allocated from different pools, add a parameter to swiotlb_alloc_pool() to let the caller know which memory pool is used. Add swiotlb_find_pool() to find the memory pool corresponding to an address. This function is now also used by is_swiotlb_buffer(), because a simple boundary check is no longer sufficient. The logic in swiotlb_alloc_tlb() is taken from __dma_direct_alloc_pages(), simplified and enhanced to use coherent memory pools if needed. Note that this is not the most efficient way to provide a bounce buffer, but when a DMA buffer can't be mapped, something may (and will) actually break. At that point it is better to make an allocation, even if it may be an expensive operation. Signed-off-by: Petr Tesarik <petr.tesarik.ext@huawei.com> Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Christoph Hellwig <hch@lst.de>
1 parent 62708b2 commit 79636ca

File tree

5 files changed

+345
-10
lines changed

5 files changed

+345
-10
lines changed

include/linux/device.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,8 @@ struct device_physical_location {
626626
* @dma_mem: Internal for coherent mem override.
627627
* @cma_area: Contiguous memory area for dma allocations
628628
* @dma_io_tlb_mem: Software IO TLB allocator. Not for driver use.
629+
* @dma_io_tlb_pools: List of transient swiotlb memory pools.
630+
* @dma_io_tlb_lock: Protects changes to the list of active pools.
629631
* @archdata: For arch-specific additions.
630632
* @of_node: Associated device tree node.
631633
* @fwnode: Associated device node supplied by platform firmware.
@@ -731,6 +733,10 @@ struct device {
731733
#endif
732734
#ifdef CONFIG_SWIOTLB
733735
struct io_tlb_mem *dma_io_tlb_mem;
736+
#endif
737+
#ifdef CONFIG_SWIOTLB_DYNAMIC
738+
struct list_head dma_io_tlb_pools;
739+
spinlock_t dma_io_tlb_lock;
734740
#endif
735741
/* arch specific additions */
736742
struct dev_archdata archdata;

include/linux/dma-mapping.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,8 @@ static inline void dma_sync_sgtable_for_device(struct device *dev,
418418
#define dma_get_sgtable(d, t, v, h, s) dma_get_sgtable_attrs(d, t, v, h, s, 0)
419419
#define dma_mmap_coherent(d, v, c, h, s) dma_mmap_attrs(d, v, c, h, s, 0)
420420

421+
bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size);
422+
421423
static inline void *dma_alloc_coherent(struct device *dev, size_t size,
422424
dma_addr_t *dma_handle, gfp_t gfp)
423425
{

include/linux/swiotlb.h

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ dma_addr_t swiotlb_map(struct device *dev, phys_addr_t phys,
8080
* @area_nslabs: Number of slots in each area.
8181
* @areas: Array of memory area descriptors.
8282
* @slots: Array of slot descriptors.
83+
* @node: Member of the IO TLB memory pool list.
84+
* @rcu: RCU head for swiotlb_dyn_free().
85+
* @transient: %true if transient memory pool.
8386
*/
8487
struct io_tlb_pool {
8588
phys_addr_t start;
@@ -91,6 +94,11 @@ struct io_tlb_pool {
9194
unsigned int area_nslabs;
9295
struct io_tlb_area *areas;
9396
struct io_tlb_slot *slots;
97+
#ifdef CONFIG_SWIOTLB_DYNAMIC
98+
struct list_head node;
99+
struct rcu_head rcu;
100+
bool transient;
101+
#endif
94102
};
95103

96104
/**
@@ -122,6 +130,20 @@ struct io_tlb_mem {
122130
#endif
123131
};
124132

133+
#ifdef CONFIG_SWIOTLB_DYNAMIC
134+
135+
struct io_tlb_pool *swiotlb_find_pool(struct device *dev, phys_addr_t paddr);
136+
137+
#else
138+
139+
static inline struct io_tlb_pool *swiotlb_find_pool(struct device *dev,
140+
phys_addr_t paddr)
141+
{
142+
return &dev->dma_io_tlb_mem->defpool;
143+
}
144+
145+
#endif
146+
125147
/**
126148
* is_swiotlb_buffer() - check if a physical address belongs to a swiotlb
127149
* @dev: Device which has mapped the buffer.
@@ -137,7 +159,12 @@ static inline bool is_swiotlb_buffer(struct device *dev, phys_addr_t paddr)
137159
{
138160
struct io_tlb_mem *mem = dev->dma_io_tlb_mem;
139161

140-
return mem && paddr >= mem->defpool.start && paddr < mem->defpool.end;
162+
if (!mem)
163+
return false;
164+
165+
if (IS_ENABLED(CONFIG_SWIOTLB_DYNAMIC))
166+
return swiotlb_find_pool(dev, paddr);
167+
return paddr >= mem->defpool.start && paddr < mem->defpool.end;
141168
}
142169

143170
static inline bool is_swiotlb_force_bounce(struct device *dev)

kernel/dma/direct.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ static gfp_t dma_direct_optimal_gfp_mask(struct device *dev, u64 *phys_limit)
6666
return 0;
6767
}
6868

69-
static bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size)
69+
bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size)
7070
{
7171
dma_addr_t dma_addr = phys_to_dma_direct(dev, phys);
7272

0 commit comments

Comments
 (0)