浏览代码

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>
Takashi Iwai 10 年之前
父节点
当前提交
95926035b1
共有 1 个文件被更改,包括 2 次插入4 次删除
  1. 2 4
      sound/pci/emu10k1/emu10k1_callback.c

+ 2 - 4
sound/pci/emu10k1/emu10k1_callback.c

@@ -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;