aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2006-11-14 07:03:19 -0500
committerJaroslav Kysela <perex@server.perex.cz>2006-12-20 02:55:54 -0500
commite3a5d59a17e9a42e3f3e0e37342b2679bab2ff43 (patch)
tree631335a715870175f194b4bfec4690e6041fed79
parentba8bdf8584c6f8af6d009dfd716ea5ee37fc15cd (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.h1
-rw-r--r--sound/core/oss/pcm_oss.c52
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
1356static ssize_t snd_pcm_oss_read2(struct snd_pcm_substream *substream, char *buf, size_t bytes, int in_kernel) 1369static 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
1433static int snd_pcm_oss_reset(struct snd_pcm_oss_file *pcm_oss_file) 1454static 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;