alsa/0018-pcm-Add-the-PCM-state-checks-to-plugins.patch
Ismail Dönmez 6117f9abfc Accepting request 442719 from home:tiwai:branches:multimedia:libs
- Backport upstream fixes (bsc#1012594):
  - A few PCM bugs have been fixed:
    * Stall of dmix and others in a wrong PCM state
    * Refactoring of PCM locking scheme
    * SHM initialization race fix
    * plug PCM memory leaks
    * Improvement of dshare/dmix delay calculation
    * Fix endless dshare draining
    * Fix semaphore discard race fix of direct plugins
  - UCM fixes and updates for DB410c and skylake-r5286
  - Mixer code cleanup not to install bogus plugin codes
  - Documentation fixes / updates
  0001-ucm-Add-ucm-files-for-DB410c-board.patch
  0002-mixer-Fix-rounding-mode-documentation.patch
  0003-pcm-Fix-shm-initialization-race-condition.patch
  0004-pcm-Better-understandable-locking-code.patch
  0005-ucm-fix-crash-when-calling-snd_use_case_geti-with-no.patch
  0006-ucm-docs-typeset-lists-of-identifiers-explicitly.patch
  0007-Update-include-sound-tlv.h-from-4.9-pre-kernel-uapi.patch
  0008-test-use-actual-information-for-TLV-operation.patch
  0009-ctl-improve-API-documentation-for-TLV-operation.patch
  0010-ctl-improve-documentation-about-TLV-related-APIs.patch
  0011-ctl-correct-documentation-about-TLV-feature.patch
  0012-conf-ucm-skylake-add-skylake-rt286-conf-files.patch
  0013-pcm_plug-Clear-plugins-on-all-error-conditions.patch
  0014-mixer-Don-t-install-smixer-modules-unless-python-is-.patch
  0015-pcm_dshare-Do-not-discard-slave-reported-delay-in-st.patch
  0016-pcm-direct-Protect-from-freeing-semaphore-when-alrea.patch
  0017-pcm-dshare-Fix-endless-playback-of-buffer.patch
  0018-pcm-Add-the-PCM-state-checks-to-plugins.patch

OBS-URL: https://build.opensuse.org/request/show/442719
OBS-URL: https://build.opensuse.org/package/show/multimedia:libs/alsa?expand=0&rev=202
2016-11-29 18:09:28 +00:00

221 lines
7.1 KiB
Diff

From ea74ebbe4db81aeaedcff11bc5eb6ce139db1118 Mon Sep 17 00:00:00 2001
From: Takashi Iwai <tiwai@suse.de>
Date: Tue, 29 Nov 2016 14:31:20 +0100
Subject: [PATCH] pcm: Add the PCM state checks to plugins
I noticed that some plugin codes have no proper PCM state checks and
it results in expected outcomes. For example, when snd_pcm_drain() is
called for a dmix PCM after calling snd_pcm_drop(), it stalls
unexpectedly. It's just because its drain callback doesn't expect the
SND_PCM_SETUP state.
We can fix such a bug in each place one by one, but a safer way would
be to filter out all such cases commonly in the PCM API functions
themselves. This patch adds the PCM state sanity checks to major API
functions so that they return -EBADFD when called in the unexpected
PCM states.
As well as for the thread-safety extension, it'd be a question of the
performance; again at this time, the hw PCM is considered as an
exception, and it has pcm->own_state_check flag set, which means that
the common PCM state checks are skipped.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
src/pcm/pcm.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
src/pcm/pcm_hw.c | 1 +
src/pcm/pcm_local.h | 1 +
3 files changed, 50 insertions(+)
diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c
index cd87bc759ded..f2ca02b55973 100644
--- a/src/pcm/pcm.c
+++ b/src/pcm/pcm.c
@@ -655,6 +655,23 @@ playback devices.
#include <limits.h>
#include "pcm_local.h"
+#ifndef DOC_HIDDEN
+#define P_STATE(x) (1U << SND_PCM_STATE_ ## x)
+#define P_STATE_RUNNABLE (P_STATE(PREPARED) | \
+ P_STATE(RUNNING) | \
+ P_STATE(XRUN) | \
+ P_STATE(PAUSED) | \
+ P_STATE(DRAINING))
+
+/* check whether the PCM is in the unexpected state */
+static int bad_pcm_state(snd_pcm_t *pcm, unsigned int supported_states)
+{
+ if (pcm->own_state_check)
+ return 0; /* don't care, the plugin checks by itself */
+ return !(supported_states & (1U << snd_pcm_state(pcm)));
+}
+#endif
+
/**
* \brief get identifier of PCM handle
* \param pcm PCM handle
@@ -1120,6 +1137,8 @@ int snd_pcm_prepare(snd_pcm_t *pcm)
SNDMSG("PCM not set up");
return -EIO;
}
+ if (bad_pcm_state(pcm, ~P_STATE(DISCONNECTED)))
+ return -EBADFD;
snd_pcm_lock(pcm);
err = pcm->fast_ops->prepare(pcm->fast_op_arg);
snd_pcm_unlock(pcm);
@@ -1166,6 +1185,8 @@ int snd_pcm_start(snd_pcm_t *pcm)
SNDMSG("PCM not set up");
return -EIO;
}
+ if (bad_pcm_state(pcm, P_STATE(PREPARED)))
+ return -EBADFD;
snd_pcm_lock(pcm);
err = __snd_pcm_start(pcm);
snd_pcm_unlock(pcm);
@@ -1194,6 +1215,9 @@ int snd_pcm_drop(snd_pcm_t *pcm)
SNDMSG("PCM not set up");
return -EIO;
}
+ if (bad_pcm_state(pcm, P_STATE_RUNNABLE | P_STATE(SETUP) |
+ P_STATE(SUSPENDED)))
+ return -EBADFD;
snd_pcm_lock(pcm);
err = pcm->fast_ops->drop(pcm->fast_op_arg);
snd_pcm_unlock(pcm);
@@ -1222,6 +1246,8 @@ int snd_pcm_drain(snd_pcm_t *pcm)
SNDMSG("PCM not set up");
return -EIO;
}
+ if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
+ return -EBADFD;
/* lock handled in the callback */
return pcm->fast_ops->drain(pcm->fast_op_arg);
}
@@ -1247,6 +1273,8 @@ int snd_pcm_pause(snd_pcm_t *pcm, int enable)
SNDMSG("PCM not set up");
return -EIO;
}
+ if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
+ return -EBADFD;
snd_pcm_lock(pcm);
err = pcm->fast_ops->pause(pcm->fast_op_arg, enable);
snd_pcm_unlock(pcm);
@@ -1273,6 +1301,8 @@ snd_pcm_sframes_t snd_pcm_rewindable(snd_pcm_t *pcm)
SNDMSG("PCM not set up");
return -EIO;
}
+ if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
+ return -EBADFD;
snd_pcm_lock(pcm);
result = pcm->fast_ops->rewindable(pcm->fast_op_arg);
snd_pcm_unlock(pcm);
@@ -1299,6 +1329,8 @@ snd_pcm_sframes_t snd_pcm_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
}
if (frames == 0)
return 0;
+ if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
+ return -EBADFD;
snd_pcm_lock(pcm);
result = pcm->fast_ops->rewind(pcm->fast_op_arg, frames);
snd_pcm_unlock(pcm);
@@ -1325,6 +1357,8 @@ snd_pcm_sframes_t snd_pcm_forwardable(snd_pcm_t *pcm)
SNDMSG("PCM not set up");
return -EIO;
}
+ if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
+ return -EBADFD;
snd_pcm_lock(pcm);
result = pcm->fast_ops->forwardable(pcm->fast_op_arg);
snd_pcm_unlock(pcm);
@@ -1355,6 +1389,8 @@ snd_pcm_sframes_t snd_pcm_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
}
if (frames == 0)
return 0;
+ if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
+ return -EBADFD;
snd_pcm_lock(pcm);
result = pcm->fast_ops->forward(pcm->fast_op_arg, frames);
snd_pcm_unlock(pcm);
@@ -1393,6 +1429,8 @@ snd_pcm_sframes_t snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_ufr
SNDMSG("invalid access type %s", snd_pcm_access_name(pcm->access));
return -EINVAL;
}
+ if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
+ return -EBADFD;
return _snd_pcm_writei(pcm, buffer, size);
}
@@ -1427,6 +1465,8 @@ snd_pcm_sframes_t snd_pcm_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t
SNDMSG("invalid access type %s", snd_pcm_access_name(pcm->access));
return -EINVAL;
}
+ if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
+ return -EBADFD;
return _snd_pcm_writen(pcm, bufs, size);
}
@@ -1461,6 +1501,8 @@ snd_pcm_sframes_t snd_pcm_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t
SNDMSG("invalid access type %s", snd_pcm_access_name(pcm->access));
return -EINVAL;
}
+ if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
+ return -EBADFD;
return _snd_pcm_readi(pcm, buffer, size);
}
@@ -1495,6 +1537,8 @@ snd_pcm_sframes_t snd_pcm_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t s
SNDMSG("invalid access type %s", snd_pcm_access_name(pcm->access));
return -EINVAL;
}
+ if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
+ return -EBADFD;
return _snd_pcm_readn(pcm, bufs, size);
}
@@ -6938,6 +6982,8 @@ int snd_pcm_mmap_begin(snd_pcm_t *pcm,
{
int err;
+ if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
+ return -EBADFD;
snd_pcm_lock(pcm);
err = __snd_pcm_mmap_begin(pcm, areas, offset, frames);
snd_pcm_unlock(pcm);
@@ -7033,6 +7079,8 @@ snd_pcm_sframes_t snd_pcm_mmap_commit(snd_pcm_t *pcm,
{
snd_pcm_sframes_t result;
+ if (bad_pcm_state(pcm, P_STATE_RUNNABLE))
+ return -EBADFD;
snd_pcm_lock(pcm);
result = __snd_pcm_mmap_commit(pcm, offset, frames);
snd_pcm_unlock(pcm);
diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c
index 56e88b6bf6c0..30cd5d0f6503 100644
--- a/src/pcm/pcm_hw.c
+++ b/src/pcm/pcm_hw.c
@@ -1516,6 +1516,7 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name,
#ifdef THREAD_SAFE_API
pcm->need_lock = 0; /* hw plugin is thread-safe */
#endif
+ pcm->own_state_check = 1; /* skip the common state check */
ret = snd_pcm_hw_mmap_status(pcm);
if (ret < 0) {
diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h
index bba2f15ac463..32e6dcdf6fcd 100644
--- a/src/pcm/pcm_local.h
+++ b/src/pcm/pcm_local.h
@@ -234,6 +234,7 @@ struct _snd_pcm {
* use the mmaped buffer of the slave
*/
unsigned int donot_close: 1; /* don't close this PCM */
+ unsigned int own_state_check:1; /* plugin has own PCM state check */
snd_pcm_channel_info_t *mmap_channels;
snd_pcm_channel_area_t *running_areas;
snd_pcm_channel_area_t *stopped_areas;
--
2.10.2