Skip to content

Commit b36f09c

Browse files
larsclausenVinod Koul
authored andcommitted
dmaengine: Add transfer termination synchronization support
The DMAengine API has a long standing race condition that is inherent to the API itself. Calling dmaengine_terminate_all() is supposed to stop and abort any pending or active transfers that have previously been submitted. Unfortunately it is possible that this operation races against a currently running (or with some drivers also scheduled) completion callback. Since the API allows dmaengine_terminate_all() to be called from atomic context as well as from within a completion callback it is not possible to synchronize to the execution of the completion callback from within dmaengine_terminate_all() itself. This means that a user of the DMAengine API does not know when it is safe to free resources used in the completion callback, which can result in a use-after-free race condition. This patch addresses the issue by introducing an explicit synchronization primitive to the DMAengine API called dmaengine_synchronize(). The existing dmaengine_terminate_all() is deprecated in favor of dmaengine_terminate_sync() and dmaengine_terminate_async(). The former aborts all pending and active transfers and synchronizes to the current context, meaning it will wait until all running completion callbacks have finished. This means it is only possible to call this function from non-atomic context. The later function does not synchronize, but can still be used in atomic context or from within a complete callback. It has to be followed up by dmaengine_synchronize() before a client can free the resources used in a completion callback. In addition to this the semantics of the device_terminate_all() callback are slightly relaxed by this patch. It is now OK for a driver to only schedule the termination of the active transfer, but does not necessarily have to wait until the DMA controller has completely stopped. The driver must ensure though that the controller has stopped and no longer accesses any memory when the device_synchronize() callback returns. This was in part done since most drivers do not pay attention to this anyway at the moment and to emphasize that this needs to be done when the device_synchronize() callback is implemented. But it also helps with implementing support for devices where stopping the controller can require operations that may sleep. Signed-off-by: Lars-Peter Clausen <[email protected]> Signed-off-by: Vinod Koul <[email protected]>
1 parent 8005c49 commit b36f09c

File tree

4 files changed

+148
-5
lines changed

4 files changed

+148
-5
lines changed

Documentation/dmaengine/client.txt

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ The slave DMA usage consists of following steps:
128128
transaction.
129129

130130
For cyclic DMA, a callback function may wish to terminate the
131-
DMA via dmaengine_terminate_all().
131+
DMA via dmaengine_terminate_async().
132132

133133
Therefore, it is important that DMA engine drivers drop any
134134
locks before calling the callback function which may cause a
@@ -166,12 +166,29 @@ The slave DMA usage consists of following steps:
166166

167167
Further APIs:
168168

169-
1. int dmaengine_terminate_all(struct dma_chan *chan)
169+
1. int dmaengine_terminate_sync(struct dma_chan *chan)
170+
int dmaengine_terminate_async(struct dma_chan *chan)
171+
int dmaengine_terminate_all(struct dma_chan *chan) /* DEPRECATED */
170172

171173
This causes all activity for the DMA channel to be stopped, and may
172174
discard data in the DMA FIFO which hasn't been fully transferred.
173175
No callback functions will be called for any incomplete transfers.
174176

177+
Two variants of this function are available.
178+
179+
dmaengine_terminate_async() might not wait until the DMA has been fully
180+
stopped or until any running complete callbacks have finished. But it is
181+
possible to call dmaengine_terminate_async() from atomic context or from
182+
within a complete callback. dmaengine_synchronize() must be called before it
183+
is safe to free the memory accessed by the DMA transfer or free resources
184+
accessed from within the complete callback.
185+
186+
dmaengine_terminate_sync() will wait for the transfer and any running
187+
complete callbacks to finish before it returns. But the function must not be
188+
called from atomic context or from within a complete callback.
189+
190+
dmaengine_terminate_all() is deprecated and should not be used in new code.
191+
175192
2. int dmaengine_pause(struct dma_chan *chan)
176193

177194
This pauses activity on the DMA channel without data loss.
@@ -197,3 +214,20 @@ Further APIs:
197214
a running DMA channel. It is recommended that DMA engine users
198215
pause or stop (via dmaengine_terminate_all()) the channel before
199216
using this API.
217+
218+
5. void dmaengine_synchronize(struct dma_chan *chan)
219+
220+
Synchronize the termination of the DMA channel to the current context.
221+
222+
This function should be used after dmaengine_terminate_async() to synchronize
223+
the termination of the DMA channel to the current context. The function will
224+
wait for the transfer and any running complete callbacks to finish before it
225+
returns.
226+
227+
If dmaengine_terminate_async() is used to stop the DMA channel this function
228+
must be called before it is safe to free memory accessed by previously
229+
submitted descriptors or to free any resources accessed within the complete
230+
callback of previously submitted descriptors.
231+
232+
The behavior of this function is undefined if dma_async_issue_pending() has
233+
been called between dmaengine_terminate_async() and this function.

Documentation/dmaengine/provider.txt

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,24 @@ supported.
327327

328328
* device_terminate_all
329329
- Aborts all the pending and ongoing transfers on the channel
330-
- This command should operate synchronously on the channel,
331-
terminating right away all the channels
330+
- For aborted transfers the complete callback should not be called
331+
- Can be called from atomic context or from within a complete
332+
callback of a descriptor. Must not sleep. Drivers must be able
333+
to handle this correctly.
334+
- Termination may be asynchronous. The driver does not have to
335+
wait until the currently active transfer has completely stopped.
336+
See device_synchronize.
337+
338+
* device_synchronize
339+
- Must synchronize the termination of a channel to the current
340+
context.
341+
- Must make sure that memory for previously submitted
342+
descriptors is no longer accessed by the DMA controller.
343+
- Must make sure that all complete callbacks for previously
344+
submitted descriptors have finished running and none are
345+
scheduled to run.
346+
- May sleep.
347+
332348

