Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ASoC: samsung: Use IRQ safe spin lock calls

Lockdep warns of a potential lock inversion, i2s->lock is held numerous
times whilst we are under the substream lock (snd_pcm_stream_lock). If
we use the IRQ unsafe spin lock calls, you can also end up locking
snd_pcm_stream_lock whilst under i2s->lock (if an IRQ happens whilst we
are holding i2s->lock). This could result in deadlock.

[ 18.147001] CPU0 CPU1
[ 18.151509] ---- ----
[ 18.156022] lock(&(&pri_dai->spinlock)->rlock);
[ 18.160701] local_irq_disable();
[ 18.166622] lock(&(&substream->self_group.lock)->rlock);
[ 18.174595] lock(&(&pri_dai->spinlock)->rlock);
[ 18.181806] <Interrupt>
[ 18.184408] lock(&(&substream->self_group.lock)->rlock);
[ 18.190045]
[ 18.190045] *** DEADLOCK ***

This patch changes to using the irq safe spinlock calls, to avoid this
issue.

Fixes: ce8bcdbb61d9 ("ASoC: samsung: i2s: Protect more registers with a spinlock")
Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Tested-by: Anand Moon <linux.amoon@gmail.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Cc: stable@vger.kernel.org

authored by

Charles Keepax and committed by
Mark Brown
316fa9e0 92e963f5

+12 -9
+12 -9
sound/soc/samsung/i2s.c
··· 481 481 unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off; 482 482 unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off; 483 483 u32 mod, mask, val = 0; 484 + unsigned long flags; 484 485 485 - spin_lock(i2s->lock); 486 + spin_lock_irqsave(i2s->lock, flags); 486 487 mod = readl(i2s->addr + I2SMOD); 487 - spin_unlock(i2s->lock); 488 + spin_unlock_irqrestore(i2s->lock, flags); 488 489 489 490 switch (clk_id) { 490 491 case SAMSUNG_I2S_OPCLK: ··· 576 575 return -EINVAL; 577 576 } 578 577 579 - spin_lock(i2s->lock); 578 + spin_lock_irqsave(i2s->lock, flags); 580 579 mod = readl(i2s->addr + I2SMOD); 581 580 mod = (mod & ~mask) | val; 582 581 writel(mod, i2s->addr + I2SMOD); 583 - spin_unlock(i2s->lock); 582 + spin_unlock_irqrestore(i2s->lock, flags); 584 583 585 584 return 0; 586 585 } ··· 591 590 struct i2s_dai *i2s = to_info(dai); 592 591 int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave; 593 592 u32 mod, tmp = 0; 593 + unsigned long flags; 594 594 595 595 lrp_shift = i2s->variant_regs->lrp_off; 596 596 sdf_shift = i2s->variant_regs->sdf_off; ··· 651 649 return -EINVAL; 652 650 } 653 651 654 - spin_lock(i2s->lock); 652 + spin_lock_irqsave(i2s->lock, flags); 655 653 mod = readl(i2s->addr + I2SMOD); 656 654 /* 657 655 * Don't change the I2S mode if any controller is active on this ··· 659 657 */ 660 658 if (any_active(i2s) && 661 659 ((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) { 662 - spin_unlock(i2s->lock); 660 + spin_unlock_irqrestore(i2s->lock, flags); 663 661 dev_err(&i2s->pdev->dev, 664 662 "%s:%d Other DAI busy\n", __func__, __LINE__); 665 663 return -EAGAIN; ··· 668 666 mod &= ~(sdf_mask | lrp_rlow | mod_slave); 669 667 mod |= tmp; 670 668 writel(mod, i2s->addr + I2SMOD); 671 - spin_unlock(i2s->lock); 669 + spin_unlock_irqrestore(i2s->lock, flags); 672 670 673 671 return 0; 674 672 } ··· 678 676 { 679 677 struct i2s_dai *i2s = to_info(dai); 680 678 u32 mod, mask = 0, val = 0; 679 + unsigned long flags; 681 680 682 681 if (!is_secondary(i2s)) 683 682 mask |= (MOD_DC2_EN | MOD_DC1_EN); ··· 747 744 return -EINVAL; 748 745 } 749 746 750 - spin_lock(i2s->lock); 747 + spin_lock_irqsave(i2s->lock, flags); 751 748 mod = readl(i2s->addr + I2SMOD); 752 749 mod = (mod & ~mask) | val; 753 750 writel(mod, i2s->addr + I2SMOD); 754 - spin_unlock(i2s->lock); 751 + spin_unlock_irqrestore(i2s->lock, flags); 755 752 756 753 samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); 757 754