diff options
author | Takashi Iwai <tiwai@suse.de> | 2009-06-15 16:50:52 +0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-06-15 16:52:55 +0400 |
commit | 8dca419721d188bfee5f19fad45275856c619a5c (patch) | |
tree | e953048997ccb98e3275dc0438fc02f481ad52e3 /sound | |
parent | 635c265f32d8a3f73402813d6a8dd47f2a363df5 (diff) | |
download | linux-8dca419721d188bfee5f19fad45275856c619a5c.tar.xz |
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>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/pci/ctxfi/cttimer.c | 18 |
1 files changed, 10 insertions, 8 deletions
diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c index 779c6c3591a5..93b0aedc36d4 100644 --- a/sound/pci/ctxfi/cttimer.c +++ b/sound/pci/ctxfi/cttimer.c @@ -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); } |