Commit 86e1956a authored by Olivier Moysan's avatar Olivier Moysan Committed by Mark Brown

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

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.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent 2859b178
...@@ -220,6 +220,7 @@ ...@@ -220,6 +220,7 @@
* @slave_config: dma slave channel runtime config pointer * @slave_config: dma slave channel runtime config pointer
* @phys_addr: SPDIFRX registers physical base address * @phys_addr: SPDIFRX registers physical base address
* @lock: synchronization enabling lock * @lock: synchronization enabling lock
* @irq_lock: prevent race condition with IRQ on stream state
* @cs: channel status buffer * @cs: channel status buffer
* @ub: user data buffer * @ub: user data buffer
* @irq: SPDIFRX interrupt line * @irq: SPDIFRX interrupt line
...@@ -240,6 +241,7 @@ struct stm32_spdifrx_data { ...@@ -240,6 +241,7 @@ struct stm32_spdifrx_data {
struct dma_slave_config slave_config; struct dma_slave_config slave_config;
dma_addr_t phys_addr; dma_addr_t phys_addr;
spinlock_t lock; /* Sync enabling lock */ 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 cs[SPDIFRX_CS_BYTES_NB];
unsigned char ub[SPDIFRX_UB_BYTES_NB]; unsigned char ub[SPDIFRX_UB_BYTES_NB];
int irq; int irq;
...@@ -667,7 +669,6 @@ static const struct regmap_config stm32_h7_spdifrx_regmap_conf = { ...@@ -667,7 +669,6 @@ static const struct regmap_config stm32_h7_spdifrx_regmap_conf = {
static irqreturn_t stm32_spdifrx_isr(int irq, void *devid) static irqreturn_t stm32_spdifrx_isr(int irq, void *devid)
{ {
struct stm32_spdifrx_data *spdifrx = (struct stm32_spdifrx_data *)devid; struct stm32_spdifrx_data *spdifrx = (struct stm32_spdifrx_data *)devid;
struct snd_pcm_substream *substream = spdifrx->substream;
struct platform_device *pdev = spdifrx->pdev; struct platform_device *pdev = spdifrx->pdev;
unsigned int cr, mask, sr, imr; unsigned int cr, mask, sr, imr;
unsigned int flags, sync_state; unsigned int flags, sync_state;
...@@ -747,14 +748,19 @@ static irqreturn_t stm32_spdifrx_isr(int irq, void *devid) ...@@ -747,14 +748,19 @@ static irqreturn_t stm32_spdifrx_isr(int irq, void *devid)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
if (substream) spin_lock(&spdifrx->irq_lock);
snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); if (spdifrx->substream)
snd_pcm_stop(spdifrx->substream,
SNDRV_PCM_STATE_DISCONNECTED);
spin_unlock(&spdifrx->irq_lock);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
if (err_xrun && substream) spin_lock(&spdifrx->irq_lock);
snd_pcm_stop_xrun(substream); if (err_xrun && spdifrx->substream)
snd_pcm_stop_xrun(spdifrx->substream);
spin_unlock(&spdifrx->irq_lock);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -763,9 +769,12 @@ static int stm32_spdifrx_startup(struct snd_pcm_substream *substream, ...@@ -763,9 +769,12 @@ static int stm32_spdifrx_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai) struct snd_soc_dai *cpu_dai)
{ {
struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai); struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(cpu_dai);
unsigned long flags;
int ret; int ret;
spin_lock_irqsave(&spdifrx->irq_lock, flags);
spdifrx->substream = substream; spdifrx->substream = substream;
spin_unlock_irqrestore(&spdifrx->irq_lock, flags);
ret = clk_prepare_enable(spdifrx->kclk); ret = clk_prepare_enable(spdifrx->kclk);
if (ret) if (ret)
...@@ -841,8 +850,12 @@ static void stm32_spdifrx_shutdown(struct snd_pcm_substream *substream, ...@@ -841,8 +850,12 @@ static void stm32_spdifrx_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai) struct snd_soc_dai *cpu_dai)
{ {
struct stm32_spdifrx_data *spdifrx = snd_soc_dai_get_drvdata(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; spdifrx->substream = NULL;
spin_unlock_irqrestore(&spdifrx->irq_lock, flags);
clk_disable_unprepare(spdifrx->kclk); clk_disable_unprepare(spdifrx->kclk);
} }
...@@ -946,6 +959,7 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) ...@@ -946,6 +959,7 @@ static int stm32_spdifrx_probe(struct platform_device *pdev)
spdifrx->pdev = pdev; spdifrx->pdev = pdev;
init_completion(&spdifrx->cs_completion); init_completion(&spdifrx->cs_completion);
spin_lock_init(&spdifrx->lock); spin_lock_init(&spdifrx->lock);
spin_lock_init(&spdifrx->irq_lock);
platform_set_drvdata(pdev, spdifrx); platform_set_drvdata(pdev, spdifrx);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment