|
| 1 | +swiotlb: Fix alignment checks when both allocation and DMA masks are present |
| 2 | + |
| 3 | +jira LE-1907 |
| 4 | +cve CVE-2024-35814 |
| 5 | +Rebuild_History Non-Buildable kernel-4.18.0-553.16.1.el8_10 |
| 6 | +commit-author Will Deacon < [email protected]> |
| 7 | +commit 51b30ecb73b481d5fac6ccf2ecb4a309c9ee3310 |
| 8 | +Empty-Commit: Cherry-Pick Conflicts during history rebuild. |
| 9 | +Will be included in final tarball splat. Ref for failed cherry-pick at: |
| 10 | +ciq/ciq_backports/kernel-4.18.0-553.16.1.el8_10/51b30ecb.failed |
| 11 | + |
| 12 | +Nicolin reports that swiotlb buffer allocations fail for an NVME device |
| 13 | +behind an IOMMU using 64KiB pages. This is because we end up with a |
| 14 | +minimum allocation alignment of 64KiB (for the IOMMU to map the buffer |
| 15 | +safely) but a minimum DMA alignment mask corresponding to a 4KiB NVME |
| 16 | +page (i.e. preserving the 4KiB page offset from the original allocation). |
| 17 | +If the original address is not 4KiB-aligned, the allocation will fail |
| 18 | +because swiotlb_search_pool_area() erroneously compares these unmasked |
| 19 | +bits with the 64KiB-aligned candidate allocation. |
| 20 | + |
| 21 | +Tweak swiotlb_search_pool_area() so that the DMA alignment mask is |
| 22 | +reduced based on the required alignment of the allocation. |
| 23 | + |
| 24 | +Fixes: 82612d66d51d ("iommu: Allow the dma-iommu api to use bounce buffers") |
| 25 | +Link: https://lore.kernel.org/r/ [email protected] |
| 26 | + Reported-by: Nicolin Chen < [email protected]> |
| 27 | + Signed-off-by: Will Deacon < [email protected]> |
| 28 | + Reviewed-by: Michael Kelley < [email protected]> |
| 29 | + Tested-by: Nicolin Chen < [email protected]> |
| 30 | + Tested-by: Michael Kelley < [email protected]> |
| 31 | + Signed-off-by: Christoph Hellwig < [email protected]> |
| 32 | +(cherry picked from commit 51b30ecb73b481d5fac6ccf2ecb4a309c9ee3310) |
| 33 | + Signed-off-by: Jonathan Maple < [email protected]> |
| 34 | + |
| 35 | +# Conflicts: |
| 36 | +# kernel/dma/swiotlb.c |
| 37 | +diff --cc kernel/dma/swiotlb.c |
| 38 | +index c0e227dcb45e,f212943e51ca..000000000000 |
| 39 | +--- a/kernel/dma/swiotlb.c |
| 40 | ++++ b/kernel/dma/swiotlb.c |
| 41 | +@@@ -588,21 -923,87 +588,25 @@@ static unsigned int wrap_area_index(str |
| 42 | + } |
| 43 | + |
| 44 | + /* |
| 45 | + - * Track the total used slots with a global atomic value in order to have |
| 46 | + - * correct information to determine the high water mark. The mem_used() |
| 47 | + - * function gives imprecise results because there's no locking across |
| 48 | + - * multiple areas. |
| 49 | + + * Find a suitable number of IO TLB entries size that will fit this request and |
| 50 | + + * allocate a buffer from that IO TLB pool. |
| 51 | + */ |
| 52 | + -#ifdef CONFIG_DEBUG_FS |
| 53 | + -static void inc_used_and_hiwater(struct io_tlb_mem *mem, unsigned int nslots) |
| 54 | + -{ |
| 55 | + - unsigned long old_hiwater, new_used; |
| 56 | + - |
| 57 | + - new_used = atomic_long_add_return(nslots, &mem->total_used); |
| 58 | + - old_hiwater = atomic_long_read(&mem->used_hiwater); |
| 59 | + - do { |
| 60 | + - if (new_used <= old_hiwater) |
| 61 | + - break; |
| 62 | + - } while (!atomic_long_try_cmpxchg(&mem->used_hiwater, |
| 63 | + - &old_hiwater, new_used)); |
| 64 | + -} |
| 65 | + - |
| 66 | + -static void dec_used(struct io_tlb_mem *mem, unsigned int nslots) |
| 67 | + -{ |
| 68 | + - atomic_long_sub(nslots, &mem->total_used); |
| 69 | + -} |
| 70 | + - |
| 71 | + -#else /* !CONFIG_DEBUG_FS */ |
| 72 | + -static void inc_used_and_hiwater(struct io_tlb_mem *mem, unsigned int nslots) |
| 73 | + -{ |
| 74 | + -} |
| 75 | + -static void dec_used(struct io_tlb_mem *mem, unsigned int nslots) |
| 76 | + -{ |
| 77 | + -} |
| 78 | + -#endif /* CONFIG_DEBUG_FS */ |
| 79 | + - |
| 80 | + -#ifdef CONFIG_SWIOTLB_DYNAMIC |
| 81 | + -#ifdef CONFIG_DEBUG_FS |
| 82 | + -static void inc_transient_used(struct io_tlb_mem *mem, unsigned int nslots) |
| 83 | + -{ |
| 84 | + - atomic_long_add(nslots, &mem->transient_nslabs); |
| 85 | + -} |
| 86 | + - |
| 87 | + -static void dec_transient_used(struct io_tlb_mem *mem, unsigned int nslots) |
| 88 | + -{ |
| 89 | + - atomic_long_sub(nslots, &mem->transient_nslabs); |
| 90 | + -} |
| 91 | + - |
| 92 | + -#else /* !CONFIG_DEBUG_FS */ |
| 93 | + -static void inc_transient_used(struct io_tlb_mem *mem, unsigned int nslots) |
| 94 | + -{ |
| 95 | + -} |
| 96 | + -static void dec_transient_used(struct io_tlb_mem *mem, unsigned int nslots) |
| 97 | + -{ |
| 98 | + -} |
| 99 | + -#endif /* CONFIG_DEBUG_FS */ |
| 100 | + -#endif /* CONFIG_SWIOTLB_DYNAMIC */ |
| 101 | + - |
| 102 | + -/** |
| 103 | + - * swiotlb_search_pool_area() - search one memory area in one pool |
| 104 | + - * @dev: Device which maps the buffer. |
| 105 | + - * @pool: Memory pool to be searched. |
| 106 | + - * @area_index: Index of the IO TLB memory area to be searched. |
| 107 | + - * @orig_addr: Original (non-bounced) IO buffer address. |
| 108 | + - * @alloc_size: Total requested size of the bounce buffer, |
| 109 | + - * including initial alignment padding. |
| 110 | + - * @alloc_align_mask: Required alignment of the allocated buffer. |
| 111 | + - * |
| 112 | + - * Find a suitable sequence of IO TLB entries for the request and allocate |
| 113 | + - * a buffer from the given IO TLB memory area. |
| 114 | + - * This function takes care of locking. |
| 115 | + - * |
| 116 | + - * Return: Index of the first allocated slot, or -1 on error. |
| 117 | + - */ |
| 118 | + -static int swiotlb_search_pool_area(struct device *dev, struct io_tlb_pool *pool, |
| 119 | + - int area_index, phys_addr_t orig_addr, size_t alloc_size, |
| 120 | + +static int swiotlb_do_find_slots(struct device *dev, int area_index, |
| 121 | + + phys_addr_t orig_addr, size_t alloc_size, |
| 122 | + unsigned int alloc_align_mask) |
| 123 | + { |
| 124 | + - struct io_tlb_area *area = pool->areas + area_index; |
| 125 | + + struct io_tlb_mem *mem = dev->dma_io_tlb_mem; |
| 126 | + + struct io_tlb_area *area = mem->areas + area_index; |
| 127 | + unsigned long boundary_mask = dma_get_seg_boundary(dev); |
| 128 | + dma_addr_t tbl_dma_addr = |
| 129 | + - phys_to_dma_unencrypted(dev, pool->start) & boundary_mask; |
| 130 | + + phys_to_dma_unencrypted(dev, mem->start) & boundary_mask; |
| 131 | + unsigned long max_slots = get_max_slots(boundary_mask); |
| 132 | +++<<<<<<< HEAD |
| 133 | + + unsigned int iotlb_align_mask = |
| 134 | + + dma_get_min_align_mask(dev) | alloc_align_mask; |
| 135 | +++======= |
| 136 | ++ unsigned int iotlb_align_mask = dma_get_min_align_mask(dev); |
| 137 | +++>>>>>>> 51b30ecb73b4 (swiotlb: Fix alignment checks when both allocation and DMA masks are present) |
| 138 | + unsigned int nslots = nr_slots(alloc_size), stride; |
| 139 | + unsigned int offset = swiotlb_align_offset(dev, orig_addr); |
| 140 | + unsigned int index, slots_checked, count = 0, i; |
| 141 | +@@@ -611,7 -1012,21 +615,25 @@@ |
| 142 | + unsigned int slot_index; |
| 143 | + |
| 144 | + BUG_ON(!nslots); |
| 145 | +++<<<<<<< HEAD |
| 146 | + + BUG_ON(area_index >= mem->nareas); |
| 147 | +++======= |
| 148 | ++ BUG_ON(area_index >= pool->nareas); |
| 149 | ++ |
| 150 | ++ /* |
| 151 | ++ * Ensure that the allocation is at least slot-aligned and update |
| 152 | ++ * 'iotlb_align_mask' to ignore bits that will be preserved when |
| 153 | ++ * offsetting into the allocation. |
| 154 | ++ */ |
| 155 | ++ alloc_align_mask |= (IO_TLB_SIZE - 1); |
| 156 | ++ iotlb_align_mask &= ~alloc_align_mask; |
| 157 | ++ |
| 158 | ++ /* |
| 159 | ++ * For mappings with an alignment requirement don't bother looping to |
| 160 | ++ * unaligned slots once we found an aligned one. |
| 161 | ++ */ |
| 162 | ++ stride = get_max_slots(max(alloc_align_mask, iotlb_align_mask)); |
| 163 | +++>>>>>>> 51b30ecb73b4 (swiotlb: Fix alignment checks when both allocation and DMA masks are present) |
| 164 | + |
| 165 | + /* |
| 166 | + * For allocations of PAGE_SIZE or larger only look for page aligned |
| 167 | +* Unmerged path kernel/dma/swiotlb.c |
0 commit comments