aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2005-05-31 08:35:31 -0400
committerJaroslav Kysela <perex@suse.cz>2005-06-22 06:27:53 -0400
commit21cb2a2ec5818cbba01bcb7f24388670322c77f9 (patch)
treeabd491e1b613f200970359546185619228d3ec02 /sound
parentce43fbaececc82196d321671159483b3287de128 (diff)
[ALSA] Fix races between PCM drain and other ops
PCM Midlevel Fix semaphore races between PCM drain and other ops. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r--sound/core/pcm_native.c70
1 files changed, 36 insertions, 34 deletions
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 4e582415a086..10c2c9832649 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -1368,43 +1368,32 @@ static int snd_pcm_drain(snd_pcm_substream_t *substream)
1368 if (runtime->status->state == SNDRV_PCM_STATE_OPEN) 1368 if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
1369 return -EBADFD; 1369 return -EBADFD;
1370 1370
1371 down_read(&snd_pcm_link_rwsem);
1372 snd_power_lock(card); 1371 snd_power_lock(card);
1373 if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { 1372 if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
1374 result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile); 1373 result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
1375 if (result < 0) 1374 if (result < 0) {
1376 goto _unlock; 1375 snd_power_unlock(card);
1376 return result;
1377 }
1377 } 1378 }
1378 1379
1379 /* allocate temporary record for drain sync */ 1380 /* allocate temporary record for drain sync */
1381 down_read(&snd_pcm_link_rwsem);
1380 if (snd_pcm_stream_linked(substream)) { 1382 if (snd_pcm_stream_linked(substream)) {
1381 drec = kmalloc(substream->group->count * sizeof(*drec), GFP_KERNEL); 1383 drec = kmalloc(substream->group->count * sizeof(*drec), GFP_KERNEL);
1382 if (! drec) { 1384 if (! drec) {
1383 result = -ENOMEM; 1385 up_read(&snd_pcm_link_rwsem);
1384 goto _unlock; 1386 snd_power_unlock(card);
1387 return -ENOMEM;
1385 } 1388 }
1386 } else 1389 } else
1387 drec = &drec_tmp; 1390 drec = &drec_tmp;
1388 1391
1389 snd_pcm_stream_lock_irq(substream); 1392 /* count only playback streams */
1390 /* resume pause */
1391 if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
1392 snd_pcm_pause(substream, 0);
1393
1394 /* pre-start/stop - all running streams are changed to DRAINING state */
1395 result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0);
1396 if (result < 0)
1397 goto _end;
1398
1399 /* check streams with PLAYBACK & DRAINING */
1400 num_drecs = 0; 1393 num_drecs = 0;
1401 snd_pcm_group_for_each(pos, substream) { 1394 snd_pcm_group_for_each(pos, substream) {
1402 snd_pcm_substream_t *s = snd_pcm_group_substream_entry(pos); 1395 snd_pcm_substream_t *s = snd_pcm_group_substream_entry(pos);
1403 runtime = s->runtime; 1396 runtime = s->runtime;
1404 if (runtime->status->state != SNDRV_PCM_STATE_DRAINING) {
1405 runtime->status->state = SNDRV_PCM_STATE_SETUP;
1406 continue;
1407 }
1408 if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { 1397 if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
1409 d = &drec[num_drecs++]; 1398 d = &drec[num_drecs++];
1410 d->substream = s; 1399 d->substream = s;
@@ -1418,9 +1407,21 @@ static int snd_pcm_drain(snd_pcm_substream_t *substream)
1418 runtime->stop_threshold = runtime->buffer_size; 1407 runtime->stop_threshold = runtime->buffer_size;
1419 } 1408 }
1420 } 1409 }
1421 1410 up_read(&snd_pcm_link_rwsem);
1422 if (! num_drecs) 1411 if (! num_drecs)
1423 goto _end; 1412 goto _error;
1413
1414 snd_pcm_stream_lock_irq(substream);
1415 /* resume pause */
1416 if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
1417 snd_pcm_pause(substream, 0);
1418
1419 /* pre-start/stop - all running streams are changed to DRAINING state */
1420 result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0);
1421 if (result < 0) {
1422 snd_pcm_stream_unlock_irq(substream);
1423 goto _error;
1424 }
1424 1425
1425 for (;;) { 1426 for (;;) {
1426 long tout; 1427 long tout;
@@ -1428,6 +1429,15 @@ static int snd_pcm_drain(snd_pcm_substream_t *substream)
1428 result = -ERESTARTSYS; 1429 result = -ERESTARTSYS;
1429 break; 1430 break;
1430 } 1431 }
1432 /* all finished? */
1433 for (i = 0; i < num_drecs; i++) {
1434 runtime = drec[i].substream->runtime;
1435 if (runtime->status->state == SNDRV_PCM_STATE_DRAINING)
1436 break;
1437 }
1438 if (i == num_drecs)
1439 break; /* yes, all drained */
1440
1431 set_current_state(TASK_INTERRUPTIBLE); 1441 set_current_state(TASK_INTERRUPTIBLE);
1432 snd_pcm_stream_unlock_irq(substream); 1442 snd_pcm_stream_unlock_irq(substream);
1433 snd_power_unlock(card); 1443 snd_power_unlock(card);
@@ -1444,15 +1454,11 @@ static int snd_pcm_drain(snd_pcm_substream_t *substream)
1444 } 1454 }
1445 break; 1455 break;
1446 } 1456 }
1447 /* all finished? */
1448 for (i = 0; i < num_drecs; i++) {
1449 runtime = drec[i].substream->runtime;
1450 if (runtime->status->state == SNDRV_PCM_STATE_DRAINING)
1451 break;
1452 }
1453 if (i == num_drecs)
1454 break;
1455 } 1457 }
1458
1459 snd_pcm_stream_unlock_irq(substream);
1460
1461 _error:
1456 for (i = 0; i < num_drecs; i++) { 1462 for (i = 0; i < num_drecs; i++) {
1457 d = &drec[i]; 1463 d = &drec[i];
1458 runtime = d->substream->runtime; 1464 runtime = d->substream->runtime;
@@ -1460,13 +1466,9 @@ static int snd_pcm_drain(snd_pcm_substream_t *substream)
1460 runtime->stop_threshold = d->stop_threshold; 1466 runtime->stop_threshold = d->stop_threshold;
1461 } 1467 }
1462 1468
1463 _end:
1464 snd_pcm_stream_unlock_irq(substream);
1465 if (drec != &drec_tmp) 1469 if (drec != &drec_tmp)
1466 kfree(drec); 1470 kfree(drec);
1467 _unlock:
1468 snd_power_unlock(card); 1471 snd_power_unlock(card);
1469 up_read(&snd_pcm_link_rwsem);
1470 1472
1471 return result; 1473 return result;
1472} 1474}