Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7835b072 authored by Olivier Moysan's avatar Olivier Moysan Committed by Greg Kroah-Hartman
Browse files

ASoC: stm32: spdifrx: fix race condition in irq handler



commit 86e1956af4c863d653136fd6e5694adf2054dbaa upstream.

When snd_pcm_stop() is called in interrupt routine,
substream context may have already been released.
Add protection on substream context.

Fixes: 03e4d5d5 ("ASoC: stm32: Add SPDIFRX support")

Signed-off-by: default avatarOlivier Moysan <olivier.moysan@st.com>
Link: https://lore.kernel.org/r/20191204154333.7152-3-olivier.moysan@st.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 5063469c
Loading
Loading
Loading
Loading
+19 −5
Original line number Diff line number Diff line
@@ -213,6 +213,7 @@
 * @slave_config: dma slave channel runtime config pointer
 * @phys_addr: SPDIFRX registers physical base address
 * @lock: synchronization enabling lock
 * @irq_lock: prevent race condition with IRQ on stream state
 * @cs: channel status buffer
 * @ub: user data buffer
 * @irq: SPDIFRX interrupt line
@@ -233,6 +234,7 @@ struct stm32_spdifrx_data {
	struct dma_slave_config slave_config;
	dma_addr_t phys_addr;
	spinlock_t lock;  /* Sync enabling lock */
	spinlock_t irq_lock; /* Prevent race condition on stream state */
	unsigned char cs[SPDIFRX_CS_BYTES_NB];
	unsigned char ub[SPDIFRX_UB_BYTES_NB];
	int irq;
@@ -645,7 +647,6 @@ static const struct regmap_config stm32_h7_spdifrx_regmap_conf = {
static irqreturn_t stm32_spdifrx_isr(int irq, void *devid)
{
	struct stm32_spdifrx_data *spdifrx = (struct stm32_spdifrx_data *)devid;
	struct snd_pcm_substream *substream = spdifrx->substream;
	struct platform_device *pdev = spdifrx->pdev;
	unsigned int cr, mask, sr, imr;
	unsigned int flags;
@@ -713,14 +714,19 @@ static irqreturn_t stm32_spdifrx_isr(int irq, void *devid)
		regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR,
				   SPDIFRX_CR_SPDIFEN_MASK, cr);

		if (substream)
			snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
		spin_lock(&spdifrx->irq_lock);
		if (spdifrx->substream)
			snd_pcm_stop(spdifrx->substream,
				     SNDRV_PCM_STATE_DISCONNECTED);
		spin_unlock(&spdifrx->irq_lock);

		return IRQ_HANDLED;
	}

	if (err_xrun && substream)
		snd_pcm_stop_xrun(substream);
	spin_lock(&spdifrx->irq_lock);
	if (err_xrun && spdifrx->substream)
		snd_pcm_stop_xrun(spdifrx->substream);
	spin_unlock(&spdifrx->irq_lock);

	return IRQ_HANDLED;
}
@@ -729,9 +735,12 @@ static int stm32_spdifrx_startup(struct snd_pcm_substream *substream,
				 struct snd_soc_dai *cpu_dai)
{
	struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai);
	unsigned long flags;
	int ret;

	spin_lock_irqsave(&spdifrx->irq_lock, flags);
	spdifrx->substream = substream;
	spin_unlock_irqrestore(&spdifrx->irq_lock, flags);

	ret = clk_prepare_enable(spdifrx->kclk);
	if (ret)
@@ -807,8 +816,12 @@ static void stm32_spdifrx_shutdown(struct snd_pcm_substream *substream,
				   struct snd_soc_dai *cpu_dai)
{
	struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai);
	unsigned long flags;

	spin_lock_irqsave(&spdifrx->irq_lock, flags);
	spdifrx->substream = NULL;
	spin_unlock_irqrestore(&spdifrx->irq_lock, flags);

	clk_disable_unprepare(spdifrx->kclk);
}

@@ -912,6 +925,7 @@ static int stm32_spdifrx_probe(struct platform_device *pdev)
	spdifrx->pdev = pdev;
	init_completion(&spdifrx->cs_completion);
	spin_lock_init(&spdifrx->lock);
	spin_lock_init(&spdifrx->irq_lock);

	platform_set_drvdata(pdev, spdifrx);