ASoC: wm_adsp: Avoid calling snd_compr_stop_error from WDT expiry
authorCharles Keepax <ckeepax@opensource.cirrus.com>
Thu, 4 Apr 2019 12:56:01 +0000 (13:56 +0100)
committerMark Brown <broonie@kernel.org>
Thu, 4 Apr 2019 13:51:34 +0000 (20:51 +0700)
It is unsafe to call snd_compr_stop_error from outside of the
compressed ops. Firstly the compressed device lock needs to be held
and secondly it queues error work to issue a trigger stop which
should not happen after the stream has been freed. To avoid these
issues use the same trick used for the IRQ handling, simply send a
snd_compr_fragment_elapsed to cause user-space to wake on the poll,
then report the error when user-space issues the pointer request
after it wakes.

Fixes: a2bcbc1b9ac2f ("ASoC: wm_adsp: Shutdown any compressed streams on DSP watchdog timeout")
Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Cc: stable@kernel.org
sound/soc/codecs/wm_adsp.c

index c8c49d5b8ac957d5e7d7095f66da410fac15af87..a9298bfddd9c77db8d4a4ef57f749b0d58801f75 100644 (file)
@@ -4092,7 +4092,7 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
 
        buf = compr->buf;
 
-       if (!buf || buf->error) {
+       if (dsp->fatal_error || !buf || buf->error) {
                snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN);
                ret = -EIO;
                goto out;
@@ -4196,12 +4196,13 @@ static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target)
 static int wm_adsp_compr_read(struct wm_adsp_compr *compr,
                              char __user *buf, size_t count)
 {
+       struct wm_adsp *dsp = compr->dsp;
        int ntotal = 0;
        int nwords, nbytes;
 
        compr_dbg(compr, "Requested read of %zu bytes\n", count);
 
-       if (!compr->buf || compr->buf->error) {
+       if (dsp->fatal_error || !compr->buf || compr->buf->error) {
                snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN);
                return -EIO;
        }
@@ -4262,11 +4263,8 @@ static void wm_adsp_fatal_error(struct wm_adsp *dsp)
        dsp->fatal_error = true;
 
        list_for_each_entry(compr, &dsp->compr_list, list) {
-               if (compr->stream) {
-                       snd_compr_stop_error(compr->stream,
-                                            SNDRV_PCM_STATE_XRUN);
+               if (compr->stream)
                        snd_compr_fragment_elapsed(compr->stream);
-               }
        }
 }