ALSA: ctxfi - Fix deadlock with xfi-timer

The PCM x-fi native update routine can cause deadlocks when the
trigger(START) is called while the stream is running.

This patch fixes the deadlock by just postponing the pcm period update
to the next possible wake-up.  Also it adds the flip of ti->running
flag (just to be sure as now).

Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2009-06-15 14:50:52 +02:00
parent 635c265f32
commit 8dca419721
1 changed files with 10 additions and 8 deletions

View File

@ -180,7 +180,7 @@ static inline unsigned int ct_xfitimer_get_wc(struct ct_timer *atimer)
*
* call this inside the lock and irq disabled
*/
static int ct_xfitimer_reprogram(struct ct_timer *atimer)
static int ct_xfitimer_reprogram(struct ct_timer *atimer, int can_update)
{
struct ct_timer_instance *ti;
unsigned int min_intr = (unsigned int)-1;
@ -216,6 +216,8 @@ static int ct_xfitimer_reprogram(struct ct_timer *atimer)
ti->frag_count = div_u64((u64)pos * CT_TIMER_FREQ +
rate - 1, rate);
}
if (ti->need_update && !can_update)
min_intr = 0; /* pending to the next irq */
if (ti->frag_count < min_intr)
min_intr = ti->frag_count;
}
@ -235,7 +237,7 @@ static void ct_xfitimer_check_period(struct ct_timer *atimer)
spin_lock_irqsave(&atimer->list_lock, flags);
list_for_each_entry(ti, &atimer->instance_head, instance_list) {
if (ti->need_update) {
if (ti->running && ti->need_update) {
ti->need_update = 0;
ti->apcm->interrupt(ti->apcm);
}
@ -252,7 +254,7 @@ static void ct_xfitimer_callback(struct ct_timer *atimer)
spin_lock_irqsave(&atimer->lock, flags);
atimer->irq_handling = 1;
do {
update = ct_xfitimer_reprogram(atimer);
update = ct_xfitimer_reprogram(atimer, 1);
spin_unlock(&atimer->lock);
if (update)
ct_xfitimer_check_period(atimer);
@ -265,6 +267,7 @@ static void ct_xfitimer_callback(struct ct_timer *atimer)
static void ct_xfitimer_prepare(struct ct_timer_instance *ti)
{
ti->frag_count = ti->substream->runtime->period_size;
ti->running = 0;
ti->need_update = 0;
}
@ -273,7 +276,6 @@ static void ct_xfitimer_prepare(struct ct_timer_instance *ti)
static void ct_xfitimer_update(struct ct_timer *atimer)
{
unsigned long flags;
int update;
spin_lock_irqsave(&atimer->lock, flags);
if (atimer->irq_handling) {
@ -284,10 +286,8 @@ static void ct_xfitimer_update(struct ct_timer *atimer)
}
ct_xfitimer_irq_stop(atimer);
update = ct_xfitimer_reprogram(atimer);
ct_xfitimer_reprogram(atimer, 0);
spin_unlock_irqrestore(&atimer->lock, flags);
if (update)
ct_xfitimer_check_period(atimer);
}
static void ct_xfitimer_start(struct ct_timer_instance *ti)
@ -298,6 +298,8 @@ static void ct_xfitimer_start(struct ct_timer_instance *ti)
spin_lock_irqsave(&atimer->lock, flags);
if (list_empty(&ti->running_list))
atimer->wc = ct_xfitimer_get_wc(atimer);
ti->running = 1;
ti->need_update = 0;
list_add(&ti->running_list, &atimer->running_head);
spin_unlock_irqrestore(&atimer->lock, flags);
ct_xfitimer_update(atimer);
@ -310,7 +312,7 @@ static void ct_xfitimer_stop(struct ct_timer_instance *ti)
spin_lock_irqsave(&atimer->lock, flags);
list_del_init(&ti->running_list);
ti->need_update = 0;
ti->running = 0;
spin_unlock_irqrestore(&atimer->lock, flags);
ct_xfitimer_update(atimer);
}