diff options
author | Takashi Iwai <tiwai@suse.de> | 2006-11-14 07:03:19 -0500 |
---|---|---|
committer | Jaroslav Kysela <perex@server.perex.cz> | 2006-12-20 02:55:54 -0500 |
commit | e3a5d59a17e9a42e3f3e0e37342b2679bab2ff43 (patch) | |
tree | 631335a715870175f194b4bfec4690e6041fed79 | |
parent | ba8bdf8584c6f8af6d009dfd716ea5ee37fc15cd (diff) |
[ALSA] Fix races in PCM OSS emulation
Fixed the race among multiple threads accessing the OSS PCM
instance concurrently by simply introducing a mutex for protecting
a setup of the PCM.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>
-rw-r--r-- | include/sound/pcm_oss.h | 1 | ||||
-rw-r--r-- | sound/core/oss/pcm_oss.c | 52 |
2 files changed, 41 insertions, 12 deletions
diff --git a/include/sound/pcm_oss.h b/include/sound/pcm_oss.h index c854647b6f3c..1cd4f64cdf31 100644 --- a/include/sound/pcm_oss.h +++ b/include/sound/pcm_oss.h | |||
@@ -56,6 +56,7 @@ struct snd_pcm_oss_runtime { | |||
56 | size_t mmap_bytes; | 56 | size_t mmap_bytes; |
57 | char *buffer; /* vmallocated period */ | 57 | char *buffer; /* vmallocated period */ |
58 | size_t buffer_used; /* used length from period buffer */ | 58 | size_t buffer_used; /* used length from period buffer */ |
59 | struct mutex params_lock; | ||
59 | #ifdef CONFIG_SND_PCM_OSS_PLUGINS | 60 | #ifdef CONFIG_SND_PCM_OSS_PLUGINS |
60 | struct snd_pcm_plugin *plugin_first; | 61 | struct snd_pcm_plugin *plugin_first; |
61 | struct snd_pcm_plugin *plugin_last; | 62 | struct snd_pcm_plugin *plugin_last; |
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index e0821eb3d851..786a82e68890 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c | |||
@@ -810,6 +810,8 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) | |||
810 | struct snd_mask sformat_mask; | 810 | struct snd_mask sformat_mask; |
811 | struct snd_mask mask; | 811 | struct snd_mask mask; |
812 | 812 | ||
813 | if (mutex_lock_interruptible(&runtime->oss.params_lock)) | ||
814 | return -EINTR; | ||
813 | sw_params = kmalloc(sizeof(*sw_params), GFP_KERNEL); | 815 | sw_params = kmalloc(sizeof(*sw_params), GFP_KERNEL); |
814 | params = kmalloc(sizeof(*params), GFP_KERNEL); | 816 | params = kmalloc(sizeof(*params), GFP_KERNEL); |
815 | sparams = kmalloc(sizeof(*sparams), GFP_KERNEL); | 817 | sparams = kmalloc(sizeof(*sparams), GFP_KERNEL); |
@@ -1020,6 +1022,7 @@ failure: | |||
1020 | kfree(sw_params); | 1022 | kfree(sw_params); |
1021 | kfree(params); | 1023 | kfree(params); |
1022 | kfree(sparams); | 1024 | kfree(sparams); |
1025 | mutex_unlock(&runtime->oss.params_lock); | ||
1023 | return err; | 1026 | return err; |
1024 | } | 1027 | } |
1025 | 1028 | ||
@@ -1307,14 +1310,17 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha | |||
1307 | 1310 | ||
1308 | if ((tmp = snd_pcm_oss_make_ready(substream)) < 0) | 1311 | if ((tmp = snd_pcm_oss_make_ready(substream)) < 0) |
1309 | return tmp; | 1312 | return tmp; |
1313 | mutex_lock(&runtime->oss.params_lock); | ||
1310 | while (bytes > 0) { | 1314 | while (bytes > 0) { |
1311 | if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) { | 1315 | if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) { |
1312 | tmp = bytes; | 1316 | tmp = bytes; |
1313 | if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes) | 1317 | if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes) |
1314 | tmp = runtime->oss.period_bytes - runtime->oss.buffer_used; | 1318 | tmp = runtime->oss.period_bytes - runtime->oss.buffer_used; |
1315 | if (tmp > 0) { | 1319 | if (tmp > 0) { |
1316 | if (copy_from_user(runtime->oss.buffer + runtime->oss.buffer_used, buf, tmp)) | 1320 | if (copy_from_user(runtime->oss.buffer + runtime->oss.buffer_used, buf, tmp)) { |
1317 | return xfer > 0 ? (snd_pcm_sframes_t)xfer : -EFAULT; | 1321 | tmp = -EFAULT; |
1322 | goto err; | ||
1323 | } | ||
1318 | } | 1324 | } |
1319 | runtime->oss.buffer_used += tmp; | 1325 | runtime->oss.buffer_used += tmp; |
1320 | buf += tmp; | 1326 | buf += tmp; |
@@ -1325,22 +1331,24 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha | |||
1325 | tmp = snd_pcm_oss_write2(substream, runtime->oss.buffer + runtime->oss.period_ptr, | 1331 | tmp = snd_pcm_oss_write2(substream, runtime->oss.buffer + runtime->oss.period_ptr, |
1326 | runtime->oss.buffer_used - runtime->oss.period_ptr, 1); | 1332 | runtime->oss.buffer_used - runtime->oss.period_ptr, 1); |
1327 | if (tmp <= 0) | 1333 | if (tmp <= 0) |
1328 | return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; | 1334 | goto err; |
1329 | runtime->oss.bytes += tmp; | 1335 | runtime->oss.bytes += tmp; |
1330 | runtime->oss.period_ptr += tmp; | 1336 | runtime->oss.period_ptr += tmp; |
1331 | runtime->oss.period_ptr %= runtime->oss.period_bytes; | 1337 | runtime->oss.period_ptr %= runtime->oss.period_bytes; |
1332 | if (runtime->oss.period_ptr == 0 || | 1338 | if (runtime->oss.period_ptr == 0 || |
1333 | runtime->oss.period_ptr == runtime->oss.buffer_used) | 1339 | runtime->oss.period_ptr == runtime->oss.buffer_used) |
1334 | runtime->oss.buffer_used = 0; | 1340 | runtime->oss.buffer_used = 0; |
1335 | else if ((substream->f_flags & O_NONBLOCK) != 0) | 1341 | else if ((substream->f_flags & O_NONBLOCK) != 0) { |
1336 | return xfer > 0 ? xfer : -EAGAIN; | 1342 | tmp = -EAGAIN; |
1343 | goto err; | ||
1344 | } | ||
1337 | } | 1345 | } |
1338 | } else { | 1346 | } else { |
1339 | tmp = snd_pcm_oss_write2(substream, | 1347 | tmp = snd_pcm_oss_write2(substream, |
1340 | (const char __force *)buf, | 1348 | (const char __force *)buf, |
1341 | runtime->oss.period_bytes, 0); | 1349 | runtime->oss.period_bytes, 0); |
1342 | if (tmp <= 0) | 1350 | if (tmp <= 0) |
1343 | return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; | 1351 | goto err; |
1344 | runtime->oss.bytes += tmp; | 1352 | runtime->oss.bytes += tmp; |
1345 | buf += tmp; | 1353 | buf += tmp; |
1346 | bytes -= tmp; | 1354 | bytes -= tmp; |
@@ -1350,7 +1358,12 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha | |||
1350 | break; | 1358 | break; |
1351 | } | 1359 | } |
1352 | } | 1360 | } |
1361 | mutex_unlock(&runtime->oss.params_lock); | ||
1353 | return xfer; | 1362 | return xfer; |
1363 | |||
1364 | err: | ||
1365 | mutex_unlock(&runtime->oss.params_lock); | ||
1366 | return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; | ||
1354 | } | 1367 | } |
1355 | 1368 | ||
1356 | static ssize_t snd_pcm_oss_read2(struct snd_pcm_substream *substream, char *buf, size_t bytes, int in_kernel) | 1369 | static ssize_t snd_pcm_oss_read2(struct snd_pcm_substream *substream, char *buf, size_t bytes, int in_kernel) |
@@ -1397,12 +1410,13 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use | |||
1397 | 1410 | ||
1398 | if ((tmp = snd_pcm_oss_make_ready(substream)) < 0) | 1411 | if ((tmp = snd_pcm_oss_make_ready(substream)) < 0) |
1399 | return tmp; | 1412 | return tmp; |
1413 | mutex_lock(&runtime->oss.params_lock); | ||
1400 | while (bytes > 0) { | 1414 | while (bytes > 0) { |
1401 | if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) { | 1415 | if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) { |
1402 | if (runtime->oss.buffer_used == 0) { | 1416 | if (runtime->oss.buffer_used == 0) { |
1403 | tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1); | 1417 | tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1); |
1404 | if (tmp <= 0) | 1418 | if (tmp <= 0) |
1405 | return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; | 1419 | goto err; |
1406 | runtime->oss.bytes += tmp; | 1420 | runtime->oss.bytes += tmp; |
1407 | runtime->oss.period_ptr = tmp; | 1421 | runtime->oss.period_ptr = tmp; |
1408 | runtime->oss.buffer_used = tmp; | 1422 | runtime->oss.buffer_used = tmp; |
@@ -1410,8 +1424,10 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use | |||
1410 | tmp = bytes; | 1424 | tmp = bytes; |
1411 | if ((size_t) tmp > runtime->oss.buffer_used) | 1425 | if ((size_t) tmp > runtime->oss.buffer_used) |
1412 | tmp = runtime->oss.buffer_used; | 1426 | tmp = runtime->oss.buffer_used; |
1413 | if (copy_to_user(buf, runtime->oss.buffer + (runtime->oss.period_ptr - runtime->oss.buffer_used), tmp)) | 1427 | if (copy_to_user(buf, runtime->oss.buffer + (runtime->oss.period_ptr - runtime->oss.buffer_used), tmp)) { |
1414 | return xfer > 0 ? (snd_pcm_sframes_t)xfer : -EFAULT; | 1428 | tmp = -EFAULT; |
1429 | goto err; | ||
1430 | } | ||
1415 | buf += tmp; | 1431 | buf += tmp; |
1416 | bytes -= tmp; | 1432 | bytes -= tmp; |
1417 | xfer += tmp; | 1433 | xfer += tmp; |
@@ -1420,14 +1436,19 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use | |||
1420 | tmp = snd_pcm_oss_read2(substream, (char __force *)buf, | 1436 | tmp = snd_pcm_oss_read2(substream, (char __force *)buf, |
1421 | runtime->oss.period_bytes, 0); | 1437 | runtime->oss.period_bytes, 0); |
1422 | if (tmp <= 0) | 1438 | if (tmp <= 0) |
1423 | return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; | 1439 | goto err; |
1424 | runtime->oss.bytes += tmp; | 1440 | runtime->oss.bytes += tmp; |
1425 | buf += tmp; | 1441 | buf += tmp; |
1426 | bytes -= tmp; | 1442 | bytes -= tmp; |
1427 | xfer += tmp; | 1443 | xfer += tmp; |
1428 | } | 1444 | } |
1429 | } | 1445 | } |
1446 | mutex_unlock(&runtime->oss.params_lock); | ||
1430 | return xfer; | 1447 | return xfer; |
1448 | |||
1449 | err: | ||
1450 | mutex_unlock(&runtime->oss.params_lock); | ||
1451 | return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; | ||
1431 | } | 1452 | } |
1432 | 1453 | ||
1433 | static int snd_pcm_oss_reset(struct snd_pcm_oss_file *pcm_oss_file) | 1454 | static int snd_pcm_oss_reset(struct snd_pcm_oss_file *pcm_oss_file) |
@@ -1528,6 +1549,7 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file) | |||
1528 | return err; | 1549 | return err; |
1529 | format = snd_pcm_oss_format_from(runtime->oss.format); | 1550 | format = snd_pcm_oss_format_from(runtime->oss.format); |
1530 | width = snd_pcm_format_physical_width(format); | 1551 | width = snd_pcm_format_physical_width(format); |
1552 | mutex_lock(&runtime->oss.params_lock); | ||
1531 | if (runtime->oss.buffer_used > 0) { | 1553 | if (runtime->oss.buffer_used > 0) { |
1532 | #ifdef OSS_DEBUG | 1554 | #ifdef OSS_DEBUG |
1533 | printk("sync: buffer_used\n"); | 1555 | printk("sync: buffer_used\n"); |
@@ -1537,8 +1559,10 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file) | |||
1537 | runtime->oss.buffer + runtime->oss.buffer_used, | 1559 | runtime->oss.buffer + runtime->oss.buffer_used, |
1538 | size); | 1560 | size); |
1539 | err = snd_pcm_oss_sync1(substream, runtime->oss.period_bytes); | 1561 | err = snd_pcm_oss_sync1(substream, runtime->oss.period_bytes); |
1540 | if (err < 0) | 1562 | if (err < 0) { |
1563 | mutex_unlock(&runtime->oss.params_lock); | ||
1541 | return err; | 1564 | return err; |
1565 | } | ||
1542 | } else if (runtime->oss.period_ptr > 0) { | 1566 | } else if (runtime->oss.period_ptr > 0) { |
1543 | #ifdef OSS_DEBUG | 1567 | #ifdef OSS_DEBUG |
1544 | printk("sync: period_ptr\n"); | 1568 | printk("sync: period_ptr\n"); |
@@ -1548,8 +1572,10 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file) | |||
1548 | runtime->oss.buffer, | 1572 | runtime->oss.buffer, |
1549 | size * 8 / width); | 1573 | size * 8 / width); |
1550 | err = snd_pcm_oss_sync1(substream, size); | 1574 | err = snd_pcm_oss_sync1(substream, size); |
1551 | if (err < 0) | 1575 | if (err < 0) { |
1576 | mutex_unlock(&runtime->oss.params_lock); | ||
1552 | return err; | 1577 | return err; |
1578 | } | ||
1553 | } | 1579 | } |
1554 | /* | 1580 | /* |
1555 | * The ALSA's period might be a bit large than OSS one. | 1581 | * The ALSA's period might be a bit large than OSS one. |
@@ -1579,6 +1605,7 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file) | |||
1579 | snd_pcm_lib_writev(substream, buffers, size); | 1605 | snd_pcm_lib_writev(substream, buffers, size); |
1580 | } | 1606 | } |
1581 | } | 1607 | } |
1608 | mutex_unlock(&runtime->oss.params_lock); | ||
1582 | /* | 1609 | /* |
1583 | * finish sync: drain the buffer | 1610 | * finish sync: drain the buffer |
1584 | */ | 1611 | */ |
@@ -2172,6 +2199,7 @@ static void snd_pcm_oss_init_substream(struct snd_pcm_substream *substream, | |||
2172 | runtime->oss.params = 1; | 2199 | runtime->oss.params = 1; |
2173 | runtime->oss.trigger = 1; | 2200 | runtime->oss.trigger = 1; |
2174 | runtime->oss.rate = 8000; | 2201 | runtime->oss.rate = 8000; |
2202 | mutex_init(&runtime->oss.params_lock); | ||
2175 | switch (SNDRV_MINOR_OSS_DEVICE(minor)) { | 2203 | switch (SNDRV_MINOR_OSS_DEVICE(minor)) { |
2176 | case SNDRV_MINOR_OSS_PCM_8: | 2204 | case SNDRV_MINOR_OSS_PCM_8: |
2177 | runtime->oss.format = AFMT_U8; | 2205 | runtime->oss.format = AFMT_U8; |