mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 12:44:03 +02:00
dmaengine: Add safe API to combine configuration and preparation
Introduce dmaengine_prep_config_single_safe() and dmaengine_prep_config_sg_safe() to provide a reentrant-safe way to combine slave configuration and transfer preparation. Drivers may implement the new device_prep_config_sg() callback to perform both steps atomically. If the callback is not provided, the helpers fall back to calling dmaengine_slave_config() followed by dmaengine_prep_slave_sg() under per-channel spinlock protection. Tested-by: Niklas Cassel <cassel@kernel.org> Signed-off-by: Frank Li <Frank.Li@nxp.com> Link: https://patch.msgid.link/20260521-dma_prep_config-v7-2-1f73f4899883@nxp.com Signed-off-by: Vinod Koul <vkoul@kernel.org>
This commit is contained in:
parent
796bdb33e8
commit
af900b7dc1
|
|
@ -1100,6 +1100,8 @@ static int __dma_async_device_channel_register(struct dma_device *device,
|
|||
chan->dev->device.parent = device->dev;
|
||||
chan->dev->chan = chan;
|
||||
chan->dev->dev_id = device->dev_id;
|
||||
spin_lock_init(&chan->lock);
|
||||
|
||||
if (!name)
|
||||
dev_set_name(&chan->dev->device, "dma%dchan%d", device->dev_id, chan->chan_id);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -322,6 +322,8 @@ struct dma_router {
|
|||
* @slave: ptr to the device using this channel
|
||||
* @cookie: last cookie value returned to client
|
||||
* @completed_cookie: last completed cookie for this channel
|
||||
* @lock: protect between config and prepare transfer when driver have not
|
||||
* implemented callback device_prep_config_sg().
|
||||
* @chan_id: channel ID for sysfs
|
||||
* @dev: class device for sysfs
|
||||
* @name: backlink name for sysfs
|
||||
|
|
@ -341,6 +343,12 @@ struct dma_chan {
|
|||
dma_cookie_t cookie;
|
||||
dma_cookie_t completed_cookie;
|
||||
|
||||
/*
|
||||
* protect between config and prepare transfer because *_prep() may be
|
||||
* called from complete callback, which is in GFP_NOSLEEP context.
|
||||
*/
|
||||
spinlock_t lock;
|
||||
|
||||
/* sysfs */
|
||||
int chan_id;
|
||||
struct dma_chan_dev *dev;
|
||||
|
|
@ -1068,6 +1076,84 @@ dmaengine_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
|
|||
return dmaengine_prep_config_sg(chan, sgl, sg_len, dir, flags, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* dmaengine_prep_config_sg_safe - prepare a scatter-gather DMA transfer
|
||||
* with atomic slave configuration update
|
||||
* @chan: DMA channel
|
||||
* @sgl: scatterlist for the transfer
|
||||
* @sg_len: number of entries in @sgl
|
||||
* @dir: DMA transfer direction
|
||||
* @flags: transfer preparation flags
|
||||
* @config: DMA slave configuration for this transfer
|
||||
*
|
||||
* Prepare a DMA scatter-gather transfer together with a corresponding slave
|
||||
* configuration update in a re-entrant and race-safe manner.
|
||||
*
|
||||
* DMA engine drivers may implement the optional
|
||||
* device_prep_config_sg() callback to perform both the slave configuration
|
||||
* and descriptor preparation atomically. In this case, the operation is
|
||||
* fully handled by the DMA engine driver.
|
||||
*
|
||||
* If the DMA engine driver does not implement device_prep_config_sg(), falls
|
||||
* back to calling dmaengine_slave_config() followed by dmaengine_prep_slave_sg().
|
||||
* The fallback path is protected by a per-channel spinlock to ensure that
|
||||
* concurrent callers cannot interleave configuration and descriptor preparation
|
||||
* on the same DMA channel.
|
||||
*
|
||||
* Return: Pointer to a prepared DMA async transaction descriptor on success,
|
||||
* or %NULL if the transfer could not be prepared.
|
||||
*/
|
||||
static inline struct dma_async_tx_descriptor *
|
||||
dmaengine_prep_config_sg_safe(struct dma_chan *chan, struct scatterlist *sgl,
|
||||
unsigned int sg_len,
|
||||
enum dma_transfer_direction dir,
|
||||
unsigned long flags,
|
||||
struct dma_slave_config *config)
|
||||
{
|
||||
struct dma_async_tx_descriptor *tx;
|
||||
unsigned long spinlock_flags;
|
||||
|
||||
if (!chan || !chan->device)
|
||||
return NULL;
|
||||
|
||||
if (!chan->device->device_prep_config_sg)
|
||||
spin_lock_irqsave(&chan->lock, spinlock_flags);
|
||||
|
||||
tx = dmaengine_prep_config_sg(chan, sgl, sg_len, dir, flags, config);
|
||||
|
||||
if (!chan->device->device_prep_config_sg)
|
||||
spin_unlock_irqrestore(&chan->lock, spinlock_flags);
|
||||
|
||||
return tx;
|
||||
}
|
||||
|
||||
/**
|
||||
* dmaengine_prep_config_single_safe - prepare a single-buffer DMA transfer
|
||||
* with atomic slave configuration update
|
||||
* @chan: DMA channel
|
||||
* @buf: DMA buffer address
|
||||
* @len: length of the transfer in bytes
|
||||
* @dir: DMA transfer direction
|
||||
* @flags: transfer preparation flags
|
||||
* @config: DMA slave configuration for this transfer
|
||||
*
|
||||
* Detail see dmaengine_prep_config_sg_safe().
|
||||
*/
|
||||
static inline struct dma_async_tx_descriptor *
|
||||
dmaengine_prep_config_single_safe(struct dma_chan *chan, dma_addr_t buf,
|
||||
size_t len, enum dma_transfer_direction dir,
|
||||
unsigned long flags,
|
||||
struct dma_slave_config *config)
|
||||
{
|
||||
struct scatterlist sg;
|
||||
|
||||
sg_init_table(&sg, 1);
|
||||
sg_dma_address(&sg) = buf;
|
||||
sg_dma_len(&sg) = len;
|
||||
|
||||
return dmaengine_prep_config_sg_safe(chan, &sg, 1, dir, flags, config);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_RAPIDIO_DMA_ENGINE
|
||||
struct rio_dma_ext;
|
||||
static inline struct dma_async_tx_descriptor *dmaengine_prep_rio_sg(
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user