Skip to content

Commit 327e2c9

Browse files
mhklinuxChristoph Hellwig
authored andcommitted
swiotlb: remove alloc_size argument to swiotlb_tbl_map_single()
Currently swiotlb_tbl_map_single() takes alloc_align_mask and alloc_size arguments to specify an swiotlb allocation that is larger than mapping_size. This larger allocation is used solely by iommu_dma_map_single() to handle untrusted devices that should not have DMA visibility to memory pages that are partially used for unrelated kernel data. Having two arguments to specify the allocation is redundant. While alloc_align_mask naturally specifies the alignment of the starting address of the allocation, it can also implicitly specify the size by rounding up the mapping_size to that alignment. Additionally, the current approach has an edge case bug. iommu_dma_map_page() already does the rounding up to compute the alloc_size argument. But swiotlb_tbl_map_single() then calculates the alignment offset based on the DMA min_align_mask, and adds that offset to alloc_size. If the offset is non-zero, the addition may result in a value that is larger than the max the swiotlb can allocate. If the rounding up is done _after_ the alignment offset is added to the mapping_size (and the original mapping_size conforms to the value returned by swiotlb_max_mapping_size), then the max that the swiotlb can allocate will not be exceeded. In view of these issues, simplify the swiotlb_tbl_map_single() interface by removing the alloc_size argument. Most call sites pass the same value for mapping_size and alloc_size, and they pass alloc_align_mask as zero. Just remove the redundant argument from these callers, as they will see no functional change. For iommu_dma_map_page() also remove the alloc_size argument, and have swiotlb_tbl_map_single() compute the alloc_size by rounding up mapping_size after adding the offset based on min_align_mask. This has the side effect of fixing the edge case bug but with no other functional change. Also add a sanity test on the alloc_align_mask. While IOMMU code currently ensures the granule is not larger than PAGE_SIZE, if that guarantee were to be removed in the future, the downstream effect on the swiotlb might go unnoticed until strange allocation failures occurred. Tested on an ARM64 system with 16K page size and some kernel test-only hackery to allow modifying the DMA min_align_mask and the granule size that becomes the alloc_align_mask. Tested these combinations with a variety of original memory addresses and sizes, including those that reproduce the edge case bug: * 4K granule and 0 min_align_mask * 4K granule and 0xFFF min_align_mask (4K - 1) * 16K granule and 0xFFF min_align_mask * 64K granule and 0xFFF min_align_mask * 64K granule and 0x3FFF min_align_mask (16K - 1) With the changes, all combinations pass. Signed-off-by: Michael Kelley <[email protected]> Reviewed-by: Petr Tesarik <[email protected]> Signed-off-by: Christoph Hellwig <[email protected]>
1 parent c93f261 commit 327e2c9

File tree

4 files changed

+45
-17
lines changed

4 files changed

+45
-17
lines changed

drivers/iommu/dma-iommu.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1165,7 +1165,7 @@ static dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
11651165
trace_swiotlb_bounced(dev, phys, size);
11661166

