7.2.7-rc1 review
Subsystems (5)
Greg Kroah-Hartman
maintainerPATCH 115/733 Bluetooth: hci_mrvl: Fix wrong return value check of wait_on_bit_timeout()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gongwei Li <[email protected]>
[ Upstream commit 2deb76c21b81e42b3282224f7dd2046fe73fd1e0 ]
wait_on_bit_timeout() returns 0 if the bit was cleared, -EINTR if the process received a signal and the mode permitted wake up on that signal, or -EAGAIN if the timeout elapsed. It never returns 1.
Hence the check "err == 1" in mrvl_load_firmware() is dead code: when the waiting task is interrupted by a signal (-EINTR), the code falls into the "else if (err)" branch and misreports it as "Firmware request timeout" with -ETIMEDOUT instead of propagating -EINTR.
Fix this by testing for -EINTR so that an interrupted firmware load is properly detected and reported.
--- drivers/bluetooth/hci_mrvl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/bluetooth/hci_mrvl.c +1 -2
@@ -307,9 +307,8 @@ static int mrvl_load_firmware(struct hci_dev *hdev, const char *name) err =wait_on_bit_timeout (&mrvl->flags,STATE_FW_REQ_PENDING ,TASK_INTERRUPTIBLE ,msecs_to_jiffies (2000 )); -if (err ==1 ) { +if (err == -EINTR ) {bt_dev_err (hdev,"Firmware load interrupted" ); - err = -EINTR ;break ; }else if (err) {bt_dev_err (hdev,"Firmware request timeout" );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 116/733 platform/x86: asus-laptop: Fix ACPI event handling
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Armin Wolf <[email protected]>
[ Upstream commit 6bb4fb72c00dc2a9cb663e2d16adce15e4170cdf ]
The event codes inside asus_keymap[] span a wide range from 0x02 till 0xC5, but using ACPI_DEVICE_NOTIFY prevents us from receiving event codes below 0x80.
Fix this by using ACPI_ALL_NOTIFY instead.
--- drivers/platform/x86/asus-laptop.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/platform/x86/asus-laptop.c +2 -2
@@ -1887,7 +1887,7 @@ static int asus_acpi_probe(struct platform_device *pdev)if (result && result != -ENODEV ) goto fail_pega_rfkill; - result =acpi_dev_install_notify_handler (device,ACPI_DEVICE_NOTIFY , + result =acpi_dev_install_notify_handler (device,ACPI_ALL_NOTIFY , asus_acpi_notify, asus);if (result) goto fail_pega_rfkill;
@@ -1917,7 +1917,7 @@ static void asus_acpi_remove(struct platform_device *pdev) {struct asus_laptop *asus =platform_get_drvdata (pdev); -acpi_dev_remove_notify_handler (asus->device,ACPI_DEVICE_NOTIFY , +acpi_dev_remove_notify_handler (asus->device,ACPI_ALL_NOTIFY , asus_acpi_notify);asus_backlight_exit (asus);asus_rfkill_exit (asus);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 117/733 ASoC: ab8500: Reset the audio block before configuring it
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 8e839bca7793a0b03c005f4b2b0825464290d425 ]
ResetAudn is active low, but the codec probe only deasserts it. It also clears Clk32kOut2Dis despite claiming to disable that output, and writes codec registers before releasing reset.
Pulse ResetAudn before the first audio-bank access and leave the unused 32 kHz output disabled.
--- sound/soc/codecs/ab8500-codec.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-)
sound/soc/codecs/ab8500-codec.c +13 -13
@@ -1640,18 +1640,18 @@ static struct snd_kcontrol_new ab8500_ctrls[] = {static int ab8500_audio_init_audioblock (struct snd_soc_component *component) {int status; +u8 mask =AB8500_STW4500CTRL3_CLK32KOUT2DIS | +AB8500_STW4500CTRL3_RESETAUDN ;dev_dbg (component->dev,"%s: Enter.\n" , __func__); -/* Reset audio-registers and disable 32kHz-clock output 2 */ - status =ab8500_sysctrl_write (AB8500_STW4500CTRL3 , -AB8500_STW4500CTRL3_CLK32KOUT2DIS | -AB8500_STW4500CTRL3_RESETAUDN , -AB8500_STW4500CTRL3_RESETAUDN ); +/* Reset the audio registers and disable the unused 32 kHz output. */ + status =ab8500_sysctrl_write (AB8500_STW4500CTRL3 , mask, +AB8500_STW4500CTRL3_CLK32KOUT2DIS );if (status <0 )return status; -return 0 ; +return ab8500_sysctrl_write (AB8500_STW4500CTRL3 , mask, mask); } static int ab8500_audio_setup_mics(struct snd_soc_component *component,
@@ -2183,6 +2183,13 @@ static int ab8500_codec_probe(struct snd_soc_component *component)ab8500_codec_of_probe (dev, np, &codec_pdata); + status =ab8500_audio_init_audioblock (component); +if (status <0 ) { +dev_err (dev,"%s: failed to init audio-block (%d)!\n" , + __func__, status); +return status; + } + status =ab8500_audio_setup_mics (component, &codec_pdata.amics);if (status <0 ) {pr_err ("%s: Failed to setup mics (%d)!\n" , __func__, status);
@@ -2195,13 +2202,6 @@ static int ab8500_codec_probe(struct snd_soc_component *component)return status; } - status =ab8500_audio_init_audioblock (component); -if (status <0 ) { -dev_err (dev,"%s: failed to init audio-block (%d)!\n" , - __func__, status); -return status; - } -/* Override HW-defaults */ snd_soc_component_write (component,AB8500_ANACONF5 ,BIT (AB8500_ANACONF5_HSAUTOEN ));
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 118/733 ASoC: ab8500: Repair the DAPM capture graph
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 103fe1a37f040ef6ac9ed1cf33be786149d2bb15 ]
The capture stream routes point away from the stream widget. Digital microphone mux routes are unconditional and bypass their enable bits, and several widgets independently own shared AD path enable bits. The dummy ADC and DAC widgets hide the resulting power graph errors.
Connect each real AIF widget to the stream and main supply, use the mux item names on digital microphone routes, and model shared AD enables as supplies. Also make the ANC DAPM switch writable.
--- sound/soc/codecs/ab8500-codec.c | 120 +++++++++++++++----------------- 1 file changed, 56 insertions(+), 64 deletions(-)
sound/soc/codecs/ab8500-codec.c +56 -64
@@ -258,7 +258,7 @@ static const struct snd_kcontrol_new dapm_anc_in_select[] = {/* ANC - Enable/Disable */ static const struct snd_kcontrol_new dapm_anc_enable[] = {SOC_DAPM_SINGLE ("Switch" ,AB8500_ANCCONF1 , -AB8500_ANCCONF1_ENANC ,0 ,0 ), +AB8500_ANCCONF1_ENANC ,1 ,0 ), };/* ANC to Earpiece - Mute */
@@ -340,12 +340,6 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {/* DA/AD */ -SND_SOC_DAPM_INPUT ("ADC Input" ), -SND_SOC_DAPM_ADC ("ADC" ,"ab8500_0c" ,SND_SOC_NOPM ,0 ,0 ), - -SND_SOC_DAPM_DAC ("DAC" ,NULL ,SND_SOC_NOPM ,0 ,0 ), -SND_SOC_DAPM_OUTPUT ("DAC Output" ), -SND_SOC_DAPM_AIF_IN ("DA_IN1" ,NULL ,0 ,SND_SOC_NOPM ,0 ,0 ),SND_SOC_DAPM_AIF_IN ("DA_IN2" ,NULL ,0 ,SND_SOC_NOPM ,0 ,0 ), SND_SOC_DAPM_AIF_IN("DA_IN3", NULL, 0, SND_SOC_NOPM, 0, 0),
@@ -537,9 +531,8 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {SND_SOC_DAPM_MIXER ("AD3 Channel Volume" ,SND_SOC_NOPM ,0 ,0 ,NULL ,0 ), -SND_SOC_DAPM_MIXER ("AD3 Enable" , -AB8500_ADPATHENA ,AB8500_ADPATHENA_ENAD34 ,0 , - NULL, 0), +SND_SOC_DAPM_SUPPLY ("AD34 Enable" ,AB8500_ADPATHENA , + AB8500_ADPATHENA_ENAD34, 0, NULL, 0),/* Mic 2 */
@@ -598,9 +591,8 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = { SND_SOC_NOPM, 0, 0, NULL, 0), -SND_SOC_DAPM_MIXER ("AD12 Enable" , -AB8500_ADPATHENA ,AB8500_ADPATHENA_ENAD12 ,0 , - NULL, 0), +SND_SOC_DAPM_SUPPLY ("AD12 Enable" ,AB8500_ADPATHENA , + AB8500_ADPATHENA_ENAD12, 0, NULL, 0),/* HD Capture path */
@@ -614,12 +606,8 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {SND_SOC_DAPM_MIXER ("AD6 Channel Volume" ,SND_SOC_NOPM ,0 ,0 ,NULL ,0 ), -SND_SOC_DAPM_MIXER ("AD57 Enable" , -AB8500_ADPATHENA ,AB8500_ADPATHENA_ENAD5768 ,0 , -NULL ,0 ), -SND_SOC_DAPM_MIXER ("AD68 Enable" , -AB8500_ADPATHENA ,AB8500_ADPATHENA_ENAD5768 ,0 , - NULL, 0), +SND_SOC_DAPM_SUPPLY ("AD5768 Enable" ,AB8500_ADPATHENA , + AB8500_ADPATHENA_ENAD5768, 0, NULL, 0),/* Digital Microphone path */
@@ -651,10 +639,6 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {SND_SOC_DAPM_MIXER ("AD4 Channel Volume" ,SND_SOC_NOPM ,0 ,0 ,NULL ,0 ), -SND_SOC_DAPM_MIXER ("AD4 Enable" , -AB8500_ADPATHENA ,AB8500_ADPATHENA_ENAD34 , -0 ,NULL ,0 ), -/* Acoustical Noise Cancellation path */ SND_SOC_DAPM_INPUT("ANC Configure Input"),
@@ -702,24 +686,17 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { {"Main Supply", NULL, "Audio Power"}, {"Main Supply", NULL, "Audio Analog Power"}, - {"DAC", NULL, "ab8500_0p"}, - {"DAC", NULL, "Main Supply"}, - {"ADC", NULL, "ab8500_0c"}, - {"ADC", NULL, "Main Supply"}, -/* ANC Configure */ {"ANC Configure Input", NULL, "Main Supply"}, {"ANC Configure Output", NULL, "ANC Configure Input"}, -/* AD/DA */ - {"ADC", NULL, "ADC Input"}, - {"DAC Output", NULL, "DAC"}, -/* Powerup charge pump if DA1/2 is in use */ {"DA_IN1", NULL, "ab8500_0p"}, + {"DA_IN1", NULL, "Main Supply"}, {"DA_IN1", NULL, "Charge Pump"}, {"DA_IN2", NULL, "ab8500_0p"}, + {"DA_IN2", NULL, "Main Supply"}, {"DA_IN2", NULL, "Charge Pump"}, /* Headset path */
@@ -754,8 +731,10 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = {/* HF or LineOut path */ {"DA_IN3", NULL, "ab8500_0p"}, + {"DA_IN3", NULL, "Main Supply"}, {"DA3 Channel Volume", NULL, "DA_IN3"}, {"DA_IN4", NULL, "ab8500_0p"}, + {"DA_IN4", NULL, "Main Supply"}, {"DA4 Channel Volume", NULL, "DA_IN4"}, {"Speaker Left Source", "Audio Path", "DA3 Channel Volume"},
@@ -813,8 +792,10 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = {/* Vibrator path */ {"DA_IN5", NULL, "ab8500_0p"}, + {"DA_IN5", NULL, "Main Supply"}, {"DA5 Channel Volume", NULL, "DA_IN5"}, {"DA_IN6", NULL, "ab8500_0p"}, + {"DA_IN6", NULL, "Main Supply"}, {"DA6 Channel Volume", NULL, "DA_IN6"}, {"VIB1 DAC", NULL, "DA5 Channel Volume"},
@@ -856,13 +837,15 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { {"AD1 Channel Volume", NULL, "AD1 Source Select"}, {"AD2 Channel Volume", NULL, "AD2 Source Select"}, - {"AD12 Enable", NULL, "AD1 Channel Volume"}, - {"AD12 Enable", NULL, "AD2 Channel Volume"}, + {"AD1 Channel Volume", NULL, "AD12 Enable"}, + {"AD2 Channel Volume", NULL, "AD12 Enable"}, - {"AD_OUT1", NULL, "ab8500_0c"}, - {"AD_OUT1", NULL, "AD12 Enable"}, - {"AD_OUT2", NULL, "ab8500_0c"}, - {"AD_OUT2", NULL, "AD12 Enable"}, + {"ab8500_0c", NULL, "AD_OUT1"}, + {"AD_OUT1", NULL, "Main Supply"}, + {"AD_OUT1", NULL, "AD1 Channel Volume"}, + {"ab8500_0c", NULL, "AD_OUT2"}, + {"AD_OUT2", NULL, "Main Supply"}, + {"AD_OUT2", NULL, "AD2 Channel Volume"}, /* Mic 1 */
@@ -879,11 +862,11 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { {"AD3 Source Select", "Mic 1", "MIC1 ADC"}, {"AD3 Channel Volume", NULL, "AD3 Source Select"}, + {"AD3 Channel Volume", NULL, "AD34 Enable"}, - {"AD3 Enable", NULL, "AD3 Channel Volume"}, - - {"AD_OUT3", NULL, "ab8500_0c"}, - {"AD_OUT3", NULL, "AD3 Enable"}, + {"ab8500_0c", NULL, "AD_OUT3"}, + {"AD_OUT3", NULL, "Main Supply"}, + {"AD_OUT3", NULL, "AD3 Channel Volume"}, /* HD Capture path */
@@ -892,14 +875,15 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { {"AD5 Channel Volume", NULL, "AD5 Source Select"}, {"AD6 Channel Volume", NULL, "AD6 Source Select"}, + {"AD5 Channel Volume", NULL, "AD5768 Enable"}, + {"AD6 Channel Volume", NULL, "AD5768 Enable"}, - {"AD57 Enable", NULL, "AD5 Channel Volume"}, - {"AD68 Enable", NULL, "AD6 Channel Volume"}, - - {"AD_OUT57", NULL, "ab8500_0c"}, - {"AD_OUT57", NULL, "AD57 Enable"}, - {"AD_OUT68", NULL, "ab8500_0c"}, - {"AD_OUT68", NULL, "AD68 Enable"}, + {"ab8500_0c", NULL, "AD_OUT57"}, + {"AD_OUT57", NULL, "Main Supply"}, + {"AD_OUT57", NULL, "AD5 Channel Volume"}, + {"ab8500_0c", NULL, "AD_OUT68"}, + {"AD_OUT68", NULL, "Main Supply"}, + {"AD_OUT68", NULL, "AD6 Channel Volume"}, /* Digital Microphone path */
@@ -910,17 +894,25 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { {"DMic 5", NULL, "V-DMIC"}, {"DMic 6", NULL, "V-DMIC"}, - {"AD1 Source Select", NULL, "DMic 1"}, - {"AD2 Source Select", NULL, "DMic 2"}, - {"AD3 Source Select", NULL, "DMic 3"}, - {"AD5 Source Select", NULL, "DMic 5"}, - {"AD6 Source Select", NULL, "DMic 6"}, + {"DMIC1", NULL, "DMic 1"}, + {"DMIC2", NULL, "DMic 2"}, + {"DMIC3", NULL, "DMic 3"}, + {"DMIC4", NULL, "DMic 4"}, + {"DMIC5", NULL, "DMic 5"}, + {"DMIC6", NULL, "DMic 6"}, + + {"AD1 Source Select", "DMic 1", "DMIC1"}, + {"AD2 Source Select", "DMic 2", "DMIC2"}, + {"AD3 Source Select", "DMic 3", "DMIC3"}, + {"AD5 Source Select", "DMic 5", "DMIC5"}, + {"AD6 Source Select", "DMic 6", "DMIC6"}, - {"AD4 Channel Volume", NULL, "DMic 4"}, - {"AD4 Enable", NULL, "AD4 Channel Volume"}, + {"AD4 Channel Volume", NULL, "DMIC4"}, + {"AD4 Channel Volume", NULL, "AD34 Enable"}, - {"AD_OUT4", NULL, "ab8500_0c"}, - {"AD_OUT4", NULL, "AD4 Enable"}, + {"ab8500_0c", NULL, "AD_OUT4"}, + {"AD_OUT4", NULL, "Main Supply"}, + {"AD_OUT4", NULL, "AD4 Channel Volume"}, /* LineIn Bypass path */
@@ -945,13 +937,13 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = {/* Sidetone Filter path */ - {"Sidetone Left Source", "LineIn Left", "AD12 Enable"}, - {"Sidetone Left Source", "LineIn Right", "AD12 Enable"}, - {"Sidetone Left Source", "Mic 1", "AD3 Enable"}, + {"Sidetone Left Source", "LineIn Left", "AD1 Channel Volume"}, + {"Sidetone Left Source", "LineIn Right", "AD2 Channel Volume"}, + {"Sidetone Left Source", "Mic 1", "AD3 Channel Volume"}, {"Sidetone Left Source", "Headset Left", "DA_IN1"}, - {"Sidetone Right Source", "LineIn Right", "AD12 Enable"}, - {"Sidetone Right Source", "Mic 1", "AD3 Enable"}, - {"Sidetone Right Source", "DMic 4", "AD4 Enable"}, + {"Sidetone Right Source", "LineIn Right", "AD2 Channel Volume"}, + {"Sidetone Right Source", "Mic 1", "AD3 Channel Volume"}, + {"Sidetone Right Source", "DMic 4", "AD4 Channel Volume"}, {"Sidetone Right Source", "Headset Right", "DA_IN2"}, {"STFIR1 Control", NULL, "Sidetone Left Source"},
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 119/733 ASoC: ab8500: Correct digital interface format setup
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit f98785adf004db6b1c9f4cea9dadae7b800db72f ]
The codec programs I2S as an undelayed left-aligned format, although the hardware manual defines delayed left-aligned as I2S compatible. It also enables the master generator when the codec is a clock consumer, changes registers before the complete format has been validated, and discards register I/O errors.
Build all three interface register values before writing them, use the required one-bit I2S delay, only run the master generator for a provider configuration, and propagate write failures.
--- sound/soc/codecs/ab8500-codec.c | 175 ++++++++++++-------------------- 1 file changed, 64 insertions(+), 111 deletions(-)
sound/soc/codecs/ab8500-codec.c +64 -111
@@ -1739,149 +1739,91 @@ static int ab8500_audio_set_ear_cmv(struct snd_soc_component *component,return 0 ; } -static int ab8500_audio_set_bit_delay (struct snd_soc_dai *dai, -unsigned int delay) -{ -unsigned int mask, val; -struct snd_soc_component *component = dai->component; - - mask =BIT (AB8500_DIGIFCONF2_IF0DEL ); - val =0 ; - -switch (delay) { -case 0 : -break ; -case 1 : - val |=BIT (AB8500_DIGIFCONF2_IF0DEL ); -break ; -default : -dev_err (dai->component->dev, -"%s: ERROR: Unsupported bit-delay (0x%x)!\n" , - __func__, delay); -return -EINVAL ; - } - -dev_dbg (dai->component->dev,"%s: IF0 Bit-delay: %d bits.\n" , - __func__, delay); -snd_soc_component_update_bits (component,AB8500_DIGIFCONF2 , mask, val); - -return 0 ; -} - -/* Gates clocking according format mask */ -static int ab8500_codec_set_dai_clock_gate (struct snd_soc_component *component, -unsigned int fmt) -{ -unsigned int mask; -unsigned int val; - - mask =BIT (AB8500_DIGIFCONF1_ENMASTGEN ) | -BIT (AB8500_DIGIFCONF1_ENFSBITCLK0 ); - - val =BIT (AB8500_DIGIFCONF1_ENMASTGEN ); - -switch (fmt &SND_SOC_DAIFMT_CLOCK_MASK ) { -case SND_SOC_DAIFMT_CONT :/* continuous clock */ -dev_dbg (component->dev,"%s: IF0 Clock is continuous.\n" , - __func__); - val |=BIT (AB8500_DIGIFCONF1_ENFSBITCLK0 ); -break ; -case SND_SOC_DAIFMT_GATED :/* clock is gated */ -dev_dbg (component->dev,"%s: IF0 Clock is gated.\n" , - __func__); -break ; -default : -dev_err (component->dev, -"%s: ERROR: Unsupported clock mask (0x%x)!\n" , - __func__, fmt &SND_SOC_DAIFMT_CLOCK_MASK ); -return -EINVAL ; - } - -snd_soc_component_update_bits (component,AB8500_DIGIFCONF1 , mask, val); - -return 0 ; -} -static int ab8500_codec_set_dai_fmt (struct snd_soc_dai *dai,unsigned int fmt) { -unsigned int mask; -unsigned int val;struct snd_soc_component *component = dai->component; -int status; +unsigned int conf1_mask, conf1_val =0 ; +unsigned int conf2_mask, conf2_val =0 ; +unsigned int conf3_mask, conf3_val =0 ; +bool provider = false; +int ret;dev_dbg (component->dev,"%s: Enter (fmt = 0x%x)\n" , __func__, fmt); - mask =BIT (AB8500_DIGIFCONF3_IF1DATOIF0AD ) | + conf3_mask =BIT (AB8500_DIGIFCONF3_IF1DATOIF0AD ) |BIT (AB8500_DIGIFCONF3_IF1CLKTOIF0CLK ) |BIT (AB8500_DIGIFCONF3_IF0BFIFOEN ) |BIT (AB8500_DIGIFCONF3_IF0MASTER ); - val =0 ;switch (fmt &SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK ) {case SND_SOC_DAIFMT_CBP_CFP : -dev_dbg (dai->component->dev, +dev_dbg (component->dev,"%s: IF0 Master-mode: AB8500 provider.\n" , __func__); - val |=BIT (AB8500_DIGIFCONF3_IF0MASTER ); + conf3_val |=BIT (AB8500_DIGIFCONF3_IF0MASTER ); + provider = true;break ;case SND_SOC_DAIFMT_CBC_CFC : -dev_dbg (dai->component->dev, +dev_dbg (component->dev,"%s: IF0 Master-mode: AB8500 consumer.\n" , __func__);break ;case SND_SOC_DAIFMT_CBC_CFP :case SND_SOC_DAIFMT_CBP_CFC : -dev_err (dai->component->dev, +dev_err (component->dev,"%s: ERROR: The device is either a provider or a consumer.\n" , __func__); fallthrough;default : -dev_err (dai->component->dev, -"%s: ERROR: Unsupporter clocking mask 0x%x\n" , +dev_err (component->dev, +"%s: ERROR: Unsupported clocking mask 0x%x\n" , __func__, fmt &SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK );return -EINVAL ; } -snd_soc_component_update_bits (component,AB8500_DIGIFCONF3 , mask, val); - -/* Set clock gating */ - status =ab8500_codec_set_dai_clock_gate (component, fmt); -if (status) { -dev_err (dai->component->dev, -"%s: ERROR: Failed to set clock gate (%d).\n" , - __func__, status); -return status; + conf1_mask =BIT (AB8500_DIGIFCONF1_ENMASTGEN ) | +BIT (AB8500_DIGIFCONF1_ENFSBITCLK0 ); +switch (fmt &SND_SOC_DAIFMT_CLOCK_MASK ) { +case SND_SOC_DAIFMT_CONT : +if (provider) + conf1_val = conf1_mask; +break ; +case SND_SOC_DAIFMT_GATED : +if (provider) + conf1_val =BIT (AB8500_DIGIFCONF1_ENMASTGEN ); +break ; +default : +dev_err (component->dev,"%s: Unsupported clock mask 0x%x\n" , + __func__, fmt &SND_SOC_DAIFMT_CLOCK_MASK ); +return -EINVAL ; } -/* Setting data transfer format */ - - mask =BIT (AB8500_DIGIFCONF2_IF0FORMAT0 ) | -BIT (AB8500_DIGIFCONF2_IF0FORMAT1 ) | -BIT (AB8500_DIGIFCONF2_FSYNC0P ) | -BIT (AB8500_DIGIFCONF2_BITCLK0P ); - val =0 ; + conf2_mask =BIT (AB8500_DIGIFCONF2_IF0FORMAT0 ) | +BIT (AB8500_DIGIFCONF2_IF0FORMAT1 ) | +BIT (AB8500_DIGIFCONF2_IF0DEL ) | +BIT (AB8500_DIGIFCONF2_FSYNC0P ) | +BIT (AB8500_DIGIFCONF2_BITCLK0P );switch (fmt &SND_SOC_DAIFMT_FORMAT_MASK ) {case SND_SOC_DAIFMT_I2S :/* I2S mode */ -dev_dbg (dai->component->dev,"%s: IF0 Protocol: I2S\n" , __func__); - val |=BIT (AB8500_DIGIFCONF2_IF0FORMAT1 ); -ab8500_audio_set_bit_delay (dai,0 ); +dev_dbg (component->dev,"%s: IF0 Protocol: I2S\n" , __func__); + conf2_val |=BIT (AB8500_DIGIFCONF2_IF0FORMAT1 ) | +BIT (AB8500_DIGIFCONF2_IF0DEL );break ;case SND_SOC_DAIFMT_DSP_A :/* L data MSB after FRM LRC */ -dev_dbg (dai->component->dev, +dev_dbg (component->dev,"%s: IF0 Protocol: DSP A (TDM)\n" , __func__); - val |=BIT (AB8500_DIGIFCONF2_IF0FORMAT0 ); -ab8500_audio_set_bit_delay (dai,1 ); + conf2_val |=BIT (AB8500_DIGIFCONF2_IF0FORMAT0 ) | +BIT (AB8500_DIGIFCONF2_IF0DEL );break ;case SND_SOC_DAIFMT_DSP_B :/* L data MSB during FRM LRC */ -dev_dbg (dai->component->dev, +dev_dbg (component->dev,"%s: IF0 Protocol: DSP B (TDM)\n" , __func__); - val |=BIT (AB8500_DIGIFCONF2_IF0FORMAT0 ); -ab8500_audio_set_bit_delay (dai,0 ); + conf2_val |=BIT (AB8500_DIGIFCONF2_IF0FORMAT0 );break ;default : -dev_err (dai->component->dev, +dev_err (component->dev,"%s: ERROR: Unsupported format (0x%x)!\n" , __func__, fmt &SND_SOC_DAIFMT_FORMAT_MASK );return -EINVAL ;
@@ -1889,39 +1831,50 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)switch (fmt &SND_SOC_DAIFMT_INV_MASK ) {case SND_SOC_DAIFMT_NB_NF :/* normal bit clock + frame */ -dev_dbg (dai->component->dev, +dev_dbg (component->dev,"%s: IF0: Normal bit clock, normal frame\n" , __func__);break ;case SND_SOC_DAIFMT_NB_IF :/* normal BCLK + inv FRM */ -dev_dbg (dai->component->dev, +dev_dbg (component->dev,"%s: IF0: Normal bit clock, inverted frame\n" , __func__); - val |=BIT (AB8500_DIGIFCONF2_FSYNC0P ); + conf2_val |=BIT (AB8500_DIGIFCONF2_FSYNC0P );break ;case SND_SOC_DAIFMT_IB_NF :/* invert BCLK + nor FRM */ -dev_dbg (dai->component->dev, +dev_dbg (component->dev,"%s: IF0: Inverted bit clock, normal frame\n" , __func__); - val |=BIT (AB8500_DIGIFCONF2_BITCLK0P ); + conf2_val |=BIT (AB8500_DIGIFCONF2_BITCLK0P );break ;case SND_SOC_DAIFMT_IB_IF :/* invert BCLK + FRM */ -dev_dbg (dai->component->dev, +dev_dbg (component->dev,"%s: IF0: Inverted bit clock, inverted frame\n" , __func__); - val |=BIT (AB8500_DIGIFCONF2_FSYNC0P ); - val |=BIT (AB8500_DIGIFCONF2_BITCLK0P ); + conf2_val |=BIT (AB8500_DIGIFCONF2_FSYNC0P ) | +BIT (AB8500_DIGIFCONF2_BITCLK0P );break ;default : -dev_err (dai->component->dev, +dev_err (component->dev,"%s: ERROR: Unsupported INV mask 0x%x\n" , __func__, fmt &SND_SOC_DAIFMT_INV_MASK );return -EINVAL ; } -snd_soc_component_update_bits (component,AB8500_DIGIFCONF2 , mask, val); + ret =snd_soc_component_update_bits (component,AB8500_DIGIFCONF3 , + conf3_mask, conf3_val); +if (ret <0 ) +return ret; -return 0 ; + ret =snd_soc_component_update_bits (component,AB8500_DIGIFCONF1 , + conf1_mask, conf1_val); +if (ret <0 ) +return ret; + + ret =snd_soc_component_update_bits (component,AB8500_DIGIFCONF2 , + conf2_mask, conf2_val); + +return ret <0 ? ret :0 ; } static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 120/733 ASoC: ab8500: Validate and program TDM slots correctly
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 85cef7e2004ef5c1a715feaddb55d3f3d27bac0a ]
The interface clock ratio is selected from the slot count alone, ffs() and fls() produce one-based hardware slot numbers, eight-channel mode does not program any mappings, and all register errors are ignored. Invalid masks can also leave a partially programmed interface.
Validate the complete configuration first, derive the supported BCLK ratio from slots times slot width, use zero-based slot indices, program deterministic eight-channel maps, and propagate register failures.
--- sound/soc/codecs/ab8500-codec.c | 216 ++++++++++++++++++-------------- 1 file changed, 123 insertions(+), 93 deletions(-)
sound/soc/codecs/ab8500-codec.c +123 -93
@@ -1882,23 +1882,27 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai, int slots, int slot_width) {struct snd_soc_component *component = dai->component; -unsigned int val, mask, slot, slots_active; +unsigned int active_mask, clock_ratio, slot, value, ad_out, reg; +unsigned int tx_active, rx_active; +unsigned int conf1_val, conf2_val; +unsigned int mask; +int channel, ret; mask =BIT (AB8500_DIGIFCONF2_IF0WL0 ) |BIT (AB8500_DIGIFCONF2_IF0WL1 ); - val =0 ; + conf2_val =0 ;switch (slot_width) {case 16 :break ;case 20 : - val |=BIT (AB8500_DIGIFCONF2_IF0WL0 ); + conf2_val |=BIT (AB8500_DIGIFCONF2_IF0WL0 );break ;case 24 : - val |=BIT (AB8500_DIGIFCONF2_IF0WL1 ); + conf2_val |=BIT (AB8500_DIGIFCONF2_IF0WL1 );break ;case 32 : - val |=BIT (AB8500_DIGIFCONF2_IF0WL1 ) | + conf2_val |=BIT (AB8500_DIGIFCONF2_IF0WL1 ) |BIT (AB8500_DIGIFCONF2_IF0WL0 );break ;default :
@@ -1907,27 +1911,11 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,return -EINVAL ; } -dev_dbg (dai->component->dev,"%s: IF0 slot-width: %d bits.\n" , - __func__, slot_width); -snd_soc_component_update_bits (component,AB8500_DIGIFCONF2 , mask, val); - -/* Setup TDM clocking according to slot count */ -dev_dbg (dai->component->dev,"%s: Slots, total: %d\n" , __func__, slots); - mask =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS0 ) | -BIT (AB8500_DIGIFCONF1_IF0BITCLKOS1 );switch (slots) {case 2 : - val =AB8500_MASK_NONE ; -break ;case 4 : - val =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS0 ); -break ;case 8 : - val =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS1 ); -break ;case 16 : - val =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS0 ) | -BIT (AB8500_DIGIFCONF1_IF0BITCLKOS1 );break ;default : dev_err(dai->component->dev,
@@ -1935,92 +1923,134 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai, __func__, slots);return -EINVAL ; } -snd_soc_component_update_bits (component,AB8500_DIGIFCONF1 , mask, val); - -/* Setup TDM DA according to active tx slots */ -if (tx_mask & ~0xff ) -return -EINVAL ; - - mask =AB8500_DASLOTCONFX_SLTODAX_MASK ; - tx_mask = tx_mask <<AB8500_DA_DATA0_OFFSET ; - slots_active =hweight32 (tx_mask); - -dev_dbg (dai->component->dev,"%s: Slots, active, TX: %d\n" , __func__, - slots_active); - -switch (slots_active) { -case 0 : + clock_ratio = slots * slot_width; +switch (clock_ratio) { +case 32 : + conf1_val =0 ;break ; -case 1 : - slot =ffs (tx_mask); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF1 , mask, slot); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF3 , mask, slot); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF2 , mask, slot); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF4 , mask, slot); +case 64 : + conf1_val =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS0 );break ; -case 2 : - slot =ffs (tx_mask); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF1 , mask, slot); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF3 , mask, slot); - slot =fls (tx_mask); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF2 , mask, slot); -snd_soc_component_update_bits (component,AB8500_DASLOTCONF4 , mask, slot); +case 128 : + conf1_val =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS1 );break ; -case 8 : -dev_dbg (dai->component->dev, -"%s: In 8-channel mode DA-from-slot mapping is set manually." , - __func__); +case 256 : + conf1_val =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS0 ) | +BIT (AB8500_DIGIFCONF1_IF0BITCLKOS1 );break ;default : -dev_err (dai->component->dev, -"%s: Unsupported number of active TX-slots (%d)!\n" , - __func__, slots_active); +dev_err (component->dev,"%s: Unsupported BCLK ratio (%u)!\n" , + __func__, clock_ratio);return -EINVAL ; } -/* Setup TDM AD according to active RX-slots */ + active_mask =GENMASK (min (slots,8 ) -1 ,0 ); +if ((tx_mask | rx_mask) & ~active_mask) { +dev_err (component->dev,"%s: Slot mask exceeds slot count\n" , + __func__); +return -EINVAL ; + } -if (rx_mask & ~0xff ) + tx_active =hweight32 (tx_mask); + rx_active =hweight32 (rx_mask); +if (tx_active !=0 && tx_active !=1 && tx_active !=2 && + tx_active !=8 ) { +dev_err (component->dev,"%s: Unsupported active TX slots (%u)!\n" , + __func__, tx_active); +return -EINVAL ; + } +if (rx_active !=0 && rx_active !=1 && rx_active !=2 && + rx_active !=8 ) { +dev_err (component->dev,"%s: Unsupported active RX slots (%u)!\n" , + __func__, rx_active);return -EINVAL ; + } + +dev_dbg (component->dev, +"%s: %d slots of %d bits, TX active: %u, RX active: %u\n" , + __func__, slots, slot_width, tx_active, rx_active); - rx_mask = rx_mask <<AB8500_AD_DATA0_OFFSET ; - slots_active =hweight32 (rx_mask); + ret =snd_soc_component_update_bits (component,AB8500_DIGIFCONF2 , + mask, conf2_val); +if (ret <0 ) +return ret; -dev_dbg (dai->component->dev,"%s: Slots, active, RX: %d\n" , __func__, - slots_active); + mask =BIT (AB8500_DIGIFCONF1_IF0BITCLKOS0 ) | +BIT (AB8500_DIGIFCONF1_IF0BITCLKOS1 ); + ret =snd_soc_component_update_bits (component,AB8500_DIGIFCONF1 , + mask, conf1_val); +if (ret <0 ) +return ret; -switch (slots_active) { -case 0 : -break ; -case 1 : - slot =ffs (rx_mask); -snd_soc_component_update_bits (component,AB8500_ADSLOTSEL (slot), -AB8500_MASK_SLOT (slot), -AB8500_ADSLOTSELX_AD_OUT_TO_SLOT (AB8500_AD_OUT3 , slot)); -break ; -case 2 : - slot =ffs (rx_mask); -snd_soc_component_update_bits (component, -AB8500_ADSLOTSEL (slot), -AB8500_MASK_SLOT (slot), -AB8500_ADSLOTSELX_AD_OUT_TO_SLOT (AB8500_AD_OUT3 , slot)); - slot =fls (rx_mask); -snd_soc_component_update_bits (component, -AB8500_ADSLOTSEL (slot), -AB8500_MASK_SLOT (slot), -AB8500_ADSLOTSELX_AD_OUT_TO_SLOT (AB8500_AD_OUT2 , slot)); -break ; -case 8 : -dev_dbg (dai->component->dev, -"%s: In 8-channel mode AD-to-slot mapping is set manually." , - __func__); -break ; -default : -dev_err (dai->component->dev, -"%s: Unsupported number of active RX-slots (%d)!\n" , - __func__, slots_active); -return -EINVAL ; + mask =AB8500_DASLOTCONFX_SLTODAX_MASK ; +if (tx_active ==1 || tx_active ==2 ) { + slot =__ffs (tx_mask) +AB8500_DA_DATA0_OFFSET ; + reg =AB8500_DASLOTCONF1 ; + ret =snd_soc_component_update_bits (component, reg, mask, slot); +if (ret <0 ) +return ret; + reg =AB8500_DASLOTCONF3 ; + ret =snd_soc_component_update_bits (component, reg, mask, slot); +if (ret <0 ) +return ret; + +if (tx_active ==2 ) + slot =__fls (tx_mask) +AB8500_DA_DATA0_OFFSET ; + reg =AB8500_DASLOTCONF2 ; + ret =snd_soc_component_update_bits (component, reg, mask, slot); +if (ret <0 ) +return ret; + reg =AB8500_DASLOTCONF4 ; + ret =snd_soc_component_update_bits (component, reg, mask, slot); +if (ret <0 ) +return ret; + }else if (tx_active ==8 ) { + channel =0 ; +for (slot =0 ; slot <8 ; slot++) { +if (!(tx_mask &BIT (slot))) +continue ; + reg =AB8500_DASLOTCONF1 + channel++; + value = slot +AB8500_DA_DATA0_OFFSET ; + ret =snd_soc_component_update_bits (component, reg, mask, value); +if (ret <0 ) +return ret; + } + } + +if (rx_active ==1 || rx_active ==2 ) { + slot =__ffs (rx_mask) +AB8500_AD_DATA0_OFFSET ; + value =AB8500_ADSLOTSELX_AD_OUT_TO_SLOT (AB8500_AD_OUT3 , + slot); + reg =AB8500_ADSLOTSEL (slot); + mask =AB8500_MASK_SLOT (slot); + ret =snd_soc_component_update_bits (component, reg, mask, value); +if (ret <0 ) +return ret; + +if (rx_active ==2 ) { + slot =__fls (rx_mask) +AB8500_AD_DATA0_OFFSET ; + value =AB8500_ADSLOTSELX_AD_OUT_TO_SLOT (AB8500_AD_OUT2 , + slot); + reg =AB8500_ADSLOTSEL (slot); + mask =AB8500_MASK_SLOT (slot); + ret =snd_soc_component_update_bits (component, reg, mask, value); +if (ret <0 ) +return ret; + } + }else if (rx_active ==8 ) { + channel =0 ; +for (slot =0 ; slot <8 ; slot++) { +if (!(rx_mask &BIT (slot))) +continue ; + ad_out =AB8500_AD_OUT1 + channel++; + value =AB8500_ADSLOTSELX_AD_OUT_TO_SLOT (ad_out, slot); + reg =AB8500_ADSLOTSEL (slot); + mask =AB8500_MASK_SLOT (slot); + ret =snd_soc_component_update_bits (component, reg, mask, value); +if (ret <0 ) +return ret; + } }return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 121/733 ASoC: codecs: ab8500: Use guard() for mutex locks
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: bui duc phuc <[email protected]>
[ Upstream commit 350b7eae8e8b2bd3885a3e2424381494d6bde038 ]
Clean up the code using guard() for mutex locks.
Merely code refactoring, and no behavior change.
Stable-dep-of: ec75e653b70c ("ASoC: ab8500: Remove the nonfunctional sidetone apply control")
--- sound/soc/codecs/ab8500-codec.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-)
sound/soc/codecs/ab8500-codec.c +5 -7
@@ -14,6 +14,7 @@* for ST-Ericsson. */ +#include <linux/cleanup.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/device.h>
@@ -981,9 +982,8 @@ static int sid_status_control_get(struct snd_kcontrol *kcontrol,struct snd_soc_component *component =snd_kcontrol_chip (kcontrol);struct ab8500_codec_drvdata *drvdata =dev_get_drvdata (component->dev); -mutex_lock (&drvdata->ctrl_lock); +guard (mutex)(&drvdata->ctrl_lock); ucontrol->value.enumerated.item[0 ] = drvdata->sid_status; -mutex_unlock (&drvdata->ctrl_lock);return 0 ; }
@@ -1006,7 +1006,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol,return -EIO ; } -mutex_lock (&drvdata->ctrl_lock); +guard (mutex)(&drvdata->ctrl_lock); sidconf =snd_soc_component_read (component,AB8500_SIDFIRCONF );if (((sidconf &BIT (AB8500_SIDFIRCONF_FIRSIDBUSY )) !=0 )) {
@@ -1017,7 +1017,8 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol, }else { status = -EBUSY ; } - goto out; +dev_dbg (component->dev,"%s: Exit\n" , __func__); +return status; }snd_soc_component_write (component,AB8500_SIDFIRADR ,0 );
@@ -1035,9 +1036,6 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol, drvdata->sid_status =SID_FIR_CONFIGURED ; -out: -mutex_unlock (&drvdata->ctrl_lock); -dev_dbg (component->dev,"%s: Exit\n" , __func__);return status;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 122/733 ASoC: ab8500: Remove the nonfunctional sidetone apply control
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit ec75e653b70ce26ea68187c2069722baa80efadb ]
After the coefficient controls were removed, writing "Apply FIR" programs 128 zero coefficients and reports that the sidetone filter is configured. The associated ANC configuration DAPM pins are also now unreachable dead infrastructure.
Remove the misleading status/apply control, its private state, and the obsolete configuration-only DAPM pins. Keep the direct sidetone reset and remaining hardware controls.
--- sound/soc/codecs/ab8500-codec.c | 114 -------------------------------- 1 file changed, 114 deletions(-)
sound/soc/codecs/ab8500-codec.c +0 -114
@@ -14,17 +14,14 @@* for ST-Ericsson. */ -#include <linux/cleanup.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/device.h> #include <linux/slab.h> #include <linux/moduleparam.h> #include <linux/init.h> -#include <linux/delay.h> #include <linux/pm.h> #include <linux/platform_device.h> -#include <linux/mutex.h> #include <linux/mfd/abx500/ab8500.h> #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500-sysctrl.h>
@@ -54,32 +51,9 @@/* Macrocell register definitions */ #define AB8500_GPIO_DIR4_REG 0x13/* Bank AB8500_MISC */ -/* Nr of FIR/IIR-coeff banks in ANC-block */ -#define AB8500_NR_OF_ANC_COEFF_BANKS 2 - -/* Minimum duration to keep ANC IIR Init bit high or -low before proceeding with the configuration sequence */ -#define AB8500_ANC_SM_DELAY 2000 - -/* Sidetone states */ -static const char *const enum_sid_state[] = { -"Unconfigured" , -"Apply FIR" , -"FIR is configured" , -}; -enum sid_state { -SID_UNCONFIGURED =0 , -SID_APPLY_FIR =1 , -SID_FIR_CONFIGURED =2 , -}; -/* Private data for AB8500 device-driver */ struct ab8500_codec_drvdata {struct regmap *regmap; -struct mutex ctrl_lock; - -/* Sidetone */ -enum sid_state sid_status; };static inline const char *amic_micbias_str (enum amic_micbias micbias)
@@ -642,9 +616,6 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = { NULL, 0),/* Acoustical Noise Cancellation path */ -SND_SOC_DAPM_INPUT ("ANC Configure Input" ), -SND_SOC_DAPM_OUTPUT ("ANC Configure Output" ), -SND_SOC_DAPM_MUX ("ANC Source" ,SND_SOC_NOPM ,0 ,0 , dapm_anc_in_select),
@@ -687,10 +658,6 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { {"Main Supply", NULL, "Audio Power"}, {"Main Supply", NULL, "Audio Analog Power"}, -/* ANC Configure */ - {"ANC Configure Input", NULL, "Main Supply"}, - {"ANC Configure Output", NULL, "ANC Configure Input"}, -/* Powerup charge pump if DA1/2 is in use */ {"DA_IN1", NULL, "ab8500_0p"},
@@ -972,75 +939,6 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes_mic2_vamicx[] = { {"MIC2 V-AMICx Enable", NULL, "V-AMIC2"}, }; -/* -* Control-events -*/ - -static int sid_status_control_get (struct snd_kcontrol *kcontrol, -struct snd_ctl_elem_value *ucontrol) -{ -struct snd_soc_component *component =snd_kcontrol_chip (kcontrol); -struct ab8500_codec_drvdata *drvdata =dev_get_drvdata (component->dev); - -guard (mutex)(&drvdata->ctrl_lock); - ucontrol->value.enumerated.item[0 ] = drvdata->sid_status; - -return 0 ; -} - -/* Write sidetone FIR-coefficients configuration sequence */ -static int sid_status_control_put (struct snd_kcontrol *kcontrol, -struct snd_ctl_elem_value *ucontrol) -{ -struct snd_soc_component *component =snd_kcontrol_chip (kcontrol); -struct ab8500_codec_drvdata *drvdata =dev_get_drvdata (component->dev); -unsigned int param, sidconf; -int status =1 ; - -dev_dbg (component->dev,"%s: Enter\n" , __func__); - -if (ucontrol->value.enumerated.item[0 ] !=SID_APPLY_FIR ) { -dev_err (component->dev, -"%s: ERROR: This control supports '%s' only!\n" , - __func__, enum_sid_state[SID_APPLY_FIR ]); -return -EIO ; - } - -guard (mutex)(&drvdata->ctrl_lock); - - sidconf =snd_soc_component_read (component,AB8500_SIDFIRCONF ); -if (((sidconf &BIT (AB8500_SIDFIRCONF_FIRSIDBUSY )) !=0 )) { -if ((sidconf &BIT (AB8500_SIDFIRCONF_ENFIRSIDS )) ==0 ) { -dev_err (component->dev,"%s: Sidetone busy while off!\n" , - __func__); - status = -EPERM ; - }else { - status = -EBUSY ; - } -dev_dbg (component->dev,"%s: Exit\n" , __func__); -return status; - } - -snd_soc_component_write (component,AB8500_SIDFIRADR ,0 ); - -for (param =0 ; param <AB8500_SID_FIR_COEFFS ; param++) { -snd_soc_component_write (component,AB8500_SIDFIRCOEF1 ,0 ); -snd_soc_component_write (component,AB8500_SIDFIRCOEF2 ,0 ); - } - -snd_soc_component_update_bits (component,AB8500_SIDFIRADR , -BIT (AB8500_SIDFIRADR_FIRSIDSET ), -BIT (AB8500_SIDFIRADR_FIRSIDSET )); -snd_soc_component_update_bits (component,AB8500_SIDFIRADR , -BIT (AB8500_SIDFIRADR_FIRSIDSET ),0 ); - - drvdata->sid_status =SID_FIR_CONFIGURED ; - -dev_dbg (component->dev,"%s: Exit\n" , __func__); - -return status; -} -/* * Controls - Non-DAPM ASoC */
@@ -1324,9 +1222,6 @@ static SOC_ENUM_SINGLE_DECL(soc_enum_bfifomast,AB8500_FIFOCONF3 ,AB8500_FIFOCONF3_BFIFOMAST_SHIFT , enum_slavemaster); -/* Sidetone */ -static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_sidstate, enum_sid_state); -/* ANC */ static struct snd_kcontrol_new ab8500_ctrls[] = {
@@ -1617,8 +1512,6 @@ static struct snd_kcontrol_new ab8500_ctrls[] = { AB8500_ANC_WARP_DELAY_MIN, AB8500_ANC_WARP_DELAY_MAX, 0),/* Sidetone */ -SOC_ENUM_EXT ("Sidetone Status" , soc_enum_sidstate, - sid_status_control_get, sid_status_control_put), SOC_SINGLE_STROBE("Sidetone Reset", AB8500_SIDFIRADR, AB8500_SIDFIRADR_FIRSIDSET, 0), };
@@ -2145,10 +2038,8 @@ static void ab8500_codec_of_probe(struct device *dev, struct device_node *np,static int ab8500_codec_probe (struct snd_soc_component *component) { -struct snd_soc_dapm_context *dapm =snd_soc_component_to_dapm (component);struct device *dev = component->dev;struct device_node *np = dev->of_node; -struct ab8500_codec_drvdata *drvdata =dev_get_drvdata (dev);struct ab8500_codec_platform_data codec_pdata;int status;
@@ -2181,10 +2072,6 @@ static int ab8500_codec_probe(struct snd_soc_component *component)snd_soc_component_write (component,AB8500_SHORTCIRCONF ,BIT (AB8500_SHORTCIRCONF_HSZCDDIS )); -snd_soc_dapm_disable_pin (dapm,"ANC Configure Input" ); - -mutex_init (&drvdata->ctrl_lock); -return status; }
@@ -2213,7 +2100,6 @@ static int ab8500_codec_driver_probe(struct platform_device *pdev) GFP_KERNEL);if (!drvdata)return -ENOMEM ; - drvdata->sid_status =SID_UNCONFIGURED ;dev_set_drvdata (&pdev->dev, drvdata); drvdata->regmap = devm_regmap_init(&pdev->dev, NULL, &pdev->dev,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 123/733 ASoC: ab8500: Skip missing DMIC GPIOs on AB8505
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 711178754287db3fd0f7accff3c2a7575f8873b7 ]
GPIO27, GPIO29 and GPIO31 provide the digital microphone clock outputs on AB8500, but these GPIOs do not exist on AB8505. The shared codec driver nevertheless accesses their direction register while setting up every AB8505 codec.
Identify the parent MFD device and leave the nonexistent GPIOs untouched on AB8505.
--- sound/soc/codecs/ab8500-codec.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-)
sound/soc/codecs/ab8500-codec.c +17 -14
@@ -1540,6 +1540,8 @@ static int ab8500_audio_init_audioblock(struct snd_soc_component *component)static int ab8500_audio_setup_mics (struct snd_soc_component *component,struct amic_settings *amics) { +struct device *dev = component->dev; +struct ab8500 *ab8500 =dev_get_drvdata (dev->parent);struct snd_soc_dapm_context *dapm =snd_soc_component_to_dapm (component);u8 value8;unsigned int value;
@@ -1548,20 +1550,21 @@ static int ab8500_audio_setup_mics(struct snd_soc_component *component,dev_dbg (component->dev,"%s: Enter.\n" , __func__); -/* Set DMic-clocks to outputs */ - status =abx500_get_register_interruptible (component->dev,AB8500_MISC , -AB8500_GPIO_DIR4_REG , - &value8); -if (status <0 ) -return status; - value = value8 |GPIO27_DIR_OUTPUT |GPIO29_DIR_OUTPUT | -GPIO31_DIR_OUTPUT ; - status =abx500_set_register_interruptible (component->dev, -AB8500_MISC , -AB8500_GPIO_DIR4_REG , - value); -if (status <0 ) -return status; +/* Set DMic-clocks to outputs; these GPIOs do not exist on AB8505. */ +if (!is_ab8505 (ab8500)) { + status =abx500_get_register_interruptible (dev,AB8500_MISC , +AB8500_GPIO_DIR4_REG , + &value8); +if (status <0 ) +return status; + value = value8 |GPIO27_DIR_OUTPUT |GPIO29_DIR_OUTPUT | +GPIO31_DIR_OUTPUT ; + status =abx500_set_register_interruptible (dev,AB8500_MISC , +AB8500_GPIO_DIR4_REG , + value); +if (status <0 ) +return status; + }/* Attach regulators to AMic DAPM-paths */ dev_dbg(component->dev, "%s: Mic 1a regulator: %s\n", __func__,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 124/733 drm/pagemap: Prevent double migration of device pages
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Arvind Yadav <[email protected]>
[ Upstream commit c4126f1db36e6b2e1c79b0e30a8a2de91c568f4c ]
A device-private folio migrated to system memory by a CPU fault can remain reachable through the raw-PFN eviction path until migration finalization drops the source reference.
If eviction selects the same device-private folio during this window, it can attempt to migrate the folio again. The second migration can leave an uncharged folio on an LRU list, causing folio_lruvec_lock_irqsave() to retry indefinitely and resulting in a soft lockup and RCU stall.
Mark successfully migrated device-private folios using a low bit of their zone_device_data before migration finalization. Make both CPU-fault and raw-PFN migration paths skip device-private folios carrying this flag.
Mask the flag when retrieving the drm_pagemap_zdd pointer and preserve it when a device-private folio is split. Keeping the state on the physical folio also avoids depending on a virtual address that may change before a fault occurs.
v2:
- Replace the retired-PFN XArray with an embedded bitmap. (Matthew Brost)
- Mark every base page covered by a migrated folio so retirement remains valid if the folio is later split.
v3:
- Store the migrated state in a low bit of zone_device_data instead of adding virtual-range and bitmap tracking to the ZDD. (Matthew Brost)
- Mask the flag when retrieving the ZDD and preserve it when splitting a folio.
- Drop the pre-existing fixes already covered by Matthew Brost's series:
https://patchwork.freedesktop.org/series/171651/
v4:
- Advance by the folio size only for migration entries marked with MIGRATE_PFN_COMPOUND. (Sashiko)
v5:
- Simplify ZDD flag updates and folio iteration. (Matthew Brost)
- Skip retired device-private folios in the CPU-fault path. (Matthew Brost)
- Preserve flag bits while taking a new ZDD reference for split folios.
v6:
- Restore MIGRATE_PFN_COMPOUND-aware stepping so non-compound migration entries are processed one at a time. (Sashiko)
- Drop the pre-existing fixes already covered by Matthew Brost's series:
https://patchwork.freedesktop.org/series/171651/
The lockup was observed as:
[10109.860465] watchdog: BUG: soft lockup - CPU#9 stuck for 26s! [kworker/u65:5:6557] [10109.860524] Tainted: [S]=CPU_OUT_OF_SPEC, [O]=OOT_MODULE [10109.860524] Hardware name: ASUS System Product Name/PRIME Z790-P WIFI, BIOS 0812 02/24/2023 [10109.860525] Workqueue: xe_page_fault_work_queue xe_pagefault_queue_work [xe] [10109.860644] RIP: 0010:_raw_spin_unlock_irqrestore+0x57/0x80 [10109.860655] Call Trace: [10109.860655] <TASK> [10109.860657] folio_lruvec_lock_irqsave+0x216/0x220 [10109.860661] ? __pfx_lru_add+0x10/0x10 [10109.860665] folio_batch_move_lru+0xc8/0x450 [10109.860670] ? lock_acquire+0xc4/0x2d0 [10109.860674] ? __folio_batch_add_and_move+0x60/0x2e0 [10109.860677] ? folio_migrate_mapping+0xa6/0x110 [10109.860679] ? folio_migrate_flags+0x13b/0x1b0 [10109.860681] ? __pfx_lru_add+0x10/0x10 [10109.860683] __folio_batch_add_and_move+0xe7/0x2e0 [10109.860685] ? dma_iova_try_alloc+0xb0/0x140 [10109.860689] folio_add_lru+0x64/0x80 [10109.860691] __migrate_device_finalize+0x12c/0x270 [10109.860695] migrate_device_finalize+0x10/0x20 [10109.860698] drm_pagemap_evict_to_ram+0x185/0x370 [drm_gpusvm_helper] [10109.860704] ? drm_pagemap_evict_to_ram+0x96/0x370 [drm_gpusvm_helper] [10109.860709] xe_svm_bo_evict+0x15/0x20 [xe] [10109.860819] ? xe_svm_bo_evict+0x15/0x20 [xe] [10109.860921] xe_bo_move+0x107e/0x1570 [xe] [10109.860992] ? xe_ttm_tt_create+0x168/0x340 [xe] [10109.861059] ? __up_read+0x98/0x2b0 [10109.861061] ? lock_is_held_type+0xa3/0x130 [10109.861067] ttm_bo_handle_move_mem+0xe8/0x1e0 [ttm] [10109.861075] ttm_bo_evict+0x141/0x1c0 [ttm] [10109.861081] ttm_bo_evict_cb+0x9f/0x100 [ttm] [10109.861086] ttm_lru_walk_for_evict+0x84/0x190 [ttm] [10109.861091] ? xe_ttm_vram_mgr_new+0x258/0x3a0 [xe] [10109.861198] ttm_bo_alloc_resource+0x219/0x750 [ttm] [10109.861203] ? ttm_bo_alloc_resource+0xa9/0x750 [ttm] [10109.861208] ? lock_acquire+0xc4/0x2d0 [10109.861214] ttm_bo_validate+0x94/0x1c0 [ttm] [10109.861218] ? ww_mutex_trylock+0x19d/0x3d0 [10109.861219] ? _raw_write_unlock+0x22/0x50 [10109.861223] ttm_bo_init_reserved+0x17d/0x1f0 [ttm] [10109.861228] xe_bo_init_locked+0x20a/0x620 [xe] [10109.861294] ? __pfx_xe_ttm_bo_destroy+0x10/0x10 [xe] [10109.861359] ? mark_held_locks+0x46/0x90 [10109.861361] ? __create_object+0x68/0xc0 [10109.861366] __xe_bo_create_locked+0x384/0xa20 [xe] [10109.861432] ? lock_acquire+0xc4/0x2d0 [10109.861434] ? xe_drm_pagemap_populate_mm+0xd3/0x340 [xe] [10109.861542] xe_bo_create_locked+0x23/0x40 [xe] [10109.861609] xe_drm_pagemap_populate_mm+0x12e/0x340 [xe] [10109.861707] ? __lock_acquire+0x43e/0x2930 [10109.861716] drm_pagemap_populate_mm+0x74/0xe0 [drm_gpusvm_helper] [10109.861720] xe_svm_alloc_vram+0xb5/0x2c0 [xe] [10109.861817] ? seqcount_lockdep_reader_access.constprop.0+0x9f/0xc0 [10109.861819] ? ktime_get+0x23/0x130 [10109.861821] ? trace_hardirqs_on+0x22/0xe0 [10109.861823] ? seqcount_lockdep_reader_access.constprop.0+0x9f/0xc0 [10109.861826] __xe_svm_handle_pagefault+0x77d/0xbf0 [xe] [10109.861924] ? rwsem_down_write_slowpath+0x43a/0x9a0 [10109.861926] ? _raw_spin_unlock_irq+0x27/0x70 [10109.861928] ? rwsem_down_write_slowpath+0x43a/0x9a0 [10109.861929] ? trace_hardirqs_on+0x22/0xe0 [10109.861931] ? _raw_spin_unlock_irq+0x27/0x70 [10109.861933] ? rwsem_down_write_slowpath+0x459/0x9a0 [10109.861937] xe_svm_handle_pagefault+0x3d/0xb0 [xe] [10109.862030] xe_pagefault_queue_work+0x1a9/0x520 [xe] [10109.862122] process_one_work+0x239/0x730 [10109.862127] worker_thread+0x200/0x3f0 [10109.862130] ? __pfx_worker_thread+0x10/0x10 [10109.862132] kthread+0x10d/0x150 [10109.862133] ? __pfx_kthread+0x10/0x10 [10109.862135] ret_from_fork+0x3bd/0x470 [10109.862138] ? __pfx_kthread+0x10/0x10 [10109.862140] ret_from_fork_asm+0x1a/0x30 [10109.862146] </TASK>
--- drivers/gpu/drm/drm_pagemap.c | 127 ++++++++++++++++++++++++++++++++-- include/drm/drm_pagemap.h | 8 ++- 2 files changed, 129 insertions(+), 6 deletions(-)
drivers/gpu/drm/drm_pagemap.c +122 -5
@@ -1195,12 +1195,117 @@ void drm_pagemap_put(struct drm_pagemap *dpagemap) }EXPORT_SYMBOL (drm_pagemap_put); +/** +* drm_pagemap_page_get_flags() - Read flags from a device-private folio +* @page: Pointer to a page of the device-private folio +* +* Return: The DRM_PAGEMAP_ZDD_FLAG_* bits encoded in zone_device_data. +*/ +static unsigned long drm_pagemap_page_get_flags (struct page *page) +{ +struct folio *folio =page_folio (page); + +return (unsigned long )folio_zone_device_data (folio) & +DRM_PAGEMAP_ZDD_FLAG_MASK ; +} + +/** +* drm_pagemap_page_set_flags() - Set flags on a device-private folio +* @page: Pointer to a page of the device-private folio +* @flags: DRM_PAGEMAP_ZDD_FLAG_* bits to set +* +* Preserve any flags already encoded alongside the ZDD pointer. +*/ +static void drm_pagemap_page_set_flags (struct page *page, +unsigned long flags) +{ +struct folio *folio =page_folio (page); +unsigned long old; + +if (WARN_ON_ONCE (flags & ~DRM_PAGEMAP_ZDD_FLAG_MASK )) +return ; + + old = (unsigned long )folio_zone_device_data (folio); +folio_set_zone_device_data (folio, (void *)(old | flags)); +} + +/** +* drm_pagemap_retire_migrated_pages() - Record migrated device-private folios +* @src_pfns: source array after migrate_vma_pages() or migrate_device_pages() +* @npages: number of entries in @src_pfns +* +* Flag device-private folios successfully migrated to RAM before finalize +* unlocks the sources. The migrated state is stored in the physical folio, so +* it survives later folio splits and subsequent migrations can skip it. +*/ +static void drm_pagemap_retire_migrated_pages (unsigned long *src_pfns, +unsigned long npages) +{ +unsigned long i =0 ; + +while (i < npages) { +struct page *page =migrate_pfn_to_page (src_pfns[i]); +unsigned long nr =1 ; + +if (!page) { + i++; +continue ; + } + +if (src_pfns[i] &MIGRATE_PFN_COMPOUND ) + nr =folio_nr_pages (page_folio (page)); + +if ((src_pfns[i] &MIGRATE_PFN_MIGRATE ) && +is_device_private_page (page)) +drm_pagemap_page_set_flags (page, +DRM_PAGEMAP_ZDD_FLAG_MIGRATED ); + + i += nr; + } +} + +/** +* drm_pagemap_skip_retired_pages() - Skip retired device-private folios +* @src_pfns: MIGRATE_PFN-encoded source array +* @npages: number of entries in @src_pfns +* +* Skip source folios already migrated to RAM, identified by the migrated flag +* stored in the physical folio's zone_device_data. +*/ +static void drm_pagemap_skip_retired_pages (unsigned long *src_pfns, +unsigned long npages) +{ +unsigned long i =0 ; + +while (i < npages) { +struct page *page =migrate_pfn_to_page (src_pfns[i]); +unsigned long nr =1 ; + +if (!page) { + i++; +continue ; + } + +if (src_pfns[i] &MIGRATE_PFN_COMPOUND ) + nr =folio_nr_pages (page_folio (page)); + +if ((src_pfns[i] &MIGRATE_PFN_MIGRATE ) && +is_device_private_page (page) && + (drm_pagemap_page_get_flags (page) & +DRM_PAGEMAP_ZDD_FLAG_MIGRATED )) + src_pfns[i] &= ~MIGRATE_PFN_MIGRATE ; + + i += nr; + } +} +/** * drm_pagemap_evict_to_ram() - Evict GPU SVM range to RAM * @devmem_allocation: Pointer to the device memory allocation * -* Similar to __drm_pagemap_migrate_to_ram but does not require mmap lock and -* migration done via migrate_device_* functions. +* Similar to __drm_pagemap_migrate_to_ram(), but uses the +* migrate_device_* helpers and does not require the mmap lock. +* Device-private PFNs already migrated to RAM by either path are skipped. * * Return: 0 on success, negative error code on failure. */
@@ -1241,6 +1346,8 @@ int drm_pagemap_evict_to_ram(struct drm_pagemap_devmem *devmem_allocation)if (err) goto err_free; +drm_pagemap_skip_retired_pages (src, npages); + err =drm_pagemap_migrate_populate_ram_pfn (NULL ,NULL , npages, &mpages, src, dst,0 );if (err || !mpages)
@@ -1263,6 +1370,7 @@ int drm_pagemap_evict_to_ram(struct drm_pagemap_devmem *devmem_allocation)if (err)drm_pagemap_migration_unlock_put_pages (npages, dst);migrate_device_pages (src, dst, npages); +drm_pagemap_retire_migrated_pages (src, npages);migrate_device_finalize (src, dst, npages);drm_pagemap_migrate_unmap_pages (devmem_allocation->dev, pagemap_addr, dst, npages,DMA_FROM_DEVICE , &state);
@@ -1360,13 +1468,15 @@ static int __drm_pagemap_migrate_to_ram(struct vm_area_struct *vas,if (!migrate.cpages) goto err_free; +drm_pagemap_skip_retired_pages (migrate.src, npages); + ops = zdd->devmem_allocation->ops; dev = zdd->devmem_allocation->dev; err =drm_pagemap_migrate_populate_ram_pfn (vas, page, npages, &mpages, migrate.src, migrate.dst, start); -if (err) +if (err || !mpages) goto err_finalize; err = drm_pagemap_migrate_map_system_pages(dev, pagemap_addr,
@@ -1386,6 +1496,7 @@ static int __drm_pagemap_migrate_to_ram(struct vm_area_struct *vas,if (err)drm_pagemap_migration_unlock_put_pages (npages, migrate.dst);migrate_vma_pages (&migrate); +drm_pagemap_retire_migrated_pages (migrate.src, npages);migrate_vma_finalize (&migrate);if (dev) drm_pagemap_migrate_unmap_pages(dev, pagemap_addr, migrate.dst,
@@ -1438,13 +1549,19 @@ static vm_fault_t drm_pagemap_migrate_to_ram(struct vm_fault *vmf)static void drm_pagemap_folio_split (struct folio *orig_folio,struct folio *new_folio) {struct drm_pagemap_zdd *zdd; +unsigned long orig_data, new_data;if (!new_folio)return ; new_folio->pgmap = orig_folio->pgmap; - zdd =folio_zone_device_data (orig_folio); -folio_set_zone_device_data (new_folio,drm_pagemap_zdd_get (zdd)); + + orig_data = (unsigned long )folio_zone_device_data (orig_folio); + zdd = (struct drm_pagemap_zdd *)(orig_data & ~DRM_PAGEMAP_ZDD_FLAG_MASK ); + + new_data = (unsigned long )drm_pagemap_zdd_get (zdd); + new_data |= orig_data &DRM_PAGEMAP_ZDD_FLAG_MASK ; +folio_set_zone_device_data (new_folio, (void *)new_data); }static const struct dev_pagemap_ops drm_pagemap_pagemap_ops = {
include/drm/drm_pagemap.h +7 -1
@@ -2,6 +2,7 @@#ifndef _DRM_PAGEMAP_H_#define _DRM_PAGEMAP_H_ +#include <linux/bits.h> #include <linux/dma-direction.h> #include <linux/hmm.h> #include <linux/memremap.h>
@@ -339,6 +340,9 @@ struct drm_pagemap_migrate_details {#if IS_ENABLED (CONFIG_ZONE_DEVICE ) +#define DRM_PAGEMAP_ZDD_FLAG_MIGRATED BIT(0) +#define DRM_PAGEMAP_ZDD_FLAG_MASK DRM_PAGEMAP_ZDD_FLAG_MIGRATED + int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation, struct mm_struct *mm, unsigned long start, unsigned long end,
@@ -373,7 +377,9 @@ static inline struct drm_pagemap_zdd *drm_pagemap_page_zone_device_data(struct p {struct folio *folio =page_folio (page); -return folio_zone_device_data (folio); +return (struct drm_pagemap_zdd *) + ((unsigned long )folio_zone_device_data (folio) & + ~DRM_PAGEMAP_ZDD_FLAG_MASK ); }#else
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 125/733 drm/pagemap: Reset migration page count on eviction retry
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Arvind Yadav <[email protected]>
[ Upstream commit 8eae39cd0adf28ba81a46090b10484cf402c0ac8 ]
drm_pagemap_evict_to_ram() may retry eviction, but mpages retains the count from the previous attempt. A retry can therefore continue to the copy path even when no RAM pages were populated.
Reset mpages at the retry label so it reflects only the current attempt.
--- drivers/gpu/drm/drm_pagemap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/gpu/drm/drm_pagemap.c +2 -1
@@ -1313,7 +1313,7 @@ int drm_pagemap_evict_to_ram(struct drm_pagemap_devmem *devmem_allocation) {const struct drm_pagemap_devmem_ops *ops = devmem_allocation->ops;struct drm_pagemap_iova_state state = {}; -unsigned long npages, mpages =0 ; +unsigned long npages, mpages;struct page **pages;unsigned long *src, *dst;struct drm_pagemap_addr *pagemap_addr;
@@ -1324,6 +1324,7 @@ int drm_pagemap_evict_to_ram(struct drm_pagemap_devmem *devmem_allocation) npages = devmem_allocation->size >>PAGE_SHIFT ; retry: + mpages =0 ;if (!mmget_not_zero (devmem_allocation->mm))return -EFAULT ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 126/733 net: ethernet: oa_tc6: Handle the OA TC6 SPI protected mode
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ciprian Regus <[email protected]>
[ Upstream commit 7d0e4c4b8c85d8ea2c77a90e1f7a7f74ce531e52 ]
Implement the OA TC6 standard defined protected mode for control (register access) transactions. In addition to the current register access formats the oa_tc6 driver handles, 1's complement values of the data field are included (by both the host and the MACPHY) in the SPI transfer frames. This feature acts as an integrity check.
Control write transactions look like this:
|<- 32 bits ->|<--- data_size --->|<- 32 bits ->|
MOSI: | ctrl header | reg write data | ignored |
MISO: | (discard) | echoed ctrl hdr | echoed data | data_size (LEN = number of registers to read in a sequence):
Unprotected: 32 x (LEN + 1) bits
Protected: 2 x 32 x (LEN + 1) bits
Control read transaction:
|<- 32 bits ->|<--- 32 bits --> |<- data_size ->|
MOSI: | ctrl header | ignored ... |
MISO: | (discard) | echoed ctrl hdr | reg read data | data_size (LEN = number of registers to read in a sequence):
Unprotected: 32 x (LEN + 1) bits
Protected: 2 x 32 x (LEN + 1) bits
Register data format ("reg write data" and "reg read data"):
Unprotected:
| W1 (normal) | W2 (normal) | ... | Wx (normal) |
Protected:
| W1 (normal) | W1 (complement) | ... | Wx (normal) | Wx (complement)|The protected mode state can be read from the bit 5 of CONFIG0 (0x4) register, and this setting is usually only configured during the MACPHY's reset (depending on the device it can be done by setting the state of a pin). We can read the protected mode configuration before any other register access and since the SPI transfer is initially sized for an unprotected read, the MACPHY's complement words are never clocked out and no checking is required. The data transactions (Ethernet frames) remain unchanged.
Stable-dep-of: 5443d9c4f55d ("net: ethernet: oa_tc6: Protect skb pointer used by two different kernel instances")
--- drivers/net/ethernet/oa_tc6.c | 93 ++++++++++++++++++++++++++++------- 1 file changed, 76 insertions(+), 17 deletions(-)
drivers/net/ethernet/oa_tc6.c +76 -17
@@ -25,6 +25,7 @@#define OA_TC6_REG_CONFIG0 0x0004#define CONFIG0_SYNC BIT(15)#define CONFIG0_ZARFE_ENABLE BIT(12) +#define CONFIG0_PROTE BIT(5)/* Status Register #0 */ #define OA_TC6_REG_STATUS0 0x0008
@@ -90,14 +91,17 @@#define OA_TC6_PHY_C45_AUTO_NEG_MMS5 5/* MMD 7 */ #define OA_TC6_PHY_C45_POWER_UNIT_MMS6 6/* MMD 13 */ +#define OA_TC6_CTRL_PROT_REPLY_SIZE 4#define OA_TC6_CTRL_HEADER_SIZE 4#define OA_TC6_CTRL_REG_VALUE_SIZE 4#define OA_TC6_CTRL_IGNORED_SIZE 4#define OA_TC6_CTRL_MAX_REGISTERS 128 -#define OA_TC6_CTRL_SPI_BUF_SIZE (OA_TC6_CTRL_HEADER_SIZE +\ - (OA_TC6_CTRL_MAX_REGISTERS *\ - OA_TC6_CTRL_REG_VALUE_SIZE) +\ - OA_TC6_CTRL_IGNORED_SIZE) +#define OA_TC6_CTRL_SPI_BUF_SIZE (OA_TC6_CTRL_HEADER_SIZE +\ + (OA_TC6_CTRL_MAX_REGISTERS *\ + (OA_TC6_CTRL_REG_VALUE_SIZE +\ + OA_TC6_CTRL_PROT_REPLY_SIZE)) +\ + OA_TC6_CTRL_IGNORED_SIZE) +#define OA_TC6_CHUNK_PAYLOAD_SIZE 64#define OA_TC6_DATA_HEADER_SIZE 4#define OA_TC6_CHUNK_SIZE (OA_TC6_DATA_HEADER_SIZE +\
@@ -130,6 +134,7 @@ struct oa_tc6 {bool rx_buf_overflow;bool int_flag;bool disable_traffic; +bool prot_ctrl; };enum oa_tc6_header_type {
@@ -213,25 +218,36 @@ static void oa_tc6_update_ctrl_write_data(struct oa_tc6 *tc6, u32 value[], {__be32 *tx_buf = tc6->spi_ctrl_tx_buf +OA_TC6_CTRL_HEADER_SIZE ; -for (int i =0 ; i < length; i++) +for (int i =0 ; i < length; i++) { *tx_buf++ =cpu_to_be32 (value[i]); +if (tc6->prot_ctrl) + *tx_buf++ =cpu_to_be32 (~value[i]); + } } -static u16 oa_tc6_calculate_ctrl_buf_size (u8 length) +static u16 oa_tc6_calculate_ctrl_buf_size (u8 length,bool ctrl_prot) { +u32 reply_size =OA_TC6_CTRL_REG_VALUE_SIZE ; + +if (ctrl_prot) + reply_size +=OA_TC6_CTRL_PROT_REPLY_SIZE ; +/* Control command consists 4 bytes header + 4 bytes register value for -* each register + 4 bytes ignored value. +* each register (+ 4 bytes for the register value complement in case +* protected mode is used) + 4 bytes ignored value. */ -return OA_TC6_CTRL_HEADER_SIZE +OA_TC6_CTRL_REG_VALUE_SIZE * length + +return OA_TC6_CTRL_HEADER_SIZE + reply_size * length +OA_TC6_CTRL_IGNORED_SIZE ; }static void oa_tc6_prepare_ctrl_spi_buf (struct oa_tc6 *tc6,u32 address,u32 value[],u8 length, -enum oa_tc6_register_op reg_op) +enum oa_tc6_register_op reg_op, +u16 buf_size) {__be32 *tx_buf = tc6->spi_ctrl_tx_buf; +memset (tx_buf,0 , buf_size); *tx_buf =oa_tc6_prepare_ctrl_header (address, length, reg_op);if (reg_op ==OA_TC6_CTRL_REG_WRITE )
@@ -254,10 +270,12 @@ static int oa_tc6_check_ctrl_write_reply(struct oa_tc6 *tc6, u8 size)return 0 ; } -static int oa_tc6_check_ctrl_read_reply (struct oa_tc6 *tc6,u8 size) +static int oa_tc6_check_ctrl_read_reply (struct oa_tc6 *tc6,u8 length) { -u32 *rx_buf = tc6->spi_ctrl_rx_buf +OA_TC6_CTRL_IGNORED_SIZE ; -u32 *tx_buf = tc6->spi_ctrl_tx_buf; +__be32 *rx_buf = tc6->spi_ctrl_rx_buf +OA_TC6_CTRL_IGNORED_SIZE ; +__be32 *tx_buf = tc6->spi_ctrl_tx_buf; +u32 complement; +u32 reply;/* The echoed control read header must match with the one that was * transmitted.
@@ -265,6 +283,20 @@ static int oa_tc6_check_ctrl_read_reply(struct oa_tc6 *tc6, u8 size)if (*tx_buf != *rx_buf)return -EPROTO ; +if (tc6->prot_ctrl) { +/* Skip past the echoed header to the value/complement pairs */ + rx_buf +=1 ; +for (int i =0 ; i < length; i++) { + reply =be32_to_cpu (rx_buf[0 ]); + complement =be32_to_cpu (rx_buf[1 ]); + +if (complement != ~reply) +return -EPROTO ; + + rx_buf +=2 ; + } + } +return 0 ; }
@@ -274,8 +306,13 @@ static void oa_tc6_copy_ctrl_read_data(struct oa_tc6 *tc6, u32 value[],__be32 *rx_buf = tc6->spi_ctrl_rx_buf +OA_TC6_CTRL_IGNORED_SIZE +OA_TC6_CTRL_HEADER_SIZE ; -for (int i =0 ; i < length; i++) +for (int i =0 ; i < length; i++) { value[i] =be32_to_cpu (*rx_buf++); + +/* skip complement word */ +if (tc6->prot_ctrl) + rx_buf++; + } } static int oa_tc6_perform_ctrl(struct oa_tc6 *tc6, u32 address, u32 value[],
@@ -284,10 +321,10 @@ static int oa_tc6_perform_ctrl(struct oa_tc6 *tc6, u32 address, u32 value[],u16 size;int ret; -/* Prepare control command and copy to SPI control buffer */ -oa_tc6_prepare_ctrl_spi_buf (tc6, address, value, length, reg_op); + size =oa_tc6_calculate_ctrl_buf_size (length, tc6->prot_ctrl); - size =oa_tc6_calculate_ctrl_buf_size (length); +/* Prepare control command and copy to SPI control buffer */ +oa_tc6_prepare_ctrl_spi_buf (tc6, address, value, length, reg_op, size);/* Perform SPI transfer */ ret =oa_tc6_spi_transfer (tc6,OA_TC6_CTRL_HEADER , size);
@@ -302,7 +339,7 @@ static int oa_tc6_perform_ctrl(struct oa_tc6 *tc6, u32 address, u32 value[],return oa_tc6_check_ctrl_write_reply (tc6, size);/* Check echoed/received control read command reply for errors */ - ret =oa_tc6_check_ctrl_read_reply (tc6, size); + ret =oa_tc6_check_ctrl_read_reply (tc6, length);if (ret)return ret;
@@ -1272,6 +1309,20 @@ netdev_tx_t oa_tc6_start_xmit(struct oa_tc6 *tc6, struct sk_buff *skb) }EXPORT_SYMBOL_GPL (oa_tc6_start_xmit); +static int oa_tc6_check_ctrl_protection (struct oa_tc6 *tc6) +{ +u32 regval; +int ret; + + ret =oa_tc6_read_register (tc6,OA_TC6_REG_CONFIG0 , ®val); +if (ret) +return ret; + + tc6->prot_ctrl =FIELD_GET (CONFIG0_PROTE , regval); + +return 0 ; +} +/** * oa_tc6_init - allocates and initializes oa_tc6 structure. * @spi: device with which data will be exchanged.
@@ -1324,6 +1375,14 @@ struct oa_tc6 *oa_tc6_init(struct spi_device *spi, struct net_device *netdev)if (!tc6->spi_data_rx_buf)return NULL ; +/* Check the PROTE bit status so that we can reset the device */ + ret =oa_tc6_check_ctrl_protection (tc6); +if (ret) { +dev_err (&tc6->spi->dev, +"Failed to check the protection mode: %d\n" , ret); +return NULL ; + } + ret =oa_tc6_sw_reset_macphy (tc6);if (ret) { dev_err(&tc6->spi->dev,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 127/733 net: ethernet: oa_tc6: Export standard defined registers
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ciprian Regus <[email protected]>
[ Upstream commit 9210d402bdf54240b8aec9815b2f9aad360fcb42 ]
Move defines for standard Open Alliance TC6 register addresses and subfields in the oa_tc6's header. As such, other ethernet drivers that rely on oa_tc6 can use them directly.
Stable-dep-of: 5443d9c4f55d ("net: ethernet: oa_tc6: Protect skb pointer used by two different kernel instances")
--- drivers/net/ethernet/oa_tc6.c | 50 ----------------------------------- include/linux/oa_tc6.h | 50 +++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 50 deletions(-)
drivers/net/ethernet/oa_tc6.c +0 -50
@@ -12,47 +12,6 @@#include <linux/phy.h> #include <linux/oa_tc6.h> -/* OPEN Alliance TC6 registers */ -/* Standard Capabilities Register */ -#define OA_TC6_REG_STDCAP 0x0002 -#define STDCAP_DIRECT_PHY_REG_ACCESS BIT(8) - -/* Reset Control and Status Register */ -#define OA_TC6_REG_RESET 0x0003 -#define RESET_SWRESET BIT(0)/* Software Reset */ - -/* Configuration Register #0 */ -#define OA_TC6_REG_CONFIG0 0x0004 -#define CONFIG0_SYNC BIT(15) -#define CONFIG0_ZARFE_ENABLE BIT(12) -#define CONFIG0_PROTE BIT(5) - -/* Status Register #0 */ -#define OA_TC6_REG_STATUS0 0x0008 -#define STATUS0_RESETC BIT(6)/* Reset Complete */ -#define STATUS0_HEADER_ERROR BIT(5) -#define STATUS0_LOSS_OF_FRAME_ERROR BIT(4) -#define STATUS0_RX_BUFFER_OVERFLOW_ERROR BIT(3) -#define STATUS0_TX_PROTOCOL_ERROR BIT(0) - -/* Buffer Status Register */ -#define OA_TC6_REG_BUFFER_STATUS 0x000B -#define BUFFER_STATUS_TX_CREDITS_AVAILABLE GENMASK(15, 8) -#define BUFFER_STATUS_RX_CHUNKS_AVAILABLE GENMASK(7, 0) - -/* Interrupt Mask Register #0 */ -#define OA_TC6_REG_INT_MASK0 0x000C -#define INT_MASK0_HEADER_ERR_MASK BIT(5) -#define INT_MASK0_LOSS_OF_FRAME_ERR_MASK BIT(4) -#define INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK BIT(3) -#define INT_MASK0_TX_PROTOCOL_ERR_MASK BIT(0) -#define INT_MASK0_ALL_INTERRUPTS (GENMASK(5, 0) | \ - GENMASK(12, 7)) - -/* PHY Clause 22 registers base address and mask */ -#define OA_TC6_PHY_STD_REG_ADDR_BASE 0xFF00 -#define OA_TC6_PHY_STD_REG_ADDR_MASK 0x1F -/* Control command header */ #define OA_TC6_CTRL_HEADER_DATA_NOT_CTRL BIT(31)#define OA_TC6_CTRL_HEADER_WRITE_NOT_READ BIT(29)
@@ -82,15 +41,6 @@#define OA_TC6_DATA_FOOTER_END_BYTE_OFFSET GENMASK(13, 8)#define OA_TC6_DATA_FOOTER_TX_CREDITS GENMASK(5, 1) -/* PHY – Clause 45 registers memory map selector (MMS) as per table 6 in the -* OPEN Alliance specification. -*/ -#define OA_TC6_PHY_C45_PCS_MMS2 2/* MMD 3 */ -#define OA_TC6_PHY_C45_PMA_PMD_MMS3 3/* MMD 1 */ -#define OA_TC6_PHY_C45_VS_PLCA_MMS4 4/* MMD 31 */ -#define OA_TC6_PHY_C45_AUTO_NEG_MMS5 5/* MMD 7 */ -#define OA_TC6_PHY_C45_POWER_UNIT_MMS6 6/* MMD 13 */ -#define OA_TC6_CTRL_PROT_REPLY_SIZE 4#define OA_TC6_CTRL_HEADER_SIZE 4#define OA_TC6_CTRL_REG_VALUE_SIZE 4
include/linux/oa_tc6.h +50 -0
56 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 128/733 net: ethernet: oa_tc6: Add the OA_TC6_ prefix to standard registers
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ciprian Regus <[email protected]>
[ Upstream commit 31bc75f17c1f5ff989fa896aae3e4e411d9b0b7a ]
The OA TC6 standard registers are currently exported in a header file. Add the OA_TC6_ prefix to the register address and subfield mask macros to avoid future naming conflicts.
Stable-dep-of: 5443d9c4f55d ("net: ethernet: oa_tc6: Protect skb pointer used by two different kernel instances")
--- drivers/net/ethernet/oa_tc6.c | 37 ++++++++++++++++---------------- include/linux/oa_tc6.h | 40 +++++++++++++++++------------------ 2 files changed, 39 insertions(+), 38 deletions(-)
drivers/net/ethernet/oa_tc6.c +19 -18
@@ -395,7 +395,7 @@ static int oa_tc6_check_phy_reg_direct_access_capability(struct oa_tc6 *tc6)if (ret)return ret; -if (!(regval &STDCAP_DIRECT_PHY_REG_ACCESS )) +if (!(regval &OA_TC6_STDCAP_DIRECT_PHY_REG_ACCESS ))return -ENODEV ;return 0 ;
@@ -588,7 +588,7 @@ static int oa_tc6_read_status0(struct oa_tc6 *tc6)static int oa_tc6_sw_reset_macphy (struct oa_tc6 *tc6) { -u32 regval =RESET_SWRESET ; +u32 regval =OA_TC6_RESET_SWRESET ;int ret; ret =oa_tc6_write_register (tc6,OA_TC6_REG_RESET , regval);
@@ -597,7 +597,7 @@ static int oa_tc6_sw_reset_macphy(struct oa_tc6 *tc6)/* Poll for soft reset complete for every 1ms until 1s timeout */ ret =readx_poll_timeout (oa_tc6_read_status0, tc6, regval, - regval &STATUS0_RESETC , + regval &OA_TC6_STATUS0_RESETC ,STATUS0_RESETC_POLL_DELAY ,STATUS0_RESETC_POLL_TIMEOUT );if (ret)
@@ -616,10 +616,10 @@ static int oa_tc6_unmask_macphy_error_interrupts(struct oa_tc6 *tc6)if (ret)return ret; - regval &= ~(INT_MASK0_TX_PROTOCOL_ERR_MASK | -INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK | -INT_MASK0_LOSS_OF_FRAME_ERR_MASK | -INT_MASK0_HEADER_ERR_MASK ); + regval &= ~(OA_TC6_INT_MASK0_TX_PROTOCOL_ERR_MASK | +OA_TC6_INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK | +OA_TC6_INT_MASK0_LOSS_OF_FRAME_ERR_MASK | +OA_TC6_INT_MASK0_HEADER_ERR_MASK );return oa_tc6_write_register (tc6,OA_TC6_REG_INT_MASK0 , regval); }
@@ -634,7 +634,7 @@ static int oa_tc6_enable_data_transfer(struct oa_tc6 *tc6)return ret;/* Enable configuration synchronization for data transfer */ - value |=CONFIG0_SYNC ; + value |=OA_TC6_CONFIG0_SYNC ;return oa_tc6_write_register (tc6,OA_TC6_REG_CONFIG0 , value); }
@@ -679,7 +679,7 @@ static void oa_tc6_free_pending_skbs(struct oa_tc6 *tc6)*/ static void oa_tc6_disable_traffic (struct oa_tc6 *tc6) { -u32 regval =INT_MASK0_ALL_INTERRUPTS ; +u32 regval =OA_TC6_INT_MASK0_ALL_INTERRUPTS ; tc6->disable_traffic = true;oa_tc6_free_pending_skbs (tc6);
@@ -709,25 +709,25 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6)return ret; } -if (FIELD_GET (STATUS0_RX_BUFFER_OVERFLOW_ERROR , value)) { +if (FIELD_GET (OA_TC6_STATUS0_RX_BUFFER_OVERFLOW_ERROR , value)) { tc6->rx_buf_overflow = true;oa_tc6_cleanup_ongoing_rx_skb (tc6);net_err_ratelimited ("%s: Receive buffer overflow error\n" , tc6->netdev->name);return -EAGAIN ; } -if (FIELD_GET (STATUS0_TX_PROTOCOL_ERROR , value)) { +if (FIELD_GET (OA_TC6_STATUS0_TX_PROTOCOL_ERROR , value)) {netdev_err (tc6->netdev,"Transmit protocol error\n" );return -ENODEV ; }/* TODO: Currently loss of frame and header errors are treated as * non-recoverable errors. They will be handled in the next version. */ -if (FIELD_GET (STATUS0_LOSS_OF_FRAME_ERROR , value)) { +if (FIELD_GET (OA_TC6_STATUS0_LOSS_OF_FRAME_ERROR , value)) {netdev_err (tc6->netdev,"Loss of frame error\n" );return -ENODEV ; } -if (FIELD_GET (STATUS0_HEADER_ERROR , value)) { +if (FIELD_GET (OA_TC6_STATUS0_HEADER_ERROR , value)) {netdev_err (tc6->netdev,"Header error\n" );return -ENODEV ; }
@@ -1174,9 +1174,10 @@ static int oa_tc6_update_buffer_status_from_register(struct oa_tc6 *tc6)if (ret)return ret; - tc6->tx_credits =FIELD_GET (BUFFER_STATUS_TX_CREDITS_AVAILABLE , value); - tc6->rx_chunks_available =FIELD_GET (BUFFER_STATUS_RX_CHUNKS_AVAILABLE , - value); + tc6->tx_credits =FIELD_GET (OA_TC6_BUFFER_STATUS_TX_CREDITS_AVAILABLE , + value); + tc6->rx_chunks_available = +FIELD_GET (OA_TC6_BUFFER_STATUS_RX_CHUNKS_AVAILABLE , value);return 0 ; }
@@ -1220,7 +1221,7 @@ int oa_tc6_zero_align_receive_frame_enable(struct oa_tc6 *tc6)return ret;/* Set Zero-Align Receive Frame Enable */ - regval |=CONFIG0_ZARFE_ENABLE ; + regval |=OA_TC6_CONFIG0_ZARFE_ENABLE ;return oa_tc6_write_register (tc6,OA_TC6_REG_CONFIG0 , regval); }
@@ -1268,7 +1269,7 @@ static int oa_tc6_check_ctrl_protection(struct oa_tc6 *tc6)if (ret)return ret; - tc6->prot_ctrl =FIELD_GET (CONFIG0_PROTE , regval); + tc6->prot_ctrl =FIELD_GET (OA_TC6_CONFIG0_PROTE , regval);return 0 ; }
include/linux/oa_tc6.h +20 -20
@@ -13,39 +13,39 @@/* OPEN Alliance TC6 registers */ /* Standard Capabilities Register */ #define OA_TC6_REG_STDCAP 0x0002 -#define STDCAP_DIRECT_PHY_REG_ACCESS BIT(8) +#define OA_TC6_STDCAP_DIRECT_PHY_REG_ACCESS BIT(8)/* Reset Control and Status Register */ #define OA_TC6_REG_RESET 0x0003 -#define RESET_SWRESET BIT(0)/* Software Reset */ +#define OA_TC6_RESET_SWRESET BIT(0)/* Software Reset */ /* Configuration Register #0 */ #define OA_TC6_REG_CONFIG0 0x0004 -#define CONFIG0_SYNC BIT(15) -#define CONFIG0_ZARFE_ENABLE BIT(12) -#define CONFIG0_PROTE BIT(5) +#define OA_TC6_CONFIG0_SYNC BIT(15) +#define OA_TC6_CONFIG0_ZARFE_ENABLE BIT(12) +#define OA_TC6_CONFIG0_PROTE BIT(5)/* Status Register #0 */ #define OA_TC6_REG_STATUS0 0x0008 -#define STATUS0_RESETC BIT(6)/* Reset Complete */ -#define STATUS0_HEADER_ERROR BIT(5) -#define STATUS0_LOSS_OF_FRAME_ERROR BIT(4) -#define STATUS0_RX_BUFFER_OVERFLOW_ERROR BIT(3) -#define STATUS0_TX_PROTOCOL_ERROR BIT(0) +#define OA_TC6_STATUS0_RESETC BIT(6)/* Reset Complete */ +#define OA_TC6_STATUS0_HEADER_ERROR BIT(5) +#define OA_TC6_STATUS0_LOSS_OF_FRAME_ERROR BIT(4) +#define OA_TC6_STATUS0_RX_BUFFER_OVERFLOW_ERROR BIT(3) +#define OA_TC6_STATUS0_TX_PROTOCOL_ERROR BIT(0)/* Buffer Status Register */ -#define OA_TC6_REG_BUFFER_STATUS 0x000B -#define BUFFER_STATUS_TX_CREDITS_AVAILABLE GENMASK(15, 8) -#define BUFFER_STATUS_RX_CHUNKS_AVAILABLE GENMASK(7, 0) +#define OA_TC6_REG_BUFFER_STATUS 0x000B +#define OA_TC6_BUFFER_STATUS_TX_CREDITS_AVAILABLE GENMASK(15, 8) +#define OA_TC6_BUFFER_STATUS_RX_CHUNKS_AVAILABLE GENMASK(7, 0)/* Interrupt Mask Register #0 */ -#define OA_TC6_REG_INT_MASK0 0x000C -#define INT_MASK0_HEADER_ERR_MASK BIT(5) -#define INT_MASK0_LOSS_OF_FRAME_ERR_MASK BIT(4) -#define INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK BIT(3) -#define INT_MASK0_TX_PROTOCOL_ERR_MASK BIT(0) -#define INT_MASK0_ALL_INTERRUPTS (GENMASK(5, 0) | \ - GENMASK(12, 7)) +#define OA_TC6_REG_INT_MASK0 0x000C +#define OA_TC6_INT_MASK0_HEADER_ERR_MASK BIT(5) +#define OA_TC6_INT_MASK0_LOSS_OF_FRAME_ERR_MASK BIT(4) +#define OA_TC6_INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK BIT(3) +#define OA_TC6_INT_MASK0_TX_PROTOCOL_ERR_MASK BIT(0) +#define OA_TC6_INT_MASK0_ALL_INTERRUPTS (GENMASK(5, 0) | \ + GENMASK(12, 7))/* PHY Clause 22 registers base address and mask */ #define OA_TC6_PHY_STD_REG_ADDR_BASE 0xFF00
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 129/733 net: ethernet: oa_tc6: Protect skb pointer used by two different kernel instances
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Selvamani Rajagopal <[email protected]>
[ Upstream commit 5443d9c4f55d46634b95432e1e8a40b824019bbb ]
Threaded IRQ uses waiting_tx_skb. Transmit path also uses this pointer without any mutual exclusion protection. As a result, it might leak skb buffer, particularly if threaded IRQ sets disable_traffic true after start_xmit already checked and found that disable_traffic being false, if they happen to run on different cores.
On fatal error, where disable_traffic is set, transmit function drops the packet and return NETDEV_TX_OK. Due to this change, skb_linearize call is moved up to the beginning of the transmit function.
Since skb buffer may be freed from different contexts, dev_kfree_skb_any is used to free skb buffer now, replacing one of the kfree_skb call.
oa_tc6_exit disables the irq before setting disable_traffic true.
--- drivers/net/ethernet/oa_tc6.c | 109 ++++++++++++++++++++++++---------- 1 file changed, 76 insertions(+), 33 deletions(-)
drivers/net/ethernet/oa_tc6.c +76 -33
@@ -639,6 +639,26 @@ static int oa_tc6_enable_data_transfer(struct oa_tc6 *tc6)return oa_tc6_write_register (tc6,OA_TC6_REG_CONFIG0 , value); } +/* Called when a frame that is meant to be transmitted, is dropped. */ +static void oa_tc6_drop_tx_skb (struct oa_tc6 *tc6,struct sk_buff *skb) +{ +if (skb) { + tc6->netdev->stats.tx_dropped++; +dev_kfree_skb_any (skb); + } +} + +static struct sk_buff *oa_tc6_detach_waiting_tx_skb (struct oa_tc6 *tc6) +{ +struct sk_buff *skb; + +lockdep_assert_held (&tc6->tx_skb_lock); + skb = tc6->waiting_tx_skb; + tc6->waiting_tx_skb =NULL ; + +return skb; +} +static void oa_tc6_cleanup_ongoing_rx_skb (struct oa_tc6 *tc6) {if (tc6->rx_skb) {
@@ -650,26 +670,30 @@ static void oa_tc6_cleanup_ongoing_rx_skb(struct oa_tc6 *tc6)static void oa_tc6_cleanup_ongoing_tx_skb (struct oa_tc6 *tc6) { -if (tc6->ongoing_tx_skb) { - tc6->netdev->stats.tx_dropped++; -kfree_skb (tc6->ongoing_tx_skb); - tc6->ongoing_tx_skb =NULL ; - } +oa_tc6_drop_tx_skb (tc6, tc6->ongoing_tx_skb); + tc6->ongoing_tx_skb =NULL ; }static void oa_tc6_cleanup_waiting_tx_skb (struct oa_tc6 *tc6) { -if (tc6->waiting_tx_skb) { - tc6->netdev->stats.tx_dropped++; -kfree_skb (tc6->waiting_tx_skb); - tc6->waiting_tx_skb =NULL ; - } +struct sk_buff *skb; + +spin_lock_bh (&tc6->tx_skb_lock); + skb =oa_tc6_detach_waiting_tx_skb (tc6); +spin_unlock_bh (&tc6->tx_skb_lock); + +oa_tc6_drop_tx_skb (tc6, skb); } -static void oa_tc6_free_pending_skbs (struct oa_tc6 *tc6) +static void oa_tc6_free_ongoing_skbs (struct oa_tc6 *tc6) {oa_tc6_cleanup_ongoing_tx_skb (tc6);oa_tc6_cleanup_ongoing_rx_skb (tc6); +} + +static void oa_tc6_free_pending_skbs (struct oa_tc6 *tc6) +{ +oa_tc6_free_ongoing_skbs (tc6);oa_tc6_cleanup_waiting_tx_skb (tc6); }
@@ -680,9 +704,15 @@ static void oa_tc6_free_pending_skbs(struct oa_tc6 *tc6)static void oa_tc6_disable_traffic (struct oa_tc6 *tc6) {u32 regval =OA_TC6_INT_MASK0_ALL_INTERRUPTS ; +struct sk_buff *skb; +spin_lock_bh (&tc6->tx_skb_lock); tc6->disable_traffic = true; -oa_tc6_free_pending_skbs (tc6); + skb =oa_tc6_detach_waiting_tx_skb (tc6); +spin_unlock_bh (&tc6->tx_skb_lock); + +oa_tc6_drop_tx_skb (tc6, skb); +oa_tc6_free_ongoing_skbs (tc6);oa_tc6_write_register (tc6,OA_TC6_REG_INT_MASK0 , regval);oa_tc6_read_register (tc6,OA_TC6_REG_STATUS0 , ®val);oa_tc6_write_register (tc6,OA_TC6_REG_STATUS0 , regval);
@@ -1123,8 +1153,7 @@ static int oa_tc6_try_spi_transfer(struct oa_tc6 *tc6)if (ret == -EAGAIN )continue ; -oa_tc6_cleanup_ongoing_tx_skb (tc6); -oa_tc6_cleanup_ongoing_rx_skb (tc6); +oa_tc6_free_ongoing_skbs (tc6);netdev_err (tc6->netdev,"Device error: %d\n" , ret);return ret; }
@@ -1146,15 +1175,20 @@ static irqreturn_t oa_tc6_macphy_threaded_irq(int irq, void *data)* no need to attempt spi transfer, once it fails. Pending skbs * are already freed. */ -if (!tc6->disable_traffic) { -while (tc6->int_flag || - (tc6->waiting_tx_skb && tc6->tx_credits)) { - ret =oa_tc6_try_spi_transfer (tc6); -if (ret) { -disable_irq_nosync (tc6->spi->irq); -oa_tc6_disable_traffic (tc6); -break ; - } +spin_lock_bh (&tc6->tx_skb_lock); +if (tc6->disable_traffic) { +spin_unlock_bh (&tc6->tx_skb_lock); +return IRQ_HANDLED ; + } +spin_unlock_bh (&tc6->tx_skb_lock); + +while (tc6->int_flag || + (tc6->waiting_tx_skb && tc6->tx_credits)) { + ret =oa_tc6_try_spi_transfer (tc6); +if (ret) { +disable_irq_nosync (tc6->spi->irq); +oa_tc6_disable_traffic (tc6); +break ; } }
@@ -1233,23 +1267,30 @@ EXPORT_SYMBOL_GPL(oa_tc6_zero_align_receive_frame_enable);* @tc6: oa_tc6 struct. * @skb: socket buffer in which the ethernet frame is stored. * -* Return: NETDEV_TX_OK if the transmit ethernet frame skb added in the tx_skb_q -* otherwise returns NETDEV_TX_BUSY. +* Return: NETDEV_TX_OK either on successful queueing of the packet for +* transmission, or on packet getting dropped. Packet can be dropped due to +* failure in linearizing the buffer or disable_traffic is set due to +* earlier fatal error. Returns NETDEV_TX_BUSY when there is no room +* to queue the packet. */ netdev_tx_t oa_tc6_start_xmit (struct oa_tc6 *tc6,struct sk_buff *skb) { -if (tc6->disable_traffic || tc6->waiting_tx_skb) { -netif_stop_queue (tc6->netdev); -return NETDEV_TX_BUSY ; - } -if (skb_linearize (skb)) { -dev_kfree_skb_any (skb); - tc6->netdev->stats.tx_dropped++; +oa_tc6_drop_tx_skb (tc6, skb);return NETDEV_TX_OK ; }spin_lock_bh (&tc6->tx_skb_lock); +if (tc6->waiting_tx_skb) { +netif_stop_queue (tc6->netdev); +spin_unlock_bh (&tc6->tx_skb_lock); +return NETDEV_TX_BUSY ; + } +if (tc6->disable_traffic) { +spin_unlock_bh (&tc6->tx_skb_lock); +oa_tc6_drop_tx_skb (tc6, skb); +return NETDEV_TX_OK ; + } tc6->waiting_tx_skb = skb;spin_unlock_bh (&tc6->tx_skb_lock);
@@ -1403,8 +1444,10 @@ EXPORT_SYMBOL_GPL(oa_tc6_init);*/ void oa_tc6_exit (struct oa_tc6 *tc6) { - tc6->disable_traffic = true;disable_irq (tc6->spi->irq); +spin_lock_bh (&tc6->tx_skb_lock); + tc6->disable_traffic = true; +spin_unlock_bh (&tc6->tx_skb_lock);oa_tc6_phy_exit (tc6);oa_tc6_free_pending_skbs (tc6); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 130/733 net: ethernet: oa_tc6: Improve the error recovery
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Selvamani Rajagopal <[email protected]>
[ Upstream commit 172c974113bffe5723b80b1acac17593bb50513c ]
When oversubscribed traffic causes lot of buffer overflow errors, probably due to loss of data chunks, driver fails to find a data chunk with end_valid bit set, before it runs out of sk buffer space. As a result, assert is seen during skb_put.
Now, check is made if skb buffer has enough tailroom for the incoming data before accepting. If there is no room, current frame is abandoned and it will start looking for a data chunk with start_valid bit, that is a new frame.
SK buffer allocation error is considered as recoverable error.
rx_buf_overflow flag is too specific and no longer the only condition this flag is used for. Therefore it is renamed as wait_until_start_valid. This is more appropriate as this flag is used to look for the next data chunk with SV bit set, after failures like buffer overflow, buffer allocation failure, skb pointer validity besides buffer overflow error.
Not writing to status0 if it reads 0.
--- drivers/net/ethernet/oa_tc6.c | 143 +++++++++++++++++++++++++--------- 1 file changed, 108 insertions(+), 35 deletions(-)
drivers/net/ethernet/oa_tc6.c +108 -35
@@ -81,7 +81,7 @@ struct oa_tc6 {u16 spi_data_tx_buf_offset;u16 tx_credits;u8 rx_chunks_available; -bool rx_buf_overflow; +bool wait_until_start_valid;bool int_flag;bool disable_traffic;bool prot_ctrl;
@@ -697,6 +697,12 @@ static void oa_tc6_free_pending_skbs(struct oa_tc6 *tc6)oa_tc6_cleanup_waiting_tx_skb (tc6); } +static void oa_tc6_look_for_new_frame (struct oa_tc6 *tc6) +{ + tc6->wait_until_start_valid = true; +oa_tc6_cleanup_ongoing_rx_skb (tc6); +} +/* If the failure is at SPI interface level, masking and clearing * the interrupt of the device won't work. Since SPI interrupt is * disabled, it should stop the repeated interrupts.
@@ -731,6 +737,13 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6)return ret; } +/* This function is called for each chunk received in a given SPI +* transaction. In case, extended status bit is set in more than +* one chunk, skip the write, if status0 is already cleared. +*/ +if (!value) +return 0 ; +/* Clear the error interrupts status */ ret =oa_tc6_write_register (tc6,OA_TC6_REG_STATUS0 , value);if (ret) {
@@ -740,8 +753,7 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6) }if (FIELD_GET (OA_TC6_STATUS0_RX_BUFFER_OVERFLOW_ERROR , value)) { - tc6->rx_buf_overflow = true; -oa_tc6_cleanup_ongoing_rx_skb (tc6); +oa_tc6_look_for_new_frame (tc6);net_err_ratelimited ("%s: Receive buffer overflow error\n" , tc6->netdev->name);return -EAGAIN ;
@@ -767,6 +779,8 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6)static int oa_tc6_process_rx_chunk_footer (struct oa_tc6 *tc6,u32 footer) { +int ret =0 ; +/* Process rx chunk footer for the following, * 1. tx credits * 2. errors if any from MAC-PHY
@@ -777,9 +791,11 @@ static int oa_tc6_process_rx_chunk_footer(struct oa_tc6 *tc6, u32 footer) footer);if (FIELD_GET (OA_TC6_DATA_FOOTER_EXTENDED_STS , footer)) { -int ret =oa_tc6_process_extended_status (tc6); - -if (ret) + ret =oa_tc6_process_extended_status (tc6); +/* EAGAIN error is recoverable. Move on to check +* HEADER and SYNC errors before returning. +*/ +if (ret && ret != -EAGAIN )return ret; }
@@ -797,7 +813,7 @@ static int oa_tc6_process_rx_chunk_footer(struct oa_tc6 *tc6, u32 footer)return -ENODEV ; } -return 0 ; +return ret; }static void oa_tc6_submit_rx_skb (struct oa_tc6 *tc6)
@@ -822,13 +838,35 @@ static void oa_tc6_submit_rx_skb(struct oa_tc6 *tc6) tc6->rx_skb =NULL ; } -static void oa_tc6_update_rx_skb (struct oa_tc6 *tc6,u8 *payload,u8 length) +/* On oversubscribed traffic condition, particularly with overwhelming rx +* buffer overflow errors, there could be data chunk loss. If tail + length +* goes beyond end pointer, that is an indication that the data chunk with +* end_valid bit is lost. Time to look for a data chunk with start_valid bit. +* +* If rx_skb is NULL, it is time to start looking for data chunk with +* start_bit. +*/ +static int oa_tc6_update_rx_skb (struct oa_tc6 *tc6,u8 *payload,u8 length) { +if (!tc6->rx_skb || +skb_tailroom (tc6->rx_skb) < length) { +oa_tc6_look_for_new_frame (tc6); +return -EAGAIN ; + } +memcpy (skb_put (tc6->rx_skb, length), payload, length); +return 0 ; } +/* On overwhelming rx buffer overflow errors, due to data chunk loss, it is +* possible that we get two data chunks with start_valid bit set, without +* end_valid bit set in between. In this case, rx_skb would have a valid +* buffer pointer. We should release, if a valid pointer is found before +* allocating a new one. +*/ static int oa_tc6_allocate_rx_skb (struct oa_tc6 *tc6) { +oa_tc6_cleanup_ongoing_rx_skb (tc6); tc6->rx_skb =netdev_alloc_skb_ip_align (tc6->netdev, tc6->netdev->mtu +ETH_HLEN +ETH_FCS_LEN );if (!tc6->rx_skb) {
@@ -848,7 +886,9 @@ static int oa_tc6_prcs_complete_rx_frame(struct oa_tc6 *tc6, u8 *payload,if (ret)return ret; -oa_tc6_update_rx_skb (tc6, payload, size); + ret =oa_tc6_update_rx_skb (tc6, payload, size); +if (ret) +return ret;oa_tc6_submit_rx_skb (tc6);
@@ -863,22 +903,24 @@ static int oa_tc6_prcs_rx_frame_start(struct oa_tc6 *tc6, u8 *payload, u16 size)if (ret)return ret; -oa_tc6_update_rx_skb (tc6, payload, size); - -return 0 ; +return oa_tc6_update_rx_skb (tc6, payload, size); } -static void oa_tc6_prcs_rx_frame_end (struct oa_tc6 *tc6,u8 *payload,u16 size) +static int oa_tc6_prcs_rx_frame_end (struct oa_tc6 *tc6,u8 *payload,u16 size) { -oa_tc6_update_rx_skb (tc6, payload, size); +int ret; -oa_tc6_submit_rx_skb (tc6); + ret =oa_tc6_update_rx_skb (tc6, payload, size); +if (!ret) +oa_tc6_submit_rx_skb (tc6); +return ret; } -static void oa_tc6_prcs_ongoing_rx_frame (struct oa_tc6 *tc6,u8 *payload, -u32 footer) +static int oa_tc6_prcs_ongoing_rx_frame (struct oa_tc6 *tc6,u8 *payload, +u32 footer) { -oa_tc6_update_rx_skb (tc6, payload,OA_TC6_CHUNK_PAYLOAD_SIZE ); +return oa_tc6_update_rx_skb (tc6, payload, +OA_TC6_CHUNK_PAYLOAD_SIZE ); } static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,
@@ -893,10 +935,10 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,u16 size;/* Restart the new rx frame after receiving rx buffer overflow error */ -if (start_valid && tc6->rx_buf_overflow) - tc6->rx_buf_overflow = false; +if (start_valid && tc6->wait_until_start_valid) + tc6->wait_until_start_valid = false; -if (tc6->rx_buf_overflow) +if (tc6->wait_until_start_valid)return 0 ;/* Process the chunk with complete rx frame */
@@ -918,8 +960,7 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,/* Process the chunk with only rx frame end */ if (end_valid && !start_valid) { size = end_byte_offset +1 ; -oa_tc6_prcs_rx_frame_end (tc6, data, size); -return 0 ; +return oa_tc6_prcs_rx_frame_end (tc6, data, size); }/* Process the chunk with previous rx frame end and next rx frame
@@ -933,6 +974,15 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,if (tc6->rx_skb) { size = end_byte_offset +1 ;oa_tc6_prcs_rx_frame_end (tc6, data, size); + +/* Return value from oa_tc6_prcs_rx_frame_end is not +* checked. If it returned an error, it is to make +* the code to look for new frame. At this stage, +* code below is going to process a new frame. So, +* error condition is set to false, in case it is +* set before proceeding. +*/ + tc6->wait_until_start_valid = false; } size =OA_TC6_CHUNK_PAYLOAD_SIZE - start_byte_offset; return oa_tc6_prcs_rx_frame_start(tc6,
@@ -941,9 +991,7 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data, }/* Process the chunk with ongoing rx frame data */ -oa_tc6_prcs_ongoing_rx_frame (tc6, data, footer); - -return 0 ; +return oa_tc6_prcs_ongoing_rx_frame (tc6, data, footer); }static u32 oa_tc6_get_rx_chunk_footer (struct oa_tc6 *tc6,u16 footer_offset)
@@ -959,8 +1007,9 @@ static u32 oa_tc6_get_rx_chunk_footer(struct oa_tc6 *tc6, u16 footer_offset)static int oa_tc6_process_spi_data_rx_buf (struct oa_tc6 *tc6,u16 length) {u16 no_of_rx_chunks = length /OA_TC6_CHUNK_SIZE ; +bool retry = false; +int ret =0 ;u32 footer; -int ret;/* All the rx chunks in the receive SPI data buffer are examined here */ for (int i =0 ; i < no_of_rx_chunks; i++) {
@@ -969,8 +1018,11 @@ static int oa_tc6_process_spi_data_rx_buf(struct oa_tc6 *tc6, u16 length) OA_TC6_CHUNK_PAYLOAD_SIZE); ret =oa_tc6_process_rx_chunk_footer (tc6, footer); -if (ret) -return ret; +if (ret) { +if (ret != -EAGAIN ) +return ret; + retry = true; + }/* If there is a data valid chunks then process it for the * information needed to determine the validity and the location
@@ -982,12 +1034,35 @@ static int oa_tc6_process_spi_data_rx_buf(struct oa_tc6 *tc6, u16 length) ret =oa_tc6_prcs_rx_chunk_payload (tc6, payload, footer); -if (ret) -return ret; +if (ret) { +if (ret != -ENOMEM && ret != -EAGAIN ) +return ret; + retry = true; + } } } -return 0 ; +/* Not bailing out on recoverable error codes, -EAGAIN and +* -ENOMEM. If subsequent loop iterations, if any, succeeds, +* error code would be overwritten. retry flag helps to +* make the caller to continue and retry. Since recovery +* action for -ENOMEM and -EAGAIN are same, we are returning +* one of the error codes, that is -EAGAIN. +* +* Successful recovery depends on how small the frames are, +* how many chunks, among the received chunks triggered the +* error, whether data is intact even with error conditions. +* As a result, there is no single, best method to recover +* most data when error conditions hit. We do our best by +* processing all the chunks with good "footer header" and +* "data valid" bit set. +*/ +if (retry) { + ret = -EAGAIN ; +oa_tc6_look_for_new_frame (tc6); + } + +return ret; } static __be32 oa_tc6_prepare_data_header(bool data_valid, bool start_valid,
@@ -1149,10 +1224,8 @@ static int oa_tc6_try_spi_transfer(struct oa_tc6 *tc6) } ret =oa_tc6_process_spi_data_rx_buf (tc6, spi_len); -if (ret) { -if (ret == -EAGAIN ) -continue ; +if (ret && ret != -EAGAIN ) {oa_tc6_free_ongoing_skbs (tc6);netdev_err (tc6->netdev,"Device error: %d\n" , ret);return ret;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 131/733 net: ethernet: oa_tc6: Disable tx queues on fatal error
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Selvamani Rajagopal <[email protected]>
[ Upstream commit 349c366365876b7f67120827a0deb44899f59303 ]
Previously, TX queue interface was stopped when disable_traffic flag was set, which would indicate fatal error. It is more appropriate to disable the queue as, unless driver is unloaded and reloaded, there is no recovery after disable_traffic is set.
Queues may be re-enabled inadvertently by other layers. Intention of disable_traffic is only to stop the traffic from flowing on fatal error.
--- drivers/net/ethernet/oa_tc6.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/net/ethernet/oa_tc6.c +4 -0
@@ -717,6 +717,10 @@ static void oa_tc6_disable_traffic(struct oa_tc6 *tc6) skb =oa_tc6_detach_waiting_tx_skb (tc6);spin_unlock_bh (&tc6->tx_skb_lock); +/* disable_traffic, when set, is a point of no return to +* working state. Keeping the TX queues disabled. +*/ +netif_tx_disable (tc6->netdev);oa_tc6_drop_tx_skb (tc6, skb);oa_tc6_free_ongoing_skbs (tc6);oa_tc6_write_register (tc6,OA_TC6_REG_INT_MASK0 , regval);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 132/733 net: ethernet: oa_tc6: Fix for the wrong data type
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Selvamani Rajagopal <[email protected]>
[ Upstream commit 3cc2aa96b97184abd6fc106aac626ddf14389813 ]
Inadvertently bool data type is used where int is supposed to be used. This might turn a negative error code into true or false and sign of the return code would be lost.
--- drivers/net/ethernet/oa_tc6.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/oa_tc6.c +1 -1
@@ -410,7 +410,7 @@ static int oa_tc6_mdiobus_read(struct mii_bus *bus, int addr, int regnum) {struct oa_tc6 *tc6 = bus->priv;u32 regval; -bool ret; +int ret; ret = oa_tc6_read_register(tc6, OA_TC6_PHY_STD_REG_ADDR_BASE | (regnum & OA_TC6_PHY_STD_REG_ADDR_MASK),
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 133/733 net/sched: cls_u32: fix duplicate handle when node ID pool is exhausted
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit d7e7e98d23f42a92d9ab7e36302bd96bd9b33b5f ]
gen_new_kid() falls back to returning max (htid | 0xFFF) when both idr_alloc_u32() ranges are full, instead of reporting an error. u32_change() trusts that value and inserts a new knode with a handle that is already live in the hash table, breaking handle uniqueness within the table's node ID space.
The handle was never reserved in ht->handle_idr, so every later error path that does idr_remove(&ht->handle_idr, handle) removes the reservation of a different, live knode, which is then reused — one failed add compounds into further duplicates.
The 4095 limit is per (table, bucket) — ht->handle_idr is per hash table and the range is derived from htid (bucketid), so a table with divisor 256 can legitimately hold 256*4095 knodes.
The sibling helper gen_new_htid() has the same silent in-band failure:
it returns 0 when the tp_c handle pool (1..0x7FF) is full, and u32_init() publishes the root hash table with handle 0 without checking. Two root tables with handle 0 alias in u32_lookup_ht(), allowing cross-tcf_proto knode add/lookup/delete. Add the same exhaustion check that the divisor path already has.
Return an error so u32_change() fails with ENOSPC/ENOMEM when the node ID space is exhausted, and so u32_init() fails with -ENOMEM when the hash table ID space is exhausted. The extack message distinguishes pool exhaustion (-ENOSPC) from a transient allocation failure (-ENOMEM).
Conditions to recreate the bug:
- CONFIG_NET_SCHED=y, CONFIG_CLS_U32=y (or =m with module loaded)
- Create a clsact qdisc on a device, then add 4095 u32 filters with auto-generated handles to fill the node ID space for the root hash table (single bucket). The 4096th auto-handle filter add triggers the duplicate handle (fh 800::fff reused). Reachable at Level 2 (unshare -Urn, namespace-local CAP_NET_ADMIN).
- For gen_new_htid: create 2047 u32 proto entries on the same block to fill the tp_c handle pool, then create one more. The root table gets handle 0 and aliases with other handle-0 root tables.
--- net/sched/cls_u32.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-)
net/sched/cls_u32.c +26 -6
@@ -370,6 +370,10 @@ static int u32_init(struct tcf_proto *tp)refcount_set (&root_ht->refcnt,1 ); root_ht->handle = tp_c ?gen_new_htid (tp_c, root_ht) :id2handle (0 ); +if (root_ht->handle ==0 ) { +kfree (root_ht); +return -ENOMEM ; + } root_ht->prio = tp->prio; root_ht->is_root = true;idr_init (&root_ht->handle_idr);
@@ -695,21 +699,33 @@ static int u32_delete(struct tcf_proto *tp, void *arg, bool *last,return ret; } -static u32 gen_new_kid (struct tc_u_hnode *ht,u32 htid) +static u32 gen_new_kid (struct tc_u_hnode *ht,u32 htid,int *err) {u32 index = htid |0x800 ;u32 max = htid |0xFFF ; + *err =0 ; +if (idr_alloc_u32 (&ht->handle_idr,NULL , &index, max,GFP_KERNEL )) { index = htid +1 ; -if (idr_alloc_u32 (&ht->handle_idr,NULL , &index, max, -GFP_KERNEL )) - index = max; + *err =idr_alloc_u32 (&ht->handle_idr,NULL , &index, max, +GFP_KERNEL ); +if (*err) +return 0 ; }return index; } +static int u32_kid_extack (int err,struct netlink_ext_ack *extack) +{ +if (err == -ENOSPC ) +NL_SET_ERR_MSG_MOD (extack,"Hash table node ID pool exhausted" ); +else +NL_SET_ERR_MSG_MOD (extack,"Failed to allocate node ID" ); +return err; +} +static const struct nla_policy u32_policy[TCA_U32_MAX +1 ] = { [TCA_U32_CLASSID ] = { .type =NLA_U32 }, [TCA_U32_HASH ] = { .type =NLA_U32 },
@@ -1079,7 +1095,9 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,* handle which is used to uniquely identify the match entry. */ if (!TC_U32_NODE (handle)) { - handle =gen_new_kid (ht, htid); + handle =gen_new_kid (ht, htid, &err); +if (err) +return u32_kid_extack (err, extack); }else { handle = htid |TC_U32_NODE (handle); err = idr_alloc_u32(&ht->handle_idr, NULL, &handle,
@@ -1091,7 +1109,9 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,/* The user did not give us a handle; lets just generate one * from the table's pool of nodeids. */ - handle =gen_new_kid (ht, htid); + handle =gen_new_kid (ht, htid, &err); +if (err) +return u32_kid_extack (err, extack); }if (tb[TCA_U32_SEL ] ==NULL ) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 134/733 rust: samples: add missing newlines in rust_print_main
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mehmet Koseoglu <[email protected]>
[ Upstream commit e510334fbaeaa016ac76d80b4c5f47611c5f7860 ]
Calls to pr_info! in arc_print are missing trailing newlines, which are expected as the pr_*! documentation shows.
Add the missing \n to all four formatting strings.
--- samples/rust/rust_print_main.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
samples/rust/rust_print_main.rs +4 -4
@@ -23,10 +23,10 @@ fn arc_print() -> Result {let b =UniqueArc ::new ("hello, world" ,GFP_KERNEL )?;// Prints the value of data in `a`. -pr_info! ("{}" , a); +pr_info! ("{}\n" , a);// Uses ":?" to print debug fmt of `b`. -pr_info! ("{:?}" , b); +pr_info! ("{:?}\n" , b);let a:Arc <&str > = b.into ();let c = a.clone ();
@@ -42,7 +42,7 @@ fn arc_print() -> Result {use kernel::fmt::Display ;fn arc_dyn_print (arc: &Arc <dyn Display >) { -pr_info! ("Arc<dyn Display> says {arc}" ); +pr_info! ("Arc<dyn Display> says {arc}\n" ); }let a_i32_display:Arc <dyn Display > =Arc ::new (42i32 ,GFP_KERNEL )?;
@@ -53,7 +53,7 @@ fn arc_dyn_print(arc: &Arc<dyn Display>) { }// Pretty-prints the debug formatting with lower-case hexadecimal integers. -pr_info! ("{:#x?}" , a); +pr_info! ("{:#x?}\n" , a);Ok (()) }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 135/733 igmp: convert struct ip_sf_list to RCU
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 2987ee196c88dbde0463dc87d5fb209c684e34a2 ]
Commit 23d2b94043ca ("igmp: Add ip_mc_list lock in ip_check_mc_rcu") added spin_lock_bh(&im->lock) to ip_check_mc_rcu() to prevent a use-after-free while iterating im->sources during concurrent deletions.
However, ip_check_mc_rcu() is called from RCU read-side critical sections in packet receive and route lookup fast paths (e.g. __mkroute_output(), ip_route_input_rcu(), and __udp4_lib_rcv()).
When igmpv3_send_cr() or igmpv3_send_report() holds &pmc->lock and calls add_grec() -> igmpv3_newpack() -> ip_route_output_ports(), an XFRM policy matching a multicast destination triggers xfrm_tmpl_resolve_one() -> xfrm4_get_saddr() -> __mkroute_output() -> ip_check_mc_rcu(). This attempts to acquire &im->lock while &pmc->lock is already held on the same CPU, triggering a lockdep recursive locking warning / deadlock.
Fix this by converting IPv4 struct ip_sf_list to RCU, mirroring the IPv6 implementation in net/ipv6/mcast.c:
1. Add struct rcu_head to struct ip_sf_list and annotate sf_next, sources, and tomb as __rcu pointers.
2. Use rcu_assign_pointer() and kfree_rcu() for list updates and deletions.
3. Remove spin_lock_bh(&im->lock) from ip_check_mc_rcu() and traverse im->sources locklessly with for_each_psf_rcu(), reading and writing counter fields with READ_ONCE() and WRITE_ONCE().
Note: RCU conversion of /proc/net/mcfilter will be done in a separate patch.
--- include/linux/igmp.h | 7 +- net/ipv4/igmp.c | 210 +++++++++++++++++++++++++++---------------- 2 files changed, 135 insertions(+), 82 deletions(-)
include/linux/igmp.h +4 -3
@@ -57,20 +57,21 @@ struct ip_mc_socklist { };struct ip_sf_list { -struct ip_sf_list *sf_next; +struct ip_sf_list __rcu *sf_next;unsigned long sf_count[2 ];/* include/exclude counts */ __be32 sf_inaddr;unsigned char sf_gsresp;/* include in g & s response? */ unsigned char sf_oldin;/* change state */ unsigned char sf_crcount;/* retrans. left to send */ +struct rcu_head rcu; };struct ip_mc_list {struct in_device *interface;__be32 multiaddr;unsigned int sfmode; -struct ip_sf_list *sources; -struct ip_sf_list *tomb; +struct ip_sf_list __rcu *sources; +struct ip_sf_list __rcu *tomb;unsigned long sfcount[2 ];union {struct ip_mc_list *next;
net/ipv4/igmp.c +131 -79
466 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 136/733 ppp: ppp_async: simplify tty disc_data access
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qingfang Deng <[email protected]>
[ Upstream commit 9feb069e5ed03582fbf6272539f1caa2a17dc6d5 ]
tty_ldisc_hangup() invokes the hangup callback while holding only a read lock on tty->ldisc_sem, so it can run concurrently with other line discipline callbacks. This currently forces async PPP to maintain separate lifetime protection around tty->disc_data.
Line discipline close is called under the write lock during hangup processing. Remove the hangup callback and rely on close for teardown, as done for SLIP by commit 23c53269f2ba ("slip: remove slip_hangup() to fix use-after-free in slip_receive_buf()"). This serializes teardown with all other line discipline operations.
disc_data_lock, refcount and completion are redundant with that serialization. Remove them and access tty->disc_data directly.
This also eliminates a lockdep warning reported by syzbot. The warning does not indicate a real deadlock because the write side runs only in process context with hardirqs disabled.
--- drivers/net/ppp/ppp_async.c | 82 ++++--------------------------------- 1 file changed, 7 insertions(+), 75 deletions(-)
drivers/net/ppp/ppp_async.c +7 -75
@@ -65,8 +65,6 @@ struct asyncppp {struct tasklet_struct tsk; -refcount_t refcnt; -struct completion dead;struct ppp_channel chan;/* interface to generic ppp layer */ unsigned char obuf[OBUFSIZE ]; };
@@ -116,38 +114,6 @@ static const struct ppp_channel_ops async_ops = {* Routines implementing the PPP line discipline. */ -/* -* We have a potential race on dereferencing tty->disc_data, -* because the tty layer provides no locking at all - thus one -* cpu could be running ppp_asynctty_receive while another -* calls ppp_asynctty_close, which zeroes tty->disc_data and -* frees the memory that ppp_asynctty_receive is using. The best -* way to fix this is to use a rwlock in the tty struct, but for now -* we use a single global rwlock for all ttys in ppp line discipline. -* -* FIXME: this is no longer true. The _close path for the ldisc is -* now guaranteed to be sane. -*/ -static DEFINE_RWLOCK (disc_data_lock); - -static struct asyncppp *ap_get (struct tty_struct *tty) -{ -struct asyncppp *ap; - -read_lock (&disc_data_lock); - ap = tty->disc_data; -if (ap !=NULL ) -refcount_inc (&ap->refcnt); -read_unlock (&disc_data_lock); -return ap; -} - -static void ap_put (struct asyncppp *ap) -{ -if (refcount_dec_and_test (&ap->refcnt)) -complete (&ap->dead); -} -/* * Called when a tty is put into PPP line discipline. Called in process * context.
@@ -182,9 +148,6 @@ ppp_asynctty_open(struct tty_struct *tty)skb_queue_head_init (&ap->rqueue);tasklet_setup (&ap->tsk, ppp_async_process); -refcount_set (&ap->refcnt,1 ); -init_completion (&ap->dead); - ap->chan.private = ap; ap->chan.ops = &async_ops; ap->chan.mtu =PPP_MRU ;
@@ -205,34 +168,18 @@ ppp_asynctty_open(struct tty_struct *tty) }/* -* Called when the tty is put into another line discipline -* or it hangs up. We have to wait for any cpu currently -* executing in any of the other ppp_asynctty_* routines to -* finish before we can call ppp_unregister_channel and free -* the asyncppp struct. This routine must be called from -* process context, not interrupt or softirq context. +* Called when the tty is put into another line discipline or it hangs up. +* This call is serialized against other ldisc functions. */ static void ppp_asynctty_close (struct tty_struct *tty) { -struct asyncppp *ap; +struct asyncppp *ap = tty->disc_data; -write_lock_irq (&disc_data_lock); - ap = tty->disc_data; - tty->disc_data =NULL ; -write_unlock_irq (&disc_data_lock);if (!ap)return ; -/* -* We have now ensured that nobody can start using ap from now -* on, but we have to wait for all existing users to finish. -* Note that ppp_unregister_channel ensures that no calls to -* our channel ops (i.e. ppp_async_send/ioctl) are in progress -* by the time it returns. -*/ -if (!refcount_dec_and_test (&ap->refcnt)) -wait_for_completion (&ap->dead); + tty->disc_data =NULL ;tasklet_kill (&ap->tsk);ppp_unregister_channel (&ap->chan);
@@ -242,17 +189,6 @@ ppp_asynctty_close(struct tty_struct *tty)kfree (ap); } -/* -* Called on tty hangup in process context. -* -* Wait for I/O to driver to complete and unregister PPP channel. -* This is already done by the close routine, so just call that. -*/ -static void ppp_asynctty_hangup (struct tty_struct *tty) -{ -ppp_asynctty_close (tty); -} -/* * Read does nothing - no data is ever available this way. * Pppd reads and writes packets via /dev/ppp instead.
@@ -283,7 +219,7 @@ ppp_asynctty_write(struct tty_struct *tty, struct file *file, const u8 *buf,static int ppp_asynctty_ioctl (struct tty_struct *tty,unsigned int cmd,unsigned long arg) { -struct asyncppp *ap =ap_get (tty); +struct asyncppp *ap = tty->disc_data;int err, val;int __user *p = (int __user *)arg;
@@ -324,7 +260,6 @@ ppp_asynctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) err =tty_mode_ioctl (tty, cmd, arg); } -ap_put (ap);return err; }
@@ -333,7 +268,7 @@ static void ppp_asynctty_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags, size_t count) { - struct asyncppp *ap = ap_get(tty); + struct asyncppp *ap = tty->disc_data;unsigned long flags;if (!ap)
@@ -343,21 +278,19 @@ ppp_asynctty_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags,spin_unlock_irqrestore (&ap->recv_lock, flags);if (!skb_queue_empty (&ap->rqueue))tasklet_schedule (&ap->tsk); -ap_put (ap);tty_unthrottle (tty); }static void ppp_asynctty_wakeup (struct tty_struct *tty) { -struct asyncppp *ap =ap_get (tty); +struct asyncppp *ap = tty->disc_data;clear_bit (TTY_DO_WRITE_WAKEUP , &tty->flags);if (!ap)return ;set_bit (XMIT_WAKEUP , &ap->xmit_flags);tasklet_schedule (&ap->tsk); -ap_put (ap); }
@@ -367,7 +300,6 @@ static struct tty_ldisc_ops ppp_ldisc = { .name = "ppp", .open = ppp_asynctty_open, .close = ppp_asynctty_close, - .hangup = ppp_asynctty_hangup, .read = ppp_asynctty_read, .write = ppp_asynctty_write, .ioctl = ppp_asynctty_ioctl,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 137/733 ppp: ppp_synctty: simplify tty disc_data access
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qingfang Deng <[email protected]>
[ Upstream commit d8d4d1cf40d541a5d7cc3b15d57e42d0815c7d53 ]
Apply the same simplification as the preceding ppp_async change.
--- drivers/net/ppp/ppp_synctty.c | 83 +++-------------------------------- 1 file changed, 7 insertions(+), 76 deletions(-)
drivers/net/ppp/ppp_synctty.c +7 -76
@@ -38,11 +38,9 @@#include <linux/ppp-ioctl.h> #include <linux/ppp_channel.h> #include <linux/spinlock.h> -#include <linux/completion.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/slab.h> -#include <linux/refcount.h> #include <linux/unaligned.h> #include <linux/uaccess.h>
@@ -69,8 +67,6 @@ struct syncppp {struct tasklet_struct tsk; -refcount_t refcnt; -struct completion dead_cmp;struct ppp_channel chan;/* interface to generic ppp layer */ };
@@ -118,37 +114,6 @@ ppp_print_buffer (const char *name, const __u8 *buf, int count)* Routines implementing the synchronous PPP line discipline. */ -/* -* We have a potential race on dereferencing tty->disc_data, -* because the tty layer provides no locking at all - thus one -* cpu could be running ppp_synctty_receive while another -* calls ppp_synctty_close, which zeroes tty->disc_data and -* frees the memory that ppp_synctty_receive is using. The best -* way to fix this is to use a rwlock in the tty struct, but for now -* we use a single global rwlock for all ttys in ppp line discipline. -* -* FIXME: Fixed in tty_io nowadays. -*/ -static DEFINE_RWLOCK (disc_data_lock); - -static struct syncppp *sp_get (struct tty_struct *tty) -{ -struct syncppp *ap; - -read_lock (&disc_data_lock); - ap = tty->disc_data; -if (ap !=NULL ) -refcount_inc (&ap->refcnt); -read_unlock (&disc_data_lock); -return ap; -} - -static void sp_put (struct syncppp *ap) -{ -if (refcount_dec_and_test (&ap->refcnt)) -complete (&ap->dead_cmp); -} -/* * Called when a tty is put into sync-PPP line discipline. */
@@ -179,9 +144,6 @@ ppp_sync_open(struct tty_struct *tty)skb_queue_head_init (&ap->rqueue);tasklet_setup (&ap->tsk, ppp_sync_process); -refcount_set (&ap->refcnt,1 ); -init_completion (&ap->dead_cmp); - ap->chan.private = ap; ap->chan.ops = &sync_ops; ap->chan.mtu =PPP_MRU ;
@@ -203,34 +165,18 @@ ppp_sync_open(struct tty_struct *tty) }/* -* Called when the tty is put into another line discipline -* or it hangs up. We have to wait for any cpu currently -* executing in any of the other ppp_synctty_* routines to -* finish before we can call ppp_unregister_channel and free -* the syncppp struct. This routine must be called from -* process context, not interrupt or softirq context. +* Called when the tty is put into another line discipline or it hangs up. +* This call is serialized against other ldisc functions. */ static void ppp_sync_close (struct tty_struct *tty) { -struct syncppp *ap; +struct syncppp *ap = tty->disc_data; -write_lock_irq (&disc_data_lock); - ap = tty->disc_data; - tty->disc_data =NULL ; -write_unlock_irq (&disc_data_lock);if (!ap)return ; -/* -* We have now ensured that nobody can start using ap from now -* on, but we have to wait for all existing users to finish. -* Note that ppp_unregister_channel ensures that no calls to -* our channel ops (i.e. ppp_sync_send/ioctl) are in progress -* by the time it returns. -*/ -if (!refcount_dec_and_test (&ap->refcnt)) -wait_for_completion (&ap->dead_cmp); + tty->disc_data =NULL ;tasklet_kill (&ap->tsk);ppp_unregister_channel (&ap->chan);
@@ -239,17 +185,6 @@ ppp_sync_close(struct tty_struct *tty)kfree (ap); } -/* -* Called on tty hangup in process context. -* -* Wait for I/O to driver to complete and unregister PPP channel. -* This is already done by the close routine, so just call that. -*/ -static void ppp_sync_hangup (struct tty_struct *tty) -{ -ppp_sync_close (tty); -} -/* * Read does nothing - no data is ever available this way. * Pppd reads and writes packets via /dev/ppp instead.
@@ -275,7 +210,7 @@ ppp_sync_write(struct tty_struct *tty, struct file *file, const u8 *buf,static int ppp_synctty_ioctl (struct tty_struct *tty,unsigned int cmd,unsigned long arg) { -struct syncppp *ap =sp_get (tty); +struct syncppp *ap = tty->disc_data;int __user *p = (int __user *)arg;int err, val;
@@ -316,7 +251,6 @@ ppp_synctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)break ; } -sp_put (ap);return err; }
@@ -325,7 +259,7 @@ static void ppp_sync_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags, size_t count) { - struct syncppp *ap = sp_get(tty); + struct syncppp *ap = tty->disc_data;unsigned long flags;if (!ap)
@@ -335,21 +269,19 @@ ppp_sync_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags,spin_unlock_irqrestore (&ap->recv_lock, flags);if (!skb_queue_empty (&ap->rqueue))tasklet_schedule (&ap->tsk); -sp_put (ap);tty_unthrottle (tty); }static void ppp_sync_wakeup (struct tty_struct *tty) { -struct syncppp *ap =sp_get (tty); +struct syncppp *ap = tty->disc_data;clear_bit (TTY_DO_WRITE_WAKEUP , &tty->flags);if (!ap)return ;set_bit (XMIT_WAKEUP , &ap->xmit_flags);tasklet_schedule (&ap->tsk); -sp_put (ap); }
@@ -359,7 +291,6 @@ static struct tty_ldisc_ops ppp_sync_ldisc = { .name = "pppsync", .open = ppp_sync_open, .close = ppp_sync_close, - .hangup = ppp_sync_hangup, .read = ppp_sync_read, .write = ppp_sync_write, .ioctl = ppp_synctty_ioctl,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 138/733 klp-build: Fix wrong index in funcs cleanup error path
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yafang Shao <[email protected]>
[ Upstream commit 4825ef699cda4c6f2f0586b17a5e225560481da6 ]
In the object allocation loop, when kzalloc() for funcs fails, the cleanup loop uses objs[i].funcs instead of objs[j].funcs. Since objs[i].funcs is still NULL at that point, it repeatedly calls kfree(NULL) and leaks all previously allocated funcs arrays.
--- scripts/livepatch/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
scripts/livepatch/init.c +1 -1
@@ -51,7 +51,7 @@ static int __init livepatch_mod_init(void)if (!funcs) { ret = -ENOMEM ;for (int j =0 ; j < i; j++) -kfree (objs[i].funcs); +kfree (objs[j].funcs); goto err_free_objs; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 139/733 objtool/klp: Fix checksums for constant pool references
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josh Poimboeuf <[email protected]>
[ Upstream commit ac323c9467092479dc1e5bc138c9abbe015b0069 ]
Adding a line of code to __link_shadow_page() with a literal string causes a false positive changed function with GCC:
arch/x86/kvm/kvm.ko.o: changed function: kvm_tdp_mmu_map_private_pfn
While the patch only touched __link_shadow_page(), the string addition triggered a rename of .LC64 -> .LC65 in kvm_tdp_mmu_map_private_pfn() even though the underlying referenced constant data didn't change.
So for .LC* symbols, the suffix is arbitrary but the data isn't. Add the underlying data to the checksum calculation rather than the symbol name.
Clang also uses .LC* symbols, but also uses anonymous data. Both compilers put this data in .rodata.cst<num> sections.
--- tools/objtool/klp-checksum.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
tools/objtool/klp-checksum.c +21 -0
@@ -54,6 +54,19 @@ static int checksum_debug_init(struct objtool_file *file)return 0 ; } +/* +* Detect a reference to anonymous constant pool data which the compiler places +* in .rodata.cst<num> and which either has an .LC<num> symbol associated with +* it or (with Clang) no symbol at all. These are typically initializers for +* local function stack data, so they're considered part of the function rather +* than data per se. +*/ +static bool is_anonymous_const_data (struct symbol *sym) +{ +return strstarts (sym->sec->name,".rodata.cst" ) && + (is_sec_sym (sym) ||strstarts (sym->name,".LC" )); +} +static void checksum_update_insn (struct objtool_file *file,struct symbol *func,struct instruction *insn) {
@@ -129,6 +142,14 @@ static void checksum_update_insn(struct objtool_file *file, struct symbol *func, goto alts; } +if (is_anonymous_const_data (sym)) { +void *cst; + + cst = sym->sec->data->d_buf + sym->offset + offset; +__checksum_update_insn (func, insn, cst, sym->sec->sh.sh_entsize); + goto alts; + } +if (is_sec_sym (sym)) { sym =find_symbol_containing (reloc->sym->sec, offset);if (!sym)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 140/733 ipv6: mcast: fix RCU list diversion in ip6_mc_del1_src()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 93b49239840b91313adbd77b8b52993eff2d08c1 ]
When removing a source filter whose count reaches zero, ip6_mc_del1_src() unlinks psf from pmc->mca_sources. If the filter was previously active, the code moved psf directly into pmc->mca_tomb by updating psf->sf_next.
Because pmc->mca_sources is traversed locklessly under RCU (e.g. by ipv6_chk_mcast_addr()), mutating psf->sf_next before a grace period elapses diverts concurrent readers to the tombstone list. Consequently, readers miss remaining active sources in pmc->mca_sources and improperly examine deleted tombstone entries.
Fix this by allocating a new tombstone node for pmc->mca_tomb (as done in sf_setstate()) and retiring the original psf via kfree_rcu().
--- net/ipv6/mcast.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-)
net/ipv6/mcast.c +11 -7
@@ -2350,14 +2350,18 @@ static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode,if (psf->sf_oldin && !(pmc->mca_flags &MAF_NOREPORT ) && !mld_in_v1_mode (idev)) { - psf->sf_crcount = idev->mc_qrv; -rcu_assign_pointer (psf->sf_next, -mc_dereference (pmc->mca_tomb, idev)); -rcu_assign_pointer (pmc->mca_tomb, psf); - rv =1 ; - }else { -kfree_rcu (psf, rcu); +struct ip6_sf_list *dpsf =kmalloc_obj (*dpsf); + +if (dpsf) { + *dpsf = *psf; + dpsf->sf_crcount = idev->mc_qrv; +rcu_assign_pointer (dpsf->sf_next, +mc_dereference (pmc->mca_tomb, idev)); +rcu_assign_pointer (pmc->mca_tomb, dpsf); + rv =1 ; + } } +kfree_rcu (psf, rcu); }return rv; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 141/733 ipv6: mcast: use copy-on-write RCU updates in ip6_mc_source()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit c073d1b070f171d206b19c98d71739a97f15b3f1 ]
pmc->sflist is read locklessly under rcu_read_lock() by inet6_mc_check() during packet reception in the UDP and RAW multicast receive paths.
ip6_mc_source() mutated psl->sl_addr and psl->sl_count in-place when adding or removing a source filter. Additionally, when expanding the filter buffer, newpsl was published via rcu_assign_pointer() before writing the new source into the array.
Because 16-byte struct in6_addr writes are not atomic and array shifting is not synchronized with RCU readers, concurrent readers in inet6_mc_check() could read torn IPv6 addresses or observe duplicated/missed source entries.
Fix this by switching ip6_mc_source() to copy-on-write RCU updates:
allocate and fully populate newpsl before publishing it via rcu_assign_pointer(), and reclaim the old filter via kfree_rcu(), matching ip6_mc_msfilter().
Also remove the now unused IP6_SFBLOCK macro.
--- include/net/if_inet6.h | 2 - net/ipv6/mcast.c | 98 ++++++++++++++++++++++++------------------ 2 files changed, 56 insertions(+), 44 deletions(-)
include/net/if_inet6.h +0 -2
@@ -88,8 +88,6 @@ struct ip6_sf_socklist { struct in6_addr sl_addr[] __counted_by(sl_max); }; -#define IP6_SFBLOCK 10/* allocate this many at once */ -struct ipv6_mc_socklist {struct in6_addr addr;int ifindex;
net/ipv6/mcast.c +56 -42
139 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 142/733 ipv6: mcast: fix delay calculation in igmp6_join_group()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 75fa9caeb8aaba19c2463dee0b0a1e09d39c04af ]
When joining a multicast group, if a report work is already pending (e.g. scheduled by a query or a previous join), igmp6_join_group() cancels the delayed work and recalculates the delay:
if (cancel_delayed_work(&ma->mca_work)) {
refcount_dec(&ma->mca_refcnt);
delay = ma->mca_work.timer.expires - jiffies;
}Unlike igmp6_group_queried(), igmp6_join_group() did not check if delay >= interval. This leads to two issues:
1. If the timer has already expired (timer.expires <= jiffies), the stale expiry is reused by mod_delayed_work(), causing the second unsolicited report to fire on the very next tick without a randomized delay.
2. If the timer was originally armed by a query with a large maximum response delay, delay could exceed unsolicited_report_interval(ma->idev).
Fix this by initializing delay to unsolicited_report_interval(ma->idev) and re-randomizing it with get_random_u32_below(interval) when delay >= interval, mirroring the logic in igmp6_group_queried().
--- net/ipv6/mcast.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
net/ipv6/mcast.c +6 -2
@@ -2638,7 +2638,7 @@ static void ip6_mc_clear_src(struct ifmcaddr6 *pmc)static void igmp6_join_group (struct ifmcaddr6 *ma) { -unsigned long delay; +unsigned long delay, interval;mc_assert_locked (ma->idev);
@@ -2647,13 +2647,17 @@ static void igmp6_join_group(struct ifmcaddr6 *ma)igmp6_send (&ma->mca_addr, ma->idev->dev,ICMPV6_MGM_REPORT ); - delay =get_random_u32_below (unsolicited_report_interval (ma->idev)); + interval =unsolicited_report_interval (ma->idev); + delay = interval;if (cancel_delayed_work (&ma->mca_work)) {refcount_dec (&ma->mca_refcnt); delay = ma->mca_work.timer.expires - jiffies; } +if (delay >= interval) + delay =get_random_u32_below (interval); +if (!mod_delayed_work (mld_wq, &ma->mca_work, delay))refcount_inc (&ma->mca_refcnt); WRITE_ONCE(ma->mca_flags, ma->mca_flags |
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 143/733 ipv6: mcast: use rcu_assign_pointer() for __rcu list updates
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 0c8f56c583c3250408367880c98e4d6fbc929315 ]
Several places in net/ipv6/mcast.c update RCU-protected lists (np->ipv6_mc_list, idev->mc_list, idev->mc_tomb) using direct pointer assignments instead of rcu_assign_pointer():
1. In __ipv6_dev_mc_dec(), unlinking a group from idev->mc_list did:
*map = ma->next;
without rcu_assign_pointer() while concurrent readers traverse idev->mc_list locklessly under rcu_read_lock().
2. In ipv6_sock_mc_drop() and __ipv6_sock_mc_close(), unlinking a group from np->ipv6_mc_list directly assigned *lnk = mc_lst->next and np->ipv6_mc_list = mc_lst->next without rcu_assign_pointer(), racing with lockless readers in inet6_mc_check().
3. In __ipv6_sock_mc_join(), mc_lst->next was initialized to np->ipv6_mc_list via raw assignment before publishing mc_lst.
4. In mld_del_delrec() and __ipv6_dev_mc_inc(), __rcu source pointers passed into rcu_assign_pointer() lacked explicit dereference helpers.
Fix these by consistently using rcu_assign_pointer() along with mc_dereference() / sock_dereference().
--- net/ipv6/mcast.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-)
net/ipv6/mcast.c +13 -7
@@ -240,7 +240,8 @@ static int __ipv6_sock_mc_join(struct sock *sk, int ifindex,return err; } - mc_lst->next = np->ipv6_mc_list; +rcu_assign_pointer (mc_lst->next, +sock_dereference (np->ipv6_mc_list, sk));rcu_assign_pointer (np->ipv6_mc_list, mc_lst);return 0 ;
@@ -300,7 +301,8 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) lnk = &mc_lst->next) {if ((ifindex ==0 || mc_lst->ifindex == ifindex) &&ipv6_addr_equal (&mc_lst->addr, addr)) { - *lnk = mc_lst->next; +rcu_assign_pointer (*lnk, +sock_dereference (mc_lst->next, sk));__ipv6_sock_mc_drop (sk, mc_lst);return 0 ; }
@@ -333,7 +335,8 @@ void __ipv6_sock_mc_close(struct sock *sk)struct ipv6_mc_socklist *mc_lst;while ((mc_lst =sock_dereference (np->ipv6_mc_list, sk)) !=NULL ) { - np->ipv6_mc_list = mc_lst->next; +rcu_assign_pointer (np->ipv6_mc_list, +sock_dereference (mc_lst->next, sk));__ipv6_sock_mc_drop (sk, mc_lst); } }
@@ -798,9 +801,11 @@ static void mld_del_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im)if (!pmc)return ;if (pmc_prev) -rcu_assign_pointer (pmc_prev->next, pmc->next); +rcu_assign_pointer (pmc_prev->next, +mc_dereference (pmc->next, idev));else -rcu_assign_pointer (idev->mc_tomb, pmc->next); +rcu_assign_pointer (idev->mc_tomb, +mc_dereference (pmc->next, idev)); im->idev = pmc->idev;if (im->mca_sfmode ==MCAST_INCLUDE ) {
@@ -979,7 +984,7 @@ static int __ipv6_dev_mc_inc(struct net_device *dev,return -ENOMEM ; } -rcu_assign_pointer (mc->next, idev->mc_list); +rcu_assign_pointer (mc->next,mc_dereference (idev->mc_list, idev));rcu_assign_pointer (idev->mc_list, mc);mld_del_delrec (idev, mc);
@@ -1013,7 +1018,8 @@ int __ipv6_dev_mc_dec(struct inet6_dev *idev, const struct in6_addr *addr)WRITE_ONCE (ma->mca_users, new_users);if (new_users ==0 ) { - *map = ma->next; +rcu_assign_pointer (*map, +mc_dereference (ma->next, idev));igmp6_group_dropped (ma); inet6_ifmcaddr_notify(idev->dev, ma,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 144/733 ipv6: mcast: use jiffies_delta_to_clock_t() in igmp6_mc_seq_show()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit b4cf4a092a7bdaa62acca39c28f386b6d1674968 ]
If a multicast group timer has expired but the delayed work has not yet run to clear MAF_TIMER_RUNNING, expires - jiffies produces a negative value.
Because unsigned arithmetic was used with jiffies_to_clock_t(), expires - jiffies underflows to a huge value and reports invalid timer durations in /proc/net/igmp6.
Use jiffies_delta_to_clock_t() with a signed long delta to properly cap expired deltas to 0, matching IPv4 igmp_mc_seq_show() and commit a399a8053164 ("time: jiffies_delta_to_clock_t() helper to the rescue").
--- net/ipv6/mcast.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/ipv6/mcast.c +2 -2
@@ -3028,7 +3028,7 @@ static int igmp6_mc_seq_show(struct seq_file *seq, void *v)struct ifmcaddr6 *im = (struct ifmcaddr6 *)v;struct igmp6_mc_iter_state *state =igmp6_mc_seq_private (seq);unsigned int mca_flags =READ_ONCE (im->mca_flags); -unsigned long expires =READ_ONCE (im->mca_work.timer.expires); +long delta =READ_ONCE (im->mca_work.timer.expires) - jiffies; seq_printf(seq, "%-4d %-15s %pi6 %5d %08X %ld\n",
@@ -3036,7 +3036,7 @@ static int igmp6_mc_seq_show(struct seq_file *seq, void *v) &im->mca_addr,READ_ONCE (im->mca_users), mca_flags, (mca_flags &MAF_TIMER_RUNNING ) ? - jiffies_to_clock_t(expires - jiffies) : 0); + jiffies_delta_to_clock_t(delta) : 0);return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 145/733 ip6_gre: check tunnel info before xmit in ip6gre_tunnel_xmit
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 97cc84dad1d7f68a36b71b69b361d88482707673 ]
Shuangpeng Bai reported a KASAN slab-use-after-free in ip6gre_tunnel_xmit().
The precise KASAN bug was caused by ip6_tnl_xmit() consuming the skb during headroom expansion and returning an error, while ip6gre_tunnel_xmit() still held the stale pointer and called skb_tunnel_info_txcheck(skb) at tx_err. That specific bug was fixed by commit 87f21b59ddc6 ("ip6_tunnel: use skb_cow_head() in ip6_tnl_xmit()").
However, calling skb_tunnel_info_txcheck(skb) at the tx_err label after the transmission attempt remains problematic:
Downstream helpers like ip6_tnl_xmit() call skb_scrub_packet(), which drops the skb's metadata_dst before transmission. If an error occurs later during transmit, inspecting skb at tx_err sees a scrubbed dst and misclassifies tx_errors vs tx_dropped.
Commit e5f7e211b6aa ("ip6gre: avoid tx_error when sending MLD/DAD on external tunnels") already handled this correctly in ip6erspan_tunnel_xmit() by checking and caching tun_info before transmit.
Align ip6gre_tunnel_xmit() with ip6erspan_tunnel_xmit() by caching tun_info before xmit and checking it at tx_err.
--- net/ipv6/ip6_gre.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
net/ipv6/ip6_gre.c +5 -1
@@ -878,6 +878,7 @@ static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev)static netdev_tx_t ip6gre_tunnel_xmit (struct sk_buff *skb,struct net_device *dev) { +struct ip_tunnel_info *tun_info =NULL ;struct ip6_tnl *t =netdev_priv (dev);__be16 payload_protocol;int ret;
@@ -888,6 +889,9 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,if (!ip6_tnl_xmit_ctl (t, &t->parms.laddr, &t->parms.raddr)) goto tx_err; +if (t->parms.collect_md) + tun_info =skb_tunnel_info_txcheck (skb); + payload_protocol =skb_protocol (skb, true);switch (payload_protocol) {case htons (ETH_P_IP ):
@@ -907,7 +911,7 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,return NETDEV_TX_OK ; tx_err: -if (!t->parms.collect_md || !IS_ERR (skb_tunnel_info_txcheck (skb))) +if (!IS_ERR (tun_info))DEV_STATS_INC (dev, tx_errors);DEV_STATS_INC (dev, tx_dropped);kfree_skb (skb);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 146/733 tipc: fix NULL deref in tipc_named_node_up() on empty publication list
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tung Nguyen <[email protected]>
[ Upstream commit b3b76e9f4f2476f1135b2ba7743a821db4a0df4b ]
User-space applications can bind a large number of service addresses to one or more sockets. Each binding of a local-scope service address inserts one entry (publication) into the TIPC name table. If the number of these publications exceeds TIPC_MAX_PUBL (65535), protocol service types (such as node state and link state) are no longer inserted into the name table. This causes two issues:
1. User-space applications subscribing to node or link up/down events stop receiving notifications.
2. A NULL pointer dereference can occur:
BUG: kernel NULL pointer dereference, address: 00000000000000d0 ... CPU: 0 UID: 0 PID: 0 Comm: swapper/0 Not tainted 7.2.0-rc4-default+ #5 PREEMPT(full) ... RIP: 0010:tipc_named_node_up (./include/linux/skbuff.h:2251 net/tipc/name_distr.c:195 net/tipc/name_distr.c:221) ... Call Trace: <IRQ> tipc_node_write_unlock (net/tipc/node.c:428) tipc_rcv (net/tipc/node.c:934 net/tipc/node.c:2189) tipc_udp_recv (net/tipc/udp_media.c:389)
Thread 1 (tipc_net_finalize) | Thread 2 (named_distribute)
-----------------------------|-----------------------------
| ...
| list_for_each_entry(publ, pls, binding_node) {
| ...
| __skb_queue_tail(list, skb);
| ...
| }
| ...
| hdr = buf_msg(skb_peek_tail(list));
... |
tipc_nametbl_publish(); | If 'tipc_nametbl_publish()' (Thread 1) fails because the number of local publications reaches TIPC_MAX_PUBL, list (Thread 2) will be empty. As a result, NULL is passed to 'buf_msg()', leading to a NULL pointer dereference.
Fix these issues by allowing protocol service types (node state, link state, and topology server) to be inserted into the name table unconditionally. This ensures that users subscribing to these types always receive notifications. In addition, the maximum number of local user publications is reduced to (TIPC_MAX_PUBL - 1). This ensures that the maximum bulk size calculated in tipc_link_set_queue_limits() remains valid.
--- net/tipc/name_table.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-)
net/tipc/name_table.c +26 -4
@@ -763,21 +763,40 @@ struct publication *tipc_nametbl_publish(struct net *net, struct tipc_uaddr *ua, struct tipc_socket_addr *sk, u32 key) {struct name_table *nt =tipc_name_table (net); +u32 max_user_pub =TIPC_MAX_PUBL -1 ;struct tipc_net *tn =tipc_net (net);struct publication *p =NULL ;struct sk_buff *skb =NULL ; +bool protocol_type = false;u32 rc_dests; +if (ua->sr.type ==TIPC_NODE_STATE || ua->sr.type ==TIPC_LINK_STATE || + ua->sr.type ==TIPC_TOP_SRV ) + protocol_type = true; +spin_lock_bh (&tn->nametbl_lock); +if (protocol_type) + goto insert; -if (nt->local_publ_count >=TIPC_MAX_PUBL ) { -pr_warn ("Bind failed, max limit %u reached\n" ,TIPC_MAX_PUBL ); +/* Reserve one entry for node state service type because it has cluster +* scope and it is distributed in bulk. So, the maximum number of user's +* publications is (TIPC_MAX_PUBL - 1). +*/ +if (nt->local_publ_count >= max_user_pub) { +pr_warn ("Bind failed, max limit %u reached\n" , max_user_pub); goto exit; } +insert: p =tipc_nametbl_insert_publ (net, ua, sk, key);if (p) { - nt->local_publ_count++; +/* Not count node state, link state and topology server types +* so that maximum nt->local_publ_count does not prevent +* protocol service types from being inserted into the name +* table. +*/ +if (!protocol_type) + nt->local_publ_count++; skb =tipc_named_publish (net, p); } rc_dests = nt->rc_dests;
@@ -810,7 +829,10 @@ void tipc_nametbl_withdraw(struct net *net, struct tipc_uaddr *ua, p =tipc_nametbl_remove_publ (net, ua, sk, key);if (p) { - nt->local_publ_count--; +if (p->sr.type !=TIPC_NODE_STATE && + p->sr.type !=TIPC_LINK_STATE && + p->sr.type !=TIPC_TOP_SRV ) + nt->local_publ_count--; skb =tipc_named_withdraw (net, p);list_del_init (&p->binding_sock);kfree_rcu (p, rcu);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 147/733 tipc: Dont send random pad bytes in RESET/ACTIVATE messages
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Laight <[email protected]>
[ Upstream commit 81c600c26302a27852ed8b19c5f2f647ea3555c9 ]
The interface name is passed in a fixed length (TIPC_MAX_IF_NAME) buffer. Replace the strcpy(data, l->if_name) with memcpy() so that the pad bytes are actually written (l->if_name[] is zero padded) rather than sending random bytes from the skb to the remote system.
Replace two other strcpy() with strscpy().
--- net/tipc/link.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
net/tipc/link.c +3 -3
@@ -504,7 +504,7 @@ bool tipc_link_create(struct net *net, char *if_name, int bearer_id,snprintf (l->name,sizeof (l->name),"%s:%s-%s:unknown" , self_str, if_name, peer_str); -strcpy (l->if_name, if_name); +strscpy (l->if_name, if_name); l->addr = peer; l->peer_caps = peer_caps; l->net = net;
@@ -574,7 +574,7 @@ bool tipc_link_bc_create(struct net *net, u32 ownnode, u32 peer, u8 *peer_id,snprintf (l->name,sizeof (l->name),"%s:%s" , tipc_bclink_name, peer_str); }else { -strcpy (l->name, tipc_bclink_name); +strscpy (l->name, tipc_bclink_name); }trace_tipc_link_reset (l,TIPC_DUMP_ALL ,"bclink created!" );tipc_link_reset (l);
@@ -1898,7 +1898,7 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,msg_set_dest_session (hdr, l->peer_session); }msg_set_max_pkt (hdr, l->advertised_mtu); -strcpy (data, l->if_name); +memcpy (data, l->if_name,TIPC_MAX_IF_NAME );msg_set_size (hdr,INT_H_SIZE +TIPC_MAX_IF_NAME );skb_trim (skb,INT_H_SIZE +TIPC_MAX_IF_NAME ); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 148/733 ipv6: sr: restore network header before routing and forwarding
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 975b5b067f525a1b1338c4a3bee1c46545801518 ]
ipv6_srh_rcv() runs with skb->data at the Segment Routing Header (SRH) while skb_network_header() points at the IPv6 header.
When segments_left > 0, ipv6_srh_rcv() previously restored the skb->data position by pushing sizeof(struct ipv6hdr), assuming the SRH immediately followed the fixed IPv6 header. If another extension header (such as a Hop-by-Hop options header) precedes the SRH, skb_network_offset() remained negative.
This led to two problems:
1. During ip6_route_input(), fib6_rules_early_flow_dissect() invokes __skb_flow_dissect() which passes the negative skb_network_offset() to flow dissection, breaking BPF and C flow dissector logic.
2. If forwarded via ip6_forward() or redirected via act_mirred, downstream handlers (like sch_fragment() or neighbour output) pass the negative offset as an unsigned length, triggering OOB memcpy or buffer overflows.
Fix this by pushing -skb_network_offset(skb) before routing, ensuring skb_network_offset(skb) is 0 for route lookup / flow dissection as well as downstream forwarding. On the loopback path, pull skb_transport_offset(skb) to restore skb->data to the SRH before looping back.
--- net/ipv6/exthdrs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/ipv6/exthdrs.c +2 -2
@@ -445,7 +445,7 @@ static int ipv6_srh_rcv(struct sk_buff *skb, struct inet6_dev *idev) hdr->segments_left--; addr = hdr->segments + hdr->segments_left; -skb_push (skb,sizeof (struct ipv6hdr )); +skb_push (skb, -skb_network_offset (skb));if (skb->ip_summed ==CHECKSUM_COMPLETE )seg6_update_csum (skb);
@@ -469,7 +469,7 @@ static int ipv6_srh_rcv(struct sk_buff *skb, struct inet6_dev *idev) }ipv6_hdr (skb)->hop_limit--; -skb_pull (skb,sizeof (struct ipv6hdr )); +skb_pull (skb,skb_transport_offset (skb)); goto looped_back; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 149/733 af_packet: Dont cast tpacket_hdr.tp_len to int in tpacket_parse_header().
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit 73e594c19b4f815d8343461cec7074c4713bbde7 ]
syzbot reported BUG() in sock_sendmsg_nosec(). [0]
The problem is that tpacket_parse_header() casts user-provided tpacket_hdr.tp_len, which is u32, to int.
If the length is larger than INT_MAX, the following condition in tpacket_parse_header() passes,
if (unlikely(tp_len > size_max))
and any negative value can be returned to the caller, up to sock_sendmsg_nosec().
The repro set tpacket_hdr.tp_len to 0xfffffdef, which is cast to -EIOCBQUEUED (-529), triggering BUG() in sock_sendmsg_nosec().
*(uint64_t*)0x200000000008 = 0xfffffdef; ... syscall(__NR_write, /*fd=*/r[0], /*buf=*/0x200000000000ul, /*count=*/1ul);
Let's define the local tp_len as u32 in tpacket_parse_header().
[0]:
kernel BUG at net/socket.c:803!
Oops: invalid opcode: 0000 [#1] SMP KASAN PTI CPU: 0 UID: 0 PID: 5628 Comm: syz-executor176 Not tainted syzkaller #0 PREEMPT(full) Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/24/2026 RIP: 0010:sock_sendmsg_nosec+0x145/0x180 net/socket.c:803 Code: 06 67 48 0f b9 3a eb 95 e8 e8 3a 22 f8 48 89 df 4c 89 f6 4c 89 e2 4d 89 fb 2e e8 32 a5 5c 16 e9 51 ff ff ff e8 cc 3a 22 f8 90 <0f> 0b e8 c4 3a 22 f8 48 83 c3 18 48 89 d8 48 c1 e8 03 42 80 3c 28 RSP: 0018:ffffc90003aefb48 EFLAGS: 00010293 RAX: ffffffff89a578d4 RBX: ffff8880764c67c0 RCX: ffff88807fb23e80 RDX: 0000000000000000 RSI: 00000000fffffdef RDI: 00000000fffffdef RBP: 00000000fffffdef R08: ffffc90003aef747 R09: 1ffff9200075dee8 R10: dffffc0000000000 R11: fffff5200075dee9 R12: 0000000000000001 R13: dffffc0000000000 R14: ffffc90003aefbc0 R15: ffffffff8aac4310 FS: 000055559101b400(0000) GS:ffff888124ce0000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000200000000210 CR3: 0000000073dca000 CR4: 00000000003526f0 Call Trace: <TASK> __sock_sendmsg net/socket.c:815 [inline] sock_write_iter+0x2de/0x3e0 net/socket.c:1266 new_sync_write fs/read_write.c:595 [inline] vfs_write+0x612/0xba0 fs/read_write.c:687 ksys_write+0x150/0x270 fs/read_write.c:739 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline] do_syscall_64+0x166/0x520 arch/x86/entry/syscall_64.c:84 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f173130ecb9 Code: c0 79 93 eb d5 48 8d 7c 1d 00 eb 99 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 d8 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007ffd67e44248 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 RAX: ffffffffffffffda RBX: 0000200000000000 RCX: 00007f173130ecb9 RDX: 0000000000000001 RSI: 0000200000000000 RDI: 0000000000000003 RBP: 0000000000000001 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 00007ffd67e44388 R13: 0000000000000002 R14: 00002000000000c0 R15: 0000000000000002 </TASK>
--- net/packet/af_packet.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
net/packet/af_packet.c +3 -2
@@ -2675,7 +2675,8 @@ static int tpacket_parse_header(struct packet_sock *po, void *frame, int size_max, void **data) { union tpacket_uhdr ph; -int tp_len, off; +u32 tp_len; +int off; ph.raw = frame;
@@ -2695,7 +2696,7 @@ static int tpacket_parse_header(struct packet_sock *po, void *frame,break ; }if (unlikely (tp_len > size_max)) { -pr_err ("packet size is too long (%d > %d)\n" , tp_len, size_max); +pr_err ("packet size is too long (%u > %d)\n" , tp_len, size_max);return -EMSGSIZE ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 150/733 staging: fbtft: make dirty_lock IRQ-safe
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hui Su <[email protected]>
[ Upstream commit f576944a59f31bcffff121117ebf452c5dd162b7 ]
fbtft_mkdirty() can be reached from the fbcon rendering path while processing printk() in hardirq context. Meanwhile, dirty_lock is also taken by fbtft_deferred_io() in workqueue context with local interrupts enabled.
Lockdep reports a possible IRQ lock inversion involving dirty_lock and console_owner. A hardirq can interrupt a CPU holding dirty_lock and enter the console rendering path, which can attempt to acquire dirty_lock again.
The following lockdep report was observed on an RK3566 system with CONFIG_PROVE_LOCKING enabled:
WARNING: possible irq lock inversion dependency detected swapper/2/0 just changed the state of lock:
(console_owner){-...}-{0:0}
but this lock took another, HARDIRQ-unsafe lock in the past:
(&par->dirty_lock){+.+.}-{2:2}
CPU0 CPU1
---- ----
lock(&par->dirty_lock);
local_irq_disable();
lock(console_owner);
lock(&par->dirty_lock);
<Interrupt>
lock(console_owner);*** DEADLOCK ***
Use spin_lock_irqsave() for fbtft_mkdirty() and spin_lock_irq() for fbtft_deferred_io(). They only access the dirty line range, so the IRQ-off regions remain short.
--- drivers/staging/fbtft/fbtft-core.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
drivers/staging/fbtft/fbtft-core.c +5 -4
@@ -298,14 +298,15 @@ static void fbtft_mkdirty(struct fb_info *info, int y, int height) {struct fbtft_par *par = info->par;struct fb_deferred_io *fbdefio = info->fbdefio; +unsigned long flags;/* Mark display lines/area as dirty */ -spin_lock (&par->dirty_lock); +spin_lock_irqsave (&par->dirty_lock, flags);if (y < par->dirty_lines_start) par->dirty_lines_start = y;if (y + height -1 > par->dirty_lines_end) par->dirty_lines_end = y + height -1 ; -spin_unlock (&par->dirty_lock); +spin_unlock_irqrestore (&par->dirty_lock, flags);/* Schedule deferred_io to update display (no-op if already on queue)*/ schedule_delayed_work (&info->deferred_work, fbdefio->delay);
@@ -318,13 +319,13 @@ static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagereflisstruct fb_deferred_io_pageref *pageref;unsigned int y_low =0 , y_high =0 ; -spin_lock (&par->dirty_lock); +spin_lock_irq (&par->dirty_lock); dirty_lines_start = par->dirty_lines_start; dirty_lines_end = par->dirty_lines_end;/* set display line markers as clean */ par->dirty_lines_start = par->info->var.yres -1 ; par->dirty_lines_end =0 ; -spin_unlock (&par->dirty_lock); +spin_unlock_irq (&par->dirty_lock);/* Mark display lines as dirty */ list_for_each_entry (pageref, pagereflist, list) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 151/733 net: bonding: annotate lockless writes with WRITE_ONCE()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit bc93419130bb70fabf6561e197054caae85c160c ]
Several fields in bonding are read locklessly using READ_ONCE() (or ACCESS_ONCE() previously) but have corresponding writes that do not use WRITE_ONCE().
Add WRITE_ONCE() annotations to:
- bond->send_peer_notif decrements in bond_peer_notify_may_events() and reset in bond_close().
- bond->slave_cnt increments and decrements in bond_enslave() and __bond_release_one().
- bond->recv_probe updates in bond_open(), bond_option_arp_interval_set() and rlb_initialize().
- slaves->count decrement in bond_skip_slave().
--- drivers/net/bonding/bond_alb.c | 2 +- drivers/net/bonding/bond_main.c | 14 +++++++------- drivers/net/bonding/bond_options.c | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-)
drivers/net/bonding/bond_alb.c +1 -1
@@ -875,7 +875,7 @@ static int rlb_initialize(struct bonding *bond)spin_unlock_bh (&bond->mode_lock);/* register to receive ARPs */ - bond->recv_probe = rlb_arp_recv; +WRITE_ONCE (bond->recv_probe, rlb_arp_recv);return 0 ; }
drivers/net/bonding/bond_main.c +7 -7
@@ -1245,7 +1245,7 @@ static void bond_peer_notify_may_events(struct bonding *bond, bool force) }if (notified || force) - bond->send_peer_notif--; +WRITE_ONCE (bond->send_peer_notif, bond->send_peer_notif -1 ); }/**
@@ -2284,7 +2284,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev, } } - bond->slave_cnt++; +WRITE_ONCE (bond->slave_cnt, bond->slave_cnt +1 );netdev_compute_master_upper_features (bond->dev, true);bond_set_carrier (bond);
@@ -2533,7 +2533,7 @@ static int __bond_release_one(struct net_device *bond_dev,unblock_netpoll_tx ();synchronize_rcu (); - bond->slave_cnt--; +WRITE_ONCE (bond->slave_cnt, bond->slave_cnt -1 );if (!bond_has_slaves (bond)) {call_netdevice_notifiers (NETDEV_CHANGEADDR , bond->dev);
@@ -4385,13 +4385,13 @@ static int bond_open(struct net_device *bond_dev)if (bond->params.arp_interval) {/* arp interval, in milliseconds. */ queue_delayed_work (bond->wq, &bond->arp_work,0 ); - bond->recv_probe = bond_rcv_validate; +WRITE_ONCE (bond->recv_probe, bond_rcv_validate); }if (BOND_MODE (bond) ==BOND_MODE_8023AD ) {queue_delayed_work (bond->wq, &bond->ad_work,0 );/* register to receive LACPDUs */ - bond->recv_probe = bond_3ad_lacpdu_recv; +WRITE_ONCE (bond->recv_probe, bond_3ad_lacpdu_recv);bond_3ad_initiate_agg_selection (bond,1 );bond_for_each_slave (bond, slave, iter)
@@ -4413,7 +4413,7 @@ static int bond_close(struct net_device *bond_dev)struct slave *slave;bond_work_cancel_all (bond); - bond->send_peer_notif =0 ; +WRITE_ONCE (bond->send_peer_notif,0 );WRITE_ONCE (bond->recv_probe,NULL );/* Wait for any in-flight RX handlers */
@@ -5124,7 +5124,7 @@ static void bond_skip_slave(struct bond_up_slave *slaves,if (skipslave == slaves->arr[idx]) { slaves->arr[idx] = slaves->arr[slaves->count -1 ]; - slaves->count--; +WRITE_ONCE (slaves->count, slaves->count -1 );break ; } }
drivers/net/bonding/bond_options.c +2 -2
@@ -1147,11 +1147,11 @@ static int bond_option_arp_interval_set(struct bonding *bond,*/ if (!newval->value) {if (bond->params.arp_validate) - bond->recv_probe =NULL ; +WRITE_ONCE (bond->recv_probe,NULL );cancel_delayed_work_sync (&bond->arp_work); }else {/* arp_validate can be set only in active-backup mode */ - bond->recv_probe = bond_rcv_validate; +WRITE_ONCE (bond->recv_probe, bond_rcv_validate);cancel_delayed_work_sync (&bond->mii_work);queue_delayed_work (bond->wq, &bond->arp_work,0 ); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 152/733 ALSA: hda: restore MFG widget enumeration after core split
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xu Rao <[email protected]>
[ Upstream commit 32d7226e6105c257ef7b3d0ec819f11a81f53b6d ]
Before commit 7639a06c23c7 ("ALSA: hda - Move a part of hda_codec stuff into hdac_device"), widget enumeration selected the function group with
codec->afg ? codec->afg : codec->mfg
and read subordinate nodes from that group.
The core split moved this logic into snd_hdac_refresh_widgets(), but hard-coded codec->afg there. For an MFG-only codec, codec->afg is zero, so the Root Node is queried and codec->start_nid/num_nodes are populated from the function-group range instead of the MFG's subordinate nodes.
Restore the pre-split AFG-or-MFG selection.
--- sound/hda/core/device.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
sound/hda/core/device.c +3 -2
@@ -404,6 +404,7 @@ static void setup_fg_nodes(struct hdac_device *codec)*/ int snd_hdac_refresh_widgets (struct hdac_device *codec) { +hda_nid_t fg = codec->afg ? codec->afg : codec->mfg;hda_nid_t start_nid;int nums, err =0 ;
@@ -412,10 +413,10 @@ int snd_hdac_refresh_widgets(struct hdac_device *codec)* widgets array. */ guard (mutex)(&codec->widget_lock); - nums =snd_hdac_get_sub_nodes (codec, codec->afg, &start_nid); + nums =snd_hdac_get_sub_nodes (codec, fg, &start_nid);if (!start_nid || nums <=0 || nums >=0xff ) {dev_err (&codec->dev,"cannot read sub nodes for FG 0x%02x\n" , - codec->afg); + fg);return -EINVAL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 153/733 s390/boot: Fix physical memory search range
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vasily Gorbik <[email protected]>
[ Upstream commit a0c798ed4103316c23938bdf625af364fbd38016 ]
search_mem_end() calculates the number of 1MB blocks with a signed int literal. CONFIG_MAX_PHYSMEM_BITS values of 51 and above either overflow the signed int or shift beyond its width. This produces an invalid search range when the binary-search memory detection fallback is used.
Use an unsigned long literal so the full supported physical address range is represented.
--- arch/s390/boot/physmem_info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/s390/boot/physmem_info.c +1 -1
@@ -141,7 +141,7 @@ static int tprot(unsigned long addr)static unsigned long search_mem_end (void ) { -unsigned long range =1 << (MAX_PHYSMEM_BITS -20 );/* in 1MB blocks */ +unsigned long range =1UL << (MAX_PHYSMEM_BITS -20 );/* in 1MB blocks */ unsigned long offset =0 ;unsigned long pivot;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 154/733 s390/boot: Avoid IPL parameter append past command line
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vasily Gorbik <[email protected]>
[ Upstream commit d76181dfabdaa720703167393704efacba343442 ]
A command line may occupy all but the terminating byte of COMMAND_LINE_SIZE. In that case append_ipl_block_parm() passes a zero size to the IPL parameter conversion helpers and points the destination one byte past early_command_line. The helpers subtract one from the unsigned size and write the converted parameter outside the command line buffer.
Convert the IPL parameter in the command line parsing buffer first. A parameter beginning with '=' can then replace the existing command line regardless of its length, while other parameters are appended only when space remains.
--- arch/s390/boot/ipl_parm.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-)
arch/s390/boot/ipl_parm.c +12 -14
@@ -23,6 +23,7 @@ struct parmarea parmarea __section(".parmarea") = { };char __bootdata (early_command_line )[COMMAND_LINE_SIZE ]; +static char command_line_buf[COMMAND_LINE_SIZE ];unsigned int __bootdata_preserved (zlib_dfltcc_support ) =ZLIB_DFLTCC_FULL ;struct ipl_parameter_block __bootdata_preserved (ipl_block );
@@ -135,31 +136,29 @@ static size_t ipl_block_get_ascii_scpdata(char *dest, size_t size,static void append_ipl_block_parm (void ) { -char *parm, *delim; -size_t len, rc =0 ; +size_t len, extra =0 ; +char *delim; len =strlen (early_command_line); - - delim = early_command_line + len;/* '\0' character position */ - parm = early_command_line + len +1 ;/* append right after '\0' */ + delim = early_command_line + len;/* '\0' character position */ switch (ipl_block.pb0_hdr.pbt) {case IPL_PBT_CCW : - rc =ipl_block_get_ascii_vmparm ( - parm,COMMAND_LINE_SIZE - len -1 , &ipl_block); + extra =ipl_block_get_ascii_vmparm (command_line_buf,sizeof (command_line_buf), &ipl_block);break ;case IPL_PBT_FCP :case IPL_PBT_NVME :case IPL_PBT_ECKD : - rc =ipl_block_get_ascii_scpdata ( - parm,COMMAND_LINE_SIZE - len -1 , &ipl_block); + extra =ipl_block_get_ascii_scpdata (command_line_buf,sizeof (command_line_buf), &ipl_block);break ; } -if (rc) { -if (*parm =='=' ) -memmove (early_command_line, parm +1 , rc); -else +if (extra) { +if (command_line_buf[0 ] =='=' ) { +memmove (early_command_line, command_line_buf +1 , extra); + }else if (len <COMMAND_LINE_SIZE -2 ) { *delim =' ' ;/* replace '\0' with space */ +sized_strscpy (delim +1 , command_line_buf,COMMAND_LINE_SIZE - len -1 ); + } } }
@@ -245,7 +244,6 @@ static void modify_fac_list(char *str)check_cleared_facilities (); } -static char command_line_buf[COMMAND_LINE_SIZE ];void parse_boot_command_line (void ) {char *param, *val;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 155/733 ASoC: fsl_micfil: balance mclk enable/disable
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ștefan Ghețu <[email protected]>
[ Upstream commit d3dbccfe6afa7b9a6a7ed65cfaa76a47fa050a56 ]
hw_params() enables mclk unconditionally and hw_free() disables it unconditionally, but the PCM core does not guarantee 1:1 pairing:hw_free() can run without hw_params(), and hw_params() can be called multiple times from the SETUP state. This triggers an "already disabled" WARN() in the first case and leaks an enable reference in the second, leaving the clock ungateable.
Guard both sides with the existing mclk_flag, as fsl_sai.c does with mclk_streams.
--- sound/soc/fsl/fsl_micfil.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-)
sound/soc/fsl/fsl_micfil.c +13 -8
@@ -953,12 +953,17 @@ static int fsl_micfil_reparent_rootclk(struct fsl_micfil *micfil, unsigned int s/* Get root clock */ clk = micfil->mclk; -/* Disable clock first, for it was enabled by pm_runtime */ +/* Reparent root clock to the PLL matching this sample rate */ fsl_asoc_reparent_pll_clocks (dev, clk, micfil->pll8k_clk, micfil->pll11k_clk, ratio); - ret =clk_prepare_enable (clk); -if (ret) -return ret; + +/* Enable only once; hw_params can be called multiple times */ +if (!micfil->mclk_flag) { + ret =clk_prepare_enable (clk); +if (ret) +return ret; + micfil->mclk_flag = true; + }return 0 ; }
@@ -991,8 +996,6 @@ static int fsl_micfil_hw_params(struct snd_pcm_substream *substream,if (ret)return ret; - micfil->mclk_flag = true; -/* floor(K * CLKDIV) */ switch (micfil->quality) {case QUALITY_HIGH :
@@ -1068,8 +1071,10 @@ static int fsl_micfil_hw_free(struct snd_pcm_substream *substream, {struct fsl_micfil *micfil =snd_soc_dai_get_drvdata (dai); -clk_disable_unprepare (micfil->mclk); - micfil->mclk_flag = false; +if (micfil->mclk_flag) { +clk_disable_unprepare (micfil->mclk); + micfil->mclk_flag = false; + }return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 156/733 ASoC: amd: renoir: fix disable_pdm_interrupts() to clear mask bits
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: wangdicheng <[email protected]>
[ Upstream commit 0c06c4ce0206290c9a934a1e7196aaa86adfe018 ]
disable_pdm_interrupts() uses |= ~PDM_DMA_INTR_MASK which sets all bits except the PDM DMA interrupt bit instead of clearing only the PDM DMA interrupt bit. Use &= ~PDM_DMA_INTR_MASK to clear only the target bit.
--- sound/soc/amd/renoir/acp3x-pdm-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
sound/soc/amd/renoir/acp3x-pdm-dma.c +1 -1
@@ -104,7 +104,7 @@ static void disable_pdm_interrupts(void __iomem *acp_base)u32 ext_int_ctrl; ext_int_ctrl =rn_readl (acp_base +ACP_EXTERNAL_INTR_CNTL ); - ext_int_ctrl |= ~PDM_DMA_INTR_MASK ; + ext_int_ctrl &= ~PDM_DMA_INTR_MASK ;rn_writel (ext_int_ctrl, acp_base +ACP_EXTERNAL_INTR_CNTL ); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 157/733 ASoC: amd: yc: fix memory leak in acp6x_pdm_dma_close()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: wangdicheng <[email protected]>
[ Upstream commit 1b67e0d3b9691d7b6b74e18960ddd2be24f9dc9d ]
acp6x_pdm_dma_close() does not free the runtime->private_data buffer allocated in acp6x_pdm_dma_open(). Add the missing kfree.
--- sound/soc/amd/yc/acp6x-pdm-dma.c | 2 ++ 1 file changed, 2 insertions(+)
sound/soc/amd/yc/acp6x-pdm-dma.c +2 -0
@@ -275,9 +275,11 @@ static int acp6x_pdm_dma_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) {struct pdm_dev_data *adata =dev_get_drvdata (component->dev); +struct snd_pcm_runtime *runtime = substream->runtime;acp6x_disable_pdm_interrupts (adata->acp6x_base); adata->capture_stream =NULL ; +kfree (runtime->private_data);return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 158/733 block: save page offset gaps in cloned bio
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Keith Busch <[email protected]>
[ Upstream commit 96c8ea3c5add7920b3c43840d1ea76b3354c8d2d ]
The cloned bio needs to inherit the accumulated gaps between vectors so that we can know if this bio can subscribe to the iova coalescing optimization.
When cloning for a split, the gap only applies to the front bio since that's as far as has been processed. The remaining bio can reset its gaps to 0 since it advanced past the checked vectors, and will start its accounting from there on the next split check.
--- block/bio.c | 9 +++++++++ 1 file changed, 9 insertions(+)
block/bio.c +9 -0
@@ -859,6 +859,7 @@ static int __bio_clone(struct bio *bio, struct bio *bio_src, gfp_t gfp) bio->bi_ioprio = bio_src->bi_ioprio; bio->bi_write_hint = bio_src->bi_write_hint; bio->bi_write_stream = bio_src->bi_write_stream; + bio->bi_bvec_gap_bit = bio_src->bi_bvec_gap_bit; bio->bi_iter = bio_src->bi_iter;if (bio->bi_bdev) {
@@ -1913,6 +1914,14 @@ struct bio *bio_split(struct bio *bio, int sectors,bio_advance (bio, split->bi_iter.bi_size); +/* +* The gap bit is set when splitting to limits and only applies to the +* front bio that was split off. The remaining bio will calcualte its +* gap value when it is subsequently split to limits, so it is safe to +* re-initialize the value back to 0. +*/ + bio->bi_bvec_gap_bit =0 ; +if (bio_flagged (bio,BIO_TRACE_COMPLETION ))bio_set_flag (split,BIO_TRACE_COMPLETION );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 159/733 ASoC: cs35l56: Request IRQ in cs35l56_common_probe()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Richard Fitzgerald <[email protected]>
[ Upstream commit 888162dabf64128603b12ac2d23236cf2086b7ef ]
Call cs35l56_irq_request() in cs35l56_common_probe() instead of calling it afterwards in the probe() for each bus type.
Calling cs35l56_irq_request() in each bus probe() is a legacy of dealing with the oddities of the SoundWire framework. It's no longer serving any useful purpose to do it outside of the main cs35l56_common_probe().
Stable-dep-of: 1d80a4792f1d ("ASoC: cs35l56: Fix probe deadlock waiting for SoundWire enumeration")
--- sound/soc/codecs/cs35l56-i2c.c | 10 +--------- sound/soc/codecs/cs35l56-sdw.c | 6 +----- sound/soc/codecs/cs35l56-spi.c | 10 +--------- sound/soc/codecs/cs35l56.c | 12 ++++++++++-- sound/soc/codecs/cs35l56.h | 2 +- 5 files changed, 14 insertions(+), 26 deletions(-)
sound/soc/codecs/cs35l56-i2c.c +1 -9
@@ -51,15 +51,7 @@ static int cs35l56_i2c_probe(struct i2c_client *client)return dev_err_probe (cs35l56->base.dev, ret,"Failed to allocate register map\n" ); } - ret =cs35l56_common_probe (cs35l56); -if (ret !=0 ) -return ret; - - ret =cs35l56_irq_request (&cs35l56->base, client->irq); -if (ret <0 ) -cs35l56_remove (cs35l56); - -return ret; +return cs35l56_common_probe (cs35l56, client->irq); }static void cs35l56_i2c_remove (struct i2c_client *client)
sound/soc/codecs/cs35l56-sdw.c +1 -5
@@ -483,11 +483,7 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi/* Start in cache-only until device is enumerated */ regcache_cache_only (cs35l56->base.regmap, true); - ret =cs35l56_common_probe (cs35l56); -if (ret !=0 ) -return ret; - -return 0 ; +return cs35l56_common_probe (cs35l56, -EINVAL ); }static void cs35l56_sdw_remove (struct sdw_slave *peripheral)
sound/soc/codecs/cs35l56-spi.c +1 -9
@@ -40,15 +40,7 @@ static int cs35l56_spi_probe(struct spi_device *spi)if (ret)return ret; - ret =cs35l56_common_probe (cs35l56); -if (ret !=0 ) -return ret; - - ret =cs35l56_irq_request (&cs35l56->base, spi->irq); -if (ret <0 ) -cs35l56_remove (cs35l56); - -return ret; +return cs35l56_common_probe (cs35l56, spi->irq); }static void cs35l56_spi_remove (struct spi_device *spi)
sound/soc/codecs/cs35l56.c +10 -2
@@ -1941,7 +1941,7 @@ static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l5return ret; } -int cs35l56_common_probe (struct cs35l56_private *cs35l56) +int cs35l56_common_probe (struct cs35l56_private *cs35l56,int irq) {int ret;
@@ -2018,16 +2018,24 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) goto err_remove_wm_adsp; } + ret =cs35l56_irq_request (&cs35l56->base, irq); +if (ret) + goto err_remove_wm_adsp; + ret =snd_soc_register_component (cs35l56->base.dev, &soc_component_dev_cs35l56, cs35l56_dai,ARRAY_SIZE (cs35l56_dai));if (ret <0 ) {dev_err_probe (cs35l56->base.dev, ret,"Register codec failed\n" ); - goto err_remove_wm_adsp; + goto err_free_irq; }return 0 ; +err_free_irq: +if (cs35l56->base.irq) +devm_free_irq (cs35l56->base.dev, cs35l56->base.irq, &cs35l56->base); + err_remove_wm_adsp:wm_adsp2_remove (&cs35l56->dsp);
sound/soc/codecs/cs35l56.h +1 -1
@@ -78,7 +78,7 @@ int cs35l56_system_resume_early(struct device *dev);int cs35l56_system_resume (struct device *dev);irqreturn_t cs35l56_irq (int irq,void *data);int cs35l56_irq_request (struct cs35l56_base *cs35l56_base,int irq); -int cs35l56_common_probe (struct cs35l56_private *cs35l56); +int cs35l56_common_probe (struct cs35l56_private *cs35l56,int irq);int cs35l56_init (struct cs35l56_private *cs35l56);void cs35l56_remove (struct cs35l56_private *cs35l56);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 160/733 ASoC: cs35l56: Fix probe deadlock waiting for SoundWire enumeration
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Richard Fitzgerald <[email protected]>
[ Upstream commit 1d80a4792f1de236c157bcee2e5400fad4c66c65 ]
On SoundWire, don't call snd_soc_register_component() from driver probe(). Instead, queue a work item after first SoundWire attach to call snd_soc_register_component(). This prevents a deadlock if snd_soc_register_component() directly calls cs35l56_component_probe().
On SoundWire, the registers are not accessible during driver probe(). Drivers must return from their probe() and wait for the SoundWire core to call their update_status() callback to report an ATTACHED status. The cs35l56 driver handled this by calling snd_soc_register_component() from driver probe() as usual, and cs35l56_component_probe() waited for init_completion to be signalled. A SoundWire attach calls cs35l56_init() which then signals init_completion.
This created a deadlock if this was the last component needed to complete a card. In that case, snd_soc_register_component() directly called cs35l56_component_probe() which led to this:
driver probe()
calls snd_soc_register_component()
calls cs35l56_component_probe()
waits for init_completion
In this case the driver probe() has not returned, so the SoundWire core would not call update_status() and init_completion would not be signalled.
Fortunately, snd_soc_register_component() never returns -EPROBE_DEFER, so it doesn't need to be called from a driver probe(). It can be deferred to a work item. This work is queued after the first completed pass through cs35l56_init(), so there is no need for it to wait for init_completion. snd_soc_register_component() isn't called directly from cs35l56_init() because cs35l56_init() runs in the SoundWire bus driver thread, and there would be a risk of nested locking or lock inversion.
The work item is queued on a freezable workqueue to prevent a race between the work item and system_suspend of another instance. If the workqueue were not frozen it would be possible for the work item of one driver instance to call snd_soc_register_component() which then calls cs35l56_component_probe() of another instance while that instance is already executing its system suspend functions.
The non-SoundWire case still calls snd_soc_register_component() from cs35l56_common_probe() so that it is the last initialization action. There's no need defer the call for I2S/SPI buses so we can also leave it able to return errors during probe.
--- sound/soc/codecs/cs35l56.c | 71 +++++++++++++++++++++++++++++++------- sound/soc/codecs/cs35l56.h | 2 ++ 2 files changed, 60 insertions(+), 13 deletions(-)
sound/soc/codecs/cs35l56.c +58 -13
@@ -1368,12 +1368,6 @@ static int _cs35l56_component_probe(struct snd_soc_component *component)BUILD_BUG_ON (ARRAY_SIZE (cs35l56_tx_input_texts) !=ARRAY_SIZE (cs35l56_tx_input_values)); -if (!wait_for_completion_timeout (&cs35l56->init_completion, -msecs_to_jiffies (5000 ))) { -dev_err (cs35l56->base.dev,"%s: init_completion timed out\n" , __func__); -return -ENODEV ; - } - cs35l56->dsp.part =kasprintf (GFP_KERNEL ,"cs35l%02x" , cs35l56->base.type);if (!cs35l56->dsp.part)return -ENOMEM ;
@@ -1941,6 +1935,40 @@ static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l5return ret; } +static int cs35l56_component_register (struct cs35l56_private *cs35l56) +{ +int ret; + + ret =snd_soc_register_component (cs35l56->base.dev, + &soc_component_dev_cs35l56, + cs35l56_dai,ARRAY_SIZE (cs35l56_dai)); +if (ret <0 ) { +dev_err (cs35l56->base.dev,"Register codec failed: %d\n" , ret); +return ret; + } + + cs35l56->component_registered = true; + +return 0 ; +} + +static void cs35l56_component_register_work (struct work_struct *work) +{ +struct cs35l56_private *cs35l56 =container_of (work, + struct cs35l56_private, + component_register_work); +int ret; + +PM_RUNTIME_ACQUIRE_AUTOSUSPEND (cs35l56->base.dev, pm_err); + ret =PM_RUNTIME_ACQUIRE_ERR (&pm_err); +if (ret) { +dev_err (cs35l56->base.dev,"register_work failed to get pm_runtime: %d\n" , ret); +return ; + } + +cs35l56_component_register (cs35l56); +} +int cs35l56_common_probe (struct cs35l56_private *cs35l56,int irq) {int ret;
@@ -1949,6 +1977,7 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56, int irq)mutex_init (&cs35l56->base.irq_lock); cs35l56->base.cal_index =-1 ; cs35l56->speaker_id = -ENOENT ; +INIT_WORK (&cs35l56->component_register_work, cs35l56_component_register_work);dev_set_drvdata (cs35l56->base.dev, cs35l56);
@@ -2022,12 +2051,17 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56, int irq)if (ret) goto err_remove_wm_adsp; - ret =snd_soc_register_component (cs35l56->base.dev, - &soc_component_dev_cs35l56, - cs35l56_dai,ARRAY_SIZE (cs35l56_dai)); -if (ret <0 ) { -dev_err_probe (cs35l56->base.dev, ret,"Register codec failed\n" ); - goto err_free_irq; +/* +* Defer calling snd_soc_register_component() on SoundWire to prevent +* a deadlock where it calls our component_probe(), which requires the +* SoundWire enumeration to complete, but because we are still in probe() +* the SoundWire core will not call the update_status() callback. At time +* of writing snd_soc_register_component() never returns EPROBE_DEFER. +*/ +if (!cs35l56->sdw_peripheral) { + ret =cs35l56_component_register (cs35l56); +if (ret <0 ) + goto err_free_irq; }return 0 ;
@@ -2057,6 +2091,7 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_common_probe, "SND_SOC_CS35L56_CORE");int cs35l56_init (struct cs35l56_private *cs35l56) { +bool first_time_init = !cs35l56->base.init_done;int ret;/*
@@ -2133,13 +2168,23 @@ int cs35l56_init(struct cs35l56_private *cs35l56) cs35l56->base.init_done = true;complete_all (&cs35l56->init_completion); +if (cs35l56->sdw_peripheral && first_time_init) { +/* +* Hardware now accessible, queue work to call +* snd_soc_register_component(). +*/ +queue_work (system_freezable_wq, &cs35l56->component_register_work); + } +return 0 ; }EXPORT_SYMBOL_NS_GPL (cs35l56_init,"SND_SOC_CS35L56_CORE" );void cs35l56_remove (struct cs35l56_private *cs35l56) { -snd_soc_unregister_component (cs35l56->base.dev); +cancel_work_sync (&cs35l56->component_register_work); +if (cs35l56->component_registered) +snd_soc_unregister_component (cs35l56->base.dev); cs35l56->base.init_done = false;
sound/soc/codecs/cs35l56.h +2 -0
@@ -32,6 +32,7 @@ struct sdw_slave;struct cs35l56_private {struct wm_adsp dsp;/* must be first member */ struct cs35l56_base base; +struct work_struct component_register_work;struct work_struct dsp_work;struct workqueue_struct *dsp_wq;struct snd_soc_component *component;
@@ -43,6 +44,7 @@ struct cs35l56_private {bool sdw_irq_no_unmask;bool soft_resetting;bool sdw_attached; +bool component_registered;struct completion init_completion;int speaker_id;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 161/733 drm/xe/oa: Remove sysfs entry on idr_alloc failure in xe_oa_add_config_ioctl()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lu Yao <[email protected]>
[ Upstream commit 3663c8d1f31e65771bd73ee3259f35fd397f9933 ]
If idr_alloc() fails after create_dynamic_oa_sysfs_entry() has succeeded, the error path frees the OA config without removing the metrics sysfs group.
Remove the sysfs group before releasing the config, and fix up the misleading error message copied from the sysfs creation failure path.
--- drivers/gpu/drm/xe/xe_oa.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/gpu/drm/xe/xe_oa.c +4 -2
@@ -2431,9 +2431,9 @@ int xe_oa_add_config_ioctl(struct drm_device *dev, u64 data, struct drm_file *fi oa_config->id =idr_alloc (&oa->metrics_idr, oa_config,1 ,0 ,GFP_KERNEL );if (oa_config->id <0 ) { -drm_dbg (&oa->xe->drm,"Failed to create sysfs entry for OA config\n" ); +drm_dbg (&oa->xe->drm,"Failed to allocate id for OA config\n" ); err = oa_config->id; - goto sysfs_err; + goto id_alloc_err; } id = oa_config->id;
@@ -2444,6 +2444,8 @@ int xe_oa_add_config_ioctl(struct drm_device *dev, u64 data, struct drm_file *fireturn id; +id_alloc_err: +sysfs_remove_group (oa->metrics_kobj, &oa_config->sysfs_metric); sysfs_err:mutex_unlock (&oa->metrics_lock); reg_err:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 162/733 ALSA: dummy: Report a change when one capture switch channel moves
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
[ Upstream commit 83162eeaf78c71ff6f6fa31dc95e3b6e90ee593f ]
The snd_dummy_capsrc_put() builds its change flag with &&, so it reports a change only when both channels move at once. Writing a single channel stores the new value and returns 0, the control core then sends no SNDRV_CTL_EVENT_MASK_VALUE, and a second reader keeps showing the old setting until it polls again.
The volume put a few lines above compares the same pair of channels with ||.
The mixer selftest already reports this. With snd-dummy loaded it fails event_missing on all five capture switches:
# CD Capture Switch.1 orig 0 read 1, is_volatile 0 not ok 13 event_missing.Dummy.9
--- sound/drivers/dummy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
sound/drivers/dummy.c +1 -1
@@ -782,7 +782,7 @@ static int snd_dummy_capsrc_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el left = ucontrol->value.integer.value[0 ] &1 ; right = ucontrol->value.integer.value[1 ] &1 ;guard (spinlock_irq)(&dummy->mixer_lock); - change = dummy->capture_source[addr][0 ] != left && + change = dummy->capture_source[addr][0 ] != left || dummy->capture_source[addr][1 ] != right; dummy->capture_source[addr][0 ] = left; dummy->capture_source[addr][1 ] = right;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 163/733 accel/amdxdna: refuse to flush an imported BO
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Taimuraz Kaitmazov <[email protected]>
[ Upstream commit 0ba8e0f90039da68342febf613019f4a68d86620 ]
SYNC_BO clflushes an imported BO's scatterlist. An importer may not do
that: the memory belongs to the exporter, and dma-buf gives the importer no interface to ask for maintenance on it. Refuse the request instead.
is_import_bo() is (obj)->attach, which covers more than foreign buffers. A userptr BO arrives through a ubuf, and on a carveout device every share BO and the device heap arrive through a cbuf, so SYNC_BO answers -EOPNOTSUPP for those too, including the AMDXDNA_BO_DEV path that flushes through its heap.
Only the ubuf case gives up maintenance it was getting: on a 64 MiB userptr BO a 4 KiB sync and a full sync both cost 659 us, this arm having ignored the range. amdxdna_cbuf_map() fills in only the DMA address and length, so drm_clflush_sg() already walks zero pages on carveout memory. Userspace maintains these through the mapping it already holds, as XRT's buffer::sync() does unless it is told to sync through the driver.
--- drivers/accel/amdxdna/amdxdna_gem.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
drivers/accel/amdxdna/amdxdna_gem.c +4 -3
@@ -1240,6 +1240,9 @@ static int amdxdna_flush_bo(struct amdxdna_gem_obj *abo, u64 offset, u64 size) {u64 end; +if (is_import_bo (abo)) +return -EOPNOTSUPP ; +if (offset >= abo->mem.size)return -EINVAL ;
@@ -1250,9 +1253,7 @@ static int amdxdna_flush_bo(struct amdxdna_gem_obj *abo, u64 offset, u64 size)if (!size)return 0 ; -if (is_import_bo (abo)) -drm_clflush_sg (abo->base.sgt); -else if (amdxdna_gem_vmap (abo)) +if (amdxdna_gem_vmap (abo))drm_clflush_virt_range (amdxdna_gem_vmap (abo) + offset, size);else if (abo->base.pages)drm_clflush_pages (abo->base.pages, abo->mem.size >>PAGE_SHIFT );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 164/733 btrfs: detach failed sprout device from transaction update list
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guanghui Yang <[email protected]>
[ Upstream commit c93b3c43df561cd9f592cee20ae058b563f9e5b6 ]
When creating the first metadata chunk for a sprout filesystem, create_chunk() adds the new device to the transaction dev_update_list through device->post_commit_list.
If the subsequent system chunk creation fails, btrfs_init_new_device() aborts the transaction and releases the device while post_commit_list is still linked. This triggers a warning in btrfs_free_device() and leaves the transaction list referencing freed memory.
Detach the device while holding chunk_mutex before releasing it.
--- fs/btrfs/volumes.c | 2 ++ 1 file changed, 2 insertions(+)
fs/btrfs/volumes.c +2 -0
@@ -3071,6 +3071,8 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_pathbtrfs_sysfs_remove_device (device);mutex_lock (&fs_info->fs_devices->device_list_mutex);mutex_lock (&fs_info->chunk_mutex); +if (!list_empty (&device->post_commit_list)) +list_del_init (&device->post_commit_list);list_del_rcu (&device->dev_list);list_del (&device->dev_alloc_list); fs_info->fs_devices->num_devices--;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 165/733 btrfs: restore active device pointers after failed sprout
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guanghui Yang <[email protected]>
[ Upstream commit e0b54613aabeb8e9da597f23b90c6a03d0981986 ]
btrfs_init_new_device() switches latest_dev and possibly s_bdev from the seed device to the new sprout device before creating the first writable chunks.
If chunk creation or the subsequent sprout setup fails, the error path releases the new device without switching those pointers back. btrfs_show_devname() can then dereference the freed latest_dev and crash.
Restore the active device pointers to the latest seed device before removing and releasing the failed sprout device.
--- fs/btrfs/volumes.c | 2 ++ 1 file changed, 2 insertions(+)
fs/btrfs/volumes.c +2 -0
@@ -3070,6 +3070,8 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path error_sysfs:btrfs_sysfs_remove_device (device);mutex_lock (&fs_info->fs_devices->device_list_mutex); +if (seeding_dev) +btrfs_assign_next_active_device (device, seed_devices->latest_dev);mutex_lock (&fs_info->chunk_mutex);if (!list_empty (&device->post_commit_list))list_del_init (&device->post_commit_list);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 166/733 bonding: alb: fix uninitialized transport header access in alb_determine_nd()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 70f3995830d3f1e79faa14eb0605914f778feca9 ]
alb_determine_nd() uses icmp6_hdr(skb) to inspect ICMPv6 headers. However, in xmit paths (e.g. packets sent via AF_PACKET / raw sockets or forwarded packets), skb->transport_header is not guaranteed to be initialized. While pskb_network_may_pull() ensures the packet data is linear starting from the network header, it does not set or adjust the transport header offset.
Dereferencing icmp6_hdr(skb) can therefore access out-of-bounds memory.
Fetch the icmp6hdr directly after ipv6hdr following pskb_network_may_pull(), and reload ipv6hdr in case pskb_may_pull() reallocated skb->head. Also remove the unused bond argument from alb_determine_nd().
--- drivers/net/bonding/bond_alb.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
drivers/net/bonding/bond_alb.c +7 -6
@@ -1281,10 +1281,10 @@ static int alb_set_mac_address(struct bonding *bond, void *addr) }/* determine if the packet is NA or NS */ -static bool alb_determine_nd (struct sk_buff *skb,struct bonding *bond) +static bool alb_determine_nd (struct sk_buff *skb) { -struct ipv6hdr *ip6hdr; -struct icmp6hdr *hdr; +const struct ipv6hdr *ip6hdr; +const struct icmp6hdr *hdr;if (!pskb_network_may_pull (skb,sizeof (*ip6hdr)))return true;
@@ -1296,7 +1296,8 @@ static bool alb_determine_nd(struct sk_buff *skb, struct bonding *bond)if (!pskb_network_may_pull (skb,sizeof (*ip6hdr) +sizeof (*hdr)))return true; - hdr =icmp6_hdr (skb); + ip6hdr =ipv6_hdr (skb); + hdr = (const struct icmp6hdr *)(ip6hdr +1 );return hdr->icmp6_type ==NDISC_NEIGHBOUR_ADVERTISEMENT || hdr->icmp6_type ==NDISC_NEIGHBOUR_SOLICITATION ; }
@@ -1381,7 +1382,7 @@ struct slave *bond_xmit_tlb_slave_get(struct bonding *bond,if (!is_multicast_ether_addr (eth_data->h_dest)) {switch (skb->protocol) {case htons (ETH_P_IPV6 ): -if (alb_determine_nd (skb, bond)) +if (alb_determine_nd (skb))break ; fallthrough;case htons (ETH_P_IP ):
@@ -1467,7 +1468,7 @@ struct slave *bond_xmit_alb_slave_get(struct bonding *bond,break ; } -if (alb_determine_nd (skb, bond)) { +if (alb_determine_nd (skb)) { do_tx_balance = false;break ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 167/733 perf: RISC-V: check cpu_hw_evt before dereference in overflow IRQ
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xixin Liu <[email protected]>
[ Upstream commit f643f520c4c6998fa27dce90dd3b3ff6414e0bae ]
The overflow IRQ handler dereferences cpu_hw_evt before the null check. Move the check first. Defensive only; the cookie is valid on the normal path today.
--- drivers/perf/riscv_pmu_sbi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/perf/riscv_pmu_sbi.c +3 -1
@@ -1050,11 +1050,13 @@ static irqreturn_t pmu_sbi_ovf_handler(int irq, void *dev)u64 overflowed_ctrs =0 ;struct cpu_hw_events *cpu_hw_evt = dev;u64 start_clock =sched_clock (); -struct riscv_pmu_snapshot_data *sdata = cpu_hw_evt->snapshot_addr; +struct riscv_pmu_snapshot_data *sdata;if (WARN_ON_ONCE (!cpu_hw_evt))return IRQ_NONE ; + sdata = cpu_hw_evt->snapshot_addr; +/* Firmware counter don't support overflow yet */ fidx =find_first_bit (cpu_hw_evt->used_hw_ctrs,RISCV_MAX_COUNTERS );if (fidx ==RISCV_MAX_COUNTERS ) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 168/733 scsi: mpt3sas: Avoid out-of-bounds cpumask_of_node() call in _base_assign_reply_queues()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ivy Lopez <[email protected]>
[ Upstream commit e0d26fe176a8db6ccad4ab38c5bab29391c1946b ]
dev_to_node() can return NUMA_NO_NODE (-1) on systems without NUMA topology information for the PCI device, such as single-socket boards that don't expose device-to-node affinity. Passing -1 directly into cpumask_of_node() indexes node_to_cpumask_map[-1], an out-of-bounds array read caught by UBSAN:
UBSAN: array-index-out-of-bounds in arch/x86/include/asm/topology.h:72:28 index -1 is out of range for type 'cpumask *[1024]'
Fall back to cpu_online_mask when no NUMA node is available, rather than assuming dev_to_node() always returns a valid node index.
--- drivers/scsi/mpt3sas/mpt3sas_base.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/scsi/mpt3sas/mpt3sas_base.c +4 -1
@@ -3238,7 +3238,10 @@ _base_assign_reply_queues(struct MPT3SAS_ADAPTER *ioc)* corresponding to high iops queues. */ if (ioc->high_iops_queues) { - mask =cpumask_of_node (dev_to_node (&ioc->pdev->dev)); +int node =dev_to_node (&ioc->pdev->dev); + + mask = (node ==NUMA_NO_NODE ) ? + cpu_online_mask :cpumask_of_node (node);for (index =0 ; index < ioc->high_iops_queues; index++) { irq =pci_irq_vector (ioc->pdev, index);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 169/733 sched/rt,dl: Skip migrate-disabled tasks when picking a push candidate
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Seiji Nishikawa <[email protected]>
[ Upstream commit dae5c0292080dd7b9c7d784268dcf443f1f3d15e ]
A migrate_disable()'d RT task cannot be moved to another CPU, but the scheduler still keeps such a task on that CPU's pushable list (rq->rt.pushable_tasks) and still marks the runqueue RT-overloaded (rq->rt.overloaded = 1). So the RT balancer keeps treating this CPU as having a task to move away, and keeps trying to move the task, but the push can never succeed. When the head is pinned, push_rt_task() does not give up either. It falls back to pushing rq->curr instead, using the per-CPU stopper, as added by commit a7c81556ec4d ("sched: Fix migrate_disable() vs rt/dl balancing").
The CPU spends tens of milliseconds in this retry loop. The core is isolated for real-time work, but during the loop nearly half of its time is consumed by pushes that cannot succeed.
An ftrace capture of the affected CPU, with sched_switch enabled and commit 94894c9c477e ("sched/rt: Skip currently executing CPU in rto_next_cpu()") applied, shows where the CPU time went. Two SCHED_FIFO tasks at equal priority shared the CPU, taskA migrate_disable()'d and queued, taskB as rq->curr. In one 89 ms window, taskB got only 52 ms of CPU. The other 37 ms went to the stopper thread.
The scheduler kept trying to push taskA, the pinned head of the pushable list, fell back to pushing taskB instead, and woke the stopper 5204 times. Every one of those pushes failed and no task was moved. taskA stayed runnable and queued the whole time, and never ran.
Pushing taskB fails on a re-check. find_lock_lowest_rq() drops the rq lock to take the target rq lock, then checks again with "task != pick_next_pushable_task(rq)".
The task being pushed is taskB, but the pick returns taskA, the head of the pushable list. taskB is rq->curr, and set_next_task_rt() removes the running task from that list, so taskB can never be the head. The check expects a candidate taken from the pushable list, but the fallback pushes rq->curr, which is never on that list. So the check fails every time.
.--> push-IPI arrives
| |
| v
| pushable head = taskA -> pinned, cannot be pushed
| |
| v
| so push taskB instead -> wake migration/N, a stop-class
| | thread, so it preempts taskB
| v
| re-check compares taskB against the pushable head,
| which is still taskA -> give up
| |
| v
| nothing moved, taskA still queued, rq still overloaded
| |
'----------'
repeats every ~17 us, 5204 times, for 89 msThe loop cannot stop itself. Every round leaves the runqueue exactly as it was, so the next push-IPI does the same thing. In the capture it ended only when taskB went to sleep on its own. taskA was then picked locally and left the pushable list.
CPU time per task in the window, from sched_switch:
taskB 51.95 ms real work
migration/N 37.18 ms nothing moved
taskA 0.00 ms queued the whole time, never pickedidle 0.01 ms
Counts over the same window:
7667 push-IPIs handled on this CPU
17481 pick_next_pushable_task() returned taskA, still pinned
5204 find_lock_lowest_rq() gave up on the re-check
1 push that actually completed
0 migrations of taskA The CPU times and the window length come from the standard sched_switch tracepoint. The counts needed tracepoints added inside the RT balancer for this investigation.
The self-IPI path is closed by the rto_next_cpu() fix above, and that part works. But the runqueue is still marked overloaded, because the pinned task is still advertised as pushable. Other CPUs now send the push-IPIs during their own RT balancing, and the same loop runs again. Closing the self-IPI path did not stop a pinned task from triggering push balancing.
A pinned task should never have been returned as a push candidate in the first place. A migrate_disable()'d task cannot be migrated, so it belongs in the same skip that was added for on_cpu tasks by commit e0ca8991b2de ("sched: Make class_schedulers avoid pushing current, and get rid of proxy_tag_curr()"). Add is_migration_disabled() to the skip condition in pick_next_pushable_task() and pick_next_pushable_dl_task().
With the skip in place, if the pinned task is the only extra runnable task the helpers return NULL, push_rt_task() and push_dl_task() give up early, and no stopper is woken. The pinned task then runs locally once curr yields. If a task that really can be migrated is queued behind the pinned head, it is now picked and pushed for real.
This makes the fallback that pushes rq->curr unreachable when the pushable head is migrate-disabled. Nothing is lost, because that path was always stopped by the re-check described above. In the capture it ran 5204 times and moved nothing.
--- kernel/sched/deadline.c | 4 ++-- kernel/sched/rt.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-)
kernel/sched/deadline.c +2 -2
@@ -3028,8 +3028,8 @@ static struct task_struct *pick_next_pushable_dl_task(struct rq *rq) next_node =rb_first_cached (&rq->dl.pushable_dl_tasks_root);while (next_node) { i =__node_2_pdl (next_node); -/* make sure task isn't on_cpu (possible with proxy-exec) */ -if (!task_on_cpu (rq, i)) { +/* skip tasks that cannot be migrated */ +if (!task_on_cpu (rq, i) && !is_migration_disabled (i)) { p = i;break ; }
kernel/sched/rt.c +2 -2
@@ -1872,8 +1872,8 @@ static struct task_struct *pick_next_pushable_task(struct rq *rq)return NULL ;plist_for_each_entry (i, head, pushable_tasks) { -/* make sure task isn't on_cpu (possible with proxy-exec) */ -if (!task_on_cpu (rq, i)) { +/* skip tasks that cannot be migrated */ +if (!task_on_cpu (rq, i) && !is_migration_disabled (i)) { p = i;break ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 170/733 sched/core: Skip rq->avg_idle update without a valid idle_stamp
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shubhang Kaushik (Ampere) <[email protected]>
[ Upstream commit c6dcd97c8be75f052a1ca52cf79b03e7292962f1 ]
Commit 4b603f1551a73 ("sched: Update rq->avg_idle when a task is moved to an idle CPU") moved rq->avg_idle accounting out of the wakeup path and into put_prev_task_idle(), so that the idle interval is consumed whenever the idle task is switched out.
The wakeup-side accounting that it replaced only updated rq->avg_idle when rq->idle_stamp was non-zero. The new helper lost that validity check and unconditionally computes:
rq_clock(rq) - rq->idle_stamp
If rq->idle_stamp is zero, this uses rq_clock(rq) as the sample. That is not a valid idle duration and can immediately drive rq->avg_idle to its clamp.
This can happen when sched_balance_newidle() returns before setting rq->idle_stamp, for example when this_rq->ttwu_pending is set. In that case the rq can switch to the idle task with idle_stamp still zero and leave idle again when the pending wakeup is processed.
Other paths can also switch to the idle task without setting rq->idle_stamp via newidle_balance(), for example find_proxy_task() or force-idling.
Restore the idle_stamp validity check in update_rq_avg_idle() and skip the rq->avg_idle update when there is no measured idle interval.
--- kernel/sched/core.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
kernel/sched/core.c +8 -2
@@ -3743,11 +3743,17 @@ static inline void ttwu_do_wakeup(struct task_struct *p)void update_rq_avg_idle (struct rq *rq) { -u64 delta =rq_clock (rq) - rq->idle_stamp; -u64 max =2 *rq->max_idle_balance_cost; +u64 idle_stamp = rq->idle_stamp; +u64 delta, max; + +if (!idle_stamp) +return ; + + delta =rq_clock (rq) - idle_stamp;update_avg (&rq->avg_idle, delta); + max =2 * rq->max_idle_balance_cost;if (rq->avg_idle > max) rq->avg_idle = max; rq->idle_stamp =0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 171/733 x86/itmt: Dont make ITMT enablement depend on debugfs
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mario Limonciello <[email protected]>
[ Upstream commit eaece4849991d62fcd6f46637c55dcce00e25d70 ]
sched_set_itmt_support() treats debugfs file creation failures as fatal. When CONFIG_DEBUG_FS is disabled, debugfs stubs return ERR_PTR(-ENODEV), causing ITMT to be silently disabled.
debugfs is a debug-only facility; its return values should be ignored. Drop the fatal error handling and enable ITMT unconditionally.
--- arch/x86/kernel/itmt.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-)
arch/x86/kernel/itmt.c +2 -6
@@ -110,18 +110,14 @@ int sched_set_itmt_support(void) arch_debugfs_dir, &sysctl_sched_itmt_enabled, &dfs_sched_itmt_fops); -if (IS_ERR_OR_NULL (dfs_sched_itmt)) { +if (IS_ERR (dfs_sched_itmt)) dfs_sched_itmt =NULL ; -return -ENOMEM ; - } dfs_sched_core_prio =debugfs_create_file ("sched_core_priority" ,0644 , arch_debugfs_dir,NULL , &sched_core_priority_fops); -if (IS_ERR_OR_NULL (dfs_sched_core_prio)) { +if (IS_ERR (dfs_sched_core_prio)) dfs_sched_core_prio =NULL ; -return -ENOMEM ; - } sched_itmt_capable = true;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 172/733 perf/core: Skip empty AUX records with only format flags
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Leo Yan <[email protected]>
[ Upstream commit 8a7f5b5e860b5c113ca99acd5b1e9074f5c5af3c ]
perf_aux_output_end() emits a PERF_RECORD_AUX when the recorded size is nonzero or when any flag other than PERF_AUX_FLAG_OVERWRITE is set.
PMU format flags describe how an AUX payload is encoded. TRBE driver sets PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW for raw trace buffers, causing an AUX record to be emitted even when no trace data.
This is noticeable when tracing a task with strace. Ptrace stops repeatedly end empty AUX transactions, producing many zero-sized PERF_RECORD_AUX records. For example:
perf record -e cs_etm//u -m,128M -- strace ls
perf script -D 2>&1 |
awk '/PERF_RECORD_AUX offset/ {
for (i = 1; i <= NF; i++)
if ($i == "size:" && $(i + 1) == "0")
count++
}
END { print count }'
165This recording contains 165 zero-sized AUX records which provide no useful information to userspace.
Ignore PERF_AUX_FLAG_PMU_FORMAT_TYPE_MASK, together with PERF_AUX_FLAG_OVERWRITE, when deciding whether an empty AUX record is useful. Zero-sized records carrying TRUNCATED, PARTIAL or COLLISION are still emitted.
--- kernel/events/ring_buffer.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
kernel/events/ring_buffer.c +7 -2
@@ -509,7 +509,10 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size)/* * Only send RECORD_AUX if we have something useful to communicate * -* Note: the OVERWRITE records by themselves are not considered +* PMU_FORMAT bits identify the PMU type rather than an AUX event +* has occurred, so ignore them for zero-sized records. +* +* The OVERWRITE records by themselves are not considered * useful, as they don't communicate any *new* information, * aside from the short-lived offset, that becomes history at * the next event sched-in and therefore isn't useful.
@@ -518,7 +521,9 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size)* offset. So, from now on we don't output AUX records that * have *only* OVERWRITE flag set. */ -if (size || (handle->aux_flags & ~(u64 )PERF_AUX_FLAG_OVERWRITE )) +if (size || + (handle->aux_flags & ~(u64)(PERF_AUX_FLAG_PMU_FORMAT_TYPE_MASK | +PERF_AUX_FLAG_OVERWRITE )))perf_event_aux_event (handle->event, aux_head, size, handle->aux_flags);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 173/733 locking/lockdep: Invalidate stale class_cache entries for zapped classes
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 02c6be7d675b21d81f0ba3a524346850a8c0e3bf ]
syzbot reported a lockdep splat hitting DEBUG_LOCKS_WARN_ON(1) in hlock_class() due to an invalid class_idx:
WARNING: kernel/locking/lockdep.c:238 at __lock_acquire+0x382/0x2cf0 kernel/locking/lockdep.c:5203 Workqueue: wg-crypt-wg0 wg_packet_tx_worker RIP: 0010:hlock_class kernel/locking/lockdep.c:238 [inline] RIP: 0010:check_wait_context kernel/locking/lockdep.c:4870 [inline] RIP: 0010:__lock_acquire+0x389/0x2cf0 kernel/locking/lockdep.c:5203 Call Trace: <IRQ> lock_acquire+0x106/0x350 kernel/locking/lockdep.c:5886 _raw_spin_lock+0x2e/0x40 kernel/locking/spinlock.c:173 tcp_tsq_handler+0x29/0x200 net/ipv4/tcp_output.c:1291 tcp_tsq_workfn+0x384/0x410 net/ipv4/tcp_output.c:1325 ...
When a lock class is zapped (e.g. during module unload or key unregistration), zap_class() clears the class's bit in lock_classes_in_use and removes it from the class hash table. However, existing lockdep_map instances embedded in data structures may still retain a pointer to the zapped class in their class_cache[] array.
When __lock_acquire() subsequently runs on such a lock, it finds lock->class_cache[subclass] != NULL, skipping register_lock_class() and assigning hlock->class_idx to the index of the zapped class. When check_wait_context() or hlock_class() inspects the held_lock, it finds !test_bit(class_idx, lock_classes_in_use) and warns. Furthermore, if the zapped slot is subsequently re-allocated to an unrelated lock key, the stale class_cache entry would erroneously match the unrelated class (ABA issue).
Add lock_class_cache_is_valid() to validate that the cached class is within lock_classes bounds, still allocated in lock_classes_in_use (using uninstrumented arch_test_bit() in __always_inline context so it is safe in noinstr contexts like match_held_lock()), and that class->key matches the expected subkey (taking lockdep_set_subclass() overrides into account). Also use READ_ONCE()/WRITE_ONCE() when accessing class_cache[]. If the entry is invalid or stale, fall back to register_lock_class() / look_up_lock_class().
--- kernel/locking/lockdep.c | 50 +++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 8 deletions(-)
kernel/locking/lockdep.c +42 -8
@@ -947,6 +947,34 @@ look_up_lock_class(const struct lockdep_map *lock, unsigned int subclass)return NULL ; } +static __always_inline bool lock_class_cache_is_valid(const struct lockdep_map *lock, +const struct lock_class *class, +unsigned int subclass) +{ +unsigned int class_subclass; + +if (!class) +return false; + +if (unlikely (class < lock_classes || class >= lock_classes +MAX_LOCKDEP_KEYS )) +return false; + +if (unlikely (!arch_test_bit (class - lock_classes, lock_classes_in_use))) +return false; + +if (unlikely (!lock->key)) +return false; + + class_subclass = subclass ? subclass : class->subclass; +if (unlikely (class_subclass >=MAX_LOCKDEP_SUBCLASSES )) +return false; + +if (unlikely (READ_ONCE (class->key) != lock->key->subkeys + class_subclass)) +return false; + +return true; +} +/* * Static locks do not have their class-keys yet - for them the key is * the lock object itself. If the lock is in the per cpu area, the
@@ -1379,9 +1407,9 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force) out_set_class_cache:if (!subclass || force) - lock->class_cache[0 ] = class; +WRITE_ONCE (lock->class_cache[0 ], class);else if (subclass <NR_LOCKDEP_CACHING_CLASSES ) - lock->class_cache[subclass] = class; +WRITE_ONCE (lock->class_cache[subclass], class);/* * Hash collision, did we smoke some? We found a class with a matching
@@ -4941,7 +4969,7 @@ void lockdep_init_map_type(struct lockdep_map *lock, const char *name,int i;for (i =0 ; i <NR_LOCKDEP_CACHING_CLASSES ; i++) - lock->class_cache[i] =NULL ; +WRITE_ONCE (lock->class_cache[i],NULL );#ifdef CONFIG_LOCK_STAT lock->cpu =raw_smp_processor_id ();
@@ -5006,12 +5034,15 @@ EXPORT_SYMBOL_GPL(__lockdep_no_track__);void lockdep_set_lock_cmp_fn (struct lockdep_map *lock,lock_cmp_fn cmp_fn,lock_print_fn print_fn) { -struct lock_class *class = lock->class_cache[0 ]; +struct lock_class *class =READ_ONCE (lock->class_cache[0 ]);unsigned long flags;raw_local_irq_save (flags);lockdep_recursion_inc (); +if (!lock_class_cache_is_valid (lock, class,0 )) + class =NULL ; +if (!class) class =register_lock_class (lock,0 ,0 );
@@ -5103,8 +5134,11 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,if (DEBUG_LOCKS_WARN_ON (subclass >=MAX_LOCKDEP_SUBCLASSES ))return 0 ; -if (subclass <NR_LOCKDEP_CACHING_CLASSES ) - class = lock->class_cache[subclass]; +if (subclass <NR_LOCKDEP_CACHING_CLASSES ) { + class =READ_ONCE (lock->class_cache[subclass]); +if (!lock_class_cache_is_valid (lock, class, subclass)) + class =NULL ; + }/* * Not cached? */
@@ -5307,9 +5341,9 @@ static noinstr int match_held_lock(const struct held_lock *hlock,return 1 ;if (hlock->references) { -const struct lock_class *class = lock->class_cache[0 ]; +const struct lock_class *class =READ_ONCE (lock->class_cache[0 ]); -if (!class) +if (!lock_class_cache_is_valid (lock, class,0 )) class =look_up_lock_class (lock,0 );/*
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 174/733 octeontx2-af: Fix limiting SRIOV VF count logic
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sunil Goutham <[email protected]>
[ Upstream commit f695390ea63941a9e412bf1f3afe65ab245fc681 ]
When RVU PF0/AF's VFs are SDP instead of LBK, limiting the VF count based on the LBK channel count is incorrect.
Apply LBK channel-based VF limits only when the VF device ID matches the LBK RVU AFVF device.
--- .../net/ethernet/marvell/octeontx2/af/rvu.c | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-)
drivers/net/ethernet/marvell/octeontx2/af/rvu.c +19 -14
@@ -3468,6 +3468,8 @@ int rvu_get_num_lbk_chans(void)return ret; } +#define PCI_DEVID_OCTEONTX2_RVU_AFVF 0xA0F8 +static int rvu_enable_sriov (struct rvu *rvu) {struct pci_dev *pdev = rvu->pdev;
@@ -3486,24 +3488,27 @@ static int rvu_enable_sriov(struct rvu *rvu)return 0 ;pci_read_config_word (pdev, pos +PCI_SRIOV_VF_DID , &rvu->vf_devid); - chans =rvu_get_num_lbk_chans (); -if (chans <0 ) -return chans; - vfs =pci_sriov_get_totalvfs (pdev); - -/* Limit VFs in case we have more VFs than LBK channels available. */ -if (vfs > chans) - vfs = chans; -if (!vfs)return 0 ; -/* LBK channel number 63 is used for switching packets between -* CGX mapped VFs. Hence limit LBK pairs till 62 only. -*/ -if (vfs >62 ) - vfs =62 ; +if (rvu->vf_devid ==PCI_DEVID_OCTEONTX2_RVU_AFVF ) { + chans =rvu_get_num_lbk_chans (); +if (chans <0 ) +return chans; + +/* The last LBK channel is reserved for switching packets between +* CGX mapped VFs. Also, since LBK VFs work in pairs, limit VF +* count to available LBK channels minus 2. +*/ + vfs =min (vfs, chans -2 ); + +if (vfs <=0 ) { +dev_warn (&pdev->dev, +"Skipping SRIOV enablement, not enough LBK channels available\n" ); +return 0 ; + } + }/* Save VFs number for reference in VF interrupts handlers. * Since interrupts might start arriving during SRIOV enablement
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 175/733 ksmbd: fix sparc build with atomic work state
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
[ Upstream commit 636abbe7a66d80e179011a31754d55001cd44f63 ]
Use an unsigned int for the work state so xchg() uses a supported 4-byte operation on sparc.
--- fs/smb/server/ksmbd_work.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/smb/server/ksmbd_work.h +1 -1
@@ -82,7 +82,7 @@ struct ksmbd_work {/* Contiguous SMB2 compression transform owned by this work item. */ void *compress_buf; -unsigned char state; +unsigned int state;/* No response for cancelled request */ bool send_no_response:1;/* Request is encrypted */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 176/733 ALSA: ump: do not touch legacy_rmidi before it exists
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qingyu Zhang <[email protected]>
[ Upstream commit adeee7187694719890aaffdc14b7e89cfd736f1d ]
snd_ump_parse_endpoint() sets ump->parsed on every exit, including error, before the caller attaches the legacy rawmidi device. ump_handle_ep_name_msg() then treats parsed as "legacy_rmidi is live" and calls ump_legacy_set_rawmidi_name(), which snprintf()s into ump->legacy_rmidi->name. If a UMP packet arrives in that window (IRQ path from snd_ump_receive), legacy_rmidi is still NULL (KASAN null-ptr-deref in snprintf).
Guard the legacy helpers. parsed only means endpoint info was parsed, not that legacy_rmidi exists.
--- sound/core/ump.c | 4 ++++ 1 file changed, 4 insertions(+)
sound/core/ump.c +4 -0
@@ -1333,6 +1333,8 @@ static void update_legacy_names(struct snd_ump_endpoint *ump) {struct snd_rawmidi *rmidi = ump->legacy_rmidi; +if (!rmidi) +return ;update_legacy_substreams (ump, rmidi,SNDRV_RAWMIDI_STREAM_INPUT );update_legacy_substreams (ump, rmidi,SNDRV_RAWMIDI_STREAM_OUTPUT ); }
@@ -1341,6 +1343,8 @@ static void ump_legacy_set_rawmidi_name(struct snd_ump_endpoint *ump) {struct snd_rawmidi *rmidi = ump->legacy_rmidi; +if (!rmidi) +return ;snprintf (rmidi->name,sizeof (rmidi->name),"%.68s (MIDI 1.0)" , ump->core.name); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 177/733 printk/nbcon: Change nbcon_irq_work to IRQ_WORK_LAZY
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: John Ogness <[email protected]>
[ Upstream commit 560f4deda32785e260056200f8bb911c475c5b88 ]
Change the nbcon_irq_work to be IRQ_WORK_LAZY, thus not raising an IRQ upon irq_work queuing. The irq_work is then handled on the next kernel tick. This additional delay is acceptable because nbcon_irq_work is only responsible for non-emergency deferred printing, which is delayed anyway. This has the benefit of not needing to raise an IRQ for each printk() call.
On a side note, the Tegra20 and Tegra30 platforms can hang if an irq_work IRQ is raised while entering cpuidle states. This problem was reproducible by calling printk() while entering cpuidle. So this change also provides a workaround for these platforms (as long as they are not running tickless).
--- kernel/printk/nbcon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/printk/nbcon.c +1 -1
@@ -1782,7 +1782,7 @@ bool nbcon_alloc(struct console *con) }rcuwait_init (&con->rcuwait); -init_irq_work (&con->irq_work, nbcon_irq_work); + con->irq_work =IRQ_WORK_INIT_LAZY (nbcon_irq_work);atomic_long_set (&ACCESS_PRIVATE (con, nbcon_prev_seq),-1UL );nbcon_state_set (con, &state);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 178/733 platform/x86: x86-android-tablets: hold device reference for secondary fwnode teardown
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Torokhov <[email protected]>
[ Upstream commit 144113b0a70fa18033a747ee5db6803308f7688c ]
In gpio_secondary_fwnode_init(), acpi_bus_find_device_by_name() returns a device reference, but the local dev variable is declared with __free(put_device), dropping the reference at the end of each iteration. Meanwhile, devm_add_action_or_reset() saves the dev pointer for gpio_secondary_unset() without incrementing its reference count, which could lead to a use-after-free during driver teardown if the device is released in the interim.
Acquire an explicit device reference with get_device() when registering the devres action, and drop it with put_device() inside gpio_secondary_unset().
--- drivers/platform/x86/x86-android-tablets/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/platform/x86/x86-android-tablets/core.c +2 -1
@@ -367,6 +367,7 @@ static void gpio_secondary_unset(void *data)struct device *dev = data;set_secondary_fwnode (dev,NULL ); +put_device (dev); }static void gpio_secondary_unregister_node_group (void *data)
@@ -409,7 +410,7 @@ static int gpio_secondary_fwnode_init(struct device *parent)set_secondary_fwnode (dev, fwnode); - ret =devm_add_action_or_reset (parent, gpio_secondary_unset, dev); + ret =devm_add_action_or_reset (parent, gpio_secondary_unset,get_device (dev));if (ret)return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 179/733 ASoC: ux500: Fix MSP stream lifecycle handling
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit c37ba8fe00f264eee2fd18b0bff7c5f188136c51 ]
The trigger stop path drops the direction busy flag even though ALSA still owns the stream until shutdown. A later trigger cannot reliably restart it, shutdown may leave the block configured, and a second stream may overwrite shared duplex configuration.
Keep configured and running directions as separate state. Program shared settings only for the first direction, require a compatible configuration for the other half of a duplex stream, and enable the frame generator only while a provider stream is running. Also fix the RX-disable direction test and preserve the other direction multichannel setup.
--- sound/soc/ux500/ux500_msp_dai.c | 8 +- sound/soc/ux500/ux500_msp_i2s.c | 183 ++++++++++++++++++++++++-------- sound/soc/ux500/ux500_msp_i2s.h | 4 + 3 files changed, 149 insertions(+), 46 deletions(-)
sound/soc/ux500/ux500_msp_dai.c +6 -2
@@ -34,8 +34,10 @@ static int setup_pcm_multichan(struct snd_soc_dai *dai,if (drvdata->slots >1 ) { msp_config->multichannel_configured =1 ; - multi->tx_multichannel_enable = true; - multi->rx_multichannel_enable = true; + multi->tx_multichannel_enable = + msp_config->direction &MSP_DIR_TX ; + multi->rx_multichannel_enable = + msp_config->direction &MSP_DIR_RX ; multi->rx_comparison_enable_mode =MSP_COMPARISON_DISABLED ; multi->tx_channel_0_enable = drvdata->tx_mask;
@@ -192,6 +194,7 @@ static int setup_clocking(struct snd_soc_dai *dai,case SND_SOC_DAIFMT_BC_FC :dev_dbg (dai->dev,"%s: Codec is master.\n" , __func__); + msp_config->clock_provider = false; msp_config->iodelay =0x20 ; msp_config->rx_fsync_sel =0 ; msp_config->tx_fsync_sel =1 <<TFSSEL_SHIFT ;
@@ -204,6 +207,7 @@ static int setup_clocking(struct snd_soc_dai *dai,case SND_SOC_DAIFMT_BP_FP :dev_dbg (dai->dev,"%s: Codec is slave.\n" , __func__); + msp_config->clock_provider = true; msp_config->tx_clk_sel =TX_CLK_SEL_SRG ; msp_config->tx_fsync_sel =TX_SYNC_SRG_PROG ; msp_config->rx_clk_sel =RX_CLK_SEL_SRG ;
sound/soc/ux500/ux500_msp_i2s.c +139 -44
284 lines not on this page · raw message
sound/soc/ux500/ux500_msp_i2s.h +4 -0
@@ -460,6 +460,7 @@ struct ux500_msp_config {enum msp_data_size data_size;unsigned int def_elem_len;unsigned int iodelay; +bool clock_provider; };struct ux500_msp {
@@ -470,8 +471,11 @@ struct ux500_msp {enum msp_state msp_state;int def_elem_len;unsigned int dir_busy; +unsigned int dir_running;int loopback_enable;unsigned int f_bitclk; +bool clock_provider; +struct ux500_msp_config config; }; int ux500_msp_i2s_init_msp(struct platform_device *pdev,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 180/733 ASoC: ux500: Propagate MSP setup errors
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 3415421a2b0bc4e32bb5a9df24ed7863512d47a7 ]
The prepare callback continues with a partly initialized configuration when format setup fails. Probe likewise tests the allocated pointer instead of the return value, so an MMIO resource or mapping failure can be ignored after allocation succeeds.
Return configuration failures from prepare and test the MSP initialization result directly.
--- sound/soc/ux500/ux500_msp_dai.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
sound/soc/ux500/ux500_msp_dai.c +4 -2
@@ -468,7 +468,9 @@ static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream,dev_dbg (dai->dev,"%s: MSP %d (%s): Enter (rate = %d).\n" , __func__, dai->id,snd_pcm_stream_str (substream), runtime->rate); -setup_msp_config (substream, dai, &msp_config); + ret =setup_msp_config (substream, dai, &msp_config); +if (ret) +return ret; ret =ux500_msp_i2s_open (drvdata->msp, &msp_config);if (ret <0 ) {
@@ -764,7 +766,7 @@ static int ux500_msp_drv_probe(struct platform_device *pdev) } ret =ux500_msp_i2s_init_msp (pdev, &drvdata->msp); -if (!drvdata->msp) { +if (ret) {dev_err (&pdev->dev,"%s: ERROR: Failed to init MSP-struct (%d)!" , __func__, ret);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 181/733 ASoC: ux500: Correct MSP frame and bit clock setup
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 94c18cea657c48680e4ee20b635b6c01f3eb352e ]
FRPER plus one is the number of bit clocks in a frame. It must follow the configured slot count and width. The legacy rate-dependent constants produce malformed frames; notably, a 16-slot, 16-bit frame is programmed as 278 rather than 256 clocks.
Derive the frame period from the TDM geometry and use the real functional clock rate. Validate that the requested bit clock has an exact, representable divider, program SCKDIV as divider minus one, and report the resulting bit clock using that same divisor.
--- sound/soc/ux500/ux500_msp_dai.c | 75 +++++++-------------------------- sound/soc/ux500/ux500_msp_dai.h | 11 ----- sound/soc/ux500/ux500_msp_i2s.c | 54 ++++++++++++++---------- sound/soc/ux500/ux500_msp_i2s.h | 2 - 4 files changed, 46 insertions(+), 96 deletions(-)
sound/soc/ux500/ux500_msp_dai.c +15 -60
@@ -59,72 +59,21 @@ static int setup_pcm_multichan(struct snd_soc_dai *dai,return 0 ; } -static int setup_frameper (struct snd_soc_dai *dai,unsigned int rate, -struct msp_protdesc *prot_desc) +static void setup_frameper (struct snd_soc_dai *dai, +struct msp_protdesc *prot_desc) {struct ux500_msp_i2s_drvdata *drvdata =dev_get_drvdata (dai->dev); -switch (drvdata->slots) { -case 1 : -switch (rate) { -case 8000 : - prot_desc->frame_period = -FRAME_PER_SINGLE_SLOT_8_KHZ ; -break ; - -case 16000 : - prot_desc->frame_period = -FRAME_PER_SINGLE_SLOT_16_KHZ ; -break ; - -case 44100 : - prot_desc->frame_period = -FRAME_PER_SINGLE_SLOT_44_1_KHZ ; -break ; - -case 48000 : - prot_desc->frame_period = -FRAME_PER_SINGLE_SLOT_48_KHZ ; -break ; - -default : -dev_err (dai->dev, -"%s: Error: Unsupported sample-rate (freq = %d)!\n" , - __func__, rate); -return -EINVAL ; - } -break ; - -case 2 : - prot_desc->frame_period =FRAME_PER_2_SLOTS ; -break ; - -case 8 : - prot_desc->frame_period =FRAME_PER_8_SLOTS ; -break ; - -case 16 : - prot_desc->frame_period =FRAME_PER_16_SLOTS ; -break ; -default : -dev_err (dai->dev, -"%s: Error: Unsupported slot-count (slots = %d)!\n" , - __func__, drvdata->slots); -return -EINVAL ; - } - - prot_desc->clocks_per_frame = - prot_desc->frame_period+1 ; + prot_desc->clocks_per_frame = drvdata->slots * drvdata->slot_width; + prot_desc->frame_period = prot_desc->clocks_per_frame -1 ;dev_dbg (dai->dev,"%s: Clocks per frame: %u\n" , __func__, prot_desc->clocks_per_frame); - -return 0 ; } -static int setup_pcm_framing (struct snd_soc_dai *dai,unsigned int rate, -struct msp_protdesc *prot_desc) +static int setup_pcm_framing (struct snd_soc_dai *dai, +struct msp_protdesc *prot_desc) {struct ux500_msp_i2s_drvdata *drvdata =dev_get_drvdata (dai->dev);
@@ -165,7 +114,9 @@ static int setup_pcm_framing(struct snd_soc_dai *dai, unsigned int rate, prot_desc->tx_elem_len_2 =MSP_ELEM_LEN_16 ; prot_desc->rx_elem_len_2 =MSP_ELEM_LEN_16 ; -return setup_frameper (dai, rate, prot_desc); +setup_frameper (dai, prot_desc); + +return 0 ; } static int setup_clocking(struct snd_soc_dai *dai,
@@ -366,7 +317,7 @@ static int setup_msp_config(struct snd_pcm_substream *substream,if (ret <0 )return ret; - ret =setup_pcm_framing (dai, runtime->rate, prot_desc); + ret =setup_pcm_framing (dai, prot_desc);if (ret <0 )return ret;
@@ -735,7 +686,6 @@ static int ux500_msp_drv_probe(struct platform_device *pdev) drvdata->tx_mask =0x01 ; drvdata->rx_mask =0x01 ; drvdata->slot_width =16 ; - drvdata->master_clk =MSP_INPUT_FREQ_APB ; drvdata->reg_vape =devm_regulator_get (&pdev->dev,"v-ape" );if (IS_ERR (drvdata->reg_vape)) {
@@ -764,6 +714,11 @@ static int ux500_msp_drv_probe(struct platform_device *pdev) __func__, ret);return ret; } + drvdata->master_clk =clk_get_rate (drvdata->clk); +if (!drvdata->master_clk) { +dev_err (&pdev->dev,"MSP clock has no rate\n" ); +return -EINVAL ; + } ret =ux500_msp_i2s_init_msp (pdev, &drvdata->msp);if (ret) {
sound/soc/ux500/ux500_msp_dai.h +0 -11
@@ -22,17 +22,6 @@#define UX500_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) -#define FRAME_PER_SINGLE_SLOT_8_KHZ 31 -#define FRAME_PER_SINGLE_SLOT_16_KHZ 124 -#define FRAME_PER_SINGLE_SLOT_44_1_KHZ 63 -#define FRAME_PER_SINGLE_SLOT_48_KHZ 49 -#define FRAME_PER_2_SLOTS 31 -#define FRAME_PER_8_SLOTS 138 -#define FRAME_PER_16_SLOTS 277 - -#define UX500_MSP_INTERNAL_CLOCK_FREQ 40000000 -#define UX500_MSP1_INTERNAL_CLOCK_FREQ UX500_MSP_INTERNAL_CLOCK_FREQ -#define UX500_MSP_MIN_CHANNELS 1#define UX500_MSP_MAX_CHANNELS 8
sound/soc/ux500/ux500_msp_i2s.c +31 -23
78 lines not on this page · raw message
sound/soc/ux500/ux500_msp_i2s.h +0 -2
@@ -12,8 +12,6 @@#include <linux/platform_device.h> -#define MSP_INPUT_FREQ_APB 48000000 -/*** Stereo mode. Used for APB data accesses as 16 bits accesses (mono), * 32 bits accesses (stereo). ***/
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 182/733 ASoC: ux500: Validate MSP DAI configuration
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 9ccbacf5a0120964fc1ffacb8151e3347bee9287 ]
Installing channel constraints from hw_params is too late to affect the parameters being committed. The driver consequently accepts channel counts which disagree with the I2S or TDM setup. It also silently truncates out-of-range slot masks and accepts inverted bit clock formats which prepare then rejects.
Validate the selected channel count directly, reject invalid masks before changing cached TDM state, and implement all four standard clock and frame inversion combinations. Use the requested format in validation diagnostics.
--- sound/soc/ux500/ux500_msp_dai.c | 41 ++++++++++++++++++++++----------- sound/soc/ux500/ux500_msp_i2s.c | 7 ++++-- sound/soc/ux500/ux500_msp_i2s.h | 1 + 3 files changed, 33 insertions(+), 16 deletions(-)
sound/soc/ux500/ux500_msp_dai.c +27 -14
@@ -130,7 +130,16 @@ static int setup_clocking(struct snd_soc_dai *dai,case SND_SOC_DAIFMT_NB_IF : msp_config->tx_fsync_pol ^=1 <<TFSPOL_SHIFT ; msp_config->rx_fsync_pol ^=1 <<RFSPOL_SHIFT ; +break ; + +case SND_SOC_DAIFMT_IB_NF : + msp_config->bclk_inverted = true; +break ; +case SND_SOC_DAIFMT_IB_IF : + msp_config->bclk_inverted = true; + msp_config->tx_fsync_pol ^=1 <<TFSPOL_SHIFT ; + msp_config->rx_fsync_pol ^=1 <<RFSPOL_SHIFT ;break ;default :
@@ -453,7 +462,6 @@ static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) {unsigned int mask, slots_active; -struct snd_pcm_runtime *runtime = substream->runtime;struct ux500_msp_i2s_drvdata *drvdata =dev_get_drvdata (dai->dev); dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n",
@@ -461,9 +469,8 @@ static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream,switch (drvdata->fmt &SND_SOC_DAIFMT_FORMAT_MASK ) {case SND_SOC_DAIFMT_I2S : -snd_pcm_hw_constraint_minmax (runtime, -SNDRV_PCM_HW_PARAM_CHANNELS , -1 ,2 ); +if (params_channels (params) <1 ||params_channels (params) >2 ) +return -EINVAL ;break ;case SND_SOC_DAIFMT_DSP_B :
@@ -475,9 +482,8 @@ static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream, slots_active =hweight32 (mask);dev_dbg (dai->dev,"TDM-slots active: %d" , slots_active); -snd_pcm_hw_constraint_single (runtime, -SNDRV_PCM_HW_PARAM_CHANNELS , - slots_active); +if (!slots_active ||params_channels (params) != slots_active) +return -EINVAL ;break ;default :
@@ -510,20 +516,21 @@ static int ux500_msp_dai_set_dai_fmt(struct snd_soc_dai *dai,default :dev_err (dai->dev,"%s: Error: Unsupported protocol/master (fmt = 0x%x)!\n" , - __func__, drvdata->fmt); + __func__, fmt);return -EINVAL ; }switch (fmt &SND_SOC_DAIFMT_INV_MASK ) {case SND_SOC_DAIFMT_NB_NF :case SND_SOC_DAIFMT_NB_IF : +case SND_SOC_DAIFMT_IB_NF :case SND_SOC_DAIFMT_IB_IF :break ;default :dev_err (dai->dev,"%s: Error: Unsupported inversion (fmt = 0x%x)!\n" , - __func__, drvdata->fmt); + __func__, fmt);return -EINVAL ; }
@@ -557,17 +564,23 @@ static int ux500_msp_dai_set_tdm_slot(struct snd_soc_dai *dai, __func__, slots);return -EINVAL ; } - drvdata->slots = slots; -if (!(slot_width ==16 )) { +if (slot_width !=16 ) {dev_err (dai->dev,"%s: Error: Unsupported slot-width (%d)!\n" , __func__, slot_width);return -EINVAL ; } - drvdata->slot_width = slot_width; - drvdata->tx_mask = tx_mask & cap; - drvdata->rx_mask = rx_mask & cap; +if ((tx_mask | rx_mask) & ~cap) { +dev_err (dai->dev,"%s: Slot mask exceeds %d slots\n" , + __func__, slots); +return -EINVAL ; + } + + drvdata->slots = slots; + drvdata->slot_width = slot_width; + drvdata->tx_mask = tx_mask; + drvdata->rx_mask = rx_mask;return 0 ; }
sound/soc/ux500/ux500_msp_i2s.c +5 -2
@@ -201,10 +201,12 @@ static int configure_protocol(struct ux500_msp *msp,/* The code below should not be separated. */ temp_reg =readl (msp->registers +MSP_GCR ) & ~TX_CLK_POL_RISING ; - temp_reg |=MSP_TX_CLKPOL_BIT (~protdesc->tx_clk_pol); + temp_reg |=MSP_TX_CLKPOL_BIT (!protdesc->tx_clk_pol ^ + config->bclk_inverted);writel (temp_reg, msp->registers +MSP_GCR ); temp_reg =readl (msp->registers +MSP_GCR ) & ~RX_CLK_POL_RISING ; - temp_reg |=MSP_RX_CLKPOL_BIT (protdesc->rx_clk_pol); + temp_reg |=MSP_RX_CLKPOL_BIT (protdesc->rx_clk_pol ^ + config->bclk_inverted);writel (temp_reg, msp->registers +MSP_GCR );return 0 ;
@@ -441,6 +443,7 @@ static bool ux500_msp_config_compatible(struct ux500_msp *msp, active->data_size == config->data_size && active->def_elem_len == config->def_elem_len && active->clock_provider == config->clock_provider && + active->bclk_inverted == config->bclk_inverted && !memcmp (&active->protdesc, &config->protdesc,sizeof (active->protdesc)); }
sound/soc/ux500/ux500_msp_i2s.h +1 -0
@@ -459,6 +459,7 @@ struct ux500_msp_config {unsigned int def_elem_len;unsigned int iodelay;bool clock_provider; +bool bclk_inverted; };struct ux500_msp {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 183/733 mfd: db8500-prcmu: Fold dbx500 header into db8500
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit b8bc38bcecb77880a802d0430862b023c0aa7392 ]
Move the DBx500 PRCMU definitions into the DB8500 PRCMU header and delete the wrapper header.
Convert users of simple PRCMU wrappers to call the DB8500 helpers directly.
The dbx500-prcmu.h header was the result of an earlier attempt to abstract several DBx5x SoC PRCMU units to use the same abstract header. They are deleted from the kernel and this is not just causing maintenance burden and build errors.
The stub code is using -ENOSYS in a way checkpatch complains about so replace these with -EINVAL while we're at it.
Stable-dep-of: 66ec63e7a90b ("ASoC: ux500: Deassert the MSP reset during probe")
--- arch/arm/mach-ux500/cpu-db8500.c | 6 +- drivers/clk/ux500/clk-prcmu.c | 20 +- drivers/clk/ux500/u8500_of_clk.c | 2 +- drivers/cpuidle/cpuidle-ux500.c | 6 +- drivers/mfd/ab8500-core.c | 2 +- drivers/mfd/db8500-prcmu.c | 6 +- drivers/regulator/db8500-prcmu.c | 12 +- drivers/thermal/db8500_thermal.c | 10 +- drivers/watchdog/db8500_wdt.c | 22 +- include/linux/mfd/db8500-prcmu.h | 252 +++++++++++++- include/linux/mfd/dbx500-prcmu.h | 575 ------------------------------- sound/soc/ux500/ux500_msp_dai.c | 2 +- 12 files changed, 294 insertions(+), 621 deletions(-) delete mode 100644 include/linux/mfd/dbx500-prcmu.h
arch/arm/mach-ux500/cpu-db8500.c +3 -3
@@ -12,7 +12,7 @@#include <linux/irq.h> #include <linux/irqchip.h> #include <linux/irqchip/arm-gic.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include <linux/platform_data/arm-ux500-pm.h> #include <linux/platform_device.h> #include <linux/io.h>
@@ -81,7 +81,7 @@ static void __init ux500_init_irq(void)struct resource r;irqchip_init (); -prcmu_early_init (); +db8500_prcmu_early_init (); np =of_find_compatible_node (NULL ,NULL ,"stericsson,db8500-prcmu" );of_address_to_resource (np,0 , &r);of_node_put (np);
@@ -101,7 +101,7 @@ static void ux500_restart(enum reboot_mode mode, const char *cmd)local_irq_disable ();local_fiq_disable (); -prcmu_system_reset (0 ); +db8500_prcmu_system_reset (0 ); }static const struct of_device_id u8500_local_bus_nodes[] = {
drivers/clk/ux500/clk-prcmu.c +10 -10
@@ -7,7 +7,7 @@*/ #include <linux/clk-provider.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include <linux/slab.h> #include <linux/io.h> #include <linux/err.h>
@@ -35,13 +35,13 @@ static int clk_prcmu_prepare(struct clk_hw *hw) {struct clk_prcmu *clk =to_clk_prcmu (hw); -return prcmu_request_clock (clk->cg_sel, true); +return db8500_prcmu_request_clock (clk->cg_sel, true); }static void clk_prcmu_unprepare (struct clk_hw *hw) {struct clk_prcmu *clk =to_clk_prcmu (hw); -if (prcmu_request_clock (clk->cg_sel, false)) +if (db8500_prcmu_request_clock (clk->cg_sel, false))pr_err ("clk_prcmu: %s failed to disable %s.\n" , __func__,clk_hw_get_name (hw)); }
@@ -86,7 +86,7 @@ static int clk_prcmu_opp_prepare(struct clk_hw *hw) clk->opp_requested =1 ; } - err =prcmu_request_clock (clk->cg_sel, true); + err =db8500_prcmu_request_clock (clk->cg_sel, true);if (err) {prcmu_qos_remove_requirement (PRCMU_QOS_APE_OPP , (char *)clk_hw_get_name (hw));
@@ -101,7 +101,7 @@ static void clk_prcmu_opp_unprepare(struct clk_hw *hw) {struct clk_prcmu *clk =to_clk_prcmu (hw); -if (prcmu_request_clock (clk->cg_sel, false)) { +if (db8500_prcmu_request_clock (clk->cg_sel, false)) {pr_err ("clk_prcmu: %s failed to disable %s.\n" , __func__,clk_hw_get_name (hw));return ;
@@ -120,7 +120,7 @@ static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw)struct clk_prcmu *clk =to_clk_prcmu (hw);if (!clk->opp_requested) { - err =prcmu_request_ape_opp_100_voltage (true); + err =db8500_prcmu_request_ape_opp_100_voltage (true);if (err) {pr_err ("clk_prcmu: %s fail req APE OPP VOLT for %s.\n" , __func__,clk_hw_get_name (hw));
@@ -129,9 +129,9 @@ static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw) clk->opp_requested =1 ; } - err =prcmu_request_clock (clk->cg_sel, true); + err =db8500_prcmu_request_clock (clk->cg_sel, true);if (err) { -prcmu_request_ape_opp_100_voltage (false); +db8500_prcmu_request_ape_opp_100_voltage (false); clk->opp_requested =0 ;return err; }
@@ -143,14 +143,14 @@ static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw) {struct clk_prcmu *clk =to_clk_prcmu (hw); -if (prcmu_request_clock (clk->cg_sel, false)) { +if (db8500_prcmu_request_clock (clk->cg_sel, false)) {pr_err ("clk_prcmu: %s failed to disable %s.\n" , __func__,clk_hw_get_name (hw));return ; }if (clk->opp_requested) { -prcmu_request_ape_opp_100_voltage (false); +db8500_prcmu_request_ape_opp_100_voltage (false); clk->opp_requested =0 ; } }
drivers/clk/ux500/u8500_of_clk.c +1 -1
@@ -9,7 +9,7 @@#include <linux/of.h> #include <linux/of_address.h> #include <linux/clk-provider.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include "clk.h" #include "prcc.h"
drivers/cpuidle/cpuidle-ux500.c +3 -3
@@ -11,7 +11,7 @@#include <linux/spinlock.h> #include <linux/atomic.h> #include <linux/smp.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include <linux/platform_data/arm-ux500-pm.h> #include <linux/platform_device.h>
@@ -66,7 +66,7 @@ static inline int ux500_enter_idle(struct cpuidle_device *dev,/* Go to the retention state, the prcmu will wait for the * cpu to go WFI and this is what happens after exiting this * 'master' critical section */ -if (prcmu_set_power_state (PRCMU_AP_IDLE , true, true)) +if (db8500_prcmu_set_power_state (PRCMU_AP_IDLE , true, true)) goto out;/* When we switch to retention, the prcmu is in charge
@@ -109,7 +109,7 @@ static struct cpuidle_driver ux500_idle_driver = {static int dbx500_cpuidle_probe (struct platform_device *pdev) {/* Configure wake up reasons */ -prcmu_enable_wakeups (PRCMU_WAKEUP (ARM ) |PRCMU_WAKEUP (RTC ) | +db8500_prcmu_enable_wakeups (PRCMU_WAKEUP (ARM ) |PRCMU_WAKEUP (RTC ) |PRCMU_WAKEUP (ABB ));return cpuidle_register (&ux500_idle_driver,NULL );
drivers/mfd/ab8500-core.c +1 -1
@@ -19,7 +19,7 @@#include <linux/mfd/core.h> #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include <linux/of.h> /*
drivers/mfd/db8500-prcmu.c +3 -3
@@ -32,7 +32,7 @@#include <linux/platform_device.h> #include <linux/uaccess.h> #include <linux/mfd/core.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include <linux/mfd/abx500/ab8500.h> #include <linux/regulator/db8500-prcmu.h> #include <linux/regulator/machine.h>
@@ -2285,7 +2285,7 @@ void db8500_prcmu_system_reset(u16 reset_code)/** * db8500_prcmu_get_reset_code - Retrieve SW reset reason code * -* Retrieves the reset reason code stored by prcmu_system_reset() before +* Retrieves the reset reason code stored by db8500_prcmu_system_reset() before * last restart. */ u16 db8500_prcmu_get_reset_code (void )
@@ -3041,7 +3041,7 @@ static int db8500_prcmu_probe(struct platform_device *pdev)db8500_irq_init (np); -prcmu_config_esram0_deep_sleep (ESRAM0_DEEP_SLEEP_STATE_RET ); +db8500_prcmu_config_esram0_deep_sleep (ESRAM0_DEEP_SLEEP_STATE_RET ); err =mfd_add_devices (&pdev->dev,0 , common_prcmu_devs,ARRAY_SIZE (common_prcmu_devs),NULL ,0 , db8500_irq_domain);
drivers/regulator/db8500-prcmu.c +6 -6
@@ -13,7 +13,7 @@#include <linux/err.h> #include <linux/spinlock.h> #include <linux/platform_device.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> #include <linux/regulator/db8500-prcmu.h>
@@ -93,13 +93,13 @@ static int enable_epod(u16 epod_id, bool ramret)if (ramret) {if (!epod_on[epod_id]) { - ret =prcmu_set_epod (epod_id,EPOD_STATE_RAMRET ); + ret =db8500_prcmu_set_epod (epod_id,EPOD_STATE_RAMRET );if (ret <0 )return ret; } epod_ramret[epod_id] = true; }else { - ret =prcmu_set_epod (epod_id,EPOD_STATE_ON ); + ret =db8500_prcmu_set_epod (epod_id,EPOD_STATE_ON );if (ret <0 )return ret; epod_on[epod_id] = true;
@@ -114,18 +114,18 @@ static int disable_epod(u16 epod_id, bool ramret)if (ramret) {if (!epod_on[epod_id]) { - ret =prcmu_set_epod (epod_id,EPOD_STATE_OFF ); + ret =db8500_prcmu_set_epod (epod_id,EPOD_STATE_OFF );if (ret <0 )return ret; } epod_ramret[epod_id] = false; }else {if (epod_ramret[epod_id]) { - ret =prcmu_set_epod (epod_id,EPOD_STATE_RAMRET ); + ret =db8500_prcmu_set_epod (epod_id,EPOD_STATE_RAMRET );if (ret <0 )return ret; }else { - ret =prcmu_set_epod (epod_id,EPOD_STATE_OFF ); + ret =db8500_prcmu_set_epod (epod_id,EPOD_STATE_OFF );if (ret <0 )return ret; }
drivers/thermal/db8500_thermal.c +5 -5
@@ -10,7 +10,7 @@#include <linux/cpu_cooling.h> #include <linux/interrupt.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h>
@@ -82,7 +82,7 @@ static void db8500_thermal_update_config(struct db8500_thermal_zone *th,unsigned long next_low, unsigned long next_high) { -prcmu_stop_temp_sense (); +db8500_prcmu_stop_temp_sense (); th->cur_index = idx; th->interpolated_temp = (next_low + next_high)/2 ;
@@ -91,8 +91,8 @@ static void db8500_thermal_update_config(struct db8500_thermal_zone *th,* The PRCMU accept absolute temperatures in celsius so divide * down the millicelsius with 1000 */ -prcmu_config_hotmon ((u8)(next_low/1000 ), (u8)(next_high/1000 )); -prcmu_start_temp_sense (PRCMU_DEFAULT_MEASURE_TIME ); +db8500_prcmu_config_hotmon ((u8)(next_low /1000 ), (u8)(next_high /1000 )); +db8500_prcmu_start_temp_sense (PRCMU_DEFAULT_MEASURE_TIME ); }static irqreturn_t prcmu_low_irq_handler (int irq,void *irq_data)
@@ -204,7 +204,7 @@ static int db8500_thermal_probe(struct platform_device *pdev)static int db8500_thermal_suspend (struct platform_device *pdev,pm_message_t state) { -prcmu_stop_temp_sense (); +db8500_prcmu_stop_temp_sense ();return 0 ; }
drivers/watchdog/db8500_wdt.c +11 -11
@@ -16,7 +16,7 @@#include <linux/watchdog.h> #include <linux/platform_device.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #define WATCHDOG_TIMEOUT 600/* 10 minutes */
@@ -37,24 +37,24 @@ MODULE_PARM_DESC(nowayout,static int db8500_wdt_start (struct watchdog_device *wdd) { -return prcmu_enable_a9wdog (PRCMU_WDOG_ALL ); +return db8500_prcmu_enable_a9wdog (PRCMU_WDOG_ALL ); }static int db8500_wdt_stop (struct watchdog_device *wdd) { -return prcmu_disable_a9wdog (PRCMU_WDOG_ALL ); +return db8500_prcmu_disable_a9wdog (PRCMU_WDOG_ALL ); }static int db8500_wdt_keepalive (struct watchdog_device *wdd) { -return prcmu_kick_a9wdog (PRCMU_WDOG_ALL ); +return db8500_prcmu_kick_a9wdog (PRCMU_WDOG_ALL ); }static int db8500_wdt_set_timeout (struct watchdog_device *wdd,unsigned int timeout) {db8500_wdt_stop (wdd); -prcmu_load_a9wdog (PRCMU_WDOG_ALL , timeout *1000 ); +db8500_prcmu_load_a9wdog (PRCMU_WDOG_ALL , timeout *1000 );db8500_wdt_start (wdd);return 0 ;
@@ -91,10 +91,10 @@ static int db8500_wdt_probe(struct platform_device *pdev)watchdog_set_nowayout (&db8500_wdt, nowayout);/* disable auto off on sleep */ -prcmu_config_a9wdog (PRCMU_WDOG_CPU1 , false); +db8500_prcmu_config_a9wdog (PRCMU_WDOG_CPU1 , false);/* set HW initial value */ -prcmu_load_a9wdog (PRCMU_WDOG_ALL , timeout *1000 ); +db8500_prcmu_load_a9wdog (PRCMU_WDOG_ALL , timeout *1000 ); ret =devm_watchdog_register_device (dev, &db8500_wdt);if (ret)
@@ -110,9 +110,9 @@ static int db8500_wdt_suspend(struct platform_device *pdev, {if (watchdog_active (&db8500_wdt)) {db8500_wdt_stop (&db8500_wdt); -prcmu_config_a9wdog (PRCMU_WDOG_CPU1 , true); +db8500_prcmu_config_a9wdog (PRCMU_WDOG_CPU1 , true); -prcmu_load_a9wdog (PRCMU_WDOG_ALL , timeout *1000 ); +db8500_prcmu_load_a9wdog (PRCMU_WDOG_ALL , timeout *1000 );db8500_wdt_start (&db8500_wdt); }return 0 ;
@@ -122,9 +122,9 @@ static int db8500_wdt_resume(struct platform_device *pdev) {if (watchdog_active (&db8500_wdt)) {db8500_wdt_stop (&db8500_wdt); -prcmu_config_a9wdog (PRCMU_WDOG_CPU1 , false); +db8500_prcmu_config_a9wdog (PRCMU_WDOG_CPU1 , false); -prcmu_load_a9wdog (PRCMU_WDOG_ALL , timeout *1000 ); +db8500_prcmu_load_a9wdog (PRCMU_WDOG_ALL , timeout *1000 );db8500_wdt_start (&db8500_wdt); }return 0 ;
include/linux/mfd/db8500-prcmu.h +250 -2
312 lines not on this page · raw message
include/linux/mfd/dbx500-prcmu.h deleted +0 -575
575 lines not on this page · raw message
sound/soc/ux500/ux500_msp_dai.c +1 -1
@@ -14,7 +14,7 @@#include <linux/clk.h> #include <linux/of.h> #include <linux/regulator/consumer.h> -#include <linux/mfd/dbx500-prcmu.h> +#include <linux/mfd/db8500-prcmu.h> #include <sound/soc.h> #include <sound/soc-dai.h>
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 184/733 ASoC: ux500: Deassert the MSP reset during probe
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 66ec63e7a90bedc56aa050fbe437964008c3d584 ]
The devicetree has described each MSP reset line since the PRCC reset controller was added, but the driver never acquires or deasserts it. The block can consequently remain inaccessible when firmware has left it in reset.
Acquire the reset exclusively and keep it deasserted for the lifetime of the bound device.
--- sound/soc/ux500/ux500_msp_dai.c | 7 +++++++ 1 file changed, 7 insertions(+)
sound/soc/ux500/ux500_msp_dai.c +7 -0
@@ -14,6 +14,7 @@#include <linux/clk.h> #include <linux/of.h> #include <linux/regulator/consumer.h> +#include <linux/reset.h> #include <linux/mfd/db8500-prcmu.h> #include <sound/soc.h>
@@ -686,6 +687,7 @@ static const struct snd_soc_component_driver ux500_msp_component = {static int ux500_msp_drv_probe (struct platform_device *pdev) {struct ux500_msp_i2s_drvdata *drvdata; +struct reset_control *reset;int ret =0 ; drvdata = devm_kzalloc(&pdev->dev,
@@ -733,6 +735,11 @@ static int ux500_msp_drv_probe(struct platform_device *pdev)return -EINVAL ; } + reset =devm_reset_control_get_exclusive_deasserted (&pdev->dev,NULL ); +if (IS_ERR (reset)) +return dev_err_probe (&pdev->dev,PTR_ERR (reset), +"Failed to deassert MSP reset\n" ); + ret =ux500_msp_i2s_init_msp (pdev, &drvdata->msp);if (ret) { dev_err(&pdev->dev,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 185/733 ASoC: ux500: Request the MSP MMIO resource
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 4fb67925f33ad789e9e00903a73306ed40f7ae32 ]
A bare devm_ioremap() neither reserves the register range nor preserves the platform resource error. This permits another driver to claim the same range and reports every mapping failure as an allocation failure.
Use the managed platform resource helper, retaining the resolved resource only to derive the DMA register address.
--- sound/soc/ux500/ux500_msp_i2s.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-)
sound/soc/ux500/ux500_msp_i2s.c +4 -14
@@ -733,7 +733,7 @@ int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir)int ux500_msp_i2s_init_msp (struct platform_device *pdev,struct ux500_msp **msp_p) { -struct resource *res =NULL ; +struct resource *res;struct ux500_msp *msp; *msp_p =devm_kzalloc (&pdev->dev,sizeof (struct ux500_msp ),GFP_KERNEL );
@@ -743,20 +743,10 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev, msp->dev = &pdev->dev; - res =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); -if (res ==NULL ) { -dev_err (&pdev->dev,"%s: ERROR: Unable to get resource!\n" , - __func__); -return -ENOMEM ; - } - + msp->registers =devm_platform_get_and_ioremap_resource (pdev,0 , &res); +if (IS_ERR (msp->registers)) +return PTR_ERR (msp->registers); msp->tx_rx_addr = res->start +MSP_DR ; - msp->registers =devm_ioremap (&pdev->dev, res->start, -resource_size (res)); -if (msp->registers ==NULL ) { -dev_err (&pdev->dev,"%s: ERROR: ioremap failed!\n" , __func__); -return -ENOMEM ; - } msp->msp_state =MSP_STATE_IDLE ; msp->loopback_enable =0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 186/733 ASoC: ux500: Remove obsolete PRCMU QoS calls
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 7b819677b503667422b0b7bdb21853e0066f8606 ]
The DB8500 PRCMU QoS interface consists of unconditional inline stubs, so the MSP calls and cached constraint state have no effect. Device power and clocks are already represented by the regulator, power-domain and common-clock frameworks.
Remove the dead calls and their private state instead of pretending to change the APE operating point.
Stable-dep-of: dc1a1b1e2206 ("ASoC: ux500: Allow repeated MSP prepare calls")
--- sound/soc/ux500/ux500_msp_dai.c | 26 -------------------------- sound/soc/ux500/ux500_msp_dai.h | 2 -- 2 files changed, 28 deletions(-)
sound/soc/ux500/ux500_msp_dai.c +0 -26
@@ -15,7 +15,6 @@#include <linux/of.h> #include <linux/regulator/consumer.h> #include <linux/reset.h> -#include <linux/mfd/db8500-prcmu.h> #include <sound/soc.h> #include <sound/soc-dai.h>
@@ -393,12 +392,6 @@ static void ux500_msp_dai_shutdown(struct snd_pcm_substream *substream,dev_dbg (dai->dev,"%s: MSP %d (%s): Enter.\n" , __func__, dai->id,snd_pcm_stream_str (substream)); -if (drvdata->vape_opp_constraint ==1 ) { -prcmu_qos_update_requirement (PRCMU_QOS_APE_OPP , -"ux500_msp_i2s" ,50 ); - drvdata->vape_opp_constraint =0 ; - } -if (ux500_msp_i2s_close (drvdata->msp, is_playback ?MSP_DIR_TX :MSP_DIR_RX )) { dev_err(dai->dev,
@@ -440,21 +433,6 @@ static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream,return ret; } -/* Set OPP-level */ -if ((drvdata->fmt &SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK ) && - (drvdata->msp->f_bitclk >19200000 )) { -/* If the bit-clock is higher than 19.2MHz, Vape should be -* run in 100% OPP. Only when bit-clock is used (MSP master) -*/ -prcmu_qos_update_requirement (PRCMU_QOS_APE_OPP , -"ux500-msp-i2s" ,100 ); - drvdata->vape_opp_constraint =1 ; - }else { -prcmu_qos_update_requirement (PRCMU_QOS_APE_OPP , -"ux500-msp-i2s" ,50 ); - drvdata->vape_opp_constraint =0 ; - } -return ret; }
@@ -710,8 +688,6 @@ static int ux500_msp_drv_probe(struct platform_device *pdev) __func__, ret);return ret; } -prcmu_qos_add_requirement (PRCMU_QOS_APE_OPP , (char *)pdev->name,50 ); - drvdata->pclk =devm_clk_get (&pdev->dev,"apb_pclk" );if (IS_ERR (drvdata->pclk)) { ret =PTR_ERR (drvdata->pclk);
@@ -780,8 +756,6 @@ static void ux500_msp_drv_remove(struct platform_device *pdev)snd_soc_unregister_component (&pdev->dev); -prcmu_qos_remove_requirement (PRCMU_QOS_APE_OPP ,"ux500_msp_i2s" ); -ux500_msp_i2s_cleanup_msp (pdev, drvdata->msp); }
sound/soc/ux500/ux500_msp_dai.h +0 -2
@@ -46,8 +46,6 @@ struct ux500_msp_i2s_drvdata {struct clk *clk;struct clk *pclk; -/* Regulators */ -int vape_opp_constraint; };int ux500_msp_dai_set_data_delay (struct snd_soc_dai *dai,int delay);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 187/733 ASoC: ux500: Allow repeated MSP prepare calls
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit dc1a1b1e22066f01bb86a9b11ee998d4dc72db66 ]
ALSA can call the DAI prepare callback again after an XRUN without first shutting down the stream. The MSP open helper rejects the second call with -EBUSY because the direction remains configured.
Track successful playback and capture configurations at the DAI layer. Make repeated prepare calls no-ops and only close directions which were successfully prepared.
--- sound/soc/ux500/ux500_msp_dai.c | 28 +++++++++++++++++++++------- sound/soc/ux500/ux500_msp_dai.h | 1 + 2 files changed, 22 insertions(+), 7 deletions(-)
sound/soc/ux500/ux500_msp_dai.c +21 -7
@@ -388,15 +388,21 @@ static void ux500_msp_dai_shutdown(struct snd_pcm_substream *substream,int ret;struct ux500_msp_i2s_drvdata *drvdata =dev_get_drvdata (dai->dev);bool is_playback = (substream->stream ==SNDRV_PCM_STREAM_PLAYBACK ); +unsigned int configured = is_playback ?PLAYBACK_CONFIGURED : +CAPTURE_CONFIGURED ; +unsigned int dir = is_playback ?MSP_DIR_TX :MSP_DIR_RX ;dev_dbg (dai->dev,"%s: MSP %d (%s): Enter.\n" , __func__, dai->id,snd_pcm_stream_str (substream)); -if (ux500_msp_i2s_close (drvdata->msp, - is_playback ?MSP_DIR_TX :MSP_DIR_RX )) { -dev_err (dai->dev, -"%s: Error: MSP %d (%s): Unable to close i2s.\n" , - __func__, dai->id,snd_pcm_stream_str (substream)); +if (drvdata->configured & configured) { +if (ux500_msp_i2s_close (drvdata->msp, dir)) { +dev_err (dai->dev, +"%s: Error: MSP %d (%s): Unable to close i2s.\n" , + __func__, dai->id, +snd_pcm_stream_str (substream)); + } + drvdata->configured &= ~configured; }/* Disable and unprepare clocks */
@@ -414,14 +420,20 @@ static void ux500_msp_dai_shutdown(struct snd_pcm_substream *substream,static int ux500_msp_dai_prepare (struct snd_pcm_substream *substream,struct snd_soc_dai *dai) { -int ret =0 ;struct ux500_msp_i2s_drvdata *drvdata =dev_get_drvdata (dai->dev);struct snd_pcm_runtime *runtime = substream->runtime;struct ux500_msp_config msp_config; +bool is_playback = substream->stream ==SNDRV_PCM_STREAM_PLAYBACK ; +unsigned int configured = is_playback ?PLAYBACK_CONFIGURED : +CAPTURE_CONFIGURED ; +int ret;dev_dbg (dai->dev,"%s: MSP %d (%s): Enter (rate = %d).\n" , __func__, dai->id,snd_pcm_stream_str (substream), runtime->rate); +if (drvdata->configured & configured) +return 0 ; + ret =setup_msp_config (substream, dai, &msp_config);if (ret)return ret;
@@ -433,7 +445,9 @@ static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream,return ret; } -return ret; + drvdata->configured |= configured; + +return 0 ; } static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream,
sound/soc/ux500/ux500_msp_dai.h +1 -0
@@ -36,6 +36,7 @@ struct ux500_msp_i2s_drvdata {struct ux500_msp *msp;struct regulator *reg_vape;unsigned int fmt; +unsigned int configured;unsigned int tx_mask;unsigned int rx_mask;int slots;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 188/733 ASoC: ux500: Program the MSP FIFO watermarks
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 2519439b4b5f6ee95879b1a44fc373127291b1e4 ]
The DMA engine is configured for four-element bursts, but the MSP driver never programs the FIFO watermark register and instead depends on its previous or reset value. The DB8500 DMA request protocol requires the peripheral watermark to match the DMA packet size.
Program four-element receive and transmit watermarks when configuring the first direction, before enabling MSP DMA requests.
--- sound/soc/ux500/ux500_msp_i2s.c | 2 ++ sound/soc/ux500/ux500_msp_i2s.h | 5 +++++ 2 files changed, 7 insertions(+)
sound/soc/ux500/ux500_msp_i2s.c +2 -0
@@ -507,6 +507,8 @@ int ux500_msp_i2s_open(struct ux500_msp *msp, old_reg &= ~mask; new_reg |= old_reg;writel (new_reg, msp->registers +MSP_GCR ); +writel (MSP_WMRK_TX_4_ELEMENTS |MSP_WMRK_RX_4_ELEMENTS , + msp->registers +MSP_WMRK ); } res =enable_msp (msp, config, first);
sound/soc/ux500/ux500_msp_i2s.h +5 -0
@@ -62,6 +62,7 @@ enum msp_direction {#define MSP_SRG 0x10#define MSP_FLR 0x14#define MSP_DMACR 0x18 +#define MSP_WMRK 0x1c#define MSP_IMSC 0x20#define MSP_RIS 0x24
@@ -228,6 +229,10 @@ enum msp_direction {#define RDMAE_SHIFT 0#define TDMAE_SHIFT 1 +/* FIFO watermark register */ +#define MSP_WMRK_RX_4_ELEMENTS BIT(0) +#define MSP_WMRK_TX_4_ELEMENTS BIT(3) +/* Interrupt Register */ #define RX_SERVICE_INT BIT(0)#define RX_OVERRUN_ERROR_INT BIT(1)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 189/733 bpf: backtrack_insn(): Handle ld_{abs,ind} subprog exit edge
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eduard Zingerman <[email protected]>
[ Upstream commit 387b1baefbb776e3f48dc2261e77a49213f470f7 ]
Nicholas Carlini reported a bug in precision backtracking mechanism for BPF_LD | BPF_{IND,ABS} instructions. These instructions are modelled as two branches:
- fallthrough;
- implicit exit from current subprogram.
The implicit exit case was not handled by the backtrack_insn() function. When backtracking such a path backtrack_insn() did not call bt_subprog_enter(), which meant that backtracking continued manipulating precision marks in a caller frame, while looking at instructions in a callee frame.
This lead to segmentation faults during verification (see the selftest), or unsound state pruning.
--- kernel/bpf/backtrack.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-)
kernel/bpf/backtrack.c +19 -6
@@ -583,16 +583,29 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,*/ } }else if (class ==BPF_LD ) { -if (!bt_is_reg_set (bt, dreg)) -return 0 ; -bt_clear_reg (bt, dreg);/* It's ld_imm64 or ld_abs or ld_ind. * For ld_imm64 no further tracking of precision * into parent is necessary */ -if (mode ==BPF_IND || mode ==BPF_ABS ) -/* to be analyzed */ -return -ENOTSUPP ; +if (mode ==BPF_IMM ) { +bt_clear_reg (bt, dreg); +return 0 ; + } +/* +* BPF_{IND,ABS} are modelled as two branches: +* - fallthrough; +* - implicit subprogram exit. +* It is necessary to switch current frame if +* implicit subprogram exit branch is backtracked. +*/ +if (mode ==BPF_IND || mode ==BPF_ABS ) { +if (bt_is_reg_set (bt, dreg)) +return -ENOTSUPP ; +if (subseq_idx != idx +1 ) +if (bt_subprog_enter (bt)) +return -EFAULT ; +return 0 ; + } }/* Propagate precision marks to linked registers, to account for * registers marked as precise in this function.
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 190/733 bpf: backtracking shouldnt clear outer frame R1-R5 for callbacks
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eduard Zingerman <[email protected]>
[ Upstream commit e3e4f66cc4b72333d0886ae2673c360248987889 ]
When processing calls to bpf_loop() verifier marks R1 (and R4) as precise. R1 tracks loop iterations number and because of the 'callback_depth < R1' mechanics in check_helper_call() must be marked precise. However, precision propagation for R1 was broken, when bpf_loop() call was verified on a second iteration.
Consider the following verification trace:
- main: bpf_loop(nr_loops, callback ...)
- callback: BPF_EXIT
- main: bpf_loop(nr_loops, callback ...)
- ...
While the first visit of the call to bpf_loop() propagated R1 precision as expected, the second call to mark_chain_precision() in the check_helper_call() set R1, but it was immediately reset when backtrack_insn() processed preceding BPF_EXIT in the loop deleted in this patch.
Because of that, the second visit of the call to bpf_loop() injected checkpoint with R1 not marked as precise. Which could trick the verifier into accepting unsafe programs. See the next patch for an example of such program.
Commit is structured in a way to minimize conflicts when 'bpf' would be eventually merged with 'bpf-next'.
--- kernel/bpf/backtrack.c | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-)
kernel/bpf/backtrack.c +17 -20
@@ -521,37 +521,34 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,return -EFAULT ; } }else if (opcode ==BPF_EXIT ) { -bool r0_precise; +bool from_subprog_call, r0_precise; + +/* BPF_EXIT in subprog or callback always returns +* right after the call instruction, so by checking +* whether the instruction at subseq_idx-1 is subprog +* call or not we can distinguish actual exit from +* *subprog* from exit from *callback*. In the former +* case, we need to propagate r0 precision, if +* necessary. In the former we never do that. +*/ + from_subprog_call = subseq_idx -1 >=0 && +bpf_pseudo_call (&env->prog->insnsi[subseq_idx -1 ]); + + r0_precise = from_subprog_call &&bt_is_reg_set (bt,BPF_REG_0 );/* Backtracking to a nested function call, 'idx' is a part of * the inner frame 'subseq_idx' is a part of the outer frame. * In case of a regular function call, instructions giving * precision to registers R1-R5 should have been found already. -* In case of a callback, it is ok to have R1-R5 marked for -* backtracking, as these registers are set by the function -* invoking callback. +* In case of a callback from bpf_loop(), R{1,4} in the calling +* frame would be set as precise and that is correct. */ -if (subseq_idx >=0 &&bpf_calls_callback (env, subseq_idx)) -for (i =BPF_REG_1 ; i <=BPF_REG_5 ; i++) -bt_clear_reg (bt, i); -if (bt_reg_mask (bt) &BPF_REGMASK_ARGS ) { +if (from_subprog_call && (bt_reg_mask (bt) &BPF_REGMASK_ARGS )) {verifier_bug (env,"backtracking exit unexpected regs %x" ,bt_reg_mask (bt));return -EFAULT ; } -/* BPF_EXIT in subprog or callback always returns -* right after the call instruction, so by checking -* whether the instruction at subseq_idx-1 is subprog -* call or not we can distinguish actual exit from -* *subprog* from exit from *callback*. In the former -* case, we need to propagate r0 precision, if -* necessary. In the former we never do that. -*/ - r0_precise = subseq_idx -1 >=0 && -bpf_pseudo_call (&env->prog->insnsi[subseq_idx -1 ]) && -bt_is_reg_set (bt,BPF_REG_0 ); -bt_clear_reg (bt,BPF_REG_0 );if (bt_subprog_enter (bt))return -EFAULT ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 191/733 btrfs: scrub: report the failing sectors address, not the stripe base
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: James C. Owens <[email protected]>
[ Upstream commit 33ce0aa4c57611c3a3485ec7c01ad67b3751447d ]
scrub_stripe_report_errors() iterates over the sectors of a stripe, but every message it emits passes stripe->logical, the address of the first sector of the 64KiB stripe, rather than the address of the sector being reported. The physical address is likewise computed once, before the loop, from stripe->logical.
This matters because scrub_print_common_warning() uses that logical address for the backref walk which produces the "root %llu inode %llu offset %llu ... (path: ...)" part of the message. As the address is always the stripe base, the reported root/inode/offset/path can identify a different file from the one whose sector actually failed.
A 64KiB stripe routinely spans several extents belonging to unrelated files. On the machine where this was found, the stripe at logical 0x17D9380000 holds four sectors of /usr/share/plasma/emoji/bg.dict, then a file inside a docker volume, then sectors referenced only by snapshots. Every error anywhere in that stripe is attributed to bg.dict.
The effect is visible statistically: across ten months and four kernel series that machine logged 81 distinct flagged logical addresses, and every one of them is exactly 64KiB aligned. Since BTRFS_STRIPE_LEN is 64KiB and stripe->logical is stripe aligned by construction, real failures distributed across sectors could not produce that.
Report the address of the sector actually being examined. Adding the sector offset to the physical address is valid because BTRFS_STRIPE_LEN is the unit contiguous on a single device for every profile, so a stripe never crosses a device boundary.
--- fs/btrfs/scrub.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-)
fs/btrfs/scrub.c +14 -10
@@ -1041,6 +1041,10 @@ static void scrub_stripe_report_errors(struct scrub_ctx *sctx, skip:for_each_set_bit (sector_nr, &extent_bitmap, stripe->nr_sectors) { +const u64 sector_logical = stripe->logical + + ((u64 )sector_nr << fs_info->sectorsize_bits); +const u64 sector_physical = physical + + ((u64 )sector_nr << fs_info->sectorsize_bits);bool repaired = false;if (scrub_bitmap_test_bit_is_metadata (stripe, sector_nr)) {
@@ -1069,12 +1073,12 @@ static void scrub_stripe_report_errors(struct scrub_ctx *sctx,if (dev) {btrfs_err_rl (fs_info,"scrub: fixed up error at logical %llu on dev %s physical %llu" , - stripe->logical,btrfs_dev_name (dev), - physical); + sector_logical,btrfs_dev_name (dev), + sector_physical); }else {btrfs_err_rl (fs_info,"scrub: fixed up error at logical %llu on mirror %u" , - stripe->logical, stripe->mirror_num); + sector_logical, stripe->mirror_num); }continue ; }
@@ -1083,30 +1087,30 @@ static void scrub_stripe_report_errors(struct scrub_ctx *sctx,if (dev) {btrfs_err_rl (fs_info,"scrub: unable to fixup (regular) error at logical %llu on dev %s physical %llu" , - stripe->logical,btrfs_dev_name (dev), - physical); + sector_logical,btrfs_dev_name (dev), + sector_physical); }else {btrfs_err_rl (fs_info,"scrub: unable to fixup (regular) error at logical %llu on mirror %u" , - stripe->logical, stripe->mirror_num); + sector_logical, stripe->mirror_num); }if (scrub_bitmap_test_bit_io_error (stripe, sector_nr))if (__ratelimit (&rs) && dev)scrub_print_common_warning ("i/o error" , dev, false, - stripe->logical, physical); + sector_logical, sector_physical);if (scrub_bitmap_test_bit_csum_error (stripe, sector_nr))if (__ratelimit (&rs) && dev)scrub_print_common_warning ("checksum error" , dev, false, - stripe->logical, physical); + sector_logical, sector_physical);if (scrub_bitmap_test_bit_meta_error (stripe, sector_nr))if (__ratelimit (&rs) && dev)scrub_print_common_warning ("header error" , dev, false, - stripe->logical, physical); + sector_logical, sector_physical);if (scrub_bitmap_test_bit_meta_gen_error (stripe, sector_nr))if (__ratelimit (&rs) && dev)scrub_print_common_warning ("generation error" , dev, false, - stripe->logical, physical); + sector_logical, sector_physical); }/* Update the device stats. */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 192/733 btrfs: fix transaction use-after-free in raid stripe insertion
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shuangpeng Bai <[email protected]>
[ Upstream commit a8813a923f9e43f788b357fb55c35f7f6ed6f98c ]
If allocation of a RAID stripe extent fails, btrfs_insert_one_raid_extent() aborts and ends the transaction before returning -ENOMEM.
btrfs_finish_one_ordered(), the production caller through btrfs_insert_raid_extent(), still owns the transaction handle. It handles the error by aborting the transaction and then reaches the common exit path, which ends the transaction again.
The premature end can free the handle and drop its transaction reference. Transaction cleanup can then free the transaction before the caller's second abort accesses the handle and transaction, resulting in use-after-free.
Keep the abort at the failure site, but let the caller's common exit path end the transaction once, after it has finished using both objects.
--- fs/btrfs/raid-stripe-tree.c | 1 - 1 file changed, 1 deletion(-)
fs/btrfs/raid-stripe-tree.c +0 -1
@@ -337,7 +337,6 @@ int btrfs_insert_one_raid_extent(struct btrfs_trans_handle *trans, stripe_extent =kzalloc (item_size,GFP_NOFS );if (unlikely (!stripe_extent)) {btrfs_abort_transaction (trans, -ENOMEM ); -btrfs_end_transaction (trans);return -ENOMEM ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 193/733 btrfs: fix the possible bioc_list memory leak during error
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qu Wenruo <[email protected]>
[ Upstream commit afbe73778338e6d1ac8c4486fbdf33f0cc1f2624 ]
There are two possible ways to leak bioc memory on btrfs_ordered_extent::bioc_list:
- An error occurred for btrfs_insert_one_raid_extent() Then the function btrfs_insert_raid_extent() immediately return without freeing any bioc in the bioc_list.
- An ordered extent hit an IO error
In that case the ordered extent will have BTRFS_ORDERED_IOERR set, and skip the call on btrfs_insert_raid_extent() completely.
Fix the problem by:
- Introduce a new helper, btrfs_cleanup_ordered_bioc_list() Which will remove all bioc from the bioc_list, and release the bioc.
- Call the above helper for btrfs_insert_raid_extent() So that the cleanup helper is always called no matter what.
- Call the above helper for btrfs_finish_one_ordered() This is called just before the final release on the ordered extent.
This was reported by Sashiko when reviewing another patch.
--- fs/btrfs/inode.c | 3 +++ fs/btrfs/raid-stripe-tree.c | 18 ++++++++++++------ fs/btrfs/raid-stripe-tree.h | 1 + 3 files changed, 16 insertions(+), 6 deletions(-)
fs/btrfs/inode.c +3 -0
@@ -3435,6 +3435,9 @@ int btrfs_finish_one_ordered(struct btrfs_ordered_extent *ordered_extent)*/ btrfs_remove_ordered_extent (ordered_extent); +/* Cleanup any remaining biocs attached to the OE. */ +btrfs_cleanup_ordered_bioc_list (ordered_extent); +/* once for us */ btrfs_put_ordered_extent (ordered_extent);/* once for the tree */
fs/btrfs/raid-stripe-tree.c +12 -6
@@ -373,7 +373,7 @@ int btrfs_insert_raid_extent(struct btrfs_trans_handle *trans, struct btrfs_ordered_extent *ordered_extent) {struct btrfs_io_context *bioc; -int ret; +int ret =0 ;if (!btrfs_fs_incompat (trans->fs_info,RAID_STRIPE_TREE ))return 0 ;
@@ -381,17 +381,23 @@ int btrfs_insert_raid_extent(struct btrfs_trans_handle *trans,list_for_each_entry (bioc, &ordered_extent->bioc_list, rst_ordered_entry) { ret =btrfs_insert_one_raid_extent (trans, bioc);if (ret) -return ret; +break ; } -while (!list_empty (&ordered_extent->bioc_list)) { - bioc =list_first_entry (&ordered_extent->bioc_list, +btrfs_cleanup_ordered_bioc_list (ordered_extent); +return ret; +} + +void btrfs_cleanup_ordered_bioc_list (struct btrfs_ordered_extent *ordered) +{ +while (!list_empty (&ordered->bioc_list)) { +struct btrfs_io_context *bioc; + + bioc =list_first_entry (&ordered->bioc_list,typeof (*bioc), rst_ordered_entry);list_del (&bioc->rst_ordered_entry);btrfs_put_bioc (bioc); } - -return 0 ; } int btrfs_get_raid_extent_offset(struct btrfs_fs_info *fs_info,
fs/btrfs/raid-stripe-tree.h +1 -0
@@ -28,6 +28,7 @@ int btrfs_get_raid_extent_offset(struct btrfs_fs_info *fs_info, u32 stripe_index, struct btrfs_io_stripe *stripe);int btrfs_insert_raid_extent (struct btrfs_trans_handle *trans,struct btrfs_ordered_extent *ordered_extent); +void btrfs_cleanup_ordered_bioc_list (struct btrfs_ordered_extent *ordered);#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS int btrfs_insert_one_raid_extent(struct btrfs_trans_handle *trans,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 194/733 btrfs: return proper negative error code for update_raid_extent_item()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qu Wenruo <[email protected]>
[ Upstream commit a03fa65184545837d6461413275da71f30527385 ]
The function btrfs_abort_transaction() only accepts negative error code, and have the macro VERIFY_NEGATIVE_ERROR() to verify that error code.
But inside update_raid_extent_item(), if there is such key found, we return 1, breaking the negative error code scheme.
Furthermore if we hit some real error during the tree search, e.g. -EIO, then the error code is always over-written to -EINVAL.
Fix both problems by following other call sites by overwriting @ret to -ENOENT if the btrfs_search_slot() failed to locate the key.
This is very unlikely to hit, as we only enter update_raid_extent_item() if there is a conflicting key already in the raid stripe tree.
This was reported by Sashiko when reviewing another patch.
--- fs/btrfs/raid-stripe-tree.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
fs/btrfs/raid-stripe-tree.c +4 -2
@@ -310,8 +310,10 @@ static int update_raid_extent_item(struct btrfs_trans_handle *trans, ret =btrfs_search_slot (trans, trans->fs_info->stripe_root, key, path,0 ,1 ); -if (ret) -return (ret ==1 ? ret : -EINVAL ); +if (ret >0 ) + ret = -ENOENT ; +if (ret <0 ) +return ret; leaf = path->nodes[0 ]; slot = path->slots[0 ];
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 195/733 btrfs: zoned: finish active block group cleanup if call_zone_finish() fails
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johannes Thumshirn <[email protected]>
[ Upstream commit a18a6b93a2843b9d103d3456bbd4b3f90282a379 ]
do_zone_finish() clears BLOCK_GROUP_FLAG_ZONE_IS_ACTIVE before finishing the zones. If call_zone_finish() then fails it returned early, leaving the now inactive block group on fs_info->zone_active_bgs, leaking its reference, the BTRFS_FS_NEED_ZONE_FINISH waiters are never woken, and as its alloc_offset equals the zone capacity btrfs_zone_finish_one_bg() keeps selecting it, spinning btrfs_zoned_activate_one_bg().
Fall through to the cleanup on failure too and return the error, but keep the block group read-only as its zones are left inconsistent.
--- fs/btrfs/zoned.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-)
fs/btrfs/zoned.c +4 -7
@@ -2626,16 +2626,13 @@ static int do_zone_finish(struct btrfs_block_group *block_group, bool fully_writdown_read (&dev_replace->rwsem); map = block_group->physical_map;for (i =0 ; i < map->num_stripes; i++) { - ret =call_zone_finish (block_group, &map->stripes[i]); -if (ret) { -up_read (&dev_replace->rwsem); -return ret; - } +if (ret) +break ; }up_read (&dev_replace->rwsem); -if (!fully_written) +if (!ret && !fully_written)btrfs_dec_block_group_ro (block_group);spin_lock (&fs_info->zone_active_bgs_lock);
@@ -2648,7 +2645,7 @@ static int do_zone_finish(struct btrfs_block_group *block_group, bool fully_writclear_and_wake_up_bit (BTRFS_FS_NEED_ZONE_FINISH , &fs_info->flags); -return 0 ; +return ret; }int btrfs_zone_finish (struct btrfs_block_group *block_group)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 196/733 btrfs: send: fix lost error return value in will_overwrite_ref()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Avi Weiss <[email protected]>
[ Upstream commit d0285dfbc3b46f41395b26ee2f4a16d99fb3e736 ]
The direct-return refactoring in commit b3047a42f55d ("btrfs: send:
directly return from will_overwrite_ref() and simplify it") changed will_overwrite_ref() to return directly instead of going through the common out label.
That resulted in a negative return value from is_inode_existent() to start being converted to 0, making lookup errors unable to be distinguished from the inode not existing.
process_recorded_refs() expects negative errors from will_overwrite_ref() and aborts processing when it receives one.
Return the value from is_inode_existent() to restore the previous error propagation behavior as it was before the refactor.
--- fs/btrfs/send.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/btrfs/send.c +1 -1
@@ -2066,7 +2066,7 @@ static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen, ret =is_inode_existent (sctx, dir, dir_gen,NULL , &parent_root_dir_gen);if (ret <=0 ) -return 0 ; +return ret;/* * If we have a parent root we need to verify that the parent dir was
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 197/733 btrfs: do not force reloc root creation during qgroup_account_snapshot()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qu Wenruo <[email protected]>
[ Upstream commit cacf35832292997018837e484283f95a9301ebf5 ]
[BUG]
When running btrfs/252 with quota enabled through MKFS_OPTIONS="-O quota", it has a high chance to trigger the following kernel warning and flips the fs RO:
BTRFS info (device dm-2): relocating block group 30408704 flags metadata|dup
------------[ cut here ]------------
WARNING: fs/btrfs/extent-tree.c:879 at lookup_inline_extent_backref+0x74b/0x960 [btrfs], CPU#4: btrfs/2173
CPU: 4 UID: 0 PID: 2173 Comm: btrfs Not tainted 7.2.0-rc6-custom+ #457 PREEMPT(full) 3adc6528fb66f7a55fe1095385818e742f200aab
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS unknown 02/02/2022
RIP: 0010:lookup_inline_extent_backref+0x74b/0x960 [btrfs]
Call Trace:
<TASK>
insert_inline_extent_backref+0x7c/0x160 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
__btrfs_inc_extent_ref+0xa9/0x270 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
__btrfs_run_delayed_refs+0x4af/0x11c0 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
btrfs_run_delayed_refs+0x9d/0xf0 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
create_pending_snapshot+0x39d/0xf00 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
create_pending_snapshots+0x9b/0xc0 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
btrfs_commit_transaction+0x280/0xeb0 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
prepare_to_relocate+0x147/0x200 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
relocate_block_group+0x6b/0x5e0 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
btrfs_relocate_block_group+0x92c/0x2380 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
btrfs_relocate_chunk+0x3f/0x1a0 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
btrfs_balance+0xa2c/0x19c0 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
btrfs_ioctl+0x2839/0x2d30 [btrfs 32f09462c54d9c922fca74a3e4866f4aa7737b72]
__x64_sys_ioctl+0x416/0x9a0
do_syscall_64+0xe1/0x790
entry_SYSCALL_64_after_hwframe+0x4b/0x53
</TASK>
---[ end trace 0000000000000000 ]---
BTRFS info (device dm-2): leaf 4593991680 gen 233 total ptrs 175 free space 5953 owner 2
BTRFS info (device dm-2): refs 3 lock_owner 2173 current 2173
item 0 key (166772736 METADATA_ITEM 1) itemoff 16250 itemsize 33
extent refs 1 gen 222 flags 2
ref#0: tree block backref root 266
[ Skip the tree dump ]
item 174 key (263225344 METADATA_ITEM 0) itemoff 10328 itemsize 33
extent refs 1 gen 162 flags 258
ref#0: tree block backref root 267
BTRFS error (device dm-2): extent item not found for insert, bytenr 179847168 num_bytes 16384 parent 4594335744 root_objectid 273 owner 0 offset 0
BTRFS error (device dm-2): failed to run delayed ref for logical 179847168 num_bytes 16384 type 182 action 1 ref_mod 1: -117[CAUSE]
The above error is showing that there is a tree reference to a metadata extent that is no longer there.
With "ref_verify" mount option (requires CONFIG_BTRFS_DEBUG), there is some extra debug output:
BTRFS error (device dm-2): dumping block entry [180961280 16384], num_refs 0, metadata 1, from disk 0
BTRFS error (device dm-2): root entry 256, num_refs 18446744073709551615
BTRFS error (device dm-2): root entry 273, num_refs 18446744073709551615
BTRFS error (device dm-2): Ref action 3, root 273, ref_root 273, parent 0, owner 0, offset 0, num_refs 1
btrfs_force_cow_block+0x129/0x7d0 [btrfs]
btrfs_cow_block+0x10a/0x250 [btrfs]
btrfs_search_slot+0x5eb/0xf40 [btrfs]
btrfs_insert_empty_items+0x3a/0x70 [btrfs]
insert_with_overflow+0x53/0x130 [btrfs]
btrfs_insert_dir_item+0x125/0x290 [btrfs]
btrfs_add_link+0xaa/0x410 [btrfs]
btrfs_rename+0x5ea/0xcd0 [btrfs]
btrfs_rename2+0x28/0x60 [btrfs]
vfs_rename+0x5b2/0xe10
filename_renameat2+0x244/0x430
__x64_sys_rename+0x48/0x70
do_syscall_64+0xe1/0x790
entry_SYSCALL_64_after_hwframe+0x4b/0x53
BTRFS error (device dm-2): Ref action 2, root 273, ref_root 273, parent 0, owner 0, offset 0, num_refs 18446744073709551615
btrfs_force_cow_block+0x327/0x7d0 [btrfs]
btrfs_cow_block+0x10a/0x250 [btrfs]
btrfs_search_slot+0x5eb/0xf40 [btrfs]
btrfs_lookup_file_extent+0x4d/0x70 [btrfs]
btrfs_drop_extents+0x151/0xf00 [btrfs]
insert_reserved_file_extent+0xfe/0x3e0 [btrfs]
btrfs_finish_one_ordered+0x549/0xc40 [btrfs]
btrfs_work_helper+0xde/0x350 [btrfs]
process_one_work+0x198/0x380
worker_thread+0x1c8/0x330
kthread+0xee/0x120
ret_from_fork+0x28f/0x310
ret_from_fork_asm+0x11/0x20
BTRFS error (device dm-2): Ref action 1, root 273, ref_root 0, parent 4594335744, owner 0, offset 0, num_refs 1
__btrfs_mod_ref+0x1c5/0x2d0 [btrfs]
btrfs_copy_root+0x262/0x390 [btrfs]
create_reloc_root+0xb9/0x370 [btrfs]
btrfs_init_reloc_root+0xb0/0x1b0 [btrfs]
record_root_in_trans+0xa6/0xd0 [btrfs]
create_pending_snapshot+0x383/0xf00 [btrfs]
create_pending_snapshots+0x9b/0xc0 [btrfs]
btrfs_commit_transaction+0x280/0xeb0 [btrfs]
prepare_to_relocate+0x147/0x200 [btrfs]
relocate_block_group+0x6b/0x5e0 [btrfs]
btrfs_relocate_block_group+0x92c/0x2380 [btrfs]
btrfs_relocate_chunk+0x3f/0x1a0 [btrfs]
btrfs_balance+0xa2c/0x19c0 [btrfs]
btrfs_ioctl+0x2839/0x2d30 [btrfs]
__x64_sys_ioctl+0x416/0x9a0
do_syscall_64+0xe1/0x790The above shows the direct cause, Ref action 3 is the oldest operation, which shows the tree block is created by COW. Then ref action 2 shows it's COWed away, by a metadata update, meaning the tree block is already released, should not be referred any more.
Then the final one, is trying to create a reloc tree for subvolume 273, and that reloc root creation is referring to the already dropped tree block.
The root cause is that, during qgroup_account_snapshot(), we are calling record_root_in_trans() with "force = true".
So if the root has no reloc root, we will create one, but at that timing it's already too late.
Normally reloc root should be created before the commit and current roots diverge, to avoid the same problem we are hitting.
But during relocation initialization, we are committing the current running transaction, with a new reloc_control attached halfway.
And if qgroup is enabled, the record_root_in_trans() with "force = true" calls will force reloc root creation even if we do not and should not create reloc root at that timing.
[FIX]
Do not force reloc root creation during record_root_in_trans() with "force = true" cases, which is only called by qgroup_account_snapshot().
If we're really under relocation, the reloc root should be created way early, before the commit and current root diverge. If the root has no reloc tree yet, it means we're still initializing the reloc, and do not need a reloc root.
So skipping the reloc tree creation in qgroup_account_snapshot() should be safe.
--- fs/btrfs/transaction.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
fs/btrfs/transaction.c +12 -1
@@ -458,8 +458,19 @@ static int record_root_in_trans(struct btrfs_trans_handle *trans,* through btrfs_record_root_in_trans without having to take the * lock. smp_wmb() makes sure that all the writes above are * done before we pop in the zero below +* +* If @force is true, it means the call is from +* qgroup_account_snapshot(), which only requires radix tree +* tracking. +* We should not force reloc root creation here, as the root +* may have already been modified, and in that case +* root->commit_root has already been dropped. +* +* Using that commit root will cause the reloc root to refer +* to a deleted extent, causing extent tree corruption. */ - ret =btrfs_init_reloc_root (trans, root); +if (!force) + ret =btrfs_init_reloc_root (trans, root);smp_mb__before_atomic ();clear_bit (BTRFS_ROOT_IN_TRANS_SETUP , &root->state); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 198/733 btrfs: zstd: fix lost wakeup when waiting for a workspace
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: FAN YE <[email protected]>
[ Upstream commit 2acb9f3d1cc8f65dc81ed55e238cbf8e5b60bff7 ]
A writer can sleep forever in zstd_get_workspace() even though a workspace is free. When zstd_alloc_workspace() fails, the task is queued on zwsm->wait and schedules unconditionally, never re-testing the pool. zstd_put_workspace() publishes the workspace and then calls cond_wake_up(), which only wakes when a sleeper is already visible, so a workspace returned between the failed allocation and prepare_to_wait() wakes nobody. The window is wide: zstd_alloc_workspace() goes through kvmalloc() and may enter reclaim.
Only a max level workspace triggers the wakeup and one is deliberately kept allocated as the fallback every waiter waits for, so once its wakeup is lost the writer stays in TASK_UNINTERRUPTIBLE until some other task happens to return one. Re-check the pool after prepare_to_wait() has published the waiter, and use the workspace if one turned up.
--- fs/btrfs/zstd.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
fs/btrfs/zstd.c +10 -1
@@ -307,8 +307,17 @@ struct list_head *zstd_get_workspace(struct btrfs_fs_info *fs_info, int level)DEFINE_WAIT (wait);prepare_to_wait (&zwsm->wait, &wait,TASK_UNINTERRUPTIBLE ); -schedule (); +/* +* Re-check after being queued: zstd_put_workspace() only wakes +* a queue that already has a sleeper, so a workspace returned +* since the failed allocation woke nobody. +*/ + ws =zstd_find_workspace (fs_info, level); +if (!ws) +schedule ();finish_wait (&zwsm->wait, &wait); +if (ws) +return ws; goto again; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 199/733 drm/amdgpu/userq: fix struct drm_amdgpu_info_device padding for 32bit compile
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yogesh Mohan Marimuthu <[email protected]>
[ Upstream commit 13af55f71399f5e562f6cb59ad413476e513c4d4 ]
need to pad before __u64 tcc_disabled_mask variable.
This patch fixes 64bit Kernel + 32 bit mesa combination. But at the same time it will break 32bit Kernel(using this patch) + older 32bit mesa(not using this patch).
This issue was discussd with [email protected], [email protected] and [email protected]. Currently today 32 bit kernel + 32 bit userspace and 64 bit kernel and 64 bit userspace work. Mixed 64 bit kernel and 32 bit userspace is currently broken. Since 32 bit kernel and userspace is probably pretty rare these days and the data affected by this is not critical, Hence we can go ahead with this patch.
--- include/uapi/drm/amdgpu_drm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
include/uapi/drm/amdgpu_drm.h +1 -1
@@ -1510,6 +1510,7 @@ struct drm_amdgpu_info_device {__u64 high_va_max;/* gfx10 pa_sc_tile_steering_override */ __u32 pa_sc_tile_steering_override; +__u32 pad;/* disabled TCCs */ __u64 tcc_disabled_mask;__u64 min_engine_clock;
@@ -1534,7 +1535,6 @@ struct drm_amdgpu_info_device {__u32 csa_alignment;/* Userq IP mask (1 << AMDGPU_HW_IP_*) */ __u32 userq_ip_mask; -__u32 pad; };struct drm_amdgpu_info_hw_ip {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 200/733 ipvs: fix reversed sequence option serialization
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kyle Zeng <[email protected]>
[ Upstream commit b04578b74f2d3755548fe9e829e3b2a6c6f966a1 ]
hton_seq() expects the host-order source first and the unaligned network-order destination second. The version 1 sync sender passes these arguments in reverse for both sequence blocks. This leaves 24 bytes of the kmalloc-backed message unwritten. It may disclose stale heap data and replace the live connection sequence state with values read from the buffer.
Pass the connection sequence state as the source and the message payload as the destination for both blocks.
--- net/netfilter/ipvs/ip_vs_sync.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/netfilter/ipvs/ip_vs_sync.c +2 -2
@@ -747,9 +747,9 @@ void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts)if (cp->flags &IP_VS_CONN_F_SEQ_MASK ) { *(p++) =IPVS_OPT_SEQ_DATA ; *(p++) =sizeof (struct ip_vs_sync_conn_options ); -hton_seq ((struct ip_vs_seq *)p, &cp->in_seq); +hton_seq (&cp->in_seq, (struct ip_vs_seq *)p); p +=sizeof (struct ip_vs_seq ); -hton_seq ((struct ip_vs_seq *)p, &cp->out_seq); +hton_seq (&cp->out_seq, (struct ip_vs_seq *)p); p +=sizeof (struct ip_vs_seq ); }/* Handle pe data */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 201/733 netfilter: nf_conntrack_sip: fix OOB read in sip_skip_whitespace()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joas Antonio dos Santos <[email protected]>
[ Upstream commit e8f8231824b5815f57ce62cba116e511b10196de ]
sip_skip_whitespace() returns dptr unchanged when its own loop exhausts the buffer (dptr == limit), instead of NULL like its sibling sip_follow_continuation() returns on its own "no more data" path.
ct_sip_get_header() only checks for NULL after calling it:
dptr = sip_skip_whitespace(dptr, limit);
if (dptr == NULL)
break;
if (*dptr != ':' || ++dptr >= limit)
break;so a recognized header name followed only by spaces/tabs running to the exact end of the SIP payload, with no colon, makes the very next statement read one byte past the buffer.
Make both "no more data" outcomes return NULL, matching the convention sip_follow_continuation() already uses and that both existing callers already check for.
--- net/netfilter/nf_conntrack_sip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/netfilter/nf_conntrack_sip.c +1 -1
@@ -429,7 +429,7 @@ static const char *sip_skip_whitespace(const char *dptr, const char *limit) dptr =sip_follow_continuation (dptr, limit);break ; } -return dptr; +return dptr < limit ? dptr :NULL ; }/* Search within a SIP header value, dealing with continuation lines */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 202/733 tracing/probes: Fix use-after-free on field name/type of events with multiple probes
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Henry Martin <[email protected]>
[ Upstream commit 86b7a239ec6b14a7544200ede85474c6f5526049 ]
The fields of a probe-based dynamic event (kprobe, uprobe, eprobe and fprobe events) are created in traceprobe_define_arg_fields() by handing the probe_arg name/type strings to trace_define_field(), which only stores the pointers without copying. Those strings are owned by the trace_probe and are freed when that probe is removed.
An event can have several probes attached. The field list is defined only once, by the first probe that registers the event, but it is kept alive by any surviving sibling probe. Deleting just that first probe by symbol -
# primary A: fields are defined from A's args echo 'p:kprobes/ev vfs_read a1=$arg1' > kprobe_events # append B: shares A's event call echo 'p:kprobes/ev vfs_write a1=$arg1' >> kprobe_events # delete only A (matched by symbol), B survives echo '-:kprobes/ev vfs_read' >> kprobe_events
frees A's args (trace_probe_cleanup() -> traceprobe_free_probe_arg()), but trace_probe_unlink() keeps the trace_probe_event because the probe list is not empty. The event call stays registered via B while its fields now reference freed memory. Any field lookup then reads it, e.g.
echo 'a1 == 1' > events/kprobes/ev/filter
BUG: KASAN: slab-use-after-free in strcmp+0xa7/0xb0 Call Trace: strcmp trace_find_event_field parse_pred process_preds create_filter apply_event_filter event_filter_write
field->name references parg->name (kstrdup'd, freed with the probe) and, for array arguments, field->type references parg->fmt (kmalloc'd, freed with the probe) - the scalar type otherwise points at the static fmttype rodata, which is safe.
Have traceprobe_define_arg_fields() duplicate the name and type strings and anchor the copies on the trace_probe_event, which embeds the event call and outlives every individual probe; trace_probe_event_free() releases them.
The reproducer above triggers reliably; the field lookup and the delete both run under event_mutex, so this is a dangling reference after removal rather than a race.
The issue was found by the autokbug dynamic kernel fuzzer at Tencent Yunding Lab.
--- kernel/trace/trace_probe.c | 48 +++++++++++++++++++++++++++++++++++++- kernel/trace/trace_probe.h | 2 ++ 2 files changed, 49 insertions(+), 1 deletion(-)
kernel/trace/trace_probe.c +47 -1
@@ -2108,19 +2108,60 @@ int traceprobe_set_print_fmt(struct trace_probe *tp, enum probe_print_type ptypeint traceprobe_define_arg_fields (struct trace_event_call *event_call,size_t offset,struct trace_probe *tp) { +struct trace_probe_event *tpe =trace_probe_event_from_call (event_call);int ret, i; +/* +* A field created by trace_define_field() only stores the name and +* type pointers, it does not copy the strings. Here they point into +* the probe_arg of @tp, which is freed when @tp is removed. For an +* event with multiple probes attached, the field list is defined +* once by the first probe but kept alive by the surviving siblings, +* so removing that first probe would leave the fields referencing +* freed memory. Duplicate the strings and anchor the copies on the +* trace_probe_event, which lives as long as the field list itself. +* +* event_define_fields() ignores the return value of this hook, so +* if a previous attempt failed before creating any field, it may +* call here again. Release duplicates left behind by such an +* attempt before starting over. +*/ +for (i =0 ; i < tpe->nr_field_strings; i++) +kfree (tpe->field_strings[i]); +kfree (tpe->field_strings); + tpe->field_strings =NULL ; + tpe->nr_field_strings =0 ; + +if (tp->nr_args) { + tpe->field_strings =kcalloc (tp->nr_args *2 ,sizeof (char *), +GFP_KERNEL ); +if (!tpe->field_strings) +return -ENOMEM ; + } +/* Set argument names as fields */ for (i =0 ; i < tp->nr_args; i++) {struct probe_arg *parg = &tp->args[i];const char *fmt = parg->type->fmttype;int size = parg->type->size; +char *name, *type;if (parg->fmt) fmt = parg->fmt;if (parg->count) size *= parg->count; - ret =trace_define_field (event_call, fmt, parg->name, + + name =kstrdup (parg->name,GFP_KERNEL ); + type =kstrdup (fmt,GFP_KERNEL ); +if (!name || !type) { +kfree (name); +kfree (type); +return -ENOMEM ; + } + tpe->field_strings[tpe->nr_field_strings++] = name; + tpe->field_strings[tpe->nr_field_strings++] = type; + + ret =trace_define_field (event_call, type, name, offset + parg->offset, size, parg->type->is_signed,FILTER_OTHER );
@@ -2132,6 +2173,11 @@ int traceprobe_define_arg_fields(struct trace_event_call *event_call,static void trace_probe_event_free (struct trace_probe_event *tpe) { +int i; + +for (i =0 ; i < tpe->nr_field_strings; i++) +kfree (tpe->field_strings[i]); +kfree (tpe->field_strings);kfree (tpe->class.system);kfree (tpe->call.name);kfree (tpe->call.print_fmt);
kernel/trace/trace_probe.h +2 -0
@@ -255,6 +255,8 @@ struct trace_probe_event {struct trace_event_call call;struct list_head files;struct list_head probes; +char **field_strings; +int nr_field_strings;struct trace_uprobe_filter filter[]; };
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 203/733 bpf: reject BPF_PSEUDO_FUNC reference to the main program
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eduard Zingerman <[email protected]>
[ Upstream commit 374b2c5561db80fcdd7cdce44af37a49416f61c7 ]
fixups.c:jit_subprogs() rewrites BPF_PSEUDO_FUNC loads to contain real function addresses. This function is invoked from bpf_jit_subprogs() only when env->subprog_cnt > 1. Meaning that for any program like below:
int main(void *ctx) {
void *ptr = main;
...
bpf_timer_set_callback(..., ptr);
...
}The 'ptr' won't be ever converted to contain an address.
In combination with e.g. bpf_timer_set_callback() this would lead to a function call at a bogus address.
Instead of complicating the implementation, just assume that no useful program needs main to be a sync or async callback and reject BPF_PSEUDO_FUNC loads for the main subprogram.
--- kernel/bpf/verifier.c | 9 +++++++++ 1 file changed, 9 insertions(+)
kernel/bpf/verifier.c +9 -0
@@ -16291,6 +16291,15 @@ static int check_ld_imm(struct bpf_verifier_env *env, struct bpf_insn *insn)verbose (env,"callback function not static\n" );return -EINVAL ; } +/* +* When env->subprog_cnt == 1 this instruction won't be rewritten +* to hold a real function address. Assume that no usable program +* combines e.g. main and timer callback and just reject here. +*/ +if (subprogno ==0 ) { +verbose (env,"callback function cannot be the main program\n" ); +return -EINVAL ; + } dst_reg->type =PTR_TO_FUNC ; dst_reg->subprogno = subprogno;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 204/733 bonding: do not clear curr_active_slave prematurely when releasing all slaves
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit af602c7aa5fedc9be3043244017aef4f26c96b70 ]
When releasing all slaves during bond destruction (all == true), __bond_release_one() unconditionally clears bond->curr_active_slave to NULL in every iteration.
If a backup slave is released before the active slave, bond_alb_deinit_slave() triggers rlb_teach_disabled_mac_on_primary(), which increments the active slave dev promiscuity counter and sets bond_info->primary_is_promisc = 1.
Because bond->curr_active_slave was prematurely cleared to NULL when releasing the backup slave, the subsequent iteration releasing the active slave evaluates oldcurrent as NULL, so bond_change_active_slave(bond, NULL) is skipped. Consequently, bond_alb_handle_active_change() is never called to decrement the promiscuity counter, permanently leaking promiscuous mode on the physical device after bond teardown.
When oldcurrent == slave, bond_change_active_slave(bond, NULL) already sets bond->curr_active_slave to NULL. We only need to avoid selecting a new active slave when all == true. Replace the if (all) branch with if (!all && oldcurrent == slave).
--- drivers/net/bonding/bond_main.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
drivers/net/bonding/bond_main.c +1 -3
@@ -2517,9 +2517,7 @@ static int __bond_release_one(struct net_device *bond_dev,bond_alb_deinit_slave (bond, slave); } -if (all) { -RCU_INIT_POINTER (bond->curr_active_slave,NULL ); - }else if (oldcurrent == slave) { +if (!all && oldcurrent == slave) {/* Note that we hold RTNL over this sequence, so there * is no concern that another slave add/remove event * will interfere.
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 205/733 net: Remove conflicting altnames for dying netns in __dev_change_net_namespace().
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit debac3a20dec524a59625cf10fa2f18571127824 ]
syzbot reported the warning in cfg80211_pernet_exit(). [0]
The repro does the following:
1. create two device in root netns and non-root netns
2. assign the same altname for the two devices
3. remove the non-root netns
Since commit 7663d522099e ("net: check for altname conflicts when changing netdev's netns"), cfg80211_switch_netns() and cfg802154_switch_netns() fail if init_net has a device with the conflicting altname.
default_device_exit_net() had the same issue and commit d09486a04f5d ("net: fix removing a namespace with conflicting altnames") fixed it.
cfg80211_pernet_exit() and cfg802154_pernet_exit() need the same fix.
Let's generalise the fix by removing conflicting altnames for dying netns in __dev_change_net_namespace().
[0]:cfg80211_switch_netns(rdev, &init_net)
WARNING: net/wireless/core.c:1871 at cfg80211_pernet_exit+0xd5/0x120 net/wireless/core.c:1871, CPU#1: kworker/u8:9/1160 Modules linked in: CPU: 1 UID: 0 PID: 1160 Comm: kworker/u8:9 Not tainted syzkaller #0 PREEMPT(full) Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/24/2026 Workqueue: netns cleanup_net RIP: 0010:cfg80211_pernet_exit+0xd5/0x120 net/wireless/core.c:1871 Code: e8 03 42 80 3c 20 00 74 08 4c 89 f7 e8 b4 ef 0e f7 4d 8b 36 49 81 fe 20 10 4a 90 74 12 e8 03 3d 9f f6 eb 85 e8 fc 3c 9f f6 90 <0f> 0b 90 eb cc e8 f1 3c 9f f6 eb 05 e8 ea 3c 9f f6 5b 41 5c 41 5e RSP: 0018:ffffc900057a78f0 EFLAGS: 00010293 RAX: ffffffff8b287154 RBX: ffff88807ba72780 RCX: ffff8880213e8000 RDX: 0000000000000000 RSI: 00000000ffffffef RDI: 0000000000000000 RBP: 00000000ffffffef R08: ffffffff9024cc67 R09: 0000000000000000 R10: fffff52000af4eb0 R11: fffffbfff204998d R12: dffffc0000000000 R13: ffffffff904a1080 R14: ffff888144ed0008 R15: ffff888144ed0e20 FS: 0000000000000000(0000) GS:ffff888124de6000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00005642de0a8a70 CR3: 000000007a40c000 CR4: 00000000003526f0 Call Trace: <TASK> ops_exit_list net/core/net_namespace.c:200 [inline] ops_undo_list+0x43d/0x8d0 net/core/net_namespace.c:253 cleanup_net+0x572/0x810 net/core/net_namespace.c:706 process_one_work kernel/workqueue.c:3387 [inline] process_scheduled_works+0xc3d/0x1630 kernel/workqueue.c:3470 worker_thread+0xa47/0xfb0 kernel/workqueue.c:3551 kthread+0x38b/0x480 kernel/kthread.c:436 ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 </TASK>
--- net/core/dev.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-)
net/core/dev.c +13 -12
@@ -12541,7 +12541,7 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net, const char *pat, int new_ifindex, struct netlink_ext_ack *extack) { - struct netdev_name_node *name_node; + struct netdev_name_node *name_node, *tmp;struct net *net_old =dev_net (dev);char new_name[IFNAMSIZ ] = {};int err, new_nsid;
@@ -12587,13 +12587,19 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net, }/* Check that none of the altnames conflicts. */ err = -EEXIST ; -netdev_for_each_altname (dev, name_node) { -if (netdev_name_in_use (net, name_node->name)) { -NL_SET_ERR_MSG_FMT (extack, -"An interface with the altname %s exists in the target netns" , - name_node->name); - goto out; +netdev_for_each_altname_safe (dev, name_node, tmp) { +if (!netdev_name_in_use (net, name_node->name)) +continue ; + +if (!check_net (net_old)) { +__netdev_name_node_alt_destroy (name_node); +continue ; } + +NL_SET_ERR_MSG_FMT (extack, +"An interface with the altname %s exists in the target netns" , + name_node->name); + goto out; }/* Check that new_ifindex isn't used yet. */
@@ -13044,7 +13050,6 @@ static struct pernet_operations __net_initdata netdev_net_ops = { static void __net_exit default_device_exit_net(struct net *net) { -struct netdev_name_node *name_node, *tmp;struct net_device *dev, *aux;/* * Push all migratable network devices back to the
@@ -13068,10 +13073,6 @@ static void __net_exit default_device_exit_net(struct net *net)if (netdev_name_in_use (&init_net, fb_name))snprintf (fb_name,IFNAMSIZ ,"dev%%d" ); -netdev_for_each_altname_safe (dev, name_node, tmp) -if (netdev_name_in_use (&init_net, name_node->name)) -__netdev_name_node_alt_destroy (name_node); - err =dev_change_net_namespace (dev, &init_net, fb_name);if (err) { pr_emerg("%s: failed to move %s to init_net: %d\n",
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 206/733 net: macb: unify device pointer naming convention
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Théo Lebrun <[email protected]>
[ Upstream commit 07362f68e61d82ee53da0e9ae2c7f981c3538861 ]
Here are all device pointer variable permutations inside MACB:
struct device *dev;
struct net_device *dev;
struct net_device *ndev;
struct net_device *netdev;
struct pci_dev *pdev; // inside macb_pci.c
struct phy_device *phy;
struct phy_device *phydev;
struct platform_device *pdev;
struct platform_device *plat_dev; // inside macb_pci.c
Unify to this convention:
struct device *dev;
struct net_device *netdev;
struct pci_dev *pci;
struct phy_device *phydev;
struct platform_device *pdev;
Ensure nothing slipped through using ctags tooling:
⟩ ctags -o - --kinds-c='{local}{member}{parameter}' \
--fields='{typeref}' drivers/net/ethernet/cadence/* | \
awk -F"\t" '
$NF~/struct:.*(device|dev) / {print $NF, $1}' | \ sort -u
typeref:struct:device * dev
typeref:struct:in_device * idev // ignored
typeref:struct:net_device * netdev
typeref:struct:pci_dev * pci
typeref:struct:phy_device * phydev
typeref:struct:platform_device * pdev
Also fix some printk() calls to use __func__ instead of hardcoding. This silences some checkpatch.pl warnings and doesn't deserve a separate commit.
Stable-dep-of: d85f521a9afb ("net: macb: exclude software FCS from TX byte statistics")
--- drivers/net/ethernet/cadence/macb.h | 20 +- drivers/net/ethernet/cadence/macb_main.c | 642 ++++++++++++----------- drivers/net/ethernet/cadence/macb_pci.c | 46 +- drivers/net/ethernet/cadence/macb_ptp.c | 18 +- 4 files changed, 365 insertions(+), 361 deletions(-)
drivers/net/ethernet/cadence/macb.h +10 -10
@@ -1207,11 +1207,11 @@ struct macb_or_gem_ops {/* MACB-PTP interface: adapt to platform needs. */ struct macb_ptp_info { -void (*ptp_init)(struct net_device *ndev); -void (*ptp_remove)(struct net_device *ndev); +void (*ptp_init)(struct net_device *netdev); +void (*ptp_remove)(struct net_device *netdev);s32 (*get_ptp_max_adj)(void );unsigned int (*get_tsu_rate)(struct macb *bp); -int (*get_ts_info)(struct net_device *dev, +int (*get_ts_info)(struct net_device *netdev,struct kernel_ethtool_ts_info *info);int (*get_hwtst)(struct net_device *netdev,struct kernel_hwtstamp_config *tstamp_config);
@@ -1326,7 +1326,7 @@ struct macb {struct clk *tx_clk;struct clk *rx_clk;struct clk *tsu_clk; -struct net_device *dev; +struct net_device *netdev;/* Protects hw_stats and ethtool_stats */ spinlock_t stats_lock;union {
@@ -1406,8 +1406,8 @@ enum macb_bd_control { TSTAMP_ALL_FRAMES, }; -void gem_ptp_init (struct net_device *ndev); -void gem_ptp_remove (struct net_device *ndev); +void gem_ptp_init (struct net_device *netdev); +void gem_ptp_remove (struct net_device *netdev);void gem_ptp_txstamp (struct macb *bp,struct sk_buff *skb,struct macb_dma_desc *desc);void gem_ptp_rxstamp (struct macb *bp,struct sk_buff *skb,struct macb_dma_desc *desc);static inline void gem_ptp_do_txstamp (struct macb *bp,struct sk_buff *skb,struct macb_dma_desc *desc)
@@ -1426,14 +1426,14 @@ static inline void gem_ptp_do_rxstamp(struct macb *bp, struct sk_buff *skb, stru gem_ptp_rxstamp(bp, skb, desc); } -int gem_get_hwtst (struct net_device *dev, +int gem_get_hwtst (struct net_device *netdev,struct kernel_hwtstamp_config *tstamp_config); -int gem_set_hwtst (struct net_device *dev, +int gem_set_hwtst (struct net_device *netdev,struct kernel_hwtstamp_config *tstamp_config,struct netlink_ext_ack *extack);#else -static inline void gem_ptp_init (struct net_device *ndev) { } -static inline void gem_ptp_remove (struct net_device *ndev) { } +static inline void gem_ptp_init (struct net_device *netdev) { } +static inline void gem_ptp_remove (struct net_device *netdev) { }static inline void gem_ptp_do_txstamp (struct macb *bp,struct sk_buff *skb,struct macb_dma_desc *desc) { }static inline void gem_ptp_do_rxstamp (struct macb *bp,struct sk_buff *skb,struct macb_dma_desc *desc) { }
drivers/net/ethernet/cadence/macb_main.c +323 -319
1,880 lines not on this page · raw message
drivers/net/ethernet/cadence/macb_pci.c +23 -23
109 lines not on this page · raw message
drivers/net/ethernet/cadence/macb_ptp.c +9 -9
@@ -324,9 +324,9 @@ void gem_ptp_txstamp(struct macb *bp, struct sk_buff *skb,skb_tstamp_tx (skb, &shhwtstamps); } -void gem_ptp_init (struct net_device *dev) +void gem_ptp_init (struct net_device *netdev) { -struct macb *bp =netdev_priv (dev); +struct macb *bp =netdev_priv (netdev); bp->ptp_clock_info = gem_ptp_caps_template;
@@ -334,7 +334,7 @@ void gem_ptp_init(struct net_device *dev) bp->tsu_rate = bp->ptp_info->get_tsu_rate (bp); bp->ptp_clock_info.max_adj = bp->ptp_info->get_ptp_max_adj ();gem_ptp_init_timer (bp); - bp->ptp_clock =ptp_clock_register (&bp->ptp_clock_info, &dev->dev); + bp->ptp_clock =ptp_clock_register (&bp->ptp_clock_info, &netdev->dev);if (IS_ERR (bp->ptp_clock)) {pr_err ("ptp clock register failed: %ld\n" ,PTR_ERR (bp->ptp_clock));
@@ -353,9 +353,9 @@ void gem_ptp_init(struct net_device *dev) GEM_PTP_TIMER_NAME); } -void gem_ptp_remove (struct net_device *ndev) +void gem_ptp_remove (struct net_device *netdev) { -struct macb *bp =netdev_priv (ndev); +struct macb *bp =netdev_priv (netdev);if (bp->ptp_clock) {ptp_clock_unregister (bp->ptp_clock);
@@ -378,10 +378,10 @@ static int gem_ptp_set_ts_mode(struct macb *bp,return 0 ; } -int gem_get_hwtst (struct net_device *dev, +int gem_get_hwtst (struct net_device *netdev,struct kernel_hwtstamp_config *tstamp_config) { -struct macb *bp =netdev_priv (dev); +struct macb *bp =netdev_priv (netdev); *tstamp_config = bp->tstamp_config;if (!macb_dma_ptp (bp))
@@ -402,13 +402,13 @@ static void gem_ptp_set_one_step_sync(struct macb *bp, u8 enable)macb_writel (bp,NCR , reg_val & ~MACB_BIT (OSSMODE )); } -int gem_set_hwtst (struct net_device *dev, +int gem_set_hwtst (struct net_device *netdev,struct kernel_hwtstamp_config *tstamp_config,struct netlink_ext_ack *extack) {enum macb_bd_control tx_bd_control =TSTAMP_DISABLED ;enum macb_bd_control rx_bd_control =TSTAMP_DISABLED ; -struct macb *bp =netdev_priv (dev); +struct macb *bp =netdev_priv (netdev);u32 regval;if (!macb_dma_ptp (bp))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 207/733 net: macb: exclude software FCS from TX byte statistics
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolai Buchwitz <[email protected]>
[ Upstream commit d85f521a9afb786b1d95bbcb218d3afdf3fe73ab ]
Frames for which macb_pad_and_fcs() supplies the FCS have four FCS bytes appended, and TX completion then accounts the grown skb->len. tx_bytes is defined to exclude the FCS, so these frames are reported four bytes too large.
Track only the number of FCS bytes appended in software, 0 or ETH_FCS_LEN, and subtract that from skb->len at completion. skb->len already reflects the padded length by then, so there is nothing else to store. macb_pad_and_fcs() already returns 0 on every non-error path. Return the FCS length from there instead, rather than recomputing the same check in the caller. BQL stays on the padded skb->len that netdev_tx_sent_queue() saw.
--- drivers/net/ethernet/cadence/macb.h | 3 +++ drivers/net/ethernet/cadence/macb_main.c | 21 +++++++++++++-------- 2 files changed, 16 insertions(+), 8 deletions(-)
drivers/net/ethernet/cadence/macb.h +3 -0
@@ -974,6 +974,8 @@ struct macb_dma_desc_ptp {* of the frame * @mapping: DMA address of the skb's fragment buffer * @size: size of the DMA mapped buffer +* @fcs_len: FCS bytes appended in software, 0 or ETH_FCS_LEN, only +* set for the last buffer of the frame * @mapped_as_page: true when buffer was mapped with skb_frag_dma_map(), * false when buffer was mapped with dma_map_single() */
@@ -981,6 +983,7 @@ struct macb_tx_skb {struct sk_buff *skb;dma_addr_t mapping;size_t size; +u8 fcs_len;bool mapped_as_page; };
drivers/net/ethernet/cadence/macb_main.c +13 -8
@@ -1323,8 +1323,8 @@ static void macb_tx_error_task(struct work_struct *work) bp->netdev->stats.tx_packets++; queue->stats.tx_packets++; packets++; - bp->netdev->stats.tx_bytes += skb->len; - queue->stats.tx_bytes += skb->len; + bp->netdev->stats.tx_bytes += skb->len - tx_skb->fcs_len; + queue->stats.tx_bytes += skb->len - tx_skb->fcs_len; bytes += skb->len; } }else {
@@ -1451,8 +1451,8 @@ static int macb_tx_complete(struct macb_queue *queue, int budget) skb->data); bp->netdev->stats.tx_packets++; queue->stats.tx_packets++; - bp->netdev->stats.tx_bytes += skb->len; - queue->stats.tx_bytes += skb->len; + bp->netdev->stats.tx_bytes += skb->len - tx_skb->fcs_len; + queue->stats.tx_bytes += skb->len - tx_skb->fcs_len; packets++; bytes += skb->len; }
@@ -2200,7 +2200,8 @@ static void macb_poll_controller(struct net_device *netdev)static unsigned int macb_tx_map (struct macb *bp,struct macb_queue *queue,struct sk_buff *skb, -unsigned int hdrlen) +unsigned int hdrlen, +u8 fcs_len) {unsigned int f, nr_frags =skb_shinfo (skb)->nr_frags;unsigned int len, i, tx_head = queue->tx_head;
@@ -2285,6 +2286,7 @@ static unsigned int macb_tx_map(struct macb *bp,/* This is the last buffer of the frame: save socket buffer */ tx_skb->skb = skb; + tx_skb->fcs_len = fcs_len;/* Update TX ring: update buffer descriptors in reverse order * to avoid race condition
@@ -2418,6 +2420,7 @@ static inline int macb_clear_csum(struct sk_buff *skb)return 0 ; } +/* Returns a negative errno, or the FCS bytes appended (0 or ETH_FCS_LEN). */ static int macb_pad_and_fcs (struct sk_buff **skb,struct net_device *netdev) { bool cloned = skb_cloned(*skb) || skb_header_cloned(*skb) ||
@@ -2466,7 +2469,7 @@ static int macb_pad_and_fcs(struct sk_buff **skb, struct net_device *netdev)skb_put_u8 (*skb, (fcs >>16 ) &0xff );skb_put_u8 (*skb, (fcs >>24 ) &0xff ); -return 0 ; +return ETH_FCS_LEN ; } static netdev_tx_t macb_start_xmit(struct sk_buff *skb,
@@ -2478,6 +2481,7 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb,unsigned int desc_cnt, nr_frags, frag_size, f;unsigned int hdrlen;unsigned long flags; +int fcs_len;bool is_lso;netdev_tx_t ret =NETDEV_TX_OK ;
@@ -2486,7 +2490,8 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb,return ret; } -if (macb_pad_and_fcs (&skb, netdev)) { + fcs_len =macb_pad_and_fcs (&skb, netdev); +if (fcs_len <0 ) {dev_kfree_skb_any (skb);return ret; }
@@ -2549,7 +2554,7 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb, }/* Map socket buffer for DMA transfer */ -if (macb_tx_map (bp, queue, skb, hdrlen)) { +if (macb_tx_map (bp, queue, skb, hdrlen, fcs_len)) {dev_kfree_skb_any (skb); goto unlock; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 208/733 net/rds: use wq_has_sleeper() in release_in_xmit()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Allison Henderson <[email protected]>
[ Upstream commit 6d0c8b7073913011459cf968cbbadd341e166bc3 ]
release_in_xmit() clears RDS_IN_XMIT with clear_bit_unlock() and then checks waitqueue_active() to decide whether anyone needs waking. clear_bit_unlock() is only a release operation: it orders the critical section before the bit clear, but does not order the subsequent plain load of the wait queue head after it. The waiter side does the mirror image - it adds itself to the wait queue and then tests the bit. That is the classic store-buffering pattern: the releasing CPU can read the wait queue as empty while the waiting CPU still reads the bit as set, so the sleeper is never woken.
The waiters are rds_conn_shutdown() and rds_tcp_reset_callbacks(), both in uninterruptible wait_event() with no timeout. A lost wake-up strands the shutdown worker on its single-threaded workqueue until some other sender releases the bit again - and on a connection that is being torn down precisely because it failed, there may never be another sender.
The barrier used to be there: release_in_xmit() did clear_bit() followed by smp_mb__after_atomic() until commit 1422f28826d2 ("rds:
introduce acquire/release ordering in acquire/release_in_xmit()") folded both into clear_bit_unlock(), which strengthened the lock hand-off but silently dropped the full barrier the wake-up check depends on. The refill counterpart, release_refill() in net/rds/ib_recv.c, still carries its smp_mb__after_atomic() for exactly this reason.
Use wq_has_sleeper(), which is waitqueue_active() preceded by the required full barrier.
--- net/rds/send.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
net/rds/send.c +6 -1
@@ -114,8 +114,13 @@ static void release_in_xmit(struct rds_conn_path *cp)* hot path and finding waiters is very rare. We don't want to walk * the system-wide hashed waitqueue buckets in the fast path only to * almost never find waiters. +* +* wq_has_sleeper() supplies the full barrier that orders the wait +* queue read after the bit clear; clear_bit_unlock() alone is only +* a release and would let this check read a stale empty queue, +* losing the wake-up. */ -if (waitqueue_active (&cp->cp_waitq)) +if (wq_has_sleeper (&cp->cp_waitq))wake_up_all (&cp->cp_waitq); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 209/733 net/rds: use clear_bit_unlock() in release_refill()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Allison Henderson <[email protected]>
[ Upstream commit 17c4476dbb9c3bfd34193a6c22f2c3da8747134a ]
release_refill() drops the RDS_RECV_REFILL bit with a plain clear_bit(). clear_bit() has no ordering semantics, and the smp_mb__after_atomic() that follows it sits on the wrong side for a lock release: it orders the clear against the waitqueue_active() load below it, but does nothing to order the refill critical section's ring and descriptor stores before the clear itself.
That matters once connection teardown owns RDS_RECV_REFILL as a lock across the transport shutdown and path reset, rather than sampling it clear, which "net/rds: acquire the fastpath locks in rds_conn_shutdown()" later in this series arranges: on a weakly ordered architecture the teardown can win the bit and start the shutdown and reset while some of the refill's stores are not yet visible to it. The same gap existed under the sample-based scheme - a waiter that saw the bit clear had no guarantee it also observed the refill's stores - but taking the bit as a lock makes the missing release pairing load-bearing.
Switch to clear_bit_unlock(), which orders the critical section before the release, and replace the open-coded barrier-plus-waitqueue_active() with wq_has_sleeper(), whose internal full barrier keeps the store-buffering guarantee between clearing the bit and checking for sleepers. This mirrors what "net/rds: use wq_has_sleeper() in release_in_xmit()" does for RDS_IN_XMIT.
The fast-path acquire side, acquire_refill(), uses test_and_set_bit(), a full-barrier RMW that pairs with this release. The teardown at this point in the series still samples the bit, so on its own this change is release-side hardening; the shutdown-conversion patch named above makes the teardown acquire the bit with the same RMW, completing the pairing at the end of the series.
--- net/rds/ib_recv.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
net/rds/ib_recv.c +2 -3
@@ -363,15 +363,14 @@ static int acquire_refill(struct rds_connection *conn)static void release_refill (struct rds_connection *conn) { -clear_bit (RDS_RECV_REFILL , &conn->c_flags); -smp_mb__after_atomic (); +clear_bit_unlock (RDS_RECV_REFILL , &conn->c_flags);/* We don't use wait_on_bit()/wake_up_bit() because our waking is in a * hot path and finding waiters is very rare. We don't want to walk * the system-wide hashed waitqueue buckets in the fast path only to * almost never find waiters. */ -if (waitqueue_active (&conn->c_waitq)) +if (wq_has_sleeper (&conn->c_waitq))wake_up_all (&conn->c_waitq); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 210/733 net/rds: clear cp_flags bits individually in rds_conn_path_reset()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Allison Henderson <[email protected]>
[ Upstream commit 103c4b13c4f50322910078d1c02f29334a574122 ]
rds_conn_path_reset() wipes the whole flag word with a plain cp->cp_flags = 0 store. Every other accessor of that word uses atomic bitops, and some of them can run concurrently with the reset:RDS_LL_SEND_FULL is set from rds_send_xmit() and cleared from the transport completion paths, neither of which holds anything that excludes the shutdown worker. A plain store racing an atomic read-modify-write on the same word is a data race, and whichever side loses has its update silently discarded.
Clear the two bits the reset is actually responsible for instead. RDS_IN_XMIT and RDS_RECV_REFILL need no store at all here: they belong to the caller, rds_conn_shutdown(), which waits for both to be clear before calling the transport shutdown and this reset.
This also gives every bit in cp_flags a single well-defined writer discipline, which the following patches rely on when they turn RDS_IN_XMIT and RDS_RECV_REFILL into bit locks held across the
teardown: a blanket store mid-teardown would destroy lock ownership that an atomic clear preserves.
Oracle UEK carries the same conversion ("net/rds: Preserve essential connection state flags"), motivated by its asynchronous shutdown state machine, whose progress and destroy flags must survive the reset. UEK's variant also clears RDS_IN_XMIT and RDS_RECV_REFILL because there the reset runs as the final step of a teardown that owns both bits, making those clears its unlock. Upstream that release belongs in rds_conn_shutdown(): once a later patch in this series turns the two bits into locks held across the teardown, ending ownership needs release semantics and a wake-up that a plain clear inside the reset would not provide.
Based on Oracle UEK commit "net/rds: Preserve essential connection state flags" by Gerd Rausch.
--- net/rds/connection.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
net/rds/connection.c +9 -1
@@ -120,7 +120,15 @@ static void rds_conn_path_reset(struct rds_conn_path *cp)rds_stats_inc (s_conn_reset);rds_send_path_reset (cp); - cp->cp_flags =0 ; + +/* Clear the bits the reset is responsible for individually: a +* blanket cp_flags = 0 is a plain store that can clobber a +* concurrent atomic read-modify-write on the same word. +* RDS_IN_XMIT and RDS_RECV_REFILL belong to the caller, +* rds_conn_shutdown(), and are left alone here. +*/ +clear_bit (RDS_LL_SEND_FULL , &cp->cp_flags); +clear_bit (RDS_RECONNECT_PENDING , &cp->cp_flags);/* Do not clear next_rx_seq here, else we cannot distinguish * retransmitted packets from new packets, and will hand all
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 211/733 net/rds: tcp: dont force RDS_CONN_RESETTING over a concurrent shutdown
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gerd Rausch <[email protected]>
[ Upstream commit e8e60d74fec49ccae2aea9b04a6eb162feb8d9af ]
rds_tcp_reset_callbacks() resolves a duelling SYN by storing RDS_CONN_RESETTING into cp_state unconditionally. Nothing serializes that store against the shutdown path: rds_tcp_accept_one() checks for RDS_CONN_CONNECTING or RDS_CONN_ERROR under t_conn_path_lock, but neither rds_conn_path_drop(), which forces RDS_CONN_ERROR, nor rds_conn_shutdown(), which moves the path to RDS_CONN_DISCONNECTING under cp_cm_lock, takes that lock. The store can therefore land on top of a shutdown that is already in progress, or that gets queued right after the accept-side check.
When it does, the shutdown worker's final DISCONNECTING -> DOWN transition fails and the path goes through rds_conn_path_error() and a second drop/shutdown cycle instead of a clean reconnect, tearing down the socket the accept path has just installed. Before commit ad22d24be635 ("net/rds: No shortcut out of RDS_CONN_ERROR") a path found in RDS_CONN_RESETTING even made rds_conn_shutdown() bail out altogether.
Make the transition conditional: move CONNECTING -> RESETTING (or stay in RESETTING from an earlier duel), and drop the path in any other state. The drop has side effects of its own: it replaces the shutdown's RDS_CONN_DISCONNECTING (or RDS_CONN_ERROR) with RDS_CONN_ERROR and queues one more cp_down_w run. The difference is that rds_conn_shutdown() accepts RDS_CONN_ERROR in its final transition to RDS_CONN_DOWN, so the shutdown in flight completes normally instead of through rds_conn_path_error(); the extra down-work pass then finds the path already down and falls through to the reconnect check, or catches a reconnect that has already started and restarts it. The accept path still installs the new socket, rds_connect_path_complete() then fails its RESETTING -> UP transition and drops it: the raced socket ends up torn down as it does today. The comment at that call site, which promised that rds_connect_path_complete() marks the path RDS_CONN_UP, is updated to name this outcome as well.
The state can change again between the failed transitions and the drop. That is inherent to rds_conn_path_drop(), which the socket state-change callbacks also call unconditionally, and costs at most one extra drop/reconnect cycle.
Based on Oracle UEK commit "net/rds: Don't force state RDS_CONN_RESETTING" by Gerd Rausch.
[achender: port to net-next: use the two-argument
rds_conn_path_transition()/rds_conn_path_drop() and rewrite the changelog for the upstream shutdown path]
--- net/rds/tcp.c | 17 +++++++++++++++-- net/rds/tcp_listen.c | 6 +++++- 2 files changed, 20 insertions(+), 3 deletions(-)
net/rds/tcp.c +15 -2
@@ -150,9 +150,22 @@ void rds_tcp_reset_callbacks(struct socket *sock,* end up deadlocking with tcp_sendmsg(), and the RDS_IN_XMIT * would not get set. As a result, we set c_state to * RDS_CONN_RESETTTING, to ensure that rds_tcp_state_change -* cannot mark rds_conn_path_up() in the window before lock_sock() +* cannot mark rds_conn_path_up() in the window before lock_sock(). +* +* Only make that transition if the path is still connecting +* (or already resetting from an earlier duel). A path in any +* other state - typically RDS_CONN_DISCONNECTING or +* RDS_CONN_ERROR with a shutdown in flight - is dropped +* instead. That still replaces its state, with RDS_CONN_ERROR, +* and queues one more shutdown pass, but rds_conn_shutdown() +* accepts RDS_CONN_ERROR in its final transition to +* RDS_CONN_DOWN, so the shutdown in flight completes normally. */ -atomic_set (&cp->cp_state,RDS_CONN_RESETTING ); +if (!rds_conn_path_transition (cp,RDS_CONN_CONNECTING , +RDS_CONN_RESETTING ) && + !rds_conn_path_transition (cp,RDS_CONN_RESETTING , +RDS_CONN_RESETTING )) +rds_conn_path_drop (cp,0 );wait_event (cp->cp_waitq, !test_bit (RDS_IN_XMIT , &cp->cp_flags));/* reset receive side state for rds_tcp_data_recv() for osock */ cancel_delayed_work_sync (&cp->cp_send_w);
net/rds/tcp_listen.c +5 -1
@@ -295,7 +295,11 @@ int rds_tcp_accept_one(struct rds_tcp_net *rtn)if (rs_tcp->t_sock) {/* Duelling SYN has been handled in rds_tcp_accept_one() */ rds_tcp_reset_callbacks (new_sock, cp); -/* rds_connect_path_complete() marks RDS_CONN_UP */ +/* rds_connect_path_complete() marks RDS_CONN_UP, or, +* if a concurrent shutdown won the duel, drops the +* path again and the pass that drop queues reaps the +* socket installed above. +*/ rds_connect_path_complete (cp,RDS_CONN_RESETTING ); }else {rds_tcp_set_callbacks (new_sock, cp);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 212/733 net/rds: acquire RDS_IN_XMIT in rds_tcp_reset_callbacks()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Allison Henderson <[email protected]>
[ Upstream commit 02c5f9dc2efd823e061954d564ce00bacd1bebeb ]
rds_tcp_reset_callbacks() quiesces the transmit path by setting the path state to RDS_CONN_RESETTING and then waiting for RDS_IN_XMIT to be sampled clear before swapping the underlying socket and calling rds_send_path_reset().
Sampling the bit clear is not the same as owning it: rds_send_xmit() can re-acquire RDS_IN_XMIT right after the wait_event() returns. Its state recheck after taking the lock is a store-buffering pattern (the resetter writes the state and reads the bit, the sender writes the bit and reads the state) and acquire_in_xmit() is only an acquire operation, so on weakly ordered architectures both sides can miss each other's write and the transmit path then runs concurrently with rds_send_path_reset() rewriting cp_xmit_* state - which is exactly what the comment above rds_send_path_reset() tells its callers to prevent.
Take the lock instead, hold it across the socket swap and rds_send_path_reset(), and release it with a wake-up at the end. The lock-ordering constraint documented above the wait still holds: the lock is acquired before lock_sock(), so a sender inside tcp_sendmsg() can never be waited on while we hold the socket lock.
Two details of the old code go away with the same change:
- t_sock is now read only after the lock is acquired. The old code cached it before waiting; the teardown in rds_conn_shutdown() releases that socket and clears t_sock, so a pointer cached before the wait can be stale by the time the accept path resumes. Reading it under RDS_IN_XMIT is what makes the exclusion complete once the teardown owns the same lock, which the next patch arranges; until then the teardown still only samples the bit, and the two paths remain as exposed to each other as they are today.
- The old !osock early path called rds_send_path_reset() with no serialization at all. It now runs under the lock like the normal path. The conditional RDS_CONN_RESETTING transition of the previous patch happens before the socket check either way: a path found without a socket is either still connecting (its reconnect worker blocked on t_conn_path_lock) and legitimately goes RESETTING -> UP on the new socket, or it has been torn down meanwhile and is dropped.
The in-function comment describing the old wait-based quiesce is rewritten to describe the lock-based one, and the stale block comment above the function (which still described a return value and an incomplete list of t_sock writers) is refreshed to name all four writers - the connect, accept, teardown and swap paths - and what serializes each of them.
--- net/rds/tcp.c | 70 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 25 deletions(-)
net/rds/tcp.c +45 -25
@@ -115,42 +115,48 @@ void rds_tcp_restore_callbacks(struct socket *sock, }/* -* rds_tcp_reset_callbacks() switches the to the new sock and -* returns the existing tc->t_sock. +* rds_tcp_reset_callbacks() switches a path to a new socket and +* releases the old one it finds in tc->t_sock, resolving a duelling +* SYN. * -* The only functions that set tc->t_sock are rds_tcp_set_callbacks -* and rds_tcp_reset_callbacks. Send and receive trust that -* it is set. The absence of RDS_CONN_UP bit protects those paths -* from being called while it isn't set. +* tc->t_sock is set by rds_tcp_set_callbacks() and cleared by +* rds_tcp_restore_callbacks(). Four paths write it: the active +* connect in rds_tcp_conn_path_connect(), which sets it and clears it +* again on failure; the accept path in rds_tcp_accept_one(), which +* sets it for a path with no socket yet; the teardown in +* rds_tcp_conn_path_shutdown(), which clears it; and the swap done +* here, which does both. The connect and accept paths are serialized +* against each other by t_conn_path_lock. Send and receive trust +* that it is set: the absence of RDS_CONN_UP protects those paths +* from being called while it isn't, and the swap done here runs under +* RDS_IN_XMIT so that it cannot interleave with a sender already +* inside rds_send_xmit(). */ void rds_tcp_reset_callbacks (struct socket *sock,struct rds_conn_path *cp) {struct rds_tcp_connection *tc = cp->cp_transport_data; -struct socket *osock = tc->t_sock; - -if (!osock) - goto newsock; +struct socket *osock;/* Need to resolve a duelling SYN between peers. * We have an outstanding SYN to this peer, which may * potentially have transitioned to the RDS_CONN_UP state, * so we must quiesce any send threads before resetting -* cp_transport_data. We quiesce these threads by setting -* cp_state to something other than RDS_CONN_UP, and then -* waiting for any existing threads in rds_send_xmit to -* complete release_in_xmit(). (Subsequent threads entering -* rds_send_xmit() will bail on !rds_conn_up(). +* cp_transport_data. Setting cp_state to something other +* than RDS_CONN_UP stops new senders, and owning RDS_IN_XMIT +* excludes any thread already inside rds_send_xmit() for the +* whole socket swap and the rds_send_path_reset() below. * -* However an incoming syn-ack at this point would end up -* marking the conn as RDS_CONN_UP, and would again permit -* rds_send_xmi() threads through, so ideally we would -* synchronize on RDS_CONN_UP after lock_sock(), but cannot -* do that: waiting on !RDS_IN_XMIT after lock_sock() may -* end up deadlocking with tcp_sendmsg(), and the RDS_IN_XMIT -* would not get set. As a result, we set c_state to -* RDS_CONN_RESETTTING, to ensure that rds_tcp_state_change -* cannot mark rds_conn_path_up() in the window before lock_sock(). +* An incoming syn-ack at this point would end up marking the +* conn as RDS_CONN_UP, and would again permit rds_send_xmit() +* threads through, so ideally we would synchronize on +* RDS_CONN_UP after lock_sock(), but cannot do that: acquiring +* RDS_IN_XMIT after lock_sock() may end up deadlocking with +* tcp_sendmsg(), which takes the socket lock while holding +* RDS_IN_XMIT. As a result, we set c_state to +* RDS_CONN_RESETTING, to ensure that rds_tcp_state_change +* cannot mark rds_conn_path_up() in the window before +* lock_sock(). * * Only make that transition if the path is still connecting * (or already resetting from an earlier duel). A path in any
@@ -166,7 +172,18 @@ void rds_tcp_reset_callbacks(struct socket *sock, !rds_conn_path_transition(cp, RDS_CONN_RESETTING, RDS_CONN_RESETTING))rds_conn_path_drop (cp,0 ); -wait_event (cp->cp_waitq, !test_bit (RDS_IN_XMIT , &cp->cp_flags)); +wait_event (cp->cp_waitq, + !test_and_set_bit_lock (RDS_IN_XMIT , &cp->cp_flags)); + +/* Read t_sock only while owning RDS_IN_XMIT, never before the +* wait: the teardown in rds_conn_shutdown() releases the old +* socket and clears t_sock, so a pointer sampled earlier can +* be stale by the time we wake up. +*/ + osock = tc->t_sock; +if (!osock) + goto newsock; +/* reset receive side state for rds_tcp_data_recv() for osock */ cancel_delayed_work_sync (&cp->cp_send_w);cancel_delayed_work_sync (&cp->cp_recv_w);
@@ -185,6 +202,9 @@ void rds_tcp_reset_callbacks(struct socket *sock,lock_sock (sock->sk);rds_tcp_set_callbacks (sock, cp);release_sock (sock->sk); + +clear_bit_unlock (RDS_IN_XMIT , &cp->cp_flags); +wake_up_all (&cp->cp_waitq); }/* Add tc to rds_tcp_tc_list and set tc->t_sock. See comments
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 213/733 net/rds: acquire the fastpath locks in rds_conn_shutdown()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Håkon Bugge <[email protected]>
[ Upstream commit 813f3582ac7ae9f60f917937d54660e0952d5f2d ]
rds_conn_shutdown() quiesces the transmit and receive-refill paths by waiting for RDS_IN_XMIT and RDS_RECV_REFILL to be sampled clear, and then runs the transport shutdown and rds_conn_path_reset(). Sampling the bits clear is not the same as owning them: the moment after the wait_event() returns, rds_send_xmit() can re-acquire RDS_IN_XMIT (or rds_ib_recv_refill() can re-acquire RDS_RECV_REFILL) and run concurrently with the teardown.
The sender does recheck the connection state after taking the lock, but that recheck is a classic store-buffering pattern: teardown writes the state and reads the bit while the sender writes the bit and reads the state. acquire_in_xmit() is only an acquire operation, so on weakly ordered architectures both sides can miss each other's write, and the transmit path then runs while the transport zeroes its rings (e.g. rds_ib_ring_init()) and rds_send_path_reset() rewrites the transmit state under it.
Oracle UEK fixed the same class of crashes - a 14-year tail of BUG_ON()s in rds_ib_sub_signaled(), unexpected op-codes and NULL dereferences in rds_ib_send_cqe_handler() during failover testing - by making the teardown path *acquire* the fastpath bit locks instead of testing them ("rds: Make sure transmit path and connection tear-down does not run concurrently"). Ownership of a single word is decided by RMW atomicity, so no cross-variable ordering is needed.
Do the same here: take both locks before calling the transport shutdown, hold them across rds_conn_path_reset(), and release them explicitly with a wake-up afterwards. Both are released with clear_bit_unlock(), so that the ring re-initialization done by the transport shutdown and the transmit state rewritten by rds_send_path_reset() are ordered before either bit is seen clear by the next acquire_in_xmit() or acquire_refill().
The fastpath users of these bits - rds_send_xmit() and rds_ib_recv_refill() - are trylock style and back off while teardown owns the locks, so no new lock dependency is introduced for them. rds_tcp_reset_callbacks() is different: since the previous patch it acquires RDS_IN_XMIT as well, and it blocks doing so, so its wait now spans the teardown instead of at most one send batch. That waiter runs from rds_tcp_accept_one() on the single-threaded krdsd workqueue and holds rds_tcp_accept_lock and t_conn_path_lock while it waits, so a duelling SYN accepted while its path is being torn down parks accept processing for the duration of the teardown - for TCP bounded by the (up to 5 s) drain loop in rds_tcp_conn_path_shutdown(). An IB path's drain in rds_ib_conn_path_shutdown() has no round cap, but no blocking waiter either: rds_tcp_reset_callbacks() is the only blocking acquirer of these bits and waits only on its own TCP path, and the fastpaths are trylock-and-back-off on both transports, so a long IB drain lengthens only that path's own quiesce. The
window is narrow: the accept-side state check has to pass before the teardown moves the path to RDS_CONN_DISCONNECTING.
Because krdsd is a single global workqueue, everything else queued there - accept processing for other connections and network namespaces, and the flush_workqueue(rds_wq) in rds_tcp_listen_stop() during namespace teardown - waits behind the parked accept worker for that time. It cannot deadlock, although the waits do point at each
other: the teardown blocks until the bit's holder releases it, and the holder may be that krdsd accept worker. The holder finishes without needing anything the teardown owns: the sync cancels rds_tcp_reset_callbacks() issues target cp_send_w and cp_recv_w on the path's ordered cp_wq, whose only execution slot is occupied by the blocked cp_down_w itself, so they are pending at most and cancel without flushing - a reliance on cp_wq being ordered that is now noted next to those cancels (on the allocation-failure fallback where a path shares rds_wq, the work items simply serialize). Nor is the blocking wait itself new: rds_tcp_reset_callbacks() has waited on RDS_IN_XMIT from the krdsd work item since commit 335b48d980f6 ("RDS: TCP: Add/use rds_tcp_reset_callbacks to reset tcp socket safely"); this patch stretches its worst case from a sender's batch to the teardown's drain. The alternative to parking is the accept path racing the teardown, which is what these patches close; making the teardown itself non-blocking is a separate item.
One observable side effect: the SENDING flag reported by rds-info has always mirrored RDS_IN_XMIT, so it now also covers the window where teardown owns the bit.
The comments that describe the old sample-based handshake or name rds_send_xmit() as the only other holder of these bits - in rds_send_xmit(), above rds_conn_path_reset(), in rds_ib_recv_refill() and in rds_tcp_reset_callbacks() - are updated to match.
For anyone backporting this patch standalone: it depends on "net/rds: clear cp_flags bits individually in rds_conn_path_reset()" and "net/rds: acquire RDS_IN_XMIT in rds_tcp_reset_callbacks()" earlier in this series. Without the former, the blanket cp_flags clear in rds_conn_path_reset() would drop both held bits in the middle of the teardown; without the latter, rds_tcp_reset_callbacks() would still sample t_sock without owning RDS_IN_XMIT. "net/rds: use clear_bit_unlock() in release_refill()" is needed for the refill side's release to pair with the acquire added here, and the follow-up "net/rds: don't let rds_conn_shutdown() consume a concurrent drop" completes the teardown-state handling for the waiter this patch parks; a backport should carry all four.
[achender: reimplement for net-next shutdown path: acquire the existing
RDS_IN_XMIT/RDS_RECV_REFILL bit locks in rds_conn_shutdown() and release after teardown; update comments and commit message]
--- net/rds/connection.c | 40 ++++++++++++++++++++++++++++++++-------- net/rds/ib_recv.c | 4 +++- net/rds/send.c | 7 +++++-- net/rds/tcp.c | 19 +++++++++++++++---- 4 files changed, 55 insertions(+), 15 deletions(-)
net/rds/connection.c +32 -8
@@ -106,10 +106,12 @@ static struct rds_connection *rds_conn_lookup(struct net *net, }/* -* This is called by transports as they're bringing down a connection. -* It clears partial message state so that the transport can start sending -* and receiving over this connection again in the future. It is up to -* the transport to have serialized this call with its send and recv. +* This is called by rds_conn_shutdown() once the transport has brought +* a path down. It clears partial message state so that the transport +* can start sending and receiving over this path again in the future. +* The caller owns RDS_IN_XMIT and RDS_RECV_REFILL across this call, +* which is what serializes it against the send and receive-refill +* paths. */ static void rds_conn_path_reset (struct rds_conn_path *cp) {
@@ -124,8 +126,9 @@ static void rds_conn_path_reset(struct rds_conn_path *cp)/* Clear the bits the reset is responsible for individually: a * blanket cp_flags = 0 is a plain store that can clobber a * concurrent atomic read-modify-write on the same word. -* RDS_IN_XMIT and RDS_RECV_REFILL belong to the caller, -* rds_conn_shutdown(), and are left alone here. +* RDS_IN_XMIT and RDS_RECV_REFILL are held as locks by the +* caller, rds_conn_shutdown(), which releases them once the +* teardown is complete. */ clear_bit (RDS_LL_SEND_FULL , &cp->cp_flags);clear_bit (RDS_RECONNECT_PENDING , &cp->cp_flags);
@@ -414,14 +417,35 @@ void rds_conn_shutdown(struct rds_conn_path *cp) }mutex_unlock (&cp->cp_cm_lock); +/* Quiesce the transmit and receive-refill paths by +* acquiring their bit locks, not merely waiting for +* them to be released: with a plain wait, either path +* can re-take its lock the instant after we sample it +* clear and then run concurrently with the transport +* shutdown and the path reset below. Holding both +* locks across the teardown makes that structurally +* impossible. +*/ wait_event (cp->cp_waitq, - !test_bit (RDS_IN_XMIT , &cp->cp_flags)); + !test_and_set_bit_lock (RDS_IN_XMIT , &cp->cp_flags));wait_event (cp->cp_waitq, - !test_bit (RDS_RECV_REFILL , &cp->cp_flags)); + !test_and_set_bit (RDS_RECV_REFILL , &cp->cp_flags)); conn->c_trans->conn_path_shutdown (cp);rds_conn_path_reset (cp); +/* Release the two locks and wake any waiter (e.g. +* rds_tcp_reset_callbacks()) that blocked on them while +* we held them. The unlock orders the transport's ring +* re-initialization and the path reset above before +* either bit is seen clear. rds_conn_path_reset() leaves +* both bits alone: ownership ends here, not inside the +* reset. +*/ +clear_bit_unlock (RDS_IN_XMIT , &cp->cp_flags); +clear_bit_unlock (RDS_RECV_REFILL , &cp->cp_flags); +wake_up_all (&cp->cp_waitq); +if (!rds_conn_path_transition (cp,RDS_CONN_DISCONNECTING ,RDS_CONN_DOWN ) && !rds_conn_path_transition(cp, RDS_CONN_ERROR,
net/rds/ib_recv.c +3 -1
@@ -391,7 +391,9 @@ void rds_ib_recv_refill(struct rds_connection *conn, int prefill, gfp_t gfp)/* the goal here is to just make sure that someone, somewhere * is posting buffers. If we can't get the refill lock, -* let them do their thing +* let them do their thing. The holder may also be +* rds_conn_shutdown() tearing the path down, in which case +* there is nothing to post. */ if (!acquire_refill (conn))return ;
net/rds/send.c +5 -2
@@ -236,8 +236,11 @@ int rds_send_xmit(struct rds_conn_path *cp)WRITE_ONCE (cp->cp_send_gen, send_gen);/* -* rds_conn_shutdown() sets the conn state and then tests RDS_IN_XMIT, -* we do the opposite to avoid races. +* rds_conn_shutdown() sets the conn state and then acquires +* RDS_IN_XMIT; we take the lock first and then check the state. +* Ownership is decided by the atomic RMW on the cp_flags word: +* if the teardown won the bit we back off here, and if we won +* it the teardown waits until we release it. */ if (!rds_conn_path_up (cp)) {release_in_xmit (cp);
net/rds/tcp.c +15 -4
@@ -144,8 +144,10 @@ void rds_tcp_reset_callbacks(struct socket *sock,* so we must quiesce any send threads before resetting * cp_transport_data. Setting cp_state to something other * than RDS_CONN_UP stops new senders, and owning RDS_IN_XMIT -* excludes any thread already inside rds_send_xmit() for the -* whole socket swap and the rds_send_path_reset() below. +* excludes any thread already inside rds_send_xmit() - or a +* teardown in rds_conn_shutdown(), which holds the same lock +* for the duration of the transport shutdown - for the whole +* socket swap and the rds_send_path_reset() below. * * An incoming syn-ack at this point would end up marking the * conn as RDS_CONN_UP, and would again permit rds_send_xmit()
@@ -178,13 +180,22 @@ void rds_tcp_reset_callbacks(struct socket *sock,/* Read t_sock only while owning RDS_IN_XMIT, never before the * wait: the teardown in rds_conn_shutdown() releases the old * socket and clears t_sock, so a pointer sampled earlier can -* be stale by the time we wake up. +* be stale by the time we wake up. The teardown holds the +* same lock while it does so, so what we read here cannot +* change under us until we release it. */ osock = tc->t_sock;if (!osock) goto newsock; -/* reset receive side state for rds_tcp_data_recv() for osock */ +/* reset receive side state for rds_tcp_data_recv() for osock. +* +* The sync cancels while owning RDS_IN_XMIT rely on cp_wq +* being ordered: a teardown blocked on the bit occupies +* cp_wq's only execution slot, so cp_send_w and cp_recv_w are +* pending at most and the cancels never flush. Nothing here +* may flush or wait on cp_wq itself. +*/ cancel_delayed_work_sync (&cp->cp_send_w);cancel_delayed_work_sync (&cp->cp_recv_w);lock_sock (osock->sk);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 214/733 net/rds: dont let rds_conn_shutdown() consume a concurrent drop
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Allison Henderson <[email protected]>
[ Upstream commit 260c6308fe2e19ad519389d44d582e292aecc3af ]
rds_conn_shutdown() finishes by moving the path from RDS_CONN_DISCONNECTING to RDS_CONN_DOWN, and also accepts RDS_CONN_ERROR as the starting state of that final transition, so that a FIN processed in softirq context during the teardown does not derail the shutdown into a noisy error path.
But consuming that RDS_CONN_ERROR also consumes the shutdown pass that came with it: rds_conn_path_drop() sets RDS_CONN_ERROR and then queues cp_down_w, and a pass that starts on a path already in RDS_CONN_DOWN is a no-op. For the FIN case that is harmless - the socket the FIN arrived on is the very socket the teardown just released. It is not harmless for a dropper that attached something to the path first.
rds_tcp_accept_one() is such a dropper. Its path claim in rds_tcp_accept_one_path() transitions RDS_CONN_DOWN -> RDS_CONN_CONNECTING, and a concurrent drop - a FIN on a previous socket in softirq context, an administrative reset - can put the path into RDS_CONN_ERROR between that claim and the state check that follows, which accepts RDS_CONN_ERROR. The accept then installs the freshly accepted socket with rds_tcp_set_callbacks() while the queued teardown - which sampled tc->t_sock before this socket existed - is still running. rds_connect_path_complete() fails its transition to RDS_CONN_UP and drops the path again, queueing the pass that should reap the socket it just installed. If the in-flight shutdown's final transition consumes that drop's RDS_CONN_ERROR, the queued pass finds the path in RDS_CONN_DOWN and does nothing. The installed socket is never torn down: it sits established with its callbacks armed and its rds_tcp_connection on rds_tcp_tc_list, the peer sees a connection that nothing ever reads, and the path is wedged in RDS_CONN_DOWN until some later event drops it again. Reproduced with widened race windows as an ever-growing receive queue on a socket owned by a path stuck in RDS_CONN_DOWN, with the peer's send path wedged behind it.
Make the final transition only DISCONNECTING -> DOWN. If it fails because the path is in RDS_CONN_ERROR, a drop raced the teardown:
cancel the reconnect timer and clear RDS_RECONNECT_PENDING - the one piece of the skipped tail that must not be left behind - and return, letting the pass the drop queued finish the job: it tears down whatever attached to the path in the meantime, completes the transition to RDS_CONN_DOWN, and re-arms the reconnect from its own tail.
The timer quiesce in that branch matters because the racing drop does not always queue that pass: rds_conn_path_drop() returns without queueing when a destroy is pending - exactly the situation during a netns teardown or module unload, when a FIN on the dying socket is processed while rds_conn_path_destroy() flushes cp_down_w. If the flushed pass is the one that takes this return, no later pass exists, and rds_conn_path_destroy() would find cp_conn_w still armed (WARN_ON) and then free a path whose reconnect timer can still fire. With the cancel in the branch, every exit of a shutdown pass leaves the timer quiesced no matter which pass completes the transition.
The FIN case keeps making progress, one pass later and still without noisy logging. Any other state keeps today's rds_conn_path_error() handling; no current cp_state writer can leave a DISCONNECTING path in anything but RDS_CONN_ERROR (every other writer is a cmpxchg from a non-DISCONNECTING state), so that branch is defensive.
On kernels without the preceding patches the same hazard exists with the sample-based quiesce; the fix applies there equally.
--- net/rds/connection.c | 43 ++++++++++++++++++++++++++++++++----------- net/rds/tcp.c | 9 ++++++--- 2 files changed, 38 insertions(+), 14 deletions(-)
net/rds/connection.c +32 -11
@@ -447,19 +447,40 @@ void rds_conn_shutdown(struct rds_conn_path *cp)wake_up_all (&cp->cp_waitq);if (!rds_conn_path_transition (cp,RDS_CONN_DISCONNECTING , -RDS_CONN_DOWN ) && - !rds_conn_path_transition (cp,RDS_CONN_ERROR ,RDS_CONN_DOWN )) { -/* This can happen - eg when we're in the middle of tearing -* down the connection, and someone unloads the rds module. -* Quite reproducible with loopback connections. -* Mostly harmless. +/* The path was dropped again while we tore it +* down: by a socket state-change callback in +* irq context on receipt of a FIN, or by an +* accept that claimed the path just before a +* drop put it back to RDS_CONN_ERROR and then +* installed a fresh socket on it. Unless a +* pending destroy suppressed it, the drop also +* queued another shutdown pass, and that pass +* must run, because it is what tears down +* whatever attached to the path after the +* transport shutdown above sampled its state. +* Consuming the RDS_CONN_ERROR here would turn +* that pass into a no-op: leave the state +* alone, and let the pass finish the job. * -* Note that this also happens with rds-tcp because -* we could have triggered rds_conn_path_drop in irq -* mode from rds_tcp_state change on the receipt of -* a FIN, thus we need to recheck for RDS_CONN_ERROR -* here. +* Quiesce the reconnect timer before bailing +* out, though. When a pending destroy did +* suppress the queue, no later pass runs, and +* rds_conn_path_destroy() is about to flush +* cp_down_w and free the path: it must not +* find cp_conn_w still armed. A successor +* pass, when there is one, re-arms the +* reconnect from its own tail. +*/ +cancel_delayed_work_sync (&cp->cp_conn_w); +clear_bit (RDS_RECONNECT_PENDING , &cp->cp_flags); + +if (rds_conn_path_state (cp) ==RDS_CONN_ERROR ) +return ; +/* No current cp_state writer leaves a +* DISCONNECTING path in any state but +* RDS_CONN_ERROR; report loudly if one ever +* does. */ rds_conn_path_error (cp,"%s: failed to transition " "to state DOWN, current state "
net/rds/tcp.c +6 -3
@@ -165,9 +165,12 @@ void rds_tcp_reset_callbacks(struct socket *sock,* other state - typically RDS_CONN_DISCONNECTING or * RDS_CONN_ERROR with a shutdown in flight - is dropped * instead. That still replaces its state, with RDS_CONN_ERROR, -* and queues one more shutdown pass, but rds_conn_shutdown() -* accepts RDS_CONN_ERROR in its final transition to -* RDS_CONN_DOWN, so the shutdown in flight completes normally. +* and, unless a pending destroy is about to reap the whole +* connection anyway, queues one more shutdown pass. A shutdown +* already in flight leaves that RDS_CONN_ERROR alone when it +* finishes; the queued pass then completes the transition to +* RDS_CONN_DOWN and tears down anything that attached to the +* path in the meantime. */ if (!rds_conn_path_transition(cp, RDS_CONN_CONNECTING, RDS_CONN_RESETTING) &&
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 215/733 powerpc: Dont drop _TIF_RESTOREALL on syscall restart
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ritesh Harjani (IBM) <[email protected]>
[ Upstream commit c7585b8e99ad97a0f5dd21e45c90a33aeab0d92b ]
So the syscall return sequence is as follows:
A syscall return to userspace is prepared and then a short asm sequence that actually does the RFI. Note that this asm range is restartable i.e. EE is still on, so an interrupt (e.g. decrementer or external interrupt) can hit while SRR/GPRs are being loaded. This is defined via:
RESTART_TABLE(.Lsyscall_rst_start, .Lsyscall_rst_end, syscall_restart)
This restart table then sends us to syscall_restart rather than resuming in the middle of the RFI. The same stub is also used if irq_happened already has a pending bit (soft-masked irq that has not been replayed yet (PowerPC special case of local_irq_disable())).
Here is a bit of a flow of sequence of code to visualize:
syscall_exit_prepare
decide full-GPR restore (_TIF_RESTOREALL) for signal, rt_sigreturn or syscall trace
save that in regs->exit_result and return it in r3
|
v
.Lsyscall_rst_start .. _end EE still on
irq_happened set or interrupt in this range?
| no | yes
v v
cmpdi r3,0 syscall_exit_restart
restore all / zero replay irq, try exit again
volatiles; RFI must return flags in r3
again for the same cmpdi
Now r3 after prepare is the flags word, not the actual syscall return. A nested interrupt clobbers it, so the restart stub reloads RESULT into r3 and the C handler (syscall_exit_restart()) should put the flags back (because later asm checks whether r3 returned from C has _TIF_RESTOREALL set or not):
cmpdi r3, 0 bne .Lsyscall_restore_regs
Note that syscall_exit_restart() already ORs any new _TIF_RESTOREALL into exit_result, but then it only returns the new sample and not the full regs->exit_result.
That sample could be often 0 even when restore-all is still required:
- rt_sigreturn / syscall trace set the bit in prepare's local ret and in exit_result. They never set exit_flags, which is what restart samples.
- a signal does set exit_flags but restart clears it. A second pass through the stub then returns 0 while exit_result still has the bit.
The asm as mentioned earlier then treats r3==0 as the fast path and zeros r0/r4-r12. That means the userspace that needed the full register set could SIGSEGVs, (which could happen often in ld64.so.2 like while doing a parallel kernel build as reported by Venkat).
So we should instead return the accumulated exit_result, like how we do in interrupt_exit_user_restart(). Note that prior to this commit 263e5159e00a ("powerpc: Fix exit_flags field placement in pt_regs for ptrace") we were returning regs->exit_result from syscall_exit_restart(), but this commit changed that behaviour.
--- arch/powerpc/kernel/interrupt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/powerpc/kernel/interrupt.c +1 -1
@@ -175,7 +175,7 @@ notrace unsigned long syscall_exit_restart(unsigned long r3, struct pt_regs *regcurrent_thread_info ()->exit_flags &= ~_TIF_RESTOREALL; regs->exit_result |= ret; - return ret; + return regs->exit_result; }#endif
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 216/733 powerpc: Do not restore KUAP in arch_exit_to_user_mode_prepare()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ritesh Harjani (IBM) <[email protected]>
[ Upstream commit c2549d749539487239475fbc8c614a1f9244d655 ]
KUAP means kernel cannot touch user memory unless it explicitly is enabled. In the kernel it should stay AMR_KUAP_BLOCKED. While returning to userspace just before RFI, kernel should restore the user AMR value back.
Looks like GENERIC_ENTRY might be treating arch_exit_to_user_mode_prepare() as the last architecture step before returning to userspace. commit bee25f97ad24 ("powerpc: Enable GENERIC_ENTRY feature") therefore called kuap_user_restore() from that hook. But on PowerPC that is too early. After irqentry_exit() / syscall_exit_to_user_mode() we still run platform specific exit routines.
e.g. code snippets showing both exception handling and system call handling as the callers of function arch_exit_to_user_mode_prepare() which does kuap_user_restore(). The below path shows that calling kuap_user_restore() is too early when called from arch_exit_to_user_mode_prepare().
Exception handling in exceptions-64s.S
=======================================
bl CFUNC(do_page_fault)
..DEFINE_INTERRUPT_HANDLER_ASYNC(do_page_fault) arch_interrupt_async_enter_prepare(regs); state = irqentry_enter(regs); instrumentation_begin(); irq_enter_rcu(); handler(regs); nap_adjust_return(regs); irq_exit_rcu(); instrumentation_end(); arch_interrupt_async_exit_prepare(regs); irqentry_exit(regs, state); <<< too early irqentry_exit_to_user_mode() __exit_to_user_mode_prepare(regs, EXIT_TO_USER_MODE_WORK_IRQ); arch_exit_to_user_mode_prepare(regs, ti_work); <<< too early
b interrupt_return_srr
.. bl CFUNC(interrupt_exit_user_prepare) <<< already calls kuap_user_restore
prep_irq_for_enabled_exit() retry can run kernel code with IRQs on. So only when that routine is fully finished is when the user KUAP should be fully restored which interrupt_exit_user_prepare() already takes care of before returning.
Similarly for system call handling in interrupt_64.S
======================================================
bl CFUNC(system_call_exception)
.Lsyscall_exit:
addi r4,r1,STACK_INT_FRAME_REGS li r5,0 /* !scv */ bl CFUNC(syscall_exit_prepare) .. kuap_assert_locked(); syscall_exit_to_user_mode(regs); <<< too early syscall_exit_to_user_mode_prepare(regs); <<< too early
kuap_user_restore(regs); <<< already calls
syscall_exit_prepare(), which can enable IRQs, replay a pending interrupt, and only then rfi. Those functions already restore KUAP immediately before rfi.
Note that if we restore the user AMR too early like in the current code as shown from the code snippets above, then we get the following warning when CONFIG_PPC_KUAP_DEBUG is enabled:
WARNING: arch/powerpc/include/asm/book3s/64/kup.h:293 at interrupt_exit_user_prepare+0x1a0/0x1c0 Hardware name: IBM pSeries (emulated by qemu) POWER10 (architected) TRAP: 0700 LR: c00000000000d8d4 CTR: c0000000021fe500 MSR: <SF,EE,ME,IR,DR,RI,LE> CR: 44000804 XER: 20040000 interrupt_exit_user_prepare+0x1a0/0x1c0 interrupt_return_srr_user+0x8/0x12c
--- arch/powerpc/include/asm/entry-common.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
arch/powerpc/include/asm/entry-common.h +8 -2
@@ -515,8 +515,14 @@ static inline void arch_exit_to_user_mode_prepare(struct pt_regs *regs,#ifdef CONFIG_PPC_TRANSACTIONAL_MEM local_paca->tm_scratch = regs->msr;#endif -/* Restore user access locks last */ -kuap_user_restore (regs); +/* +* Do not restore KUAP here. Generic entry might treat this as the last +* arch step before userspace but PowerPC still has kernel work after +* irqentry_exit()/syscall_exit_to_user_mode() i.e. in +* interrupt_exit_user_prepare() / syscall_exit_prepare() may enable +* IRQs and retry. Those functions restore KUAP immediately before rfi, +* which is where it should belong. +*/ }#define arch_exit_to_user_mode_prepare arch_exit_to_user_mode_prepare
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 217/733 net: airoha: enable RX_DONE interrupt for RX queue 31
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit 7db28abbea0f7dc1ec4fdfdc149db5fbd9e4c994 ]
RX queue 31 has always been allocated and filled by airoha_qdma_init_rx() since RX_DONE_INT_MASK spans queues 0-31, but none of the RX_IRQ* _BANK_PIN_MASK values covered BIT(31). As a consequence the RX_DONE interrupt for queue 31 was never enabled, airoha_qdma_rx_process() never ran on that queue and its buffers were never reaped.
Route RX queue 31's RX_DONE interrupt to IRQ bank 1 so that the queue is drained and its buffers returned to the page pool.
--- drivers/net/ethernet/airoha/airoha_eth.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/airoha/airoha_eth.h +1 -1
@@ -538,7 +538,7 @@ struct airoha_wdma_info {/* RX queue to IRQ mapping: BIT(q) in IRQ(n) */ #define RX_IRQ0_BANK_PIN_MASK 0x839f -#define RX_IRQ1_BANK_PIN_MASK 0x7fe00000 +#define RX_IRQ1_BANK_PIN_MASK 0xffe00000#define RX_IRQ2_BANK_PIN_MASK 0x20#define RX_IRQ3_BANK_PIN_MASK 0x40#define RX_IRQ_BANK_PIN_MASK (_n) \
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 218/733 net: stmmac: reconfigure RX packet parser table in stmmac_hw_setup() after reset
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit 6b8fed2675fb75d23e6cf2b7e49c94926e884b34 ]
The core software reset issued in stmmac_init_dma_engine() during ndo_open() callback clears the MTL RX packet parser registers, but stmmac_rxp_config() is only invoked from the cls_u32 add/delete paths. After an ifdown/ifup cycle the hardware therefore runs with the default all-pass table while priv->tc_entries still reports the filters as installed. Re-apply the RX packet parser table from priv->tc_entries in stmmac_hw_setup(), right after the software reset, so the filters are restored when the interface is brought up again.
--- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +8 -0
@@ -3676,6 +3676,14 @@ static int stmmac_hw_setup(struct net_device *dev)/* Initialize MTL*/ stmmac_mtl_configuration (priv); +/* Apply the RX packet parser table */ +if (priv->tc_entries) { + ret =stmmac_rxp_config (priv, priv->hw->pcsr, priv->tc_entries, + priv->tc_entries_max); +if (ret) +return ret; + } +/* Initialize Safety Features */ stmmac_safety_feat_configuration (priv);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 219/733 net: gro: Fix nesting of TCP GSO SKBs in skb_gro_receive_list()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: HW He <[email protected]>
[ Upstream commit 66817a9794263cd2a5dc4e99bf8e5fcc5ff7181e ]
Fraglist GRO and hardware GRO can create an fraglist of HW-GRO packets. This cannot be segmented back into the original form on TCP tethering scenario.
Avoid constructing such a GSO packet, by flushing an already built fraglist GRO packet if a hardware GRO packet arrives.
Scenario (Tethering/Forwarding):
1.Driver submits a single TCP packet, P1. P1 is kept in the gro_list as the first packet.
2. The driver submits a TCP GSO skb, P2. P2 has already aggregated multiple TCP packets by HW_GRO, and its non-linear data is stored in frags[].
3. P1 and P2 match the GRO rules, and since there is no local socket, they are aggregated by skb_gro_receive_list(). The resulting skb, P3, has a frag_list entry that still contains frags[]:
P3: [ Linear Data ] -> frag_list -> [ Linear Data ]
[ frag[1] ]
[ frag[2] ]
...4. Later, tcp4_gso_segment() or tcp6_gso_segment() calls skb_segment_list() to segment P3. However, skb_segment_list() only segments the entries in frag_list. It does not segment the frags[] inside P2, so P3 is not restored to the original packets, which leads to IP fragmentation or packet drop in the following path.
Check skb_is_gso(skb) and current GRO method, make sure fraglist GRO applies to consecutive non-GSO skb, others adopt regular GRO path.
--- net/ipv4/tcp_offload.c | 22 ++++++++++++++++------ net/ipv6/tcpv6_offload.c | 15 +++++++++++++-- 2 files changed, 29 insertions(+), 8 deletions(-)
net/ipv4/tcp_offload.c +16 -6
@@ -332,6 +332,7 @@ struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb, flush |= skb->ip_summed != p->ip_summed; flush |= skb->csum_level != p->csum_level; flush |=NAPI_GRO_CB (p)->count >=64 ; + flush |=NAPI_GRO_CB (p)->is_flist !=NAPI_GRO_CB (skb)->is_flist;skb_set_network_header (skb,skb_gro_receive_network_offset (skb));if (flush ||skb_gro_receive_list (p, skb))
@@ -395,12 +396,20 @@ static void tcp4_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,struct net *net;int iif, sdif; -if (likely (!(skb->dev->features &NETIF_F_GRO_FRAGLIST ))) -return ; - p =tcp_gro_lookup (head, th);if (p) { -NAPI_GRO_CB (skb)->is_flist =NAPI_GRO_CB (p)->is_flist; +/* flist GRO applies to consecutive non-GSO skbs */ +if (!skb_is_gso (skb) || !NAPI_GRO_CB (p)->is_flist) { +NAPI_GRO_CB (skb)->is_flist =NAPI_GRO_CB (p)->is_flist; +return ; + } + +/* Fall back to the regular GRO path */ +if (NAPI_GRO_CB (p)->count ==1 ) +NAPI_GRO_CB (p)->is_flist =0 ; + +NAPI_GRO_CB (skb)->is_flist =0 ; +return ; }
@@ -410,7 +419,7 @@ static void tcp4_check_fraglist_gro(struct list_head *head, struct sk_buff *skb, sk =__inet_lookup_established (net, iph->saddr, th->source, iph->daddr,ntohs (th->dest), iif, sdif); -NAPI_GRO_CB (skb)->is_flist = !sk; +NAPI_GRO_CB (skb)->is_flist = !sk && !skb_is_gso (skb);if (sk)sock_gen_put (sk); }
@@ -430,7 +439,8 @@ struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb)if (!th) goto flush; -tcp4_check_fraglist_gro (head, skb, th); +if (unlikely (skb->dev->features &NETIF_F_GRO_FRAGLIST )) +tcp4_check_fraglist_gro (head, skb, th);return tcp_gro_receive (head, skb, th);
net/ipv6/tcpv6_offload.c +13 -2
@@ -26,7 +26,18 @@ static void tcp6_check_fraglist_gro(struct list_head *head, struct sk_buff *skb, p =tcp_gro_lookup (head, th);if (p) { -NAPI_GRO_CB (skb)->is_flist =NAPI_GRO_CB (p)->is_flist; +/* flist GRO applies to consecutive non-GSO skbs */ +if (!skb_is_gso (skb) || !NAPI_GRO_CB (p)->is_flist) { +NAPI_GRO_CB (skb)->is_flist =NAPI_GRO_CB (p)->is_flist; +return ; + } + +/* Fall back to the regular GRO path */ +if (NAPI_GRO_CB (p)->count ==1 ) +NAPI_GRO_CB (p)->is_flist =0 ; + +NAPI_GRO_CB (skb)->is_flist =0 ; +return ; }
@@ -36,7 +47,7 @@ static void tcp6_check_fraglist_gro(struct list_head *head, struct sk_buff *skb, sk =__inet6_lookup_established (net, &hdr->saddr, th->source, &hdr->daddr,ntohs (th->dest), iif, sdif); -NAPI_GRO_CB (skb)->is_flist = !sk; +NAPI_GRO_CB (skb)->is_flist = !sk && !skb_is_gso (skb);if (sk)sock_gen_put (sk);#endif /* IS_ENABLED(CONFIG_IPV6) */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 220/733 arm64: trans_pgd: clone only the linear map that exists at runtime
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Breno Leitao <[email protected]>
[ Upstream commit 1537e55728ec2bc506c74ea69b93cd859da58fb8 ]
kexec_file_load() fails on arm64 if we have CONFIG_ARM64_VA_BITS_52 but it runs on a !FEAT_LPA2 host (such as my loving Grace machine).
That is because trans_pgd_create_copy() uses the compile time PAGE_OFFSET (VA 52) instead of the actual VA size (48 -- due to the lack of LPA2). With the fifth level folded, pgd_none() is always false, so the walk cannot skip the 15 extra PGDIR_SIZE slots, and they all alias back to the same table: the whole kernel page table gets cloned 16 times, KASAN shadow included. Without KASAN it does not blow up, it just wastes ~RAM/32 in page tables.
Fix it by copying the linear map that is the actual one, not the compiled one.
--- arch/arm64/kernel/machine_kexec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
arch/arm64/kernel/machine_kexec.c +2 -1
@@ -129,7 +129,8 @@ int machine_kexec_post_load(struct kimage *kimage) }/* Create a copy of the linear map */ - rc =trans_pgd_create_copy (&info, &trans_pgd,PAGE_OFFSET ,PAGE_END ); + rc =trans_pgd_create_copy (&info, &trans_pgd, +_PAGE_OFFSET (vabits_actual),PAGE_END );if (rc)return rc; kimage->arch.ttbr1 =__pa (trans_pgd);
Signature · 1 line
2.53.0