ASoC: Intel: Fix audio crash due to race condition in stream deletion
There is a race between sst_byt_stream_free() and sst_byt_get_stream() if sst_byt_get_stream() called from sst_byt_irq_thread() context is accessing the byt->stream_list while a stream is deleted from the list. A stream is added to byt->stream_list in sst_byt_stream_new() and deleted in sst_byt_stream_free(). sst_byt_get_stream() is always protected by sst->spinlock, but the stream addition and deletion are not protected. The patch adds spinlock to both stream addition and deletion. [Jarkko: Same fix added to sst-haswell-ipc.c too] Signed-off-by: Wenkai Du <wenkai.du@intel.com> Signed-off-by: Jarkko Nikula <jarkko.nikula@linux.intel.com> Signed-off-by: Mark Brown <broonie@linaro.org>
This commit is contained in:
parent
95e9ee92e2
commit
d132cb0a16
2 changed files with 16 additions and 0 deletions
|
@ -542,16 +542,20 @@ struct sst_byt_stream *sst_byt_stream_new(struct sst_byt *byt, int id,
|
||||||
void *data)
|
void *data)
|
||||||
{
|
{
|
||||||
struct sst_byt_stream *stream;
|
struct sst_byt_stream *stream;
|
||||||
|
struct sst_dsp *sst = byt->dsp;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
|
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
|
||||||
if (stream == NULL)
|
if (stream == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&sst->spinlock, flags);
|
||||||
list_add(&stream->node, &byt->stream_list);
|
list_add(&stream->node, &byt->stream_list);
|
||||||
stream->notify_position = notify_position;
|
stream->notify_position = notify_position;
|
||||||
stream->pdata = data;
|
stream->pdata = data;
|
||||||
stream->byt = byt;
|
stream->byt = byt;
|
||||||
stream->str_id = id;
|
stream->str_id = id;
|
||||||
|
spin_unlock_irqrestore(&sst->spinlock, flags);
|
||||||
|
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
@ -630,6 +634,8 @@ int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream)
|
||||||
{
|
{
|
||||||
u64 header;
|
u64 header;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
struct sst_dsp *sst = byt->dsp;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
if (!stream->commited)
|
if (!stream->commited)
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -644,8 +650,10 @@ int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream)
|
||||||
|
|
||||||
stream->commited = false;
|
stream->commited = false;
|
||||||
out:
|
out:
|
||||||
|
spin_lock_irqsave(&sst->spinlock, flags);
|
||||||
list_del(&stream->node);
|
list_del(&stream->node);
|
||||||
kfree(stream);
|
kfree(stream);
|
||||||
|
spin_unlock_irqrestore(&sst->spinlock, flags);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1159,11 +1159,14 @@ struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id,
|
||||||
void *data)
|
void *data)
|
||||||
{
|
{
|
||||||
struct sst_hsw_stream *stream;
|
struct sst_hsw_stream *stream;
|
||||||
|
struct sst_dsp *sst = hsw->dsp;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
|
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
|
||||||
if (stream == NULL)
|
if (stream == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&sst->spinlock, flags);
|
||||||
list_add(&stream->node, &hsw->stream_list);
|
list_add(&stream->node, &hsw->stream_list);
|
||||||
stream->notify_position = notify_position;
|
stream->notify_position = notify_position;
|
||||||
stream->pdata = data;
|
stream->pdata = data;
|
||||||
|
@ -1172,6 +1175,7 @@ struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id,
|
||||||
|
|
||||||
/* work to process notification messages */
|
/* work to process notification messages */
|
||||||
INIT_WORK(&stream->notify_work, hsw_notification_work);
|
INIT_WORK(&stream->notify_work, hsw_notification_work);
|
||||||
|
spin_unlock_irqrestore(&sst->spinlock, flags);
|
||||||
|
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
@ -1180,6 +1184,8 @@ int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
|
||||||
{
|
{
|
||||||
u32 header;
|
u32 header;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
struct sst_dsp *sst = hsw->dsp;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
/* dont free DSP streams that are not commited */
|
/* dont free DSP streams that are not commited */
|
||||||
if (!stream->commited)
|
if (!stream->commited)
|
||||||
|
@ -1201,8 +1207,10 @@ int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
|
||||||
trace_hsw_stream_free_req(stream, &stream->free_req);
|
trace_hsw_stream_free_req(stream, &stream->free_req);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
spin_lock_irqsave(&sst->spinlock, flags);
|
||||||
list_del(&stream->node);
|
list_del(&stream->node);
|
||||||
kfree(stream);
|
kfree(stream);
|
||||||
|
spin_unlock_irqrestore(&sst->spinlock, flags);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue