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

iommu/dma: force bouncing if the size is not cacheline-aligned

Similarly to the direct DMA, bounce small allocations as they may have
originated from a kmalloc() cache not safe for DMA. Unlike the direct
DMA, iommu_dma_map_sg() cannot call iommu_dma_map_sg_swiotlb() for all
non-coherent devices as this would break some cases where the iova is
expected to be contiguous (dmabuf). Instead, scan the scatterlist for
any small sizes and only go the swiotlb path if any element of the list
needs bouncing (note that iommu_dma_map_page() would still only bounce
those buffers which are not DMA-aligned).

To avoid scanning the scatterlist on the 'sync' operations, introduce an
SG_DMA_SWIOTLB flag set by iommu_dma_map_sg_swiotlb(). The
dev_use_swiotlb() function together with the newly added
dev_use_sg_swiotlb() now check for both untrusted devices and unaligned
kmalloc() buffers (suggested by Robin Murphy).

Link: https://lkml.kernel.org/r/20230612153201.554742-16-catalin.marinas@arm.com
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Reviewed-by: Robin Murphy <robin.murphy@arm.com>
Tested-by: Isaac J. Manjarres <isaacmanjarres@google.com>
Cc: Joerg Roedel <joro@8bytes.org>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Alasdair Kergon <agk@redhat.com>
Cc: Ard Biesheuvel <ardb@kernel.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Daniel Vetter <daniel@ffwll.ch>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Herbert Xu <herbert@gondor.apana.org.au>
Cc: Jerry Snitselaar <jsnitsel@redhat.com>
Cc: Jonathan Cameron <jic23@kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Lars-Peter Clausen <lars@metafoo.de>
Cc: Logan Gunthorpe <logang@deltatee.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Mike Snitzer <snitzer@kernel.org>
Cc: "Rafael J. Wysocki" <rafael@kernel.org>
Cc: Saravana Kannan <saravanak@google.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Will Deacon <will@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Catalin Marinas and committed by
Andrew Morton
861370f4 370645f4

