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: 03e4d5d56f ("ASoC: stm32: Add SPDIFRX support")

Signed-off-by: Olivier Moysan <olivier.moysan@st.com>
Link: https://lore.kernel.org/r/20191204154333.7152-3-olivier.moysan@st.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Olivier Moysan 2019-12-04 16:43:32 +01:00 committed by Mark Brown
parent 2859b17840
commit 86e1956af4
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0

View File

@ -220,6 +220,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
@ -240,6 +241,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;
@ -667,7 +669,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, sync_state;
@ -747,14 +748,19 @@ static irqreturn_t stm32_spdifrx_isr(int irq, void *devid)
return IRQ_HANDLED;
}
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;
}
@ -763,9 +769,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)
@ -841,8 +850,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);
}
@ -946,6 +959,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);