Commit 9984d1b5 authored by Takashi Iwai's avatar Takashi Iwai

ALSA: timer: Protect the whole snd_timer_close() with open race

In order to make the open/close more robust, widen the register_mutex
protection over the whole snd_timer_close() function.  Also, the close
procedure is slightly shuffled to be in the safer order, as well as a
few code refactoring.
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 4dff5c7b
...@@ -318,25 +318,14 @@ int snd_timer_close(struct snd_timer_instance *timeri) ...@@ -318,25 +318,14 @@ int snd_timer_close(struct snd_timer_instance *timeri)
if (snd_BUG_ON(!timeri)) if (snd_BUG_ON(!timeri))
return -ENXIO; return -ENXIO;
mutex_lock(&register_mutex);
list_del(&timeri->open_list);
/* force to stop the timer */ /* force to stop the timer */
snd_timer_stop(timeri); snd_timer_stop(timeri);
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { timer = timeri->timer;
/* wait, until the active callback is finished */ if (timer) {
spin_lock_irq(&slave_active_lock);
while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
spin_unlock_irq(&slave_active_lock);
udelay(10);
spin_lock_irq(&slave_active_lock);
}
spin_unlock_irq(&slave_active_lock);
mutex_lock(&register_mutex);
list_del(&timeri->open_list);
mutex_unlock(&register_mutex);
} else {
timer = timeri->timer;
if (snd_BUG_ON(!timer))
goto out;
/* wait, until the active callback is finished */ /* wait, until the active callback is finished */
spin_lock_irq(&timer->lock); spin_lock_irq(&timer->lock);
while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
...@@ -345,11 +334,7 @@ int snd_timer_close(struct snd_timer_instance *timeri) ...@@ -345,11 +334,7 @@ int snd_timer_close(struct snd_timer_instance *timeri)
spin_lock_irq(&timer->lock); spin_lock_irq(&timer->lock);
} }
spin_unlock_irq(&timer->lock); spin_unlock_irq(&timer->lock);
mutex_lock(&register_mutex);
list_del(&timeri->open_list);
if (list_empty(&timer->open_list_head) &&
timer->hw.close)
timer->hw.close(timer);
/* remove slave links */ /* remove slave links */
spin_lock_irq(&slave_active_lock); spin_lock_irq(&slave_active_lock);
spin_lock(&timer->lock); spin_lock(&timer->lock);
...@@ -363,18 +348,27 @@ int snd_timer_close(struct snd_timer_instance *timeri) ...@@ -363,18 +348,27 @@ int snd_timer_close(struct snd_timer_instance *timeri)
} }
spin_unlock(&timer->lock); spin_unlock(&timer->lock);
spin_unlock_irq(&slave_active_lock); spin_unlock_irq(&slave_active_lock);
/* release a card refcount for safe disconnection */
if (timer->card) /* slave doesn't need to release timer resources below */
put_device(&timer->card->card_dev); if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
mutex_unlock(&register_mutex); timer = NULL;
} }
out:
if (timeri->private_free) if (timeri->private_free)
timeri->private_free(timeri); timeri->private_free(timeri);
kfree(timeri->owner); kfree(timeri->owner);
kfree(timeri); kfree(timeri);
if (timer)
if (timer) {
if (list_empty(&timer->open_list_head) && timer->hw.close)
timer->hw.close(timer);
/* release a card refcount for safe disconnection */
if (timer->card)
put_device(&timer->card->card_dev);
module_put(timer->module); module_put(timer->module);
}
mutex_unlock(&register_mutex);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment