Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

swiotlb: don't assume PA 0 is invalid

In 2.6.29 io_tlb_orig_addr[] got converted from storing virtual addresses
to storing physical ones. While checking virtual addresses against NULL
is a legitimate thing to catch invalid entries, checking physical ones
against zero isn't: There's no guarantee that PFN 0 is reserved on a
particular platform.

Since it is unclear whether the check in swiotlb_tbl_unmap_single() is
actually needed, retain it but check against a guaranteed invalid physical
address. This requires setting up the array in a suitable fashion. And
since the original code failed to invalidate array entries when regions
get unmapped, this is being fixed at once along with adding a similar
check to swiotlb_tbl_sync_single().

Obviously the less intrusive change would be to simply drop the check in
swiotlb_tbl_unmap_single().

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>

authored by

Jan Beulich and committed by
Konrad Rzeszutek Wilk
8e0629c1 7171511e

+18 -10
+18 -10
lib/swiotlb.c
··· 86 86 * We need to save away the original address corresponding to a mapped entry 87 87 * for the sync operations. 88 88 */ 89 + #define INVALID_PHYS_ADDR (~(phys_addr_t)0) 89 90 static phys_addr_t *io_tlb_orig_addr; 90 91 91 92 /* ··· 189 188 io_tlb_list = memblock_virt_alloc( 190 189 PAGE_ALIGN(io_tlb_nslabs * sizeof(int)), 191 190 PAGE_SIZE); 192 - for (i = 0; i < io_tlb_nslabs; i++) 193 - io_tlb_list[i] = IO_TLB_SEGSIZE - OFFSET(i, IO_TLB_SEGSIZE); 194 - io_tlb_index = 0; 195 191 io_tlb_orig_addr = memblock_virt_alloc( 196 192 PAGE_ALIGN(io_tlb_nslabs * sizeof(phys_addr_t)), 197 193 PAGE_SIZE); 194 + for (i = 0; i < io_tlb_nslabs; i++) { 195 + io_tlb_list[i] = IO_TLB_SEGSIZE - OFFSET(i, IO_TLB_SEGSIZE); 196 + io_tlb_orig_addr[i] = INVALID_PHYS_ADDR; 197 + } 198 + io_tlb_index = 0; 198 199 199 200 if (verbose) 200 201 swiotlb_print_info(); ··· 316 313 if (!io_tlb_list) 317 314 goto cleanup3; 318 315 319 - for (i = 0; i < io_tlb_nslabs; i++) 320 - io_tlb_list[i] = IO_TLB_SEGSIZE - OFFSET(i, IO_TLB_SEGSIZE); 321 - io_tlb_index = 0; 322 - 323 316 io_tlb_orig_addr = (phys_addr_t *) 324 317 __get_free_pages(GFP_KERNEL, 325 318 get_order(io_tlb_nslabs * ··· 323 324 if (!io_tlb_orig_addr) 324 325 goto cleanup4; 325 326 326 - memset(io_tlb_orig_addr, 0, io_tlb_nslabs * sizeof(phys_addr_t)); 327 + for (i = 0; i < io_tlb_nslabs; i++) { 328 + io_tlb_list[i] = IO_TLB_SEGSIZE - OFFSET(i, IO_TLB_SEGSIZE); 329 + io_tlb_orig_addr[i] = INVALID_PHYS_ADDR; 330 + } 331 + io_tlb_index = 0; 327 332 328 333 swiotlb_print_info(); 329 334 ··· 559 556 /* 560 557 * First, sync the memory before unmapping the entry 561 558 */ 562 - if (orig_addr && ((dir == DMA_FROM_DEVICE) || (dir == DMA_BIDIRECTIONAL))) 559 + if (orig_addr != INVALID_PHYS_ADDR && 560 + ((dir == DMA_FROM_DEVICE) || (dir == DMA_BIDIRECTIONAL))) 563 561 swiotlb_bounce(orig_addr, tlb_addr, size, DMA_FROM_DEVICE); 564 562 565 563 /* ··· 577 573 * Step 1: return the slots to the free list, merging the 578 574 * slots with superceeding slots 579 575 */ 580 - for (i = index + nslots - 1; i >= index; i--) 576 + for (i = index + nslots - 1; i >= index; i--) { 581 577 io_tlb_list[i] = ++count; 578 + io_tlb_orig_addr[i] = INVALID_PHYS_ADDR; 579 + } 582 580 /* 583 581 * Step 2: merge the returned slots with the preceding slots, 584 582 * if available (non zero) ··· 599 593 int index = (tlb_addr - io_tlb_start) >> IO_TLB_SHIFT; 600 594 phys_addr_t orig_addr = io_tlb_orig_addr[index]; 601 595 596 + if (orig_addr == INVALID_PHYS_ADDR) 597 + return; 602 598 orig_addr += (unsigned long)tlb_addr & ((1 << IO_TLB_SHIFT) - 1); 603 599 604 600 switch (target) {