+81 -11
+1
drivers/iommu/Kconfig
··· 152 152 select IOMMU_IOVA 153 153 select IRQ_MSI_IOMMU 154 154 select NEED_SG_DMA_LENGTH 155 + select NEED_SG_DMA_FLAGS if SWIOTLB 155 156 156 157 # Shared Virtual Addressing 157 158 config IOMMU_SVA
+41 -9
drivers/iommu/dma-iommu.c
··· 520 520 return dev_is_pci(dev) && to_pci_dev(dev)->untrusted; 521 521 } 522 522 523 - static bool dev_use_swiotlb(struct device *dev) 523 + static bool dev_use_swiotlb(struct device *dev, size_t size, 524 + enum dma_data_direction dir) 524 525 { 525 - return IS_ENABLED(CONFIG_SWIOTLB) && dev_is_untrusted(dev); 526 + return IS_ENABLED(CONFIG_SWIOTLB) && 527 + (dev_is_untrusted(dev) || 528 + dma_kmalloc_needs_bounce(dev, size, dir)); 529 + } 530 + 531 + static bool dev_use_sg_swiotlb(struct device *dev, struct scatterlist *sg, 532 + int nents, enum dma_data_direction dir) 533 + { 534 + struct scatterlist *s; 535 + int i; 536 + 537 + if (!IS_ENABLED(CONFIG_SWIOTLB)) 538 + return false; 539 + 540 + if (dev_is_untrusted(dev)) 541 + return true; 542 + 543 + /* 544 + * If kmalloc() buffers are not DMA-safe for this device and 545 + * direction, check the individual lengths in the sg list. If any 546 + * element is deemed unsafe, use the swiotlb for bouncing. 547 + */ 548 + if (!dma_kmalloc_safe(dev, dir)) { 549 + for_each_sg(sg, s, nents, i) 550 + if (!dma_kmalloc_size_aligned(s->length)) 551 + return true; 552 + } 553 + 554 + return false; 526 555 } 527 556 528 557 /** ··· 951 922 { 952 923 phys_addr_t phys; 953 924 954 - if (dev_is_dma_coherent(dev) && !dev_use_swiotlb(dev)) 925 + if (dev_is_dma_coherent(dev) && !dev_use_swiotlb(dev, size, dir)) 955 926 return; 956 927 957 928 phys = iommu_iova_to_phys(iommu_get_dma_domain(dev), dma_handle); ··· 967 938 { 968 939 phys_addr_t phys; 969 940 970 - if (dev_is_dma_coherent(dev) && !dev_use_swiotlb(dev)) 941 + if (dev_is_dma_coherent(dev) && !dev_use_swiotlb(dev, size, dir)) 971 942 return; 972 943 973 944 phys = iommu_iova_to_phys(iommu_get_dma_domain(dev), dma_handle); ··· 985 956 struct scatterlist *sg; 986 957 int i; 987 958 988 - if (dev_use_swiotlb(dev)) 959 + if (sg_dma_is_swiotlb(sgl)) 989 960 for_each_sg(sgl, sg, nelems, i) 990 961 iommu_dma_sync_single_for_cpu(dev, sg_dma_address(sg), 991 962 sg->length, dir); ··· 1001 972 struct scatterlist *sg; 1002 973 int i; 1003 974 1004 - if (dev_use_swiotlb(dev)) 975 + if (sg_dma_is_swiotlb(sgl)) 1005 976 for_each_sg(sgl, sg, nelems, i) 1006 977 iommu_dma_sync_single_for_device(dev, 1007 978 sg_dma_address(sg), ··· 1027 998 * If both the physical buffer start address and size are 1028 999 * page aligned, we don't need to use a bounce page. 1029 1000 */ 1030 - if (dev_use_swiotlb(dev) && iova_offset(iovad, phys | size)) { 1001 + if (dev_use_swiotlb(dev, size, dir) && 1002 + iova_offset(iovad, phys | size)) { 1031 1003 void *padding_start; 1032 1004 size_t padding_size, aligned_size; 1033 1005 ··· 1196 1166 struct scatterlist *s; 1197 1167 int i; 1198 1168 1169 + sg_dma_mark_swiotlb(sg); 1170 + 1199 1171 for_each_sg(sg, s, nents, i) { 1200 1172 sg_dma_address(s) = iommu_dma_map_page(dev, sg_page(s), 1201 1173 s->offset, s->length, dir, attrs); ··· 1242 1210 goto out; 1243 1211 } 1244 1212 1245 - if (dev_use_swiotlb(dev)) 1213 + if (dev_use_sg_swiotlb(dev, sg, nents, dir)) 1246 1214 return iommu_dma_map_sg_swiotlb(dev, sg, nents, dir, attrs); 1247 1215 1248 1216 if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC)) ··· 1347 1315 struct scatterlist *tmp; 1348 1316 int i; 1349 1317 1350 - if (dev_use_swiotlb(dev)) { 1318 + if (sg_dma_is_swiotlb(sg)) { 1351 1319 iommu_dma_unmap_sg_swiotlb(dev, sg, nents, dir, attrs); 1352 1320 return; 1353 1321 }
+39 -2
include/linux/scatterlist.h
··· 251 251 /* 252 252 * One 64-bit architectures there is a 4-byte padding in struct scatterlist 253 253 * (assuming also CONFIG_NEED_SG_DMA_LENGTH is set). Use this padding for DMA 254 - * flags bits to indicate when a specific dma address is a bus address. 254 + * flags bits to indicate when a specific dma address is a bus address or the 255 + * buffer may have been bounced via SWIOTLB. 255 256 */ 256 257 #ifdef CONFIG_NEED_SG_DMA_FLAGS 257 258 258 - #define SG_DMA_BUS_ADDRESS (1 << 0) 259 + #define SG_DMA_BUS_ADDRESS (1 << 0) 260 + #define SG_DMA_SWIOTLB (1 << 1) 259 261 260 262 /** 261 263 * sg_dma_is_bus_address - Return whether a given segment was marked ··· 300 298 sg->dma_flags &= ~SG_DMA_BUS_ADDRESS; 301 299 } 302 300 301 + /** 302 + * sg_dma_is_swiotlb - Return whether the scatterlist was marked for SWIOTLB 303 + * bouncing 304 + * @sg: SG entry 305 + * 306 + * Description: 307 + * Returns true if the scatterlist was marked for SWIOTLB bouncing. Not all 308 + * elements may have been bounced, so the caller would have to check 309 + * individual SG entries with is_swiotlb_buffer(). 310 + */ 311 + static inline bool sg_dma_is_swiotlb(struct scatterlist *sg) 312 + { 313 + return sg->dma_flags & SG_DMA_SWIOTLB; 314 + } 315 + 316 + /** 317 + * sg_dma_mark_swiotlb - Mark the scatterlist for SWIOTLB bouncing 318 + * @sg: SG entry 319 + * 320 + * Description: 321 + * Marks a a scatterlist for SWIOTLB bounce. Not all SG entries may be 322 + * bounced. 323 + */ 324 + static inline void sg_dma_mark_swiotlb(struct scatterlist *sg) 325 + { 326 + sg->dma_flags |= SG_DMA_SWIOTLB; 327 + } 328 + 303 329 #else 304 330 305 331 static inline bool sg_dma_is_bus_address(struct scatterlist *sg) ··· 338 308 { 339 309 } 340 310 static inline void sg_dma_unmark_bus_address(struct scatterlist *sg) 311 + { 312 + } 313 + static inline bool sg_dma_is_swiotlb(struct scatterlist *sg) 314 + { 315 + return false; 316 + } 317 + static inline void sg_dma_mark_swiotlb(struct scatterlist *sg) 341 318 { 342 319 } 343 320