Takashi Iwai ea9d0d771f 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
2014-11-04 17:18:32 +00:00
..
2012-10-09 07:07:14 +09:00
2012-10-09 07:07:14 +09:00
2011-08-17 00:53:54 +09:00
2011-05-19 14:10:45 -07:00
2014-05-05 18:20:22 -07:00
2013-02-14 17:27:17 +00:00
2012-09-28 14:21:54 +01:00
2012-01-03 22:54:56 -05:00
2013-12-30 12:19:26 +00:00
2014-07-04 18:50:51 +01:00
2014-04-18 18:52:18 +01:00
2014-07-02 20:49:25 +01:00
2012-12-24 15:57:20 +00:00
2013-05-08 15:43:56 +02:00
2014-09-15 15:52:03 +02:00
2011-09-20 16:02:16 +01:00
2011-08-08 14:30:37 +09:00