ALSA: timer: Revert active callback sync check at close
authorTakashi Iwai <tiwai@suse.de>
Tue, 9 Apr 2019 10:25:32 +0000 (12:25 +0200)
committerTakashi Iwai <tiwai@suse.de>
Tue, 9 Apr 2019 10:29:34 +0000 (12:29 +0200)
This is essentially a revert of the commit a7588c896b05 ("ALSA: timer:
Check ack_list emptiness instead of bit flag").  The intended change
by the commit turns out to be insufficient, as snd_timer_close*()
always calls snd_timer_stop() that deletes the ack_list beforehand.

In theory, we can change the behavior of snd_timer_stop() to sync the
pending ack_list, but this will become a deadlock for the callback
like sequencer that calls again snd_timer_stop() from itself.  So,
reverting the change is a more straightforward solution.

Fixes: a7588c896b05 ("ALSA: timer: Check ack_list emptiness instead of bit flag")
Reported-by: syzbot+58813d77154713f4de15@syzkaller.appspotmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
include/sound/timer.h
sound/core/timer.c

index bcfee20..7ae226a 100644 (file)
@@ -43,6 +43,7 @@
 #define SNDRV_TIMER_IFLG_START   0x00000004
 #define SNDRV_TIMER_IFLG_AUTO    0x00000008    /* auto restart */
 #define SNDRV_TIMER_IFLG_FAST    0x00000010    /* fast callback (do not use tasklet) */
+#define SNDRV_TIMER_IFLG_CALLBACK 0x00000020   /* timer callback is active */
 #define SNDRV_TIMER_IFLG_EXCLUSIVE 0x00000040  /* exclusive owner - no more instances */
 #define SNDRV_TIMER_IFLG_EARLY_EVENT 0x00000080        /* write early event to the poll queue */
 
index bb7e90a..df52d29 100644 (file)
@@ -372,7 +372,7 @@ static int snd_timer_close_locked(struct snd_timer_instance *timeri)
                timer->num_instances--;
                /* wait, until the active callback is finished */
                spin_lock_irq(&timer->lock);
-               while (!list_empty(&timeri->ack_list)) {
+               while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
                        spin_unlock_irq(&timer->lock);
                        udelay(10);
                        spin_lock_irq(&timer->lock);
@@ -748,19 +748,20 @@ static void snd_timer_process_callbacks(struct snd_timer *timer,
                ti = list_first_entry(head, struct snd_timer_instance,
                                      ack_list);
 
+               /* remove from ack_list and make empty */
+               list_del_init(&ti->ack_list);
+
                if (!(ti->flags & SNDRV_TIMER_IFLG_DEAD)) {
                        ticks = ti->pticks;
                        ti->pticks = 0;
                        resolution = ti->resolution;
-
+                       ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
                        spin_unlock(&timer->lock);
                        if (ti->callback)
                                ti->callback(ti, resolution, ticks);
                        spin_lock(&timer->lock);
+                       ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
                }
-
-               /* remove from ack_list and make empty */
-               list_del_init(&ti->ack_list);
        }
 }