[PATCH v3 2/9] dmaengine: Add safe API to combine configuration and preparation
Frank Li <[email protected]>
| Newsgroups | dev.linux.lists.mhi,dev.linux.lists.imx,org.infradead.lists.linux-arm-kernel,org.infradead.lists.linux-nvme,org.kernel.vger.dmaengine,org.kernel.vger.linux-arm-msm,org.kernel.vger.linux-crypto,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pci |
|---|---|
| Message-ID | <[email protected]> |
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 mutex protection. Signed-off-by: Frank Li <[email protected]> --- change in v3 - new patch --- drivers/dma/dmaengine.c | 3 +++ include/linux/dmaengine.h | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index ca13cd39330ba4d822baaab412356a166b656350..53116300e61078ca89e78109bcf24a5f8c7e3369 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -1097,6 +1097,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; + mutex_init(&chan->lock); + if (!name) dev_set_name(&chan->dev->device, "dma%dchan%d", device->dev_id, chan->chan_id); else @@ -1147,6 +1149,7 @@ static void __dma_async_device_channel_unregister(struct dma_device *device, device->chancnt--; chan->dev->chan = NULL; mutex_unlock(&dma_list_mutex); + mutex_destroy(&chan->lock); ida_free(&device->chan_ida, chan->chan_id); device_unregister(&chan->dev->device); free_percpu(chan->local); diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 4994236aaadc45dbda260b63abe1fef47aa3d51e..abb4a7424a0083c00730a945c1cb645f831fbd6f 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -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 @@ -340,6 +342,7 @@ struct dma_chan { struct device *slave; dma_cookie_t cookie; dma_cookie_t completed_cookie; + struct mutex lock; /* protect between config and prepare transfer */ /* sysfs */ int chan_id; @@ -1068,6 +1071,56 @@ 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_single(sg)_safe() is re-entrant version. + * + * The unsafe variant (without the _safe suffix) falls back to calling + * dmaengine_slave_config() and dmaengine_prep_slave_sg() separately. + * In this case, additional locking may be required, depending on the + * DMA consumer's usage. + * + * If dmaengine driver have not implemented call back device_prep_config_sg() + * safe version use per-channel mutex to protect call dmaengine_slave_config() + * and dmaengine_prep_slave_sg(). + */ +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; + + if (!chan || !chan->device) + return NULL; + + if (!chan->device->device_prep_config_sg) + mutex_lock(&chan->lock); + + tx = dmaengine_prep_config_sg(chan, sgl, sg_len, dir, flags, config); + + if (!chan->device->device_prep_config_sg) + mutex_unlock(&chan->lock); + + return tx; +} + +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( -- 2.34.1