alsa/0035-PCM-Avoid-busy-loop-in-snd_pcm_write_areas-with-rate.patch
Takashi Iwai 7a3b45a028 Accepting request 141190 from home:tiwai:branches:multimedia:libs
- backport fixes from upstream tree:
  0031-pcm-support-for-audio-timestamps.patch
  0032-pcm-fix-64-bit-SNDRV_PCM_IOCTL_STATUS-ABI-breakage.patch
  0033-PCM-Fix-memory-leak-for-pcm-empty-and-asym-plugins.patch
  0034-Reduce-compilation-warnings.patch
  0035-PCM-Avoid-busy-loop-in-snd_pcm_write_areas-with-rate.patch

OBS-URL: https://build.opensuse.org/request/show/141190
OBS-URL: https://build.opensuse.org/package/show/multimedia:libs/alsa?expand=0&rev=115
2012-11-13 16:37:14 +00:00

223 lines
8.3 KiB
Diff

From 3fd4ab9be0db7c7430ebd258f2717a976381715d Mon Sep 17 00:00:00 2001
From: Takashi Iwai <tiwai@suse.de>
Date: Tue, 13 Nov 2012 16:16:26 +0100
Subject: [PATCH 35/35] 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>
---
src/pcm/pcm.c | 8 ++++----
src/pcm/pcm_generic.c | 6 ++++++
src/pcm/pcm_generic.h | 4 +++-
src/pcm/pcm_hooks.c | 1 +
src/pcm/pcm_local.h | 11 +++++++++++
src/pcm/pcm_meter.c | 1 +
src/pcm/pcm_mmap_emul.c | 1 +
src/pcm/pcm_multi.c | 8 ++++++++
src/pcm/pcm_plugin.c | 1 +
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 359d295..f8b68ed 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 d56e5d3..5fc4888 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 916c6ec..d189077 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 0feb4a3..f837282 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 e7798fd..8cf7c3d 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 42a125e..e60b92d 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 811cb1c..67d2e05 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 ffb1b53..2db82c0 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 d88e117..96218a8 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 4ba8521..54a3e67 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 = {
--
1.8.0