diff options
author | Takashi Iwai <tiwai@suse.de> | 2011-03-18 07:31:53 +0100 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2011-03-18 07:31:53 +0100 |
commit | 98d21df431ad55281e1abf780f8d51e3391900b2 (patch) | |
tree | 19d365c9407ce581098596ec913a9cdb9ff7dd8a /sound/drivers | |
parent | ALSA: sound/core: merge list_del()/list_add_tail() to list_move_tail() (diff) | |
download | linux-98d21df431ad55281e1abf780f8d51e3391900b2.tar.xz 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>
Diffstat (limited to 'sound/drivers')
-rw-r--r-- | sound/drivers/aloop.c | 19 |
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; } |