Skip to content

Commit ddd7ad5

Browse files
committed
Merge tag 'dma-maping-6.9-2024-04-14' of git://git.infradead.org/users/hch/dma-mapping
Pull dma-mapping fixes from Christoph Hellwig: - fix up swiotlb buffer padding even more (Petr Tesarik) - fix for partial dma_sync on swiotlb (Michael Kelley) - swiotlb debugfs fix (Dexuan Cui) * tag 'dma-maping-6.9-2024-04-14' of git://git.infradead.org/users/hch/dma-mapping: swiotlb: do not set total_used to 0 in swiotlb_create_debugfs_files() swiotlb: fix swiotlb_bounce() to do partial sync's correctly swiotlb: extend buffer pre-padding to alloc_align_mask if necessary
2 parents 7efd0a7 + a1255cc commit ddd7ad5

File tree

1 file changed

+58
-33
lines changed

1 file changed

+58
-33
lines changed

kernel/dma/swiotlb.c

Lines changed: 58 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,14 @@
6969
* @alloc_size: Size of the allocated buffer.
7070
* @list: The free list describing the number of free entries available
7171
* from each index.
72+
* @pad_slots: Number of preceding padding slots. Valid only in the first
73+
* allocated non-padding slot.
7274
*/
7375
struct io_tlb_slot {
7476
phys_addr_t orig_addr;
7577
size_t alloc_size;
76-
unsigned int list;
78+
unsigned short list;
79+
unsigned short pad_slots;
7780
};
7881

7982
static bool swiotlb_force_bounce;
@@ -287,6 +290,7 @@ static void swiotlb_init_io_tlb_pool(struct io_tlb_pool *mem, phys_addr_t start,
287290
mem->nslabs - i);
288291
mem->slots[i].orig_addr = INVALID_PHYS_ADDR;
289292
mem->slots[i].alloc_size = 0;
293+
mem->slots[i].pad_slots = 0;
290294
}
291295

292296
memset(vaddr, 0, bytes);
@@ -821,12 +825,30 @@ void swiotlb_dev_init(struct device *dev)
821825
#endif
822826
}
823827

824-
/*
825-
* Return the offset into a iotlb slot required to keep the device happy.
828+
/**
829+
* swiotlb_align_offset() - Get required offset into an IO TLB allocation.
830+
* @dev: Owning device.
831+
* @align_mask: Allocation alignment mask.
832+
* @addr: DMA address.
833+
*
834+
* Return the minimum offset from the start of an IO TLB allocation which is
835+
* required for a given buffer address and allocation alignment to keep the
836+
* device happy.
837+
*
838+
* First, the address bits covered by min_align_mask must be identical in the
839+
* original address and the bounce buffer address. High bits are preserved by
840+
* choosing a suitable IO TLB slot, but bits below IO_TLB_SHIFT require extra
841+
* padding bytes before the bounce buffer.
842+
*
843+
* Second, @align_mask specifies which bits of the first allocated slot must
844+
* be zero. This may require allocating additional padding slots, and then the
845+
* offset (in bytes) from the first such padding slot is returned.
826846
*/
827-
static unsigned int swiotlb_align_offset(struct device *dev, u64 addr)
847+
static unsigned int swiotlb_align_offset(struct device *dev,
848+
unsigned int align_mask, u64 addr)
828849
{
829-
return addr & dma_get_min_align_mask(dev) & (IO_TLB_SIZE - 1);
850+
return addr & dma_get_min_align_mask(dev) &
851+
(align_mask | (IO_TLB_SIZE - 1));
830852
}
831853

832854
/*
@@ -841,27 +863,23 @@ static void swiotlb_bounce(struct device *dev, phys_addr_t tlb_addr, size_t size
841863
size_t alloc_size = mem->slots[index].alloc_size;
842864
unsigned long pfn = PFN_DOWN(orig_addr);
843865
unsigned char *vaddr = mem->vaddr + tlb_addr - mem->start;
844-
unsigned int tlb_offset, orig_addr_offset;
866+
int tlb_offset;
845867

846868
if (orig_addr == INVALID_PHYS_ADDR)
847869
return;
848870

849-
tlb_offset = tlb_addr & (IO_TLB_SIZE - 1);
850-
orig_addr_offset = swiotlb_align_offset(dev, orig_addr);
851-
if (tlb_offset < orig_addr_offset) {
852-
dev_WARN_ONCE(dev, 1,
853-
"Access before mapping start detected. orig offset %u, requested offset %u.\n",
854-
orig_addr_offset, tlb_offset);
855-
return;
856-
}
857-
858-
tlb_offset -= orig_addr_offset;
859-
if (tlb_offset > alloc_size) {
860-
dev_WARN_ONCE(dev, 1,
861-
"Buffer overflow detected. Allocation size: %zu. Mapping size: %zu+%u.\n",
862-
alloc_size, size, tlb_offset);
863-
return;
864-
}
871+
/*
872+
* It's valid for tlb_offset to be negative. This can happen when the
873+
* "offset" returned by swiotlb_align_offset() is non-zero, and the
874+
* tlb_addr is pointing within the first "offset" bytes of the second
875+
* or subsequent slots of the allocated swiotlb area. While it's not
876+
* valid for tlb_addr to be pointing within the first "offset" bytes
877+
* of the first slot, there's no way to check for such an error since
878+
* this function can't distinguish the first slot from the second and
879+
* subsequent slots.
880+
*/
881+
tlb_offset = (tlb_addr & (IO_TLB_SIZE - 1)) -
882+
swiotlb_align_offset(dev, 0, orig_addr);
865883

