On 2020-09-03 3:16 PM, Mark Brown wrote:
On Thu, Sep 03, 2020 at 10:31:35AM +0200, Cezary Rojewski wrote:
Some time ago negative-tests found out that behavior of soc_pcm_open has changed, quite sure this might be a regression hence my email. Till v5.6 soc_pcm_open was invoking ::shutdown() for cpu_dai in error path only if ::startup() succeeded first (label: 'out'). After addition of commit:
Please don't invent new notation that nobody else uses, it just makes your messages harder to read.
Should dai's ::shutdown() be introducing some kind of state-check from now on? - similarly to how developers deal with some of the core pcm operations e.g.: ::prepare() (as it may get invoked multiple times in a row so check is there to prevent redundancy).
If there are stateful things it's probably better to do that from a robustness point of view whatever is going on.
Or, perhaps behavior change should be reverted with ::shutdown() routine again being called only after successful ::startup()?
IIRC part of the thinking there was that we were getting the keeping track part of things wrong and sometimes missing things that should be being shut down in error paths. Anything that tries to stop extra calls would need to be very clearly robust and easily maintainable.
I'm sorry if my explanation was somewhat lackluster. In fact, thread's name is misleading too -> regression sincec v5.7, not v5.6. Comparison of code pieces found below should make it clearer:
v5.6 soc_pcm_open: https://elixir.bootlin.com/linux/v5.6.19/source/sound/soc/soc-pcm.c#L534
static int soc_pcm_open(struct snd_pcm_substream *substream) {
(...)
/* startup the audio subsystem */ ret = snd_soc_dai_startup(cpu_dai, substream); if (ret < 0) { dev_err(cpu_dai->dev, "ASoC: can't open interface %s: %d\n", cpu_dai->name, ret); goto out; }
ret = soc_pcm_components_open(substream, &component); if (ret < 0) goto component_err;
for_each_rtd_codec_dai(rtd, i, codec_dai) { ret = snd_soc_dai_startup(codec_dai, substream); if (ret < 0) { dev_err(codec_dai->dev, "ASoC: can't open codec %s: %d\n", codec_dai->name, ret); goto codec_dai_err; }
(...)
codec_dai_err: for_each_rtd_codec_dai_rollback(rtd, i, codec_dai) snd_soc_dai_shutdown(codec_dai, substream);
component_err: soc_pcm_components_close(substream, component);
snd_soc_dai_shutdown(cpu_dai, substream); out: mutex_unlock(&rtd->card->pcm_mutex);
-
Now the equivalent from newer kernel e.g. v5.8: https://elixir.bootlin.com/linux/v5.8.6/source/sound/soc/soc-pcm.c#L711
static int soc_pcm_open(struct snd_pcm_substream *substream) {
(...)
/* startup the audio subsystem */ for_each_rtd_dais(rtd, i, dai) { ret = snd_soc_dai_startup(dai, substream); if (ret < 0) { dev_err(dai->dev, "ASoC: can't open DAI %s: %d\n", dai->name, ret); goto config_err; }
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) dai->tx_mask = 0; else dai->rx_mask = 0; }
(...)
config_err: for_each_rtd_dais(rtd, i, dai) snd_soc_dai_shutdown(dai, substream);
-
Let's assume we have 10 dais. In newer kernels, if snd_soc_dai_startup() fails at i=5, error path will attempt to perform snd_soc_dai_shutdown() for all dais (all 10) regardless if respective dai was opened or not. This is a clear behavior change when compared to v5.6 where cpu_dai was cleaned-up only if it was previously started successfully. Due to usage of for_each_rtd_codec_dai_rollback macro, the same applies to codec_dais.
Czarek