ALSA: firewire-lib: taskletize the snd_pcm_period_elapsed() call
authorClemens Ladisch <clemens@ladisch.de>
Sun, 13 May 2012 20:03:09 +0000 (22:03 +0200)
committerTakashi Iwai <tiwai@suse.de>
Mon, 14 May 2012 08:43:30 +0000 (10:43 +0200)
The following patch might introduce this call chain:
  PCM .pointer callback
  + fw_iso_context_flush_completions
    + packet callback
      + snd_pcm_period_elapsed
        + PCM .pointer callback
Recursive calls to the pointer callback are not possible due to the PCM
group locking, so avoid this by moving the period notification into
a separate tasklet.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/firewire/amdtp.c
sound/firewire/amdtp.h

index 87657dd7714ccccb59f4922f12a880680b7838c8..3284ee9c1ecac7b86ec33a2d0953b886e3c4d4a1 100644 (file)
@@ -31,6 +31,8 @@
 #define INTERRUPT_INTERVAL     16
 #define QUEUE_LENGTH           48
 
+static void pcm_period_tasklet(unsigned long data);
+
 /**
  * amdtp_out_stream_init - initialize an AMDTP output stream structure
  * @s: the AMDTP output stream to initialize
@@ -47,6 +49,7 @@ int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
        s->flags = flags;
        s->context = ERR_PTR(-1);
        mutex_init(&s->mutex);
+       tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s);
        s->packet_index = 0;
 
        return 0;
@@ -164,6 +167,20 @@ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
 }
 EXPORT_SYMBOL(amdtp_out_stream_set_pcm_format);
 
+/**
+ * amdtp_out_stream_pcm_prepare - prepare PCM device for running
+ * @s: the AMDTP output stream
+ *
+ * This function should be called from the PCM device's .prepare callback.
+ */
+void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s)
+{
+       tasklet_kill(&s->period_tasklet);
+       s->pcm_buffer_pointer = 0;
+       s->pcm_period_pointer = 0;
+}
+EXPORT_SYMBOL(amdtp_out_stream_pcm_prepare);
+
 static unsigned int calculate_data_blocks(struct amdtp_out_stream *s)
 {
        unsigned int phase, data_blocks;
@@ -376,11 +393,20 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
                s->pcm_period_pointer += data_blocks;
                if (s->pcm_period_pointer >= pcm->runtime->period_size) {
                        s->pcm_period_pointer -= pcm->runtime->period_size;
-                       snd_pcm_period_elapsed(pcm);
+                       tasklet_hi_schedule(&s->period_tasklet);
                }
        }
 }
 
+static void pcm_period_tasklet(unsigned long data)
+{
+       struct amdtp_out_stream *s = (void *)data;
+       struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
+
+       if (pcm)
+               snd_pcm_period_elapsed(pcm);
+}
+
 static void out_packet_callback(struct fw_iso_context *context, u32 cycle,
                                size_t header_length, void *header, void *data)
 {
@@ -532,6 +558,7 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s)
                return;
        }
 
+       tasklet_kill(&s->period_tasklet);
        fw_iso_context_stop(s->context);
        fw_iso_context_destroy(s->context);
        s->context = ERR_PTR(-1);
index 537a9cb83581b1b469b83914f39d3158413beb4f..4987f826f9f3f318dc5dd98ce4318539f0599053 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED
 #define SOUND_FIREWIRE_AMDTP_H_INCLUDED
 
+#include <linux/interrupt.h>
 #include <linux/mutex.h>
 #include <linux/spinlock.h>
 #include "packets-buffer.h"
@@ -55,6 +56,7 @@ struct amdtp_out_stream {
        struct iso_packets_buffer buffer;
 
        struct snd_pcm_substream *pcm;
+       struct tasklet_struct period_tasklet;
 
        int packet_index;
        unsigned int data_block_counter;
@@ -81,6 +83,7 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s);
 
 void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
                                     snd_pcm_format_t format);
+void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s);
 void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s);
 
 /**
@@ -122,18 +125,6 @@ static inline bool amdtp_out_streaming_error(struct amdtp_out_stream *s)
        return s->packet_index < 0;
 }
 
-/**
- * amdtp_out_stream_pcm_prepare - prepare PCM device for running
- * @s: the AMDTP output stream
- *
- * This function should be called from the PCM device's .prepare callback.
- */
-static inline void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s)
-{
-       s->pcm_buffer_pointer = 0;
-       s->pcm_period_pointer = 0;
-}
-
 /**
  * amdtp_out_stream_pcm_trigger - start/stop playback from a PCM device
  * @s: the AMDTP output stream