aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorWenkai Du <wenkai.du@intel.com>2014-04-23 06:29:30 -0400
committerMark Brown <broonie@linaro.org>2014-04-23 07:11:20 -0400
commitd132cb0a162fa55c82e06b771fcaa871d30c9398 (patch)
tree73bb32ad7e94188caf7ecbc992a3eb6b12eefdba /sound
parent95e9ee92e20162681e9f65c25962e0606db9d149 (diff)
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>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/intel/sst-baytrail-ipc.c8
-rw-r--r--sound/soc/intel/sst-haswell-ipc.c8
2 files changed, 16 insertions, 0 deletions
diff --git a/sound/soc/intel/sst-baytrail-ipc.c b/sound/soc/intel/sst-baytrail-ipc.c
index d0eaeee21be4..0d31dbbf4806 100644
--- a/sound/soc/intel/sst-baytrail-ipc.c
+++ b/sound/soc/intel/sst-baytrail-ipc.c
@@ -542,16 +542,20 @@ struct sst_byt_stream *sst_byt_stream_new(struct sst_byt *byt, int id,
542 void *data) 542 void *data)
543{ 543{
544 struct sst_byt_stream *stream; 544 struct sst_byt_stream *stream;
545 struct sst_dsp *sst = byt->dsp;
546 unsigned long flags;
545 547
546 stream = kzalloc(sizeof(*stream), GFP_KERNEL); 548 stream = kzalloc(sizeof(*stream), GFP_KERNEL);
547 if (stream == NULL) 549 if (stream == NULL)
548 return NULL; 550 return NULL;
549 551
552 spin_lock_irqsave(&sst->spinlock, flags);
550 list_add(&stream->node, &byt->stream_list); 553 list_add(&stream->node, &byt->stream_list);
551 stream->notify_position = notify_position; 554 stream->notify_position = notify_position;
552 stream->pdata = data; 555 stream->pdata = data;
553 stream->byt = byt; 556 stream->byt = byt;
554 stream->str_id = id; 557 stream->str_id = id;
558 spin_unlock_irqrestore(&sst->spinlock, flags);
555 559
556 return stream; 560 return stream;
557} 561}
@@ -630,6 +634,8 @@ int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream)
630{ 634{
631 u64 header; 635 u64 header;
632 int ret = 0; 636 int ret = 0;
637 struct sst_dsp *sst = byt->dsp;
638 unsigned long flags;
633 639
634 if (!stream->commited) 640 if (!stream->commited)
635 goto out; 641 goto out;
@@ -644,8 +650,10 @@ int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream)
644 650
645 stream->commited = false; 651 stream->commited = false;
646out: 652out:
653 spin_lock_irqsave(&sst->spinlock, flags);
647 list_del(&stream->node); 654 list_del(&stream->node);
648 kfree(stream); 655 kfree(stream);
656 spin_unlock_irqrestore(&sst->spinlock, flags);
649 657
650 return ret; 658 return ret;
651} 659}
diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c
index 50e4246d4b57..6c0b4f247a86 100644
--- a/sound/soc/intel/sst-haswell-ipc.c
+++ b/sound/soc/intel/sst-haswell-ipc.c
@@ -1159,11 +1159,14 @@ struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id,
1159 void *data) 1159 void *data)
1160{ 1160{
1161 struct sst_hsw_stream *stream; 1161 struct sst_hsw_stream *stream;
1162 struct sst_dsp *sst = hsw->dsp;
1163 unsigned long flags;
1162 1164
1163 stream = kzalloc(sizeof(*stream), GFP_KERNEL); 1165 stream = kzalloc(sizeof(*stream), GFP_KERNEL);
1164 if (stream == NULL) 1166 if (stream == NULL)
1165 return NULL; 1167 return NULL;
1166 1168
1169 spin_lock_irqsave(&sst->spinlock, flags);
1167 list_add(&stream->node, &hsw->stream_list); 1170 list_add(&stream->node, &hsw->stream_list);
1168 stream->notify_position = notify_position; 1171 stream->notify_position = notify_position;
1169 stream->pdata = data; 1172 stream->pdata = data;
@@ -1172,6 +1175,7 @@ struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id,
1172 1175
1173 /* work to process notification messages */ 1176 /* work to process notification messages */
1174 INIT_WORK(&stream->notify_work, hsw_notification_work); 1177 INIT_WORK(&stream->notify_work, hsw_notification_work);
1178 spin_unlock_irqrestore(&sst->spinlock, flags);
1175 1179
1176 return stream; 1180 return stream;
1177} 1181}
@@ -1180,6 +1184,8 @@ int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
1180{ 1184{
1181 u32 header; 1185 u32 header;
1182 int ret = 0; 1186 int ret = 0;
1187 struct sst_dsp *sst = hsw->dsp;
1188 unsigned long flags;
1183 1189
1184 /* dont free DSP streams that are not commited */ 1190 /* dont free DSP streams that are not commited */
1185 if (!stream->commited) 1191 if (!stream->commited)
@@ -1201,8 +1207,10 @@ int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
1201 trace_hsw_stream_free_req(stream, &stream->free_req); 1207 trace_hsw_stream_free_req(stream, &stream->free_req);
1202 1208
1203out: 1209out:
1210 spin_lock_irqsave(&sst->spinlock, flags);
1204 list_del(&stream->node); 1211 list_del(&stream->node);
1205 kfree(stream); 1212 kfree(stream);
1213 spin_unlock_irqrestore(&sst->spinlock, flags);
1206 1214
1207 return ret; 1215 return ret;
1208} 1216}