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
@ -542,16 +542,20 @@ struct sst_byt_stream *sst_byt_stream_new(struct sst_byt *byt, int id,
|
||||
void *data)
|
||||
{
|
||||
struct sst_byt_stream *stream;
|
||||
struct sst_dsp *sst = byt->dsp;
|
||||
unsigned long flags;
|
||||
|
||||
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
|
||||
if (stream == NULL)
|
||||
return NULL;
|
||||
|
||||
spin_lock_irqsave(&sst->spinlock, flags);
|
||||
list_add(&stream->node, &byt->stream_list);
|
||||
stream->notify_position = notify_position;
|
||||
stream->pdata = data;
|
||||
stream->byt = byt;
|
||||
stream->str_id = id;
|
||||
spin_unlock_irqrestore(&sst->spinlock, flags);
|
||||
|
||||
return stream;
|
||||
}
|
||||
@ -630,6 +634,8 @@ int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream)
|
||||
{
|
||||
u64 header;
|
||||
int ret = 0;
|
||||
struct sst_dsp *sst = byt->dsp;
|
||||
unsigned long flags;
|
||||
|
||||
if (!stream->commited)
|
||||
goto out;
|
||||
@ -644,8 +650,10 @@ int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream)
|
||||
|
||||
stream->commited = false;
|
||||
out:
|
||||
spin_lock_irqsave(&sst->spinlock, flags);
|
||||
list_del(&stream->node);
|
||||
kfree(stream);
|
||||
spin_unlock_irqrestore(&sst->spinlock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1159,11 +1159,14 @@ struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id,
|
||||
void *data)
|
||||
{
|
||||
struct sst_hsw_stream *stream;
|
||||
struct sst_dsp *sst = hsw->dsp;
|
||||
unsigned long flags;
|
||||
|
||||
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
|
||||
if (stream == NULL)
|
||||
return NULL;
|
||||
|
||||
spin_lock_irqsave(&sst->spinlock, flags);
|
||||
list_add(&stream->node, &hsw->stream_list);
|
||||
stream->notify_position = notify_position;
|
||||
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 */
|
||||
INIT_WORK(&stream->notify_work, hsw_notification_work);
|
||||
spin_unlock_irqrestore(&sst->spinlock, flags);
|
||||
|
||||
return stream;
|
||||
}
|
||||
@ -1180,6 +1184,8 @@ int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
|
||||
{
|
||||
u32 header;
|
||||
int ret = 0;
|
||||
struct sst_dsp *sst = hsw->dsp;
|
||||
unsigned long flags;
|
||||
|
||||
/* dont free DSP streams that are not 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);
|
||||
|
||||
out:
|
||||
spin_lock_irqsave(&sst->spinlock, flags);
|
||||
list_del(&stream->node);
|
||||
kfree(stream);
|
||||
spin_unlock_irqrestore(&sst->spinlock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user