Commit 3a23fd04 authored by Takashi Iwai's avatar Takashi Iwai
Browse files

Merge branch 'topic/timer-fixes' into for-next



Pull yet another ALSA core timer fixes and cleanups.

Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parents 36b8defc fe1b26c9
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -43,7 +43,6 @@
#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 */

+74 −49
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@

/* internal flags */
#define SNDRV_TIMER_IFLG_PAUSED		0x00010000
#define SNDRV_TIMER_IFLG_DEAD		0x00020000

#if IS_ENABLED(CONFIG_SND_HRTIMER)
#define DEFAULT_TIMER_LIMIT 4
@@ -353,20 +354,25 @@ EXPORT_SYMBOL(snd_timer_open);
 */
static int snd_timer_close_locked(struct snd_timer_instance *timeri)
{
	struct snd_timer *timer = NULL;
	struct snd_timer *timer = timeri->timer;
	struct snd_timer_instance *slave, *tmp;

	if (timer) {
		spin_lock_irq(&timer->lock);
		timeri->flags |= SNDRV_TIMER_IFLG_DEAD;
		spin_unlock_irq(&timer->lock);
	}

	list_del(&timeri->open_list);

	/* force to stop the timer */
	snd_timer_stop(timeri);

	timer = timeri->timer;
	if (timer) {
		timer->num_instances--;
		/* wait, until the active callback is finished */
		spin_lock_irq(&timer->lock);
		while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
		while (!list_empty(&timeri->ack_list)) {
			spin_unlock_irq(&timer->lock);
			udelay(10);
			spin_lock_irq(&timer->lock);
@@ -497,6 +503,10 @@ static int snd_timer_start1(struct snd_timer_instance *timeri,
		return -EINVAL;

	spin_lock_irqsave(&timer->lock, flags);
	if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) {
		result = -EINVAL;
		goto unlock;
	}
	if (timer->card && timer->card->shutdown) {
		result = -ENODEV;
		goto unlock;
@@ -541,11 +551,16 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri,
				 bool start)
{
	unsigned long flags;
	int err;

	spin_lock_irqsave(&slave_active_lock, flags);
	if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) {
		err = -EINVAL;
		goto unlock;
	}
	if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
		spin_unlock_irqrestore(&slave_active_lock, flags);
		return -EBUSY;
		err = -EBUSY;
		goto unlock;
	}
	timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
	if (timeri->master && timeri->timer) {
@@ -556,8 +571,10 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri,
				  SNDRV_TIMER_EVENT_CONTINUE);
		spin_unlock(&timeri->timer->lock);
	}
	err = 1; /* delayed start */
 unlock:
	spin_unlock_irqrestore(&slave_active_lock, flags);
	return 1; /* delayed start */
	return err;
}

/* stop/pause a master timer */
@@ -720,41 +737,61 @@ static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_l
	timer->sticks = ticks;
}

/*
 * timer tasklet
 *
 */
static void snd_timer_tasklet(unsigned long arg)
/* call callbacks in timer ack list */
static void snd_timer_process_callbacks(struct snd_timer *timer,
					struct list_head *head)
{
	struct snd_timer *timer = (struct snd_timer *) arg;
	struct snd_timer_instance *ti;
	struct list_head *p;
	unsigned long resolution, ticks;
	unsigned long flags;

	if (timer->card && timer->card->shutdown)
		return;

	spin_lock_irqsave(&timer->lock, flags);
	/* now process all callbacks */
	while (!list_empty(&timer->sack_list_head)) {
		p = timer->sack_list_head.next;		/* get first item */
		ti = list_entry(p, struct snd_timer_instance, ack_list);

		/* remove from ack_list and make empty */
		list_del_init(p);
	while (!list_empty(head)) {
		ti = list_first_entry(head, struct snd_timer_instance,
				      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);
	}
}

/* clear pending instances from ack list */
static void snd_timer_clear_callbacks(struct snd_timer *timer,
				      struct list_head *head)
{
	unsigned long flags;

	spin_lock_irqsave(&timer->lock, flags);
	while (!list_empty(head))
		list_del_init(head->next);
	spin_unlock_irqrestore(&timer->lock, flags);
}

/*
 * timer tasklet
 *
 */
static void snd_timer_tasklet(unsigned long arg)
{
	struct snd_timer *timer = (struct snd_timer *) arg;
	unsigned long flags;

	if (timer->card && timer->card->shutdown) {
		snd_timer_clear_callbacks(timer, &timer->sack_list_head);
		return;
	}

	spin_lock_irqsave(&timer->lock, flags);
	snd_timer_process_callbacks(timer, &timer->sack_list_head);
	spin_unlock_irqrestore(&timer->lock, flags);
}

@@ -767,16 +804,18 @@ static void snd_timer_tasklet(unsigned long arg)
void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
{
	struct snd_timer_instance *ti, *ts, *tmp;
	unsigned long resolution, ticks;
	struct list_head *p, *ack_list_head;
	unsigned long resolution;
	struct list_head *ack_list_head;
	unsigned long flags;
	int use_tasklet = 0;

	if (timer == NULL)
		return;

	if (timer->card && timer->card->shutdown)
	if (timer->card && timer->card->shutdown) {
		snd_timer_clear_callbacks(timer, &timer->ack_list_head);
		return;
	}

	spin_lock_irqsave(&timer->lock, flags);

@@ -790,6 +829,8 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
	 */
	list_for_each_entry_safe(ti, tmp, &timer->active_list_head,
				 active_list) {
		if (ti->flags & SNDRV_TIMER_IFLG_DEAD)
			continue;
		if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING))
			continue;
		ti->pticks += ticks_left;
@@ -839,23 +880,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
	}

	/* now process all fast callbacks */
	while (!list_empty(&timer->ack_list_head)) {
		p = timer->ack_list_head.next;		/* get first item */
		ti = list_entry(p, struct snd_timer_instance, ack_list);

		/* remove from ack_list and make empty */
		list_del_init(p);

		ticks = ti->pticks;
		ti->pticks = 0;

		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;
	}
	snd_timer_process_callbacks(timer, &timer->ack_list_head);

	/* do we have any slow callbacks? */
	use_tasklet = !list_empty(&timer->sack_list_head);