11671167
aligned_size = iova_align(iovad, size);
1168-
phys = swiotlb_tbl_map_single(dev, phys, size, aligned_size,
1168+
phys = swiotlb_tbl_map_single(dev, phys, size,
11691169
iova_mask(iovad), dir, attrs);
11701170

11711171
if (phys == DMA_MAPPING_ERROR)

drivers/xen/swiotlb-xen.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ static dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page,
216216
*/
217217
trace_swiotlb_bounced(dev, dev_addr, size);
218218

219-
map = swiotlb_tbl_map_single(dev, phys, size, size, 0, dir, attrs);
219+
map = swiotlb_tbl_map_single(dev, phys, size, 0, dir, attrs);
220220
if (map == (phys_addr_t)DMA_MAPPING_ERROR)
221221
return DMA_MAPPING_ERROR;
222222

include/linux/swiotlb.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ int swiotlb_init_late(size_t size, gfp_t gfp_mask,
4343
extern void __init swiotlb_update_mem_attributes(void);
4444

4545
phys_addr_t swiotlb_tbl_map_single(struct device *hwdev, phys_addr_t phys,
46-
size_t mapping_size, size_t alloc_size,
46+
size_t mapping_size,
4747
unsigned int alloc_aligned_mask, enum dma_data_direction dir,
4848
unsigned long attrs);
4949

kernel/dma/swiotlb.c

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,15 +1340,40 @@ static unsigned long mem_used(struct io_tlb_mem *mem)
13401340

13411341
#endif /* CONFIG_DEBUG_FS */
13421342

1343+
/**
1344+
* swiotlb_tbl_map_single() - bounce buffer map a single contiguous physical area
1345+
* @dev: Device which maps the buffer.
1346+
* @orig_addr: Original (non-bounced) physical IO buffer address
1347+
* @mapping_size: Requested size of the actual bounce buffer, excluding
1348+
* any pre- or post-padding for alignment
1349+
* @alloc_align_mask: Required start and end alignment of the allocated buffer
1350+
* @dir: DMA direction
1351+
* @attrs: Optional DMA attributes for the map operation
1352+
*
1353+
* Find and allocate a suitable sequence of IO TLB slots for the request.
1354+
* The allocated space starts at an alignment specified by alloc_align_mask,
1355+
* and the size of the allocated space is rounded up so that the total amount
1356+
* of allocated space is a multiple of (alloc_align_mask + 1). If
1357+
* alloc_align_mask is zero, the allocated space may be at any alignment and
1358+
* the size is not rounded up.
1359+
*
1360+
* The returned address is within the allocated space and matches the bits
1361+
* of orig_addr that are specified in the DMA min_align_mask for the device. As
1362+
* such, this returned address may be offset from the beginning of the allocated
1363+
* space. The bounce buffer space starting at the returned address for
1364+
* mapping_size bytes is initialized to the contents of the original IO buffer
1365+
* area. Any pre-padding (due to an offset) and any post-padding (due to
1366+
* rounding-up the size) is not initialized.
1367+
*/
13431368
phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,
1344-
size_t mapping_size, size_t alloc_size,
1345-
unsigned int alloc_align_mask, enum dma_data_direction dir,
1346-
unsigned long attrs)
1369+
size_t mapping_size, unsigned int alloc_align_mask,
1370+
enum dma_data_direction dir, unsigned long attrs)
13471371
{
13481372
struct io_tlb_mem *mem = dev->dma_io_tlb_mem;
13491373
unsigned int offset;
13501374
struct io_tlb_pool *pool;
13511375
unsigned int i;
1376+
size_t size;
13521377
int index;
13531378
phys_addr_t tlb_addr;
13541379
unsigned short pad_slots;
@@ -1362,20 +1387,24 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,
13621387
if (cc_platform_has(CC_ATTR_MEM_ENCRYPT))
13631388
pr_warn_once("Memory encryption is active and system is using DMA bounce buffers\n");
13641389

1365-
if (mapping_size > alloc_size) {
1366-
dev_warn_once(dev, "Invalid sizes (mapping: %zd bytes, alloc: %zd bytes)",
1367-
mapping_size, alloc_size);
1368-
return (phys_addr_t)DMA_MAPPING_ERROR;
1369-
}
1390+
/*
1391+
* The default swiotlb memory pool is allocated with PAGE_SIZE
1392+
* alignment. If a mapping is requested with larger alignment,
1393+
* the mapping may be unable to use the initial slot(s) in all
1394+
* sets of IO_TLB_SEGSIZE slots. In such case, a mapping request
1395+
* of or near the maximum mapping size would always fail.
1396+
*/
1397+
dev_WARN_ONCE(dev, alloc_align_mask > ~PAGE_MASK,
1398+
"Alloc alignment may prevent fulfilling requests with max mapping_size\n");
13701399

13711400
offset = swiotlb_align_offset(dev, alloc_align_mask, orig_addr);
1372-
index = swiotlb_find_slots(dev, orig_addr,
1373-
alloc_size + offset, alloc_align_mask, &pool);
1401+
size = ALIGN(mapping_size + offset, alloc_align_mask + 1);
1402+
index = swiotlb_find_slots(dev, orig_addr, size, alloc_align_mask, &pool);
13741403
if (index == -1) {
13751404
if (!(attrs & DMA_ATTR_NO_WARN))
13761405
dev_warn_ratelimited(dev,
13771406
"swiotlb buffer is full (sz: %zd bytes), total %lu (slots), used %lu (slots)\n",
1378-
alloc_size, mem->nslabs, mem_used(mem));
1407+
size, mem->nslabs, mem_used(mem));
13791408
return (phys_addr_t)DMA_MAPPING_ERROR;
13801409
}
13811410

@@ -1388,7 +1417,7 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,
13881417
offset &= (IO_TLB_SIZE - 1);
13891418
index += pad_slots;
13901419
pool->slots[index].pad_slots = pad_slots;
1391-
for (i = 0; i < nr_slots(alloc_size + offset); i++)
1420+
for (i = 0; i < (nr_slots(size) - pad_slots); i++)
13921421
pool->slots[index + i].orig_addr = slot_addr(orig_addr, i);
13931422
tlb_addr = slot_addr(pool->start, index) + offset;
13941423
/*
@@ -1543,8 +1572,7 @@ dma_addr_t swiotlb_map(struct device *dev, phys_addr_t paddr, size_t size,
15431572

15441573
trace_swiotlb_bounced(dev, phys_to_dma(dev, paddr), size);
15451574

1546-
swiotlb_addr = swiotlb_tbl_map_single(dev, paddr, size, size, 0, dir,
1547-
attrs);
1575+
swiotlb_addr = swiotlb_tbl_map_single(dev, paddr, size, 0, dir, attrs);
15481576
if (swiotlb_addr == (phys_addr_t)DMA_MAPPING_ERROR)
15491577
return DMA_MAPPING_ERROR;
15501578

0 commit comments

Comments
 (0)