Skip to content

Commit 1375c8b

Browse files
committed
swiotlb: Fix alignment checks when both allocation and DMA masks are present
jira LE-1907 cve CVE-2024-35814 Rebuild_History Non-Buildable kernel-4.18.0-553.16.1.el8_10 commit-author Will Deacon <[email protected]> commit 51b30ec Empty-Commit: Cherry-Pick Conflicts during history rebuild. Will be included in final tarball splat. Ref for failed cherry-pick at: ciq/ciq_backports/kernel-4.18.0-553.16.1.el8_10/51b30ecb.failed Nicolin reports that swiotlb buffer allocations fail for an NVME device behind an IOMMU using 64KiB pages. This is because we end up with a minimum allocation alignment of 64KiB (for the IOMMU to map the buffer safely) but a minimum DMA alignment mask corresponding to a 4KiB NVME page (i.e. preserving the 4KiB page offset from the original allocation). If the original address is not 4KiB-aligned, the allocation will fail because swiotlb_search_pool_area() erroneously compares these unmasked bits with the 64KiB-aligned candidate allocation. Tweak swiotlb_search_pool_area() so that the DMA alignment mask is reduced based on the required alignment of the allocation. Fixes: 82612d6 ("iommu: Allow the dma-iommu api to use bounce buffers") Link: https://lore.kernel.org/r/[email protected] Reported-by: Nicolin Chen <[email protected]> Signed-off-by: Will Deacon <[email protected]> Reviewed-by: Michael Kelley <[email protected]> Tested-by: Nicolin Chen <[email protected]> Tested-by: Michael Kelley <[email protected]> Signed-off-by: Christoph Hellwig <[email protected]> (cherry picked from commit 51b30ec) Signed-off-by: Jonathan Maple <[email protected]> # Conflicts: # kernel/dma/swiotlb.c
1 parent 3cf5abf commit 1375c8b

File tree

1 file changed

+167
-0
lines changed

1 file changed

+167
-0
lines changed
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
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

Comments
 (0)