ALSA: emu10k1: Fix deadlock in synth voice lookup

The emu10k1 voice allocator takes voice_lock spinlock.  When there is
no empty stream available, it tries to release a voice used by synth,
and calls get_synth_voice.  The callback function,
snd_emu10k1_synth_get_voice(), however, also takes the voice_lock,
thus it deadlocks.

The fix is simply removing the voice_lock holds in
snd_emu10k1_synth_get_voice(), as this is always called in the
spinlock context.

Reported-and-tested-by: Arthur Marsh <arthur.marsh@internode.on.net>
Cc: <stable@vger.kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2014-10-13 23:18:02 +02:00
parent 811deedeba
commit 95926035b1
1 changed files with 2 additions and 4 deletions

View File

@ -85,6 +85,8 @@ snd_emu10k1_ops_setup(struct snd_emux *emux)
* get more voice for pcm * get more voice for pcm
* *
* terminate most inactive voice and give it as a pcm voice. * terminate most inactive voice and give it as a pcm voice.
*
* voice_lock is already held.
*/ */
int int
snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw) snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw)
@ -92,12 +94,10 @@ snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw)
struct snd_emux *emu; struct snd_emux *emu;
struct snd_emux_voice *vp; struct snd_emux_voice *vp;
struct best_voice best[V_END]; struct best_voice best[V_END];
unsigned long flags;
int i; int i;
emu = hw->synth; emu = hw->synth;
spin_lock_irqsave(&emu->voice_lock, flags);
lookup_voices(emu, hw, best, 1); /* no OFF voices */ lookup_voices(emu, hw, best, 1); /* no OFF voices */
for (i = 0; i < V_END; i++) { for (i = 0; i < V_END; i++) {
if (best[i].voice >= 0) { if (best[i].voice >= 0) {
@ -113,11 +113,9 @@ snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw)
vp->emu->num_voices--; vp->emu->num_voices--;
vp->ch = -1; vp->ch = -1;
vp->state = SNDRV_EMUX_ST_OFF; vp->state = SNDRV_EMUX_ST_OFF;
spin_unlock_irqrestore(&emu->voice_lock, flags);
return ch; return ch;
} }
} }
spin_unlock_irqrestore(&emu->voice_lock, flags);
/* not found */ /* not found */
return -ENOMEM; return -ENOMEM;