Re: [PATCH v2 3/4] dmaengine: imx-sdma: implement channel termination via worker
From: Lucas Stach
Date: Mon Sep 17 2018 - 07:04:12 EST
Hi Robin,
Am Montag, den 17.09.2018, 07:51 +0000 schrieb Robin Gong:
> > -----Original Message-----
> > > > From: Lucas Stach <l.stach@xxxxxxxxxxxxxx>
> > Sent: 2018å9æ15æ 1:06
> > > > To: Vinod Koul <vkoul@xxxxxxxxxx>
> > > > > > Cc: dmaengine@xxxxxxxxxxxxxxx; linux-kernel@xxxxxxxxxxxxxxx; Robin Gong
> > > > > > <yibin.gong@xxxxxxx>; dl-linux-imx <linux-imx@xxxxxxx>;
> > kernel@xxxxxxxxxxxxxx; patchwork-lst@xxxxxxxxxxxxxx
> > Subject: [PATCH v2 3/4] dmaengine: imx-sdma: implement channel termination
> > via worker
> >
> > The dmaengine documentation states that device_terminate_all may be
> > asynchronous and need not wait for the active transfers have stopped.
> >
> > This allows us to move most of the functionality currently implemented in the
> > sdma channel termination function to run in a worker, outside of any atomic
> > context. Moving this out of atomic context has two
> > benefits: we can now sleep while waiting for the channel to terminate, instead
> > of busy waiting and the freeing of the dma descriptors happens with IRQs
> > enabled, getting rid of a warning in the dma mapping code.
> >
> > As the termination is now asnc, we need to implement the device_synchronize
> > dma engine function, which simply waits for the worker to finish its execution.
> >
> > > > Signed-off-by: Lucas Stach <l.stach@xxxxxxxxxxxxxx>
> > ---
> > v2: Keep vchan_get_all_descriptors in the terminate_all call, so the
> > ÂÂÂÂworker doesn't corrupt the next transfer if that's already in
> > ÂÂÂÂthe setup process.
> > ---
> > Âdrivers/dma/imx-sdma.c | 42 ++++++++++++++++++++++++++++++++----------
> > Â1 file changed, 32 insertions(+), 10 deletions(-)
> >
> > diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index
> > 8d2fec8b16cc..da41e8fbf151 100644
> > --- a/drivers/dma/imx-sdma.c
> > +++ b/drivers/dma/imx-sdma.c
> > @@ -32,6 +32,7 @@
> > Â#include <linux/of_address.h>
> > Â#include <linux/of_device.h>
> > Â#include <linux/of_dma.h>
> > +#include <linux/workqueue.h>
> >
> > Â#include <asm/irq.h>
> > Â#include <linux/platform_data/dma-imx-sdma.h>
> > @@ -375,6 +376,8 @@ struct sdma_channel {
> > > > > > Â u32 shp_addr, per_addr;
> > > > > > Â enum dma_status status;
> > > > > > Â struct imx_dma_data data;
> > > > > > + struct work_struct terminate_worker;
> > > > > > + struct list_head deferred_desc_list;
> > Â};
> >
> > > > Â#define IMX_DMA_SG_LOOP BIT(0)
> > @@ -1025,31 +1028,47 @@ static int sdma_disable_channel(struct dma_chan
> > *chan)
> >
> > > > Â return 0;
> > Â}
> > +static void sdma_channel_terminate(struct work_struct *work) {
> > > > + struct sdma_channel *sdmac = container_of(work, struct sdma_channel,
> > > > + ÂÂterminate_worker);
> > +
> > > > + /*
> > > > + Â* According to NXP R&D team a delay of one BD SDMA cost time
> > > > + Â* (maximum is 1ms) should be added after disable of the channel
> > > > + Â* bit, to ensure SDMA core has really been stopped after SDMA
> > > > + Â* clients call .device_terminate_all.
> > > > + Â*/
> > > > + usleep_range(1000, 2000);
> > +
> > > > + vchan_dma_desc_free_list(&sdmac->vc, &sdmac->deferred_desc_list);
> > > > + INIT_LIST_HEAD(&sdmac->deferred_desc_list);
> > +}
> >
> > Âstatic int sdma_disable_channel_with_delay(struct dma_chan *chan)ÂÂ{
> > > > Â struct sdma_channel *sdmac = to_sdma_chan(chan);
> > > > Â unsigned long flags;
> > > > - LIST_HEAD(head);
> >
> > > > Â sdma_disable_channel(chan);
> > +
> > > > Â spin_lock_irqsave(&sdmac->vc.lock, flags);
> > > > - vchan_get_all_descriptors(&sdmac->vc, &head);
> > > > + vchan_get_all_descriptors(&sdmac->vc, &sdmac->deferred_desc_list);
> > > > Â sdmac->desc = NULL;
> > > > Â spin_unlock_irqrestore(&sdmac->vc.lock, flags);
> > > > - vchan_dma_desc_free_list(&sdmac->vc, &head);
> >
> > > > - /*
> > > > - Â* According to NXP R&D team a delay of one BD SDMA cost time
> > > > - Â* (maximum is 1ms) should be added after disable of the channel
> > > > - Â* bit, to ensure SDMA core has really been stopped after SDMA
> > > > - Â* clients call .device_terminate_all.
> > > > - Â*/
> > > > - mdelay(1);
> > > > + schedule_work(&sdmac->terminate_worker);
> >
> > > > Â return 0;
> > Â}
> >
> > +static void sdma_channel_synchronize(struct dma_chan *chan) {
> > > > + struct sdma_channel *sdmac = to_sdma_chan(chan);
> > +
>
> Please add the below to make sure internal virt dma tasklet killed too.
> tasklet_kill(&sdmac->vc.task);
This is not possible. We can not do anything that alters the current
state of the VC in the synchronize call. Otherwise we could be running
into the the same class of issues as the v1 had, with corrupting the
state of the next set up transfer.
Regards,
Lucas