Skip to content

Commit

Permalink
ALSA: ctxfi - Fix deadlock with xfi-timer
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Takashi Iwai committed Jun 15, 2009
1 parent 635c265 commit 8dca419
Showing 1 changed file with 10 additions and 8 deletions.
18 changes: 10 additions & 8 deletions sound/pci/ctxfi/cttimer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand All @@ -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);
}
Expand All @@ -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);
Expand All @@ -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;
}

Expand All @@ -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) {
Expand All @@ -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)
Expand All @@ -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);
Expand All @@ -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);
}
Expand Down

0 comments on commit 8dca419

Please sign in to comment.