dma-mapping: allow map_sg() ops to return negative error codes

Allow dma_map_sgtable() to pass errors from the map_sg() ops. This
will be required for returning appropriate error codes when mapping
P2PDMA memory.

Introduce __dma_map_sg_attrs() which will return the raw error code
from the map_sg operation (whether it be negative or zero). Then add a
dma_map_sg_attrs() wrapper to convert any negative errors to zero to
satisfy the existing calling convention.

dma_map_sgtable() defines three error codes that .map_sg implementations
are allowed to return: -EINVAL, -ENOMEM and -EIO. The latter of which
is a generic return for cases that are passing DMA_MAPPING_ERROR
through.

dma_map_sgtable() will convert a zero error return for old map_sg() ops
into a -EIO return and return any negative errors as reported.

This allows map_sg implementations to start returning multiple
negative error codes. Legacy map_sg implementations can continue
to return zero until they are all converted.

Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
Logan Gunthorpe 2021-07-29 14:15:19 -06:00 committed by Christoph Hellwig
parent 173735c346
commit fffe3cc8c2
3 changed files with 84 additions and 38 deletions

View File

@ -41,8 +41,9 @@ struct dma_map_ops {
size_t size, enum dma_data_direction dir, size_t size, enum dma_data_direction dir,
unsigned long attrs); unsigned long attrs);
/* /*
* map_sg returns 0 on error and a value > 0 on success. * map_sg should return a negative error code on error. See
* It should never return a value < 0. * dma_map_sgtable() for a list of appropriate error codes
* and their meanings.
*/ */
int (*map_sg)(struct device *dev, struct scatterlist *sg, int nents, int (*map_sg)(struct device *dev, struct scatterlist *sg, int nents,
enum dma_data_direction dir, unsigned long attrs); enum dma_data_direction dir, unsigned long attrs);

View File

