Skip to content

Commit 8ac0406

Browse files
Petr TesarikChristoph Hellwig
Petr Tesarik
authored and
Christoph Hellwig
committed
swiotlb: reduce the number of areas to match actual memory pool size
Although the desired size of the SWIOTLB memory pool is increased in swiotlb_adjust_nareas() to match the number of areas, the actual allocation may be smaller, which may require reducing the number of areas. For example, Xen uses swiotlb_init_late(), which in turn uses the page allocator. On x86, page size is 4 KiB and MAX_ORDER is 10 (1024 pages), resulting in a maximum memory pool size of 4 MiB. This corresponds to 2048 slots of 2 KiB each. The minimum area size is 128 (IO_TLB_SEGSIZE), allowing at most 2048 / 128 = 16 areas. If num_possible_cpus() is greater than the maximum number of areas, areas are smaller than IO_TLB_SEGSIZE and contiguous groups of free slots will span multiple areas. When allocating and freeing slots, only one area will be properly locked, causing race conditions on the unlocked slots and ultimately data corruption, kernel hangs and crashes. Fixes: 20347fc ("swiotlb: split up the global swiotlb lock") Signed-off-by: Petr Tesarik <[email protected]> Reviewed-by: Roberto Sassu <[email protected]> Signed-off-by: Christoph Hellwig <[email protected]>
1 parent aabd126 commit 8ac0406

File tree

1 file changed

+24
-3
lines changed

1 file changed

+24
-3
lines changed

kernel/dma/swiotlb.c

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,23 @@ static void swiotlb_adjust_nareas(unsigned int nareas)
138138
(default_nslabs << IO_TLB_SHIFT) >> 20);
139139
}
140140

141+
/**
142+
* limit_nareas() - get the maximum number of areas for a given memory pool size
143+
* @nareas: Desired number of areas.
144+
* @nslots: Total number of slots in the memory pool.
145+
*
146+
* Limit the number of areas to the maximum possible number of areas in
147+
* a memory pool of the given size.
148+
*
149+
* Return: Maximum possible number of areas.
150+
*/
151+
static unsigned int limit_nareas(unsigned int nareas, unsigned long nslots)
152+
{
153+
if (nslots < nareas * IO_TLB_SEGSIZE)
154+
return nslots / IO_TLB_SEGSIZE;
155+
return nareas;
156+
}
157+
141158
static int __init
142159
setup_io_tlb_npages(char *str)
143160
{
@@ -297,6 +314,7 @@ void __init swiotlb_init_remap(bool addressing_limit, unsigned int flags,
297314
{
298315
struct io_tlb_mem *mem = &io_tlb_default_mem;
299316
unsigned long nslabs;
317+
unsigned int nareas;
300318
size_t alloc_size;
301319
void *tlb;
302320

@@ -309,10 +327,12 @@ void __init swiotlb_init_remap(bool addressing_limit, unsigned int flags,
309327
swiotlb_adjust_nareas(num_possible_cpus());
310328

311329
nslabs = default_nslabs;
330+
nareas = limit_nareas(default_nareas, nslabs);
312331
while ((tlb = swiotlb_memblock_alloc(nslabs, flags, remap)) == NULL) {
313332
if (nslabs <= IO_TLB_MIN_SLABS)
314333
return;
315334
nslabs = ALIGN(nslabs >> 1, IO_TLB_SEGSIZE);
335+
nareas = limit_nareas(nareas, nslabs);
316336
}
317337

318338
if (default_nslabs != nslabs) {
@@ -358,6 +378,7 @@ int swiotlb_init_late(size_t size, gfp_t gfp_mask,
358378
{
359379
struct io_tlb_mem *mem = &io_tlb_default_mem;
360380
unsigned long nslabs = ALIGN(size >> IO_TLB_SHIFT, IO_TLB_SEGSIZE);
381+
unsigned int nareas;
361382
unsigned char *vstart = NULL;
362383
unsigned int order, area_order;
363384
bool retried = false;
@@ -403,8 +424,8 @@ int swiotlb_init_late(size_t size, gfp_t gfp_mask,
403424
(PAGE_SIZE << order) >> 20);
404425
}
405426

406-
area_order = get_order(array_size(sizeof(*mem->areas),
407-
default_nareas));
427+
nareas = limit_nareas(default_nareas, nslabs);
428+
area_order = get_order(array_size(sizeof(*mem->areas), nareas));
408429
mem->areas = (struct io_tlb_area *)
409430
__get_free_pages(GFP_KERNEL | __GFP_ZERO, area_order);
410431
if (!mem->areas)
@@ -418,7 +439,7 @@ int swiotlb_init_late(size_t size, gfp_t gfp_mask,
418439
set_memory_decrypted((unsigned long)vstart,
419440
(nslabs << IO_TLB_SHIFT) >> PAGE_SHIFT);
420441
swiotlb_init_io_tlb_mem(mem, virt_to_phys(vstart), nslabs, 0, true,
421-
default_nareas);
442+
nareas);
422443

423444
swiotlb_print_info();
424445
return 0;

0 commit comments

Comments
 (0)