mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-21 08:19:28 +00:00
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:
parent
2859b17840
commit
86e1956af4
1 changed files with 19 additions and 5 deletions
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue