]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
dmaengine: at_xdmac: fix rare residue corruption
authorMaxime Jayat <maxime.jayat@mobile-devices.fr>
Thu, 22 Feb 2018 11:39:55 +0000 (12:39 +0100)
committerSasha Levin <alexander.levin@microsoft.com>
Wed, 23 May 2018 01:36:33 +0000 (21:36 -0400)
[ Upstream commit c5637476bbf9bb86c7f0413b8f4822a73d8d2d07 ]

Despite the efforts made to correctly read the NDA and CUBC registers,
the order in which the registers are read could sometimes lead to an
inconsistent state.

Re-using the timeline from the comments, this following timing of
registers reads could lead to reading NDA with value "@desc2" and
CUBC with value "MAX desc1":

 INITD --------                    ------------
              |____________________|
       _______________________  _______________
 NDA       @desc2             \/   @desc3
       _______________________/\_______________
       __________  ___________  _______________
 CUBC       0    \/ MAX desc1 \/  MAX desc2
       __________/\___________/\_______________
        |  |          |  |
Events:(1)(2)        (3)(4)

(1) check_nda = @desc2
(2) initd = 1
(3) cur_ubc = MAX desc1
(4) cur_nda = @desc2

This is allowed by the condition ((check_nda == cur_nda) && initd),
despite cur_ubc and cur_nda being in the precise state we don't want.

This error leads to incorrect residue computation.

Fix it by inversing the order in which CUBC and INITD are read. This
makes sure that NDA and CUBC are always read together either _before_
INITD goes to 0 or _after_ it is back at 1.
The case where NDA is read before INITD is at 0 and CUBC is read after
INITD is back at 1 will be rejected by check_nda and cur_nda being
different.

Fixes: 53398f488821 ("dmaengine: at_xdmac: fix residue corruption")
Cc: stable@vger.kernel.org
Signed-off-by: Maxime Jayat <maxime.jayat@mobile-devices.fr>
Acked-by: Ludovic Desroches <ludovic.desroches@microchip.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Signed-off-by: Sasha Levin <alexander.levin@microsoft.com>
drivers/dma/at_xdmac.c

index c5e6c82516ce83bdff76c2636e8d61bc4c3855de..e4b4c5c0703723c7a987820c57c06d886691e25e 100644 (file)
@@ -1003,10 +1003,10 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
        for (retry = 0; retry < AT_XDMAC_RESIDUE_MAX_RETRIES; retry++) {
                check_nda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA) & 0xfffffffc;
                rmb();
-               initd = !!(at_xdmac_chan_read(atchan, AT_XDMAC_CC) & AT_XDMAC_CC_INITD);
-               rmb();
                cur_ubc = at_xdmac_chan_read(atchan, AT_XDMAC_CUBC);
                rmb();
+               initd = !!(at_xdmac_chan_read(atchan, AT_XDMAC_CC) & AT_XDMAC_CC_INITD);
+               rmb();
                cur_nda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA) & 0xfffffffc;
                rmb();