Commit 24e30e721438600a496bcf1ae44cb4d7c93eafd8

Authored by Robin Gong
Committed by fang hui
1 parent b0bd033022

dmaengine: imx-sdma: add terminated list for freed descriptor in worker

Add terminated list for keeping descriptor so that it could be freed in
worker without any potential involving next descriptor raised up before
this descriptor freed, because vchan_get_all_descriptors get all
descriptors including the last terminated descriptor and the next
descriptor, hence, the next descriptor maybe freed unexpectly when it's
done in worker without this patch.
https://www.spinics.net/lists/dmaengine/msg23367.html

Change-Id: Iece434f55f3c6b5cf861fc23e4076fd4aa670ac4
Signed-off-by: Robin Gong <yibin.gong@nxp.com>
Reported-by: Richard Leitner <richard.leitner@skidata.com>

Showing 1 changed file with 10 additions and 7 deletions Side-by-side Diff

drivers/dma/imx-sdma.c
... ... @@ -394,6 +394,7 @@
394 394 enum dma_status status;
395 395 struct imx_dma_data data;
396 396 struct work_struct terminate_worker;
  397 + struct list_head terminated;
397 398 bool is_ram_script;
398 399 bool src_dualfifo;
399 400 bool dst_dualfifo;
... ... @@ -1216,9 +1217,6 @@
1216 1217 {
1217 1218 struct sdma_channel *sdmac = container_of(work, struct sdma_channel,
1218 1219 terminate_worker);
1219   - unsigned long flags;
1220   - LIST_HEAD(head);
1221   -
1222 1220 /*
1223 1221 * According to NXP R&D team a delay of one BD SDMA cost time
1224 1222 * (maximum is 1ms) should be added after disable of the channel
... ... @@ -1227,10 +1225,7 @@
1227 1225 */
1228 1226 usleep_range(1000, 2000);
1229 1227  
1230   - spin_lock_irqsave(&sdmac->vc.lock, flags);
1231   - vchan_get_all_descriptors(&sdmac->vc, &head);
1232   - spin_unlock_irqrestore(&sdmac->vc.lock, flags);
1233   - vchan_dma_desc_free_list(&sdmac->vc, &head);
  1228 + vchan_dma_desc_free_list(&sdmac->vc, &sdmac->terminated);
1234 1229 }
1235 1230  
1236 1231 static int sdma_terminate_all(struct dma_chan *chan)
... ... @@ -1244,6 +1239,13 @@
1244 1239  
1245 1240 if (sdmac->desc) {
1246 1241 vchan_terminate_vdesc(&sdmac->desc->vd);
  1242 + /*
  1243 + * move out current descriptor into terminated list so that
  1244 + * it could be free in sdma_channel_terminate_work alone
  1245 + * later without potential involving next descriptor raised
  1246 + * up before the last descriptor terminated.
  1247 + */
  1248 + vchan_get_all_descriptors(&sdmac->vc, &sdmac->terminated);
1247 1249 sdmac->desc = NULL;
1248 1250 schedule_work(&sdmac->terminate_worker);
1249 1251 }
... ... @@ -2375,6 +2377,7 @@
2375 2377  
2376 2378 sdmac->channel = i;
2377 2379 sdmac->vc.desc_free = sdma_desc_free;
  2380 + INIT_LIST_HEAD(&sdmac->terminated);
2378 2381 INIT_WORK(&sdmac->terminate_worker,
2379 2382 sdma_channel_terminate_work);
2380 2383 /*