Received: by 2002:ac0:946b:0:0:0:0:0 with SMTP id j40csp2747548imj; Mon, 11 Feb 2019 07:54:30 -0800 (PST) X-Google-Smtp-Source: AHgI3IYT0jXxB+DP5sJsRbn4P7M3jZeV+E8kkC4k7KV+VUBsmPxl+w0escoQVs/PjSeJHEMoUV6U X-Received: by 2002:a63:2d6:: with SMTP id 205mr630981pgc.180.1549900470664; Mon, 11 Feb 2019 07:54:30 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1549900470; cv=none; d=google.com; s=arc-20160816; b=0Vjp7G+vHiBujYi5hwY+1RSKcumfWRn5LlrQCx+FWkvCWcvZ0vdPdHCgTnOvTQDcCz 9ND0DYPDtCAnh73Jh7Axp7YFzn81JIw2slZH67IaiZU7qWYV2pGJ7Cx+hXmESnDTkunB bBoexWdaGtYnV12a21WUOjPB7KAZf8CHIwMlyQ2Tx0URsU49uTDIpCqZXFCADyVbc2PW hzx4SLvz4oW7s3kVrQf694WBN8eXjIfcPVT17TbnrmOQDzMOsCpun2uQEq843T5/YMGT z9pX9RodmeRoWa1XTa8KFMjl3WvtYOChnM55GrzaVyhb91dcGoqUTS5inq0GZnVR4cDo TY1Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=bwXk9+a+IgGgbi8FHJIE/j4VFybR9cUMDHOw/vpfoHw=; b=Pa1+Xo6HHbkTebhilFIuLe6IrOoteyruKXFGwve1JbOe7N1DRixgB7LDaXJIMt9fb6 6cZf+4kZN2ld8MtWjn0tQA/+ge/nAr/5EFW73Yk3fKctdscgDmloteb/WX35NGRGPRK3 UBQ42MdUTHx8vlebLAMd4wNsxLd3KP2aN92e5apk+60TXbECTreV05Vj7N8WqduastJP 39f7BFiquPgmkvHyRfUwzyKM3v0KRRHyPTOo9QSzpEuD22AFagHHaMWlxuhfLsajyZhg 5RV5URERjErlNvZccntsrTzsSwYpo9IjKkhHzxKhkd66aWdSAPfSM7/qpzTfmhlLwX34 gjgQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=MCQnpgrS; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id r25si7385930pgm.349.2019.02.11.07.54.13; Mon, 11 Feb 2019 07:54:30 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=MCQnpgrS; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731899AbfBKPxU (ORCPT + 99 others); Mon, 11 Feb 2019 10:53:20 -0500 Received: from mail.kernel.org ([198.145.29.99]:45780 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731279AbfBKOgh (ORCPT ); Mon, 11 Feb 2019 09:36:37 -0500 Received: from localhost (5356596B.cm-6-7b.dynamic.ziggo.nl [83.86.89.107]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id 77E392080F; Mon, 11 Feb 2019 14:36:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1549895797; bh=523LM5F/KmEq0AIq9cyc/YRuDCTzzKL1mar9Oopdc8Q=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=MCQnpgrS3fp8Zgy6276BSlTWRrn58pZVi64Efx8Epa3UdwDY7L81bAli4hEhg0V9K gXW7v+EGtnfAo+3uq8qFt+ZYm8hXXOi5z5rXoBV9VXT+Ke36KVcqSl+si9S6GkbdYt 6Ny0Dx3hy3IE7QWr6Tc4TF8ksguaPpOgMAYUKiF8= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Lukas Wunner , Frank Pavlic , Martin Sperl , Florian Meier , Clive Messer , Matthias Reichl , Stefan Wahren , Florian Kauer , Vinod Koul Subject: [PATCH 4.20 325/352] dmaengine: bcm2835: Fix interrupt race on RT Date: Mon, 11 Feb 2019 15:19:12 +0100 Message-Id: <20190211141907.516192750@linuxfoundation.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190211141846.543045703@linuxfoundation.org> References: <20190211141846.543045703@linuxfoundation.org> User-Agent: quilt/0.65 X-stable: review X-Patchwork-Hint: ignore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 4.20-stable review patch. If anyone has any objections, please let me know. ------------------ From: Lukas Wunner commit f7da7782aba92593f7b82f03d2409a1c5f4db91b upstream. If IRQ handlers are threaded (either because CONFIG_PREEMPT_RT_BASE is enabled or "threadirqs" was passed on the command line) and if system load is sufficiently high that wakeup latency of IRQ threads degrades, SPI DMA transactions on the BCM2835 occasionally break like this: ks8851 spi0.0: SPI transfer timed out bcm2835-dma 3f007000.dma: DMA transfer could not be terminated ks8851 spi0.0 eth2: ks8851_rdfifo: spi_sync() failed The root cause is an assumption made by the DMA driver which is documented in a code comment in bcm2835_dma_terminate_all(): /* * Stop DMA activity: we assume the callback will not be called * after bcm_dma_abort() returns (even if it does, it will see * c->desc is NULL and exit.) */ That assumption falls apart if the IRQ handler bcm2835_dma_callback() is threaded: A client may terminate a descriptor and issue a new one before the IRQ handler had a chance to run. In fact the IRQ handler may miss an *arbitrary* number of descriptors. The result is the following race condition: 1. A descriptor finishes, its interrupt is deferred to the IRQ thread. 2. A client calls dma_terminate_async() which sets channel->desc = NULL. 3. The client issues a new descriptor. Because channel->desc is NULL, bcm2835_dma_issue_pending() immediately starts the descriptor. 4. Finally the IRQ thread runs and writes BCM2835_DMA_INT to the CS register to acknowledge the interrupt. This clears the ACTIVE flag, so the newly issued descriptor is paused in the middle of the transaction. Because channel->desc is not NULL, the IRQ thread finalizes the descriptor and tries to start the next one. I see two possible solutions: The first is to call synchronize_irq() in bcm2835_dma_issue_pending() to wait until the IRQ thread has finished before issuing a new descriptor. The downside of this approach is unnecessary latency if clients desire rapidly terminating and re-issuing descriptors and don't have any use for an IRQ callback. (The SPI TX DMA channel is a case in point.) A better alternative is to make the IRQ thread recognize that it has missed descriptors and avoid finalizing the newly issued descriptor. So first of all, set the ACTIVE flag when acknowledging the interrupt. This keeps a newly issued descriptor running. If the descriptor was finished, the channel remains idle despite the ACTIVE flag being set. However the ACTIVE flag can then no longer be used to check whether the channel is idle, so instead check whether the register containing the current control block address is zero and finalize the current descriptor only if so. That way, there is no impact on latency and throughput if the client doesn't care for the interrupt: Only minimal additional overhead is introduced for non-cyclic descriptors as one further MMIO read is necessary per interrupt to check for idleness of the channel. Cyclic descriptors are sped up slightly by removing one MMIO write per interrupt. Fixes: 96286b576690 ("dmaengine: Add support for BCM2835") Signed-off-by: Lukas Wunner Cc: stable@vger.kernel.org # v3.14+ Cc: Frank Pavlic Cc: Martin Sperl Cc: Florian Meier Cc: Clive Messer Cc: Matthias Reichl Tested-by: Stefan Wahren Acked-by: Florian Kauer Signed-off-by: Vinod Koul Signed-off-by: Greg Kroah-Hartman --- drivers/dma/bcm2835-dma.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) --- a/drivers/dma/bcm2835-dma.c +++ b/drivers/dma/bcm2835-dma.c @@ -421,7 +421,12 @@ static int bcm2835_dma_abort(void __iome long int timeout = 10000; cs = readl(chan_base + BCM2835_DMA_CS); - if (!(cs & BCM2835_DMA_ACTIVE)) + + /* + * A zero control block address means the channel is idle. + * (The ACTIVE flag in the CS register is not a reliable indicator.) + */ + if (!readl(chan_base + BCM2835_DMA_ADDR)) return 0; /* Write 0 to the active bit - Pause the DMA */ @@ -485,8 +490,15 @@ static irqreturn_t bcm2835_dma_callback( spin_lock_irqsave(&c->vc.lock, flags); - /* Acknowledge interrupt */ - writel(BCM2835_DMA_INT, c->chan_base + BCM2835_DMA_CS); + /* + * Clear the INT flag to receive further interrupts. Keep the channel + * active in case the descriptor is cyclic or in case the client has + * already terminated the descriptor and issued a new one. (May happen + * if this IRQ handler is threaded.) If the channel is finished, it + * will remain idle despite the ACTIVE flag being set. + */ + writel(BCM2835_DMA_INT | BCM2835_DMA_ACTIVE, + c->chan_base + BCM2835_DMA_CS); d = c->desc; @@ -494,11 +506,7 @@ static irqreturn_t bcm2835_dma_callback( if (d->cyclic) { /* call the cyclic callback */ vchan_cyclic_callback(&d->vd); - - /* Keep the DMA engine running */ - writel(BCM2835_DMA_ACTIVE, - c->chan_base + BCM2835_DMA_CS); - } else { + } else if (!readl(c->chan_base + BCM2835_DMA_ADDR)) { vchan_cookie_complete(&c->desc->vd); bcm2835_dma_start_desc(c); } @@ -798,11 +806,7 @@ static int bcm2835_dma_terminate_all(str list_del_init(&c->node); spin_unlock(&d->lock); - /* - * Stop DMA activity: we assume the callback will not be called - * after bcm_dma_abort() returns (even if it does, it will see - * c->desc is NULL and exit.) - */ + /* stop DMA activity */ if (c->desc) { vchan_terminate_vdesc(&c->desc->vd); c->desc = NULL; @@ -810,8 +814,7 @@ static int bcm2835_dma_terminate_all(str /* Wait for stopping */ while (--timeout) { - if (!(readl(c->chan_base + BCM2835_DMA_CS) & - BCM2835_DMA_ACTIVE)) + if (!readl(c->chan_base + BCM2835_DMA_ADDR)) break; cpu_relax();