ALSA: hda - Fix non-snoop page handling

For non-snoop mode, we fiddle with the page attributes of CORB/RIRB
and the position buffer, but also the ring buffers.  The problem is
that the current code blindly assumes that the buffer is contiguous.
However, the ring buffers may be SG-buffers, thus a wrong vmapped
address is passed there, leading to Oops.

This patch fixes the handling for SG-buffers.

Bugzilla: https://bugzilla.novell.com/show_bug.cgi?id=800701

Cc: <stable@vger.kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2013-01-29 18:07:22 +01:00
Родитель f748abcc5b
Коммит 9ddf1aeb21
1 изменённых файлов: 26 добавлений и 14 удалений

Просмотреть файл

@ -656,29 +656,43 @@ static char *driver_short_names[] = {
#define get_azx_dev(substream) (substream->runtime->private_data)
#ifdef CONFIG_X86
static void __mark_pages_wc(struct azx *chip, void *addr, size_t size, bool on)
static void __mark_pages_wc(struct azx *chip, struct snd_dma_buffer *dmab, bool on)
{
int pages;
if (azx_snoop(chip))
return;
if (addr && size) {
int pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
if (!dmab || !dmab->area || !dmab->bytes)
return;
#ifdef CONFIG_SND_DMA_SGBUF
if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_SG) {
struct snd_sg_buf *sgbuf = dmab->private_data;
if (on)
set_memory_wc((unsigned long)addr, pages);
set_pages_array_wc(sgbuf->page_table, sgbuf->pages);
else
set_memory_wb((unsigned long)addr, pages);
set_pages_array_wb(sgbuf->page_table, sgbuf->pages);
return;
}
#endif
pages = (dmab->bytes + PAGE_SIZE - 1) >> PAGE_SHIFT;
if (on)
set_memory_wc((unsigned long)dmab->area, pages);
else
set_memory_wb((unsigned long)dmab->area, pages);
}
static inline void mark_pages_wc(struct azx *chip, struct snd_dma_buffer *buf,
bool on)
{
__mark_pages_wc(chip, buf->area, buf->bytes, on);
__mark_pages_wc(chip, buf, on);
}
static inline void mark_runtime_wc(struct azx *chip, struct azx_dev *azx_dev,
struct snd_pcm_runtime *runtime, bool on)
struct snd_pcm_substream *substream, bool on)
{
if (azx_dev->wc_marked != on) {
__mark_pages_wc(chip, runtime->dma_area, runtime->dma_bytes, on);
__mark_pages_wc(chip, snd_pcm_get_dma_buf(substream), on);
azx_dev->wc_marked = on;
}
}
@ -689,7 +703,7 @@ static inline void mark_pages_wc(struct azx *chip, struct snd_dma_buffer *buf,
{
}
static inline void mark_runtime_wc(struct azx *chip, struct azx_dev *azx_dev,
struct snd_pcm_runtime *runtime, bool on)
struct snd_pcm_substream *substream, bool on)
{
}
#endif
@ -1968,11 +1982,10 @@ static int azx_pcm_hw_params(struct snd_pcm_substream *substream,
{
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
struct azx *chip = apcm->chip;
struct snd_pcm_runtime *runtime = substream->runtime;
struct azx_dev *azx_dev = get_azx_dev(substream);
int ret;
mark_runtime_wc(chip, azx_dev, runtime, false);
mark_runtime_wc(chip, azx_dev, substream, false);
azx_dev->bufsize = 0;
azx_dev->period_bytes = 0;
azx_dev->format_val = 0;
@ -1980,7 +1993,7 @@ static int azx_pcm_hw_params(struct snd_pcm_substream *substream,
params_buffer_bytes(hw_params));
if (ret < 0)
return ret;
mark_runtime_wc(chip, azx_dev, runtime, true);
mark_runtime_wc(chip, azx_dev, substream, true);
return ret;
}
@ -1989,7 +2002,6 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
struct azx_dev *azx_dev = get_azx_dev(substream);
struct azx *chip = apcm->chip;
struct snd_pcm_runtime *runtime = substream->runtime;
struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
/* reset BDL address */
@ -2002,7 +2014,7 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
snd_hda_codec_cleanup(apcm->codec, hinfo, substream);
mark_runtime_wc(chip, azx_dev, runtime, false);
mark_runtime_wc(chip, azx_dev, substream, false);
return snd_pcm_lib_free_pages(substream);
}