]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
dma-mapping: add dma_opt_mapping_size()
authorJohn Garry <john.garry@huawei.com>
Thu, 14 Jul 2022 11:15:24 +0000 (19:15 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 10 Apr 2024 14:18:47 +0000 (16:18 +0200)
[ Upstream commit a229cc14f3395311b899e5e582b71efa8dd01df0 ]

Streaming DMA mapping involving an IOMMU may be much slower for larger
total mapping size. This is because every IOMMU DMA mapping requires an
IOVA to be allocated and freed. IOVA sizes above a certain limit are not
cached, which can have a big impact on DMA mapping performance.

Provide an API for device drivers to know this "optimal" limit, such that
they may try to produce mapping which don't exceed it.

Signed-off-by: John Garry <john.garry@huawei.com>
Reviewed-by: Damien Le Moal <damien.lemoal@opensource.wdc.com>
Acked-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
Stable-dep-of: afc5aa46ed56 ("iommu/dma: Force swiotlb_max_mapping_size on an untrusted device")
Signed-off-by: Sasha Levin <sashal@kernel.org>
Documentation/core-api/dma-api.rst
include/linux/dma-map-ops.h
include/linux/dma-mapping.h
kernel/dma/mapping.c

index 6d6d0edd2d278972bc8cb893472a08aada395775..829f20a193cabbe78ca32ba0b1dc1939feccecb8 100644 (file)
@@ -204,6 +204,20 @@ Returns the maximum size of a mapping for the device. The size parameter
 of the mapping functions like dma_map_single(), dma_map_page() and
 others should not be larger than the returned value.
 
+::
+
+       size_t
+       dma_opt_mapping_size(struct device *dev);
+
+Returns the maximum optimal size of a mapping for the device.
+
+Mapping larger buffers may take much longer in certain scenarios. In
+addition, for high-rate short-lived streaming mappings, the upfront time
+spent on the mapping may account for an appreciable part of the total
+request lifetime. As such, if splitting larger requests incurs no
+significant performance penalty, then device drivers are advised to
+limit total DMA streaming mappings length to the returned value.
+
 ::
 
        bool
index bfffe494356adabacdbf34e21ca079714113f3cf..2ff55ec902f48665331bda4dcf6cd301f43e24b0 100644 (file)
@@ -69,6 +69,7 @@ struct dma_map_ops {
        int (*dma_supported)(struct device *dev, u64 mask);
        u64 (*get_required_mask)(struct device *dev);
        size_t (*max_mapping_size)(struct device *dev);
+       size_t (*opt_mapping_size)(void);
        unsigned long (*get_merge_boundary)(struct device *dev);
 };
 
index dca2b1355bb133f246d4a5e6d787d86539a18a5d..fe3849434b2a21ea2d5419813595c357b4f4f850 100644 (file)
@@ -144,6 +144,7 @@ int dma_set_mask(struct device *dev, u64 mask);
 int dma_set_coherent_mask(struct device *dev, u64 mask);
 u64 dma_get_required_mask(struct device *dev);
 size_t dma_max_mapping_size(struct device *dev);
+size_t dma_opt_mapping_size(struct device *dev);
 bool dma_need_sync(struct device *dev, dma_addr_t dma_addr);
 unsigned long dma_get_merge_boundary(struct device *dev);
 struct sg_table *dma_alloc_noncontiguous(struct device *dev, size_t size,
@@ -266,6 +267,10 @@ static inline size_t dma_max_mapping_size(struct device *dev)
 {
        return 0;
 }
+static inline size_t dma_opt_mapping_size(struct device *dev)
+{
+       return 0;
+}
 static inline bool dma_need_sync(struct device *dev, dma_addr_t dma_addr)
 {
        return false;
index 9478eccd1c8e6c23aae0be6b3a8cd474ce637263..c9dbc8f5812b831c57e407066a89c53f73a79650 100644 (file)
@@ -777,6 +777,18 @@ size_t dma_max_mapping_size(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(dma_max_mapping_size);
 
+size_t dma_opt_mapping_size(struct device *dev)
+{
+       const struct dma_map_ops *ops = get_dma_ops(dev);
+       size_t size = SIZE_MAX;
+
+       if (ops && ops->opt_mapping_size)
+               size = ops->opt_mapping_size();
+
+       return min(dma_max_mapping_size(dev), size);
+}
+EXPORT_SYMBOL_GPL(dma_opt_mapping_size);
+
 bool dma_need_sync(struct device *dev, dma_addr_t dma_addr)
 {
        const struct dma_map_ops *ops = get_dma_ops(dev);