6.18.53-rc1 review
Subsystems (6)
Greg Kroah-Hartman
maintainerPATCH 715/1250 ASoC: ux500: Correct MSP frame and bit clock setup
In reply to Greg Kroah-Hartman6.18-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 716/1250 ASoC: ux500: Validate MSP DAI configuration
In reply to Greg Kroah-Hartman6.18-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 717/1250 mfd: db8500-prcmu: Fold dbx500 header into db8500
In reply to Greg Kroah-Hartman6.18-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 718/1250 ASoC: ux500: Deassert the MSP reset during probe
In reply to Greg Kroah-Hartman6.18-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 719/1250 ASoC: ux500: Request the MSP MMIO resource
In reply to Greg Kroah-Hartman6.18-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 720/1250 ASoC: ux500: Remove obsolete PRCMU QoS calls
In reply to Greg Kroah-Hartman6.18-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 721/1250 ASoC: ux500: Allow repeated MSP prepare calls
In reply to Greg Kroah-Hartman6.18-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 722/1250 ASoC: ux500: Program the MSP FIFO watermarks
In reply to Greg Kroah-Hartman6.18-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 723/1250 btrfs: scrub: report the failing sectors address, not the stripe base
In reply to Greg Kroah-Hartman6.18-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
@@ -1049,6 +1049,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)) {
@@ -1077,12 +1081,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 ; }
@@ -1091,30 +1095,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 724/1250 btrfs: fix transaction use-after-free in raid stripe insertion
In reply to Greg Kroah-Hartman6.18-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
@@ -306,7 +306,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 725/1250 btrfs: fix the possible bioc_list memory leak during error
In reply to Greg Kroah-Hartman6.18-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
@@ -3380,6 +3380,9 @@ int btrfs_finish_one_ordered(struct btrfs_ordered_extent *ordered_extent)*/ btrfs_remove_ordered_extent (inode, 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
@@ -344,7 +344,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 ;
@@ -352,17 +352,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 726/1250 btrfs: return proper negative error code for update_raid_extent_item()
In reply to Greg Kroah-Hartman6.18-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
@@ -279,8 +279,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 727/1250 btrfs: zoned: finish active block group cleanup if call_zone_finish() fails
In reply to Greg Kroah-Hartman6.18-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
@@ -2592,16 +2592,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);
@@ -2614,7 +2611,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 728/1250 btrfs: send: fix lost error return value in will_overwrite_ref()
In reply to Greg Kroah-Hartman6.18-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
@@ -2074,7 +2074,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 729/1250 btrfs: do not force reloc root creation during qgroup_account_snapshot()
In reply to Greg Kroah-Hartman6.18-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
@@ -456,8 +456,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 730/1250 btrfs: zstd: fix lost wakeup when waiting for a workspace
In reply to Greg Kroah-Hartman6.18-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 731/1250 drm/amdgpu/userq: fix struct drm_amdgpu_info_device padding for 32bit compile
In reply to Greg Kroah-Hartman6.18-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 732/1250 ipvs: fix reversed sequence option serialization
In reply to Greg Kroah-Hartman6.18-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
@@ -748,9 +748,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 733/1250 netfilter: nf_conntrack_sip: fix OOB read in sip_skip_whitespace()
In reply to Greg Kroah-Hartman6.18-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 734/1250 tracing/probes: Fix use-after-free on field name/type of events with multiple probes
In reply to Greg Kroah-Hartman6.18-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
@@ -2113,19 +2113,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 );
@@ -2137,6 +2178,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 735/1250 bpf: reject BPF_PSEUDO_FUNC reference to the main program
In reply to Greg Kroah-Hartman6.18-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
@@ -17346,6 +17346,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 736/1250 bonding: do not clear curr_active_slave prematurely when releasing all slaves
In reply to Greg Kroah-Hartman6.18-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
@@ -2481,9 +2481,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 737/1250 net: Remove conflicting altnames for dying netns in __dev_change_net_namespace().
In reply to Greg Kroah-Hartman6.18-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
@@ -12443,7 +12443,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;
@@ -12489,13 +12489,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. */
@@ -12946,7 +12952,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
@@ -12970,10 +12975,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 738/1250 net/rds: use wq_has_sleeper() in release_in_xmit()
In reply to Greg Kroah-Hartman6.18-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 739/1250 net/rds: use clear_bit_unlock() in release_refill()
In reply to Greg Kroah-Hartman6.18-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 740/1250 net/rds: clear cp_flags bits individually in rds_conn_path_reset()
In reply to Greg Kroah-Hartman6.18-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 741/1250 net/rds: tcp: dont force RDS_CONN_RESETTING over a concurrent shutdown
In reply to Greg Kroah-Hartman6.18-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
@@ -181,7 +181,11 @@ int rds_tcp_accept_one(struct socket *sock)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 742/1250 net/rds: acquire RDS_IN_XMIT in rds_tcp_reset_callbacks()
In reply to Greg Kroah-Hartman6.18-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 743/1250 net/rds: acquire the fastpath locks in rds_conn_shutdown()
In reply to Greg Kroah-Hartman6.18-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);
@@ -401,14 +404,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
@@ -185,8 +185,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 744/1250 net/rds: dont let rds_conn_shutdown() consume a concurrent drop
In reply to Greg Kroah-Hartman6.18-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
@@ -434,19 +434,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 745/1250 net: airoha: enable RX_DONE interrupt for RX queue 31
In reply to Greg Kroah-Hartman6.18-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
@@ -502,7 +502,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 746/1250 net: stmmac: reconfigure RX packet parser table in stmmac_hw_setup() after reset
In reply to Greg Kroah-Hartman6.18-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
@@ -3455,6 +3455,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 747/1250 net: gro: Fix nesting of TCP GSO SKBs in skb_gro_receive_list()
In reply to Greg Kroah-Hartman6.18-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
@@ -360,6 +360,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))
@@ -423,12 +424,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 ; }
@@ -438,7 +447,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); }
@@ -458,7 +467,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
@@ -29,7 +29,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 ; }
@@ -39,7 +50,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 748/1250 arm64: trans_pgd: clone only the linear map that exists at runtime
In reply to Greg Kroah-Hartman6.18-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
Greg Kroah-Hartman
maintainerPATCH 749/1250 erofs: disable LZ4 rolling decompression for now
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gao Xiang <[email protected]>
[ Upstream commit 82e664cf1219c459c33aae931b222cf951af9cb7 ]
LZ4 rolling decompression [1] was introduced to reduce the memory footprint of temporary pages:
For many cases, it is needed for users to read small data within a compressed extent (pcluster), either due to random small read, or since uptodate folios (typically order-0) cannot be reused for decompression again since decompression algorithm refills already-uptodate folios.
Rolling decompression works because LZ4 is LZ77-based and only refers to the most recent 64 KiB of decompressed data, so in theory only a bounded rolling window of temporary pages is needed when decompressing.
It can save a lot of temporary memory, e.g.
601,960-byte data can be compressed into a 256k LZ4 compressed extent, which means it needs 146 extra pages per request in the worst case if rolling decompression is disabled.
However, the upstream LZ4 implementation is not under EROFS' control:
For example, the literal copy memmove() may still **copy long literals backward** on x86 based on the address comparison even when the source and destination ranges do not overlap (IOWs, inline decompression doesn't need to be considered here). That breaks the rolling assumption and makes the optimization broken.
Disable it for now to make sure the data correctness first since EROFS is used everywhere now: The rolling window approach can be revived once we either ensure that the official LZ4 code always copies forward for non-overlapping ranges or maintain our own LZ4 implementation in EROFS.
The main impact is a higher runtime memory footprint; However, recent commit 0f6273ab4637 ("erofs: add a reserved buffer pool for lz4 decompression") helps mitigate this when enabled but it's still not perfect.
[1] https://www.usenix.org/conference/atc19/presentation/gao
§ 3.3 Decompression
--- fs/erofs/decompressor.c | 55 +++++++++-------------------------------- fs/erofs/internal.h | 6 +---- fs/erofs/zdata.c | 18 +++----------- 3 files changed, 16 insertions(+), 63 deletions(-)
fs/erofs/decompressor.c +12 -43
@@ -7,8 +7,6 @@#include "compress.h" #include <linux/lz4.h> -#define LZ4_MAX_DISTANCE_PAGES (DIV_ROUND_UP(LZ4_DISTANCE_MAX, PAGE_SIZE) + 1) -static int z_erofs_load_lz4_config (struct super_block *sb,struct erofs_super_block *dsb,void *data,int size) {
@@ -21,8 +19,6 @@ static int z_erofs_load_lz4_config(struct super_block *sb,erofs_err (sb,"invalid lz4 cfgs, size=%u" , size);return -EINVAL ; } - distance =le16_to_cpu (lz4->max_distance); - sbi->lz4.max_pclusterblks =le16_to_cpu (lz4->max_pclusterblks);if (!sbi->lz4.max_pclusterblks) { sbi->lz4.max_pclusterblks =1 ;/* reserved case */
@@ -36,45 +32,25 @@ static int z_erofs_load_lz4_config(struct super_block *sb, distance =le16_to_cpu (dsb->u1.lz4_max_distance); sbi->lz4.max_pclusterblks =1 ; } - - sbi->lz4.max_distance_pages = distance ? -DIV_ROUND_UP (distance,PAGE_SIZE ) +1 : -LZ4_MAX_DISTANCE_PAGES ;return z_erofs_gbuf_growsize (sbi->lz4.max_pclusterblks); }/* -* Fill all gaps with bounce pages if it's a sparse page list. Also check if -* all physical pages are consecutive, which can be seen for moderate CR. +* Fill all gaps with bounce pages if it's a sparse page list (for example some +* folios are already uptodate and thus can be mapped into userspace). Also +* check if pages are physically consecutive, which can be seen for moderate CR. */ -static int z_erofs_lz4_prepare_dstpages (struct z_erofs_decompress_req *rq, -struct page **pagepool) +static int z_erofs_oneshot_prepare_dstpages (struct z_erofs_decompress_req *rq, +struct page **pagepool) { -struct page *availables[LZ4_MAX_DISTANCE_PAGES ] = {NULL }; -unsigned long bounced[DIV_ROUND_UP (LZ4_MAX_DISTANCE_PAGES , -BITS_PER_LONG )] = {0 }; -unsigned int lz4_max_distance_pages = -EROFS_SB (rq->sb)->lz4.max_distance_pages;void *kaddr =NULL ; -unsigned int i, j, top; +unsigned int i; - top =0 ; -for (i = j =0 ; i < rq->outpages; ++i, ++j) { -struct page *const page = rq->out[i]; -struct page *victim; - -if (j >= lz4_max_distance_pages) - j =0 ; - -/* 'valid' bounced can only be tested after a complete round */ -if (!rq->fillgaps &&test_bit (j, bounced)) { -DBG_BUGON (i < lz4_max_distance_pages); -DBG_BUGON (top >= lz4_max_distance_pages); - availables[top++] = rq->out[i - lz4_max_distance_pages]; - } +for (i =0 ; i < rq->outpages; ++i) { +struct page *page, *victim; + page = rq->out[i];if (page) { -__clear_bit (j, bounced);if (!PageHighMem (page)) {if (!i) { kaddr =page_address (page);
@@ -86,21 +62,14 @@ static int z_erofs_lz4_prepare_dstpages(struct z_erofs_decompress_req *rq,continue ; } } - kaddr =NULL ; -continue ; - } - kaddr =NULL ; -__set_bit (j, bounced); - -if (top) { - victim = availables[--top]; }else { victim =__erofs_allocpage (pagepool, rq->gfp, true);if (!victim)return -ENOMEM ;set_page_private (victim,Z_EROFS_SHORTLIVED_PAGE ); + rq->out[i] = victim; } - rq->out[i] = victim; + kaddr =NULL ; }return kaddr ?1 :0 ; }
@@ -271,7 +240,7 @@ static int z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq, dst_maptype =0 ; }else {/* general decoding path which can be used for all cases */ - ret =z_erofs_lz4_prepare_dstpages (rq, pagepool); + ret =z_erofs_oneshot_prepare_dstpages (rq, pagepool);if (ret <0 )return ret;if (ret >0 ) {
fs/erofs/internal.h +1 -5
@@ -70,12 +70,8 @@ struct erofs_dev_context {bool flatdev; }; -/* all filesystem-wide lz4 configurations */ struct erofs_sb_lz4_info { -/* # of pages needed for EROFS lz4 rolling decompression */ -u16 max_distance_pages; -/* maximum possible blocks for pclusters in the filesystem */ -u16 max_pclusterblks; +u16 max_pclusterblks;/* maximum physical blocks for LZ4 pclusters */ };struct erofs_domain {
fs/erofs/zdata.c +3 -15
@@ -1267,7 +1267,7 @@ static int z_erofs_decompress_pcluster(struct z_erofs_backend *be, bool eio)const struct z_erofs_decompressor *decomp = z_erofs_decomp[pcl->algorithmformat];bool try_free = true; -int i, j, jtop, err2, err = eio ? -EIO :0 ; +int i, err2, err = eio ? -EIO :0 ;struct page *page;bool overlapped;
@@ -1342,7 +1342,6 @@ static int z_erofs_decompress_pcluster(struct z_erofs_backend *be, bool eio) be->compressed_pages >= be->onstack_pages + Z_EROFS_ONSTACK_PAGES)kvfree (be->compressed_pages); - jtop =0 ;z_erofs_fill_other_copies (be, err);for (i =0 ; i < be->nr_pages; ++i) { page = be->decompressed_pages[i];
@@ -1350,22 +1349,11 @@ static int z_erofs_decompress_pcluster(struct z_erofs_backend *be, bool eio)continue ;DBG_BUGON (z_erofs_page_is_invalidated (page)); -if (!z_erofs_is_shortlived_page (page)) { +if (!z_erofs_is_shortlived_page (page))erofs_onlinefolio_end (page_folio (page), err, true); -continue ; - } -if (pcl->algorithmformat !=Z_EROFS_COMPRESSION_LZ4 ) { +else erofs_pagepool_add (be->pagepool, page); -continue ; - } -for (j =0 ; j < jtop && be->decompressed_pages[j] != page; ++j) - ; -if (j >= jtop)/* this bounce page is newly detected */ - be->decompressed_pages[jtop++] = page; } -while (jtop) -erofs_pagepool_add (be->pagepool, - be->decompressed_pages[--jtop]);if (be->decompressed_pages != be->onstack_pages)kvfree (be->decompressed_pages);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 750/1250 selftests/alsa: Fix the step check for INTEGER controls
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
[ Upstream commit 8efd5f623c63584c2e284a837a7795d95a0491cb ]
The modulo sits inside the subtraction, so the check evaluates int_val - (min % step) rather than (int_val - min) % step. The INTEGER64 branch below it is parenthesised correctly.
The written form passes only when the value equals min % step, and such a value is always on a step boundary, so it never misses a real violation. It only reports valid values as invalid.
snd-aloop declares step 1 on four controls, so every non-zero value on them is reported. Before:
# PCM Rate Shift 100000.0 value 100000 invalid for step 1 minimum 80000 # Totals: pass:660 fail:101 xfail:0 xpass:0 skip:296 error:0
After, same card, nothing else changed:
# Totals: pass:740 fail:21 xfail:0 xpass:0 skip:296 error:0
Eighteen files under sound/ declare a non-zero step.
--- tools/testing/selftests/alsa/mixer-test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
tools/testing/selftests/alsa/mixer-test.c +2 -2
@@ -319,8 +319,8 @@ static bool ctl_value_index_valid(struct ctl_data *ctl,/* Only check step size if there is one and we're in bounds */ if (snd_ctl_elem_info_get_step (ctl->info) && - (int_val -snd_ctl_elem_info_get_min (ctl->info) % -snd_ctl_elem_info_get_step (ctl->info))) { + (int_val -snd_ctl_elem_info_get_min (ctl->info)) % +snd_ctl_elem_info_get_step (ctl->info)) { ksft_print_msg("%s.%d value %ld invalid for step %ld minimum %ld\n", ctl->name, index, int_val, snd_ctl_elem_info_get_step(ctl->info),
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 751/1250 ALSA: caiaq: Fix potential double-free at error path
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
[ Upstream commit 3b26ceef88c110f4d188387cffa0df78657be904 ]
The fix for caiaq driver's resource management to handle the errors tries to release the resources in a common destructor call, but as a sashiko review for another patch suggested, some of the audio resources such as URBs have been already freed, and this may lead to a double-free.
For addressing the double-free, call the common destructor function from each place, and assure that the resource pointers get cleared.
--- sound/usb/caiaq/audio.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
sound/usb/caiaq/audio.c +5 -5
@@ -829,16 +829,13 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *cdev) cdev->data_urbs_in =alloc_urbs (cdev,SNDRV_PCM_STREAM_CAPTURE , &ret);if (ret <0 ) { -kfree (cdev->data_cb_info); -free_urbs (cdev->data_urbs_in); +snd_usb_caiaq_audio_free (cdev);return ret; } cdev->data_urbs_out =alloc_urbs (cdev,SNDRV_PCM_STREAM_PLAYBACK , &ret);if (ret <0 ) { -kfree (cdev->data_cb_info); -free_urbs (cdev->data_urbs_in); -free_urbs (cdev->data_urbs_out); +snd_usb_caiaq_audio_free (cdev);return ret; }
@@ -859,6 +856,9 @@ void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *cdev)dev_dbg (dev,"%s(%p)\n" , __func__, cdev);free_urbs (cdev->data_urbs_in); + cdev->data_urbs_in =NULL ;free_urbs (cdev->data_urbs_out); + cdev->data_urbs_out =NULL ;kfree (cdev->data_cb_info); + cdev->data_cb_info =NULL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 752/1250 drm/rockchip: dw_dp: Select DRM_BRIDGE_CONNECTOR
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Igor Paunovic <[email protected]>
[ Upstream commit c83e3e806d4c115f1dad9ef756db4cd91448a262 ]
dw_dp-rockchip.c calls drm_bridge_connector_init(), but ROCKCHIP_DW_DP does not select DRM_BRIDGE_CONNECTOR. A configuration with ROCKCHIP_DW_DP as the only enabled Rockchip output option fails to link:
aarch64-linux-gnu-ld: drivers/gpu/drm/rockchip/dw_dp-rockchip.o: in function `dw_dp_rockchip_bind':
dw_dp-rockchip.c:(.text+0x1d4): undefined reference to `drm_bridge_connector_init'
Five other Rockchip encoder options that call drm_bridge_connector_init() (ROCKCHIP_ANALOGIX_DP, ROCKCHIP_CDN_DP, ROCKCHIP_DW_HDMI_QP, ROCKCHIP_LVDS, ROCKCHIP_RGB) already select it, which masks the gap in any configuration that enables one of them. ROCKCHIP_INNO_HDMI is covered through its DRM_INNO_HDMI core option.
The same change was posted by Marius Dinu in March and dropped when the failure stopped reproducing in his build. The failure is configuration-dependent - any other enabled option that selects DRM_BRIDGE_CONNECTOR hides it - and it still reproduces on current drm-misc-next with the configuration described above.
Select DRM_BRIDGE_CONNECTOR like the other users do.
--- drivers/gpu/drm/rockchip/Kconfig | 1 + 1 file changed, 1 insertion(+)
drivers/gpu/drm/rockchip/Kconfig +1 -0
@@ -64,6 +64,7 @@ config ROCKCHIP_CDN_DP config ROCKCHIP_DW_DP bool "Rockchip specific extensions for Synopsys DW DP" + select DRM_BRIDGE_CONNECTOR help This selects support for Rockchip SoC specific extensions to enable Synopsys DesignWare Cores based DisplayPort transmit
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 753/1250 drm/rockchip: rk3066_hdmi: Add missing Kconfig selects
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Igor Paunovic <[email protected]>
[ Upstream commit d72aa5cf045a69d5fd433cde5dd4e113d9558fd1 ]
rk3066_hdmi.c calls drm_bridge_connector_init(), but ROCKCHIP_RK3066_HDMI selects neither DRM_BRIDGE_CONNECTOR nor DRM_DISPLAY_HELPER, whose module carries the bridge-connector code. A configuration with ROCKCHIP_RK3066_HDMI as the only enabled Rockchip output option fails to link:
aarch64-linux-gnu-ld: drivers/gpu/drm/rockchip/rk3066_hdmi.o: in function `rk3066_hdmi_bind':
rk3066_hdmi.c:(.text+0x7a4): undefined reference to `drm_bridge_connector_init'
aarch64-linux-gnu-ld: drivers/gpu/drm/rockchip/rk3066_hdmi.o: in function `rk3066_hdmi_bridge_atomic_enable':
rk3066_hdmi.c:(.text+0xe74): undefined reference to `drm_atomic_helper_connector_hdmi_update_infoframes'
Select both, like ROCKCHIP_CDN_DP, ROCKCHIP_LVDS and ROCKCHIP_RGB do. DRM_BRIDGE_CONNECTOR in turn selects DRM_DISPLAY_HDMI_STATE_HELPER, which resolves the second symbol.
--- drivers/gpu/drm/rockchip/Kconfig | 2 ++ 1 file changed, 2 insertions(+)
drivers/gpu/drm/rockchip/Kconfig +2 -0
@@ -142,6 +142,8 @@ config ROCKCHIP_RGB config ROCKCHIP_RK3066_HDMI bool "Rockchip specific extensions for RK3066 HDMI" depends on DRM_ROCKCHIP + select DRM_DISPLAY_HELPER + select DRM_BRIDGE_CONNECTOR help This selects support for Rockchip SoC specific extensions for the RK3066 HDMI driver. If you want to enable
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 754/1250 bpf: Fix NULL-ptr-deref when showing a void BTF type
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiayuan Chen <[email protected]>
[ Upstream commit 4ea508b9ebd78bce7f212166d2e2cba66b875f08 ]
btf_modifier_show() resolves the modifier and then calls btf_type_ops(t)->show() unconditionally. For the void type (type_id 0, BTF_KIND_UNKN) kind_ops[] has no entry, so ->show is NULL.
A "const void" (a modifier resolving to void) cannot be a map key or value - map_check_btf() rejects it because void has no size - so the map dump path does not reach it. But bpf_snprintf_btf() takes a type_id straight from the BPF program, and passing such a "const void" from the vmlinux BTF NULL-derefs:
KASAN: null-ptr-deref in range [0x0000000000000028-0x000000000000002f] RIP: 0010:btf_modifier_show (kernel/bpf/btf.c:2914) Call Trace: <TASK> btf_type_show (kernel/bpf/btf.c:8251) btf_type_snprintf_show (kernel/bpf/btf.c:8321) bpf_snprintf_btf (kernel/trace/bpf_trace.c:1047) bpf_prog_test_run_raw_tp (net/bpf/test_run.c:829) __sys_bpf (kernel/bpf/syscall.c:4804) do_syscall_64 (arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) </TASK>
Fall back to btf_df_show() when the resolved type has no show op; it emits the "<unsupported kind:N>" placeholder already used for kinds like FWD and FUNC. bpf_snprintf_btf() then returns the length as usual.
--- kernel/bpf/btf.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
kernel/bpf/btf.c +8 -1
@@ -2795,7 +2795,14 @@ static void btf_modifier_show(const struct btf *btf,else t =btf_type_skip_modifiers (btf, type_id,NULL ); -btf_type_ops (t)->show (btf, t, type_id, data, bits_offset, show); +/* +* A modifier can resolve to void, which has no show op; print a +* placeholder rather than dereferencing NULL. +*/ +if (!btf_type_ops (t)) +btf_df_show (btf, t, type_id, data, bits_offset, show); +else +btf_type_ops (t)->show (btf, t, type_id, data, bits_offset, show); } static void btf_var_show(const struct btf *btf, const struct btf_type *t,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 755/1250 bpf: Fix NULL-ptr-deref in btf_var_show()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiayuan Chen <[email protected]>
[ Upstream commit 5403a383f52fc0905703b488f7c3db4b2447dc58 ]
btf_var_show() calls btf_type_id_resolve() unconditionally, which dereferences btf->resolved_ids. That is NULL for a base BTF - e.g. the vmlinux BTF that bpf_snprintf_btf() renders against - since base BTF is not resolved during parsing. btf_modifier_show() guards this with 'if (btf->resolved_ids)', but btf_var_show() does not.
A BPF program that passes the type_id of a BTF_KIND_VAR from the vmlinux BTF to bpf_snprintf_btf() thus NULL-derefs:
KASAN: probably user-memory-access in range [0x46638-0x4663f] RIP: 0010:btf_var_show (kernel/bpf/btf.c:2929) Call Trace: <TASK> btf_type_show (kernel/bpf/btf.c:8259) btf_type_snprintf_show (kernel/bpf/btf.c:8329) bpf_snprintf_btf (kernel/trace/bpf_trace.c:1047) bpf_prog_test_run_raw_tp (net/bpf/test_run.c:829) __sys_bpf (kernel/bpf/syscall.c:4804) do_syscall_64 (arch/x86/entry/syscall_64.c:84) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) </TASK>
Resolve the var's type directly with btf_type_skip_modifiers() when resolved_ids is NULL, mirroring btf_modifier_show().
--- kernel/bpf/btf.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
kernel/bpf/btf.c +9 -1
@@ -2809,7 +2809,15 @@ static void btf_var_show(const struct btf *btf, const struct btf_type *t, u32 type_id, void *data, u8 bits_offset, struct btf_show *show) { - t =btf_type_id_resolve (btf, &type_id); + /* + * btf_type_id_resolve() dereferences btf->resolved_ids, which is NULL + * for a base BTF (e.g. the vmlinux BTF that bpf_snprintf_btf() uses). + * Resolve the var's type directly in that case. + */ + if (btf->resolved_ids) + t =btf_type_id_resolve (btf, &type_id); + else + t =btf_type_skip_modifiers (btf, t->type, &type_id);btf_type_ops (t)->show (btf, t, type_id, data, bits_offset, show); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 756/1250 bpf: Mark signal tracepoint siginfo arguments as scalar
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit 77515ab12e4983e6416f8c35039a3f0c0822ac70 ]
The signal_generate and signal_deliver tracepoints declare their info argument as a struct kernel_siginfo pointer. btf_ctx_access() therefore treats it as a trusted pointer for tp_btf programs.
Signal delivery also uses SEND_SIG_NOINFO and SEND_SIG_PRIV as special values for this argument. Those values are zero and one respectively, and are not pointers. A tp_btf program can currently dereference either value and fault the kernel. In particular, signal_generate can run from timer interrupt context, turning the fault into a kernel panic.
Record both tracepoints in raw_tp_null_args[] and mark argument one as a non-pointer. This preserves scalar access to the cookie while rejecting direct and helper-mediated pointer use. Merely marking it nullable would not suffice because SEND_SIG_PRIV is nonzero.
--- kernel/bpf/btf.c | 3 +++ 1 file changed, 3 insertions(+)
kernel/bpf/btf.c +3 -0
@@ -6561,6 +6561,9 @@ static const struct bpf_raw_tp_null_args raw_tp_null_args[] = { { "rxrpc_resend", 0x10 }, { "rxrpc_tq", 0x10 }, { "rxrpc_client", 0x1 }, +/* signal */ + { "signal_generate", 0x20 }, + { "signal_deliver", 0x20 },/* skb */ {"kfree_skb", 0x1000}, /* sunrpc */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 757/1250 bpf: Reject tail calls directly from callback frames
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit 266aa4ad0b2e82397cd9045752c9bff03d98eddd ]
A tail call from a non-zero frame is modeled as a return from that frame. The verifier makes R0 unknown and calls prepare_func_exit() for the taken branch.
When the current frame is a synchronous callback, prepare_func_exit() enforces the callback return-value contract and marks R0 precise. Since the tail-call path synthesized R0 rather than deriving it from an instruction, precision backtracking reaches the callback-calling instruction with R0 still requested and triggers the "callback unexpected regs" verifier bug. A CAP_BPF task can therefore cause a WARN and an -EFAULT BPF_PROG_LOAD.
Tail calls reachable from callbacks are already rejected later by check_max_stack_depth(). Reject a tail call made directly by a callback before constructing the inconsistent return state, using the existing diagnostic. Tail calls from ordinary subprograms keep their current behavior.
--- kernel/bpf/verifier.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
kernel/bpf/verifier.c +11 -0
@@ -12046,6 +12046,17 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insnif (env->cur_state->curframe) {struct bpf_verifier_state *branch; +/* +* A taken tail call is modeled as a return from the current +* frame. A callback frame cannot be left that way because +* prepare_func_exit() would apply its return contract to the +* unknown R0 synthesized below. Stack-depth validation rejects +* this construct anyway. +*/ +if (cur_func (env)->in_callback_fn) { +verbose (env,"cannot tail call within callback\n" ); +return -EINVAL ; + }mark_reg_scratched (env,BPF_REG_0 ); branch =push_stack (env, env->insn_idx +1 , env->insn_idx, false);if (IS_ERR (branch))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 758/1250 bpf: Reject resilient lock operations in rbtree callbacks
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit 7b7b8b5960102566bd625ae829d1f330c5b5d104 ]
__bpf_rbtree_add() keeps parent and link pointers live across calls to the program-supplied comparison callback. The verifier therefore requires the root's lock to remain held throughout the callback.
The helper path enforces this rule for bpf_spin_lock() and bpf_spin_unlock(), but the resilient lock kfunc argument path does not. Since resilient locks may protect BPF rbtree roots, a callback can release the root lock and let another CPU remove and free the node referenced by the in-progress tree walk. The walk then resumes using freed pointers.
Reject resilient lock kfuncs in an rbtree comparison callback, matching the existing policy for the spin lock helpers. Resilient-lock-protected trees remain valid when their comparison callbacks leave lock state alone.
--- kernel/bpf/verifier.c | 5 +++++ 1 file changed, 5 insertions(+)
kernel/bpf/verifier.c +5 -0
@@ -13726,6 +13726,11 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ {int flags =PROCESS_RES_LOCK ; +if (in_rbtree_lock_required_cb (env)) { +verbose (env,"can't res_spin_{lock,unlock} in rbtree cb\n" ); +return -EACCES ; + } +if (reg->type !=PTR_TO_MAP_VALUE && reg->type != (PTR_TO_BTF_ID |MEM_ALLOC )) {verbose (env,"arg#%d doesn't point to map value or allocated object\n" , i);return -EINVAL ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 759/1250 bpf: Mark sched_process_wait argument as nullable
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit a453d6e3b8e8e1a321c8744d6189d763af9287d0 ]
do_wait() passes wo->wo_pid to the sched_process_wait tracepoint. kernel_wait4() leaves wo_pid NULL for wait4(-1), and kernel_waitid_prepare() does likewise for waitid(P_ALL).
btf_ctx_access() currently types argument 0 as PTR_TO_BTF_ID | PTR_TRUSTED. Without PTR_MAYBE_NULL, the verifier accepts an unchecked dereference. Trusted pointer loads have no fault protection, so a wait for any child can then cause a NULL pointer dereference in JITed BPF code.
Add sched_process_wait to raw_tp_null_args[] with argument 0 marked nullable. The verifier rejects an unchecked dereference while preserving access after the program checks the pointer for NULL.
--- kernel/bpf/btf.c | 4 ++++ 1 file changed, 4 insertions(+)
kernel/bpf/btf.c +4 -0
@@ -6494,6 +6494,10 @@ struct bpf_raw_tp_null_args {static const struct bpf_raw_tp_null_args raw_tp_null_args[] = {/* sched */ {"sched_pi_setprio" ,0x10 }, +/* +* do_wait() passes NULL for wait4(-1) and waitid(P_ALL). +*/ + {"sched_process_wait" ,0x1 },/* ... from sched_numa_pair_template event class */ {"sched_stick_numa" ,0x100 }, {"sched_swap_numa" ,0x100 },
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 760/1250 nvme: remove stale namespaces by NSID range during scan
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mohamed Khalfella <[email protected]>
[ Upstream commit 4ed7f3d7d435bf5b63da2814dc9270f5ba896011 ]
nvme_scan_ns_list() drops the stale namespaces in each gap in the reported NSID list one NSID at a time. Every iteration calls nvme_find_get_ns() to look the namespace up and removes it if it is present. The loop runs once per NSID in the gap rather than once per namespace actually present.
NSIDs are 32-bit, so a target with a sparse NSID space can make a single gap spin the loop billions of times with nothing to remove.
watchdog: BUG: soft lockup - CPU#4 stuck for 26s! Workqueue: nvme-wq nvme_scan_work [nvme_core] RIP: 0010:__srcu_read_unlock+0xb/0x20 Call Trace: nvme_find_get_ns+0x7d/0xb0 [nvme_core] nvme_scan_ns_list+0xe8/0x280 [nvme_core] nvme_scan_work+0x18a/0x280 [nvme_core] process_one_work+0x197/0x380 worker_thread+0x2fe/0x410 kthread+0xe0/0x100
Rename nvme_remove_invalid_namespaces() to nvme_remove_nsid_range() and give it an open (start, end) NSID range. ctrl->namespaces is sorted by NSID, so the whole gap is dropped in a single walk that stops once end is reached. This bounds the work by the namespaces that are present instead of by the size of the gap.
--- drivers/nvme/host/core.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-)
drivers/nvme/host/core.c +9 -9
@@ -148,8 +148,6 @@ static const struct class nvme_ns_chr_class = { };static void nvme_put_subsystem (struct nvme_subsystem *subsys); -static void nvme_remove_invalid_namespaces (struct nvme_ctrl *ctrl, -unsigned nsid);static void nvme_update_keep_alive (struct nvme_ctrl *ctrl,struct nvme_command *cmd); static int nvme_get_log_lsi(struct nvme_ctrl *ctrl, u32 nsid, u8 log_page,
@@ -4414,15 +4412,16 @@ static void nvme_scan_ns_async(void *data, async_cookie_t cookie)nvme_scan_ns (scan_info->ctrl, nsid); } -static void nvme_remove_invalid_namespaces (struct nvme_ctrl *ctrl, -unsigned nsid) +static void nvme_remove_nsid_range (struct nvme_ctrl *ctrl,u32 start,u32 end) {struct nvme_ns *ns, *next;LIST_HEAD (rm_list);mutex_lock (&ctrl->namespaces_lock);list_for_each_entry_safe (ns, next, &ctrl->namespaces, list) { -if (ns->head->ns_id > nsid) { +if (ns->head->ns_id >= end) +break ; +if (ns->head->ns_id > start) {list_del_rcu (&ns->list);synchronize_srcu (&ctrl->srcu);list_add_tail_rcu (&ns->list, &rm_list);
@@ -4472,13 +4471,14 @@ static int nvme_scan_ns_list(struct nvme_ctrl *ctrl) goto out;async_schedule_domain (nvme_scan_ns_async, &scan_info, &domain); -while (++prev < nsid) -nvme_ns_remove_by_nsid (ctrl, prev); +if (prev +1 < nsid) +nvme_remove_nsid_range (ctrl, prev, nsid); + prev =max (prev +1 , nsid); }async_synchronize_full_domain (&domain); } out: -nvme_remove_invalid_namespaces (ctrl, prev); +nvme_remove_nsid_range (ctrl, prev,UINT_MAX ); free:async_synchronize_full_domain (&domain);kfree (ns_list);
@@ -4498,7 +4498,7 @@ static void nvme_scan_ns_sequential(struct nvme_ctrl *ctrl)for (i =1 ; i <= nn; i++)nvme_scan_ns (ctrl, i); -nvme_remove_invalid_namespaces (ctrl, nn); +nvme_remove_nsid_range (ctrl, nn,UINT_MAX ); }static void nvme_clear_changed_ns_log (struct nvme_ctrl *ctrl)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 761/1250 nvme-tcp: defer TLS inline send to io_work
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xixin Liu <[email protected]>
[ Upstream commit 08acb54b063a33730eb1ae1e0f89bf36542bac9f ]
blk_mq holds set->srcu while queuing and running requests. The kTLS software send path takes ctx->tx_lock. lockdep knows that tx_lock nests under elevator_lock which then waits on srcu, so an inline send from that path under TLS triggers circular locking.
Skip the inline send optimization for TLS queues so the send runs from the workqueue instead. The same workqueue already retries TLS sends on write-space notifications. Plain TCP keeps the inline path.
--- drivers/nvme/host/tcp.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/nvme/host/tcp.c +5 -0
@@ -417,8 +417,13 @@ static inline void nvme_tcp_queue_request(struct nvme_tcp_request *req,* if we're the first on the send_list and we can try to send * directly, otherwise queue io_work. Also, only do that if we * are on the same cpu, so we don't introduce contention. +* +* TLS kTLS send takes ctx->tx_lock while blk_mq holds set->srcu. +* lockdep reports circular locking via elevator_lock. Defer TLS +* sends to the io workqueue instead of inline from this path. */ if (queue->io_cpu ==raw_smp_processor_id () && + !nvme_tcp_queue_tls (queue) && empty &&mutex_trylock (&queue->send_mutex)) {nvme_tcp_send_all (queue);mutex_unlock (&queue->send_mutex);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 762/1250 ASoC: Intel: avs: Clean up the bus when fetching ML caps fails
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cezary Rojewski <[email protected]>
[ Upstream commit 559ea14b7ae7c7562b48759fa545b64958f35b73 ]
snd_hdac_ext_bus_get_ml_capabilities() may fail and its return code shall be checked and accounted for. Address the issue by updating the error-path for avs_pci_probe().
At the same time, if the function in question succeeds but the next part of avs_pci_probe() fails, the hlink list shall be cleaned up before leaving the scope.
--- sound/soc/intel/avs/core.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
sound/soc/intel/avs/core.c +9 -2
@@ -473,8 +473,13 @@ static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) }snd_hdac_bus_parse_capabilities (bus); -if (bus->mlcap) -snd_hdac_ext_bus_get_ml_capabilities (bus); +if (bus->mlcap) { + ret =snd_hdac_ext_bus_get_ml_capabilities (bus); +if (ret <0 ) { +dev_err (dev,"failed to get ml capabilities: %d\n" , ret); + goto err_ml_cap; + } + }if (dma_set_mask_and_coherent (dev,DMA_BIT_MASK (64 )))dma_set_mask_and_coherent (dev,DMA_BIT_MASK (32 ));
@@ -516,6 +521,8 @@ static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)snd_hdac_bus_free_stream_pages (bus);snd_hdac_ext_stream_free_all (bus); err_init_streams: +snd_hdac_ext_link_free_all (bus); +err_ml_cap:iounmap (adev->dsp_ba); err_remap_bar4:iounmap (bus->remap_addr);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 763/1250 ASoC: Intel: avs: Clean up streams if their initialization fails
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cezary Rojewski <[email protected]>
[ Upstream commit f4ba00bb56a8511bafbd19826501d27157141c6d ]
When streams are being initialized the memory allocation may fail. Have an error path and return early if that is the case.
--- sound/soc/intel/avs/core.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-)
sound/soc/intel/avs/core.c +15 -3
@@ -91,16 +91,28 @@ static int avs_hdac_bus_init_streams(struct hdac_bus *bus) {unsigned int cp_streams, pb_streams;unsigned int gcap; +int ret; gcap =snd_hdac_chip_readw (bus,GCAP ); cp_streams = (gcap >>8 ) &0x0F ; pb_streams = (gcap >>12 ) &0x0F ; bus->num_streams = cp_streams + pb_streams; -snd_hdac_ext_stream_init_all (bus,0 , cp_streams,SNDRV_PCM_STREAM_CAPTURE ); -snd_hdac_ext_stream_init_all (bus, cp_streams, pb_streams,SNDRV_PCM_STREAM_PLAYBACK ); + ret =snd_hdac_ext_stream_init_all (bus,0 , cp_streams,SNDRV_PCM_STREAM_CAPTURE ); +if (ret) +return ret; + ret =snd_hdac_ext_stream_init_all (bus, cp_streams, pb_streams,SNDRV_PCM_STREAM_PLAYBACK ); +if (ret) + goto err; + + ret =snd_hdac_bus_alloc_stream_pages (bus); +if (ret) + goto err; -return snd_hdac_bus_alloc_stream_pages (bus); +return 0 ; +err: +snd_hdac_ext_stream_free_all (bus); +return ret; }static bool avs_hdac_bus_init_chip (struct hdac_bus *bus,bool full_reset)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 764/1250 ASoC: Intel: avs: Do not ignore -ENOENT when loading a topology
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cezary Rojewski <[email protected]>
[ Upstream commit c6dceca9f78fbd478c41735457c4de9c25a6b1c8 ]
avs_load_topology() combines request_firmware() and snd_soc_tplg_component_load(). The fallback mechanism introduced for the HDAudio based boards honors -ENOENT and checks for a generic topology if no specific is found before giving up and failing the component probing.
However, if -ENOENT is returned by the latter function - snd_soc_tplg_component_load() - is shall not be ignored. That means there is an actual problem with the topology file and no fallback shall be attempted.
--- sound/soc/intel/avs/pcm.c | 34 ++++++++++++++++++++-------------- sound/soc/intel/avs/topology.c | 2 +- sound/soc/intel/avs/topology.h | 1 + 3 files changed, 22 insertions(+), 15 deletions(-)
sound/soc/intel/avs/pcm.c +20 -14
@@ -6,6 +6,7 @@// Amadeusz Slawinski <[email protected]> // +#include <linux/cleanup.h> #include <linux/debugfs.h> #include <linux/device.h> #include <sound/hda_register.h>
@@ -987,13 +988,25 @@ static int avs_component_load_libraries(struct avs_soc_component *acomp)return ret; } +static int avs_request_topology (struct snd_soc_component *component,const char *name, +const struct firmware **fw) +{ + char *fullname __free(kfree) = NULL; + + fullname =kasprintf (GFP_KERNEL ,"%s/%s" , component->driver->topology_name_prefix, name); +if (!fullname) +return -ENOMEM ; + +return request_firmware (fw, fullname, component->dev); +} +static int avs_component_probe (struct snd_soc_component *component) {struct snd_soc_card *card = component->card;struct snd_soc_acpi_mach *mach;struct avs_soc_component *acomp; +const struct firmware *fw;struct avs_dev *adev; -char *filename;int ret;dev_dbg (card->dev,"probing %s card %s\n" , component->name, card->name);
@@ -1009,13 +1022,7 @@ static int avs_component_probe(struct snd_soc_component *component) goto finalize;/* Load specified topology and create debugfs for it. */ - filename =kasprintf (GFP_KERNEL ,"%s/%s" , component->driver->topology_name_prefix, - mach->tplg_filename); -if (!filename) -return -ENOMEM ; - - ret =avs_load_topology (component, filename); -kfree (filename); + ret =avs_request_topology (component, mach->tplg_filename, &fw);if (ret == -ENOENT && !strncmp (mach->tplg_filename,"hda-" ,4 )) {unsigned int vendor_id;
@@ -1030,18 +1037,17 @@ static int avs_component_probe(struct snd_soc_component *component) "hda-generic-tplg.bin");if (!mach->tplg_filename)return -ENOMEM ; - filename =kasprintf (GFP_KERNEL ,"%s/%s" , component->driver->topology_name_prefix, - mach->tplg_filename); -if (!filename) -return -ENOMEM ;dev_info (card->dev,"trying to load fallback topology %s\n" , mach->tplg_filename); - ret =avs_load_topology (component, filename); -kfree (filename); + ret =avs_request_topology (component, mach->tplg_filename, &fw); }if (ret <0 )return ret; + ret =snd_soc_tplg_component_load (component, &avs_tplg_ops, fw); +if (ret) +return ret; + ret =avs_component_load_libraries (acomp);if (ret <0 ) {dev_err (card->dev,"libraries loading failed: %d\n" , ret);
sound/soc/intel/avs/topology.c +1 -1
@@ -2087,7 +2087,7 @@ avs_control_load(struct snd_soc_component *comp, int index, struct snd_kcontrol_return 0 ; } -static const struct snd_soc_tplg_ops avs_tplg_ops = { +const struct snd_soc_tplg_ops avs_tplg_ops = { .io_ops = avs_control_ops, .io_ops_count =ARRAY_SIZE (avs_control_ops), .control_load = avs_control_load,
sound/soc/intel/avs/topology.h +1 -0
@@ -222,6 +222,7 @@ struct avs_tplg_module {struct list_head node; }; +extern const struct snd_soc_tplg_ops avs_tplg_ops;struct avs_tplg *avs_tplg_new (struct snd_soc_component *comp);int avs_load_topology (struct snd_soc_component *comp,const char *filename);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 765/1250 ASoC: Intel: avs: Fix unbalanced module reference count
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cezary Rojewski <[email protected]>
[ Upstream commit d4fa6f94b91137e329ea3f5b360e140b227bb696 ]
strace_open() invokes try_module_get() which on success takes the module reference. If any follow up operation causes strace_open() to fail, the refcount shall be put down.
--- sound/soc/intel/avs/debugfs.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-)
sound/soc/intel/avs/debugfs.c +9 -3
@@ -8,6 +8,7 @@#include <linux/debugfs.h> #include <linux/kfifo.h> +#include <linux/module.h> #include <linux/wait.h> #include <linux/sched/signal.h> #include <linux/string_helpers.h>
@@ -238,15 +239,20 @@ static int strace_open(struct inode *inode, struct file *file)if (!try_module_get (adev->dev->driver->owner))return -ENODEV ; -if (kfifo_initialized (&adev->trace_fifo)) -return -EBUSY ; +if (kfifo_initialized (&adev->trace_fifo)) { + ret = -EBUSY ; + goto err; + } ret =kfifo_alloc (&adev->trace_fifo,PAGE_SIZE ,GFP_KERNEL );if (ret <0 ) -return ret; + goto err; file->private_data = adev;return 0 ; +err: +module_put (adev->dev->driver->owner); +return ret; }static int strace_release (struct inode *inode,struct file *file)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 766/1250 ASoC: Intel: avs: Allow the topology to carry NHLT data
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cezary Rojewski <[email protected]>
[ Upstream commit dd9896d41fdf1050934d6a46a1c5ca2164284e72 ]
Typically the hardware configuration for I2S and DMIC devices resides in the Non-HDAudio Link Table (NHLT) that is part of the ACPI tree. As the NHLTs existing in the field are not always perfect, workaround mechanisms are provided to patch them.
Currently the avs-driver is utilizing the ->blob_fmt override (see topology.h and struct avs_tplg_modcfg_ext) when there is a valid entry within a NHLT to configure the hardware for specific format but its descriptor (header) is invalid.
A separate case is when there is no correct hardware configuration at all within the NHLT available in the system. Patching the header won't help and forcing ad-hoc BIOS updates for dated system is not feasible. Allowing the topology to carry the data is the solution of choice as replacing a userspace file that is part of /lib/firmware/intel/ is less invasive than BIOS update and solves the problem.
Stable-dep-of: 681e91035dc7 ("ASoC: Intel: avs: Refactor and fix init_config access")
--- include/uapi/sound/intel/avs/tokens.h | 5 ++ sound/soc/intel/avs/topology.c | 106 +++++++++++++++++++++++++- sound/soc/intel/avs/topology.h | 7 ++ 3 files changed, 115 insertions(+), 3 deletions(-)
include/uapi/sound/intel/avs/tokens.h +5 -0
@@ -21,6 +21,7 @@ enum avs_tplg_token {AVS_TKN_MANIFEST_NUM_BINDINGS_U32 =8 ,AVS_TKN_MANIFEST_NUM_CONDPATH_TMPLS_U32 =9 ,AVS_TKN_MANIFEST_NUM_INIT_CONFIGS_U32 =10 , +AVS_TKN_MANIFEST_NUM_NHLT_CONFIGS_U32 =11 ,/* struct avs_tplg_library */ AVS_TKN_LIBRARY_ID_U32 = 101,
@@ -160,6 +161,10 @@ enum avs_tplg_token {AVS_TKN_INIT_CONFIG_ID_U32 =2401 ,AVS_TKN_INIT_CONFIG_PARAM_U8 =2402 ,AVS_TKN_INIT_CONFIG_LENGTH_U32 =2403 , + +/* struct avs_tplg_nhlt_config */ +AVS_TKN_NHLT_CONFIG_ID_U32 =2501 , + AVS_TKN_NHLT_CONFIG_SIZE_U32 = 2502, };#endif
sound/soc/intel/avs/topology.c +103 -3
156 lines not on this page · raw message
sound/soc/intel/avs/topology.h +7 -0
@@ -37,6 +37,8 @@ struct avs_tplg {u32 num_condpath_tmpls;struct avs_tplg_init_config *init_configs;u32 num_init_configs; +struct avs_tplg_nhlt_config *nhlt_configs; +u32 num_nhlt_configs;struct list_head path_tmpl_list; };
@@ -175,6 +177,11 @@ struct avs_tplg_init_config {void *data; }; +struct avs_tplg_nhlt_config { +u32 id; +struct acpi_nhlt_config *blob; +}; +struct avs_tplg_path {u32 id;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 767/1250 ASoC: Intel: avs: Honor NHLT override when setting up a path
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cezary Rojewski <[email protected]>
[ Upstream commit d5c8b7902a41625ea328b52c78ebe750fbf6fef7 ]
In case topology provides NHLT configuration, use it instead of relying on the table in ACPI tree. Only gateway-related modules e.g.: Copier care about the process. For those the order of fetching for hardware configuration becomes:
1) check if NHLT override is set,
2) check if NHLT descriptor override is set,
3) use NHLT from ACPI directly
Such approach ensures no conflicts exist between 1) and 2) and that 1) always takes precedence.
Stable-dep-of: 681e91035dc7 ("ASoC: Intel: avs: Refactor and fix init_config access")
--- include/uapi/sound/intel/avs/tokens.h | 1 + sound/soc/intel/avs/path.c | 13 +++++++++---- sound/soc/intel/avs/topology.c | 7 +++++++ sound/soc/intel/avs/topology.h | 1 + 4 files changed, 18 insertions(+), 4 deletions(-)
include/uapi/sound/intel/avs/tokens.h +1 -0
@@ -125,6 +125,7 @@ enum avs_tplg_token {AVS_TKN_MOD_KCONTROL_ID_U32 =1707 ,AVS_TKN_MOD_INIT_CONFIG_NUM_IDS_U32 =1708 ,AVS_TKN_MOD_INIT_CONFIG_ID_U32 =1709 , +AVS_TKN_MOD_NHLT_CONFIG_ID_U32 =1710 ,/* struct avs_tplg_path_template */ AVS_TKN_PATH_TMPL_ID_U32 = 1801,
sound/soc/intel/avs/path.c +9 -4
@@ -210,9 +210,11 @@ int avs_path_set_constraint(struct avs_dev *adev, struct avs_tplg_path_templatecontinue ; } - blob =avs_nhlt_config_or_default (adev, module_template); -if (IS_ERR (blob)) -continue ; +if (!module_template->nhlt_config) { + blob =avs_nhlt_config_or_default (adev, module_template); +if (IS_ERR (blob)) +continue ; + } rlist[i] = path_template->fe_fmt->sampling_freq; clist[i] = path_template->fe_fmt->num_channels;
@@ -382,7 +384,10 @@ static int avs_fill_gtw_config(struct avs_dev *adev, struct avs_copier_gtw_cfg *struct acpi_nhlt_config *blob;size_t gtw_size; - blob =avs_nhlt_config_or_default (adev, t); +if (t->nhlt_config) + blob = t->nhlt_config->blob; +else + blob =avs_nhlt_config_or_default (adev, t);if (IS_ERR (blob))return PTR_ERR (blob);
sound/soc/intel/avs/topology.c +7 -0
@@ -350,6 +350,7 @@ AVS_DEFINE_PTR_PARSER(modcfg_base, struct avs_tplg_modcfg_base, modcfgs_base); AVS_DEFINE_PTR_PARSER(modcfg_ext, struct avs_tplg_modcfg_ext, modcfgs_ext); AVS_DEFINE_PTR_PARSER(pplcfg, struct avs_tplg_pplcfg, pplcfgs); AVS_DEFINE_PTR_PARSER(binding, struct avs_tplg_binding, bindings); +AVS_DEFINE_PTR_PARSER(nhlt_config, struct avs_tplg_nhlt_config, nhlt_configs);static int parse_audio_format_bitfield (struct snd_soc_component *comp,void *elem,void *object,u32 offset)
@@ -1200,6 +1201,12 @@ static const struct avs_tplg_token_parser module_parsers[] = { .offset = offsetof(struct avs_tplg_module, num_config_ids), .parse = avs_parse_byte_token, }, + { + .token = AVS_TKN_MOD_NHLT_CONFIG_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_module, nhlt_config), + .parse = avs_parse_nhlt_config_ptr, + }, };static const struct avs_tplg_token_parser init_config_parsers[] = {
sound/soc/intel/avs/topology.h +1 -0
@@ -223,6 +223,7 @@ struct avs_tplg_module {u32 ctl_id;u32 num_config_ids;u32 *config_ids; +struct avs_tplg_nhlt_config *nhlt_config;struct avs_tplg_pipeline *owner;/* Pipeline modules management. */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 768/1250 ASoC: Intel: avs: Refactor and fix init_config access
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cezary Rojewski <[email protected]>
[ Upstream commit 681e91035dc794896a904852040837190e5041f5 ]
Existing code accesses enties found in ->init_configs array through indexes that are part of ->config_ids array. Those two are limited by:
->num_init_configs and ->num_config_ids respectively. Using ID larger or equal to ->num_init_configs leads to out-of-bounds access:
avs_path_module_send_init_configs()
loop:
(...) &acomp->tplg->init_configs[ids[i]] ^ out-of-bounds candidate
Rather than adding another if-statement, refactor the code. There is no need to store the IDs, have a list of pointers to actual config-entries instead. As the verification of ->init_config entries does not differ from verification of other types that are part of the topology.c file, simply reuse the code.
--- sound/soc/intel/avs/path.c | 11 +++----- sound/soc/intel/avs/topology.c | 47 +++++++++++++++++++--------------- sound/soc/intel/avs/topology.h | 4 +-- 3 files changed, 32 insertions(+), 30 deletions(-)
sound/soc/intel/avs/path.c +3 -8
@@ -838,15 +838,10 @@ static int avs_path_module_type_create(struct avs_dev *adev, struct avs_path_modstatic int avs_path_module_send_init_configs (struct avs_dev *adev,struct avs_path_module *mod) { -struct avs_soc_component *acomp; - - acomp =to_avs_soc_component (mod->template->owner->owner->owner->owner->comp); - -u32 num_ids = mod->template->num_config_ids; -u32 *ids = mod->template->config_ids; +struct avs_tplg_module *template = mod->template; -for (int i =0 ; i < num_ids; i++) { -struct avs_tplg_init_config *config = &acomp->tplg->init_configs[ids[i]]; +for (int i =0 ; i < template->num_init_configs; i++) { +struct avs_tplg_init_config *config = template->init_configs[i];size_t len = config->length;void *data = config->data;u32 param = config->param;
sound/soc/intel/avs/topology.c +27 -20
77 lines not on this page · raw message
sound/soc/intel/avs/topology.h +2 -2
@@ -221,8 +221,8 @@ struct avs_tplg_module {u8 domain;struct avs_tplg_modcfg_ext *cfg_ext;u32 ctl_id; -u32 num_config_ids; -u32 *config_ids; +u32 num_init_configs; +struct avs_tplg_init_config **init_configs;struct avs_tplg_nhlt_config *nhlt_config;struct avs_tplg_pipeline *owner;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 769/1250 net: bcmasp: clear txcb->last before writing each descriptor
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Justin Chen <[email protected]>
[ Upstream commit 18e5e0ec0e9282c897e2aa81a3e43ccaee03b003 ]
bcmasp_xmit() only wrote txcb->last = true for the final fragment of an SKB; non-final fragments left the field untouched. If a descriptor slot was reused while it still held a stale true from a previous SKB (possible when tx_spb_ring_full() underreported fullness), bcmasp_tx_reclaim() would see last == true mid-SKB and call dev_consume_skb_any() prematurely, freeing the sk_buff while its remaining fragments were still in flight.
Unconditionally clear txcb->last before the conditional set so every descriptor slot starts from a known false state regardless of what a prior transmission left behind.
--- drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c +1 -0
@@ -333,6 +333,7 @@ static netdev_tx_t bcmasp_xmit(struct sk_buff *skb, struct net_device *dev) txcb->bytes_sent = total_bytes;dma_unmap_addr_set (txcb, dma_addr, mapping);dma_unmap_len_set (txcb, dma_len, size); + txcb->last = false;if (!i) { desc->flags |=DESC_SOF ;if (csum_hw)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 770/1250 net: bcmasp: fix tx_spb_ring_full() checking same slot cnt times
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Justin Chen <[email protected]>
[ Upstream commit 0c5cf62e72d7a666ee4da757e122dc1600df1ecc ]
The loop initialised next_index from intf->tx_spb_index on every iteration, so incr_ring() always produced the same result and only one slot was ever tested. Move the initialisation before the loop so each iteration advances next_index and the function correctly checks that cnt consecutive descriptor slots are available before allowing a new transmission.
--- drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c +2 -1
@@ -147,8 +147,9 @@ static int tx_spb_ring_full(struct bcmasp_intf *intf, int cnt)int next_index, i;/* Check if we have enough room for cnt descriptors */ + next_index = intf->tx_spb_index;for (i =0 ; i < cnt; i++) { - next_index =incr_ring (intf->tx_spb_index,DESC_RING_COUNT ); + next_index =incr_ring (next_index,DESC_RING_COUNT );if (next_index == intf->tx_spb_clean_index)return 1 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 771/1250 mlxsw: spectrum_ptp: Fix napi_gro_receive() call from GC workqueue context
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Taylor Bates <[email protected]>
[ Upstream commit 2ac174dfcdde399fa95ba889541fb5e688d8bb35 ]
Currently mlxsw_sp1_ptp_ht_gc_collect() is run from the PTP garbage-collection workqueue, rather than the NAPI poll context. For any unmatched PTP entries carrying an SKB, it calls mlxsw_sp1_ptp_unmatched_finish() -> mlxsw_sp1_ptp_packet_finish(). For ingress packets, this calls mlxsw_sp_rx_listener_no_mark_func(). The end of that function is the following:
skb->protocol = eth_type_trans(skb, skb->dev);
napi_gro_receive(mlxsw_skb_cb(skb)->rx_md_info.napi, skb);The napi pointer is one that was placed in the SKB control block when the trapped packet was received in the NAPI context. Later, when the GC reaps the unmatched entry (up to MLXSW_SP1_PTP_HT_GC_TIMEOUT later), the call to napi_gro_receive() mutates the NAPI instance's GRO list, which is unsafe if the poll is running concurrently on another CPU.
In mlxsw_sp1_ptp_ht_gc_collect(), local_bh_disable() is called to prevent softirq processing, but this only applies to the local CPU. Additionally, its comment is stale. It states that mlxsw_sp1_ptp_unmatched_finish() invokes netif_receive_skb(). This has not been accurate since the referenced commit; this patch makes that comment accurate again. mlxsw_pci_napi_devs_init() calls netif_threaded_enable() on the NAPI RX net_device without any conditions. The NAPI instance's poll, which may be running concurrent to the GC, is running as an independently-scheduled kthread which may be on a different CPU. The call to local_bh_disable() does not guard against this.
If a tx-timestamp timeout produces an unmatched entry (which can be easily reproduced by running ptp4l and waiting for a port to reach the UNCALIBRATED/SLAVE state) while the owning NAPI thread is in the middle of a poll on another CPU, both sides mutate the GRO list concurrently, as shown below:
[39.846] port 1 (swp1): MASTER to UNCALIBRATED on RS_SLAVE list_add corruption. next->prev should be prev (ffff8d620faf4138), but was ffff8d624150f700. (next=ffff8d620faf4138). kernel BUG at lib/list_debug.c:29! Oops: invalid opcode: 0000 [#1] SMP PTI CPU: 1 UID: 0 PID: 539 Comm: napi/mlxsw_rx-0 Not tainted 6.18.48 #1-NixOS PREEMPT(lazy) Hardware name: Mellanox Technologies Ltd. MSN2410/VMOD0001, BIOS 4.6.5 09/13/2018 RIP: 0010:__list_add_valid_or_report+0x79/0xb0 RSP: 0018:ffffcdf8c0f27c08 EFLAGS: 00010246 RAX: 0000000000000075 RBX: ffff8d624150fd00 RCX: 0000000000000000 RDX: 0000000000000000 RSI: 0000000000000001 RDI: ffff8d6315d1e540 RBP: ffff8d620faf4070 R08: 0000000000000000 R09: 00000000ffffdfff R10: ffffffffa5c60fe0 R11: ffffcdf8c0f27ab8 R12: 0000000000000003 R13: 000000000000003d R14: 00000000000001bc R15: 0000000000000001 FS: 0000000000000000(0000) GS:ffff8d636f63f000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000562689a60c24 CR3: 000000015f224004 CR4: 00000000001726f0 Call Trace: <TASK> gro_receive_skb+0xee/0x230 mlxsw_sp1_ptp_got_packet+0x61/0x140 [mlxsw_spectrum] mlxsw_core_skb_receive+0xdf/0x1b0 [mlxsw_core] mlxsw_pci_napi_poll_cq_rx+0x780/0x9d0 [mlxsw_pci] __napi_poll+0x31/0x1e0 napi_threaded_poll_loop+0x16b/0x1c0 napi_threaded_poll+0x71/0xa0 kthread+0xfb/0x260 ret_from_fork+0x22d/0x260 ret_from_fork_asm+0x1a/0x30 </TASK> Kernel panic - not syncing: Fatal exception in interrupt
The machinery that leads to this kernel panic has not been changed between 6.18.48 and mainline.
This patch adds an ingress-delivery helper for the PTP packet_finish() path that calls netif_receive_skb() instead of napi_gro_receive(). netif_receive_skb(), unlike napi_gro_receive(), can be called from outside of the NAPI instance's poll context, which can occur at the call site for this path. RX stats accounting and the skb->dev assignment are still preserved; the only change is the delivery call itself.
This removes GRO batching for any PTP event traffic received by the mlxsw trap, but given the relatively low volume of traffic characteristic of the protocol, and impact limited to only Spectrum-1 ASICs, this is an acceptable solution.
--- .../ethernet/mellanox/mlxsw/spectrum_ptp.c | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-)
drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c +33 -1
@@ -572,6 +572,38 @@ mlxsw_sp1_ptp_unmatched_remove(struct mlxsw_sp *mlxsw_sp, mlxsw_sp1_ptp_unmatched_ht_params); } +/* mlxsw_sp1_ptp_packet_finish() is reached both from the NAPI poll context +* (mlxsw_sp1_ptp_got_packet(), mlxsw_sp1_ptp_got_piece() and +* mlxsw_sp1_packet_timestamp()) and from process context, by way of the GC +* workqueue (mlxsw_sp1_ptp_ht_gc_collect() -> +* mlxsw_sp1_ptp_unmatched_finish()). +* +* mlxsw_sp_rx_listener_no_mark_func() ends in napi_gro_receive(), using the +* NAPI pointer that was placed in the SKB control block when the trapped +* packet was received in the NAPI context. That pointer may only be used +* from its own poll context, which this call site cannot guarantee. +* +* netif_receive_skb(), unlike napi_gro_receive(), can be called from outside +* of the NAPI instance's poll context. RX stats accounting and the skb->dev +* assignment are still preserved; the only change is the delivery call. +*/ +static void mlxsw_sp1_ptp_rx_finish (struct mlxsw_sp_port *mlxsw_sp_port, +struct sk_buff *skb) +{ +struct mlxsw_sp_port_pcpu_stats *pcpu_stats; + + skb->dev = mlxsw_sp_port->dev; + + pcpu_stats =this_cpu_ptr (mlxsw_sp_port->pcpu_stats); +u64_stats_update_begin (&pcpu_stats->syncp); + pcpu_stats->rx_packets++; + pcpu_stats->rx_bytes += skb->len; +u64_stats_update_end (&pcpu_stats->syncp); + + skb->protocol =eth_type_trans (skb, skb->dev); +netif_receive_skb (skb); +} +/* This function is called in the following scenarios: * * 1) When a packet is matched with its timestamp.
@@ -600,7 +632,7 @@ static void mlxsw_sp1_ptp_packet_finish(struct mlxsw_sp *mlxsw_sp,if (ingress) {if (hwtstamps) *skb_hwtstamps (skb) = *hwtstamps; -mlxsw_sp_rx_listener_no_mark_func (skb, local_port, mlxsw_sp); +mlxsw_sp1_ptp_rx_finish (mlxsw_sp_port, skb); }else {/* skb_tstamp_tx() allows hwtstamps to be NULL. */ skb_tstamp_tx (skb, hwtstamps);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 772/1250 bpf: Only enforce 8 frame call stack limit for all-static stacks
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Emil Tsalapatis <[email protected]>
[ Upstream commit ad95d3c758d8752c41781b73fa060ab3215b1584 ]
The BPF verifier currently enforces a call stack depth of 8 frames, regardless of the actual stack space consumption of those frames. The limit is necessary for static call stacks, because the bookkeeping data structures used by the verifier when stepping into static functions during verification only support 8 stack frames. However, this limitation only matters for static stack frames: Global subprogs are verified by themselves and do not require limiting the call depth.
Relax this limitation to only apply to static stack frames. Verification now only fails when there is a sequence of 8 calls to non-global subprogs. Calling into a global subprog resets the counter. This allows deeper call stacks, provided all frames still fit in the stack.
The change does not increase the maximum size of the call stack, only the maximum number of frames we can place in it.
Also change the progs/test_global_func3.c selftest to use static functions, since with the new patch it would otherwise unexpectedly pass verification.
Stable-dep-of: 369f4ce73457 ("bpf: Check ancestor frames for rbtree callbacks")
--- kernel/bpf/verifier.c | 76 +++++++++++++------ .../selftests/bpf/progs/test_global_func3.c | 18 ++--- 2 files changed, 63 insertions(+), 31 deletions(-)
kernel/bpf/verifier.c +54 -22
@@ -6626,22 +6626,30 @@ static int round_up_stack_depth(struct bpf_verifier_env *env, int stack_depth)return round_up (max_t (u32, stack_depth,1 ),32 ); } +/* temporary state used for call frame depth calculation */ +struct bpf_subprog_call_depth_info { +int ret_insn;/* caller instruction where we return to. */ +int caller;/* caller subprogram idx */ +int frame;/* # of consecutive static call stack frames on top of stack */ +}; +/* starting from main bpf function walk all instructions of the function * and recursively walk all callees that given function can call. * Ignore jump and exit insns. -* Since recursion is prevented by check_cfg() this algorithm -* only needs a local stack of MAX_CALL_FRAMES to remember callsites */ static int check_max_stack_depth_subprog (struct bpf_verifier_env *env,int idx, +struct bpf_subprog_call_depth_info *dinfo,bool priv_stack_supported) {struct bpf_subprog_info *subprog = env->subprog_info;struct bpf_insn *insn = env->prog->insnsi;int depth =0 , frame =0 , i, subprog_end, subprog_depth;bool tail_call_reachable = false; -int ret_insn[MAX_CALL_FRAMES ]; -int ret_prog[MAX_CALL_FRAMES ]; -int j; +int total; +int tmp; + +/* no caller idx */ + dinfo[idx].caller =-1 ; i = subprog[idx].start;if (!priv_stack_supported)
@@ -6693,8 +6701,12 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx, }else { depth += subprog_depth;if (depth >MAX_BPF_STACK ) { + total =0 ; +for (tmp = idx; tmp >=0 ; tmp = dinfo[tmp].caller) + total++; +verbose (env,"combined stack size of %d calls is %d. Too large\n" , - frame +1 , depth); + total, depth);return -EACCES ; } }
@@ -6708,10 +6720,8 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,if (!is_bpf_throw_kfunc (insn + i))continue ; -if (subprog[idx].is_cb) - err = true; -for (int c =0 ; c < frame && !err; c++) { -if (subprog[ret_prog[c]].is_cb) { +for (tmp = idx; tmp >=0 && !err; tmp = dinfo[tmp].caller) { +if (subprog[tmp].is_cb) { err = true;break ; }
@@ -6727,8 +6737,6 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,if (!bpf_pseudo_call (insn + i) && !bpf_pseudo_func (insn + i))continue ;/* remember insn and function to return to */ - ret_insn[frame] = i +1 ; - ret_prog[frame] = idx;/* find the callee */ next_insn = i + insn[i].imm +1 ;
@@ -6748,7 +6756,16 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,return -EINVAL ; } } + +/* store caller info for after we return from callee */ + dinfo[idx].frame = frame; + dinfo[idx].ret_insn = i +1 ; + +/* push caller idx into callee's dinfo */ + dinfo[sidx].caller = idx; + i = next_insn; + idx = sidx;if (!priv_stack_supported) subprog[idx].priv_stack_mode =NO_PRIV_STACK ;
@@ -6756,7 +6773,7 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,if (subprog[idx].has_tail_call) tail_call_reachable = true; - frame++; + frame =subprog_is_global (env, idx) ?0 : frame +1 ;if (frame >=MAX_CALL_FRAMES ) {verbose (env,"the call stack of %d frames is too deep !\n" , frame);
@@ -6770,12 +6787,12 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,* tail call counter throughout bpf2bpf calls combined with tailcalls */ if (tail_call_reachable) -for (j =0 ; j < frame; j++) { -if (subprog[ret_prog[j]].is_exception_cb) { +for (tmp = idx; tmp >=0 ; tmp = dinfo[tmp].caller) { +if (subprog[tmp].is_exception_cb) {verbose (env,"cannot tail call within exception cb\n" );return -EINVAL ; } - subprog[ret_prog[j]].tail_call_reachable = true; + subprog[tmp].tail_call_reachable = true; }if (subprog[0 ].tail_call_reachable) env->prog->aux->tail_call_reachable = true;
@@ -6783,23 +6800,33 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,/* end of for() loop means the last insn of the 'subprog' * was reached. Doesn't matter whether it was JA or EXIT */ -if (frame ==0 ) +if (frame ==0 && dinfo[idx].caller <0 )return 0 ;if (subprog[idx].priv_stack_mode !=PRIV_STACK_ADAPTIVE ) depth -=round_up_stack_depth (env, subprog[idx].stack_depth); - frame--; - i = ret_insn[frame]; - idx = ret_prog[frame]; + +/* pop caller idx from callee */ + idx = dinfo[idx].caller; + +/* retrieve caller state from its frame */ + frame = dinfo[idx].frame; + i = dinfo[idx].ret_insn; + goto continue_func; }static int check_max_stack_depth (struct bpf_verifier_env *env) {enum priv_stack_mode priv_stack_mode =PRIV_STACK_UNKNOWN ; +struct bpf_subprog_call_depth_info *dinfo;struct bpf_subprog_info *si = env->subprog_info;bool priv_stack_supported;int ret; + dinfo =kvcalloc (env->subprog_cnt,sizeof (*dinfo),GFP_KERNEL_ACCOUNT ); +if (!dinfo) +return -ENOMEM ; +for (int i =0 ; i < env->subprog_cnt; i++) {if (si[i].has_tail_call) { priv_stack_mode =NO_PRIV_STACK ;
@@ -6821,9 +6848,12 @@ static int check_max_stack_depth(struct bpf_verifier_env *env)for (int i = env->subprog_cnt -1 ; i >=0 ; i--) {if (!i || si[i].is_async_cb) { priv_stack_supported = !i && priv_stack_mode ==PRIV_STACK_ADAPTIVE ; - ret =check_max_stack_depth_subprog (env, i, priv_stack_supported); -if (ret <0 ) + ret =check_max_stack_depth_subprog (env, i, dinfo, + priv_stack_supported); +if (ret <0 ) { +kvfree (dinfo);return ret; + } } }
@@ -6834,6 +6864,8 @@ static int check_max_stack_depth(struct bpf_verifier_env *env) } } +kvfree (dinfo); +return 0 ; }
tools/testing/selftests/bpf/progs/test_global_func3.c +9 -9
@@ -5,56 +5,56 @@#include <bpf/bpf_helpers.h> #include "bpf_misc.h" -__attribute__ ((noinline)) +static __attribute__ ((noinline))int f1 (struct __sk_buff *skb) {return skb->len; } -__attribute__ ((noinline)) +static __attribute__ ((noinline))int f2 (int val,struct __sk_buff *skb) {return f1 (skb) + val; } -__attribute__ ((noinline)) +static __attribute__ ((noinline))int f3 (int val,struct __sk_buff *skb,int var) {return f2 (var, skb) + val; } -__attribute__ ((noinline)) +static __attribute__ ((noinline))int f4 (struct __sk_buff *skb) {return f3 (1 , skb,2 ); } -__attribute__ ((noinline)) +static __attribute__ ((noinline))int f5 (struct __sk_buff *skb) {return f4 (skb); } -__attribute__ ((noinline)) +static __attribute__ ((noinline))int f6 (struct __sk_buff *skb) {return f5 (skb); } -__attribute__ ((noinline)) +static __attribute__ ((noinline))int f7 (struct __sk_buff *skb) {return f6 (skb); } -__attribute__ ((noinline)) +static __attribute__ ((noinline))int f8 (struct __sk_buff *skb) {return f7 (skb); } SEC("tc") -__failure __msg("the call stack of 8 frames") +__failure __msg("the call stack of 9 frames") int global_func3(struct __sk_buff *skb) {return f8 (skb);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 773/1250 bpf: share several utility functions as internal API
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eduard Zingerman <[email protected]>
[ Upstream commit 33dfc521c20d02375c8696dcace04037d2a865e6 ]
Namely:
- bpf_subprog_is_global
- bpf_vlog_alignment
Stable-dep-of: 369f4ce73457 ("bpf: Check ancestor frames for rbtree callbacks")
--- include/linux/bpf_verifier.h | 2 ++ kernel/bpf/log.c | 4 ++-- kernel/bpf/verifier.c | 10 +++++----- 3 files changed, 9 insertions(+), 7 deletions(-)
include/linux/bpf_verifier.h +2 -0
@@ -1051,12 +1051,14 @@ void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_verifie u32 frameno, bool print_all);void print_insn_state (struct bpf_verifier_env *env,const struct bpf_verifier_state *vstate,u32 frameno); +u32 bpf_vlog_alignment (u32 pos);struct bpf_subprog_info *bpf_find_containing_subprog (struct bpf_verifier_env *env,int off);int bpf_jmp_offset (struct bpf_insn *insn);int bpf_insn_successors (struct bpf_prog *prog,u32 idx,u32 succ[2 ]);void bpf_fmt_stack_mask (char *buf,ssize_t buf_sz,u64 stack_mask);bool bpf_calls_callback (struct bpf_verifier_env *env,int insn_idx); +bool bpf_subprog_is_global (const struct bpf_verifier_env *env,int subprog);int bpf_stack_liveness_init (struct bpf_verifier_env *env);void bpf_stack_liveness_free (struct bpf_verifier_env *env);
kernel/bpf/log.c +2 -2
@@ -842,7 +842,7 @@ void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_verifiemark_verifier_state_clean (env); } -static inline u32 vlog_alignment (u32 pos) +u32 bpf_vlog_alignment (u32 pos) {return round_up (max (pos +BPF_LOG_MIN_ALIGNMENT /2 ,BPF_LOG_ALIGNMENT ),BPF_LOG_MIN_ALIGNMENT ) - pos -1 ;
@@ -854,7 +854,7 @@ void print_insn_state(struct bpf_verifier_env *env, const struct bpf_verifier_stif (env->prev_log_pos && env->prev_log_pos == env->log.end_pos) {/* remove new line character */ bpf_vlog_reset (&env->log, env->prev_log_pos -1 ); -verbose (env,"%*c;" ,vlog_alignment (env->prev_insn_print_pos),' ' ); +verbose (env,"%*c;" ,bpf_vlog_alignment (env->prev_insn_print_pos),' ' ); }else {verbose (env,"%d:" , env->insn_idx); }
kernel/bpf/verifier.c +5 -5
@@ -431,7 +431,7 @@ static struct btf_record *reg_btf_record(const struct bpf_reg_state *reg)return rec; } -static bool subprog_is_global (const struct bpf_verifier_env *env,int subprog) +bool bpf_subprog_is_global (const struct bpf_verifier_env *env,int subprog) {struct bpf_func_info_aux *aux = env->prog->aux->func_info_aux;
@@ -4390,7 +4390,7 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,if (subprog <0 )return -EFAULT ; -if (subprog_is_global (env, subprog)) { +if (bpf_subprog_is_global (env, subprog)) {/* check that jump history doesn't have any * extra instructions from subprog; the next * instruction after call to global subprog
@@ -6773,7 +6773,7 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,if (subprog[idx].has_tail_call) tail_call_reachable = true; - frame =subprog_is_global (env, idx) ?0 : frame +1 ; + frame =bpf_subprog_is_global (env, idx) ?0 : frame +1 ;if (frame >=MAX_CALL_FRAMES ) {verbose (env,"the call stack of %d frames is too deep !\n" , frame);
@@ -10813,7 +10813,7 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn, err =btf_check_subprog_call (env, subprog, caller->regs);if (err == -EFAULT )return err; -if (subprog_is_global (env, subprog)) { +if (bpf_subprog_is_global (env, subprog)) {const char *sub_name =subprog_name (env, subprog);if (env->cur_state->active_locks) {
@@ -23786,7 +23786,7 @@ static int do_check_subprogs(struct bpf_verifier_env *env) again: new_cnt =0 ;for (i =1 ; i < env->subprog_cnt; i++) { -if (!subprog_is_global (env, i)) +if (!bpf_subprog_is_global (env, i))continue ; sub_aux =subprog_aux (env, i);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 774/1250 bpf: Print breakdown of insns processed by subprogs
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Paul Chaignon <[email protected]>
[ Upstream commit f603e84ab7918db6470c0b06b46ece7fbdb71e9a ]
When using global functions (i.e. subprogs), the verifier performs function-by-function verification. In that case, the sum of the instructions processed in each global function and in the main program counts towards the 1 million instructions limit. Only that sum is reported in the verifier logs.
While starting to use global functions in Cilium (finally!), we found it can be useful to have the breakdown per global function, to understand exactly where the budget is currently spent. This patch implements this breakdown, under BPF_LOG_STATS, as done for the stack depths.
When iterating over subprogs, we need to skip the hidden subprogs at the end because they don't have a corresponding func_info_aux entry and calling bpf_subprog_is_global() would result in an OOB access.
Stable-dep-of: 369f4ce73457 ("bpf: Check ancestor frames for rbtree callbacks")
--- include/linux/bpf_verifier.h | 1 + kernel/bpf/verifier.c | 25 ++++++++++++++++--------- 2 files changed, 17 insertions(+), 9 deletions(-)
include/linux/bpf_verifier.h +1 -0
@@ -648,6 +648,7 @@ struct bpf_subprog_info {u32 postorder_start;/* The idx to the env->cfg.insn_postorder */ u16 stack_depth;/* max. stack depth used by this function */ u16 stack_extra; +u32 insn_processed;/* offsets in range [stack_depth .. fastcall_stack_off) * are used for bpf_fastcall spills and fills. */
kernel/bpf/verifier.c +16 -9
@@ -23775,6 +23775,7 @@ static int do_check_subprogs(struct bpf_verifier_env *env)struct bpf_prog_aux *aux = env->prog->aux;struct bpf_func_info_aux *sub_aux;int i, ret, new_cnt; +u32 insn_processed;if (!aux->func_info)return 0 ;
@@ -23789,6 +23790,8 @@ static int do_check_subprogs(struct bpf_verifier_env *env)if (!bpf_subprog_is_global (env, i))continue ; + insn_processed = env->insn_processed; + sub_aux =subprog_aux (env, i);if (!sub_aux->called || sub_aux->verified)continue ;
@@ -23796,6 +23799,7 @@ static int do_check_subprogs(struct bpf_verifier_env *env) env->insn_idx = env->subprog_info[i].start;WARN_ON_ONCE (env->insn_idx ==0 ); ret =do_check_common (env, i); + env->subprog_info[i].insn_processed = env->insn_processed - insn_processed;if (ret) {return ret; }else if (env->log.level &BPF_LOG_LEVEL ) {
@@ -23822,10 +23826,12 @@ static int do_check_subprogs(struct bpf_verifier_env *env)static int do_check_main (struct bpf_verifier_env *env) { +u32 insn_processed = env->insn_processed;int ret; env->insn_idx =0 ; ret =do_check_common (env,0 ); + env->subprog_info[0 ].insn_processed = env->insn_processed - insn_processed;if (!ret) env->prog->aux->stack_depth = env->subprog_info[0 ].stack_depth;return ret;
@@ -23834,19 +23840,20 @@ static int do_check_main(struct bpf_verifier_env *env)static void print_verification_stats (struct bpf_verifier_env *env) { -int i; +/* Skip over hidden subprogs which are not verified. */ +int i, subprog_cnt = env->subprog_cnt - env->hidden_subprog_cnt;if (env->log.level &BPF_LOG_STATS ) {verbose (env,"verification time %lld usec\n" ,div_u64 (env->verification_time,1000 )); -verbose (env,"stack depth " ); -for (i =0 ; i < env->subprog_cnt; i++) { -u32 depth = env->subprog_info[i].stack_depth; - -verbose (env,"%d" , depth); -if (i +1 < env->subprog_cnt) -verbose (env,"+" ); - } +verbose (env,"stack depth %d" , env->subprog_info[0 ].stack_depth); +for (i =1 ; i < subprog_cnt; i++) +verbose (env,"+%d" , env->subprog_info[i].stack_depth); +verbose (env,"\n" ); +verbose (env,"insns processed %d" , env->subprog_info[0 ].insn_processed); +for (i =1 ; i < subprog_cnt; i++) +if (bpf_subprog_is_global (env, i)) +verbose (env,"+%d" , env->subprog_info[i].insn_processed);verbose (env,"\n" ); } verbose(env, "processed %d insns (limit %d) max_states_per_insn %d "
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 775/1250 bpf: Report maximum combined stack depth
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Paul Chaignon <[email protected]>
[ Upstream commit f41f34ec64748e16e5a90ab391cec39e30942f32 ]
We've hit the 512 bytes limit on stack depth a few times in Cilium recently. As a result, we started reporting in CI our current maximum stack depth across all configurations for each BPF program.
Unfortunately, that is not trivial to compute in userspace. The verifier reports the stack depths of individual subprogs at the end of the logs. However the maximum combined stack depth also depends on the callgraph of those subprogs (the max combined stack depth is the height of the callgraph weighted by per-subprog stack depths). We can compute a callgraph in userspace from the loaded instructions, but it often doesn't match the verifier's own callgraph because of dead code elimination. Our current approach relies on dumping the BPF_LOG_LEVEL2 logs, but this feels overkill considering the verifier already has the information we need.
The patch lets the verifier dump the maximum combined stack depth in the logs, on the same line as the per-subprog stack depths:
stack depth 16+256 max 272
The per-subprog stack depths and the new max stack depth are not directly comparable. The former is sometimes updated during fixups, while the latter is not. As a result, even with a single subprog, we may end up with two slightly different values. The aim of the new max value is to be closest to what is actually enforced by the verifier.
Stable-dep-of: 369f4ce73457 ("bpf: Check ancestor frames for rbtree callbacks")
--- include/linux/bpf_verifier.h | 2 ++ kernel/bpf/verifier.c | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-)
include/linux/bpf_verifier.h +2 -0
@@ -795,6 +795,8 @@ struct bpf_verifier_env {u32 prev_insn_processed, insn_processed;/* number of jmps, calls, exits analyzed so far */ u32 prev_jmps_processed, jmps_processed; +/* maximum combined stack depth */ +u32 max_stack_depth;/* total verification time */ u64 verification_time;/* maximum number of verifier states kept in 'branching' instructions */
kernel/bpf/verifier.c +5 -1
@@ -6693,6 +6693,8 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx, }if (subprog[idx].priv_stack_mode ==PRIV_STACK_ADAPTIVE ) { +if (subprog_depth > env->max_stack_depth) + env->max_stack_depth = subprog_depth;if (subprog_depth >MAX_BPF_STACK ) {verbose (env,"stack size of subprog %d is %d. Too large\n" , idx, subprog_depth);
@@ -6700,6 +6702,8 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx, } }else { depth += subprog_depth; +if (depth > env->max_stack_depth) + env->max_stack_depth = depth;if (depth >MAX_BPF_STACK ) { total =0 ;for (tmp = idx; tmp >=0 ; tmp = dinfo[tmp].caller)
@@ -23849,7 +23853,7 @@ static void print_verification_stats(struct bpf_verifier_env *env)verbose (env,"stack depth %d" , env->subprog_info[0 ].stack_depth);for (i =1 ; i < subprog_cnt; i++)verbose (env,"+%d" , env->subprog_info[i].stack_depth); -verbose (env,"\n" ); +verbose (env," max %d\n" , env->max_stack_depth);verbose (env,"insns processed %d" , env->subprog_info[0 ].insn_processed);for (i =1 ; i < subprog_cnt; i++)if (bpf_subprog_is_global (env, i))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 776/1250 bpf: Track verifier instruction stats for each subprogram
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit 14c950ac2be8cadb63e1bfe22111ab0fdc829eb8 ]
The verifier currently records one instruction count for the main program and each global subprogram checked independently. Static subprograms are explored within callers, so their verification cost cannot be reported separately.
Track both self and inclusive instruction counts for every subprogram. Charge each processed instruction as self work to the current subprogram and to a path-local subtotal in its function frame. When a function returns, add the callee subtotal to its inclusive count and to its parent subtotal. Fold any remaining frames when a path terminates or is pruned.
Instruction subtotals are accounting state, not semantic verifier state. Clear them when a verifier state is copied so work before a path fork is charged once, rather than again when a saved branch is explored. If copying a saved state fails before all frames are allocated, skip missing frames while folding the current path.
This generic frame accounting also records self and inclusive totals when an asynchronous callback starts as a fresh frame-zero state. It does not yet charge that independently explored callback path back to the main or global exploration root which scheduled it. That will be done in subsequent changes.
This does not change the verification statistics output format. It only prepares the counters for per-subprogram reporting.
Stable-dep-of: 369f4ce73457 ("bpf: Check ancestor frames for rbtree callbacks")
--- include/linux/bpf_verifier.h | 5 +++- kernel/bpf/verifier.c | 55 ++++++++++++++++++++++++++++++------ 2 files changed, 50 insertions(+), 10 deletions(-)
include/linux/bpf_verifier.h +4 -1
@@ -305,6 +305,8 @@ struct bpf_func_state {* | number of simulations is tracked in frame N */ u32 callback_depth; +/* Instructions processed in this frame and callees on the current path. */ +u32 insns_subtotal;/* The following fields should be last. See copy_func_state() */ /* The state of the stack. Each element of the array describes BPF_REG_SIZE
@@ -648,7 +650,8 @@ struct bpf_subprog_info {u32 postorder_start;/* The idx to the env->cfg.insn_postorder */ u16 stack_depth;/* max. stack depth used by this function */ u16 stack_extra; -u32 insn_processed; +u32 insns_total; +u32 insns_self;/* offsets in range [stack_depth .. fastcall_stack_off) * are used for bpf_fastcall spills and fills. */
kernel/bpf/verifier.c +46 -9
127 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 777/1250 bpf: Check ancestor frames for rbtree callbacks
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit 369f4ce734570bdfedaa4b5ca50e2a3f6a892728 ]
bpf_rbtree_add() invokes its comparator while the caller holds the root lock. The native insertion code retains raw parent and link pointers across the callback, so the verifier prohibits unlocking, consuming tree nodes, or changing RCU state from that callback.
in_rbtree_lock_required_cb() only checks the innermost verifier frame. Static subprogram calls are permitted while holding a spin lock, and such a call pushes a frame without in_callback_fn set. Consequently, all callback restrictions disappear in the nested frame. The subprogram can unlock the tree, remove and drop the node being compared, then relock. Native insertion resumes with the stale parent pointer and links freed memory into the tree.
Walk all active frames for the rbtree callback instead. Benign static subprograms remain permitted, while callback restrictions follow execution into nested frames.
--- kernel/bpf/verifier.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-)
kernel/bpf/verifier.c +14 -11
@@ -11153,9 +11153,10 @@ static void account_current_path(struct bpf_verifier_env *env) frame ? state->frame[frame - 1] : NULL); } -/* Are we currently verifying the callback for a rbtree helper that must -* be called with lock held? If so, no need to complain about unreleased -* lock +/* +* Are we currently verifying the callback for an rbtree kfunc that must +* be called with a lock held, or one of that callback's subprogs? If so, +* no need to complain about an unreleased lock. */ static bool in_rbtree_lock_required_cb (struct bpf_verifier_env *env) {
@@ -11163,17 +11164,19 @@ static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env)struct bpf_insn *insn = env->prog->insnsi;struct bpf_func_state *callee;int kfunc_btf_id; +u32 frame; -if (!state->curframe) -return false; - - callee = state->frame[state->curframe]; +for (frame = state->curframe; frame; frame--) { + callee = state->frame[frame]; +if (!callee->in_callback_fn) +continue ; -if (!callee->in_callback_fn) -return false; + kfunc_btf_id = insn[callee->callsite].imm; +if (is_rbtree_lock_required_kfunc (kfunc_btf_id)) +return true; + } - kfunc_btf_id = insn[callee->callsite].imm; -return is_rbtree_lock_required_kfunc (kfunc_btf_id); +return false; } static bool retval_range_within(struct bpf_retval_range range, const struct bpf_reg_state *reg,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 778/1250 bpf: Mark bpf_btf_find_by_name_kind() as sleepable
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit 620614bf7672130c43b3cff375525a2202f61979 ]
When bpf_btf_find_by_name_kind() finds a type in module BTF, it returns a new BTF object fd through __btf_new_fd(). This reaches anon_inode_getfd(), which can sleep while allocating or expanding the current task fd table.
The helper prototype does not set might_sleep, so the verifier allows the helper in non-sleepable contexts such as BPF timer callbacks. The fd allocation can then sleep in softirq context and install the fd into the interrupted task.
Mark the helper as sleepable. This preserves calls from the main body of a sleepable syscall program while rejecting calls from its non-sleepable regions.
--- kernel/bpf/btf.c | 1 + 1 file changed, 1 insertion(+)
kernel/bpf/btf.c +1 -0
@@ -8430,6 +8430,7 @@ BPF_CALL_4(bpf_btf_find_by_name_kind, char *, name, int, name_sz, u32, kind, intconst struct bpf_func_proto bpf_btf_find_by_name_kind_proto = { .func = bpf_btf_find_by_name_kind, .gpl_only = false, + .might_sleep = true, .ret_type =RET_INTEGER , .arg1_type =ARG_PTR_TO_MEM |MEM_RDONLY , .arg2_type =ARG_CONST_SIZE ,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 779/1250 bpf: Mark faultable stack helpers as sleepable
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit 9d02927fdf4e930893c92e35fed01a2704496900 ]
The faultable variants of bpf_get_stack() and bpf_get_task_stack() pass may_fault=true into the common stack collection code. Resolving user-space build IDs may then call build_id_parse_file() and block on filesystem reads.
Neither helper prototype sets might_sleep. Since prototype selection uses the sleepability of the whole program, the verifier can still allow these helpers from a non-sleepable region within that program, such as an explicit RCU or preemption-disabled region. The task-stack helper can also be called from a non-sleepable timer callback of a sleepable program.
Mark both faultable prototypes as sleepable. The existing helper context check then rejects these calls while continuing to allow them in genuinely sleepable contexts.
--- kernel/bpf/stackmap.c | 2 ++ 1 file changed, 2 insertions(+)
kernel/bpf/stackmap.c +2 -0
@@ -738,6 +738,7 @@ BPF_CALL_4(bpf_get_stack_sleepable, struct pt_regs *, regs, void *, buf, u32, siconst struct bpf_func_proto bpf_get_stack_sleepable_proto = { .func = bpf_get_stack_sleepable, .gpl_only = true, + .might_sleep = true, .ret_type =RET_INTEGER , .arg1_type =ARG_PTR_TO_CTX , .arg2_type =ARG_PTR_TO_UNINIT_MEM ,
@@ -788,6 +789,7 @@ BPF_CALL_4(bpf_get_task_stack_sleepable, struct task_struct *, task, void *, bufconst struct bpf_func_proto bpf_get_task_stack_sleepable_proto = { .func = bpf_get_task_stack_sleepable, .gpl_only = false, + .might_sleep = true, .ret_type =RET_INTEGER , .arg1_type =ARG_PTR_TO_BTF_ID , .arg1_btf_id = &btf_tracing_ids[BTF_TRACING_TYPE_TASK ],
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 780/1250 bpf: Reject legacy packet loads from callbacks
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit e7d28823c662128caae63f14e16bd394916c139b ]
check_ld_abs() models a failed BPF_LD_ABS or BPF_LD_IND in a subprogram as an implicit return with R0 set to zero. It calls prepare_func_exit() to explore this synthesized path.
When the load is reached directly from a synchronous callback, prepare_func_exit() enforces the callback return contract and marks R0 precise. R0 is not derived from a real instruction on this path, so precision backtracking reaches the callback call with R0 still requested and triggers the "callback unexpected regs" verifier bug. A privileged program loader can therefore cause a verifier warning and an -EFAULT BPF_PROG_LOAD.
These legacy packet-load instructions are deprecated. Reject them from callbacks rather than complicating their implicit-return model. Check all active frames before constructing the implicit return so nested static subprograms cannot hide the callback context.
Global functions are verified independently with a fresh frame zero, so an active-frame check cannot identify a global function called from a callback. Also check the complete subprogram call graph during stack-depth validation and reject a function containing a legacy load when any caller is a callback. This covers global and static descendants without making has_ld_abs transitive, preserving its per-function BTF return-type check. Ordinary uses outside callbacks remain supported.
--- kernel/bpf/verifier.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
kernel/bpf/verifier.c +17 -0
@@ -6657,6 +6657,15 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,if (!priv_stack_supported) subprog[idx].priv_stack_mode =NO_PRIV_STACK ; process_func: +if (subprog[idx].has_ld_abs) { +for (tmp = idx; tmp >=0 ; tmp = dinfo[tmp].caller) { +if (subprog[tmp].is_cb) { +verbose (env,"cannot use BPF_LD_[ABS|IND] within callback\n" ); +return -EINVAL ; + } + } + } +/* protect against potential stack overflow that might happen when * bpf2bpf calls get combined with tailcalls. Limit the caller's stack * depth for such case down to 256 so that the worst case scenario
@@ -17508,6 +17517,7 @@ static bool may_access_skb(enum bpf_prog_type type)*/ static int check_ld_abs (struct bpf_verifier_env *env,struct bpf_insn *insn) { +struct bpf_verifier_state *state = env->cur_state;struct bpf_reg_state *regs =cur_regs (env);static const int ctx_reg =BPF_REG_6 ;u8 mode =BPF_MODE (insn->code);
@@ -17518,6 +17528,13 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)return -EINVAL ; } +for (i = state->curframe; i; i--) { +if (state->frame[i]->in_callback_fn) { +verbose (env,"cannot use BPF_LD_[ABS|IND] within callback\n" ); +return -EINVAL ; + } + } +if (!env->ops->gen_ld_abs) {verifier_bug (env,"gen_ld_abs is null" );return -EFAULT ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 781/1250 bpf: Dont predict JMP32 pointer vs zero comparisons
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eduard Zingerman <[email protected]>
[ Upstream commit e51179a4e09846f8fd0f26a05068520de2b301bf ]
Consider the following program:
r1 = map_value; /* low 32 bits are zero at runtime */
r6 = 0xdead000000000000;
if w1 != 0 goto l1;
l0: r1 += r6;
r2 = *(u64 *)(r1 + 0);
exit;
l1: r6 = 0;
goto l0;At the moment is_branch_taken() reports the jump as always taken, because it does not distinguish between BPF_JMP and BPF_JMP32 comparisons when processing 'if w1 != 0 ...'.
--- kernel/bpf/verifier.c | 7 +++++++ 1 file changed, 7 insertions(+)
kernel/bpf/verifier.c +7 -0
@@ -16572,6 +16572,13 @@ static int is_branch_taken(struct bpf_reg_state *reg1, struct bpf_reg_state *regif (__is_pointer_value (false, reg1) ||__is_pointer_value (false, reg2)) {u64 val; +/* +* The low 32 bits of a valid pointer may well be zero, hence +* nothing below applies to a 32-bit comparison. +*/ +if (is_jmp32) +return -1 ; +/* arrange that reg2 is a scalar, and reg1 is a pointer */ if (!is_reg_const (reg2, is_jmp32)) { opcode =flip_opcode (opcode);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 782/1250 thermal: sysfs: switch to use scnprintf() to suppress truncation warning
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andy Shevchenko <[email protected]>
[ Upstream commit 5ad0af4f4367202b1bc71813052fe39b5116cdb9 ]
Switch the sysfs code to use scnprintf() to avoid warnings about potential truncation of the names of the sysfs attributes. We can't increase the buffer size because the size is the part of an ABI for some reason. Note, with the current size of buffer the affected attributes have a room for up to 1000 names, which ought to be enough for all cases. There is no functional change, as the same limitation was implied before.
--- drivers/thermal/thermal_sysfs.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/thermal/thermal_sysfs.c +6 -6
@@ -401,8 +401,8 @@ static int create_trip_attrs(struct thermal_zone_device *tz)struct thermal_trip_attrs *trip_attrs = &td->trip_attrs;/* create trip type attribute */ -snprintf (trip_attrs->type.name,THERMAL_NAME_LENGTH , -"trip_point_%d_type" , i); +scnprintf (trip_attrs->type.name,sizeof (trip_attrs->type.name), +"trip_point_%d_type" , i);sysfs_attr_init (&trip_attrs->type.attr.attr); trip_attrs->type.attr.attr.name = trip_attrs->type.name;
@@ -411,8 +411,8 @@ static int create_trip_attrs(struct thermal_zone_device *tz) attrs[i] = &trip_attrs->type.attr.attr;/* create trip temp attribute */ -snprintf (trip_attrs->temp.name,THERMAL_NAME_LENGTH , -"trip_point_%d_temp" , i); +scnprintf (trip_attrs->temp.name,sizeof (trip_attrs->temp.name), +"trip_point_%d_temp" , i);sysfs_attr_init (&trip_attrs->temp.attr.attr); trip_attrs->temp.attr.attr.name = trip_attrs->temp.name;
@@ -424,8 +424,8 @@ static int create_trip_attrs(struct thermal_zone_device *tz) } attrs[i + tz->num_trips] = &trip_attrs->temp.attr.attr; -snprintf (trip_attrs->hyst.name,THERMAL_NAME_LENGTH , -"trip_point_%d_hyst" , i); +scnprintf (trip_attrs->hyst.name,sizeof (trip_attrs->hyst.name), +"trip_point_%d_hyst" , i);sysfs_attr_init (&trip_attrs->hyst.attr.attr); trip_attrs->hyst.attr.attr.name = trip_attrs->hyst.name;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 783/1250 bpf: Require MEM_PERCPU for percpu kptr stores
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit 048029ba1c793f8cabc4ad5eea765da01903f8f1 ]
map_kptr_match_type() treats perm_flags as the set of register type flags that a kptr field permits. Adding MEM_PERCPU to that set for BPF_KPTR_PERCPU does not require the source register to carry it, however. The subset test consequently accepts both a plain bpf_obj_new() allocation and a referenced kernel pointer into a __percpu_kptr map field.
Loads from the field are always marked MEM_PERCPU. Consumers then treat the stored value as the cookie returned by bpf_percpu_obj_new(): per-CPU pointer helpers relocate it, and map teardown selects the per-CPU free path. A plain allocation can therefore provide an arbitrary kernel read/write, while a kernel pointer can be relocated into an invalid address or sent through a missing destructor.
Require the source MEM_PERCPU flag to match the destination field kind. This preserves valid bpf_percpu_obj_new() stores and rejects both the program-BTF and kernel-BTF variants.
--- kernel/bpf/verifier.c | 7 +++++++ 1 file changed, 7 insertions(+)
kernel/bpf/verifier.c +7 -0
@@ -5889,6 +5889,13 @@ static int map_kptr_match_type(struct bpf_verifier_env *env,if (type_flag (reg->type) & ~perm_flags) goto bad_type; +/* +* A BPF_KPTR_PERCPU field is read back as MEM_PERCPU, so the value +* stored in it must carry the same flag. +*/ +if ((kptr_field->type ==BPF_KPTR_PERCPU ) != !!(reg->type &MEM_PERCPU )) + goto bad_type; +/* We need to verify reg->type and reg->btf, before accessing reg->btf */ reg_name =btf_type_name (reg->btf, reg->btf_id);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 784/1250 bpf: Reject untrusted allocated-object pointers
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ning Ding <[email protected]>
[ Upstream commit 7441ee8276641bddaf1cba7bb75ef9c1458ceb3b ]
When the final RCU read-side critical section ends, a local kptr is demoted to PTR_UNTRUSTED but retains MEM_ALLOC. The pointer may be NULL or may refer to an object whose lifetime is no longer protected.
type_is_ptr_alloc_obj() nevertheless recognizes any PTR_TO_BTF_ID with MEM_ALLOC as a live allocated object. In particular, a refcount-only local kptr never carries NON_OWN_REF, so it still passes the bpf_refcount_acquire() argument check after RCU protection ends. The kfunc can then dereference NULL or stale memory.
Make type_is_ptr_alloc_obj() reject PTR_UNTRUSTED pointers. Since type_is_non_owning_ref() is based on the same predicate, graph kfunc arguments obey the same live-object requirement. Fault-protected reads of the demoted pointer remain valid: writes are already rejected, and read fixups use bpf_may_fault_on_deref() rather than this predicate.
--- include/linux/bpf_verifier.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
include/linux/bpf_verifier.h +3 -1
@@ -973,7 +973,9 @@ static inline bool bpf_type_has_unsafe_modifiers(u32 type)static inline bool type_is_ptr_alloc_obj (u32 type) { -return base_type (type) ==PTR_TO_BTF_ID &&type_flag (type) &MEM_ALLOC ; +return base_type (type) ==PTR_TO_BTF_ID && +type_flag (type) &MEM_ALLOC && + !(type_flag (type) &PTR_UNTRUSTED ); }static inline bool type_is_non_owning_ref (u32 type)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 785/1250 tracing: Add boot-time backup of persistent ring buffer
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Masami Hiramatsu (Google) <[email protected]>
[ Upstream commit 20e7168326f5ccab0fc5c322af31ae6200012137 ]
Currently, the persistent ring buffer instance needs to be read before using it. This means we have to wait for boot up user space and dump the persistent ring buffer. However, in that case we can not start tracing on it from the kernel cmdline.
To solve this limitation, this adds an option which allows to create a trace instance as a backup of the persistent ring buffer at boot. If user specifies trace_instance=<BACKUP>=<PERSIST_RB> then the <BACKUP> instance is made as a copy of the <PERSIST_RB> instance.
For example, the below kernel cmdline records all syscalls, scheduler and interrupt events on the persistent ring buffer boot_map but before starting the tracing, it makes a backup instance from the boot_map. Thus, the backup instance has the previous boot events.
'reserve_mem=12M:4M:trace trace_instance=boot_map@trace,syscalls:*,sched:*,irq:* trace_instance=backup=boot_map'
As you can see, this just make a copy of entire reserved area and make a backup instance on it. So you can release (or shrink) the backup instance after use it to save the memory usage.
/sys/kernel/tracing/instances # free
total used free shared buff/cache available
Mem: 1999284 55704 1930520 10132 13060 1914628
Swap: 0 0 0
/sys/kernel/tracing/instances # rmdir backup/
/sys/kernel/tracing/instances # free
total used free shared buff/cache available
Mem: 1999284 40640 1945584 10132 13060 1929692
Swap: 0 0 0
Note: since there is no reason to make a copy of empty buffer, this backup only accepts a persistent ring buffer as the original instance. Also, since this backup is based on vmalloc(), it does not support user-space mmap().
Stable-dep-of: d7dbdd2ee01e ("tracing: Fix to avoid creating trace instances with duplicate names")
--- kernel/trace/trace.c | 63 +++++++++++++++++++++++++++++++++++++++----- kernel/trace/trace.h | 1 + 2 files changed, 58 insertions(+), 6 deletions(-)
kernel/trace/trace.c +57 -6
@@ -8838,8 +8838,8 @@ static int tracing_buffers_mmap(struct file *filp, struct vm_area_struct *vma)struct trace_iterator *iter = &info->iter;int ret =0 ; -/* A memmap'ed buffer is not supported for user space mmap */ -if (iter->tr->flags &TRACE_ARRAY_FL_MEMMAP ) +/* A memmap'ed and backup buffers are not supported for user space mmap */ +if (iter->tr->flags & (TRACE_ARRAY_FL_MEMMAP |TRACE_ARRAY_FL_VMALLOC ))return -ENODEV ; ret =get_snapshot_map (iter->tr);
@@ -10318,6 +10318,8 @@ static int __remove_instance(struct trace_array *tr)reserve_mem_release_by_name (tr->range_name);kfree (tr->range_name); } +if (tr->flags &TRACE_ARRAY_FL_VMALLOC ) +vfree ((void *)tr->range_addr_start);for (i =0 ; i < tr->nr_topts; i++) {kfree (tr->topts[i].topts);
@@ -11118,6 +11120,42 @@ __init static void do_allocate_snapshot(const char *name)static inline void do_allocate_snapshot (const char *name) { }#endif +__init static int backup_instance_area (const char *backup, +unsigned long *addr,phys_addr_t *size) +{ +struct trace_array *backup_tr; +void *allocated_vaddr =NULL ; + + backup_tr =trace_array_get_by_name (backup,NULL ); +if (!backup_tr) { +pr_warn ("Tracing: Instance %s is not found.\n" , backup); +return -ENOENT ; + } + +if (!(backup_tr->flags &TRACE_ARRAY_FL_BOOT )) { +pr_warn ("Tracing: Instance %s is not boot mapped.\n" , backup); +trace_array_put (backup_tr); +return -EINVAL ; + } + + *size = backup_tr->range_addr_size; + + allocated_vaddr =vzalloc (*size); +if (!allocated_vaddr) { +pr_warn ("Tracing: Failed to allocate memory for copying instance %s (size 0x%lx)\n" , + backup, (unsigned long )*size); +trace_array_put (backup_tr); +return -ENOMEM ; + } + +memcpy (allocated_vaddr, + (void *)backup_tr->range_addr_start, (size_t )*size); + *addr = (unsigned long )allocated_vaddr; + +trace_array_put (backup_tr); +return 0 ; +} +__init static void enable_instances (void ) {struct trace_array *tr;
@@ -11140,11 +11178,15 @@ __init static void enable_instances(void)char *flag_delim;char *addr_delim; char *rname __free(kfree) = NULL; +char *backup; tok =strsep (&curr_str,"," ); - flag_delim =strchr (tok,'^' ); - addr_delim =strchr (tok,'@' ); + name =strsep (&tok,"=" ); + backup = tok; + + flag_delim =strchr (name,'^' ); + addr_delim =strchr (name,'@' );if (addr_delim) *addr_delim++ ='\0' ;
@@ -11152,7 +11194,10 @@ __init static void enable_instances(void)if (flag_delim) *flag_delim++ ='\0' ; - name = tok; +if (backup) { +if (backup_instance_area (backup, &addr, &size) <0 ) +continue ; + }if (flag_delim) {char *flag;
@@ -11249,7 +11294,13 @@ __init static void enable_instances(void) tr->ref++; } -if (start) { +/* +* Backup buffers can be freed but need vfree(). +*/ +if (backup) + tr->flags |=TRACE_ARRAY_FL_VMALLOC ; + +if (start || backup) { tr->flags |=TRACE_ARRAY_FL_BOOT |TRACE_ARRAY_FL_LAST_BOOT ; tr->range_name =no_free_ptr (rname); }
kernel/trace/trace.h +1 -0
@@ -451,6 +451,7 @@ enum {TRACE_ARRAY_FL_LAST_BOOT =BIT (2 ),TRACE_ARRAY_FL_MOD_INIT =BIT (3 ),TRACE_ARRAY_FL_MEMMAP =BIT (4 ), + TRACE_ARRAY_FL_VMALLOC = BIT(5), };#ifdef CONFIG_MODULES
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 786/1250 tracing: Fix to avoid creating trace instances with duplicate names
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Masami Hiramatsu (Google) <[email protected]>
[ Upstream commit d7dbdd2ee01e12211046d4a535623ac732b749fb ]
Since commit e645535a954a ("tracing: Add option to use memmapped memory for trace boot instance") changed trace_array_get_by_name() to trace_array_create_systems(), enable_instances() does not reuse the same name instance. Therefore, if an administrator mistakenly specifies multiple trace_instance= options with duplicate names, all are created but only the first is accessible via tracefs.
Check whether an instance with the same name already exists before creating a new one, and reject duplicates with a warning.
--- kernel/trace/trace.c | 5 +++++ 1 file changed, 5 insertions(+)
kernel/trace/trace.c +5 -0
@@ -11194,6 +11194,11 @@ __init static void enable_instances(void)if (flag_delim) *flag_delim++ ='\0' ; +if (trace_array_find (name)) { +pr_warn ("Tracing: Instance %s already exists\n" , name); +continue ; + } +if (backup) {if (backup_instance_area (backup, &addr, &size) <0 )continue ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 787/1250 selftests/bpf: Fix flaky bpf_nf test when random NAT port is 0
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiayuan Chen <[email protected]>
[ Upstream commit 5e8c349bc8d790fe031a4332e502f5d4f9878644 ]
The bpf_nf test allocs a ct, sets snat and dnat with random addr and port via bpf_ct_set_nat_info(), then looks the ct up and checks the reply tuple against what was set.
The port comes from bpf_get_prandom_u32() and can be 0. For bpf_ct_set_nat_info(), port 0 means "port not specified", so only the addr is mapped and the kernel keeps the original port. The check then compares that port with 0 and fails, which shows up as a flaky "Test for source natting" failure in CI [1][2].
Keep the random port in 1..65535 so it is always specified.
[1] https://github.com/kernel-patches/bpf/actions/runs/33830002889/job/100893868791
[2] https://github.com/kernel-patches/bpf/actions/runs/33829976794/job/100893220999
--- tools/testing/selftests/bpf/progs/test_bpf_nf.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
tools/testing/selftests/bpf/progs/test_bpf_nf.c +4 -4
@@ -171,8 +171,8 @@ nf_ct_test(struct nf_conn *(*lookup_fn)(void *, struct bpf_sock_tuple *, u32, ct =alloc_fn (ctx, &bpf_tuple,sizeof (bpf_tuple.ipv4), &opts_def,sizeof (opts_def));if (ct) { -__u16 sport =bpf_get_prandom_u32 (); -__u16 dport =bpf_get_prandom_u32 (); +__u16 sport =bpf_get_prandom_u32 () %65535 +1 ; +__u16 dport =bpf_get_prandom_u32 () %65535 +1 ;union nf_inet_addr saddr = {};union nf_inet_addr daddr = {};struct nf_conn *ct_ins;
@@ -274,8 +274,8 @@ nf_ct_opts_new_test(struct nf_conn *(*lookup_fn)(void *, struct bpf_sock_tuple * ct =alloc_fn (ctx, &bpf_tuple,sizeof (bpf_tuple.ipv4), &opts_def,sizeof (opts_def));if (ct) { -__u16 sport =bpf_get_prandom_u32 (); -__u16 dport =bpf_get_prandom_u32 (); +__u16 sport =bpf_get_prandom_u32 () %65535 +1 ; +__u16 dport =bpf_get_prandom_u32 () %65535 +1 ;union nf_inet_addr saddr = {};union nf_inet_addr daddr = {};struct nf_conn *ct_ins;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 788/1250 nexthop: Initialize extack in remove_nh_grp_entry()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ido Schimmel <[email protected]>
[ Upstream commit 5bd9e4e7cdaa03879e9b73b12ab52cceb1edd55b ]
remove_nh_grp_entry() prints the extack message when a listener fails to replace the reduced nexthop group. However, extack is not initialized and listeners are not required to set a message when returning an error. Neither netdevsim nor mlxsw do so when an allocation fails, resulting in the dereference of an uninitialized stack pointer.
Fix by zero-initializing extack, as was done in commit 6347c5314cee ("nexthop: initialize extack in nh_res_bucket_migrate()").
--- net/ipv4/nexthop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/ipv4/nexthop.c +1 -1
@@ -2025,7 +2025,7 @@ static void remove_nh_grp_entry(struct net *net, struct nh_grp_entry *nhge, {struct nh_grp_entry *nhges, *new_nhges;struct nexthop *nhp = nhge->nh_parent; -struct netlink_ext_ack extack; +struct netlink_ext_ack extack = {};struct nexthop *nh = nhge->nh;struct nh_group *nhg, *newg;int i, j, err;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 789/1250 bonding: use skb_cow_head() in bond_do_alb_xmit() and rlb_arp_xmit()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 1746ef2e2df2ad71c66eca56364d56bde284523b ]
In bond_do_alb_xmit() and rlb_arp_xmit(), make sure to unclone skb head via skb_cow_head() before modifying the source MAC address (Ethernet header and ARP payload) to avoid silent corruption if the skb is shared or cloned. Avoid caching the header pointers across skb_cow_head().
In rlb_arp_xmit(), only modify arp->mac_src if it differs from tx_slave->dev->dev_addr to avoid an unnecessary copy and head reallocation.
Also, we should not assume mac header is set in output path.
Use skb_eth_hdr() instead of eth_hdr() to fix the issue, and remove now redundant skb_reset_mac_header() calls.
--- drivers/net/bonding/bond_alb.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-)
drivers/net/bonding/bond_alb.c +12 -7
@@ -678,9 +678,15 @@ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond)if (arp->op_code ==htons (ARPOP_REPLY )) {/* the arp must be sent on the selected rx channel */ tx_slave =rlb_choose_channel (skb, bond, arp); -if (tx_slave) +if (tx_slave && + !ether_addr_equal_64bits (arp->mac_src, + tx_slave->dev->dev_addr)) { +if (unlikely (skb_cow_head (skb,0 ))) +return NULL ; + arp = (struct arp_pkt *)skb_network_header (skb);bond_hw_addr_copy (arp->mac_src, tx_slave->dev->dev_addr, tx_slave->dev->addr_len); + }netdev_dbg (bond->dev,"(slave %s): Server sent ARP Reply packet\n" , tx_slave ? tx_slave->dev->name :"NULL" ); }else if (arp->op_code ==htons (ARPOP_REQUEST )) {
@@ -1340,7 +1346,6 @@ static netdev_tx_t bond_do_alb_xmit(struct sk_buff *skb, struct bonding *bond, struct slave *tx_slave) {struct alb_bond_info *bond_info = &(BOND_ALB_INFO (bond)); -struct ethhdr *eth_data =eth_hdr (skb);if (!tx_slave) {/* unbalanced or unassigned, send through primary */
@@ -1351,7 +1356,9 @@ static netdev_tx_t bond_do_alb_xmit(struct sk_buff *skb, struct bonding *bond,if (tx_slave &&bond_slave_can_tx (tx_slave)) {if (tx_slave !=rcu_access_pointer (bond->curr_active_slave)) { -ether_addr_copy (eth_data->h_source, +if (unlikely (skb_cow_head (skb,0 ))) +return bond_tx_drop (bond->dev, skb); +ether_addr_copy (skb_eth_hdr (skb)->h_source, tx_slave->dev->dev_addr); }
@@ -1375,8 +1382,7 @@ struct slave *bond_xmit_tlb_slave_get(struct bonding *bond,struct ethhdr *eth_data;u32 hash_index; -skb_reset_mac_header (skb); - eth_data =eth_hdr (skb); + eth_data =skb_eth_hdr (skb);/* Do not TX balance any multicast or broadcast */ if (!is_multicast_ether_addr (eth_data->h_dest)) {
@@ -1428,8 +1434,7 @@ struct slave *bond_xmit_alb_slave_get(struct bonding *bond,u32 hash_index =0 ;int hash_size =0 ; -skb_reset_mac_header (skb); - eth_data =eth_hdr (skb); + eth_data =skb_eth_hdr (skb);switch (ntohs (skb->protocol)) {case ETH_P_IP : {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 790/1250 net: dsa: bcm_sf2: bound the CFP rule dump by the callers buffer size
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit cdb719f4b8596d9ccee2d56d204c2c4dce982f46 ]
bcm_sf2_cfp_rule_get_all() walks the whole cfp.unique bitmap into rule_locs[] without consulting nfc->rule_cnt, which is how many entries the caller had room for. ETHTOOL_GRXCLSRLALL requires no CAP_NET_ADMIN and the ioctl sizes the buffer from the rule_cnt userspace passes in, so once an admin has installed CFP rules any user can ask for fewer slots than there are rules and run off the end of the allocation. A rule_cnt of 0 leaves the buffer pointer NULL and the walk dereferences it.
--- drivers/net/dsa/bcm_sf2_cfp.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/net/dsa/bcm_sf2_cfp.c +2 -0
@@ -1088,6 +1088,8 @@ static int bcm_sf2_cfp_rule_get_all(struct bcm_sf2_priv *priv,unsigned int index =1 , rules_cnt =0 ;for_each_set_bit_from (index, priv->cfp.unique, priv->num_cfp_rules) { +if (rules_cnt == nfc->rule_cnt) +return -EMSGSIZE ; rule_locs[rules_cnt] = index; rules_cnt++; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 791/1250 eth: nfp: bound the ntuple rule dump by the callers buffer size
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit f1986bf87b0709c95126fe196cf39e5b8c8453a1 ]
nfp_net_get_fs_loc() dumps every entry of nn->fs.list into rule_locs[] without consulting cmd->rule_cnt, which is how many entries the caller had room for. ETHTOOL_GRXCLSRLALL requires no CAP_NET_ADMIN and the ioctl sizes the buffer from the rule_cnt userspace passes in, so once an admin has installed flow steering rules any user can ask for fewer slots than there are rules and run off the end of the allocation. A rule_cnt of 0 leaves the buffer pointer NULL and the walk dereferences it.
Bail out with -EMSGSIZE when the buffer fills up, the way the other ntuple capable drivers do, and report how many locations were filled so a shrinking rule list does not leave the caller reading stale slots.
--- drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +8 -3
@@ -1421,7 +1421,8 @@ static int nfp_net_get_fs_rule(struct nfp_net *nn, struct ethtool_rxnfc *cmd)return -ENOENT ; } -static int nfp_net_get_fs_loc (struct nfp_net *nn,u32 *rule_locs) +static int nfp_net_get_fs_loc (struct nfp_net *nn,struct ethtool_rxnfc *cmd, +u32 *rule_locs) {struct nfp_fs_entry *entry;u32 count =0 ;
@@ -1429,8 +1430,12 @@ static int nfp_net_get_fs_loc(struct nfp_net *nn, u32 *rule_locs)if (!(nn->cap_w1 &NFP_NET_CFG_CTRL_FLOW_STEER ))return -EOPNOTSUPP ; -list_for_each_entry (entry, &nn->fs.list, node) +list_for_each_entry (entry, &nn->fs.list, node) { +if (count == cmd->rule_cnt) +return -EMSGSIZE ; rule_locs[count++] = entry->loc; + } + cmd->rule_cnt = count;return 0 ; }
@@ -1451,7 +1456,7 @@ static int nfp_net_get_rxnfc(struct net_device *netdev,return nfp_net_get_fs_rule (nn, cmd);case ETHTOOL_GRXCLSRLALL : cmd->data =NFP_FS_MAX_ENTRY ; -return nfp_net_get_fs_loc (nn, rule_locs); +return nfp_net_get_fs_loc (nn, cmd, rule_locs);default :return -EOPNOTSUPP ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 792/1250 eth: nfp: drop the replaced rule from the list when reprogramming fails
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit 108bb2142e3a12c9ad625ad662973127a113ddc6 ]
nfp_net_fs_add() replaces an existing rule by deleting it from the hardware, decrementing nn->fs.count and programming the new one. If nfp_net_fs_add_hw() fails the old entry stays on nn->fs.list - only the success path reaches list_replace() - so the list is one longer than nn->fs.count, and it advertises a rule whose hardware entry has already been torn down.
nn->fs.count is what ETHTOOL_GRXCLSRLCNT reports, so userspace then sizes its buffer one entry short of what the GRXCLSRLALL walk wants to write. That used to overwrite one u32 past the allocation; since the walk is bounded it is a permanent -EMSGSIZE instead, as nothing ever resyncs the counter.
--- drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +7 -1
@@ -1699,8 +1699,14 @@ static int nfp_net_fs_add(struct nfp_net *nn, struct ethtool_rxnfc *cmd) nn->fs.count--; err =nfp_net_fs_add_hw (nn, new); -if (err) +if (err) { +/* mbox broken, adding the old rule back will +* likely also fail. +*/ +list_del (&entry->node); +kfree (entry); goto err; + } nn->fs.count++;list_replace (&entry->node, &new->node);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 793/1250 net: dsa: mv88e6xxx: bound the policy rule dump by the callers buffer size
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit b1fffc273112e7284c5b705e186b43b5770cd3d5 ]
mv88e6xxx_get_rxnfc() uses rxnfc->rule_cnt as the write index while dumping the policy IDR, clobbering the input value before it has been looked at. That input is the number of entries the caller had room for. ETHTOOL_GRXCLSRLALL requires no CAP_NET_ADMIN and the ioctl sizes the buffer from the rule_cnt userspace passes in, so once an admin has installed policy rules any user can ask for fewer slots than there are rules and run off the end of the allocation. A rule_cnt of 0 leaves the buffer pointer NULL and the walk dereferences it.
Count into a local so the caller's limit survives the walk, and stop with -EMSGSIZE once it is reached.
--- drivers/net/dsa/mv88e6xxx/chip.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-)
drivers/net/dsa/mv88e6xxx/chip.c +12 -4
@@ -2439,6 +2439,7 @@ static int mv88e6xxx_get_rxnfc(struct dsa_switch *ds, int port,struct ethtool_rx_flow_spec *fs = &rxnfc->fs;struct mv88e6xxx_chip *chip = ds->priv;struct mv88e6xxx_policy *policy; +u32 cnt =0 ;int err;int id;
@@ -2464,11 +2465,18 @@ static int mv88e6xxx_get_rxnfc(struct dsa_switch *ds, int port,break ;case ETHTOOL_GRXCLSRLALL : rxnfc->data =0 ; - rxnfc->rule_cnt =0 ; -idr_for_each_entry (&chip->policies, policy, id) -if (policy->port == port) - rule_locs[rxnfc->rule_cnt++] = id; err =0 ; +idr_for_each_entry (&chip->policies, policy, id) { +if (policy->port != port) +continue ; +if (cnt == rxnfc->rule_cnt) { + err = -EMSGSIZE ; +break ; + } + rule_locs[cnt++] = id; + } +if (!err) + rxnfc->rule_cnt = cnt;break ;default : err = -EOPNOTSUPP ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 794/1250 net: bridge: mcast: properly convert mglist to rcu
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nikolay Aleksandrov <[email protected]>
[ Upstream commit 4b772869a1e5f9da5cef5b9c722ec0aa424ee0a0 ]
Sashiko reported a bug [1] that br_multicast_del_port_group unlists the port group not using proper rcu helper that preserves the next pointer and after that immediately frees the port group without waiting for rcu grace period. The only rcu walker of mglist is br_multicast_list_adjacent() and it turns out that function has always been buggy because mglist was never properly converted to RCU. Fix it by converting it to rcu and moving its initialization after eth_addr's. Initializing p->next can use RCU_INIT_POINTER because we have a barrier from the hlist_add_head_rcu call later, besides we're initializing an unpublished structure anyway.
[1] https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260826014200.362304-1-littleddfu%40gmail.com
--- net/bridge/br_multicast.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
net/bridge/br_multicast.c +5 -4
@@ -1437,16 +1437,17 @@ struct net_bridge_port_group *br_multicast_new_port_group( goto free_out; } -rcu_assign_pointer (p->next, next);timer_setup (&p->timer, br_multicast_port_group_expired,0 );timer_setup (&p->rexmit_timer, br_multicast_port_group_rexmit,0 ); -hlist_add_head (&p->mglist, &port->mglist);if (src)memcpy (p->eth_addr, src,ETH_ALEN );else eth_broadcast_addr (p->eth_addr); +RCU_INIT_POINTER (p->next, next); +hlist_add_head_rcu (&p->mglist, &port->mglist); +return p; free_out:
@@ -1461,11 +1462,11 @@ void br_multicast_del_port_group(struct net_bridge_port_group *p)struct net_bridge_port *port = p->key.port;__u16 vid = p->key.addr.vid; -hlist_del_init (&p->mglist); +hlist_del_init_rcu (&p->mglist);if (!br_multicast_is_star_g (&p->key.addr))rhashtable_remove_fast (&port->br->sg_port_tbl, &p->rhnode, br_sg_port_rht_params); -kfree (p); +kfree_rcu (p, rcu);br_multicast_port_ngroups_dec (port, vid); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 795/1250 octeontx2-af: mcs: Clear stale X2P calibration state before calibration
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Viswajith Murali <[email protected]>
[ Upstream commit 1f29543126dde307e8b5fb6a740c54e59deaa2ff ]
Some firmware versions leave MCSX_MIL_GLOBAL bit 5 set on boot. If the bit is already set when the driver attempts X2P calibration, the hardware sees no rising edge and calibration never triggers. Clear the bit and wait briefly before starting calibration to ensure a clean rising edge.
--- drivers/net/ethernet/marvell/octeontx2/af/mcs.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
drivers/net/ethernet/marvell/octeontx2/af/mcs.c +10 -0
@@ -1417,6 +1417,16 @@ static int mcs_x2p_calibration(struct mcs *mcs)int i, err =0 ;u64 val; +/* Clear any stale calibration state left by firmware/bootloader. +* Some firmware versions may leave MCSX_MIL_GLOBAL bit 5 set, +* preventing the hardware from detecting the rising edge needed to +* trigger X2P calibration. +*/ + val =mcs_reg_read (mcs,MCSX_MIL_GLOBAL ); + val &= ~BIT_ULL (5 ); +mcs_reg_write (mcs,MCSX_MIL_GLOBAL , val); +usleep_range (100 ,200 ); +/* set X2P calibration */ val =mcs_reg_read (mcs,MCSX_MIL_GLOBAL ); val |=BIT_ULL (5 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 796/1250 ionic: use netif_txq_maybe_stop() in ionic_tx()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nikhil P. Rao <[email protected]>
[ Upstream commit c91b4d6e5cc30ceea3f23ebe29aec012709a065f ]
Commit 061b9bedbef1 ("ionic: Rework Tx start/stop flow") replaced ionic_maybe_stop_tx() with netif_txq_maybe_stop() to get the memory barriers around the stop/start bits right, but did not cover the stop in ionic_tx() added by commit 138506ab249b ("ionic: Check stop no restart"). Convert the remaining site.
netif_txq_maybe_stop() requires the ring indexes to be updated before it is invoked, so the post has to come first. But ring_dbell comes from __netdev_tx_sent_queue(), which runs after that and reads the stop bit, so it is not known in time to pass to ionic_txq_post(). Post without the doorbell and ring it separately.
The stop condition is unchanged. The re-check only clears the stop bit when space has become available, so the doorbell starvation fixed by commit 138506ab249b ("ionic: Check stop no restart") cannot recur.
--- drivers/net/ethernet/pensando/ionic/ionic_txrx.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
drivers/net/ethernet/pensando/ionic/ionic_txrx.c +10 -3
@@ -1668,15 +1668,22 @@ static int ionic_tx(struct net_device *netdev, struct ionic_queue *q, stats->pkts++; stats->bytes += skb->len; +ionic_txq_post (q, false); +if (likely (!ionic_txq_hwstamp_enabled (q))) {struct netdev_queue *ndq =q_to_ndq (netdev, q); -if (unlikely (!ionic_q_has_space (q,MAX_SKB_FRAGS +1 ))) -netif_tx_stop_queue (ndq); +netif_txq_maybe_stop (ndq,ionic_q_space_avail (q), +MAX_SKB_FRAGS +1 ,MAX_SKB_FRAGS +1 ); ring_dbell =__netdev_tx_sent_queue (ndq, skb->len,netdev_xmit_more ()); } -ionic_txq_post (q, ring_dbell); + +if (ring_dbell) { +ionic_dbell_ring (q->lif->kern_dbpage, q->hw_type, + q->dbval | q->head_idx); + q->dbell_jiffies = jiffies; + }return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 797/1250 net: usb: cx82310_eth: drop URB after 0xffff reboot sentinel to prevent partial_data heap overflow
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jason Winter <[email protected]>
[ Upstream commit 5d50e90add8b4a978395e893e81954d19d58a7c5 ]
The 0xffff length sentinel detects a router reboot and schedules re-enabling of ethernet mode, but then falls through to the rest of the loop body. The next check is
} else if (len > CX82310_MTU) {which is the else of the just-matched if -- it never fires for len == 0xffff. The MTU bound that normally caps the incomplete-packet save path is silently bypassed.
With 0xffff > skb->len always true (rx_urb_size is 4096), the incomplete-packet branch saves dev->partial_len = skb->len bytes into dev->partial_data. partial_data is kmalloc(hard_mtu) = kmalloc(CX82310_MTU + 2) = 1516 bytes, but skb->len after the 2-byte header pull can be up to 4094. A device that sends a 4096-byte URB starting with [0xff 0xff] therefore copies 4094 device-provided bytes into a buffer allocated for 1516 bytes, exceeding its requested size by 2578 bytes.
The next URB then reads dev->partial_len (4094) back from the same 1516-byte buffer and dev->partial_rem (65535 - 4094 = 61441) from the new URB's ~4KB skb, both well past their allocations, and delivers the spliced result as a 64KB "frame" to the network stack.
Bail out of rx_fixup after scheduling the re-enable work; the remainder of a reboot-marker URB is not meaningful packet data. This restores the invariant that partial_len < CX82310_MTU + 2 on the save path, since every other route there has already passed the MTU check.
--- drivers/net/usb/cx82310_eth.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/usb/cx82310_eth.c +1 -0
@@ -282,6 +282,7 @@ static int cx82310_rx_fixup(struct usbnet *dev, struct sk_buff *skb)if (len ==0xffff ) {netdev_info (dev->net,"router was rebooted, re-enabling ethernet mode" );schedule_work (&priv->reenable_work); +return 0 ; }else if (len >CX82310_MTU ) {netdev_err (dev->net,"RX packet too long: %d B\n" , len);return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 798/1250 dibs: Remove reset of static vars in dibs_init()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexandra Winter <[email protected]>
[ Upstream commit 182663bbff784e88cf7acbae7c53c6c5e0cb9ca4 ]
'clients' and 'max_client' are static variables and therefore don't need to be initialized.
Stable-dep-of: 1668a31e3b1a ("dibs: Unregister dibs_class after error")
--- drivers/dibs/dibs_main.c | 3 --- 1 file changed, 3 deletions(-)
drivers/dibs/dibs_main.c +0 -3
@@ -248,9 +248,6 @@ static int __init dibs_init(void) {int rc; -memset (clients,0 ,sizeof (clients)); - max_client =0 ; - dibs_class =class_create ("dibs" );if (IS_ERR (dibs_class))return PTR_ERR (dibs_class);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 799/1250 dibs: change dibs_class to a const struct
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jori Koolstra <[email protected]>
[ Upstream commit ad3dfa80be765757f612da04318248f6d20e4f71 ]
The class_create() call has been deprecated in favor of class_register() as the driver core now allows for a struct class to be in read-only memory. Change dibs_class to be a const struct class and drop the class_create() call.
Stable-dep-of: 1668a31e3b1a ("dibs: Unregister dibs_class after error")
--- drivers/dibs/dibs_main.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
drivers/dibs/dibs_main.c +8 -6
@@ -20,7 +20,9 @@MODULE_DESCRIPTION ("Direct Internal Buffer Sharing class" );MODULE_LICENSE ("GPL" ); -static struct class *dibs_class; +static const struct class dibs_class = { + .name ="dibs" , +};/* use an array rather a list for fast mapping: */ static struct dibs_client *clients[MAX_DIBS_CLIENTS ];
@@ -140,7 +142,7 @@ struct dibs_dev *dibs_dev_alloc(void)return dibs;spin_lock_init (&dibs->lock); dibs->dev.release = dibs_dev_release; - dibs->dev.class = dibs_class; + dibs->dev.class = &dibs_class;device_initialize (&dibs->dev);return dibs;
@@ -248,9 +250,9 @@ static int __init dibs_init(void) {int rc; - dibs_class =class_create ("dibs" ); -if (IS_ERR (dibs_class)) -return PTR_ERR (dibs_class); + rc =class_register (&dibs_class); +if (rc) +return rc; rc =dibs_loopback_init ();if (rc)
@@ -262,7 +264,7 @@ static int __init dibs_init(void) static void __exit dibs_exit(void) {dibs_loopback_exit (); -class_destroy (dibs_class); +class_unregister (&dibs_class); }module_init (dibs_init);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 800/1250 dibs: Unregister dibs_class after error
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexandra Winter <[email protected]>
[ Upstream commit 1668a31e3b1ad358d981ddb6dbd3db1fe0533621 ]
In case dibs_loopback_init() fails, e.g. because of -ENOMEM, dibs_init() must unregister dibs_class. Otherwise dibs_class and /sys/class/dibs exist even though the functionality is not available. A retry to load the module fails with -EEXIST.
Unregister dibs_class in the error path of dibs_init.
Note that before
commit ad3dfa80be76 ("dibs: change dibs_class to a const struct") class_destroy(dibs_class) is required instead of class_unregister(&dibs_class).
--- drivers/dibs/dibs_main.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
drivers/dibs/dibs_main.c +8 -2
@@ -252,13 +252,19 @@ static int __init dibs_init(void) rc =class_register (&dibs_class);if (rc) -return rc; + goto err; rc =dibs_loopback_init ();if (rc) -pr_err ("%s fails with %d\n" , __func__, rc); + goto err_unregister;return rc; + +err_unregister: +class_unregister (&dibs_class); +err: +pr_err ("%s fails with %d\n" , __func__, rc); +return rc; } static void __exit dibs_exit(void)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 801/1250 s390/ism: folio_put() after error
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexandra Winter <[email protected]>
[ Upstream commit 907a56ab3eb8a58500a58daa76087f17bb2b6826 ]
dmb->cpu_addr was allocated via folio_alloc(). Use folio_put() instead of kfree() in the error exit of ism_alloc_dmb() to avoid slab allocator corruption.
While at it, reset dmb->cpu_addr after folio_put to avoid unintentional UAF by future callers.
--- drivers/s390/net/ism_drv.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/s390/net/ism_drv.c +3 -1
@@ -232,6 +232,7 @@ static void ism_free_dmb(struct ism_dev *ism, struct dibs_dmb *dmb)dma_unmap_page (&ism->pdev->dev, dmb->dma_addr, dmb->dmb_len,DMA_FROM_DEVICE );folio_put (virt_to_folio (dmb->cpu_addr)); + dmb->cpu_addr =NULL ; }static int ism_alloc_dmb (struct ism_dev *ism,struct dibs_dmb *dmb)
@@ -275,7 +276,8 @@ static int ism_alloc_dmb(struct ism_dev *ism, struct dibs_dmb *dmb)return 0 ; out_free: -kfree (dmb->cpu_addr); +folio_put (folio); + dmb->cpu_addr =NULL ; out_bit:clear_bit (dmb->idx, ism->sba_bitmap);return rc;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 802/1250 vxlan: reject dynamic fdb entries that reference a nexthop id
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Seungwon Bae <[email protected]>
[ Upstream commit 98fc57d167446b95b4e719815fe79edef93f8e7a ]
The commit cited in the Fixes tag allowed VXLAN FDB entries to point to FDB nexthops so that overlay traffic could be load balanced across multiple VTEPs. Such entries can only be configured from user space, cannot be learned and cannot roam. They only make sense with a user space control plane such as E-VPN where data plane learning is disabled.
Despite that, the VXLAN driver does not currently prevent such entries from being configured with the "dynamic" flag. The per-nexthop FDB list is only protected by the per-device hash lock, which is not sufficient when two VXLAN devices point to the same FDB nexthop and therefore share the list. Aging runs in softirq context without RTNL, so an entry deleted by one device can race with an addition or deletion from the other, leading to list corruption:
list_del corruption. next->prev should be ffff8881069d9548, but was dead000000000122. (next=ffff8881069d9448)
WARNING: CPU: 0 PID: 90 at lib/list_debug.c:65 __list_del_entry_valid_or_report+0x1aa/0x210 ... vxlan_fdb_destroy+0x5b8/0xad0 vxlan_cleanup+0x328/0x450 call_timer_fn+0x2a/0x1c0 run_timer_softirq+0x18c/0x210 BUG: KASAN: slab-use-after-free in vxlan_fdb_destroy
Fix this by rejecting the bogus configuration of dynamic FDB entries that point to FDB nexthops, both when created and when an existing entry is updated. As such, the per-nexthop FDB list is only ever mutated under the RTNL lock. Add test cases to make sure that this does not regress in the future.
--- drivers/net/vxlan/vxlan_core.c | 11 ++++++++ tools/testing/selftests/net/fib_nexthops.sh | 28 +++++++++++++++++++++ 2 files changed, 39 insertions(+)
drivers/net/vxlan/vxlan_core.c +11 -0
@@ -997,6 +997,12 @@ static int vxlan_fdb_update_existing(struct vxlan_dev *vxlan,return -EOPNOTSUPP ; } +if (rcu_access_pointer (f->nh) && + !(state & (NUD_PERMANENT |NUD_NOARP ))) { +NL_SET_ERR_MSG (extack,"Cannot make a nexthop fdb dynamic" ); +return -EOPNOTSUPP ; + } +/* Do not allow an externally learned entry to take over an entry added * by the user. */
@@ -1258,6 +1264,11 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],if (err)return err; +if (nhid && !(ndm->ndm_state & (NUD_PERMANENT |NUD_NOARP ))) { +NL_SET_ERR_MSG (extack,"A nexthop fdb cannot be dynamic" ); +return -EINVAL ; + } +if (vxlan->default_dst.remote_ip.sa.sa_family != ip.sa.sa_family)return -EAFNOSUPPORT ;
tools/testing/selftests/net/fib_nexthops.sh +28 -0
@@ -522,6 +522,20 @@ ipv6_fdb_grp_fcnal() run_cmd "$BRIDGE fdb add 02:02:00:00:00:14 dev vx10 nhid 61 self" log_test $? 255 "Fdb mac add with nexthop" + # fdb entries with a nexthop group cannot be aged out + run_cmd "$BRIDGE fdb add 02:02:00:00:00:15 dev vx10 nhid 102 self static" + log_test $? 0 "Fdb mac add with nexthop group and static state" + + run_cmd "$BRIDGE fdb add 02:02:00:00:00:16 dev vx10 nhid 102 self dynamic" + log_test $? 255 "Fdb mac add with nexthop group and dynamic state" + + run_cmd "$BRIDGE fdb add 02:02:00:00:00:17 dev vx10 nhid 102 self" + run_cmd "$BRIDGE fdb replace 02:02:00:00:00:17 dev vx10 dst 2001:db8:91::11 self dynamic" + log_test $? 255 "Fdb mac replace with nexthop group and dynamic state" + + run_cmd "$BRIDGE fdb append 02:02:00:00:00:17 dev vx10 dst 2001:db8:91::11 self dynamic" + log_test $? 255 "Fdb mac append with nexthop group and dynamic state" + run_cmd "$IP -6 ro add 2001:db8:101::1/128 nhid 66" log_test $? 2 "Route add with fdb nexthop"
@@ -622,6 +636,20 @@ ipv4_fdb_grp_fcnal() run_cmd "$BRIDGE fdb add 02:02:00:00:00:14 dev vx10 nhid 12 self" log_test $? 255 "Fdb mac add with nexthop" + # fdb entries with a nexthop group cannot be aged out + run_cmd "$BRIDGE fdb add 02:02:00:00:00:15 dev vx10 nhid 102 self static" + log_test $? 0 "Fdb mac add with nexthop group and static state" + + run_cmd "$BRIDGE fdb add 02:02:00:00:00:16 dev vx10 nhid 102 self dynamic" + log_test $? 255 "Fdb mac add with nexthop group and dynamic state" + + run_cmd "$BRIDGE fdb add 02:02:00:00:00:17 dev vx10 nhid 102 self" + run_cmd "$BRIDGE fdb replace 02:02:00:00:00:17 dev vx10 dst 10.0.0.3 self dynamic" + log_test $? 255 "Fdb mac replace with nexthop group and dynamic state" + + run_cmd "$BRIDGE fdb append 02:02:00:00:00:17 dev vx10 dst 10.0.0.3 self dynamic" + log_test $? 255 "Fdb mac append with nexthop group and dynamic state" + run_cmd "$IP ro add 172.16.0.0/22 nhid 16" log_test $? 2 "Route add with fdb nexthop"
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 803/1250 net: cap tx_queue_len at S16_MAX to prevent oversized ring allocations
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit 66ab4c59b74db7ab53a1c9083feaaede393a96a0 ]
Several subsystems allocate ring buffers sized by dev->tx_queue_len with no upper bound. An unprivileged user (via unshare -Urn) can set a huge tx_queue_len and exhaust global memory with ring allocations:
- pfifo_fast: pfifo_fast_init() and pfifo_fast_change_tx_queue_len() allocate 3 skb_array rings of tx_queue_len entries each.
- tun: tun_queue_resize() and the queue-attach path resize ptr_rings to tx_queue_len on the NETDEV_CHANGE_TX_QUEUE_LEN notifier.
- tap (macvtap/ipvtap): tap_queue_resize() and tap_init() resize/init ptr_rings to tx_queue_len on the same notifier.
netif_change_tx_queue_len() is the single entry point for IFLA_TXQLEN, sysfs, and the SIOCSIFTXQLEN ioctl. Cap new_len at S16_MAX (32767) there so the oversized value is rejected at set time. This takes effect whether the device is up or down, before dev->tx_queue_len is written, before any notifier fires, and before any ring is allocated. The "> S16_MAX" check also subsumes the previous unsigned-long truncation test, and a negative ifr_qlen from the ioctl lands far above the cap after conversion, so both old failure modes are covered by the one comparison.
tx_queue_len is ambigious: both a per-ring sizing multiplier and a default queue-length/limit knob for consumers that allocate nothing at set time (pfifo/bfifo/gred/plug/sfb limits, htb direct_qlen, qfq max_classes, teql). 32767 is chosen as the largest value NLA_POLICY_FULL_RANGE can express for the u32 IFLA_TXQLEN policy in patch 2/3 while staying a legitimate queue length on high-BDP paths; the ring-memory trade-off of a shared knob is disclosed below.
Conditions to recreate the bug:
- CONFIG_NET_SCHED=y, CONFIG_VETH=y, CONFIG_USER_NS=y, CONFIG_NET_NS=y.
- Unprivileged user in a fresh user+net namespace (unshare -Urn).
- pfifo_fast: create veth pairs, set tx_queue_len to 500000, attach mq+pfifo_fast. ~28 iterations OOMs a 2GB guest.
- tun: create 50 tun devices with IFF_MULTI_QUEUE, set tx_queue_len to 500000, open 8 queues each. ~1.6GB of ptr_ring allocations OOMs a 512MB guest.
- tap: same as tun with IFF_TAP. ~960MB OOMs a 512MB guest.
- On the fixed kernel the oversized tx_queue_len is rejected with -ERANGE at set time (all four paths: RTM_SETLINK, RTM_NEWLINK create, sysfs, ioctl - the latter two via this check, the former two via this check and the 2/3 parse policy respectively).
--- net/core/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/core/dev.c +1 -1
@@ -9851,7 +9851,7 @@ int netif_change_tx_queue_len(struct net_device *dev, unsigned long new_len)unsigned int orig_len = dev->tx_queue_len;int res; -if (new_len != (unsigned int )new_len) +if (new_len >S16_MAX )return -ERANGE ;if (new_len != orig_len) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 804/1250 net: reject oversized tx_queue_len at netlink parse time
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit 1aa9e143bf51405665a793d4cc925e1c4f0c5922 ]
rtnl_create_link() assigns IFLA_TXQLEN directly to dev->tx_queue_len without going through netif_change_tx_queue_len(), so a device created with "ip link add ... txqueuelen 500000" bypasses the S16_MAX cap and still triggers the oversized ring allocations in pfifo_fast, tun and tap. The veth peer nest (rtnl_nla_parse_ifinfomsg()) and the RTM_NEWLINK-on-existing-device path reach the same sinks.
Enforce the cap in ifla_policy instead: IFLA_TXQLEN becomes NLA_POLICY_FULL_RANGE(NLA_U32, &txqlen_range) with txqlen_range = { .min = 0, .max = S16_MAX }. All netlink consumers parse against this policy - rtnl_setlink(), rtnl_newlink() (create and change), and the veth peer nest - so every netlink path is capped at parse time and rejects the attribute with -ERANGE plus a proper "integer out of range" extack message before any device state is modified (the RTM_SETLINK half-application wart is gone with it).
Document the bound in the rt-link.yaml netlink spec.
Conditions to recreate the bug:
- CONFIG_NET_SCHED=y, CONFIG_VETH=y, CONFIG_USER_NS=y, CONFIG_NET_NS=y.
- Unprivileged user in a fresh user+net namespace (unshare -Urn):
ip link add v0 txqueuelen 500000 type veth peer name v1
-> on the fixed kernel this is rejected with -ERANGE ("integer out of range" extack) instead of installing an oversized tx_queue_len that later inflates pfifo_fast/tun/tap ring allocations.
- ip link set v0 txqueuelen 500000 is likewise rejected at parse time.
--- Documentation/netlink/specs/rt-link.yaml | 2 ++ net/core/rtnetlink.c | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-)
Documentation/netlink/specs/rt-link.yaml +2 -0
@@ -881,6 +881,8 @@ attribute-sets: - name: txqlen type: u32 + checks: + max: 32767 - name: map type: binary
net/core/rtnetlink.c +6 -1
@@ -2214,6 +2214,11 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,return -EMSGSIZE ; } +static const struct netlink_range_validation txqlen_range = { + .min =0 , + .max =S16_MAX , +}; +static const struct nla_policy ifla_policy[IFLA_MAX +1 ] = { [IFLA_UNSPEC ] = { .strict_start_type =IFLA_DPLL_PIN }, [IFLA_IFNAME ] = { .type =NLA_STRING , .len =IFNAMSIZ -1 },
@@ -2224,7 +2229,7 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_LINK] = { .type = NLA_U32 }, [IFLA_MASTER] = { .type = NLA_U32 }, [IFLA_CARRIER] = { .type = NLA_U8 }, - [IFLA_TXQLEN] = { .type = NLA_U32 }, + [IFLA_TXQLEN] = NLA_POLICY_FULL_RANGE(NLA_U32, &txqlen_range), [IFLA_WEIGHT] = { .type = NLA_U32 }, [IFLA_OPERSTATE] = { .type = NLA_U8 }, [IFLA_LINKMODE] = { .type = NLA_U8 },
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 805/1250 pds_core: fix cmd_regs access racing BAR unmap on reset
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nikhil P. Rao <[email protected]>
[ Upstream commit 7980325b2f71e3f65c1323c39792e2455da6fab6 ]
pdsc_reset_prepare() and pdsc_reset_done()'s pdsc_map_bars() error path clear/iounmap cmd_regs without devcmd_lock, and pdsc_legacy_firmware_update()'s download loop derefs cmd_regs after dropping and retaking the lock without re-checking. An FLR concurrent with a devlink flash can unmap cmd_regs under an in-flight devcmd, causing a NULL deref or a write to unmapped MMIO.
Take devcmd_lock across the BAR unmap/remap, and re-check cmd_regs in the download loop. Only the PF maps cmd_regs and runs devcmd, so skip the unmap on a VF, as pdsc_remove() and pdsc_reset_done() already do.
A reset that completes entirely within the unlocked window is not a correctness problem for the image: the device clears its update session, so a resumed download is rejected, and it verifies the staged image before writing a flash slot, reporting PDS_RC_BAD_FW rather than activating it.
pdsc_unmap_bars() also clears info_regs, intr_status and intr_ctrl. The interrupt and start/stop readers of those are quiesced before the unmap by pdsc_fw_down(), which frees the interrupts and tears down the queues. The debugfs readers are not, since those files outlive a reset; that is pre-existing and out of scope here.
--- drivers/net/ethernet/amd/pds_core/fw.c | 10 +++++++++- drivers/net/ethernet/amd/pds_core/main.c | 8 +++++++- 2 files changed, 16 insertions(+), 2 deletions(-)
drivers/net/ethernet/amd/pds_core/fw.c +9 -1
@@ -107,8 +107,10 @@ int pdsc_firmware_update(struct pdsc *pdsc, const struct firmware *fw,dev_info (pdsc->dev,"Installing firmware\n" ); -if (!pdsc->cmd_regs) +if (!pdsc->cmd_regs) { +NL_SET_ERR_MSG_MOD (extack,"BARs not mapped" );return -ENXIO ; + } dl =priv_to_devlink (pdsc); devlink_flash_update_status_notify(dl, "Preparing to flash",
@@ -134,6 +136,12 @@ int pdsc_firmware_update(struct pdsc *pdsc, const struct firmware *fw, copy_sz = min_t(unsigned int, buf_sz, fw->size - offset);mutex_lock (&pdsc->devcmd_lock); +if (!pdsc->cmd_regs) { +mutex_unlock (&pdsc->devcmd_lock); + err = -ENXIO ; +NL_SET_ERR_MSG_MOD (extack,"Device reset during flash" ); + goto err_out; + }memcpy_toio (&pdsc->cmd_regs->data, fw->data + offset, copy_sz); err =pdsc_devcmd_fw_download_locked (pdsc, data_addr, offset, copy_sz);
drivers/net/ethernet/amd/pds_core/main.c +7 -1
@@ -508,7 +508,11 @@ static void pdsc_reset_prepare(struct pci_dev *pdev)pdsc_auxbus_dev_del (pdsc, pdsc, &pdsc->padev); } -pdsc_unmap_bars (pdsc); +if (!pdev->is_virtfn) { +mutex_lock (&pdsc->devcmd_lock); +pdsc_unmap_bars (pdsc); +mutex_unlock (&pdsc->devcmd_lock); + }pci_release_regions (pdev);if (pci_is_enabled (pdev))pci_disable_device (pdev);
@@ -537,7 +541,9 @@ static void pdsc_reset_done(struct pci_dev *pdev)return ; } +mutex_lock (&pdsc->devcmd_lock); err =pdsc_map_bars (pdsc); +mutex_unlock (&pdsc->devcmd_lock);if (err)return ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 806/1250 pds_core: dont release PCI regions for VFs on reset
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nikhil P. Rao <[email protected]>
[ Upstream commit 73608de7e59246b4b533c1ffaee158a7048e186e ]
pdsc_reset_prepare() called pci_release_regions() unconditionally, but only PFs call pci_request_regions() (pdsc_init_pf). On a VF FLR this makes the kernel warn "Trying to free nonexistent resource".
--- drivers/net/ethernet/amd/pds_core/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/amd/pds_core/main.c +1 -1
@@ -512,8 +512,8 @@ static void pdsc_reset_prepare(struct pci_dev *pdev)mutex_lock (&pdsc->devcmd_lock);pdsc_unmap_bars (pdsc);mutex_unlock (&pdsc->devcmd_lock); +pci_release_regions (pdev); } -pci_release_regions (pdev);if (pci_is_enabled (pdev))pci_disable_device (pdev); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 807/1250 powerpc/kexec_file: Use inclusive range checks in add_usable_mem()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thorsten Blum <[email protected]>
[ Upstream commit c6755be4838d6ccd641effbcdc3d917b82631ff9 ]
add_usable_mem() adds usable memory ranges for the kdump kernel.
The ranges are inclusive, but the partial overlap check uses exclusive comparisons. This skips ranges with base == loc_end or end == loc_base. Use inclusive comparisons instead.
--- arch/powerpc/kexec/file_load_64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/powerpc/kexec/file_load_64.c +1 -1
@@ -113,7 +113,7 @@ static int add_usable_mem(struct umem_info *um_info, u64 base, u64 end) loc_end = um_info->ranges[i].end;if (loc_base >= base && loc_end <= end) add = true; -else if (base < loc_end && end > loc_base) { +else if (base <= loc_end && end >= loc_base) {if (loc_base < base) loc_base = base;if (loc_end > end)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 808/1250 powerpc/kexec_file: Use inclusive range checks for excluded memory
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thorsten Blum <[email protected]>
[ Upstream commit 449f60f99f8f3cbe80a9bd2242945e827c5ed003 ]
arch_check_excluded_range() checks if a kexec segment overlaps an excluded memory range.
Both ranges use inclusive end addresses, but the overlap check uses exclusive comparisons. This skips ranges with start == ->ranges[i].end or end == ->ranges[i].start. Use inclusive comparisons instead.
--- arch/powerpc/kexec/file_load_64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/powerpc/kexec/file_load_64.c +1 -1
@@ -57,7 +57,7 @@ int arch_check_excluded_range(struct kimage *image, unsigned long start, emem = image->arch.exclude_ranges;for (i =0 ; i < emem->nr_ranges; i++) -if (start < emem->ranges[i].end && end > emem->ranges[i].start) +if (start <= emem->ranges[i].end && end >= emem->ranges[i].start)return 1 ;return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 809/1250 net: mctp: i3c: serialize probe with bus removal
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: XingWang Xiang <[email protected]>
[ Upstream commit 2b4707a149a55e8fa75c9ef32b359d60f470a566 ]
mctp_i3c_probe() drops busdevs_lock after finding the matching bus. A concurrent I3C_NOTIFY_BUS_REMOVE can then unregister and free the bus netdev before probe passes its private data to mctp_i3c_add_device(). The latter consequently adds a list node through a freed mbus pointer.
Keep busdevs_lock held until the device has been added. This also satisfies the __must_hold annotation on mctp_i3c_add_device().
--- drivers/net/mctp/mctp-i3c.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
drivers/net/mctp/mctp-i3c.c +6 -3
@@ -288,6 +288,7 @@ __must_hold(&busdevs_lock)static int mctp_i3c_probe (struct i3c_device *i3c) {struct mctp_i3c_bus *b =NULL , *mbus =NULL ; +int rc;/* Look for a known bus */ mutex_lock (&busdevs_lock);
@@ -296,14 +297,16 @@ static int mctp_i3c_probe(struct i3c_device *i3c) mbus = b;break ; } -mutex_unlock (&busdevs_lock);if (!mbus) {/* probably no "mctp-controller" property on the i3c bus */ -return -ENODEV ; + rc = -ENODEV ; + }else { + rc =mctp_i3c_add_device (mbus, i3c); } +mutex_unlock (&busdevs_lock); -return mctp_i3c_add_device (mbus, i3c); +return rc; }static void mctp_i3c_remove_device (struct mctp_i3c_device *mi)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 810/1250 net/sched: defer qdisc freeing after failed creation
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
[ Upstream commit e6662f2100f8d33b0f4d0047c219efd6bba186ea ]
An RTM_NEWQDISC request can make clsact bind a populated shared ingress block during ->init(), publishing an embedded mini_Qdisc to lockless readers. If the same request has an invalid TCA_RATE, estimator setup fails after ->init(); the unwind removes the pointer but synchronously frees its containing qdisc while tc_run() may still hold it.
Retire failed qdiscs through the same RCU helper as normal destruction. Inline the synchronous free into the callback now that no direct callers remain.
--- include/net/sch_generic.h | 2 +- net/sched/sch_api.c | 2 +- net/sched/sch_generic.c | 20 ++++++++++---------- 3 files changed, 12 insertions(+), 12 deletions(-)
include/net/sch_generic.h +1 -1
@@ -755,7 +755,7 @@ void qdisc_offload_query_caps(struct net_device *dev,struct Qdisc *qdisc_alloc (struct netdev_queue *dev_queue,const struct Qdisc_ops *ops,struct netlink_ext_ack *extack); -void qdisc_free (struct Qdisc *qdisc); +void qdisc_free_rcu (struct Qdisc *qdisc);struct Qdisc *qdisc_create_dflt (struct netdev_queue *dev_queue,const struct Qdisc_ops *ops,u32 parentid,struct netlink_ext_ack *extack);
net/sched/sch_api.c +1 -1
@@ -1381,7 +1381,7 @@ static struct Qdisc *qdisc_create(struct net_device *dev, err_out3:lockdep_unregister_key (&sch->root_lock_key);netdev_put (dev, &sch->dev_tracker); -qdisc_free (sch); +qdisc_free_rcu (sch); err_out2:bpf_module_put (ops, ops->owner); err_out:
net/sched/sch_generic.c +10 -10
@@ -1067,21 +1067,21 @@ void qdisc_reset(struct Qdisc *qdisc) }EXPORT_SYMBOL (qdisc_reset); -void qdisc_free (struct Qdisc *qdisc) +static void qdisc_free_cb (struct rcu_head *head) { -if (qdisc_is_percpu_stats (qdisc)) { -free_percpu (qdisc->cpu_bstats); -free_percpu (qdisc->cpu_qstats); + struct Qdisc *q = container_of(head, struct Qdisc, rcu); + +if (qdisc_is_percpu_stats (q)) { +free_percpu (q->cpu_bstats); +free_percpu (q->cpu_qstats); } -kfree (qdisc); +kfree (q); } -static void qdisc_free_cb (struct rcu_head *head) +void qdisc_free_rcu (struct Qdisc *qdisc) { - struct Qdisc *q = container_of(head, struct Qdisc, rcu); - -qdisc_free (q); +call_rcu (&qdisc->rcu, qdisc_free_cb); }static void __qdisc_destroy (struct Qdisc *qdisc)
@@ -1108,7 +1108,7 @@ static void __qdisc_destroy(struct Qdisc *qdisc)trace_qdisc_destroy (qdisc); -call_rcu (&qdisc->rcu, qdisc_free_cb); +qdisc_free_rcu (qdisc); }void qdisc_destroy (struct Qdisc *qdisc)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 811/1250 net/mlx5e: Fix missing FEC mode mapping for RS_544_514_INTERLEAVED_QUAD
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shahar Shitrit <[email protected]>
[ Upstream commit 802eedcc0b25bb3e1b492f0600ab74325274d53b ]
MLX5E_FEC_RS_544_514_INTERLEAVED_QUAD is missing from pplm_fec_2_ethtool_linkmodes[], leaving index 4 zero-initialized. As a result, when this FEC mode is active, find_first_bit() returns index 4, causing __set_bit() to set bit 0 (ETHTOOL_LINK_MODE_10baseT_Half_BIT) instead of ETHTOOL_LINK_MODE_FEC_RS_BIT. Consequently, ethtool reports:
Advertised FEC modes: Not reported
Add the missing mapping to ETHTOOL_LINK_MODE_FEC_RS_BIT.
--- drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +1 -0
@@ -985,6 +985,7 @@ static const u32 pplm_fec_2_ethtool_linkmodes[] = { [MLX5E_FEC_NOFEC] = ETHTOOL_LINK_MODE_FEC_NONE_BIT, [MLX5E_FEC_FIRECODE] = ETHTOOL_LINK_MODE_FEC_BASER_BIT, [MLX5E_FEC_RS_528_514] = ETHTOOL_LINK_MODE_FEC_RS_BIT, + [MLX5E_FEC_RS_544_514_INTERLEAVED_QUAD] = ETHTOOL_LINK_MODE_FEC_RS_BIT, [MLX5E_FEC_RS_544_514] = ETHTOOL_LINK_MODE_FEC_RS_BIT, [MLX5E_FEC_LLRS_272_257_1] = ETHTOOL_LINK_MODE_FEC_LLRS_BIT, };
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 812/1250 net/mlx5e: Fix setting RS FEC after remapping
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shahar Shitrit <[email protected]>
[ Upstream commit b9d755c5a37519fb1354034db1dfeb30e1ba6856 ]
When a user sets a FEC mode via ethtool, the driver maps the ethtool FEC type to the lowest mlx5 bit of that type. For RS FEC, this is MLX5E_FEC_RS_528_514 (bit 2). The driver then checks whether this bit is supported by at least one link mode by inspecting the fec_override_cap fields via mlx5e_fec_in_caps(), and returns -EOPNOTSUPP if not.
This check is incorrect. RS FEC has three supported hardware variants:RS_528_514 (bit 2), RS_544_514_INTERLEAVED_QUAD (bit 4), and RS_544_514 (bit 7). mlx5e_remap_fec_conf_mode() already remaps bit 2 to the appropriate RS variant per link mode when writing the admin fields, but the early capability check is done against the raw unmapped bit. As a result, a device that supports RS_544_514 or RS_544_514_INTERLEAVED_QUAD but not RS_528_514 will incorrectly reject the user's RS FEC request.
Remove the early support check from mlx5e_set_fec_mode() and fold it into the existing write loop, checking caps against the remapped policy per link mode. Return -EOPNOTSUPP before the final register write if no link mode accepted the policy.
--- drivers/net/ethernet/mellanox/mlx5/core/en/port.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/en/port.c +9 -6
@@ -557,6 +557,7 @@ int mlx5e_set_fec_mode(struct mlx5_core_dev *dev, u16 fec_policy)u32 in[MLX5_ST_SZ_DW (pplm_reg)] = {};int sz =MLX5_ST_SZ_BYTES (pplm_reg);u16 fec_policy_auto =0 ; +bool fec_set = false;int err;int i;
@@ -569,9 +570,6 @@ int mlx5e_set_fec_mode(struct mlx5_core_dev *dev, u16 fec_policy)if (fec_policy >= (1 <<MLX5E_FEC_LLRS_272_257_1 ) && !fec_50g_per_lane)return -EOPNOTSUPP ; -if (fec_policy && !mlx5e_fec_in_caps (dev, fec_policy)) -return -EOPNOTSUPP ; -MLX5_SET (pplm_reg, in, local_port,1 ); err =mlx5_core_access_reg (dev, in, sz, out, sz,MLX5_REG_PPLM ,0 ,0 );if (err)
@@ -591,12 +589,17 @@ int mlx5e_set_fec_mode(struct mlx5_core_dev *dev, u16 fec_policy)mlx5e_get_fec_cap_field (out, &fec_caps, i);/* policy supported for link speed */ -if (fec_caps & conf_fec) +if (fec_caps & conf_fec) {mlx5e_fec_admin_field (out, &conf_fec,1 , i); -else -/* set FEC to auto*/ + fec_set = true; + }else { +/* set FEC to auto */ mlx5e_fec_admin_field (out, &fec_policy_auto,1 , i); + } } +if (fec_policy && !fec_set) +return -EOPNOTSUPP ; +return mlx5_core_access_reg (dev, out, sz, out, sz,MLX5_REG_PPLM ,0 ,1 ); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 813/1250 net/mlx5: LAG, use local tracker to update active ports
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Akiva Goldberger <[email protected]>
[ Upstream commit b3c79dee5038c5e8460c59d7d01cb1450bdf5ecb ]
The CREATE_LAG command is handled asynchronously by queuing a work, which stores a local copy of ldev->tracker. When the work is processed, it is possible that the values of the local copy and ldev->tracker have diverged.
A single CREATE_LAG command programs two related fields into the
firmware: the v2p (virtual-to-physical) map, which selects the physical egress port for each hash bucket, and the active_port bitmask, which tells the firmware which physical ports are currently up so it can redirect QP/TIS away from inactive ports. For the firmware to steer traffic correctly, both must be derived from the same view of the ports' link state.
The v2p map is computed by mlx5_infer_tx_affinity_mapping() from the local tracker snapshot, but lag_active_port_bits() called mlx5_infer_tx_enabled() on the live ldev->tracker instead. If ldev->tracker changed between the snapshot and command execution, the two fields reflect different port states: the v2p map may steer a bucket to a port that the active_port mask marks as inactive (or vice versa). The firmware then receives a self-contradictory configuration and can redirect or drop traffic on a port the mapping still points at, until a later event happens to reconcile the state.
Update lag_active_port_bits so that it receives the local version of the tracker from when the work was queued, effectively closing the window for injecting an inconsistency.
--- .../net/ethernet/mellanox/mlx5/core/lag/lag.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c +11 -8
@@ -62,14 +62,15 @@ static int get_port_sel_mode(enum mlx5_lag_mode mode, unsigned long flags)return MLX5_LAG_PORT_SELECT_MODE_QUEUE_AFFINITY ; } -static u8 lag_active_port_bits (struct mlx5_lag *ldev) +static u8 lag_active_port_bits (struct mlx5_lag *ldev, +struct lag_tracker *tracker) {u8 enabled_ports[MLX5_MAX_PORTS ] = {};u8 active_port =0 ;int num_enabled;int idx; -mlx5_infer_tx_enabled (&ldev->tracker, ldev, enabled_ports, +mlx5_infer_tx_enabled (tracker, ldev, enabled_ports, &num_enabled);for (idx =0 ; idx < num_enabled; idx++) active_port |=BIT_MASK (enabled_ports[idx]);
@@ -78,7 +79,8 @@ static u8 lag_active_port_bits(struct mlx5_lag *ldev) }static int mlx5_cmd_create_lag (struct mlx5_core_dev *dev,struct mlx5_lag *ldev, -int mode,unsigned long flags) +struct lag_tracker *tracker,int mode, +unsigned long flags) {bool fdb_sel_mode =test_bit (MLX5_LAG_MODE_FLAG_FDB_SEL_MODE_NATIVE , &flags);
@@ -107,7 +109,7 @@ static int mlx5_cmd_create_lag(struct mlx5_core_dev *dev, struct mlx5_lag *ldev,break ;MLX5_SET (lagc, lag_ctx, active_port, -lag_active_port_bits (mlx5_lag_dev (dev))); +lag_active_port_bits (ldev, tracker));break ;default :break ;
@@ -494,7 +496,8 @@ static int mlx5_cmd_modify_active_port(struct mlx5_core_dev *dev, u8 ports)return mlx5_cmd_exec_in (dev, modify_lag, in); } -static int _mlx5_modify_lag (struct mlx5_lag *ldev,u8 *ports) +static int _mlx5_modify_lag (struct mlx5_lag *ldev, +struct lag_tracker *tracker,u8 *ports) {int idx =mlx5_lag_get_dev_index_by_seq (ldev,MLX5_LAG_P1 );struct mlx5_core_dev *dev0;
@@ -511,7 +514,7 @@ static int _mlx5_modify_lag(struct mlx5_lag *ldev, u8 *ports) !MLX5_CAP_PORT_SELECTION(dev0, port_select_flow_table_bypass))return ret; - active_ports =lag_active_port_bits (ldev); + active_ports =lag_active_port_bits (ldev, tracker);return mlx5_cmd_modify_active_port (dev0, active_ports); }
@@ -571,7 +574,7 @@ void mlx5_modify_lag(struct mlx5_lag *ldev, idx = i * ldev->buckets + j;if (ports[idx] == ldev->v2p_map[idx])continue ; - err =_mlx5_modify_lag (ldev, ports); + err =_mlx5_modify_lag (ldev, tracker, ports);if (err) { mlx5_core_err(dev0, "Failed to modify LAG (%d)\n",
@@ -708,7 +711,7 @@ static int mlx5_create_lag(struct mlx5_lag *ldev,mlx5_core_info (dev0,"shared_fdb:%d mode:%s\n" , shared_fdb,mlx5_get_str_port_sel_mode (mode, flags)); - err =mlx5_cmd_create_lag (dev0, ldev, mode, flags); + err =mlx5_cmd_create_lag (dev0, ldev, tracker, mode, flags);if (err) { mlx5_core_err(dev0, "Failed to create LAG (%d)\n",
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 814/1250 net/mlx5e: Fix ETS zero BW reporting when one TC holds 100%
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Carolina Jubran <[email protected]>
[ Upstream commit e7ee89740800a1cf253713e9249c3ee9203ebe91 ]
When ETS TCs with zero bandwidth are configured, the driver programs the firmware using an alternate representation. On get, it needs to recognize that representation so those TCs can be translated back and reported as 0% bandwidth.
The existing detection relied on the programmed bandwidth because it was enough to identify this representation. However, when a single ETS TC owns 100% of the bandwidth, its firmware representation becomes the same as a strict-priority TC, causing zero-bandwidth ETS TCs to be reported with non-zero bandwidth values.
Use the cached TSA instead to distinguish the ETS and strict-priority cases.
--- drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c +1 -1
@@ -133,7 +133,7 @@ static int mlx5e_dcbnl_ieee_getets(struct net_device *netdev,if (err)return err; -if (ets->tc_tx_bw[i] <MLX5E_MAX_BW_ALLOC && +if (priv->dcbx.tc_tsa[i] ==IEEE_8021QAZ_TSA_ETS && tc_group[i] == (MLX5E_LOWEST_PRIO_GROUP +1 )) is_zero_bw_ets_tc = true;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 815/1250 net/mlx5e: Fix use-after-free race in sample_restore_put()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Carolina Jubran <[email protected]>
[ Upstream commit af3aef0245abbab5e9f6302e7a7d6407187afb71 ]
Concurrent teardown of TC sample rules sharing the same restore context may re-read restore->count after dropping restore_lock. At that point another thread may already have completed cleanup and freed the restore object.
Use the result of the refcount decrement while holding restore_lock to determine whether cleanup is needed.
--- drivers/net/ethernet/mellanox/mlx5/core/en/tc/sample.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/en/tc/sample.c +5 -2
@@ -311,12 +311,15 @@ sample_restore_get(struct mlx5e_tc_psample *tc_psample, u32 obj_id,static void sample_restore_put (struct mlx5e_tc_psample *tc_psample,struct mlx5e_sample_restore *restore) { +bool last; +mutex_lock (&tc_psample->restore_lock); -if (--restore->count ==0 ) + last = --restore->count ==0 ; +if (last)hash_del (&restore->hlist);mutex_unlock (&tc_psample->restore_lock); -if (!restore->count) { +if (last) {mlx5_del_flow_rules (restore->rule);mlx5_modify_header_dealloc (tc_psample->esw->dev, restore->modify_hdr);kfree (restore);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 816/1250 net/mlx5: E-Switch: fix use-after-free in mlx5_eswitch_termtbl_put
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yael Chemla <[email protected]>
[ Upstream commit 7ee07f601f8f507c9faf25c68a49396ab8950596 ]
In mlx5_eswitch_termtbl_put(), the zero-ref cleanup check reads tt->ref_count after termtbl_mutex has been released. Two concurrent callers on the same mlx5_termtbl_handle race: one decrements ref_count to zero, removes the hash entry, and calls kfree(tt) while the other has already dropped the mutex and is about to evaluate
if (!tt->ref_count), producing a use-after-free.
Fix this by capturing the result of the decrement into a stack-local last variable before dropping the mutex. The cleanup decision is now made entirely under termtbl_mutex, and tt is not touched after kfree.
--- .../ethernet/mellanox/mlx5/core/eswitch_offloads_termtbl.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_termtbl.c +5 -2
@@ -163,12 +163,15 @@ void mlx5_eswitch_termtbl_put(struct mlx5_eswitch *esw, struct mlx5_termtbl_handle *tt) { + bool last; +mutex_lock (&esw->offloads.termtbl_mutex); -if (--tt->ref_count ==0 ) + last = (--tt->ref_count ==0 ); +if (last)hash_del (&tt->termtbl_hlist);mutex_unlock (&esw->offloads.termtbl_mutex); -if (!tt->ref_count) { +if (last) {mlx5_del_flow_rules (tt->rule);mlx5_destroy_flow_table (tt->termtbl);kfree (tt);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 817/1250 net/mlx5: E-Switch, prevent mc_list repopulation during vport disable
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lama Kayal <[email protected]>
[ Upstream commit c0c6f4ba8a37688f7b4d4044898d88f0450d44c2 ]
In mlx5_esw_vport_disable(), move esw_apply_vport_rx_mode() ahead of esw_vport_change_handle_locked() so vport->allmulti_rule is NULL before the change handler observes it.
During FW-fatal recovery the disable runs while dev->state == INTERNAL_ERROR. The promisc query inside esw_update_vport_rx_mode() fails and returns early, leaving vport->allmulti_rule intact, so esw_update_vport_mc_promisc() runs and adds MLX5_ACTION_ADD entries to vport->mc_list whose flow rules are then installed in the FDB by esw_add_mc_addr(). esw_destroy_legacy_table() tears down the FDB with those refs still held, corrupting the sub-tree and leaving dangling flow_rule pointers in vport->mc_list.
Two-stage failure on echo 1 > /sys/bus/pci/devices/<bdf>/reset:
refcount_t: underflow; use-after-free.
tree_put_node+0xef/0x110 [mlx5_core] clean_tree+0x44/0xd0 [mlx5_core] (x5) mlx5_fs_core_cleanup+0x57/0x1c0 [mlx5_core] mlx5_unload+0x65/0xd0 [mlx5_core] ... mlx5_health_try_recover BUG: unable to handle page fault for address: 0000000003000055 down_write+0x1c/0x60 mlx5_del_flow_rules+0x33/0x1f0 [mlx5_core] esw_del_mc_addr+0x7b/0x170 [mlx5_core] esw_apply_vport_addr_list+0x56/0xf0 [mlx5_core] esw_vport_change_handle_locked+0x28b/0x310 [mlx5_core] mlx5_esw_vport_enable+0x270/0x4a0 [mlx5_core] ... mlx5_load ... mlx5_health_try_recover
esw_apply_vport_rx_mode(false, false) clears vport->allmulti_rule via its local state machine even when the FW del fails. With the rule NULL the !IS_ERR_OR_NULL(allmulti_rule) gate in the change handler closes, no rules are installed during disable, and the reload starts with a clean mc_list.
--- drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +7 -1
@@ -1006,13 +1006,19 @@ void mlx5_esw_vport_disable(struct mlx5_eswitch *esw, struct mlx5_vport *vport) (vport->info.ipsec_crypto_enabled || vport->info.ipsec_packet_enabled)) esw->enabled_ipsec_vf_count--; +/* Clear rx-mode before esw_vport_change_handle_locked(): on +* MLX5_VPORT_PROMISC_CHANGE it calls esw_update_vport_mc_promisc() +* when vport->allmulti_rule is set, repopulating mc_list with FDB +* rules that dangle once the FDB is destroyed. NULL allmulti_rule +* here skips that path. +*/ +esw_apply_vport_rx_mode (esw, vport, false, false);/* We don't assume VFs will cleanup after themselves. * Calling vport change handler while vport is disabled will cleanup * the vport resources. */ esw_vport_change_handle_locked (vport); vport->enabled_events =0 ; -esw_apply_vport_rx_mode (esw, vport, false, false);esw_vport_cleanup (esw, vport); esw->enabled_vports--;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 818/1250 net/mlx5e: Keep HW timestamp stats monotonic across reconfiguration
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Carolina Jubran <[email protected]>
[ Upstream commit df99553f840e4c529c1ba4c29bd39396466ca11a ]
mlx5e_stats_ts_get() currently selects either DMA or port timestamp counters based on tx_ptp_opened. This flag is intentionally kept set once the PTP TX queues have been opened so their statistics remain available after queue teardown. As a result, DMA timestamps are no longer reported after switching from port timestamping back to DMA timestamping.
The function also reads statistics only from the currently active channels and TCs. Reducing the number of channels or TCs can therefore drop previously accumulated timestamp counters from the reported value.
Read the persistent channel statistics instead and always include DMA timestamp counters. Once the PTP TX queues have been opened, also include the port timestamp counters.
This also drops state_lock. It previously protected live channel/PTP pointers, the new code only reads persistent channel_stats and ptp_stats via mlx5e_stats_nch_read(), which is already safe for lockless stats access.
--- .../ethernet/mellanox/mlx5/core/en_stats.c | 51 ++++++++----------- 1 file changed, 20 insertions(+), 31 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/en_stats.c +20 -31
@@ -1170,50 +1170,39 @@ void mlx5e_stats_rmon_get(struct mlx5e_priv *priv,void mlx5e_stats_ts_get (struct mlx5e_priv *priv,struct ethtool_ts_stats *ts_stats) { -int i, j; +u16 nch =mlx5e_stats_nch_read (priv); +int i, tc; -mutex_lock (&priv->state_lock); + ts_stats->pkts =0 ; -if (priv->tx_ptp_opened) { -struct mlx5e_ptp *ptp = priv->channels.ptp; +for (i =0 ; i < nch; i++) { +struct mlx5e_channel_stats *channel_stats = + priv->channel_stats[i]; - ts_stats->pkts =0 ; +for (tc =0 ; tc < priv->max_opened_tc; tc++) + ts_stats->pkts += channel_stats->sq[tc].timestamps; + } + +/* Accumulate DMA and port timestamp counters so values stay monotonic +* across channel teardown and mode switches. +*/ +if (priv->tx_ptp_opened) { +/* Err and Lost stats are only relevant for port timestamping, +* as the DMA layer will always successfully timestamp packets. +*/ ts_stats->err =0 ; ts_stats->lost =0 ; -if (!ptp) - goto out; - -/* Aggregate stats across all TCs */ -for (i =0 ; i < ptp->num_tc; i++) { +for (tc =0 ; tc < priv->max_opened_tc; tc++) {struct mlx5e_ptp_cq_stats *stats = - ptp->ptpsq[i].cq_stats; + &priv->ptp_stats.cq[tc]; ts_stats->pkts += stats->cqe; ts_stats->err += stats->abort + stats->err_cqe + - stats->late_cqe; + stats->late_cqe; ts_stats->lost += stats->lost_cqe; } - }else { -/* DMA layer will always successfully timestamp packets. Other -* counters do not make sense for this layer. -*/ - ts_stats->pkts =0 ; - -/* Aggregate stats across all SQs */ -for (j =0 ; j < priv->channels.num; j++) { -struct mlx5e_channel *c = priv->channels.c[j]; - -for (i =0 ; i < c->num_tc; i++) { -struct mlx5e_sq_stats *stats = c->sq[i].stats; - - ts_stats->pkts += stats->timestamps; - } - } } - -out: -mutex_unlock (&priv->state_lock); }#define PPORT_PHY_LAYER_OFF (c) \
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 819/1250 net/sched: fq_pie: clamp quantum in change path
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit 4864f58c53eb47257d55e01f47d4a9f355f7f970 ]
fq_pie_change() accepts any quantum value from userspace, including 1. With a crafted size table qdisc_pkt_len reaches ~2 GiB, so quantum=1 makes the deficit-refill loop spin ~2^31 times under the qdisc lock (a soft lockup / denial of service).
Add max(256U, ...) matching fq_codel_change().
Conditions to recreate the bug:
CONFIG_NET_SCH_FQ_PIE=y. Requires CAP_NET_ADMIN (namespace-local via unshare -Urn suffices).
tc qdisc add dev dummy0 root fq_pie
tc qdisc change dev dummy0 root fq_pie quantum 1 stab data 32768 size_log 15 cell_log 0
--- net/sched/sch_fq_pie.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/sched/sch_fq_pie.c +2 -1
@@ -341,7 +341,8 @@ static int fq_pie_change(struct Qdisc *sch, struct nlattr *opt, nla_get_u32(tb[TCA_FQ_PIE_BETA]));if (tb[TCA_FQ_PIE_QUANTUM ]) -WRITE_ONCE (q->quantum,nla_get_u32 (tb[TCA_FQ_PIE_QUANTUM ])); +WRITE_ONCE (q->quantum, +max (256U ,nla_get_u32 (tb[TCA_FQ_PIE_QUANTUM ])));if (tb[TCA_FQ_PIE_MEMORY_LIMIT ]) WRITE_ONCE(q->memory_limit,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 820/1250 net/sched: sfq: clamp quantum in change path
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit fb9f88a33c516ea5c0bcd9a22ca288b246b34567 ]
sfq_change() accepts any non-negative quantum (only rejects (int)ctl->quantum < 0). With a crafted size table qdisc_pkt_len reaches ~2 GiB, so quantum=1 makes the deficit-refill loop spin ~2^31 times under the qdisc lock (a soft lockup / denial of service).
Add max(256U, ...) matching fq_codel_change(). Reject quantum > 1<<20 with -EINVAL, matching fq_codel_change() and the init clamp.
Conditions to recreate the bug:
CONFIG_NET_SCH_SFQ=y. Requires CAP_NET_ADMIN (namespace-local via unshare -Urn suffices).
tc qdisc add dev dummy0 root sfq
tc qdisc change dev dummy0 root sfq quantum 1 stab data 32768 size_log 15 cell_log 0
--- net/sched/sch_sfq.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
net/sched/sch_sfq.c +6 -1
@@ -657,6 +657,11 @@ static int sfq_change(struct Qdisc *sch, struct nlattr *opt,return -EINVAL ; } +if (ctl->quantum >1 <<20 ) { +NL_SET_ERR_MSG_MOD (extack,"quantum too large" ); +return -EINVAL ; + } +if (ctl->perturb_period <0 || ctl->perturb_period >INT_MAX /HZ ) {NL_SET_ERR_MSG_MOD (extack,"invalid perturb period" );
@@ -685,7 +690,7 @@ static int sfq_change(struct Qdisc *sch, struct nlattr *opt,/* update and validate configuration */ if (ctl->quantum) - quantum = ctl->quantum; + quantum =max (256U , ctl->quantum);if (ctl->flows) maxflows =min_t (u32, ctl->flows,SFQ_MAX_FLOWS );if (ctl->divisor) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 821/1250 net/sched: hhf: clamp quantum in change and init paths
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit eb56a495f59baf6cad5ed80e3ffb9078098b1346 ]
hhf_change() accepts any quantum from userspace, including 1. With a crafted size table qdisc_pkt_len reaches ~2 GiB, so quantum=1 makes the deficit-refill loop spin ~2^31 times under the qdisc lock (a soft lockup / denial of service).
Add max(256U, ...) in hhf_change() matching fq_codel_change(). Clamp hhf_init() to [256, 1<<20] matching the siblings, and remove the old fallback that only set quantum=256 on overflow.
Conditions to recreate the bug:
CONFIG_NET_SCH_HHF=y. Requires CAP_NET_ADMIN (namespace-local via unshare -Urn suffices).
tc qdisc add dev dummy0 root hhf
tc qdisc change dev dummy0 root hhf quantum 1 stab data 32768 size_log 15 cell_log 0
--- net/sched/sch_hhf.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-)
net/sched/sch_hhf.c +2 -6
@@ -550,7 +550,7 @@ static int hhf_change(struct Qdisc *sch, struct nlattr *opt,return err;if (tb[TCA_HHF_QUANTUM ]) - new_quantum =nla_get_u32 (tb[TCA_HHF_QUANTUM ]); + new_quantum =max (256U ,nla_get_u32 (tb[TCA_HHF_QUANTUM ]));if (tb[TCA_HHF_NON_HH_WEIGHT ]) new_hhf_non_hh_weight =nla_get_u32 (tb[TCA_HHF_NON_HH_WEIGHT ]);
@@ -612,7 +612,7 @@ static int hhf_init(struct Qdisc *sch, struct nlattr *opt,int i; sch->limit =1000 ; - q->quantum =psched_mtu (qdisc_dev (sch)); + q->quantum =clamp_t (u32,psched_mtu (qdisc_dev (sch)),256 ,1 <<20 );get_random_bytes (&q->perturbation,sizeof (q->perturbation));INIT_LIST_HEAD (&q->new_buckets);INIT_LIST_HEAD (&q->old_buckets);
@@ -623,10 +623,6 @@ static int hhf_init(struct Qdisc *sch, struct nlattr *opt, q->hhf_evict_timeout =HZ ;/* 1 sec */ q->hhf_non_hh_weight =2 ; -if ((int )q->quantum <=0 || - (u64 )q->quantum * q->hhf_non_hh_weight >INT_MAX ) - q->quantum =256 ; -if (opt) {int err =hhf_change (sch, opt, extack);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 822/1250 net/sched: dualpi2: clamp psched_mtu at all call sites
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit 3c01f1ca5dfc6d6911b0e5b37f5062b1dc451b94 ]
dualpi2_calculate_c_protection(), must_drop(), and get_memory_limit() call psched_mtu() with no clamp. A huge MTU makes (s32)psched_mtu() overflow in the signed multiply for c_protection_init, and 2 * psched_mtu() wraps in get_memory_limit(). With a crafted size table qdisc_pkt_len reaches ~2 GiB, causing a soft lockup / denial of service.
Clamp psched_mtu() to [1, 1<<20] at all three call sites.
Conditions to recreate the bug:
CONFIG_NET_SCH_DUALPI2=y. Requires CAP_NET_ADMIN (namespace-local via unshare -Urn suffices).
tc qdisc add dev dummy0 root dualpi2
tc qdisc change dev dummy0 root dualpi2 stab data 32768 size_log 15 cell_log 0
--- net/sched/sch_dualpi2.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
net/sched/sch_dualpi2.c +7 -3
@@ -208,9 +208,11 @@ static void dualpi2_reset_c_protection(struct dualpi2_sched_data *q)static void dualpi2_calculate_c_protection (struct Qdisc *sch,struct dualpi2_sched_data *q,u32 wc) { +u32 mtu =clamp_t (u32,psched_mtu (qdisc_dev (sch)),1 ,1 <<20 ); + q->c_protection_wc = wc; q->c_protection_wl =MAX_WC - wc; - q->c_protection_init = (s32 )psched_mtu (qdisc_dev (sch)) * + q->c_protection_init = (s32 )mtu * ((int )q->c_protection_wc - (int )q->c_protection_wl);dualpi2_reset_c_protection (q); }
@@ -285,8 +287,9 @@ static bool must_drop(struct Qdisc *sch, struct dualpi2_sched_data *q,u64 local_l_prob;bool overload;u32 prob; +u32 mtu =clamp_t (u32,psched_mtu (qdisc_dev (sch)),1 ,1 <<20 ); -if (sch->qstats.backlog <2 *psched_mtu (qdisc_dev (sch))) +if (sch->qstats.backlog <2 * mtu)return false; prob =READ_ONCE (q->pi2_prob);
@@ -715,7 +718,8 @@ static u32 get_memory_limit(struct Qdisc *sch, u32 limit)/* Apply rule of thumb, i.e., doubling the packet length, * to further include per packet overhead in memory_limit. */ -u64 memlim =mul_u32_u32 (limit,2 *psched_mtu (qdisc_dev (sch))); +u64 memlim =mul_u32_u32 (limit,2 *clamp_t (u32,psched_mtu (qdisc_dev (sch)), +1 ,1 <<20 ));if (upper_32_bits (memlim))return U32_MAX ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 823/1250 net/sched: pie: clamp psched_mtu in pie_drop_early
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit 54370e44c002770ae61fc889f28f699e91616ffc ]
pie_drop_early() calls psched_mtu() with no clamp. With mtu=0x80000000 the bytemode divide silently zeroes the drop probability, disabling AQM. Clamp to [1, 1<<20].
Conditions to recreate the bug:
CONFIG_NET_SCH_PIE=y. Requires CAP_NET_ADMIN (namespace-local via unshare -Urn suffices).
tc qdisc add dev dummy0 root pie
tc qdisc change dev dummy0 root pie stab data 32768 size_log 15 cell_log 0
--- net/sched/sch_pie.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/sched/sch_pie.c +1 -1
@@ -35,7 +35,7 @@ bool pie_drop_early(struct Qdisc *sch, struct pie_params *params, {u64 rnd;u64 local_prob = vars->prob; -u32 mtu =psched_mtu (qdisc_dev (sch)); +u32 mtu =clamp_t (u32,psched_mtu (qdisc_dev (sch)),1 ,1 <<20 );/* If there is still burst allowance left skip random early drop */ if (vars->burst_time >0 )
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 824/1250 net/sched: drr: clamp quantum in change class
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit 8382abec0f1568d0a5590d75a3df92f23fcf5196 ]
drr_change_class() rejects explicit quantum==0 but falls back to psched_mtu() with no floor. With a crafted size table qdisc_pkt_len reaches ~2 GiB, so quantum=1 (or a zero psched_mtu on a headerless device) makes the deficit-refill loop spin under the qdisc lock.
Add clamp_t(u32, quantum, 256, 1<<20) after the zero reject and on the fallback path. The explicit-zero reject is preserved.
Conditions to recreate the bug:
CONFIG_NET_SCH_DRR=y. Requires CAP_NET_ADMIN (namespace-local via unshare -Urn suffices).
tc qdisc add dev dummy0 root drr
tc class add dev dummy0 parent 1: classid 1:1 drr quantum 1
--- net/sched/sch_drr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/sched/sch_drr.c +2 -1
@@ -82,8 +82,9 @@ static int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid,NL_SET_ERR_MSG (extack,"Specified DRR quantum cannot be zero" );return -EINVAL ; } + quantum =clamp_t (u32, quantum,256 ,1 <<20 ); }else - quantum =psched_mtu (qdisc_dev (sch)); + quantum =clamp_t (u32, (u32 )psched_mtu (qdisc_dev (sch)),256 ,1 <<20 );if (cl !=NULL ) {if (tca[TCA_RATE ]) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 825/1250 net/sched: ets: clamp quantum in parse and fallback paths
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit 1c38487f46b243bfeefec0c0c86023a3904f2214 ]
ets_qdisc_change() falls back to psched_mtu() with no floor for bands without an explicit quantum. With a crafted size table qdisc_pkt_len reaches ~2 GiB, so a zero psched_mtu on a headerless device makes the deficit-refill loop spin under the qdisc lock.
Move the floor into ets_quantum_parse() so explicitly configured quanta are also clamped to [256, 1<<20], not just the fallback path.
Conditions to recreate the bug:
CONFIG_NET_SCH_ETS=y. Requires CAP_NET_ADMIN (namespace-local via unshare -Urn suffices).
tc qdisc add dev dummy0 root ets bands 3 strict 2 quanta 1 1
--- net/sched/sch_ets.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-)
net/sched/sch_ets.c +5 -7
@@ -83,11 +83,7 @@ static int ets_quantum_parse(struct Qdisc *sch, const struct nlattr *attr,unsigned int *quantum, struct netlink_ext_ack *extack) { - *quantum =nla_get_u32 (attr); -if (!*quantum) { -NL_SET_ERR_MSG (extack,"ETS quantum cannot be zero" ); -return -EINVAL ; - } + *quantum =clamp_t (u32,nla_get_u32 (attr),256 ,1 <<20 );return 0 ; }
@@ -634,11 +630,13 @@ static int ets_qdisc_change(struct Qdisc *sch, struct nlattr *opt,return err; }/* If there are more bands than strict + quanta provided, the remaining -* ones are ETS with quantum of MTU. Initialize the missing values here. +* ones are ETS with quantum of max(MTU, 256). Initialize the missing +* values here. */ for (i = nstrict; i < nbands; i++) {if (!quanta[i]) - quanta[i] =psched_mtu (qdisc_dev (sch)); + quanta[i] =clamp_t (u32, (u32 )psched_mtu (qdisc_dev (sch)), +256 ,1 <<20 ); }/* Before commit, make sure we can allocate all new qdiscs */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 826/1250 net: macb: rename bp->sgmii_phy field to bp->phy
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Théo Lebrun <[email protected]>
[ Upstream commit 3f7e51cd5fbf4d970b14956ee9464515bb40666f ]
The bp->sgmii_phy field is initialised at probe by init_reset_optional() if bp->phy_interface == PHY_INTERFACE_MODE_SGMII. It gets used by:
- zynqmp_config: "cdns,zynqmp-gem" or "xlnx,zynqmp-gem" compatibles.
- mpfs_config: "microchip,mpfs-macb" compatible.
- versal_config: "xlnx,versal-gem" compatible.
Make name more generic as EyeQ5 requires the PHY in SGMII & RGMII cases.
Drop "for ZynqMP SGMII mode" comment that is already a lie, as it gets used on Microchip platforms as well. And soon it won't be SGMII-only.
Stable-dep-of: 38b6be101006 ("net: macb: fix NULL pointer dereference on unbind with fixed-link")
--- drivers/net/ethernet/cadence/macb.h | 2 +- drivers/net/ethernet/cadence/macb_main.c | 26 ++++++++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-)
drivers/net/ethernet/cadence/macb.h +1 -1
@@ -1347,7 +1347,7 @@ struct macb {struct macb_ptp_info *ptp_info;/* macb-ptp interface */ -struct phy *sgmii_phy;/* for ZynqMP SGMII mode */ +struct phy *phy;#ifdef MACB_EXT_DESC uint8_t hw_dma_cap;
drivers/net/ethernet/cadence/macb_main.c +13 -13
@@ -3113,7 +3113,7 @@ static int macb_open(struct net_device *dev)macb_init_hw (bp); - err =phy_power_on (bp->sgmii_phy); + err =phy_power_on (bp->phy);if (err) goto reset_hw;
@@ -3129,7 +3129,7 @@ static int macb_open(struct net_device *dev)return 0 ; phy_off: -phy_power_off (bp->sgmii_phy); +phy_power_off (bp->phy); reset_hw:macb_reset_hw (bp);
@@ -3161,7 +3161,7 @@ static int macb_close(struct net_device *dev)phylink_stop (bp->phylink);phylink_disconnect_phy (bp->phylink); -phy_power_off (bp->sgmii_phy); +phy_power_off (bp->phy);spin_lock_irqsave (&bp->lock, flags);macb_reset_hw (bp);
@@ -5294,13 +5294,13 @@ static int init_reset_optional(struct platform_device *pdev)if (bp->phy_interface ==PHY_INTERFACE_MODE_SGMII ) {/* Ensure PHY device used in SGMII mode is ready */ - bp->sgmii_phy =devm_phy_optional_get (&pdev->dev,NULL ); + bp->phy =devm_phy_optional_get (&pdev->dev,NULL ); -if (IS_ERR (bp->sgmii_phy)) -return dev_err_probe (&pdev->dev,PTR_ERR (bp->sgmii_phy), +if (IS_ERR (bp->phy)) +return dev_err_probe (&pdev->dev,PTR_ERR (bp->phy),"failed to get SGMII PHY\n" ); - ret =phy_init (bp->sgmii_phy); + ret =phy_init (bp->phy);if (ret)return dev_err_probe (&pdev->dev, ret,"failed to init SGMII PHY\n" );
@@ -5329,7 +5329,7 @@ static int init_reset_optional(struct platform_device *pdev)/* Fully reset controller at hardware level if mapped in device tree */ ret =device_reset_optional (&pdev->dev);if (ret) { -phy_exit (bp->sgmii_phy); +phy_exit (bp->phy);return dev_err_probe (&pdev->dev, ret,"failed to reset controller" ); }
@@ -5337,7 +5337,7 @@ static int init_reset_optional(struct platform_device *pdev) err_out_phy_exit:if (ret) -phy_exit (bp->sgmii_phy); +phy_exit (bp->phy);return ret; }
@@ -5752,7 +5752,7 @@ static int macb_probe(struct platform_device *pdev)mdiobus_free (bp->mii_bus); err_out_phy_exit: -phy_exit (bp->sgmii_phy); +phy_exit (bp->phy); err_out_free_netdev:free_netdev (dev);
@@ -5776,7 +5776,7 @@ static void macb_remove(struct platform_device *pdev)if (dev) { bp =netdev_priv (dev);unregister_netdev (dev); -phy_exit (bp->sgmii_phy); +phy_exit (bp->phy);mdiobus_unregister (bp->mii_bus);mdiobus_free (bp->mii_bus);
@@ -5803,7 +5803,7 @@ static int __maybe_unused macb_suspend(struct device *dev)int err;if (!device_may_wakeup (&bp->dev->dev)) -phy_exit (bp->sgmii_phy); +phy_exit (bp->phy);if (!netif_running (netdev))return 0 ;
@@ -5941,7 +5941,7 @@ static int __maybe_unused macb_resume(struct device *dev)int err;if (!device_may_wakeup (&bp->dev->dev)) -phy_init (bp->sgmii_phy); +phy_init (bp->phy);if (!netif_running (netdev))return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 827/1250 net: macb: fix NULL pointer dereference on unbind with fixed-link
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vineeth Karumanchi <[email protected]>
[ Upstream commit 38b6be101006d3e7af972999f45d4f1e8250587a ]
When the device tree describes a fixed-link and has no "mdio" child node, macb_mii_init() returns early without allocating the MDIO bus, leaving bp->mii_bus as NULL.
Two cleanup paths then dereference this NULL bus:
1. On driver unbind, macb_remove() unconditionally calls mdiobus_unregister(bp->mii_bus), which oopses:
Unable to handle kernel NULL pointer dereference at virtual address 00000000000004a8 pc : mdiobus_unregister+0x14/0xa4 lr : macb_remove+0x38/0xa4 Call trace: mdiobus_unregister+0x14/0xa4 (P) macb_remove+0x38/0xa4 platform_remove+0x20/0x30 device_release_driver_internal+0x1c8/0x224 unbind_store+0xb4/0xbc
2. On the probe error path in macb_probe(), reached when macb_mii_init() has succeeded but a subsequent step fails, the err_out_unregister_mdio label runs the same unconditional cleanup.
mdiobus_unregister() and mdiobus_free() do not guard against a NULL bus, so guard the calls in both macb_remove() and the probe error path.
--- drivers/net/ethernet/cadence/macb_main.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
drivers/net/ethernet/cadence/macb_main.c +8 -4
@@ -5748,8 +5748,10 @@ static int macb_probe(struct platform_device *pdev)return 0 ; err_out_unregister_mdio: -mdiobus_unregister (bp->mii_bus); -mdiobus_free (bp->mii_bus); +if (bp->mii_bus) { +mdiobus_unregister (bp->mii_bus); +mdiobus_free (bp->mii_bus); + } err_out_phy_exit:phy_exit (bp->phy);
@@ -5777,8 +5779,10 @@ static void macb_remove(struct platform_device *pdev) bp =netdev_priv (dev);unregister_netdev (dev);phy_exit (bp->phy); -mdiobus_unregister (bp->mii_bus); -mdiobus_free (bp->mii_bus); +if (bp->mii_bus) { +mdiobus_unregister (bp->mii_bus); +mdiobus_free (bp->mii_bus); + }device_set_wakeup_enable (&bp->pdev->dev,0 );cancel_work_sync (&bp->hresp_err_bh_work);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 828/1250 bpf: Reject non-scalar bpf_loop iteration counts
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kumar Kartikeya Dwivedi <[email protected]>
[ Upstream commit c3fd8e5fd100f122bad503bdc0e9277219533253 ]
bpf_loop() declares its nr_loops argument as ARG_ANYTHING. Privileged programs may pass pointer values to such arguments, so check_func_arg() lets a pointer-valued R1 reach the helper-specific checks.
Since commit bb124da69c47 ("bpf: keep track of max number of bpf_loop callback iterations"), the verifier marks R1 precise and reads its upper bound to limit callback simulation. Precision backtracking only accepts scalar registers, so passing a pointer instead triggers the "backtracking misuse" verifier warning. Kernels with panic_on_warn enabled subsequently panic.
Introduce ARG_SCALAR for helper arguments that only accept scalar values and use it for bpf_loop() nr_loops. Generic helper argument validation then rejects pointers before loop inlining and precision processing.
--- include/linux/bpf.h | 1 + kernel/bpf/bpf_iter.c | 2 +- kernel/bpf/verifier.c | 1 + 3 files changed, 3 insertions(+), 1 deletion(-)
include/linux/bpf.h +1 -0
@@ -821,6 +821,7 @@ enum bpf_arg_type {ARG_PTR_TO_CTX ,/* pointer to context */ ARG_ANYTHING ,/* any (initialized) argument is ok */ +ARG_SCALAR ,/* scalar argument */ ARG_PTR_TO_SPIN_LOCK ,/* pointer to bpf_spin_lock */ ARG_PTR_TO_SOCK_COMMON ,/* pointer to sock_common */ ARG_PTR_TO_SOCKET, /* pointer to bpf_sock (fullsock) */
kernel/bpf/bpf_iter.c +1 -1
@@ -767,7 +767,7 @@ const struct bpf_func_proto bpf_loop_proto = { .func = bpf_loop, .gpl_only = false, .ret_type = RET_INTEGER, - .arg1_type = ARG_ANYTHING, + .arg1_type = ARG_SCALAR, .arg2_type = ARG_PTR_TO_FUNC, .arg3_type = ARG_PTR_TO_STACK_OR_NULL, .arg4_type = ARG_ANYTHING,
kernel/bpf/verifier.c +1 -0
@@ -9380,6 +9380,7 @@ static const struct bpf_reg_types *compatible_reg_types[__BPF_ARG_TYPE_MAX] = { [ARG_CONST_SIZE] = &scalar_types, [ARG_CONST_SIZE_OR_ZERO] = &scalar_types, [ARG_CONST_ALLOC_SIZE_OR_ZERO] = &scalar_types, + [ARG_SCALAR] = &scalar_types, [ARG_CONST_MAP_PTR] = &const_map_ptr_types, [ARG_PTR_TO_CTX] = &context_types, [ARG_PTR_TO_SOCK_COMMON] = &sock_types,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 829/1250 ALSA: caiaq: Decoupling ep1_in_urb in caiaq dev
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Edward Adam Davis <[email protected]>
[ Upstream commit 402a9d6aab7ac787ab075adeb562c3db8b8f564b ]
The epq_in_urb object belonging to the caiaq device is coupled within the struct snd_usb_caiaqdev. After usb_submit_urb(epq_in_urb, GFP_KERNEL) executes successfully, epq_in_urb is successfully added to the urbp_list queue of the dummy HCD driver (userspace specifies dummy_hcd as the HCD layer driver for the caiaq USB device).
When init_card() calls snd_usb_caiaq_send_command() which subsequently fails due to a timeout, and proceeds to call snd_card_free() to release the card, the embedded ep1_in_urb object is also freed. When the dummy HCD driver detects that the URB has been unlinked, it returns the URB (by usb_hcd_giveback_urb()), which triggers [1].
Decouple the ep1_in_urb object from the struct snd_usb_caiaqdev and switch to using a pointer instead. Separately allocate and manage the memory for ep1_in_urb to prevent the release of the snd_card memory object from interfering with it.
midi_out_urb has the same issue as ep1_in_urb and is handled in the same way.
[1]
BUG: KASAN: slab-use-after-free in usb_free_urb+0x24/0x120 drivers/usb/core/urb.c:96 Write of size 4 at addr ffff88803cee1050 by task ktimers/1/29 Call Trace: usb_free_urb+0x24/0x120 drivers/usb/core/urb.c:96 dummy_timer+0xaac/0x4d50 drivers/usb/gadget/udc/dummy_hcd.c:2019 __run_hrtimer kernel/time/hrtimer.c:2067 [inline] __hrtimer_run_queues+0x3eb/0xaf0 kernel/time/hrtimer.c:2124 hrtimer_run_softirq+0x1e1/0x2e0 kernel/time/hrtimer.c:2141 Allocated by task 36: snd_card_new+0x7b/0x110 sound/core/init.c:184 create_card sound/usb/caiaq/device.c:429 [inline] snd_probe+0x236/0x1af0 sound/usb/caiaq/device.c:544 Freed by task 36: snd_card_free_when_closed sound/core/init.c:630 [inline] snd_card_free+0x138/0x1d0 sound/core/init.c:662 snd_probe+0x162b/0x1af0 sound/usb/caiaq/device.c:553
--- sound/usb/caiaq/device.c | 36 ++++++++++++++++++++++++------------ sound/usb/caiaq/device.h | 4 ++-- sound/usb/caiaq/midi.c | 6 +++--- 3 files changed, 29 insertions(+), 17 deletions(-)
sound/usb/caiaq/device.c +24 -12
@@ -192,8 +192,8 @@ static void usb_ep1_command_reply_dispatch (struct urb* urb)break ; } - cdev->ep1_in_urb.actual_length =0 ; - ret =usb_submit_urb (&cdev->ep1_in_urb,GFP_ATOMIC ); + cdev->ep1_in_urb->actual_length =0 ; + ret =usb_submit_urb (cdev->ep1_in_urb,GFP_ATOMIC );if (ret <0 )dev_err (dev,"unable to submit urb. OOM!?\n" ); }
@@ -408,6 +408,10 @@ static void card_free(struct snd_card *card)#endif snd_usb_caiaq_audio_free (cdev);usb_put_dev (cdev->chip.dev); +usb_free_urb (cdev->ep1_in_urb); + cdev->ep1_in_urb =NULL ; +usb_free_urb (cdev->midi_out_urb); + cdev->midi_out_urb =NULL ; } static int create_card(struct usb_device *usb_dev,
@@ -457,22 +461,30 @@ static int init_card(struct snd_usb_caiaqdev *cdev)return -EIO ; } -usb_init_urb (&cdev->ep1_in_urb); -usb_init_urb (&cdev->midi_out_urb); + cdev->ep1_in_urb =usb_alloc_urb (0 ,GFP_KERNEL ); +if (!cdev->ep1_in_urb) +return -ENOMEM ; -usb_fill_bulk_urb (&cdev->ep1_in_urb, usb_dev, + cdev->midi_out_urb =usb_alloc_urb (0 ,GFP_KERNEL ); +if (!cdev->midi_out_urb) { +usb_free_urb (cdev->ep1_in_urb); + cdev->ep1_in_urb =NULL ; +return -ENOMEM ; + } + +usb_fill_bulk_urb (cdev->ep1_in_urb, usb_dev,usb_rcvbulkpipe (usb_dev,0x1 ), cdev->ep1_in_buf,EP1_BUFSIZE , usb_ep1_command_reply_dispatch, cdev); -usb_fill_bulk_urb (&cdev->midi_out_urb, usb_dev, +usb_fill_bulk_urb (cdev->midi_out_urb, usb_dev,usb_sndbulkpipe (usb_dev,0x1 ), cdev->midi_out_buf,EP1_BUFSIZE , snd_usb_caiaq_midi_output_done, cdev);/* sanity checks of EPs before actually submitting */ -if (usb_urb_ep_type_check (&cdev->ep1_in_urb) || -usb_urb_ep_type_check (&cdev->midi_out_urb)) { +if (usb_urb_ep_type_check (cdev->ep1_in_urb) || +usb_urb_ep_type_check (cdev->midi_out_urb)) {dev_err (dev,"invalid EPs\n" );return -EINVAL ; }
@@ -480,7 +492,7 @@ static int init_card(struct snd_usb_caiaqdev *cdev)init_waitqueue_head (&cdev->ep1_wait_queue);init_waitqueue_head (&cdev->prepare_wait_queue); -if (usb_submit_urb (&cdev->ep1_in_urb,GFP_KERNEL ) !=0 ) +if (usb_submit_urb (cdev->ep1_in_urb,GFP_KERNEL ) !=0 )return -EIO ; err =snd_usb_caiaq_send_command (cdev,EP1_CMD_GET_DEVICE_INFO ,NULL ,0 );
@@ -530,7 +542,7 @@ static int init_card(struct snd_usb_caiaqdev *cdev)return 0 ; err_kill_urb: -usb_kill_urb (&cdev->ep1_in_urb); +usb_kill_urb (cdev->ep1_in_urb);return err; }
@@ -576,8 +588,8 @@ static void snd_disconnect(struct usb_interface *intf)#endif snd_usb_caiaq_audio_disconnect (cdev); -usb_kill_urb (&cdev->ep1_in_urb); -usb_kill_urb (&cdev->midi_out_urb); +usb_kill_urb (cdev->ep1_in_urb); +usb_kill_urb (cdev->midi_out_urb);snd_card_free_when_closed (card); }
sound/usb/caiaq/device.h +2 -2
@@ -60,8 +60,8 @@ struct snd_usb_caiaq_cb_info;struct snd_usb_caiaqdev {struct snd_usb_audio chip; -struct urb ep1_in_urb; -struct urb midi_out_urb; +struct urb *ep1_in_urb; +struct urb *midi_out_urb;struct urb **data_urbs_in;struct urb **data_urbs_out;struct snd_usb_caiaq_cb_info *data_cb_info;
sound/usb/caiaq/midi.c +3 -3
@@ -43,7 +43,7 @@ static int snd_usb_caiaq_midi_output_close(struct snd_rawmidi_substream *substre {struct snd_usb_caiaqdev *cdev = substream->rmidi->private_data;if (cdev->midi_out_active) { -usb_kill_urb (&cdev->midi_out_urb); +usb_kill_urb (cdev->midi_out_urb); cdev->midi_out_active =0 ; }return 0 ;
@@ -64,9 +64,9 @@ static void snd_usb_caiaq_midi_send(struct snd_usb_caiaqdev *cdev,return ; cdev->midi_out_buf[2 ] = len; - cdev->midi_out_urb.transfer_buffer_length = len+3 ; + cdev->midi_out_urb->transfer_buffer_length = len+3 ; - ret =usb_submit_urb (&cdev->midi_out_urb,GFP_ATOMIC ); + ret =usb_submit_urb (cdev->midi_out_urb,GFP_ATOMIC );if (ret <0 )dev_err (dev, "snd_usb_caiaq_midi_send(%p): usb_submit_urb() failed,"
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 830/1250 iommu/riscv: Add command queue lock
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fangyu Yu <[email protected]>
[ Upstream commit d5bcf9ccaa357396089bbfa47fc82b093f109b1e ]
Add a raw spinlock to the RISC-V IOMMU queue state so command queue publishing can be serialized by a later change.
--- drivers/iommu/riscv/iommu.c | 1 + drivers/iommu/riscv/iommu.h | 2 ++ 2 files changed, 3 insertions(+)
drivers/iommu/riscv/iommu.c +1 -0
@@ -1687,6 +1687,7 @@ int riscv_iommu_init(struct riscv_iommu_device *iommu)int rc;RISCV_IOMMU_QUEUE_INIT (&iommu->cmdq,CQ ); +raw_spin_lock_init (&iommu->cmdq.lock);RISCV_IOMMU_QUEUE_INIT (&iommu->fltq,FQ ); rc =riscv_iommu_init_check (iommu);
drivers/iommu/riscv/iommu.h +2 -0
@@ -12,6 +12,7 @@#define _RISCV_IOMMU_H_#include <linux/iommu.h> +#include <linux/spinlock.h> #include <linux/types.h> #include <linux/iopoll.h>
@@ -23,6 +24,7 @@ struct riscv_iommu_queue {atomic_t prod;/* unbounded producer allocation index */ atomic_t head;/* unbounded shadow ring buffer consumer index */ atomic_t tail;/* unbounded shadow ring buffer producer index */ +raw_spinlock_t lock;/* serialize queue publishing */ unsigned int mask;/* index mask, queue length - 1 */ unsigned int irq;/* allocated interrupt number */ struct riscv_iommu_device *iommu;/* iommu device handling the queue when active */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 831/1250 iommu/riscv: Disable SADE
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jason Gunthorpe <[email protected]>
[ Upstream commit e93e4a6363b8f812cb89f2b2d97cbaf017a3d7f8 ]
In terms of the iommu subystem the SADE/GADE feature "3.4. IOMMU updating of PTE accessed (A) and dirty (D) updates" is called dirty tracking.
There is no reason to enable HW support for this, and the HW cost associated with it, unless dirty tracking is actually enabled through iommufd. It should be a dynamic feature linked to user request.
Further, without implementing the read dirty ops the whole thing is pointless.
Do not set DC.tc.SADE just because the HW has support for dirty tracking.
Stable-dep-of: ca58afa40946 ("iommu/riscv: Serialize command queue publishing")
--- drivers/iommu/riscv/iommu.c | 6 ------ 1 file changed, 6 deletions(-)
drivers/iommu/riscv/iommu.c +0 -6
@@ -813,7 +813,6 @@ struct riscv_iommu_domain {struct list_head bonds;spinlock_t lock;/* protect bonds list updates. */ int pscid; -bool amo_enabled;int numa_node;unsigned int pgd_mode;unsigned long *pgd_root;
@@ -1275,8 +1274,6 @@ static int riscv_iommu_map_pages(struct iommu_domain *iommu_domain,if (!(prot &IOMMU_WRITE )) pte_prot = _PAGE_BASE | _PAGE_READ; -else if (domain->amo_enabled) - pte_prot = _PAGE_BASE | _PAGE_READ | _PAGE_WRITE;else pte_prot = _PAGE_BASE | _PAGE_READ | _PAGE_WRITE | _PAGE_DIRTY;
@@ -1460,7 +1457,6 @@ static struct iommu_domain *riscv_iommu_alloc_paging_domain(struct device *dev)INIT_LIST_HEAD_RCU (&domain->bonds);spin_lock_init (&domain->lock); domain->numa_node =dev_to_node (iommu->dev); - domain->amo_enabled = !!(iommu->caps &RISCV_IOMMU_CAPABILITIES_AMO_HWAD ); domain->pgd_mode = pgd_mode; domain->pgd_root =iommu_alloc_pages_node_sz (domain->numa_node,GFP_KERNEL_ACCOUNT ,SZ_4K );
@@ -1583,8 +1579,6 @@ static struct iommu_device *riscv_iommu_probe_device(struct device *dev)* the device directory. Do not mark the context valid yet. */ tc =0 ; -if (iommu->caps &RISCV_IOMMU_CAPABILITIES_AMO_HWAD ) - tc |=RISCV_IOMMU_DC_TC_SADE ;for (i =0 ; i < fwspec->num_ids; i++) { dc =riscv_iommu_get_dc (iommu, fwspec->ids[i]);if (!dc) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 832/1250 iommu/amd: Add NUMA node affinity for IOMMU log buffers
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Li RongQing <[email protected]>
[ Upstream commit 1e0c8d6b695217cb0b16f13e31c7f08b453097b4 ]
Currently, PPR Log and GA logs for AMD IOMMU are allocated using iommu_alloc_pages_sz(), which does not account for NUMA affinity. This can lead to remote memory access latencies if the memory is allocated on a different node than the IOMMU hardware.
Switch to iommu_alloc_pages_node_sz() to ensure that these data structures are allocated on the same NUMA node as the IOMMU device. If the node information is unavailable, it defaults to NUMA_NO_NODE.
Stable-dep-of: 00a7dd64888d ("iommu/amd: Do not reallocate GA log buffers on resume")
--- drivers/iommu/amd/init.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
drivers/iommu/amd/init.c +6 -3
@@ -854,10 +854,11 @@ static void __init free_command_buffer(struct amd_iommu *iommu)void *__init iommu_alloc_4k_pages (struct amd_iommu *iommu,gfp_t gfp,size_t size) { +int nid = iommu->dev ?dev_to_node (&iommu->dev->dev) :NUMA_NO_NODE ;void *buf; size =PAGE_ALIGN (size); - buf =iommu_alloc_pages_sz (gfp, size); + buf =iommu_alloc_pages_node_sz (nid, gfp, size);if (!buf)return NULL ; if (check_feature(FEATURE_SNP) &&
@@ -972,14 +973,16 @@ static int iommu_ga_log_enable(struct amd_iommu *iommu)static int iommu_init_ga_log (struct amd_iommu *iommu) { +int nid = iommu->dev ?dev_to_node (&iommu->dev->dev) :NUMA_NO_NODE ; +if (!AMD_IOMMU_GUEST_IR_VAPIC (amd_iommu_guest_ir))return 0 ; - iommu->ga_log =iommu_alloc_pages_sz (GFP_KERNEL ,GA_LOG_SIZE ); + iommu->ga_log =iommu_alloc_pages_node_sz (nid,GFP_KERNEL ,GA_LOG_SIZE );if (!iommu->ga_log) goto err_out; - iommu->ga_log_tail =iommu_alloc_pages_sz (GFP_KERNEL ,8 ); + iommu->ga_log_tail =iommu_alloc_pages_node_sz (nid,GFP_KERNEL ,8 );if (!iommu->ga_log_tail) goto err_out;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 833/1250 iommu/amd: Do not reallocate GA log buffers on resume
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Karl Mehltretter <[email protected]>
[ Upstream commit 00a7dd64888d6dd72110b40e2824a088cf7b7386 ]
Commit c5e1a1eb9279 ("iommu/amd: Simplify and Consolidate Virtual APIC (AVIC) Enablement") moved the GA log allocation from iommu_init_pci() to enable_iommus_vapic(), which is called on every resume.
iommu_init_ga_log() assigns iommu->ga_log and iommu->ga_log_tail unconditionally. Each resume therefore replaces the boot-time pointers and leaks both old allocations. The function also uses GFP_KERNEL from a syscore resume callback, where interrupts are disabled and the non-boot CPUs are offline.
Return early if both buffers are already allocated. Clear the pointers in free_ga_log() so a partial allocation failure cannot leave ga_log dangling.
--- drivers/iommu/amd/init.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/iommu/amd/init.c +5 -0
@@ -931,7 +931,9 @@ static void free_ga_log(struct amd_iommu *iommu) {#ifdef CONFIG_IRQ_REMAP iommu_free_pages (iommu->ga_log); + iommu->ga_log =NULL ;iommu_free_pages (iommu->ga_log_tail); + iommu->ga_log_tail =NULL ;#endif }
@@ -978,6 +980,9 @@ static int iommu_init_ga_log(struct amd_iommu *iommu)if (!AMD_IOMMU_GUEST_IR_VAPIC (amd_iommu_guest_ir))return 0 ; +if (iommu->ga_log && iommu->ga_log_tail) +return 0 ; + iommu->ga_log =iommu_alloc_pages_node_sz (nid,GFP_KERNEL ,GA_LOG_SIZE );if (!iommu->ga_log) goto err_out;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 834/1250 iommu/amd: Fix premature break in init_iommu_one() again
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vasant Hegde <[email protected]>
[ Upstream commit eb29b7bbc8ba28bbb0b9fdd655e931e1d1fa625c ]
Commit 283d245468a2 ("iommu/amd: Fix premature break in init_iommu_one()") unintentionally broke older platforms - such as the ASRockRack B550D4-4L - where the BIOS advertises incorrect IOMMU features.
Move the HATDis check ahead of the GASup check, and re-introduce the break inside the GASup check to restore correct behavior on affected platforms.
This is a short-term fix to resolve the regression. Longer term, we should rework how EFRs are tracked and prioritize the MMIO-advertised EFR over the one reported via IVRS. That requires more extensive changes and will be addressed separately.
--- drivers/iommu/amd/init.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-)
drivers/iommu/amd/init.c +6 -5
@@ -1938,19 +1938,20 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h,else iommu->mmio_phys_end =MMIO_CNTR_CONF_OFFSET ; +if (h->efr_attr &BIT (IOMMU_IVHD_ATTR_HATDIS_SHIFT )) { +pr_warn_once ("Host Address Translation is not supported.\n" ); + amd_iommu_hatdis = true; + } +/* XT and GAM require GA mode. */ if ((h->efr_reg & (0x1 <<IOMMU_EFR_GASUP_SHIFT )) ==0 ) { amd_iommu_guest_ir =AMD_IOMMU_GUEST_IR_LEGACY ; +break ; }else {if (h->efr_reg &BIT (IOMMU_EFR_XTSUP_SHIFT )) amd_iommu_xt_mode =IRQ_REMAP_X2APIC_MODE ; } -if (h->efr_attr &BIT (IOMMU_IVHD_ATTR_HATDIS_SHIFT )) { -pr_warn_once ("Host Address Translation is not supported.\n" ); - amd_iommu_hatdis = true; - } -early_iommu_features_init (iommu, h);break ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 835/1250 hwmon: (ltc4282) Make sure clk_init_data is fully initialized
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Geert Uytterhoeven <[email protected]>
[ Upstream commit e317326d1755ea054b98e7a4833b929157461c35 ]
The clk_init_data structure contains several mutually-exclusive members for different methods to specify the possible parents of a clock, prompting drivers to initialize only the members they need. However, not initializing all members may cause subtle issues, which are only exposed when CONFIG_INIT_STACK_ALL_PATTERN or CONFIG_INIT_STACK_NONE is enabled.
ltc428_clk_provider_setup() does not fill in any parent clocks, and assumes that init.num_parents is NULL. However, the latter in uninitialized, and thus may cause a crash.
Make sure all members are fully initialized, to fix such bugs, and to avoid future breakage when converting drivers to a different method for specifying the parents.
--- drivers/hwmon/ltc4282.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hwmon/ltc4282.c +1 -1
@@ -1140,7 +1140,7 @@ static const struct clk_ops ltc4282_ops = {static int ltc428_clk_provider_setup (struct ltc4282_state *st,struct device *dev) { -struct clk_init_data init; +struct clk_init_data init = {};int ret;if (!IS_ENABLED (CONFIG_COMMON_CLK ))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 836/1250 Documentation/hwmon: Document hwmon_notify_event()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guenter Roeck <[email protected]>
[ Upstream commit b4fffa75c1d6f87e6dc6191dec900f2b5bd23a1c ]
The hwmon core provides hwmon_notify_event() for drivers to report events such as alarm or fault conditions to userspace via sysfs notifications and uevents, as well as to the thermal subsystem for temperature sensors. However, this function is not documented in the hwmon kernel API guide.
Add the function prototype and description of hwmon_notify_event() to Documentation/hwmon/hwmon-kernel-api.rst.
--- Documentation/hwmon/hwmon-kernel-api.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
Documentation/hwmon/hwmon-kernel-api.rst +15 -0
@@ -42,6 +42,9 @@ register/unregister functions:: char *devm_hwmon_sanitize_name(struct device *dev, const char *name); + int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel); + void hwmon_lock(struct device *dev); void hwmon_unlock(struct device *dev);
@@ -90,6 +93,18 @@ implemented in the driver, or debugfs functions, hwmon_lock() and hwmon_unlock() can be used to ensure that calls to those functions are serialized. Those functions also support guard() and scoped_guard() variants. +Drivers can call hwmon_notify_event() to notify userspace and the thermal +subsystem when a hardware monitoring event (such as an alarm or a fault +condition) occurs or clears. The parameters are the hwmon device, the sensor +type, the attribute identifier associated with the event (such as +hwmon_temp_max_alarm or hwmon_fan_fault), and the sensor channel number. +hwmon_notify_event() generates a sysfs event (calling sysfs_notify()) and a +udev event with the attribute name passed in the NAME environment property +(e.g., "NAME=temp1_max_alarm"). If the event is for a temperature sensor and +the sensor is attached to a thermal zone, it also notifies the thermal +subsystem to update the thermal zone. hwmon_notify_event() returns 0 on +success or a negative error code on failure. + Using devm_hwmon_device_register_with_info() --------------------------------------------
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 837/1250 hwmon: Fix potential UAF in pec_store
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guenter Roeck <[email protected]>
[ Upstream commit 354ccc99b2dc8ba0cf6d4de34e520bcf6ecca5c2 ]
Sashiko reports:
In pec_store(), a guard(mutex)(&hwdev->lock) is taken. If the chip write operation returns an error other than -EOPNOTSUPP, the code jumps to the put label, which calls put_device(hdev). If this drops the final reference, the device is freed. When the function then returns, the guard cleanup function runs and attempts to unlock the freed mutex.
Use scoped_guard() instead of guard() to avoid the problem.
--- drivers/hwmon/hwmon.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-)
drivers/hwmon/hwmon.c +10 -11
@@ -371,18 +371,17 @@ static ssize_t pec_store(struct device *dev, struct device_attribute *devattr,* handling is not required. */ hwdev =to_hwmon_device (hdev); -guard (mutex)(&hwdev->lock); -if (hwdev->chip->ops->write) { - err = hwdev->chip->ops->write (hdev, hwmon_chip, hwmon_chip_pec,0 , val); -if (err && err != -EOPNOTSUPP ) - goto put; + scoped_guard(mutex, &hwdev->lock) { +if (hwdev->chip->ops->write) { + err = hwdev->chip->ops->write (hdev, hwmon_chip, hwmon_chip_pec,0 , val); +if (err && err != -EOPNOTSUPP ) + goto put; + } +if (!val) + client->flags &= ~I2C_CLIENT_PEC ; +else + client->flags |=I2C_CLIENT_PEC ; } - -if (!val) - client->flags &= ~I2C_CLIENT_PEC ; -else - client->flags |=I2C_CLIENT_PEC ; - err = count; put:put_device (hdev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 838/1250 hwmon: (ina2xx) Acquire hwmon_lock in shunt_resistor_show()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jared Kangas <[email protected]>
[ Upstream commit 8afc94bfb0ffdfc4a168081785820aa4818d1d23 ]
shunt_resistor_store() currently acquires hwmon_lock to set data->rshunt, but the corresponding access in shunt_resistor_show() is unprotected. Acquire the lock in shunt_resistor_show() as well to ensure proper synchronization.
--- drivers/hwmon/ina2xx.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/hwmon/ina2xx.c +5 -1
@@ -891,8 +891,12 @@ static ssize_t shunt_resistor_show(struct device *dev, struct device_attribute *da, char *buf) { struct ina2xx_data *data = dev_get_drvdata(dev); +long rshunt; -return sysfs_emit (buf,"%li\n" , data->rshunt); + scoped_guard(hwmon_lock, dev) { + rshunt = data->rshunt; + } +return sysfs_emit (buf,"%li\n" , rshunt); } static ssize_t shunt_resistor_store(struct device *dev,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 839/1250 hwmon: (ina2xx) Rely on subsystem locking
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guenter Roeck <[email protected]>
[ Upstream commit caff6fba5166b6da14ed00c64ad8acf376676e54 ]
Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it.
Stable-dep-of: 35760f5efd7b ("hwmon: (ina2xx) Decouple in0 and curr1 alarms")
--- drivers/hwmon/ina2xx.c | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-)
drivers/hwmon/ina2xx.c +10 -18
@@ -166,7 +166,6 @@ struct ina2xx_data {long rshunt;long current_lsb_uA;long power_lsb_uW; -struct mutex config_lock;struct regmap *regmap;struct i2c_client *client; };
@@ -436,22 +435,19 @@ static int ina226_alert_limit_read(struct ina2xx_data *data, u32 mask, int reg,int regval;int ret; -mutex_lock (&data->config_lock); ret =regmap_read (regmap,INA226_MASK_ENABLE , ®val);if (ret) - goto abort; +return ret;if (regval & mask) { ret =regmap_read (regmap,INA226_ALERT_LIMIT , ®val);if (ret) - goto abort; +return ret; *val =ina2xx_get_value (data, reg, regval); }else { *val =0 ; } -abort: -mutex_unlock (&data->config_lock); -return ret; +return 0 ; }static int ina226_alert_limit_write (struct ina2xx_data *data,u32 mask,int reg,long val)
@@ -467,23 +463,20 @@ static int ina226_alert_limit_write(struct ina2xx_data *data, u32 mask, int reg,* due to register write sequence. Then, only enable the alert * if the value is non-zero. */ -mutex_lock (&data->config_lock); ret =regmap_update_bits (regmap,INA226_MASK_ENABLE ,INA226_ALERT_CONFIG_MASK ,0 );if (ret <0 ) - goto abort; +return ret; ret =regmap_write (regmap,INA226_ALERT_LIMIT ,ina226_alert_to_reg (data, reg, val));if (ret <0 ) - goto abort; +return ret;if (val) - ret =regmap_update_bits (regmap,INA226_MASK_ENABLE , -INA226_ALERT_CONFIG_MASK , mask); -abort: -mutex_unlock (&data->config_lock); -return ret; +return regmap_update_bits (regmap,INA226_MASK_ENABLE , +INA226_ALERT_CONFIG_MASK , mask); +return 0 ; }static int ina2xx_chip_read (struct device *dev,u32 attr,long *val)
@@ -911,9 +904,9 @@ static ssize_t shunt_resistor_store(struct device *dev,if (status <0 )return status; -mutex_lock (&data->config_lock); +hwmon_lock (dev); status =ina2xx_set_shunt (data, val); -mutex_unlock (&data->config_lock); +hwmon_unlock (dev);if (status <0 )return status;return count;
@@ -1003,7 +996,6 @@ static int ina2xx_probe(struct i2c_client *client) data->client = client; data->config = &ina2xx_config[chip]; data->chip = chip; -mutex_init (&data->config_lock); data->regmap =devm_regmap_init_i2c (client, &ina2xx_regmap_config);if (IS_ERR (data->regmap)) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 840/1250 hwmon: (ina2xx) Parameterize ina2xx_data in ina226_alert_read()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jared Kangas <[email protected]>
[ Upstream commit 3d44ab826e0244a8b9eaf0f1f604f4cb8b890325 ]
Mirror ina226_alert_limit_read/write and use struct ina2xx_data instead of struct regmap in ina226_alert_read's parameters.
Stable-dep-of: 35760f5efd7b ("hwmon: (ina2xx) Decouple in0 and curr1 alarms")
--- drivers/hwmon/ina2xx.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
drivers/hwmon/ina2xx.c +7 -7
@@ -499,12 +499,12 @@ static int ina2xx_chip_read(struct device *dev, u32 attr, long *val)return 0 ; } -static int ina226_alert_read (struct regmap *regmap,u32 mask,long *val) +static int ina226_alert_read (struct ina2xx_data *data,u32 mask,long *val) {unsigned int regval;int ret; - ret =regmap_read_bypassed (regmap,INA226_MASK_ENABLE , ®val); + ret =regmap_read_bypassed (data->regmap,INA226_MASK_ENABLE , ®val);if (ret)return ret;
@@ -539,9 +539,9 @@ static int ina2xx_in_read(struct device *dev, u32 attr, int channel, long *val)return ina226_alert_limit_read (data, over_voltage_mask, voltage_reg, val);case hwmon_in_lcrit_alarm: -return ina226_alert_read (regmap, under_voltage_mask, val); +return ina226_alert_read (data, under_voltage_mask, val);case hwmon_in_crit_alarm: -return ina226_alert_read (regmap, over_voltage_mask, val); +return ina226_alert_read (data, over_voltage_mask, val);default :return -EOPNOTSUPP ; }
@@ -598,7 +598,7 @@ static int ina2xx_power_read(struct device *dev, u32 attr, long *val)return ina226_alert_limit_read (data,INA226_POWER_OVER_LIMIT_MASK ,INA2XX_POWER , val);case hwmon_power_crit_alarm: -return ina226_alert_read (data->regmap,INA226_POWER_OVER_LIMIT_MASK , val); +return ina226_alert_read (data,INA226_POWER_OVER_LIMIT_MASK , val);default :return -EOPNOTSUPP ; }
@@ -640,9 +640,9 @@ static int ina2xx_curr_read(struct device *dev, u32 attr, long *val)return ina226_alert_limit_read (data,INA226_SHUNT_OVER_VOLTAGE_MASK ,INA2XX_CURRENT , val);case hwmon_curr_lcrit_alarm: -return ina226_alert_read (regmap,INA226_SHUNT_UNDER_VOLTAGE_MASK , val); +return ina226_alert_read (data,INA226_SHUNT_UNDER_VOLTAGE_MASK , val);case hwmon_curr_crit_alarm: -return ina226_alert_read (regmap,INA226_SHUNT_OVER_VOLTAGE_MASK , val); +return ina226_alert_read (data,INA226_SHUNT_OVER_VOLTAGE_MASK , val);default :return -EOPNOTSUPP ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 841/1250 hwmon: (ina2xx) Replace masks with enum in alert functions
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jared Kangas <[email protected]>
[ Upstream commit e92b9208415a90e9cc1d923c5d8c058dde77be68 ]
Instead of passing an explicit mask to alert/limit functions like ina226_alert_read(), introduce an enum ina2xx_alert_type that can be converted to a mask internally. This semantically separates current from shunt voltage in helpers that use function masks, which previously saw the same mask for the two functions.
Stable-dep-of: 35760f5efd7b ("hwmon: (ina2xx) Decouple in0 and curr1 alarms")
--- drivers/hwmon/ina2xx.c | 90 +++++++++++++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 23 deletions(-)
drivers/hwmon/ina2xx.c +67 -23
@@ -144,6 +144,17 @@ enum ina2xx_ids {sy24655 }; +enum ina2xx_alert_type { +INA2XX_ALERT_NONE , +INA2XX_ALERT_CURRENT_LOW , +INA2XX_ALERT_CURRENT_HIGH , +INA2XX_ALERT_POWER_HIGH , +INA2XX_ALERT_BUS_VOLTAGE_LOW , +INA2XX_ALERT_BUS_VOLTAGE_HIGH , +INA2XX_ALERT_SHUNT_VOLTAGE_LOW , +INA2XX_ALERT_SHUNT_VOLTAGE_HIGH , +}; +struct ina2xx_config {u16 config_default;bool has_alerts;/* chip supports alerts and limits */
@@ -429,16 +440,43 @@ static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val) } } -static int ina226_alert_limit_read (struct ina2xx_data *data,u32 mask,int reg,long *val) +static u32 ina2xx_alert_type_to_mask (enum ina2xx_alert_type alert) +{ +switch (alert) { +case INA2XX_ALERT_CURRENT_LOW : +case INA2XX_ALERT_SHUNT_VOLTAGE_LOW : +return INA226_SHUNT_UNDER_VOLTAGE_MASK ; +case INA2XX_ALERT_CURRENT_HIGH : +case INA2XX_ALERT_SHUNT_VOLTAGE_HIGH : +return INA226_SHUNT_OVER_VOLTAGE_MASK ; +case INA2XX_ALERT_BUS_VOLTAGE_LOW : +return INA226_BUS_UNDER_VOLTAGE_MASK ; +case INA2XX_ALERT_BUS_VOLTAGE_HIGH : +return INA226_BUS_OVER_VOLTAGE_MASK ; +case INA2XX_ALERT_POWER_HIGH : +return INA226_POWER_OVER_LIMIT_MASK ; +case INA2XX_ALERT_NONE : +return 0 ; +default : +/* programmer error */ +WARN_ON_ONCE (1 ); +return 0 ; + } +} + +static int ina226_alert_limit_read (struct ina2xx_data *data,enum ina2xx_alert_type alert, +int reg,long *val) {struct regmap *regmap = data->regmap;int regval; +u32 mask;int ret; ret =regmap_read (regmap,INA226_MASK_ENABLE , ®val);if (ret)return ret; + mask =ina2xx_alert_type_to_mask (alert);if (regval & mask) { ret =regmap_read (regmap,INA226_ALERT_LIMIT , ®val);if (ret)
@@ -450,9 +488,11 @@ static int ina226_alert_limit_read(struct ina2xx_data *data, u32 mask, int reg,return 0 ; } -static int ina226_alert_limit_write (struct ina2xx_data *data,u32 mask,int reg,long val) +static int ina226_alert_limit_write (struct ina2xx_data *data,enum ina2xx_alert_type alert, +int reg,long val) {struct regmap *regmap = data->regmap; +u32 mask;int ret;if (val <0 )
@@ -473,9 +513,11 @@ static int ina226_alert_limit_write(struct ina2xx_data *data, u32 mask, int reg,if (ret <0 )return ret; -if (val) +if (val) { + mask =ina2xx_alert_type_to_mask (alert);return regmap_update_bits (regmap,INA226_MASK_ENABLE ,INA226_ALERT_CONFIG_MASK , mask); + }return 0 ; }
@@ -499,15 +541,17 @@ static int ina2xx_chip_read(struct device *dev, u32 attr, long *val)return 0 ; } -static int ina226_alert_read (struct ina2xx_data *data,u32 mask,long *val) +static int ina226_alert_read (struct ina2xx_data *data,enum ina2xx_alert_type alert,long *val) {unsigned int regval; +u32 mask;int ret; ret =regmap_read_bypassed (data->regmap,INA226_MASK_ENABLE , ®val);if (ret)return ret; + mask =ina2xx_alert_type_to_mask (alert); *val = (regval & mask) && (regval &INA226_ALERT_FUNCTION_FLAG );return 0 ;
@@ -516,10 +560,10 @@ static int ina226_alert_read(struct ina2xx_data *data, u32 mask, long *val)static int ina2xx_in_read (struct device *dev,u32 attr,int channel,long *val) {int voltage_reg = channel ?INA2XX_BUS_VOLTAGE :INA2XX_SHUNT_VOLTAGE ; -u32 under_voltage_mask = channel ?INA226_BUS_UNDER_VOLTAGE_MASK - :INA226_SHUNT_UNDER_VOLTAGE_MASK ; -u32 over_voltage_mask = channel ?INA226_BUS_OVER_VOLTAGE_MASK - :INA226_SHUNT_OVER_VOLTAGE_MASK ; +enum ina2xx_alert_type under_voltage_alert = channel ?INA2XX_ALERT_BUS_VOLTAGE_LOW + :INA2XX_ALERT_SHUNT_VOLTAGE_LOW ; +enum ina2xx_alert_type over_voltage_alert = channel ?INA2XX_ALERT_BUS_VOLTAGE_HIGH + :INA2XX_ALERT_SHUNT_VOLTAGE_HIGH ;struct ina2xx_data *data =dev_get_drvdata (dev);struct regmap *regmap = data->regmap;unsigned int regval;
@@ -533,15 +577,15 @@ static int ina2xx_in_read(struct device *dev, u32 attr, int channel, long *val) *val =ina2xx_get_value (data, voltage_reg, regval);break ;case hwmon_in_lcrit: -return ina226_alert_limit_read (data, under_voltage_mask, +return ina226_alert_limit_read (data, under_voltage_alert, voltage_reg, val);case hwmon_in_crit: -return ina226_alert_limit_read (data, over_voltage_mask, +return ina226_alert_limit_read (data, over_voltage_alert, voltage_reg, val);case hwmon_in_lcrit_alarm: -return ina226_alert_read (data, under_voltage_mask, val); +return ina226_alert_read (data, under_voltage_alert, val);case hwmon_in_crit_alarm: -return ina226_alert_read (data, over_voltage_mask, val); +return ina226_alert_read (data, over_voltage_alert, val);default :return -EOPNOTSUPP ; }
@@ -595,10 +639,10 @@ static int ina2xx_power_read(struct device *dev, u32 attr, long *val)case hwmon_power_average:return sy24655_average_power_read (data,SY24655_EIN , val);case hwmon_power_crit: -return ina226_alert_limit_read (data,INA226_POWER_OVER_LIMIT_MASK , +return ina226_alert_limit_read (data,INA2XX_ALERT_POWER_HIGH ,INA2XX_POWER , val);case hwmon_power_crit_alarm: -return ina226_alert_read (data,INA226_POWER_OVER_LIMIT_MASK , val); +return ina226_alert_read (data,INA2XX_ALERT_POWER_HIGH , val);default :return -EOPNOTSUPP ; }
@@ -634,15 +678,15 @@ static int ina2xx_curr_read(struct device *dev, u32 attr, long *val) *val =ina2xx_get_value (data,INA2XX_CURRENT , regval);return 0 ;case hwmon_curr_lcrit: -return ina226_alert_limit_read (data,INA226_SHUNT_UNDER_VOLTAGE_MASK , +return ina226_alert_limit_read (data,INA2XX_ALERT_CURRENT_LOW ,INA2XX_CURRENT , val);case hwmon_curr_crit: -return ina226_alert_limit_read (data,INA226_SHUNT_OVER_VOLTAGE_MASK , +return ina226_alert_limit_read (data,INA2XX_ALERT_CURRENT_HIGH ,INA2XX_CURRENT , val);case hwmon_curr_lcrit_alarm: -return ina226_alert_read (data,INA226_SHUNT_UNDER_VOLTAGE_MASK , val); +return ina226_alert_read (data,INA2XX_ALERT_CURRENT_LOW , val);case hwmon_curr_crit_alarm: -return ina226_alert_read (data,INA226_SHUNT_OVER_VOLTAGE_MASK , val); +return ina226_alert_read (data,INA2XX_ALERT_CURRENT_HIGH , val);default :return -EOPNOTSUPP ; }
@@ -686,12 +730,12 @@ static int ina2xx_in_write(struct device *dev, u32 attr, int channel, long val)switch (attr) {case hwmon_in_lcrit:return ina226_alert_limit_write (data, - channel ?INA226_BUS_UNDER_VOLTAGE_MASK :INA226_SHUNT_UNDER_VOLTAGE_MASK , + channel ?INA2XX_ALERT_BUS_VOLTAGE_LOW :INA2XX_ALERT_SHUNT_VOLTAGE_LOW , channel ?INA2XX_BUS_VOLTAGE :INA2XX_SHUNT_VOLTAGE , val);case hwmon_in_crit:return ina226_alert_limit_write (data, - channel ?INA226_BUS_OVER_VOLTAGE_MASK :INA226_SHUNT_OVER_VOLTAGE_MASK , + channel ?INA2XX_ALERT_BUS_VOLTAGE_HIGH :INA2XX_ALERT_SHUNT_VOLTAGE_HIGH , channel ?INA2XX_BUS_VOLTAGE :INA2XX_SHUNT_VOLTAGE , val);default :
@@ -706,7 +750,7 @@ static int ina2xx_power_write(struct device *dev, u32 attr, long val)switch (attr) {case hwmon_power_crit: -return ina226_alert_limit_write (data,INA226_POWER_OVER_LIMIT_MASK , +return ina226_alert_limit_write (data,INA2XX_ALERT_POWER_HIGH ,INA2XX_POWER , val);default :return -EOPNOTSUPP ;
@@ -720,10 +764,10 @@ static int ina2xx_curr_write(struct device *dev, u32 attr, long val)switch (attr) {case hwmon_curr_lcrit: -return ina226_alert_limit_write (data,INA226_SHUNT_UNDER_VOLTAGE_MASK , +return ina226_alert_limit_write (data,INA2XX_ALERT_CURRENT_LOW ,INA2XX_CURRENT , val);case hwmon_curr_crit: -return ina226_alert_limit_write (data,INA226_SHUNT_OVER_VOLTAGE_MASK , +return ina226_alert_limit_write (data,INA2XX_ALERT_CURRENT_HIGH ,INA2XX_CURRENT , val);default :return -EOPNOTSUPP ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 842/1250 hwmon: (ina2xx) Decouple in0 and curr1 alarms
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jared Kangas <[email protected]>
[ Upstream commit 35760f5efd7bfa7a44a3831f47e19fe9cbafc905 ]
INA2XX current limits are converted into shunt voltage limits internally using the shunt resistor value. Once a current limit's corresponding voltage limit is written to the hardware, shunt voltage and current alarms are indistinguishable from each other.
This causes two issues:
1. in0/curr1 alarms may be unintentionally cleared by reading from the opposite input's alarm.
2. When a limit for either in0 (shunt voltage) or curr1 (current) is set, both of their alarms are triggered, and both of their limits read nonzero.
An example of this behavior on an INA231:
# cd /sys/class/hwmon/hwmon0
# head {curr1,in0}_input
==> curr1_input <==
1713 ==> in0_input <==
2
# echo 1800 >curr1_lcrit
# head {curr1,in0}_lcrit_alarm
==> curr1_lcrit_alarm <==
1
==> in0_lcrit_alarm <==
0
# head {in0,curr1}_lcrit_alarm
==> in0_lcrit_alarm <==
1
==> curr1_lcrit_alarm <==
0
# head {in0,curr1}_lcrit_alarm
==> in0_lcrit_alarm <==
1
==> curr1_lcrit_alarm <==
1This is because curr1 uses the same underlying masks (INA226_SHUNT_*_VOLTAGE_MASK) as in0 on the hardware. As a result, ina2xx_{curr,in}_read() both read the shunt voltage alarms/limits without considering whether the voltage or current is currently set.
To fix this, track the active alarm type in ina2xx_data and guard alarm/limit reads with a check that returns zero if the active alarm is for a different type. The new field is initialized based on the MASK_ENABLE register's set function, assuming voltage instead of current when the shunt voltage mask is set.
After this fix, the alarms only read back 1 if their corresponding limit is set:
# echo 0 >curr1_lcrit
# head {curr1,in0}_lcrit_alarm
==> curr1_lcrit_alarm <==
0 ==> in0_lcrit_alarm <==
0
# echo 9999 >curr1_lcrit
# head {curr1,in0}_lcrit_alarm
==> curr1_lcrit_alarm <==
1
==> in0_lcrit_alarm <==
0
# echo 9999 >in0_lcrit
# head {curr1,in0}_lcrit_alarm
==> curr1_lcrit_alarm <==
0
==> in0_lcrit_alarm <==
1--- drivers/hwmon/ina2xx.c | 65 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-)
drivers/hwmon/ina2xx.c +63 -2
@@ -23,6 +23,7 @@*/ #include <linux/bitfield.h> +#include <linux/bitops.h> #include <linux/bits.h> #include <linux/delay.h> #include <linux/device.h>
@@ -174,6 +175,7 @@ struct ina2xx_data {const struct ina2xx_config *config;enum ina2xx_ids chip; +enum ina2xx_alert_type active_alert;long rshunt;long current_lsb_uA;long power_lsb_uW;
@@ -464,6 +466,35 @@ static u32 ina2xx_alert_type_to_mask(enum ina2xx_alert_type alert) } } +static enum ina2xx_alert_type ina2xx_mask_to_alert_type (u32 mask) +{ +int top_bit =fls (mask &INA226_ALERT_CONFIG_MASK ); + +if (!top_bit) +return INA2XX_ALERT_NONE ; + +/* +* Multiple bits may be set, with the highest-set function taking +* precedence according to the datasheet. Shunt voltage masks are +* assumed to map to voltage monitoring rather than current monitoring, +* since the latter isn't directly implemented in the hardware. +*/ +switch (BIT (top_bit -1 )) { +case INA226_SHUNT_OVER_VOLTAGE_MASK : +return INA2XX_ALERT_SHUNT_VOLTAGE_HIGH ; +case INA226_SHUNT_UNDER_VOLTAGE_MASK : +return INA2XX_ALERT_SHUNT_VOLTAGE_LOW ; +case INA226_BUS_OVER_VOLTAGE_MASK : +return INA2XX_ALERT_BUS_VOLTAGE_HIGH ; +case INA226_BUS_UNDER_VOLTAGE_MASK : +return INA2XX_ALERT_BUS_VOLTAGE_LOW ; +case INA226_POWER_OVER_LIMIT_MASK : +return INA2XX_ALERT_POWER_HIGH ; +default : +return INA2XX_ALERT_NONE ; + } +} +static int ina226_alert_limit_read (struct ina2xx_data *data,enum ina2xx_alert_type alert,int reg,long *val) {
@@ -472,6 +503,12 @@ static int ina226_alert_limit_read(struct ina2xx_data *data, enum ina2xx_alert_tu32 mask;int ret; +/* Avoid nonzero reads from inactive alerts caused by shared limit register */ +if (data->active_alert != alert) { + *val =0 ; +return 0 ; + } + ret =regmap_read (regmap,INA226_MASK_ENABLE , ®val);if (ret)return ret;
@@ -507,6 +544,7 @@ static int ina226_alert_limit_write(struct ina2xx_data *data, enum ina2xx_alert_ INA226_ALERT_CONFIG_MASK, 0);if (ret <0 )return ret; + data->active_alert =INA2XX_ALERT_NONE ; ret =regmap_write (regmap,INA226_ALERT_LIMIT ,ina226_alert_to_reg (data, reg, val));
@@ -515,9 +553,13 @@ static int ina226_alert_limit_write(struct ina2xx_data *data, enum ina2xx_alert_if (val) { mask =ina2xx_alert_type_to_mask (alert); -return regmap_update_bits (regmap,INA226_MASK_ENABLE , -INA226_ALERT_CONFIG_MASK , mask); + ret =regmap_update_bits (regmap,INA226_MASK_ENABLE , +INA226_ALERT_CONFIG_MASK , mask); +if (ret <0 ) +return ret; + data->active_alert = alert; } +return 0 ; }
@@ -547,6 +589,15 @@ static int ina226_alert_read(struct ina2xx_data *data, enum ina2xx_alert_type alu32 mask;int ret; +/* +* With alert latching, reading alerts from hardware also clears the +* alert, so return early if the alert is inactive. +*/ +if (data->active_alert != alert) { + *val =0 ; +return 0 ; + } + ret =regmap_read_bypassed (data->regmap,INA226_MASK_ENABLE , ®val);if (ret)return ret;
@@ -989,6 +1040,16 @@ static int ina2xx_init(struct device *dev, struct ina2xx_data *data)if (data->config->has_alerts) {bool active_high =device_property_read_bool (dev,"ti,alert-polarity-active-high" ); +unsigned int mask_enable; + +/* +* Infer active alert from MASK_ENABLE in case it's already +* configured (e.g., by a past probe or firmware) +*/ + ret =regmap_read (regmap,INA226_MASK_ENABLE , &mask_enable); +if (ret <0 ) +return ret; + data->active_alert =ina2xx_mask_to_alert_type (mask_enable); regmap_update_bits(regmap, INA226_MASK_ENABLE, INA226_ALERT_LATCH_ENABLE | INA226_ALERT_POLARITY,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 843/1250 Documentation: hwmon: replace full-width colon by a standard ASCII colon
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Antonin Godard <[email protected]>
[ Upstream commit 013c5a93e8062014177d68af799e8867cf03958d ]
It prevented the pdfdocs target to complete, prompting the following error:
Latexmk: ====Problematic refs and citations with line #s in .tex file:
Missing character: There is no : (U+FF1A) in font DejaVu Serif/OT:script=latn;l
--- Documentation/hwmon/gpd-fan.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Documentation/hwmon/gpd-fan.rst +1 -1
@@ -64,7 +64,7 @@ pwm1_enable at full speed. Write "1" to set to manual, write "2" to let the EC control decide fan speed. Read this attribute to see current status. - NB:In consideration of the safety of the device, when setting to manual mode, + NB: In consideration of the safety of the device, when setting to manual mode, the pwm speed will be set to the maximum value (255) by default. You can set a different value by writing pwm1 later.
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 844/1250 hwmon: (sht4x) Rely on subsystem locking
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guenter Roeck <[email protected]>
[ Upstream commit 53dfa12299c15f256e6199db1328e8bccd52ffd9 ]
Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it.
Stable-dep-of: 70c33e211b2b ("hwmon: (sht4x) Fix return value from heater_enable_store()")
--- drivers/hwmon/sht4x.c | 40 +++++++++++----------------------------- 1 file changed, 11 insertions(+), 29 deletions(-)
drivers/hwmon/sht4x.c +11 -29
@@ -55,7 +55,6 @@ DECLARE_CRC8_TABLE(sht4x_crc8_table);/** * struct sht4x_data - All the data required to operate an SHT4X chip * @client: the i2c client associated with the SHT4X -* @lock: a mutex that is used to prevent parallel access to the i2c client * @heating_complete: the time that the last heating finished * @data_pending: true if and only if there are measurements to retrieve after heating * @heater_power: the power at which the heater will be started
@@ -68,7 +67,6 @@ DECLARE_CRC8_TABLE(sht4x_crc8_table);*/ struct sht4x_data {struct i2c_client *client; -struct mutex lock;/* atomic read data updates */ unsigned long heating_complete;/* in jiffies */ bool data_pending;u32 heater_power;/* in milli-watts */
@@ -87,7 +85,7 @@ struct sht4x_data {*/ static int sht4x_read_values (struct sht4x_data *data) { -int ret =0 ; +int ret;u16 t_ticks, rh_ticks;unsigned long next_update;struct i2c_client *client = data->client;
@@ -96,8 +94,6 @@ static int sht4x_read_values(struct sht4x_data *data)u8 raw_data[SHT4X_RESPONSE_LENGTH ];unsigned long curr_jiffies; -mutex_lock (&data->lock); - curr_jiffies = jiffies;if (time_before (curr_jiffies, data->heating_complete))msleep (jiffies_to_msecs (data->heating_complete - curr_jiffies));
@@ -110,11 +106,11 @@ static int sht4x_read_values(struct sht4x_data *data)msecs_to_jiffies (data->update_interval);if (data->valid &&time_before_eq (jiffies, next_update)) - goto unlock; +return 0 ; ret =i2c_master_send (client, cmd,SHT4X_CMD_LEN );if (ret <0 ) - goto unlock; +return ret;usleep_range (SHT4X_MEAS_DELAY_HPM ,SHT4X_MEAS_DELAY_HPM +SHT4X_DELAY_EXTRA ); }
@@ -123,7 +119,7 @@ static int sht4x_read_values(struct sht4x_data *data)if (ret !=SHT4X_RESPONSE_LENGTH ) {if (ret >=0 ) ret = -ENODATA ; - goto unlock; +return ret; } t_ticks = raw_data[0 ] <<8 | raw_data[1 ];
@@ -132,26 +128,20 @@ static int sht4x_read_values(struct sht4x_data *data) crc =crc8 (sht4x_crc8_table, &raw_data[0 ],SHT4X_WORD_LEN ,CRC8_INIT_VALUE );if (crc != raw_data[2 ]) {dev_err (&client->dev,"data integrity check failed\n" ); - ret = -EIO ; - goto unlock; +return -EIO ; } crc =crc8 (sht4x_crc8_table, &raw_data[3 ],SHT4X_WORD_LEN ,CRC8_INIT_VALUE );if (crc != raw_data[5 ]) {dev_err (&client->dev,"data integrity check failed\n" ); - ret = -EIO ; - goto unlock; +return -EIO ; } data->temperature = ((21875 * (int32_t )t_ticks) >>13 ) -45000 ; data->humidity = ((15625 * (int32_t )rh_ticks) >>13 ) -6000 ; data->last_updated = jiffies; data->valid = true; - ret =0 ; - -unlock: -mutex_unlock (&data->lock); -return ret; +return 0 ; }static ssize_t sht4x_interval_write (struct sht4x_data *data,long val)
@@ -287,22 +277,16 @@ static ssize_t heater_enable_store(struct device *dev, heating_time_bound =1100 ; } -mutex_lock (&data->lock); - -if (time_before (jiffies, data->heating_complete)) { - ret = -EBUSY ; - goto unlock; - } +if (time_before (jiffies, data->heating_complete)) +return -EBUSY ; ret =i2c_master_send (data->client, &cmd,SHT4X_CMD_LEN );if (ret <0 ) - goto unlock; +return ret; data->heating_complete = jiffies +msecs_to_jiffies (heating_time_bound); data->data_pending = true; -unlock: -mutex_unlock (&data->lock); -return ret; +return 0 ; } static ssize_t heater_power_show(struct device *dev,
@@ -422,8 +406,6 @@ static int sht4x_probe(struct i2c_client *client) data->heater_time =1000 ; data->heating_complete = jiffies; -mutex_init (&data->lock); -crc8_populate_msb (sht4x_crc8_table,SHT4X_CRC8_POLYNOMIAL ); ret =i2c_master_send (client, cmd,SHT4X_CMD_LEN );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 845/1250 hwmon: (sht4x) Fix return value from heater_enable_store()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guenter Roeck <[email protected]>
[ Upstream commit 70c33e211b2b78830f76c908e5236b77ffde63a0 ]
Sashiko reports:
The return value in heater_enable_store() causes an unexpected write failure in user-space.
When the heater is successfully enabled, the function returns 0 instead of count:
drivers/hwmon/sht4x.c:heater_enable_store() {
...
data->heating_complete = jiffies + msecs_to_jiffies(heating_time_bound);
data->data_pending = true;
return 0;
}Returning 0 signals to VFS that no bytes were processed. Standard user-space tools will retry the write with the remaining bytes. On the retry, time_before(jiffies, data->heating_complete) evaluates to true, and the function immediately fails with -EBUSY.
Return count as expected to fix the problem.
--- drivers/hwmon/sht4x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hwmon/sht4x.c +1 -1
@@ -286,7 +286,7 @@ static ssize_t heater_enable_store(struct device *dev, data->heating_complete = jiffies +msecs_to_jiffies (heating_time_bound); data->data_pending = true; -return 0 ; +return count; } static ssize_t heater_power_show(struct device *dev,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 846/1250 ASoC: bcm: bcm63xx: Publish the OF module aliases
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: hpp.iscas <[email protected]>
[ Upstream commit 32689f0fc54fd801f1cd11637666e534984cb04a ]
The BCM63xx I2S platform driver matches brcm,bcm63xx-i2s using snd_soc_bcm_audio_match. With SND_BCM63XX_I2S_WHISTLER=m, the platform bus emits an OF modalias but snd-soc-63xx does not publish that table.
Export the existing OF IDs for module autoloading. The PCM companion and the probe path remain unchanged.
--- sound/soc/bcm/bcm63xx-i2s-whistler.c | 1 + 1 file changed, 1 insertion(+)
sound/soc/bcm/bcm63xx-i2s-whistler.c +1 -0
@@ -285,6 +285,7 @@ static const struct of_device_id snd_soc_bcm_audio_match[] = { {.compatible = "brcm,bcm63xx-i2s"}, { } }; +MODULE_DEVICE_TABLE (of, snd_soc_bcm_audio_match);#endif static struct platform_driver bcm63xx_i2s_driver = {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 847/1250 ASoC: Intel: SST: Publish the PCI module aliases
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: hpp.iscas <[email protected]>
[ Upstream commit d112159df5c6cc5ee6ab91cc32bf6ed29939df38 ]
The legacy SST PCI driver matches Intel Tangier devices using intel_sst_ids, but its only explicit module alias is "sst". That alias does not match PCI modalias events when this driver is built as a module.
Publish its PCI table. The independently configurable SOF driver does not provide aliases for the legacy SST module.
--- sound/soc/intel/atom/sst/sst_pci.c | 1 + 1 file changed, 1 insertion(+)
sound/soc/intel/atom/sst/sst_pci.c +1 -0
@@ -167,6 +167,7 @@ static const struct pci_device_id intel_sst_ids[] = { { PCI_DEVICE_DATA(INTEL, SST_TNG, 0) }, { 0, } }; +MODULE_DEVICE_TABLE (pci, intel_sst_ids);static struct pci_driver sst_driver = { .name = SST_DRV_NAME,
Signature · 1 line
2.53.0