summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2011-03-18 07:31:53 +0100
committerTakashi Iwai <tiwai@suse.de>2011-03-18 07:31:53 +0100
commit98d21df431ad55281e1abf780f8d51e3391900b2 (patch)
tree19d365c9407ce581098596ec913a9cdb9ff7dd8a
parent5b7c757d1a8878d49f46c826d6081816935664aa (diff)
downloadlinux-98d21df431ad55281e1abf780f8d51e3391900b2.tar.gz
linux-98d21df431ad55281e1abf780f8d51e3391900b2.tar.bz2
linux-98d21df431ad55281e1abf780f8d51e3391900b2.zip
ALSA: aloop - Fix possible IRQ lock inversion
loopback_pos_update() can be called in the timer callback, thus the lock held should be irq-safe. Otherwise you'll get AB/BA deadlock together with substream->self_group.lock. Reported-and-tested-by: Knut Petersen <Knut_Petersen@t-online.de> Cc: <stable@kernel.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--sound/drivers/aloop.c19
1 files changed, 9 insertions, 10 deletions
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
index 12b44b0b6777..a0da7755fcea 100644
--- a/sound/drivers/aloop.c
+++ b/sound/drivers/aloop.c
@@ -482,8 +482,9 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable)
cable->streams[SNDRV_PCM_STREAM_CAPTURE];
unsigned long delta_play = 0, delta_capt = 0;
unsigned int running;
+ unsigned long flags;
- spin_lock(&cable->lock);
+ spin_lock_irqsave(&cable->lock, flags);
running = cable->running ^ cable->pause;
if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) {
delta_play = jiffies - dpcm_play->last_jiffies;
@@ -495,10 +496,8 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable)
dpcm_capt->last_jiffies += delta_capt;
}
- if (delta_play == 0 && delta_capt == 0) {
- spin_unlock(&cable->lock);
- return running;
- }
+ if (delta_play == 0 && delta_capt == 0)
+ goto unlock;
if (delta_play > delta_capt) {
loopback_bytepos_update(dpcm_play, delta_play - delta_capt,
@@ -510,14 +509,14 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable)
delta_capt = delta_play;
}
- if (delta_play == 0 && delta_capt == 0) {
- spin_unlock(&cable->lock);
- return running;
- }
+ if (delta_play == 0 && delta_capt == 0)
+ goto unlock;
+
/* note delta_capt == delta_play at this moment */
loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY);
loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY);
- spin_unlock(&cable->lock);
+ unlock:
+ spin_unlock_irqrestore(&cable->lock, flags);
return running;
}