summaryrefslogtreecommitdiff
path: root/drivers/staging/line6/capture.c
diff options
context:
space:
mode:
authorStefan Hajnoczi <stefanha@gmail.com>2011-11-23 08:20:45 (GMT)
committerGreg Kroah-Hartman <gregkh@suse.de>2011-11-27 00:14:12 (GMT)
commit140e28b83c4a31831cbf293d9cab20c603821202 (patch)
treed74a1c1ca636c668226fb1fdec4b7f0fccd1c376 /drivers/staging/line6/capture.c
parent3b08db37cb04a80dccac8c2d7b03690b5f179487 (diff)
downloadlinux-140e28b83c4a31831cbf293d9cab20c603821202.tar.xz
staging: line6: alloc/free buffers in hw_params/hw_free
It is unsafe to free buffers in line6_pcm_stop(), which is not allowed to sleep, since urbs cannot be killed completely there and only unlinked. This means I/O may still be in progress and the URB completion function still gets invoked. This may result in memory corruption when buffer_in is freed but I/O is still pending. Additionally, line6_pcm_start() is not supposed to sleep so it should not use kmalloc(GFP_KERNEL). These issues can be resolved by performing buffer allocation/freeing in the .hw_params/.hw_free callbacks instead. The ALSA documentation also recommends doing buffer allocation/freeing in these callbacks. Signed-off-by: Stefan Hajnoczi <stefanha@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/line6/capture.c')
-rw-r--r--drivers/staging/line6/capture.c15
1 files changed, 15 insertions, 0 deletions
diff --git a/drivers/staging/line6/capture.c b/drivers/staging/line6/capture.c
index 9647154..d9da7ed 100644
--- a/drivers/staging/line6/capture.c
+++ b/drivers/staging/line6/capture.c
@@ -9,6 +9,7 @@
*
*/
+#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -319,6 +320,15 @@ static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream,
}
/* -- [FD] end */
+ line6pcm->buffer_in = kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS *
+ line6pcm->max_packet_size, GFP_KERNEL);
+
+ if (!line6pcm->buffer_in) {
+ dev_err(line6pcm->line6->ifcdev,
+ "cannot malloc capture buffer\n");
+ return -ENOMEM;
+ }
+
ret = snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
if (ret < 0)
@@ -331,6 +341,11 @@ static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream,
/* hw_free capture callback */
static int snd_line6_capture_hw_free(struct snd_pcm_substream *substream)
{
+ struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
+
+ line6_unlink_wait_clear_audio_in_urbs(line6pcm);
+ kfree(line6pcm->buffer_in);
+ line6pcm->buffer_in = NULL;
return snd_pcm_lib_free_pages(substream);
}