@ -110,6 +110,8 @@ int dma_map_sg_attrs(struct device *dev, struct scatterlist *sg, int nents,
void dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sg, void dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sg,
int nents, enum dma_data_direction dir, int nents, enum dma_data_direction dir,
unsigned long attrs); unsigned long attrs);
int dma_map_sgtable(struct device *dev, struct sg_table *sgt,
enum dma_data_direction dir, unsigned long attrs);
dma_addr_t dma_map_resource(struct device *dev, phys_addr_t phys_addr, 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); 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, void dma_unmap_resource(struct device *dev, dma_addr_t addr, size_t size,
@ -174,6 +176,11 @@ static inline void dma_unmap_sg_attrs(struct device *dev,
unsigned long attrs) unsigned long attrs)
{ {
} }
static inline int dma_map_sgtable(struct device *dev, struct sg_table *sgt,
enum dma_data_direction dir, unsigned long attrs)
{
return -EOPNOTSUPP;
}
static inline dma_addr_t dma_map_resource(struct device *dev, static inline dma_addr_t dma_map_resource(struct device *dev,
phys_addr_t phys_addr, size_t size, enum dma_data_direction dir, phys_addr_t phys_addr, size_t size, enum dma_data_direction dir,
unsigned long attrs) unsigned long attrs)
@ -343,34 +350,6 @@ static inline void dma_sync_single_range_for_device(struct device *dev,
return dma_sync_single_for_device(dev, addr + offset, size, dir); return dma_sync_single_for_device(dev, addr + offset, size, dir);
} }
/**
* dma_map_sgtable - Map the given buffer for DMA
* @dev: The device for which to perform the DMA operation
* @sgt: The sg_table object describing the buffer
* @dir: DMA direction
* @attrs: Optional DMA attributes for the map operation
*
* Maps a buffer described by a scatterlist stored in the given sg_table
* object for the @dir DMA operation by the @dev device. After success the
* ownership for the buffer is transferred to the DMA domain. One has to
* call dma_sync_sgtable_for_cpu() or dma_unmap_sgtable() to move the
* ownership of the buffer back to the CPU domain before touching the
* buffer by the CPU.
*
* Returns 0 on success or -EINVAL on error during mapping the buffer.
*/
static inline int dma_map_sgtable(struct device *dev, struct sg_table *sgt,
enum dma_data_direction dir, unsigned long attrs)
{
int nents;
nents = dma_map_sg_attrs(dev, sgt->sgl, sgt->orig_nents, dir, attrs);
if (nents <= 0)
return -EINVAL;
sgt->nents = nents;
return 0;
}
/** /**
* dma_unmap_sgtable - Unmap the given buffer for DMA * dma_unmap_sgtable - Unmap the given buffer for DMA
* @dev: The device for which to perform the DMA operation * @dev: The device for which to perform the DMA operation

View File

@ -177,12 +177,8 @@ void dma_unmap_page_attrs(struct device *dev, dma_addr_t addr, size_t size,
} }
EXPORT_SYMBOL(dma_unmap_page_attrs); EXPORT_SYMBOL(dma_unmap_page_attrs);
/* static int __dma_map_sg_attrs(struct device *dev, struct scatterlist *sg,
* dma_maps_sg_attrs returns 0 on error and > 0 on success. int nents, enum dma_data_direction dir, unsigned long attrs)
* It should never return a value < 0.
*/
int dma_map_sg_attrs(struct device *dev, struct scatterlist *sg, int nents,
enum dma_data_direction dir, unsigned long attrs)
{ {
const struct dma_map_ops *ops = get_dma_ops(dev); const struct dma_map_ops *ops = get_dma_ops(dev);
int ents; int ents;
@ -197,13 +193,83 @@ int dma_map_sg_attrs(struct device *dev, struct scatterlist *sg, int nents,
ents = dma_direct_map_sg(dev, sg, nents, dir, attrs); ents = dma_direct_map_sg(dev, sg, nents, dir, attrs);
else else
ents = ops->map_sg(dev, sg, nents, dir, attrs); ents = ops->map_sg(dev, sg, nents, dir, attrs);
BUG_ON(ents < 0);
debug_dma_map_sg(dev, sg, nents, ents, dir); if (ents > 0)
debug_dma_map_sg(dev, sg, nents, ents, dir);
else if (WARN_ON_ONCE(ents != -EINVAL && ents != -ENOMEM &&
ents != -EIO && ents != 0))
return -EIO;
return ents; return ents;
} }
/**
* dma_map_sg_attrs - Map the given buffer for DMA
* @dev: The device for which to perform the DMA operation
* @sg: The sg_table object describing the buffer
* @dir: DMA direction
* @attrs: Optional DMA attributes for the map operation
*
* Maps a buffer described by a scatterlist passed in the sg 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_sg_attrs() should be used to unmap the buffer with the
* original sg and original nents (not the value returned by this funciton).
*/
int dma_map_sg_attrs(struct device *dev, struct scatterlist *sg,
int nents, enum dma_data_direction dir, unsigned long attrs)
{
int ret;
ret = __dma_map_sg_attrs(dev, sg, nents, dir, attrs);
if (ret < 0)
return 0;
return ret;
}
EXPORT_SYMBOL(dma_map_sg_attrs); EXPORT_SYMBOL(dma_map_sg_attrs);
/**
* dma_map_sgtable - Map the given buffer for DMA
* @dev: The device for which to perform the DMA operation
* @sgt: The sg_table object describing the buffer
* @dir: DMA direction
* @attrs: Optional DMA attributes for the map operation
*
* Maps a buffer described by a scatterlist stored in the given sg_table
* object for the @dir DMA operation by the @dev device. After success, the
* ownership for the buffer is transferred to the DMA domain. One has to
* call dma_sync_sgtable_for_cpu() or dma_unmap_sgtable() to move the
* ownership of the buffer back to the CPU domain before touching the
* buffer by the CPU.
*
* Returns 0 on success or a negative error code on error. The following
* error codes are supported with the given meaning:
*
* -EINVAL - An invalid argument, unaligned access or other error
* in usage. Will not succeed if retried.
* -ENOMEM - Insufficient resources (like memory or IOVA space) to
* complete the mapping. Should succeed if retried later.
* -EIO - Legacy error code with an unknown meaning. eg. this is
* returned if a lower level call returned DMA_MAPPING_ERROR.
*/
int dma_map_sgtable(struct device *dev, struct sg_table *sgt,
enum dma_data_direction dir, unsigned long attrs)
{
int nents;
nents = __dma_map_sg_attrs(dev, sgt->sgl, sgt->orig_nents, dir, attrs);
if (nents == 0)
return -EIO;
if (nents < 0)
return nents;
sgt->nents = nents;
return 0;
}
EXPORT_SYMBOL_GPL(dma_map_sgtable);
void dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sg, void dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sg,
int nents, enum dma_data_direction dir, int nents, enum dma_data_direction dir,
unsigned long attrs) unsigned long attrs)