Re: Possible regression in 8250_dw driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, 25 May 2023, Richard Tresidder wrote:
> On 25/05/2023 4:43 pm, Ilpo Järvinen wrote:
> > On Thu, 25 May 2023, Richard Tresidder wrote:
> > > On 24/05/2023 6:06 pm, Ilpo Järvinen wrote:
> > > > On Wed, 24 May 2023, Richard Tresidder wrote:
> > > > > So I just started reverting the patches that had been applied to the
> > > > > 8250
> > > > > folder.
> > > > > Worked backwards from head.
> > > > > 
> > > > > After reverting 57e9af7831dcf211c5c689c2a6f209f4abdf0bce
> > > > > serial: 8250_dma: Fix DMA Rx rearm race
> > > > > 
> > > > > Things started to work again.
> > > > > 
> > > > > I reset everything and then just reverted that individual patch and
> > > > > things
> > > > > work.
> > > > > So that looks like the culprit..
> > > > Okay, that also worked great, thanks a lot. It gives something concrete
> > > > to
> > > > work with to find a fix.
> > > > 
> > > > The commit itself looks very much correct, however, my guess is that
> > > > when
> > > > serial8250_rx_dma_flush() does dmaengine_pause() dma_status somehow
> > > > does not become DMA_PAUSED which leads to not properly flushing DMA Rx
> > > > transaction. Can you try the following debug patch (if my guess is
> > > > correct, besides triggering the WARN_ON_ONCE, it also works around the
> > > > issue):
> > > > 
> > > > [PATCH] DEBUG: 8250_dma: Detect DMA_PAUSED vs DMA_IN_PROGRESS
> > > > inconsistency
> > > > 
> > > > Detect DMA state not returning DMA_PAUSED after dmaengine_pause() was
> > > > issued.
> > > > 
> > > > Not intended for upstream.
> > > Thanks Ilpo
> > >     I can confirm that the patch below works. Got the WARN_ON_ONCE dump so
> > > it's
> > > taking that path.
> > > I think the problem here is that the pl330 DMA controller thats in these
> > > SOC's
> > > doesn't "really" support pause according to the driver.
> > For this flush usecase, it is enough to support pause + read residue +
> > terminate which is supported according to the function comment for
> > pl330_pause().
> 
> Yep agree given the 8250 serial code immediately terminates the dma after the
> pause during the flush anyway..
> 
> > > It doesn't look like it can ever set "DMA_PAUSED"
> > Why not? It pauses the transfer and is even able to allow reading the
> > transferred byte count.
> > 
> > What it is claimed to not be able to do is resume. Note that w/o resume
> > serial8250_tx_dma() XON/XOFF processing will not work but that's
> > unrelated to this issue (I'll probably need to do another patch to fix
> > that on 8250 DMA side).
> 
> Yep I just meant it doesn't look capable of reporting DMA_PAUSED
> Which your patch below probably fixes.
> and it kind of does a stop without a resume capability so must be terminated
> after this.
> Though I'm not sure of the implications of reporting paused without resume
> capability.
> Kind of an odd one..

dma_slave_caps has a bool for both pause and resume support (I only found 
out about the second flag for resume today) so not unheard of it seems.

-- 
 i.


> I'll check your pl330 patch out tomorrow, and see what else might break.
>
> > > I'm not sure of the flow through of that though.
> > > There is some commenting in the pl330 driver about the pause routine.
> > Maybe the patch below will help with pl330 DMA status (based on somewhat
> > limited understanding of all the moving parts):
> > 
> > 
> > [PATCH] dmaengine: pl330: Set DMA_PAUSED when pausing
> > 
> > pl330_pause() does not set anything to indicate paused condition which
> > causes pl330_tx_status() to return DMA_IN_PROGRESS. This breaks 8250
> > DMA code after the fix in commit 57e9af7831dc ("serial: 8250_dma: Fix
> > DMA Rx rearm race"). The function comment for pl330_pause() claims
> > pause is supported but resume is not which is enough for 8250 DMA flush
> > to work as long as DMA status is reported correctly when appropriate.
> > 
> > Add PAUSED state for descriptor and mark BUSY descriptors with PAUSED
> > in pl330_pause(). Return DMA_PAUSED from pl330_tx_status() when the
> > descriptor is PAUSED.
> > 
> > Fixes: 88987d2c7534 ("dmaengine: pl330: add DMA_PAUSE feature")
> > Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@xxxxxxxxxxxxxxx>
> > 
> > ---
> >   drivers/dma/pl330.c | 18 ++++++++++++++++--
> >   1 file changed, 16 insertions(+), 2 deletions(-)
> > 
> > diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
> > index 0d9257fbdfb0..daad25f2c498 100644
> > --- a/drivers/dma/pl330.c
> > +++ b/drivers/dma/pl330.c
> > @@ -403,6 +403,12 @@ enum desc_status {
> >   	 * of a channel can be BUSY at any time.
> >   	 */
> >   	BUSY,
> > +	/*
> > +	 * Pause was called while descriptor was BUSY. Due to hardware
> > +	 * limitations, only termination is possible for descriptors
> > +	 * that have been paused.
> > +	 */
> > +	PAUSED,
> >   	/*
> >   	 * Sitting on the channel work_list but xfer done
> >   	 * by PL330 core
> > @@ -2041,7 +2047,7 @@ static inline void fill_queue(struct dma_pl330_chan
> > *pch)
> >   	list_for_each_entry(desc, &pch->work_list, node) {
> >     		/* If already submitted */
> > -		if (desc->status == BUSY)
> > +		if (desc->status == BUSY || desc->status == PAUSED)
> >   			continue;
> >     		ret = pl330_submit_req(pch->thread, desc);
> > @@ -2326,6 +2332,7 @@ static int pl330_pause(struct dma_chan *chan)
> >   {
> >   	struct dma_pl330_chan *pch = to_pchan(chan);
> >   	struct pl330_dmac *pl330 = pch->dmac;
> > +	struct dma_pl330_desc *desc;
> >   	unsigned long flags;
> >     	pm_runtime_get_sync(pl330->ddma.dev);
> > @@ -2335,6 +2342,10 @@ static int pl330_pause(struct dma_chan *chan)
> >   	_stop(pch->thread);
> >   	spin_unlock(&pl330->lock);
> >   +	list_for_each_entry(desc, &pch->work_list, node) {
> > +		if (desc->status == BUSY)
> > +			desc->status = PAUSED;
> > +	}
> >   	spin_unlock_irqrestore(&pch->lock, flags);
> >   	pm_runtime_mark_last_busy(pl330->ddma.dev);
> >   	pm_runtime_put_autosuspend(pl330->ddma.dev);
> > @@ -2425,7 +2436,7 @@ pl330_tx_status(struct dma_chan *chan, dma_cookie_t
> > cookie,
> >   		else if (running && desc == running)
> >   			transferred =
> >   				pl330_get_current_xferred_count(pch, desc);
> > -		else if (desc->status == BUSY)
> > +		else if (desc->status == BUSY || desc->status == PAUSED)
> >   			/*
> >   			 * Busy but not running means either just enqueued,
> >   			 * or finished and not yet marked done
> > @@ -2442,6 +2453,9 @@ pl330_tx_status(struct dma_chan *chan, dma_cookie_t
> > cookie,
> >   			case DONE:
> >   				ret = DMA_COMPLETE;
> >   				break;
> > +			case PAUSED:
> > +				ret = DMA_PAUSED;
> > +				break;
> >   			case PREP:
> >   			case BUSY:
> >   				ret = DMA_IN_PROGRESS;
> > 
> 

[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux