diff options
author | Takashi Iwai <tiwai@suse.de> | 2012-11-13 16:16:26 +0100 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2012-11-13 16:16:26 +0100 |
commit | 3fd4ab9be0db7c7430ebd258f2717a976381715d (patch) | |
tree | b159671766371decf9609572dbf49b4e249b8796 | |
parent | 49dde08641f8c6b480c0d410d4fdb2160752dd9a (diff) |
PCM: Avoid busy loop in snd_pcm_write_areas() with rate plugin
snd_pcm_write_areas() tries to wait until avail >= avail_min condition
is satisfied. This doesn't work always well when a rate plugin is in
the play.
When a partial data with a smaller size than a period is written, the
rate plugin doesn't transfer the data immediately to the slave PCM,
but kept in an internal buffer and it changes only the hwptr of the
plugin. Thus, the condition "avail < avail_min" is triggered for a
wait check although the underlying slave PCM has enough room. This
results in a call of snd_pcm_wait() which returns immediately after
poll() call, and the snd_pcm_write_areas() loop continues. As a
consequence, it falls into a CPU hog.
This patch fixes that busy loop by introducing a new fast_ops to check
the availability for wait of avail_min. Then a plugin can ask the
slave PCM whether the wait is required (or possible).
A few plugins like multi plugin need a special handling. Otherwise a
generic plugin function can be used.
Reported-by: Trent Piepho <tpiepho@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r-- | src/pcm/pcm.c | 8 | ||||
-rw-r--r-- | src/pcm/pcm_generic.c | 6 | ||||
-rw-r--r-- | src/pcm/pcm_generic.h | 4 | ||||
-rw-r--r-- | src/pcm/pcm_hooks.c | 1 | ||||
-rw-r--r-- | src/pcm/pcm_local.h | 11 | ||||
-rw-r--r-- | src/pcm/pcm_meter.c | 1 | ||||
-rw-r--r-- | src/pcm/pcm_mmap_emul.c | 1 | ||||
-rw-r--r-- | src/pcm/pcm_multi.c | 8 | ||||
-rw-r--r-- | src/pcm/pcm_plugin.c | 1 | ||||
-rw-r--r-- | src/pcm/pcm_rate.c | 1 |
10 files changed, 37 insertions, 5 deletions
diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 359d2955..f8b68ed9 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -2357,7 +2357,7 @@ int snd_pcm_open_named_slave(snd_pcm_t **pcmp, const char *name, */ int snd_pcm_wait(snd_pcm_t *pcm, int timeout) { - if (snd_pcm_mmap_avail(pcm) >= pcm->avail_min) { + if (!snd_pcm_may_wait_for_avail_min(pcm, snd_pcm_mmap_avail(pcm))) { /* check more precisely */ switch (snd_pcm_state(pcm)) { case SND_PCM_STATE_XRUN: @@ -6776,14 +6776,14 @@ snd_pcm_sframes_t snd_pcm_write_areas(snd_pcm_t *pcm, const snd_pcm_channel_area goto _end; } if ((state == SND_PCM_STATE_RUNNING && - (snd_pcm_uframes_t)avail < pcm->avail_min && - size > (snd_pcm_uframes_t)avail)) { + size > (snd_pcm_uframes_t)avail && + snd_pcm_may_wait_for_avail_min(pcm, avail))) { if (pcm->mode & SND_PCM_NONBLOCK) { err = -EAGAIN; goto _end; } - err = snd_pcm_wait(pcm, -1); + err = snd_pcm_wait_nocheck(pcm, -1); if (err < 0) break; goto _again; diff --git a/src/pcm/pcm_generic.c b/src/pcm/pcm_generic.c index d56e5d3f..5fc48885 100644 --- a/src/pcm/pcm_generic.c +++ b/src/pcm/pcm_generic.c @@ -341,4 +341,10 @@ int snd_pcm_generic_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map) return snd_pcm_set_chmap(generic->slave, map); } +int snd_pcm_generic_may_wait_for_avail_min(snd_pcm_t *pcm, snd_pcm_uframes_t avail ATTRIBUTE_UNUSED) +{ + snd_pcm_generic_t *generic = pcm->private_data; + return snd_pcm_may_wait_for_avail_min(generic->slave, snd_pcm_mmap_avail(generic->slave)); +} + #endif /* DOC_HIDDEN */ diff --git a/src/pcm/pcm_generic.h b/src/pcm/pcm_generic.h index 916c6eca..d1890774 100644 --- a/src/pcm/pcm_generic.h +++ b/src/pcm/pcm_generic.h @@ -109,6 +109,8 @@ typedef struct { snd1_pcm_generic_get_chmap #define snd_pcm_generic_set_chmap \ snd1_pcm_generic_set_chmap +#define snd_pcm_generic_may_wait_for_avail_min \ + snd1_pcm_generic_may_wait_for_avail_min int snd_pcm_generic_close(snd_pcm_t *pcm); int snd_pcm_generic_nonblock(snd_pcm_t *pcm, int nonblock); @@ -158,5 +160,5 @@ int snd_pcm_generic_munmap(snd_pcm_t *pcm); snd_pcm_chmap_query_t **snd_pcm_generic_query_chmaps(snd_pcm_t *pcm); snd_pcm_chmap_t *snd_pcm_generic_get_chmap(snd_pcm_t *pcm); int snd_pcm_generic_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map); - +int snd_pcm_generic_may_wait_for_avail_min(snd_pcm_t *pcm, snd_pcm_uframes_t avail); diff --git a/src/pcm/pcm_hooks.c b/src/pcm/pcm_hooks.c index 0feb4a3a..f8372824 100644 --- a/src/pcm/pcm_hooks.c +++ b/src/pcm/pcm_hooks.c @@ -199,6 +199,7 @@ static const snd_pcm_fast_ops_t snd_pcm_hooks_fast_ops = { .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count, .poll_descriptors = snd_pcm_generic_poll_descriptors, .poll_revents = snd_pcm_generic_poll_revents, + .may_wait_for_avail_min = snd_pcm_generic_may_wait_for_avail_min, }; /** diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h index e7798fd0..8cf7c3d7 100644 --- a/src/pcm/pcm_local.h +++ b/src/pcm/pcm_local.h @@ -177,6 +177,7 @@ typedef struct { int (*poll_descriptors_count)(snd_pcm_t *pcm); int (*poll_descriptors)(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space); int (*poll_revents)(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); + int (*may_wait_for_avail_min)(snd_pcm_t *pcm, snd_pcm_uframes_t avail); } snd_pcm_fast_ops_t; struct _snd_pcm { @@ -984,3 +985,13 @@ _snd_pcm_parse_config_chmaps(snd_config_t *conf); snd_pcm_chmap_t * _snd_pcm_choose_fixed_chmap(snd_pcm_t *pcm, snd_pcm_chmap_query_t * const *maps); +/* return true if the PCM stream may wait to get avail_min space */ +static inline int snd_pcm_may_wait_for_avail_min(snd_pcm_t *pcm, snd_pcm_uframes_t avail) +{ + if (avail >= pcm->avail_min) + return 0; + if (pcm->fast_ops->may_wait_for_avail_min) + return pcm->fast_ops->may_wait_for_avail_min(pcm, avail); + return 1; +} + diff --git a/src/pcm/pcm_meter.c b/src/pcm/pcm_meter.c index 42a125e5..e60b92d9 100644 --- a/src/pcm/pcm_meter.c +++ b/src/pcm/pcm_meter.c @@ -545,6 +545,7 @@ static const snd_pcm_fast_ops_t snd_pcm_meter_fast_ops = { .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count, .poll_descriptors = snd_pcm_generic_poll_descriptors, .poll_revents = snd_pcm_generic_poll_revents, + .may_wait_for_avail_min = snd_pcm_generic_may_wait_for_avail_min, }; /** diff --git a/src/pcm/pcm_mmap_emul.c b/src/pcm/pcm_mmap_emul.c index 811cb1cb..67d2e050 100644 --- a/src/pcm/pcm_mmap_emul.c +++ b/src/pcm/pcm_mmap_emul.c @@ -400,6 +400,7 @@ static const snd_pcm_fast_ops_t snd_pcm_mmap_emul_fast_ops = { .poll_descriptors = snd_pcm_generic_poll_descriptors, .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count, .poll_revents = snd_pcm_generic_poll_revents, + .may_wait_for_avail_min = snd_pcm_generic_may_wait_for_avail_min, }; #ifndef DOC_HIDDEN diff --git a/src/pcm/pcm_multi.c b/src/pcm/pcm_multi.c index ffb1b532..2db82c0c 100644 --- a/src/pcm/pcm_multi.c +++ b/src/pcm/pcm_multi.c @@ -739,6 +739,13 @@ static int snd_pcm_multi_mmap(snd_pcm_t *pcm) return 0; } +static int snd_pcm_multi_may_wait_for_avail_min(snd_pcm_t *pcm, snd_pcm_uframes_t avail) +{ + snd_pcm_multi_t *multi = pcm->private_data; + snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm; + return snd_pcm_may_wait_for_avail_min(slave, snd_pcm_mmap_avail(slave)); +} + static snd_pcm_chmap_query_t **snd_pcm_multi_query_chmaps(snd_pcm_t *pcm) { snd_pcm_multi_t *multi = pcm->private_data; @@ -937,6 +944,7 @@ static const snd_pcm_fast_ops_t snd_pcm_multi_fast_ops = { .poll_descriptors_count = snd_pcm_multi_poll_descriptors_count, .poll_descriptors = snd_pcm_multi_poll_descriptors, .poll_revents = snd_pcm_multi_poll_revents, + .may_wait_for_avail_min = snd_pcm_multi_may_wait_for_avail_min, }; /** diff --git a/src/pcm/pcm_plugin.c b/src/pcm/pcm_plugin.c index d88e1178..96218a8d 100644 --- a/src/pcm/pcm_plugin.c +++ b/src/pcm/pcm_plugin.c @@ -570,6 +570,7 @@ const snd_pcm_fast_ops_t snd_pcm_plugin_fast_ops = { .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count, .poll_descriptors = snd_pcm_generic_poll_descriptors, .poll_revents = snd_pcm_generic_poll_revents, + .may_wait_for_avail_min = snd_pcm_generic_may_wait_for_avail_min, }; #endif diff --git a/src/pcm/pcm_rate.c b/src/pcm/pcm_rate.c index 4ba8521e..54a3e670 100644 --- a/src/pcm/pcm_rate.c +++ b/src/pcm/pcm_rate.c @@ -1234,6 +1234,7 @@ static const snd_pcm_fast_ops_t snd_pcm_rate_fast_ops = { .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count, .poll_descriptors = snd_pcm_generic_poll_descriptors, .poll_revents = snd_pcm_rate_poll_revents, + .may_wait_for_avail_min = snd_pcm_generic_may_wait_for_avail_min, }; static const snd_pcm_ops_t snd_pcm_rate_ops = { |