aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>2016-05-11 13:17:39 -0400
committerTakashi Iwai <tiwai@suse.de>2016-05-11 14:34:53 -0400
commit1dba9db0eaa64d362d9d9afb5eeaececdaef948d (patch)
treebbfeec8cca7036ef9e4639857e6a847d92b86150
parent8d879be882ba5a8bd4c2bc39bd2c336392564e13 (diff)
ALSA: firewire-lib: permit to flush queued packets only in process context for better PCM period granularity
These three commits were merged to improve PCM pointer granularity. commit 76fb87894828 ("ALSA: firewire-lib: taskletize the snd_pcm_period_elapsed() call") commit e9148dddc3c7 ("ALSA: firewire-lib: flush completed packets when reading PCM position") commit 92b862c7d685 ("ALSA: firewire-lib: optimize packet flushing") The point of them is to handle queued packets not only in software IRQ context of IR/IT contexts, but also in process context. As a result of handling packets, period tasklet is scheduled when acrossing PCM period boundary. This is to prevent recursive call of 'struct snd_pcm_ops.pointer()' in the same context. When the pointer callback is executed in the process context, it's better to avoid the second callback in the software IRQ context. The software IRQ context runs immediately after scheduled in the process context because few packets are queued yet. For the aim, 'pointer_flush' is used, however it causes a race condition between the process context and software IRQ context of IR/IT contexts. Practically, this race is not so critical because it influences process context to skip flushing queued packet and to get worse granularity of PCM pointer. The race condition is quite rare but it should be improved for stable service. The similar effect can be achieved by using 'in_interrupt()' macro. This commit obsoletes 'pointer_flush' with it. Acked-by: Clemens Ladisch <clemens@ladisch.de> Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--sound/firewire/amdtp-stream.c23
-rw-r--r--sound/firewire/amdtp-stream.h1
2 files changed, 17 insertions, 7 deletions
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
index 830a95c0b0e4..024ab7fa84c9 100644
--- a/sound/firewire/amdtp-stream.c
+++ b/sound/firewire/amdtp-stream.c
@@ -251,7 +251,6 @@ void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
251 tasklet_kill(&s->period_tasklet); 251 tasklet_kill(&s->period_tasklet);
252 s->pcm_buffer_pointer = 0; 252 s->pcm_buffer_pointer = 0;
253 s->pcm_period_pointer = 0; 253 s->pcm_period_pointer = 0;
254 s->pointer_flush = true;
255} 254}
256EXPORT_SYMBOL(amdtp_stream_pcm_prepare); 255EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
257 256
@@ -356,7 +355,6 @@ static void update_pcm_pointers(struct amdtp_stream *s,
356 s->pcm_period_pointer += frames; 355 s->pcm_period_pointer += frames;
357 if (s->pcm_period_pointer >= pcm->runtime->period_size) { 356 if (s->pcm_period_pointer >= pcm->runtime->period_size) {
358 s->pcm_period_pointer -= pcm->runtime->period_size; 357 s->pcm_period_pointer -= pcm->runtime->period_size;
359 s->pointer_flush = false;
360 tasklet_hi_schedule(&s->period_tasklet); 358 tasklet_hi_schedule(&s->period_tasklet);
361 } 359 }
362} 360}
@@ -803,11 +801,24 @@ EXPORT_SYMBOL(amdtp_stream_start);
803 */ 801 */
804unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s) 802unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s)
805{ 803{
806 /* this optimization is allowed to be racy */ 804 /*
807 if (s->pointer_flush && amdtp_stream_running(s)) 805 * This function is called in software IRQ context of period_tasklet or
806 * process context.
807 *
808 * When the software IRQ context was scheduled by software IRQ context
809 * of IR/IT contexts, queued packets were already handled. Therefore,
810 * no need to flush the queue in buffer anymore.
811 *
812 * When the process context reach here, some packets will be already
813 * queued in the buffer. These packets should be handled immediately
814 * to keep better granularity of PCM pointer.
815 *
816 * Later, the process context will sometimes schedules software IRQ
817 * context of the period_tasklet. Then, no need to flush the queue by
818 * the same reason as described for IR/IT contexts.
819 */
820 if (!in_interrupt() && amdtp_stream_running(s))
808 fw_iso_context_flush_completions(s->context); 821 fw_iso_context_flush_completions(s->context);
809 else
810 s->pointer_flush = true;
811 822
812 return ACCESS_ONCE(s->pcm_buffer_pointer); 823 return ACCESS_ONCE(s->pcm_buffer_pointer);
813} 824}
diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h
index 349c405f3d99..c1bc7fad056e 100644
--- a/sound/firewire/amdtp-stream.h
+++ b/sound/firewire/amdtp-stream.h
@@ -126,7 +126,6 @@ struct amdtp_stream {
126 struct tasklet_struct period_tasklet; 126 struct tasklet_struct period_tasklet;
127 unsigned int pcm_buffer_pointer; 127 unsigned int pcm_buffer_pointer;
128 unsigned int pcm_period_pointer; 128 unsigned int pcm_period_pointer;
129 bool pointer_flush;
130 129
131 /* To wait for first packet. */ 130 /* To wait for first packet. */
132 bool callbacked; 131 bool callbacked;