866884
orig_addr += tlb_offset;
867885
alloc_size -= tlb_offset;
@@ -1005,7 +1023,7 @@ static int swiotlb_search_pool_area(struct device *dev, struct io_tlb_pool *pool
10051023
unsigned long max_slots = get_max_slots(boundary_mask);
10061024
unsigned int iotlb_align_mask = dma_get_min_align_mask(dev);
10071025
unsigned int nslots = nr_slots(alloc_size), stride;
1008-
unsigned int offset = swiotlb_align_offset(dev, orig_addr);
1026+
unsigned int offset = swiotlb_align_offset(dev, 0, orig_addr);
10091027
unsigned int index, slots_checked, count = 0, i;
10101028
unsigned long flags;
10111029
unsigned int slot_base;
@@ -1328,11 +1346,12 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,
13281346
unsigned long attrs)
13291347
{
13301348
struct io_tlb_mem *mem = dev->dma_io_tlb_mem;
1331-
unsigned int offset = swiotlb_align_offset(dev, orig_addr);
1349+
unsigned int offset;
13321350
struct io_tlb_pool *pool;
13331351
unsigned int i;
13341352
int index;
13351353
phys_addr_t tlb_addr;
1354+
unsigned short pad_slots;
13361355

13371356
if (!mem || !mem->nslabs) {
13381357
dev_warn_ratelimited(dev,
@@ -1349,6 +1368,7 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,
13491368
return (phys_addr_t)DMA_MAPPING_ERROR;
13501369
}
13511370

1371+
offset = swiotlb_align_offset(dev, alloc_align_mask, orig_addr);
13521372
index = swiotlb_find_slots(dev, orig_addr,
13531373
alloc_size + offset, alloc_align_mask, &pool);
13541374
if (index == -1) {
@@ -1364,6 +1384,10 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,
13641384
* This is needed when we sync the memory. Then we sync the buffer if
13651385
* needed.
13661386
*/
1387+
pad_slots = offset >> IO_TLB_SHIFT;
1388+
offset &= (IO_TLB_SIZE - 1);
1389+
index += pad_slots;
1390+
pool->slots[index].pad_slots = pad_slots;
13671391
for (i = 0; i < nr_slots(alloc_size + offset); i++)
13681392
pool->slots[index + i].orig_addr = slot_addr(orig_addr, i);
13691393
tlb_addr = slot_addr(pool->start, index) + offset;
@@ -1384,13 +1408,17 @@ static void swiotlb_release_slots(struct device *dev, phys_addr_t tlb_addr)
13841408
{
13851409
struct io_tlb_pool *mem = swiotlb_find_pool(dev, tlb_addr);
13861410
unsigned long flags;
1387-
unsigned int offset = swiotlb_align_offset(dev, tlb_addr);
1388-
int index = (tlb_addr - offset - mem->start) >> IO_TLB_SHIFT;
1389-
int nslots = nr_slots(mem->slots[index].alloc_size + offset);
1390-
int aindex = index / mem->area_nslabs;
1391-
struct io_tlb_area *area = &mem->areas[aindex];
1411+
unsigned int offset = swiotlb_align_offset(dev, 0, tlb_addr);
1412+
int index, nslots, aindex;
1413+
struct io_tlb_area *area;
13921414
int count, i;
13931415

1416+
index = (tlb_addr - offset - mem->start) >> IO_TLB_SHIFT;
1417+
index -= mem->slots[index].pad_slots;
1418+
nslots = nr_slots(mem->slots[index].alloc_size + offset);
1419+
aindex = index / mem->area_nslabs;
1420+
area = &mem->areas[aindex];
1421+
13941422
/*
13951423
* Return the buffer to the free list by setting the corresponding
13961424
* entries to indicate the number of contiguous entries available.
@@ -1413,6 +1441,7 @@ static void swiotlb_release_slots(struct device *dev, phys_addr_t tlb_addr)
14131441
mem->slots[i].list = ++count;
14141442
mem->slots[i].orig_addr = INVALID_PHYS_ADDR;
14151443
mem->slots[i].alloc_size = 0;
1444+
mem->slots[i].pad_slots = 0;
14161445
}
14171446

14181447
/*
@@ -1647,9 +1676,6 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_io_tlb_hiwater, io_tlb_hiwater_get,
16471676
static void swiotlb_create_debugfs_files(struct io_tlb_mem *mem,
16481677
const char *dirname)
16491678
{
1650-
atomic_long_set(&mem->total_used, 0);
1651-
atomic_long_set(&mem->used_hiwater, 0);
1652-
16531679
mem->debugfs = debugfs_create_dir(dirname, io_tlb_default_mem.debugfs);
16541680
if (!mem->nslabs)
16551681
return;
@@ -1660,7 +1686,6 @@ static void swiotlb_create_debugfs_files(struct io_tlb_mem *mem,
16601686
debugfs_create_file("io_tlb_used_hiwater", 0600, mem->debugfs, mem,
16611687
&fops_io_tlb_hiwater);
16621688
#ifdef CONFIG_SWIOTLB_DYNAMIC
1663-
atomic_long_set(&mem->transient_nslabs, 0);
16641689
debugfs_create_file("io_tlb_transient_nslabs", 0400, mem->debugfs,
16651690
mem, &fops_io_tlb_transient_used);
16661691
#endif

0 commit comments

Comments
 (0)