From: Chuck Lever <chuck.lever@xxxxxxxxxx> Cc: iommu@xxxxxxxxxxxxxxx Cc: linux-rdma@xxxxxxxxxxxxxxx Signed-off-by: Chuck Lever <chuck.lever@xxxxxxxxxx> --- include/linux/dma-map-ops.h | 4 +++ include/linux/dma-mapping.h | 4 +++ kernel/dma/mapping.c | 65 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/include/linux/dma-map-ops.h b/include/linux/dma-map-ops.h index de2a50d9207a..69ecfd403249 100644 --- a/include/linux/dma-map-ops.h +++ b/include/linux/dma-map-ops.h @@ -60,6 +60,10 @@ struct dma_map_ops { enum dma_data_direction dir, unsigned long attrs); void (*unmap_sg)(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction dir, unsigned long attrs); + int (*map_bvecs)(struct device *dev, struct bio_vec *bvecs, int nents, + enum dma_data_direction dir, unsigned long attrs); + void (*unmap_bvecs)(struct device *dev, struct bio_vec *bvecs, int nents, + enum dma_data_direction dir, unsigned long attrs); dma_addr_t (*map_resource)(struct device *dev, phys_addr_t phys_addr, size_t size, enum dma_data_direction dir, unsigned long attrs); diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index 9fb422f376b6..6f522a82cfe3 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -114,6 +114,10 @@ void dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sg, unsigned long attrs); int dma_map_sgtable(struct device *dev, struct sg_table *sgt, enum dma_data_direction dir, unsigned long attrs); +unsigned int dma_map_bvecs_attrs(struct device *dev, struct bio_vec *bvecs, + int nents, enum dma_data_direction dir, unsigned long attrs); +void dma_unmap_bvecs_attrs(struct device *dev, struct bio_vec *bvecs, + int nents, enum dma_data_direction dir, unsigned long attrs); dma_addr_t dma_map_resource(struct device *dev, phys_addr_t phys_addr, size_t size, enum dma_data_direction dir, unsigned long attrs); void dma_unmap_resource(struct device *dev, dma_addr_t addr, size_t size, diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c index 94cffc9b45a5..f53cc4da2797 100644 --- a/kernel/dma/mapping.c +++ b/kernel/dma/mapping.c @@ -296,6 +296,71 @@ void dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sg, } EXPORT_SYMBOL(dma_unmap_sg_attrs); +/** + * dma_map_sg_attrs - Map the given buffer for DMA + * @dev: The device for which to perform the DMA operation + * @bvecs: The bio_vec array describing the buffer + * @nents: Number of bio_vecs to map + * @dir: DMA direction + * @attrs: Optional DMA attributes for the map operation + * + * Maps a buffer described by a bio_vec array passed in the bvecs + * argument with nents segments for the @dir DMA operation by the + * @dev device. + * + * Returns the number of mapped entries (which can be less than nents) + * on success. Zero is returned for any error. + * + * dma_unmap_bvecs_attrs() should be used to unmap the buffer with the + * original bvecs and original nents (not the value returned by this + * function). + */ +unsigned int dma_map_bvecs_attrs(struct device *dev, struct bio_vec *bvecs, + int nents, enum dma_data_direction dir, + unsigned long attrs) +{ + const struct dma_map_ops *ops = get_dma_ops(dev); + int ents; + + BUG_ON(!valid_dma_direction(dir)); + + if (WARN_ON_ONCE(!dev->dma_mask)) + return 0; + + if (dma_map_direct(dev, ops)) + ents = dma_direct_map_bvecs(dev, bvecs, nents, dir, attrs); + else + ents = ops->map_bvecs(dev, bvecs, nents, dir, attrs); + + if (ents > 0) { + kmsan_handle_dma_bvecs(bvecs, nents, dir); + debug_dma_map_bvecs(dev, bvecs, nents, ents, dir, attrs); + } else if (WARN_ON_ONCE(ents != -EINVAL && ents != -ENOMEM && + ents != -EIO && ents != -EREMOTEIO)) { + return -EIO; + } + + return ents; +} +EXPORT_SYMBOL_GPL(dma_map_bvecs_attrs); + +void dma_unmap_bvecs_attrs(struct device *dev, struct bio_vec *bvecs, + int nents, enum dma_data_direction dir, + unsigned long attrs) +{ + const struct dma_map_ops *ops = get_dma_ops(dev); + + BUG_ON(!valid_dma_direction(dir)); + + debug_dma_unmap_bvecs(dev, bvecs, nents, dir); + + if (dma_map_direct(dev, ops)) + dma_direct_unmap_bvecs(dev, bvecs, nents, dir, attrs); + else if (ops->unmap_bvecs) + ops->unmap_bvecs(dev, bvecs, nents, dir, attrs); +} +EXPORT_SYMBOL(dma_unmap_bvecs_attrs); + dma_addr_t dma_map_resource(struct device *dev, phys_addr_t phys_addr, size_t size, enum dma_data_direction dir, unsigned long attrs) {