diff options
| author | Takashi Iwai <tiwai@suse.de> | 2014-11-04 10:52:28 -0500 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2014-11-04 12:18:32 -0500 |
| commit | ea9d0d771fcd32cd56070819749477d511ec9117 (patch) | |
| tree | 6471b62340c799e9604b92fdabdd3c0a97671182 | |
| parent | f114040e3ea6e07372334ade75d1ee0775c355e1 (diff) | |
ASoC: dpcm: Fix race between FE/BE updates and trigger
DPCM can update the FE/BE connection states totally asynchronously
from the FE's PCM state. Most of FE/BE state changes are protected by
mutex, so that they won't race, but there are still some actions that
are uncovered. For example, suppose to switch a BE while a FE's
stream is running. This would call soc_dpcm_runtime_update(), which
sets FE's runtime_update flag, then sets up and starts BEs, and clears
FE's runtime_update flag again.
When a device emits XRUN during this operation, the PCM core triggers
snd_pcm_stop(XRUN). Since the trigger action is an atomic ops, this
isn't blocked by the mutex, thus it kicks off DPCM's trigger action.
It eventually updates and clears FE's runtime_update flag while
soc_dpcm_runtime_update() is running concurrently, and it results in
confusion.
Usually, for avoiding such a race, we take a lock. There is a PCM
stream lock for that purpose. However, as already mentioned, the
trigger action is atomic, and we can't take the lock for the whole
soc_dpcm_runtime_update() or other operations that include the lengthy
jobs like hw_params or prepare.
This patch provides an alternative solution. This adds a way to defer
the conflicting trigger callback to be executed at the end of FE/BE
state changes. For doing it, two things are introduced:
- Each runtime_update state change of FEs is protected via PCM stream
lock.
- The FE's trigger callback checks the runtime_update flag. If it's
not set, the trigger action is executed there. If set, mark the
pending trigger action and returns immediately.
- At the exit of runtime_update state change, it checks whether the
pending trigger is present. If yes, it executes the trigger action
at this point.
Reported-and-tested-by: Qiao Zhou <zhouqiao@marvell.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Acked-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Cc: stable@vger.kernel.org
| -rw-r--r-- | include/sound/soc-dpcm.h | 2 | ||||
| -rw-r--r-- | sound/soc/soc-pcm.c | 72 |
2 files changed, 58 insertions, 16 deletions
diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index 2883a7a6f9f3..98f2ade0266e 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h | |||
| @@ -102,6 +102,8 @@ struct snd_soc_dpcm_runtime { | |||
| 102 | /* state and update */ | 102 | /* state and update */ |
| 103 | enum snd_soc_dpcm_update runtime_update; | 103 | enum snd_soc_dpcm_update runtime_update; |
| 104 | enum snd_soc_dpcm_state state; | 104 | enum snd_soc_dpcm_state state; |
| 105 | |||
| 106 | int trigger_pending; /* trigger cmd + 1 if pending, 0 if not */ | ||
| 105 | }; | 107 | }; |
| 106 | 108 | ||
| 107 | /* can this BE stop and free */ | 109 | /* can this BE stop and free */ |
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 002311afdeaa..57277dd79e11 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c | |||
| @@ -1522,13 +1522,36 @@ static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream) | |||
| 1522 | dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture); | 1522 | dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture); |
| 1523 | } | 1523 | } |
| 1524 | 1524 | ||
| 1525 | static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd); | ||
| 1526 | |||
| 1527 | /* Set FE's runtime_update state; the state is protected via PCM stream lock | ||
| 1528 | * for avoiding the race with trigger callback. | ||
| 1529 | * If the state is unset and a trigger is pending while the previous operation, | ||
| 1530 | * process the pending trigger action here. | ||
| 1531 | */ | ||
| 1532 | static void dpcm_set_fe_update_state(struct snd_soc_pcm_runtime *fe, | ||
| 1533 | int stream, enum snd_soc_dpcm_update state) | ||
| 1534 | { | ||
| 1535 | struct snd_pcm_substream *substream = | ||
| 1536 | snd_soc_dpcm_get_substream(fe, stream); | ||
| 1537 | |||
| 1538 | snd_pcm_stream_lock_irq(substream); | ||
| 1539 | if (state == SND_SOC_DPCM_UPDATE_NO && fe->dpcm[stream].trigger_pending) { | ||
| 1540 | dpcm_fe_dai_do_trigger(substream, | ||
| 1541 | fe->dpcm[stream].trigger_pending - 1); | ||
| 1542 | fe->dpcm[stream].trigger_pending = 0; | ||
| 1543 | } | ||
| 1544 | fe->dpcm[stream].runtime_update = state; | ||
| 1545 | snd_pcm_stream_unlock_irq(substream); | ||
| 1546 | } | ||
| 1547 | |||
| 1525 | static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) | 1548 | static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) |
| 1526 | { | 1549 | { |
| 1527 | struct snd_soc_pcm_runtime *fe = fe_substream->private_data; | 1550 | struct snd_soc_pcm_runtime *fe = fe_substream->private_data; |
| 1528 | struct snd_pcm_runtime *runtime = fe_substream->runtime; | 1551 | struct snd_pcm_runtime *runtime = fe_substream->runtime; |
| 1529 | int stream = fe_substream->stream, ret = 0; | 1552 | int stream = fe_substream->stream, ret = 0; |
| 1530 | 1553 | ||
| 1531 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; | 1554 | dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); |
| 1532 | 1555 | ||
| 1533 | ret = dpcm_be_dai_startup(fe, fe_substream->stream); | 1556 | ret = dpcm_be_dai_startup(fe, fe_substream->stream); |
| 1534 | if (ret < 0) { | 1557 | if (ret < 0) { |
| @@ -1550,13 +1573,13 @@ static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) | |||
| 1550 | dpcm_set_fe_runtime(fe_substream); | 1573 | dpcm_set_fe_runtime(fe_substream); |
| 1551 | snd_pcm_limit_hw_rates(runtime); | 1574 | snd_pcm_limit_hw_rates(runtime); |
| 1552 | 1575 | ||
| 1553 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; | 1576 | dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); |
| 1554 | return 0; | 1577 | return 0; |
| 1555 | 1578 | ||
| 1556 | unwind: | 1579 | unwind: |
| 1557 | dpcm_be_dai_startup_unwind(fe, fe_substream->stream); | 1580 | dpcm_be_dai_startup_unwind(fe, fe_substream->stream); |
| 1558 | be_err: | 1581 | be_err: |
| 1559 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; | 1582 | dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); |
| 1560 | return ret; | 1583 | return ret; |
| 1561 | } | 1584 | } |
| 1562 | 1585 | ||
| @@ -1603,7 +1626,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) | |||
| 1603 | struct snd_soc_pcm_runtime *fe = substream->private_data; | 1626 | struct snd_soc_pcm_runtime *fe = substream->private_data; |
| 1604 | int stream = substream->stream; | 1627 | int stream = substream->stream; |
| 1605 | 1628 | ||
| 1606 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; | 1629 | dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); |
| 1607 | 1630 | ||
| 1608 | /* shutdown the BEs */ | 1631 | /* shutdown the BEs */ |
| 1609 | dpcm_be_dai_shutdown(fe, substream->stream); | 1632 | dpcm_be_dai_shutdown(fe, substream->stream); |
| @@ -1617,7 +1640,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) | |||
| 1617 | dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); | 1640 | dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); |
| 1618 | 1641 | ||
| 1619 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; | 1642 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; |
| 1620 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; | 1643 | dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); |
| 1621 | return 0; | 1644 | return 0; |
| 1622 | } | 1645 | } |
| 1623 | 1646 | ||
| @@ -1665,7 +1688,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) | |||
| 1665 | int err, stream = substream->stream; | 1688 | int err, stream = substream->stream; |
| 1666 | 1689 | ||
| 1667 | mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); | 1690 | mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); |
| 1668 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; | 1691 | dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); |
| 1669 | 1692 | ||
| 1670 | dev_dbg(fe->dev, "ASoC: hw_free FE %s\n", fe->dai_link->name); | 1693 | dev_dbg(fe->dev, "ASoC: hw_free FE %s\n", fe->dai_link->name); |
| 1671 | 1694 | ||
| @@ -1680,7 +1703,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) | |||
| 1680 | err = dpcm_be_dai_hw_free(fe, stream); | 1703 | err = dpcm_be_dai_hw_free(fe, stream); |
| 1681 | 1704 | ||
| 1682 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; | 1705 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; |
| 1683 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; | 1706 | dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); |
| 1684 | 1707 | ||
| 1685 | mutex_unlock(&fe->card->mutex); | 1708 | mutex_unlock(&fe->card->mutex); |
| 1686 | return 0; | 1709 | return 0; |
| @@ -1773,7 +1796,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, | |||
| 1773 | int ret, stream = substream->stream; | 1796 | int ret, stream = substream->stream; |
| 1774 | 1797 | ||
| 1775 | mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); | 1798 | mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); |
| 1776 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; | 1799 | dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); |
| 1777 | 1800 | ||
| 1778 | memcpy(&fe->dpcm[substream->stream].hw_params, params, | 1801 | memcpy(&fe->dpcm[substream->stream].hw_params, params, |
| 1779 | sizeof(struct snd_pcm_hw_params)); | 1802 | sizeof(struct snd_pcm_hw_params)); |
| @@ -1796,7 +1819,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, | |||
| 1796 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS; | 1819 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS; |
| 1797 | 1820 | ||
| 1798 | out: | 1821 | out: |
| 1799 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; | 1822 | dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); |
| 1800 | mutex_unlock(&fe->card->mutex); | 1823 | mutex_unlock(&fe->card->mutex); |
| 1801 | return ret; | 1824 | return ret; |
| 1802 | } | 1825 | } |
| @@ -1910,7 +1933,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, | |||
| 1910 | } | 1933 | } |
| 1911 | EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger); | 1934 | EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger); |
| 1912 | 1935 | ||
| 1913 | static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd) | 1936 | static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd) |
| 1914 | { | 1937 | { |
| 1915 | struct snd_soc_pcm_runtime *fe = substream->private_data; | 1938 | struct snd_soc_pcm_runtime *fe = substream->private_data; |
| 1916 | int stream = substream->stream, ret; | 1939 | int stream = substream->stream, ret; |
| @@ -1984,6 +2007,23 @@ out: | |||
| 1984 | return ret; | 2007 | return ret; |
| 1985 | } | 2008 | } |
| 1986 | 2009 | ||
| 2010 | static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd) | ||
| 2011 | { | ||
| 2012 | struct snd_soc_pcm_runtime *fe = substream->private_data; | ||
| 2013 | int stream = substream->stream; | ||
| 2014 | |||
| 2015 | /* if FE's runtime_update is already set, we're in race; | ||
| 2016 | * process this trigger later at exit | ||
| 2017 | */ | ||
| 2018 | if (fe->dpcm[stream].runtime_update != SND_SOC_DPCM_UPDATE_NO) { | ||
| 2019 | fe->dpcm[stream].trigger_pending = cmd + 1; | ||
| 2020 | return 0; /* delayed, assuming it's successful */ | ||
| 2021 | } | ||
| 2022 | |||
| 2023 | /* we're alone, let's trigger */ | ||
| 2024 | return dpcm_fe_dai_do_trigger(substream, cmd); | ||
| 2025 | } | ||
| 2026 | |||
| 1987 | int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) | 2027 | int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) |
| 1988 | { | 2028 | { |
| 1989 | struct snd_soc_dpcm *dpcm; | 2029 | struct snd_soc_dpcm *dpcm; |
| @@ -2027,7 +2067,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) | |||
| 2027 | 2067 | ||
| 2028 | dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name); | 2068 | dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name); |
| 2029 | 2069 | ||
| 2030 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; | 2070 | dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); |
| 2031 | 2071 | ||
| 2032 | /* there is no point preparing this FE if there are no BEs */ | 2072 | /* there is no point preparing this FE if there are no BEs */ |
| 2033 | if (list_empty(&fe->dpcm[stream].be_clients)) { | 2073 | if (list_empty(&fe->dpcm[stream].be_clients)) { |
| @@ -2054,7 +2094,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) | |||
| 2054 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; | 2094 | fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; |
| 2055 | 2095 | ||
| 2056 | out: | 2096 | out: |
| 2057 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; | 2097 | dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); |
| 2058 | mutex_unlock(&fe->card->mutex); | 2098 | mutex_unlock(&fe->card->mutex); |
| 2059 | 2099 | ||
| 2060 | return ret; | 2100 | return ret; |
| @@ -2201,11 +2241,11 @@ static int dpcm_run_new_update(struct snd_soc_pcm_runtime *fe, int stream) | |||
| 2201 | { | 2241 | { |
| 2202 | int ret; | 2242 | int ret; |
| 2203 | 2243 | ||
| 2204 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE; | 2244 | dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE); |
| 2205 | ret = dpcm_run_update_startup(fe, stream); | 2245 | ret = dpcm_run_update_startup(fe, stream); |
| 2206 | if (ret < 0) | 2246 | if (ret < 0) |
| 2207 | dev_err(fe->dev, "ASoC: failed to startup some BEs\n"); | 2247 | dev_err(fe->dev, "ASoC: failed to startup some BEs\n"); |
| 2208 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; | 2248 | dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); |
| 2209 | 2249 | ||
| 2210 | return ret; | 2250 | return ret; |
| 2211 | } | 2251 | } |
| @@ -2214,11 +2254,11 @@ static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream) | |||
| 2214 | { | 2254 | { |
| 2215 | int ret; | 2255 | int ret; |
| 2216 | 2256 | ||
| 2217 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE; | 2257 | dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE); |
| 2218 | ret = dpcm_run_update_shutdown(fe, stream); | 2258 | ret = dpcm_run_update_shutdown(fe, stream); |
| 2219 | if (ret < 0) | 2259 | if (ret < 0) |
| 2220 | dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n"); | 2260 | dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n"); |
| 2221 | fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; | 2261 | dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); |
| 2222 | 2262 | ||
| 2223 | return ret; | 2263 | return ret; |
| 2224 | } | 2264 | } |
