From a514a33e6d0166938ba8513e0a2ea8a87b144613 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Fri, 2 Nov 2018 17:20:53 +0200 Subject: [PATCH] MLK-20094: ASoC: fsl: dsp: Fix crash in compress cleanup path We must find a way to no longer touch resources after they are cleand up. Now, after a stress test we get the following crash: [ 2156.863772] fsl-dsp 596e8000.dsp: xf_pool_alloc failed [ 2156.869337] Unable to handle kernel NULL pointer dereference at virtual address 00000060 [ 2157.148594] [] _raw_spin_lock+0x14/0x48 [ 2157.153995] [] xf_cmd_send_recv_complete+0x40/0xf0 [ 2157.160354] [] xf_close+0x40/0x88 [ 2157.165239] [] xaf_comp_delete+0x5c/0x70 [ 2157.170730] [] dsp_platform_compr_free+0xa0/0xe8 [ 2157.176917] [] soc_compr_free_fe+0x144/0x1a0 [ 2157.182754] [] snd_compr_free+0x64/0x98 This happens because: 1) dsp_platform_process work handler waits in a loop for messages to arrive. 2) when cplay process finishes it cleans up most of the resources. 3) when another cplay process starts it reinitializes the resources including queues for example. 4) a message will be generated and kernel will crash because dsp_platform_process uses the older queues. A solution for this is to make sure dsp_platform_process work loop is stopped at cleanup time. We use is_active state and signal dsp_platform_process handler to finish because we are on the cleanup path. While at it replace cancel_work with cancel_work sync to be sure that work handler ends before going on with the rest of the cleanup. Reviewed-by: Cosmin-Gabriel Samoila Signed-off-by: Daniel Baluta (cherry picked from commit c0f7ef8eaf096523e9ebe3acd818e5f67fdb639f) --- sound/soc/fsl/fsl_dsp.c | 1 + sound/soc/fsl/fsl_dsp_platform_compress.c | 8 +++++--- sound/soc/fsl/fsl_dsp_proxy.c | 5 +++-- sound/soc/fsl/fsl_dsp_proxy.h | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/sound/soc/fsl/fsl_dsp.c b/sound/soc/fsl/fsl_dsp.c index 9e577a93d9d1..c337a269858a 100644 --- a/sound/soc/fsl/fsl_dsp.c +++ b/sound/soc/fsl/fsl_dsp.c @@ -370,6 +370,7 @@ int fsl_dsp_open_func(struct fsl_dsp *dsp_priv, struct xf_client *client) client->global = (void *)dsp_priv; dsp_priv->proxy.is_loaded = 0; + dsp_priv->proxy.is_active = 1; pm_runtime_get_sync(dev); diff --git a/sound/soc/fsl/fsl_dsp_platform_compress.c b/sound/soc/fsl/fsl_dsp_platform_compress.c index 78cd85f3b8e8..a4018924f753 100644 --- a/sound/soc/fsl/fsl_dsp_platform_compress.c +++ b/sound/soc/fsl/fsl_dsp_platform_compress.c @@ -28,9 +28,9 @@ void dsp_platform_process(struct work_struct *w) while (1) { rmsg = xf_cmd_recv(proxy, &client->wait, &client->queue, 1); - if (IS_ERR(rmsg)) { + + if (!proxy->is_active || IS_ERR(rmsg)) return; - } if (rmsg->opcode == XF_EMPTY_THIS_BUFFER) { client->consume_bytes += rmsg->length; snd_compr_fragment_elapsed(client->cstream); @@ -101,7 +101,9 @@ static int dsp_platform_compr_free(struct snd_compr_stream *cstream) cpu_dai->driver->ops->shutdown(NULL, cpu_dai); - ret = cancel_work(&drv->client->work); + drv->client->proxy->is_active = 0; + wake_up(&drv->client->wait); + cancel_work_sync(&drv->client->work); fsl_dsp_close_func(drv->client); diff --git a/sound/soc/fsl/fsl_dsp_proxy.c b/sound/soc/fsl/fsl_dsp_proxy.c index 71b92fd1b134..665f60e05aaf 100644 --- a/sound/soc/fsl/fsl_dsp_proxy.c +++ b/sound/soc/fsl/fsl_dsp_proxy.c @@ -558,12 +558,13 @@ struct xf_message *xf_cmd_recv(struct xf_proxy *proxy, struct xf_msg_queue *queue, int wait) { - struct xf_message *m; + struct xf_message *m = NULL; int ret; /* ...wait for message reception (take lock on success) */ ret = wait_event_interruptible(*wq, - (m = xf_msg_received(proxy, queue)) != NULL || !wait); + (m = xf_msg_received(proxy, queue)) != NULL || !wait + || !proxy->is_active); if (ret) return ERR_PTR(-EINTR); diff --git a/sound/soc/fsl/fsl_dsp_proxy.h b/sound/soc/fsl/fsl_dsp_proxy.h index 440cafb9bd16..4c39c815d585 100644 --- a/sound/soc/fsl/fsl_dsp_proxy.h +++ b/sound/soc/fsl/fsl_dsp_proxy.h @@ -300,7 +300,7 @@ struct xf_proxy { struct completion cmd_complete; int is_ready; int is_loaded; - + int is_active; /* ...internal lock */ spinlock_t lock; -- 2.17.1