333349
Misc notes (stuff that should be documented, but don't really know
334350
where to put them)

drivers/dma/dmaengine.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,11 @@ static void dma_chan_put(struct dma_chan *chan)
265265
module_put(dma_chan_to_owner(chan));
266266

267267
/* This channel is not in use anymore, free it */
268-
if (!chan->client_count && chan->device->device_free_chan_resources)
268+
if (!chan->client_count && chan->device->device_free_chan_resources) {
269+
/* Make sure all operations have completed */
270+
dmaengine_synchronize(chan);
269271
chan->device->device_free_chan_resources(chan);
272+
}
270273

271274
/* If the channel is used via a DMA request router, free the mapping */
272275
if (chan->router && chan->router->route_free) {

include/linux/dmaengine.h

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,8 @@ enum dmaengine_alignment {
654654
* paused. Returns 0 or an error code
655655
* @device_terminate_all: Aborts all transfers on a channel. Returns 0
656656
* or an error code
657+
* @device_synchronize: Synchronizes the termination of a transfers to the
658+
* current context.
657659
* @device_tx_status: poll for transaction completion, the optional
658660
* txstate parameter can be supplied with a pointer to get a
659661
* struct with auxiliary transfer status information, otherwise the call
@@ -737,6 +739,7 @@ struct dma_device {
737739
int (*device_pause)(struct dma_chan *chan);
738740
int (*device_resume)(struct dma_chan *chan);
739741
int (*device_terminate_all)(struct dma_chan *chan);
742+
void (*device_synchronize)(struct dma_chan *chan);
740743

741744
enum dma_status (*device_tx_status)(struct dma_chan *chan,
742745
dma_cookie_t cookie,
@@ -828,6 +831,13 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_sg(
828831
src_sg, src_nents, flags);
829832
}
830833

834+
/**
835+
* dmaengine_terminate_all() - Terminate all active DMA transfers
836+
* @chan: The channel for which to terminate the transfers
837+
*
838+
* This function is DEPRECATED use either dmaengine_terminate_sync() or
839+
* dmaengine_terminate_async() instead.
840+
*/
831841
static inline int dmaengine_terminate_all(struct dma_chan *chan)
832842
{
833843
if (chan->device->device_terminate_all)
@@ -836,6 +846,86 @@ static inline int dmaengine_terminate_all(struct dma_chan *chan)
836846
return -ENOSYS;
837847
}
838848

849+
/**
850+
* dmaengine_terminate_async() - Terminate all active DMA transfers
851+
* @chan: The channel for which to terminate the transfers
852+
*
853+
* Calling this function will terminate all active and pending descriptors
854+
* that have previously been submitted to the channel. It is not guaranteed
855+
* though that the transfer for the active descriptor has stopped when the
856+
* function returns. Furthermore it is possible the complete callback of a
857+
* submitted transfer is still running when this function returns.
858+
*
859+
* dmaengine_synchronize() needs to be called before it is safe to free
860+
* any memory that is accessed by previously submitted descriptors or before
861+
* freeing any resources accessed from within the completion callback of any
862+
* perviously submitted descriptors.
863+
*
864+
* This function can be called from atomic context as well as from within a
865+
* complete callback of a descriptor submitted on the same channel.
866+
*
867+
* If none of the two conditions above apply consider using
868+
* dmaengine_terminate_sync() instead.
869+
*/
870+
static inline int dmaengine_terminate_async(struct dma_chan *chan)
871+
{
872+
if (chan->device->device_terminate_all)
873+
return chan->device->device_terminate_all(chan);
874+
875+
return -EINVAL;
876+
}
877+
878+
/**
879+
* dmaengine_synchronize() - Synchronize DMA channel termination
880+
* @chan: The channel to synchronize
881+
*
882+
* Synchronizes to the DMA channel termination to the current context. When this
883+
* function returns it is guaranteed that all transfers for previously issued
884+
* descriptors have stopped and and it is safe to free the memory assoicated
885+
* with them. Furthermore it is guaranteed that all complete callback functions
886+
* for a previously submitted descriptor have finished running and it is safe to
887+
* free resources accessed from within the complete callbacks.
888+
*
889+
* The behavior of this function is undefined if dma_async_issue_pending() has
890+
* been called between dmaengine_terminate_async() and this function.
891+
*
892+
* This function must only be called from non-atomic context and must not be
893+
* called from within a complete callback of a descriptor submitted on the same
894+
* channel.
895+
*/
896+
static inline void dmaengine_synchronize(struct dma_chan *chan)
897+
{
898+
if (chan->device->device_synchronize)
899+
chan->device->device_synchronize(chan);
900+
}
901+
902+
/**
903+
* dmaengine_terminate_sync() - Terminate all active DMA transfers
904+
* @chan: The channel for which to terminate the transfers
905+
*
906+
* Calling this function will terminate all active and pending transfers
907+
* that have previously been submitted to the channel. It is similar to
908+
* dmaengine_terminate_async() but guarantees that the DMA transfer has actually
909+
* stopped and that all complete callbacks have finished running when the
910+
* function returns.
911+
*
912+
* This function must only be called from non-atomic context and must not be
913+
* called from within a complete callback of a descriptor submitted on the same
914+
* channel.
915+
*/
916+
static inline int dmaengine_terminate_sync(struct dma_chan *chan)
917+
{
918+
int ret;
919+
920+
ret = dmaengine_terminate_async(chan);
921+
if (ret)
922+
return ret;
923+
924+
dmaengine_synchronize(chan);
925+
926+
return 0;
927+
}
928+
839929
static inline int dmaengine_pause(struct dma_chan *chan)
840930
{
841931
if (chan->device->device_pause)

0 commit comments

Comments
 (0)