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

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
...@@ -43,7 +43,6 @@ ...@@ -43,7 +43,6 @@
#define SNDRV_TIMER_IFLG_START 0x00000004 #define SNDRV_TIMER_IFLG_START 0x00000004
#define SNDRV_TIMER_IFLG_AUTO 0x00000008 /* auto restart */ #define SNDRV_TIMER_IFLG_AUTO 0x00000008 /* auto restart */
#define SNDRV_TIMER_IFLG_FAST 0x00000010 /* fast callback (do not use tasklet) */ #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_EXCLUSIVE 0x00000040 /* exclusive owner - no more instances */
#define SNDRV_TIMER_IFLG_EARLY_EVENT 0x00000080 /* write early event to the poll queue */ #define SNDRV_TIMER_IFLG_EARLY_EVENT 0x00000080 /* write early event to the poll queue */
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
/* internal flags */ /* internal flags */
#define SNDRV_TIMER_IFLG_PAUSED 0x00010000 #define SNDRV_TIMER_IFLG_PAUSED 0x00010000
#define SNDRV_TIMER_IFLG_DEAD 0x00020000
#if IS_ENABLED(CONFIG_SND_HRTIMER) #if IS_ENABLED(CONFIG_SND_HRTIMER)
#define DEFAULT_TIMER_LIMIT 4 #define DEFAULT_TIMER_LIMIT 4
...@@ -353,20 +354,25 @@ EXPORT_SYMBOL(snd_timer_open); ...@@ -353,20 +354,25 @@ EXPORT_SYMBOL(snd_timer_open);
*/ */
static int snd_timer_close_locked(struct snd_timer_instance *timeri) 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; 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); list_del(&timeri->open_list);
/* force to stop the timer */ /* force to stop the timer */
snd_timer_stop(timeri); snd_timer_stop(timeri);
timer = timeri->timer;
if (timer) { if (timer) {
timer->num_instances--; timer->num_instances--;
/* wait, until the active callback is finished */ /* wait, until the active callback is finished */
spin_lock_irq(&timer->lock); spin_lock_irq(&timer->lock);
while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { while (!list_empty(&timeri->ack_list)) {
spin_unlock_irq(&timer->lock); spin_unlock_irq(&timer->lock);
udelay(10); udelay(10);
spin_lock_irq(&timer->lock); spin_lock_irq(&timer->lock);
...@@ -497,6 +503,10 @@ static int snd_timer_start1(struct snd_timer_instance *timeri, ...@@ -497,6 +503,10 @@ static int snd_timer_start1(struct snd_timer_instance *timeri,
return -EINVAL; return -EINVAL;
spin_lock_irqsave(&timer->lock, flags); spin_lock_irqsave(&timer->lock, flags);
if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) {
result = -EINVAL;
goto unlock;
}
if (timer->card && timer->card->shutdown) { if (timer->card && timer->card->shutdown) {
result = -ENODEV; result = -ENODEV;
goto unlock; goto unlock;
...@@ -541,11 +551,16 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri, ...@@ -541,11 +551,16 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri,
bool start) bool start)
{ {
unsigned long flags; unsigned long flags;
int err;
spin_lock_irqsave(&slave_active_lock, flags); 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) { if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
spin_unlock_irqrestore(&slave_active_lock, flags); err = -EBUSY;
return -EBUSY; goto unlock;
} }
timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
if (timeri->master && timeri->timer) { if (timeri->master && timeri->timer) {
...@@ -556,8 +571,10 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri, ...@@ -556,8 +571,10 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri,
SNDRV_TIMER_EVENT_CONTINUE); SNDRV_TIMER_EVENT_CONTINUE);
spin_unlock(&timeri->timer->lock); spin_unlock(&timeri->timer->lock);
} }
err = 1; /* delayed start */
unlock:
spin_unlock_irqrestore(&slave_active_lock, flags); spin_unlock_irqrestore(&slave_active_lock, flags);
return 1; /* delayed start */ return err;
} }
/* stop/pause a master timer */ /* stop/pause a master timer */
...@@ -720,41 +737,61 @@ static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_l ...@@ -720,41 +737,61 @@ static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_l
timer->sticks = ticks; timer->sticks = ticks;
} }
/* /* call callbacks in timer ack list */
* timer tasklet static void snd_timer_process_callbacks(struct snd_timer *timer,
* struct list_head *head)
*/
static void snd_timer_tasklet(unsigned long arg)
{ {
struct snd_timer *timer = (struct snd_timer *) arg;
struct snd_timer_instance *ti; struct snd_timer_instance *ti;
struct list_head *p;
unsigned long resolution, ticks; unsigned long resolution, ticks;
unsigned long flags;
if (timer->card && timer->card->shutdown) while (!list_empty(head)) {
return; ti = list_first_entry(head, struct snd_timer_instance,
ack_list);
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);
if (!(ti->flags & SNDRV_TIMER_IFLG_DEAD)) {
ticks = ti->pticks; ticks = ti->pticks;
ti->pticks = 0; ti->pticks = 0;
resolution = ti->resolution; resolution = ti->resolution;
ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
spin_unlock(&timer->lock); spin_unlock(&timer->lock);
if (ti->callback) if (ti->callback)
ti->callback(ti, resolution, ticks); ti->callback(ti, resolution, ticks);
spin_lock(&timer->lock); 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); spin_unlock_irqrestore(&timer->lock, flags);
} }
...@@ -767,16 +804,18 @@ static void snd_timer_tasklet(unsigned long arg) ...@@ -767,16 +804,18 @@ static void snd_timer_tasklet(unsigned long arg)
void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
{ {
struct snd_timer_instance *ti, *ts, *tmp; struct snd_timer_instance *ti, *ts, *tmp;
unsigned long resolution, ticks; unsigned long resolution;
struct list_head *p, *ack_list_head; struct list_head *ack_list_head;
unsigned long flags; unsigned long flags;
int use_tasklet = 0; int use_tasklet = 0;
if (timer == NULL) if (timer == NULL)
return; return;
if (timer->card && timer->card->shutdown) if (timer->card && timer->card->shutdown) {
snd_timer_clear_callbacks(timer, &timer->ack_list_head);
return; return;
}
spin_lock_irqsave(&timer->lock, flags); spin_lock_irqsave(&timer->lock, flags);
...@@ -790,6 +829,8 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) ...@@ -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, list_for_each_entry_safe(ti, tmp, &timer->active_list_head,
active_list) { active_list) {
if (ti->flags & SNDRV_TIMER_IFLG_DEAD)
continue;
if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING)) if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING))
continue; continue;
ti->pticks += ticks_left; ti->pticks += ticks_left;
...@@ -839,23 +880,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) ...@@ -839,23 +880,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
} }
/* now process all fast callbacks */ /* now process all fast callbacks */
while (!list_empty(&timer->ack_list_head)) { snd_timer_process_callbacks(timer, &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;
}
/* do we have any slow callbacks? */ /* do we have any slow callbacks? */
use_tasklet = !list_empty(&timer->sack_list_head); use_tasklet = !list_empty(&timer->sack_list_head);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment