ALSA: timer: Protect the whole snd_timer_close() with open race
authorTakashi Iwai <tiwai@suse.de>
Wed, 10 Feb 2016 10:53:30 +0000 (11:53 +0100)
committerTakashi Iwai <tiwai@suse.de>
Wed, 10 Feb 2016 11:56:07 +0000 (12:56 +0100)
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: Takashi Iwai <tiwai@suse.de>
sound/core/timer.c

index dca817fc78941b5f9109e8117b3fc5adb621562e..b572a9bc31ad4957985d04879ffeaf3476f38a4c 100644 (file)
@@ -318,25 +318,14 @@ int snd_timer_close(struct snd_timer_instance *timeri)
        if (snd_BUG_ON(!timeri))
                return -ENXIO;
 
+       mutex_lock(&register_mutex);
+       list_del(&timeri->open_list);
+
        /* force to stop the timer */
        snd_timer_stop(timeri);
 
-       if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
-               /* wait, until the active callback is finished */
-               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;
+       timer = timeri->timer;
+       if (timer) {
                /* wait, until the active callback is finished */
                spin_lock_irq(&timer->lock);
                while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
@@ -345,11 +334,7 @@ int snd_timer_close(struct snd_timer_instance *timeri)
                        spin_lock_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 */
                spin_lock_irq(&slave_active_lock);
                spin_lock(&timer->lock);
@@ -363,18 +348,27 @@ int snd_timer_close(struct snd_timer_instance *timeri)
                }
                spin_unlock(&timer->lock);
                spin_unlock_irq(&slave_active_lock);
-               /* release a card refcount for safe disconnection */
-               if (timer->card)
-                       put_device(&timer->card->card_dev);
-               mutex_unlock(&register_mutex);
+
+               /* slave doesn't need to release timer resources below */
+               if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+                       timer = NULL;
        }
- out:
+
        if (timeri->private_free)
                timeri->private_free(timeri);
        kfree(timeri->owner);
        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);
+       }
+
+       mutex_unlock(&register_mutex);
        return 0;
 }