mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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:
		
							parent
							
								
									173735c346
								
							
						
					
					
						commit
						fffe3cc8c2
					
				
					 3 changed files with 84 additions and 38 deletions
				
			
		| 
						 | 
				
			
			@ -41,8 +41,9 @@ struct dma_map_ops {
 | 
			
		|||
			size_t size, enum dma_data_direction dir,
 | 
			
		||||
			unsigned long attrs);
 | 
			
		||||
	/*
 | 
			
		||||
	 * map_sg returns 0 on error and a value > 0 on success.
 | 
			
		||||
	 * It should never return a value < 0.
 | 
			
		||||
	 * map_sg should return a negative error code on error. See
 | 
			
		||||
	 * dma_map_sgtable() for a list of appropriate error codes
 | 
			
		||||
	 * and their meanings.
 | 
			
		||||
	 */
 | 
			
		||||
	int (*map_sg)(struct device *dev, struct scatterlist *sg, int nents,
 | 
			
		||||
			enum dma_data_direction dir, unsigned long attrs);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
				      int nents, enum dma_data_direction dir,
 | 
			
		||||
				      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,
 | 
			
		||||
		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,
 | 
			
		||||
| 
						 | 
				
			
			@ -174,6 +176,11 @@ static inline void dma_unmap_sg_attrs(struct device *dev,
 | 
			
		|||
		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,
 | 
			
		||||
		phys_addr_t phys_addr, size_t size, enum dma_data_direction dir,
 | 
			
		||||
		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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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
 | 
			
		||||
 * @dev:	The device for which to perform the DMA operation
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * dma_maps_sg_attrs returns 0 on error and > 0 on success.
 | 
			
		||||
 * 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)
 | 
			
		||||
static 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);
 | 
			
		||||
	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);
 | 
			
		||||
	else
 | 
			
		||||
		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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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,
 | 
			
		||||
				      int nents, enum dma_data_direction dir,
 | 
			
		||||
				      unsigned long attrs)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue