diff options
author | Stefan Hajnoczi <stefanha@gmail.com> | 2011-11-23 08:20:45 +0000 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-11-26 16:14:12 -0800 |
commit | 140e28b83c4a31831cbf293d9cab20c603821202 (patch) | |
tree | d74a1c1ca636c668226fb1fdec4b7f0fccd1c376 /drivers/staging/line6/playback.c | |
parent | 3b08db37cb04a80dccac8c2d7b03690b5f179487 (diff) |
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/playback.c')
-rw-r--r-- | drivers/staging/line6/playback.c | 15 |
1 files changed, 15 insertions, 0 deletions
diff --git a/drivers/staging/line6/playback.c b/drivers/staging/line6/playback.c index 10c543836583..b3445274072b 100644 --- a/drivers/staging/line6/playback.c +++ b/drivers/staging/line6/playback.c @@ -9,6 +9,7 @@ * */ +#include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -469,6 +470,15 @@ static int snd_line6_playback_hw_params(struct snd_pcm_substream *substream, } /* -- [FD] end */ + line6pcm->buffer_out = kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * + line6pcm->max_packet_size, GFP_KERNEL); + + if (!line6pcm->buffer_out) { + dev_err(line6pcm->line6->ifcdev, + "cannot malloc playback buffer\n"); + return -ENOMEM; + } + ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); if (ret < 0) @@ -481,6 +491,11 @@ static int snd_line6_playback_hw_params(struct snd_pcm_substream *substream, /* hw_free playback callback */ static int snd_line6_playback_hw_free(struct snd_pcm_substream *substream) { + struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); + + line6_unlink_wait_clear_audio_out_urbs(line6pcm); + kfree(line6pcm->buffer_out); + line6pcm->buffer_out = NULL; return snd_pcm_lib_free_pages(substream); } |