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:
Frank Li 2026-05-21 11:32:48 -04:00 committed by Vinod Koul
parent 796bdb33e8
commit af900b7dc1
2 changed files with 88 additions and 0 deletions

View File

@ -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

View File

@ -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(