Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+21 −27
Original line number Diff line number Diff line
@@ -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;
	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;
}