ALSA: ctxfi - Fix deadlock with xfi-timer
authorTakashi Iwai <tiwai@suse.de>
Mon, 15 Jun 2009 12:50:52 +0000 (14:50 +0200)
committerTakashi Iwai <tiwai@suse.de>
Mon, 15 Jun 2009 12:52:55 +0000 (14:52 +0200)
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>
sound/pci/ctxfi/cttimer.c

index 779c6c3591a5b297180eb001f85e682779cebf29..93b0aedc36d448be8ae902c6f1e2e5213934a84c 100644 (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);
 }