Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ALSA: hda: Set up BDL table at hw_params

So far the setup of BDL table is performed at the prepare stage, where
all PCM parameters have been already set up. When something wrong
happens at it, we return -EINVAL; it's supposed to be a rare case
since the involved memory allocation is a small chunk of kmalloc for
the table.

However, when we receive too many small non-contiguous pages in highly
fragmented memories, it may overflow the max table size, resulting in
the same -EINVAL error from the prepare, too. A bad scenario is that
user-space cannot know what went wrong (as it's an error from the
prepare stage) and -EINVAL, hence it may retry with the same
parameters, failing again repeatedly.

In this patch, we try to set up the BDL table at hw_params right after
the buffer allocation, and return -ENOMEM if it overflows.
This allows user-space knowing that it should reduce the buffer size
request accordingly and may retry with more fitting parameters.

Link: https://lore.kernel.org/r/20240221100607.6565-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>

+11 -3
+11 -3
sound/pci/hda/hda_controller.c
··· 24 24 25 25 #include <sound/core.h> 26 26 #include <sound/initval.h> 27 + #include <sound/pcm_params.h> 27 28 #include "hda_controller.h" 28 29 #include "hda_local.h" 29 30 ··· 109 108 struct azx_pcm *apcm = snd_pcm_substream_chip(substream); 110 109 struct azx *chip = apcm->chip; 111 110 struct azx_dev *azx_dev = get_azx_dev(substream); 111 + struct hdac_stream *hdas = azx_stream(azx_dev); 112 112 int ret = 0; 113 113 114 114 trace_azx_pcm_hw_params(chip, azx_dev); ··· 119 117 goto unlock; 120 118 } 121 119 122 - azx_dev->core.bufsize = 0; 123 - azx_dev->core.period_bytes = 0; 124 - azx_dev->core.format_val = 0; 120 + /* Set up BDLEs here, return -ENOMEM if too many BDLEs are required */ 121 + hdas->bufsize = params_buffer_bytes(hw_params); 122 + hdas->period_bytes = params_period_bytes(hw_params); 123 + hdas->format_val = 0; 124 + hdas->no_period_wakeup = 125 + (hw_params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) && 126 + (hw_params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP); 127 + if (snd_hdac_stream_setup_periods(hdas) < 0) 128 + ret = -ENOMEM; 125 129 126 130 unlock: 127 131 dsp_unlock(azx_dev);