| From c1f0616124c455c5c762b6f123e40bba5df759e6 Mon Sep 17 00:00:00 2001 |
| From: Takashi Iwai <tiwai@suse.de> |
| Date: Sun, 16 May 2021 18:17:55 +0200 |
| Subject: ALSA: intel8x0: Don't update period unless prepared |
| |
| From: Takashi Iwai <tiwai@suse.de> |
| |
| commit c1f0616124c455c5c762b6f123e40bba5df759e6 upstream. |
| |
| The interrupt handler of intel8x0 calls snd_intel8x0_update() whenever |
| the hardware sets the corresponding status bit for each stream. This |
| works fine for most cases as long as the hardware behaves properly. |
| But when the hardware gives a wrong bit set, this leads to a zero- |
| division Oops, and reportedly, this seems what happened on a VM. |
| |
| For fixing the crash, this patch adds a internal flag indicating that |
| the stream is ready to be updated, and check it (as well as the flag |
| being in suspended) to ignore such spurious update. |
| |
| Cc: <stable@vger.kernel.org> |
| Reported-and-tested-by: Sergey Senozhatsky <senozhatsky@chromium.org> |
| Link: https://lore.kernel.org/r/s5h5yzi7uh0.wl-tiwai@suse.de |
| Signed-off-by: Takashi Iwai <tiwai@suse.de> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| sound/pci/intel8x0.c | 7 +++++++ |
| 1 file changed, 7 insertions(+) |
| |
| --- a/sound/pci/intel8x0.c |
| +++ b/sound/pci/intel8x0.c |
| @@ -354,6 +354,7 @@ struct ichdev { |
| unsigned int ali_slot; /* ALI DMA slot */ |
| struct ac97_pcm *pcm; |
| int pcm_open_flag; |
| + unsigned int prepared:1; |
| unsigned int suspended: 1; |
| }; |
| |
| @@ -714,6 +715,9 @@ static inline void snd_intel8x0_update(s |
| int status, civ, i, step; |
| int ack = 0; |
| |
| + if (!ichdev->prepared || ichdev->suspended) |
| + return; |
| + |
| spin_lock_irqsave(&chip->reg_lock, flags); |
| status = igetbyte(chip, port + ichdev->roff_sr); |
| civ = igetbyte(chip, port + ICH_REG_OFF_CIV); |
| @@ -907,6 +911,7 @@ static int snd_intel8x0_hw_params(struct |
| if (ichdev->pcm_open_flag) { |
| snd_ac97_pcm_close(ichdev->pcm); |
| ichdev->pcm_open_flag = 0; |
| + ichdev->prepared = 0; |
| } |
| err = snd_ac97_pcm_open(ichdev->pcm, params_rate(hw_params), |
| params_channels(hw_params), |
| @@ -928,6 +933,7 @@ static int snd_intel8x0_hw_free(struct s |
| if (ichdev->pcm_open_flag) { |
| snd_ac97_pcm_close(ichdev->pcm); |
| ichdev->pcm_open_flag = 0; |
| + ichdev->prepared = 0; |
| } |
| return snd_pcm_lib_free_pages(substream); |
| } |
| @@ -1002,6 +1008,7 @@ static int snd_intel8x0_pcm_prepare(stru |
| ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1; |
| } |
| snd_intel8x0_setup_periods(chip, ichdev); |
| + ichdev->prepared = 1; |
| return 0; |
| } |
| |