diff options
author | Takashi Iwai <tiwai@suse.de> | 2005-05-31 08:35:31 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@suse.cz> | 2005-06-22 06:27:53 -0400 |
commit | 21cb2a2ec5818cbba01bcb7f24388670322c77f9 (patch) | |
tree | abd491e1b613f200970359546185619228d3ec02 /sound/core | |
parent | ce43fbaececc82196d321671159483b3287de128 (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/core')
-rw-r--r-- | sound/core/pcm_native.c | 70 |
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 | } |