diff mbox series

ALSA: timer: Revert active callback sync check at close

Message ID 20190409103113.883-1-tiwai@suse.de (mailing list archive)
State New, archived
Headers show
Series ALSA: timer: Revert active callback sync check at close | expand

Commit Message

Takashi Iwai April 9, 2019, 10:31 a.m. UTC
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 |  1 +
 sound/core/timer.c    | 11 ++++++-----
 2 files changed, 7 insertions(+), 5 deletions(-)
diff mbox series

Patch

diff --git a/include/sound/timer.h b/include/sound/timer.h
index bcfee20ea226..7ae226ab6990 100644
--- a/include/sound/timer.h
+++ b/include/sound/timer.h
@@ -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 */
 
diff --git a/sound/core/timer.c b/sound/core/timer.c
index bb7e90ab90f8..df52d2960179 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -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);
 	}
 }