6.12.111-rc1 review
Subsystems (3)
Greg Kroah-Hartman
maintainerPATCH 755/1102 net: dsa: lantiq_gswip: prepare for more CPU port options
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Daniel Golle <[email protected]>
[ Upstream commit 2bec1c3836990d1159d2fe113f3ffddb48b9e040 ]
The MaxLinear GSW1xx series of switches support using either the (R)(G)MII interface on port 5 or the SGMII interface on port 4 to be used as CPU port. Prepare for supporting them by defining a mask of allowed CPU ports instead of a single port.
Stable-dep-of: 59fb389ad6bf ("net: dsa: lantiq_gswip: fix GSWIP_MDIO_PHY_FCONTX_EN value")
--- drivers/net/dsa/lantiq_gswip.c | 79 +++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 29 deletions(-)
drivers/net/dsa/lantiq_gswip.c +50 -29
@@ -253,7 +253,7 @@struct gswip_hw_info {int max_ports; -int cpu_port; +unsigned int allowed_cpu_ports;void (*phylink_get_caps)(struct dsa_switch *ds,int port,struct phylink_config *config); };
@@ -655,7 +655,6 @@ static int gswip_add_single_port_br(struct gswip_priv *priv, int port, bool add) {struct gswip_pce_table_entry vlan_active = {0 ,};struct gswip_pce_table_entry vlan_mapping = {0 ,}; -unsigned int cpu_port = priv->hw_info->cpu_port;int err; vlan_active.index = port +1 ;
@@ -675,7 +674,7 @@ static int gswip_add_single_port_br(struct gswip_priv *priv, int port, bool add) vlan_mapping.index = port +1 ; vlan_mapping.table =GSWIP_TABLE_VLAN_MAPPING ; vlan_mapping.val[0 ] =0 /* vid */ ; - vlan_mapping.val[1 ] =BIT (port) |BIT (cpu_port); + vlan_mapping.val[1 ] =BIT (port) |dsa_cpu_ports (priv->ds); vlan_mapping.val[2 ] =0 ; err =gswip_pce_table_entry_write (priv, &vlan_mapping);if (err) {
@@ -814,10 +813,10 @@ static int gswip_port_vlan_filtering(struct dsa_switch *ds, int port,static int gswip_setup (struct dsa_switch *ds) { +unsigned int cpu_ports =dsa_cpu_ports (ds);struct gswip_priv *priv = ds->priv; -unsigned int cpu_port = priv->hw_info->cpu_port; -int i; -int err; +struct dsa_port *cpu_dp; +int err, i;gswip_switch_w (priv,GSWIP_SWRES_R0 ,GSWIP_SWRES );usleep_range (5000 ,10000 );
@@ -839,9 +838,9 @@ static int gswip_setup(struct dsa_switch *ds) }/* Default unknown Broadcast/Multicast/Unicast port maps */ -gswip_switch_w (priv,BIT (cpu_port),GSWIP_PCE_PMAP1 ); -gswip_switch_w (priv,BIT (cpu_port),GSWIP_PCE_PMAP2 ); -gswip_switch_w (priv,BIT (cpu_port),GSWIP_PCE_PMAP3 ); +gswip_switch_w (priv, cpu_ports,GSWIP_PCE_PMAP1 ); +gswip_switch_w (priv, cpu_ports,GSWIP_PCE_PMAP2 ); +gswip_switch_w (priv, cpu_ports,GSWIP_PCE_PMAP3 );/* Deactivate MDIO PHY auto polling. Some PHYs as the AR8030 have an * interoperability problem with this auto polling mechanism because
@@ -870,13 +869,15 @@ static int gswip_setup(struct dsa_switch *ds)GSWIP_MII_CFG_EN |GSWIP_MII_CFG_ISOLATE , 0, i); -/* enable special tag insertion on cpu port */ -gswip_switch_mask (priv,0 ,GSWIP_FDMA_PCTRL_STEN , -GSWIP_FDMA_PCTRLp (cpu_port)); +dsa_switch_for_each_cpu_port (cpu_dp, ds) { +/* enable special tag insertion on cpu port */ +gswip_switch_mask (priv,0 ,GSWIP_FDMA_PCTRL_STEN , +GSWIP_FDMA_PCTRLp (cpu_dp->index)); -/* accept special tag in ingress direction */ -gswip_switch_mask (priv,0 ,GSWIP_PCE_PCTRL_0_INGRESS , -GSWIP_PCE_PCTRL_0p (cpu_port)); +/* accept special tag in ingress direction */ +gswip_switch_mask (priv,0 ,GSWIP_PCE_PCTRL_0_INGRESS , +GSWIP_PCE_PCTRL_0p (cpu_dp->index)); + }gswip_switch_mask (priv,0 ,GSWIP_BM_QUEUE_GCTRL_GL_MOD ,GSWIP_BM_QUEUE_GCTRL );
@@ -972,7 +973,6 @@ static int gswip_vlan_add_unaware(struct gswip_priv *priv, {struct gswip_pce_table_entry vlan_mapping = {0 ,};unsigned int max_ports = priv->hw_info->max_ports; -unsigned int cpu_port = priv->hw_info->cpu_port;bool active_vlan_created = false;int idx =-1 ;int i;
@@ -1012,7 +1012,7 @@ static int gswip_vlan_add_unaware(struct gswip_priv *priv, }/* Update the VLAN mapping entry and write it to the switch */ - vlan_mapping.val[1 ] |=BIT (cpu_port); + vlan_mapping.val[1 ] |=dsa_cpu_ports (priv->ds); vlan_mapping.val[1 ] |=BIT (port); err =gswip_pce_table_entry_write (priv, &vlan_mapping);if (err) {
@@ -1034,7 +1034,7 @@ static int gswip_vlan_add_aware(struct gswip_priv *priv, {struct gswip_pce_table_entry vlan_mapping = {0 ,};unsigned int max_ports = priv->hw_info->max_ports; -unsigned int cpu_port = priv->hw_info->cpu_port; +unsigned int cpu_ports =dsa_cpu_ports (priv->ds);bool active_vlan_created = false;int idx =-1 ;int fid =-1 ;
@@ -1081,8 +1081,8 @@ static int gswip_vlan_add_aware(struct gswip_priv *priv, vlan_mapping.val[0 ] = vid;/* Update the VLAN mapping entry and write it to the switch */ - vlan_mapping.val[1 ] |=BIT (cpu_port); - vlan_mapping.val[2 ] |=BIT (cpu_port); + vlan_mapping.val[1 ] |= cpu_ports; + vlan_mapping.val[2 ] |= cpu_ports; vlan_mapping.val[1 ] |=BIT (port);if (untagged) vlan_mapping.val[2 ] &= ~BIT (port);
@@ -1109,7 +1109,6 @@ static int gswip_vlan_remove(struct gswip_priv *priv, {struct gswip_pce_table_entry vlan_mapping = {0 ,};unsigned int max_ports = priv->hw_info->max_ports; -unsigned int cpu_port = priv->hw_info->cpu_port;int idx =-1 ;int i;int err;
@@ -1145,7 +1144,7 @@ static int gswip_vlan_remove(struct gswip_priv *priv, }/* In case all ports are removed from the bridge, remove the VLAN */ -if ((vlan_mapping.val[1 ] & ~BIT (cpu_port)) ==0 ) { +if (!(vlan_mapping.val[1 ] & ~dsa_cpu_ports (priv->ds))) { err =gswip_vlan_active_remove (priv, idx);if (err) { dev_err(priv->dev, "failed to write active VLAN: %d\n",
@@ -2090,6 +2089,30 @@ static int gswip_gphy_fw_list(struct gswip_priv *priv,return err; } +static int gswip_validate_cpu_port (struct dsa_switch *ds) +{ +struct gswip_priv *priv = ds->priv; +struct dsa_port *cpu_dp; +int cpu_port =-1 ; + +dsa_switch_for_each_cpu_port (cpu_dp, ds) { +if (cpu_port !=-1 ) +return dev_err_probe (ds->dev, -EINVAL , +"only a single CPU port is supported\n" ); + + cpu_port = cpu_dp->index; + } + +if (cpu_port ==-1 ) +return dev_err_probe (ds->dev, -EINVAL ,"no CPU port defined\n" ); + +if (BIT (cpu_port) & ~priv->hw_info->allowed_cpu_ports) +return dev_err_probe (ds->dev, -EINVAL , +"unsupported CPU port defined\n" ); + +return 0 ; +} +static int gswip_probe (struct platform_device *pdev) {struct device_node *np, *gphy_fw_np;
@@ -2172,12 +2195,10 @@ static int gswip_probe(struct platform_device *pdev)dev_err_probe (dev, err,"dsa switch registration failed\n" ); goto gphy_fw_remove; } -if (!dsa_is_cpu_port (priv->ds, priv->hw_info->cpu_port)) { - err =dev_err_probe (dev, -EINVAL , -"wrong CPU port defined, HW only supports port: %i\n" , - priv->hw_info->cpu_port); + + err =gswip_validate_cpu_port (priv->ds); +if (err) goto disable_switch; - }platform_set_drvdata (pdev, priv);
@@ -2226,13 +2247,13 @@ static void gswip_shutdown(struct platform_device *pdev)static const struct gswip_hw_info gswip_xrx200 = { .max_ports =7 , - .cpu_port =6 , + .allowed_cpu_ports =BIT (6 ), .phylink_get_caps = gswip_xrx200_phylink_get_caps, };static const struct gswip_hw_info gswip_xrx300 = { .max_ports =7 , - .cpu_port =6 , + .allowed_cpu_ports =BIT (6 ), .phylink_get_caps = gswip_xrx300_phylink_get_caps, };
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 756/1102 net: dsa: lantiq_gswip: move definitions to header
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Daniel Golle <[email protected]>
[ Upstream commit 476c001a554dd577073a1b9e8a7ff0889632d31f ]
Introduce header file and move register definitions as well as the definitions struct gswip_hw_info and struct gswip_priv there.
Stable-dep-of: 59fb389ad6bf ("net: dsa: lantiq_gswip: fix GSWIP_MDIO_PHY_FCONTX_EN value")
--- drivers/net/dsa/lantiq_gswip.c | 250 +------------------------------- drivers/net/dsa/lantiq_gswip.h | 252 +++++++++++++++++++++++++++++++++ 2 files changed, 255 insertions(+), 247 deletions(-) create mode 100644 drivers/net/dsa/lantiq_gswip.h
drivers/net/dsa/lantiq_gswip.c +3 -247
@@ -25,7 +25,9 @@* between all LAN ports by default. */ -#include <linux/clk.h> +#include "lantiq_gswip.h" +#include "lantiq_pce.h" +#include <linux/delay.h> #include <linux/etherdevice.h> #include <linux/firmware.h>
@@ -39,259 +41,13 @@#include <linux/of_platform.h> #include <linux/phy.h> #include <linux/phylink.h> -#include <linux/platform_device.h> -#include <linux/regmap.h> -#include <linux/reset.h> -#include <net/dsa.h> #include <dt-bindings/mips/lantiq_rcu_gphy.h> -#include "lantiq_pce.h" - -/* GSWIP MDIO Registers */ -#define GSWIP_MDIO_GLOB 0x00 -#define GSWIP_MDIO_GLOB_ENABLE BIT(15) -#define GSWIP_MDIO_CTRL 0x08 -#define GSWIP_MDIO_CTRL_BUSY BIT(12) -#define GSWIP_MDIO_CTRL_RD BIT(11) -#define GSWIP_MDIO_CTRL_WR BIT(10) -#define GSWIP_MDIO_CTRL_PHYAD_MASK 0x1f -#define GSWIP_MDIO_CTRL_PHYAD_SHIFT 5 -#define GSWIP_MDIO_CTRL_REGAD_MASK 0x1f -#define GSWIP_MDIO_READ 0x09 -#define GSWIP_MDIO_WRITE 0x0A -#define GSWIP_MDIO_MDC_CFG0 0x0B -#define GSWIP_MDIO_MDC_CFG1 0x0C -#define GSWIP_MDIO_PHYp (p) (0x15 - (p)) -#define GSWIP_MDIO_PHY_LINK_MASK 0x6000 -#define GSWIP_MDIO_PHY_LINK_AUTO 0x0000 -#define GSWIP_MDIO_PHY_LINK_DOWN 0x4000 -#define GSWIP_MDIO_PHY_LINK_UP 0x2000 -#define GSWIP_MDIO_PHY_SPEED_MASK 0x1800 -#define GSWIP_MDIO_PHY_SPEED_AUTO 0x1800 -#define GSWIP_MDIO_PHY_SPEED_M10 0x0000 -#define GSWIP_MDIO_PHY_SPEED_M100 0x0800 -#define GSWIP_MDIO_PHY_SPEED_G1 0x1000 -#define GSWIP_MDIO_PHY_FDUP_MASK 0x0600 -#define GSWIP_MDIO_PHY_FDUP_AUTO 0x0000 -#define GSWIP_MDIO_PHY_FDUP_EN 0x0200 -#define GSWIP_MDIO_PHY_FDUP_DIS 0x0600 -#define GSWIP_MDIO_PHY_FCONTX_MASK 0x0180 -#define GSWIP_MDIO_PHY_FCONTX_AUTO 0x0000 -#define GSWIP_MDIO_PHY_FCONTX_EN 0x0100 -#define GSWIP_MDIO_PHY_FCONTX_DIS 0x0180 -#define GSWIP_MDIO_PHY_FCONRX_MASK 0x0060 -#define GSWIP_MDIO_PHY_FCONRX_AUTO 0x0000 -#define GSWIP_MDIO_PHY_FCONRX_EN 0x0020 -#define GSWIP_MDIO_PHY_FCONRX_DIS 0x0060 -#define GSWIP_MDIO_PHY_ADDR_MASK 0x001f -#define GSWIP_MDIO_PHY_MASK (GSWIP_MDIO_PHY_ADDR_MASK | \ - GSWIP_MDIO_PHY_FCONRX_MASK | \ - GSWIP_MDIO_PHY_FCONTX_MASK | \ - GSWIP_MDIO_PHY_LINK_MASK | \ - GSWIP_MDIO_PHY_SPEED_MASK | \ - GSWIP_MDIO_PHY_FDUP_MASK) - -/* GSWIP MII Registers */ -#define GSWIP_MII_CFGp (p) (0x2 * (p)) -#define GSWIP_MII_CFG_RESET BIT(15) -#define GSWIP_MII_CFG_EN BIT(14) -#define GSWIP_MII_CFG_ISOLATE BIT(13) -#define GSWIP_MII_CFG_LDCLKDIS BIT(12) -#define GSWIP_MII_CFG_RGMII_IBS BIT(8) -#define GSWIP_MII_CFG_RMII_CLK BIT(7) -#define GSWIP_MII_CFG_MODE_MIIP 0x0 -#define GSWIP_MII_CFG_MODE_MIIM 0x1 -#define GSWIP_MII_CFG_MODE_RMIIP 0x2 -#define GSWIP_MII_CFG_MODE_RMIIM 0x3 -#define GSWIP_MII_CFG_MODE_RGMII 0x4 -#define GSWIP_MII_CFG_MODE_GMII 0x9 -#define GSWIP_MII_CFG_MODE_MASK 0xf -#define GSWIP_MII_CFG_RATE_M2P5 0x00 -#define GSWIP_MII_CFG_RATE_M25 0x10 -#define GSWIP_MII_CFG_RATE_M125 0x20 -#define GSWIP_MII_CFG_RATE_M50 0x30 -#define GSWIP_MII_CFG_RATE_AUTO 0x40 -#define GSWIP_MII_CFG_RATE_MASK 0x70 -#define GSWIP_MII_PCDU0 0x01 -#define GSWIP_MII_PCDU1 0x03 -#define GSWIP_MII_PCDU5 0x05 -#define GSWIP_MII_PCDU_TXDLY_MASK GENMASK(2, 0) -#define GSWIP_MII_PCDU_RXDLY_MASK GENMASK(9, 7) - -/* GSWIP Core Registers */ -#define GSWIP_SWRES 0x000 -#define GSWIP_SWRES_R1 BIT(1)/* GSWIP Software reset */ -#define GSWIP_SWRES_R0 BIT(0)/* GSWIP Hardware reset */ -#define GSWIP_VERSION 0x013 -#define GSWIP_VERSION_REV_SHIFT 0 -#define GSWIP_VERSION_REV_MASK GENMASK(7, 0) -#define GSWIP_VERSION_MOD_SHIFT 8 -#define GSWIP_VERSION_MOD_MASK GENMASK(15, 8) -#define GSWIP_VERSION_2_0 0x100 -#define GSWIP_VERSION_2_1 0x021 -#define GSWIP_VERSION_2_2 0x122 -#define GSWIP_VERSION_2_2_ETC 0x022 - -#define GSWIP_BM_RAM_VAL (x) (0x043 - (x)) -#define GSWIP_BM_RAM_ADDR 0x044 -#define GSWIP_BM_RAM_CTRL 0x045 -#define GSWIP_BM_RAM_CTRL_BAS BIT(15) -#define GSWIP_BM_RAM_CTRL_OPMOD BIT(5) -#define GSWIP_BM_RAM_CTRL_ADDR_MASK GENMASK(4, 0) -#define GSWIP_BM_QUEUE_GCTRL 0x04A -#define GSWIP_BM_QUEUE_GCTRL_GL_MOD BIT(10) -/* buffer management Port Configuration Register */ -#define GSWIP_BM_PCFGp (p) (0x080 + ((p) * 2)) -#define GSWIP_BM_PCFG_CNTEN BIT(0)/* RMON Counter Enable */ -#define GSWIP_BM_PCFG_IGCNT BIT(1)/* Ingres Special Tag RMON count */ -/* buffer management Port Control Register */ -#define GSWIP_BM_RMON_CTRLp (p) (0x81 + ((p) * 2)) -#define GSWIP_BM_CTRL_RMON_RAM1_RES BIT(0)/* Software Reset for RMON RAM 1 */ -#define GSWIP_BM_CTRL_RMON_RAM2_RES BIT(1)/* Software Reset for RMON RAM 2 */ - -/* PCE */ -#define GSWIP_PCE_TBL_KEY (x) (0x447 - (x)) -#define GSWIP_PCE_TBL_MASK 0x448 -#define GSWIP_PCE_TBL_VAL (x) (0x44D - (x)) -#define GSWIP_PCE_TBL_ADDR 0x44E -#define GSWIP_PCE_TBL_CTRL 0x44F -#define GSWIP_PCE_TBL_CTRL_BAS BIT(15) -#define GSWIP_PCE_TBL_CTRL_TYPE BIT(13) -#define GSWIP_PCE_TBL_CTRL_VLD BIT(12) -#define GSWIP_PCE_TBL_CTRL_KEYFORM BIT(11) -#define GSWIP_PCE_TBL_CTRL_GMAP_MASK GENMASK(10, 7) -#define GSWIP_PCE_TBL_CTRL_OPMOD_MASK GENMASK(6, 5) -#define GSWIP_PCE_TBL_CTRL_OPMOD_ADRD 0x00 -#define GSWIP_PCE_TBL_CTRL_OPMOD_ADWR 0x20 -#define GSWIP_PCE_TBL_CTRL_OPMOD_KSRD 0x40 -#define GSWIP_PCE_TBL_CTRL_OPMOD_KSWR 0x60 -#define GSWIP_PCE_TBL_CTRL_ADDR_MASK GENMASK(4, 0) -#define GSWIP_PCE_PMAP1 0x453/* Monitoring port map */ -#define GSWIP_PCE_PMAP2 0x454/* Default Multicast port map */ -#define GSWIP_PCE_PMAP3 0x455/* Default Unknown Unicast port map */ -#define GSWIP_PCE_GCTRL_0 0x456 -#define GSWIP_PCE_GCTRL_0_MTFL BIT(0)/* MAC Table Flushing */ -#define GSWIP_PCE_GCTRL_0_MC_VALID BIT(3) -#define GSWIP_PCE_GCTRL_0_VLAN BIT(14)/* VLAN aware Switching */ -#define GSWIP_PCE_GCTRL_1 0x457 -#define GSWIP_PCE_GCTRL_1_MAC_GLOCK BIT(2)/* MAC Address table lock */ -#define GSWIP_PCE_GCTRL_1_MAC_GLOCK_MOD BIT(3)/* Mac address table lock forwarding mode */ -#define GSWIP_PCE_PCTRL_0p (p) (0x480 + ((p) * 0xA)) -#define GSWIP_PCE_PCTRL_0_TVM BIT(5)/* Transparent VLAN mode */ -#define GSWIP_PCE_PCTRL_0_VREP BIT(6)/* VLAN Replace Mode */ -#define GSWIP_PCE_PCTRL_0_INGRESS BIT(11)/* Accept special tag in ingress */ -#define GSWIP_PCE_PCTRL_0_PSTATE_LISTEN 0x0 -#define GSWIP_PCE_PCTRL_0_PSTATE_RX 0x1 -#define GSWIP_PCE_PCTRL_0_PSTATE_TX 0x2 -#define GSWIP_PCE_PCTRL_0_PSTATE_LEARNING 0x3 -#define GSWIP_PCE_PCTRL_0_PSTATE_FORWARDING 0x7 -#define GSWIP_PCE_PCTRL_0_PSTATE_MASK GENMASK(2, 0) -#define GSWIP_PCE_VCTRL (p) (0x485 + ((p) * 0xA)) -#define GSWIP_PCE_VCTRL_UVR BIT(0)/* Unknown VLAN Rule */ -#define GSWIP_PCE_VCTRL_VIMR BIT(3)/* VLAN Ingress Member violation rule */ -#define GSWIP_PCE_VCTRL_VEMR BIT(4)/* VLAN Egress Member violation rule */ -#define GSWIP_PCE_VCTRL_VSR BIT(5)/* VLAN Security */ -#define GSWIP_PCE_VCTRL_VID0 BIT(6)/* Priority Tagged Rule */ -#define GSWIP_PCE_DEFPVID (p) (0x486 + ((p) * 0xA)) - -#define GSWIP_MAC_FLEN 0x8C5 -#define GSWIP_MAC_CTRL_0p (p) (0x903 + ((p) * 0xC)) -#define GSWIP_MAC_CTRL_0_PADEN BIT(8) -#define GSWIP_MAC_CTRL_0_FCS_EN BIT(7) -#define GSWIP_MAC_CTRL_0_FCON_MASK 0x0070 -#define GSWIP_MAC_CTRL_0_FCON_AUTO 0x0000 -#define GSWIP_MAC_CTRL_0_FCON_RX 0x0010 -#define GSWIP_MAC_CTRL_0_FCON_TX 0x0020 -#define GSWIP_MAC_CTRL_0_FCON_RXTX 0x0030 -#define GSWIP_MAC_CTRL_0_FCON_NONE 0x0040 -#define GSWIP_MAC_CTRL_0_FDUP_MASK 0x000C -#define GSWIP_MAC_CTRL_0_FDUP_AUTO 0x0000 -#define GSWIP_MAC_CTRL_0_FDUP_EN 0x0004 -#define GSWIP_MAC_CTRL_0_FDUP_DIS 0x000C -#define GSWIP_MAC_CTRL_0_GMII_MASK 0x0003 -#define GSWIP_MAC_CTRL_0_GMII_AUTO 0x0000 -#define GSWIP_MAC_CTRL_0_GMII_MII 0x0001 -#define GSWIP_MAC_CTRL_0_GMII_RGMII 0x0002 -#define GSWIP_MAC_CTRL_2p (p) (0x905 + ((p) * 0xC)) -#define GSWIP_MAC_CTRL_2_LCHKL BIT(2)/* Frame Length Check Long Enable */ -#define GSWIP_MAC_CTRL_2_MLEN BIT(3)/* Maximum Untagged Frame Lnegth */ - -/* Ethernet Switch Fetch DMA Port Control Register */ -#define GSWIP_FDMA_PCTRLp (p) (0xA80 + ((p) * 0x6)) -#define GSWIP_FDMA_PCTRL_EN BIT(0)/* FDMA Port Enable */ -#define GSWIP_FDMA_PCTRL_STEN BIT(1)/* Special Tag Insertion Enable */ -#define GSWIP_FDMA_PCTRL_VLANMOD_MASK GENMASK(4, 3)/* VLAN Modification Control */ -#define GSWIP_FDMA_PCTRL_VLANMOD_SHIFT 3/* VLAN Modification Control */ -#define GSWIP_FDMA_PCTRL_VLANMOD_DIS (0x0 << GSWIP_FDMA_PCTRL_VLANMOD_SHIFT) -#define GSWIP_FDMA_PCTRL_VLANMOD_PRIO (0x1 << GSWIP_FDMA_PCTRL_VLANMOD_SHIFT) -#define GSWIP_FDMA_PCTRL_VLANMOD_ID (0x2 << GSWIP_FDMA_PCTRL_VLANMOD_SHIFT) -#define GSWIP_FDMA_PCTRL_VLANMOD_BOTH (0x3 << GSWIP_FDMA_PCTRL_VLANMOD_SHIFT) - -/* Ethernet Switch Store DMA Port Control Register */ -#define GSWIP_SDMA_PCTRLp (p) (0xBC0 + ((p) * 0x6)) -#define GSWIP_SDMA_PCTRL_EN BIT(0)/* SDMA Port Enable */ -#define GSWIP_SDMA_PCTRL_FCEN BIT(1)/* Flow Control Enable */ -#define GSWIP_SDMA_PCTRL_PAUFWD BIT(3)/* Pause Frame Forwarding */ - -#define GSWIP_TABLE_ACTIVE_VLAN 0x01 -#define GSWIP_TABLE_VLAN_MAPPING 0x02 -#define GSWIP_TABLE_MAC_BRIDGE 0x0b -#define GSWIP_TABLE_MAC_BRIDGE_KEY3_FID GENMASK(5, 0)/* Filtering identifier */ -#define GSWIP_TABLE_MAC_BRIDGE_VAL0_PORT GENMASK(7, 4)/* Port on learned entries */ -#define GSWIP_TABLE_MAC_BRIDGE_VAL1_STATIC BIT(0)/* Static, non-aging entry */ - -#define XRX200_GPHY_FW_ALIGN (16 * 1024) - -/* Maximum packet size supported by the switch. In theory this should be 10240, -* but long packets currently cause lock-ups with an MTU of over 2526. Medium -* packets are sometimes dropped (e.g. TCP over 2477, UDP over 2516-2519, ICMP -* over 2526), hence an MTU value of 2400 seems safe. This issue only affects -* packet reception. This is probably caused by the PPA engine, which is on the -* RX part of the device. Packet transmission works properly up to 10240. -*/ -#define GSWIP_MAX_PACKET_LENGTH 2400 - -struct gswip_hw_info { -int max_ports; -unsigned int allowed_cpu_ports; -void (*phylink_get_caps)(struct dsa_switch *ds,int port, -struct phylink_config *config); -}; -struct xway_gphy_match_data {char *fe_firmware_name;char *ge_firmware_name; }; -struct gswip_gphy_fw { -struct clk *clk_gate; -struct reset_control *reset; -u32 fw_addr_offset; -char *fw_name; -}; - -struct gswip_vlan { -struct net_device *bridge; -u16 vid; -u8 fid; -}; - -struct gswip_priv { -__iomem void *gswip; -__iomem void *mdio; -__iomem void *mii; -const struct gswip_hw_info *hw_info; -const struct xway_gphy_match_data *gphy_fw_name_cfg; -struct dsa_switch *ds; -struct device *dev; -struct regmap *rcu_regmap; -struct gswip_vlan vlans[64 ]; -int num_gphy_fw; -struct gswip_gphy_fw *gphy_fw; -u32 port_vlan_filter; -struct mutex pce_table_lock; -}; -struct gswip_pce_table_entry {u16 index;// PCE_TBL_ADDR.ADDR = pData->table_index u16 table;// PCE_TBL_CTRL.ADDR = pData->table
drivers/net/dsa/lantiq_gswip.h new file +252 -0
252 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 757/1102 net: dsa: lantiq_gswip: fix GSWIP_MDIO_PHY_FCONTX_EN value
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jan Havran (Advantech Czech) <[email protected]>
[ Upstream commit 59fb389ad6bf50916189e56dafcd225ab977f874 ]
Per the GSW145 data sheet, the FCONTX (bits 8:7) and FCONRX (bits 6:5) flow-control fields of the PHY_ADDR_n register both encode 00 = AUTO, 01 = EN, 10 = reserved, 11 = DIS. GSWIP_MDIO_PHY_FCONTX_EN was 0x0100, i.e. field value 10 (the reserved encoding), instead of 0x0080 (01 = EN); FCONRX_EN is already 0x0020 (01). Enabling tx flow control therefore wrote the reserved value.
Set FCONTX_EN to 0x0080. The register is shared by all supported parts.
--- drivers/net/dsa/lantiq_gswip.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/dsa/lantiq_gswip.h +1 -1
@@ -39,7 +39,7 @@#define GSWIP_MDIO_PHY_FDUP_DIS 0x0600#define GSWIP_MDIO_PHY_FCONTX_MASK 0x0180#define GSWIP_MDIO_PHY_FCONTX_AUTO 0x0000 -#define GSWIP_MDIO_PHY_FCONTX_EN 0x0100 +#define GSWIP_MDIO_PHY_FCONTX_EN 0x0080#define GSWIP_MDIO_PHY_FCONTX_DIS 0x0180#define GSWIP_MDIO_PHY_FCONRX_MASK 0x0060#define GSWIP_MDIO_PHY_FCONRX_AUTO 0x0000
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 758/1102 net/micrel: Fix typos in micrel driver code comments
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yicong Hui <[email protected]>
[ Upstream commit c2b733da93bf607d6c6f925b4d40598e6bb516be ]
Fix various typos and misspellings in code comments in the drivers/net/ethernet/micrel directory
Stable-dep-of: 66ef5adb7544 ("net: ks8851: Fix receiver error in 100BASE-TX mode following software power-down")
--- drivers/net/ethernet/micrel/ks8842.c | 4 ++-- drivers/net/ethernet/micrel/ks8851_common.c | 2 +- drivers/net/ethernet/micrel/ks8851_spi.c | 4 ++-- drivers/net/ethernet/micrel/ksz884x.c | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-)
drivers/net/ethernet/micrel/ks8842.c +2 -2
@@ -242,7 +242,7 @@ static void ks8842_reset(struct ks8842_adapter *adapter)msleep (10 );iowrite16 (0 , adapter->hw_addr +REG_GRR ); }else { -/* The KS8842 goes haywire when doing softare reset +/* The KS8842 goes haywire when doing software reset * a work around in the timberdale IP is implemented to * do a hardware reset instead ks8842_write16(adapter, 3, 1, REG_GRR);
@@ -312,7 +312,7 @@ static void ks8842_reset_hw(struct ks8842_adapter *adapter)/* aggressive back off in half duplex */ ks8842_enable_bits (adapter,32 ,1 <<8 ,REG_SGCR1 ); -/* enable no excessive collison drop */ +/* enable no excessive collision drop */ ks8842_enable_bits (adapter,32 ,1 <<3 ,REG_SGCR2 );/* Enable port 1 force flow control / back pressure / transmit / recv */
drivers/net/ethernet/micrel/ks8851_common.c +1 -1
@@ -493,7 +493,7 @@ static int ks8851_net_open(struct net_device *dev)* ks8851_net_stop - close network device * @dev: The device being closed. * -* Called to close down a network device which has been active. Cancell any +* Called to close down a network device which has been active. Cancel any * work, shutdown the RX and TX process and then place the chip into a low * power state whilst it is not being used. */
drivers/net/ethernet/micrel/ks8851_spi.c +2 -2
@@ -41,7 +41,7 @@ static int msg_enable;* * The @lock ensures that the chip is protected when certain operations are * in progress. When the read or write packet transfer is in progress, most -* of the chip registers are not ccessible until the transfer is finished and +* of the chip registers are not accessible until the transfer is finished and * the DMA has been de-asserted. */ struct ks8851_net_spi {
@@ -298,7 +298,7 @@ static unsigned int calc_txlen(unsigned int len)/** * ks8851_tx_work - process tx packet(s) -* @work: The work strucutre what was scheduled. +* @work: The work structure what was scheduled. * * This is called when a number of packets have been scheduled for * transmission and need to be sent to the device.
drivers/net/ethernet/micrel/ksz884x.c +2 -2
@@ -1166,7 +1166,7 @@ struct ksz_port_info {* @tx_cfg: Cached transmit control settings. * @rx_cfg: Cached receive control settings. * @intr_mask: Current interrupt mask. -* @intr_set: Current interrup set. +* @intr_set: Current interrupt set. * @intr_blocked: Interrupt blocked. * @rx_desc_info: Receive descriptor information. * @tx_desc_info: Transmit descriptor information.
@@ -2096,7 +2096,7 @@ static void sw_dis_prio_rate(struct ksz_hw *hw, int port) }/** -* sw_init_prio_rate - initialize switch prioirty rate +* sw_init_prio_rate - initialize switch priority rate * @hw: The hardware instance. * * This routine initializes the priority rate function of the switch.
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 759/1102 net: ks8851: Fix receiver error in 100BASE-TX mode following software power-down
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Marek Vasut <[email protected]>
[ Upstream commit 66ef5adb75446627f8b6c26cd04f2adc86d4de56 ]
KSZ8851 errata sheet DS80000716D-page 4 Module 3 [1] states that, when issuing a software power-down (PMECR[1:0] = 10) followed by a power-on (PMECR[1:0] = 00), the receiver circuit can fail to start properly preventing communication. The Transmitter will still send data, but no data will be received.
The errata sheet also includes a workaround, which states that, it is recommended that the software power-down feature not be used.
Implement that workaround and drop the entry into software power-down mode. The ks8851_write_mac_addr() calls entry into normal power-on mode at the very beginning of the function, therefore dropping the second call to enter software power-down mode is sufficient here. The ks8851_net_stop() can only be called after ks8851_net_start() was already called, and ks8851_net_start() also makes the MAC enter normal power-on mode, therefore it is also fine to drop the call to enter software power-down mode from ks8851_net_stop().
This will lead to a slight increase in power consumption, but it also fixes a sporadic reliability problem on at least KSZ8851-16MLL, which is where the problem was reported and this fix was tested.
[1] https://ww1.microchip.com/downloads/en/DeviceDoc/80000716D.pdf
--- drivers/net/ethernet/micrel/ks8851_common.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-)
drivers/net/ethernet/micrel/ks8851_common.c +1 -7
@@ -143,9 +143,6 @@ static int ks8851_write_mac_addr(struct net_device *dev)ks8851_wrreg16 (ks,KS_MAR (i), val); } -if (!netif_running (dev)) -ks8851_set_powermode (ks,PMECR_PM_SOFTDOWN ); -ks8851_unlock (ks);return 0 ;
@@ -494,8 +491,7 @@ static int ks8851_net_open(struct net_device *dev)* @dev: The device being closed. * * Called to close down a network device which has been active. Cancel any -* work, shutdown the RX and TX process and then place the chip into a low -* power state whilst it is not being used. +* work and shutdown the RX and TX process. */ static int ks8851_net_stop (struct net_device *dev) {
@@ -522,8 +518,6 @@ static int ks8851_net_stop(struct net_device *dev)/* shutdown TX process */ ks8851_wrreg16 (ks,KS_TXCR ,0x0000 ); -/* set powermode to soft power down to save power */ -ks8851_set_powermode (ks,PMECR_PM_SOFTDOWN );ks8851_unlock (ks);/* ensure any queued tx buffers are dumped */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 760/1102 hwmon: (corsair-cpro) Create debugfs entries after hwmon registration
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linmao Li <[email protected]>
[ Upstream commit 508baf1713f32f287bfb4f85d759403ec8ba35a3 ]
ccp_debugfs_init() registers debugfs files whose private data is the devm allocated ccp. It runs before hwmon_device_register_with_info(), so when that registration fails, ccp_probe() returns with the files still in place. The HID core then frees ccp, and ccp_remove() is not called for a failed probe, so nothing removes them later either. Reading one of the files dereferences the freed pointer.
Create the debugfs entries only after the hwmon device has been registered, so no failing path can leave them behind.
The two version queries stay where they are. They send USB commands without holding ccp->mutex, which is only safe as long as nothing else can call send_usb_cmd(); once the hwmon device is registered its callbacks can do so concurrently. Only the debugfs creation moves, and it is told which queries succeeded.
--- drivers/hwmon/corsair-cpro.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-)
drivers/hwmon/corsair-cpro.c +13 -7
@@ -566,21 +566,18 @@ static int bootloader_show(struct seq_file *seqf, void *unused) }DEFINE_SHOW_ATTRIBUTE (bootloader); -static void ccp_debugfs_init (struct ccp_device *ccp) +static void ccp_debugfs_init (struct ccp_device *ccp,bool fw_valid,bool bl_valid) {char name[32 ]; -int ret;scnprintf (name,sizeof (name),"corsaircpro-%s" ,dev_name (&ccp->hdev->dev)); ccp->debugfs =debugfs_create_dir (name,NULL ); - ret =get_fw_version (ccp); -if (!ret) +if (fw_valid)debugfs_create_file ("firmware_version" ,0444 , ccp->debugfs, ccp, &firmware_fops); - ret =get_bl_version (ccp); -if (!ret) +if (bl_valid)debugfs_create_file ("bootloader_version" ,0444 , ccp->debugfs, ccp, &bootloader_fops); }
@@ -588,6 +585,7 @@ static void ccp_debugfs_init(struct ccp_device *ccp)static int ccp_probe (struct hid_device *hdev,const struct hid_device_id *id) {struct ccp_device *ccp; +bool fw_valid, bl_valid;int ret; ccp =devm_kzalloc (&hdev->dev,sizeof (*ccp),GFP_KERNEL );
@@ -632,7 +630,13 @@ static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id)if (ret) goto out_hw_close; -ccp_debugfs_init (ccp); +/* +* Query the versions before registering the hwmon device: they send +* USB commands without holding ccp->mutex, which is only safe while +* nothing else can call send_usb_cmd(). +*/ + fw_valid = !get_fw_version (ccp); + bl_valid = !get_bl_version (ccp); ccp->hwmon_dev =hwmon_device_register_with_info (&hdev->dev,"corsaircpro" , ccp, &ccp_chip_info,NULL );
@@ -641,6 +645,8 @@ static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id) goto out_hw_close; } +ccp_debugfs_init (ccp, fw_valid, bl_valid); +return 0 ; out_hw_close:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 761/1102 hwmon: (gpio-fan) take fan_data->lock in gpio_fan_shutdown()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cong Nguyen <[email protected]>
[ Upstream commit bb2424c3502cc72292eedade46960c331d5f28fb ]
set_fan_speed() writes the control GPIOs one bit at a time. Every other caller locks around it; gpio_fan_shutdown() doesn't. If it races a locked caller, the GPIO writes can interleave and leave the fan at a speed neither caller asked for.
--- drivers/hwmon/gpio-fan.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/hwmon/gpio-fan.c +4 -1
@@ -561,8 +561,11 @@ static void gpio_fan_shutdown(struct platform_device *pdev) {struct gpio_fan_data *fan_data =platform_get_drvdata (pdev); -if (fan_data->gpios) +if (fan_data->gpios) { +mutex_lock (&fan_data->lock);set_fan_speed (fan_data,0 ); +mutex_unlock (&fan_data->lock); + } }static int gpio_fan_suspend (struct device *dev)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 762/1102 hwmon: (aspeed-pwm-tacho) Propagate reset deassert errors
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pengpeng Hou <[email protected]>
[ Upstream commit 09a9e1746a87845d7d8e2b4e23bb613306effdff ]
aspeed_pwm_tacho_probe() installs its reset cleanup action and configures the
controller after an unchecked reset deassertion.
Stop probing when the reset controller rejects the transition, before the hwmon device becomes visible.
--- drivers/hwmon/aspeed-pwm-tacho.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/hwmon/aspeed-pwm-tacho.c +3 -1
@@ -933,7 +933,9 @@ static int aspeed_pwm_tacho_probe(struct platform_device *pdev) "missing or invalid reset controller device tree entry");return PTR_ERR (priv->rst); } -reset_control_deassert (priv->rst); + ret =reset_control_deassert (priv->rst); +if (ret) +return ret; ret =devm_add_action_or_reset (dev, aspeed_pwm_tacho_remove, priv);if (ret)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 763/1102 hwmon: (corsair-cpro) Remove debugfs entries when probe fails
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linmao Li <[email protected]>
[ Upstream commit 4ee875c423c66c45d7ef7bbff403cd0e3971e0a2 ]
ccp_debugfs_init() registers debugfs files whose private data is the devm allocated ccp. If hwmon_device_register_with_info() fails right after it, ccp_probe() returns without removing them: the HID core then frees ccp, and ccp_remove() is not called for a failed probe, so the files stay behind. Reading one of them dereferences the freed pointer.
Remove the debugfs entries on that error path. debugfs_remove_recursive() waits for readers already inside the show callbacks, so ccp is no longer reachable through debugfs by the time probe returns.
--- drivers/hwmon/corsair-cpro.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/hwmon/corsair-cpro.c +3 -1
@@ -642,13 +642,15 @@ static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id) ccp, &ccp_chip_info, NULL);if (IS_ERR (ccp->hwmon_dev)) { ret =PTR_ERR (ccp->hwmon_dev); - goto out_hw_close; + goto out_debugfs_remove; }ccp_debugfs_init (ccp, fw_valid, bl_valid);return 0 ; +out_debugfs_remove: +debugfs_remove_recursive (ccp->debugfs); out_hw_close:hid_hw_close (hdev);hid_device_io_stop (hdev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 764/1102 octeontx2-pf: reset HTB scheduler topology before freeing queues
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ratheesh Kannoth <[email protected]>
[ Upstream commit ef39fca8508597fa565cf2be72a884a712fb98af ]
HTB offload programs NIX_AF_TLxX_TOPOLOGY on QoS-allocated scheduler queues via otx2_qos_txschq_set_parent_topology(), but teardown freed those queues without clearing TOPOLOGY. The AF only restores PARENT and SCHEDULE on free, so PRIO_ANCHOR/RR_PRIO settings can survive in the shared scheduler pool and affect later allocations.
Add otx2_qos_reset_schq_topology() and otx2_qos_free_hw_schq() to zero TL4 through TL2 TOPOLOGY before each schq is returned to the AF during hierarchy teardown and cfg rollback. Skip the aggregation level (TL1):
it is a per-tx-link queue shared by the PF, default Tx hierarchy and VFs, and is not freed back to the AF by nix_txschq_free_one().
--- .../net/ethernet/marvell/octeontx2/nic/qos.c | 60 +++++++++++++++++-- 1 file changed, 55 insertions(+), 5 deletions(-)
drivers/net/ethernet/marvell/octeontx2/nic/qos.c +55 -5
@@ -235,13 +235,63 @@ static int otx2_qos_txschq_set_parent_topology(struct otx2_nic *pfvf,return rc; } +static int otx2_qos_reset_schq_topology (struct otx2_nic *pfvf,u16 lvl, +u16 schq) +{ +struct mbox *mbox = &pfvf->mbox; +struct nix_txschq_config *cfg; +int rc; + +if (lvl <NIX_TXSCH_LVL_TL4 || lvl >=NIX_TXSCH_LVL_TL1 ) +return 0 ; + +mutex_lock (&mbox->lock); + + cfg =otx2_mbox_alloc_msg_nix_txschq_cfg (mbox); +if (!cfg) { +mutex_unlock (&mbox->lock); +return -ENOMEM ; + } + + cfg->lvl = lvl; + cfg->num_regs =1 ; + +if (lvl ==NIX_TXSCH_LVL_TL4 ) + cfg->reg[0 ] =NIX_AF_TL4X_TOPOLOGY (schq); +else if (lvl ==NIX_TXSCH_LVL_TL3 ) + cfg->reg[0 ] =NIX_AF_TL3X_TOPOLOGY (schq); +else if (lvl ==NIX_TXSCH_LVL_TL2 ) + cfg->reg[0 ] =NIX_AF_TL2X_TOPOLOGY (schq); + + cfg->regval[0 ] =0 ; + + rc =otx2_sync_mbox_msg (mbox); + +mutex_unlock (&mbox->lock); + +return rc; +} + +static void otx2_qos_free_hw_schq (struct otx2_nic *pfvf,u16 lvl,u16 schq) +{ +int err; + + err =otx2_qos_reset_schq_topology (pfvf, lvl, schq); +if (err) +netdev_warn (pfvf->netdev, +"QoS: failed to reset topology for schq %u at level %u: %d\n" , + schq, lvl, err); + +otx2_txschq_free_one (pfvf, lvl, schq); +} +static void otx2_qos_free_hw_node_schq (struct otx2_nic *pfvf,struct otx2_qos_node *parent) {struct otx2_qos_node *node;list_for_each_entry_reverse (node, &parent->child_schq_list, list) -otx2_txschq_free_one (pfvf, node->level, node->schq); +otx2_qos_free_hw_schq (pfvf, node->level, node->schq); } static void otx2_qos_free_hw_node(struct otx2_nic *pfvf,
@@ -252,7 +302,7 @@ static void otx2_qos_free_hw_node(struct otx2_nic *pfvf,list_for_each_entry_safe (node, tmp, &parent->child_list, list) {otx2_qos_free_hw_node (pfvf, node);otx2_qos_free_hw_node_schq (pfvf, node); -otx2_txschq_free_one (pfvf, node->level, node->schq); +otx2_qos_free_hw_schq (pfvf, node->level, node->schq); } }
@@ -266,7 +316,7 @@ static void otx2_qos_free_hw_cfg(struct otx2_nic *pfvf,otx2_qos_free_hw_node_schq (pfvf, node);/* free node hw mappings */ -otx2_txschq_free_one (pfvf, node->level, node->schq); +otx2_qos_free_hw_schq (pfvf, node->level, node->schq);mutex_unlock (&pfvf->qos.qos_lock); }
@@ -913,7 +963,7 @@ static void otx2_qos_free_cfg(struct otx2_nic *pfvf, struct otx2_qos_cfg *cfg)for (lvl =0 ; lvl <NIX_TXSCH_LVL_CNT ; lvl++) {for (idx =0 ; idx < cfg->schq[lvl]; idx++) { schq = cfg->schq_list[lvl][idx]; -otx2_txschq_free_one (pfvf, lvl, schq); +otx2_qos_free_hw_schq (pfvf, lvl, schq); } }
@@ -921,7 +971,7 @@ static void otx2_qos_free_cfg(struct otx2_nic *pfvf, struct otx2_qos_cfg *cfg)for (idx =0 ; idx < cfg->schq_contig[lvl]; idx++) {if (cfg->schq_index_used[lvl][idx]) { schq = cfg->schq_contig_list[lvl][idx]; -otx2_txschq_free_one (pfvf, lvl, schq); +otx2_qos_free_hw_schq (pfvf, lvl, schq); } } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 765/1102 vxlan: initialize _md in vxlan_xmit_one()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit be83178bfc44588f6e3adb827ed874c683193466 ]
If a VXLAN device is configured with both VXLAN_F_COLLECT_METADATA and VXLAN_F_GBP, and a packet is transmitted through it using an external ip_tunnel_info that lacks the IP_TUNNEL_VXLAN_OPT_BIT flag, md is left pointing to the uninitialized _md stack variable:
if (test_bit(IP_TUNNEL_VXLAN_OPT_BIT, info->key.tun_flags)) {
if (info->options_len < sizeof(*md))
goto drop;
md = ip_tunnel_info_opts(info);
}Because IP_TUNNEL_VXLAN_OPT_BIT is not set, md is not updated and remains pointing to _md. Later, vxlan_build_skb() is called with md, which eventually calls vxlan_build_gbp_hdr():
if (vxflags & VXLAN_F_GBP)
vxlan_build_gbp_hdr(vxh, md);
Inside vxlan_build_gbp_hdr(), md->gbp is read:
if (!md->gbp)
return;
gbp = (struct vxlanhdr_gbp *)vxh;
...
if (md->gbp & VXLAN_GBP_DONT_LEARN)
gbp->dont_learn = 1;If the stack contains garbage, this causes:
1) VXLAN_HF_GBP flag to be spuriously set in the VXLAN header.
2) gbp->dont_learn and gbp->policy_applied to be set from stack bits.
3) gbp->policy_id to receive 16 bits of uninitialized kernel stack data, leaking it onto the wire.
Fix this by zero-initializing _md. If IP_TUNNEL_VXLAN_OPT_BIT is not present, md->gbp remains 0, and vxlan_build_gbp_hdr() returns early without modifying the VXLAN header.
--- drivers/net/vxlan/vxlan_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/vxlan/vxlan_core.c +1 -1
@@ -2392,7 +2392,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,struct ip_tunnel_key key;struct vxlan_dev *vxlan =netdev_priv (dev);const struct iphdr *old_iph; -struct vxlan_metadata _md; +struct vxlan_metadata _md = {};struct vxlan_metadata *md = &_md;unsigned int pkt_len = skb->len;__be16 src_port =0 , dst_port;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 766/1102 ppp_synctty: ensure a writeable skb header
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qingfang Deng <[email protected]>
[ Upstream commit 8aaeb56aff2a557a88f83ae866da2c91ad247e59 ]
ppp_sync_txmunge() checks headroom before prepending the address and control bytes, but does not ensure that the skb header is writable. A received skb can reach this function through PPP channel bridging without passing through ppp_start_xmit(), which calls skb_cow_head().
For example, a PPPoE frame may share its buffer with a clone queued to an AF_PACKET socket. If it is bridged to a synchronous tty channel, the address/control bytes can overwrite data still visible to that socket.
Use skb_cow_head() to ensure both sufficient headroom and a writable header.
--- drivers/net/ppp/ppp_synctty.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-)
drivers/net/ppp/ppp_synctty.c +3 -11
@@ -457,17 +457,9 @@ ppp_sync_txmunge(struct syncppp *ap, struct sk_buff *skb)/* prepend address/control fields if necessary */ if ((ap->flags &SC_COMP_AC ) ==0 || islcp) { -if (skb_headroom (skb) <2 ) { -struct sk_buff *npkt =dev_alloc_skb (skb->len +2 ); -if (npkt ==NULL ) { -kfree_skb (skb); -return NULL ; - } -skb_reserve (npkt,2 ); -skb_copy_from_linear_data (skb, -skb_put (npkt, skb->len), skb->len); -consume_skb (skb); - skb = npkt; +if (skb_cow_head (skb,2 )) { +kfree_skb (skb); +return NULL ; }skb_push (skb,2 ); skb->data[0 ] =PPP_ALLSTATIONS ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 767/1102 net: stmmac: initialize ptp_lock at probe time
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit 0338c68e22abd2ee509ec2e32508a50896618c32 ]
priv->ptp_lock is only initialized in stmmac_ptp_register(), which runs during __stmmac_open(). However, the lock is also used while the interface is down and has never been opened: tc_taprio_configure() invokes the PTP gettime64() callback to compute the EST base time when offloading a TAPRIO schedule, and stmmac_get_time() takes priv->ptp_lock. Using an uninitialized rwlock is undefined behaviour. Move the rwlock_init() to __stmmac_dvr_probe(), together with the other private locks, so that ptp_lock is always valid regardless of the interface state.
--- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 1 + drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +1 -0
@@ -7733,6 +7733,7 @@ int stmmac_dvr_probe(struct device *device,stmmac_napi_add (ndev);mutex_init (&priv->lock); +rwlock_init (&priv->ptp_lock); priv->fpe_cfg.verify_retries =STMMAC_FPE_MM_MAX_VERIFY_RETRIES ; priv->fpe_cfg.verify_time =STMMAC_FPE_MM_MAX_VERIFY_TIME_MS ;
drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c +0 -1
@@ -309,7 +309,6 @@ void stmmac_ptp_register(struct stmmac_priv *priv) stmmac_ptp_clock_ops.n_per_out = priv->dma_cap.pps_out_num; stmmac_ptp_clock_ops.n_ext_ts = priv->dma_cap.aux_snapshot_n; -rwlock_init (&priv->ptp_lock);mutex_init (&priv->aux_ts_lock); priv->ptp_clock_ops = stmmac_ptp_clock_ops;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 768/1102 selftests/powerpc/tm: Fix tcheck() reading uninitialised CR value
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thibault Ferrante <[email protected]>
[ Upstream commit ed28b16eab705071d28edaace47189c2eb3aa108 ]
tcheck() is used to check the current transaction state (active, suspended, doomed) via the "tcheck" instruction, which writes its result into CR field 0. The inline asm declared a GPR output operand for this result but never actually moved the CR into it.
Every caller (tcheck_doomed(), tcheck_active(), tcheck_suspended(), tcheck_transactional()) has effectively been testing bits of an unrelated, arbitrary register value since this helper was introduced.
The "& 4" mask discards the TDOOMED and TS_lsb (suspended) bits before they ever reach the callers, so tcheck_doomed() and tcheck_suspended() can never return true, and tcheck_transactional() degrades to being equivalent to tcheck_active().
Fix tcheck() to actually move CR into the output register with mfcr, and widen the mask from "& 4" to "& 0xf" so the full CR0 nibble (TDOOMED | TS_msb | TS_lsb | reserved) is preserved for the callers.
This bug has been present since tcheck() was introduced.
--- tools/testing/selftests/powerpc/tm/tm.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
tools/testing/selftests/powerpc/tm/tm.h +6 -2
@@ -105,8 +105,12 @@ static inline bool failure_is_nesting(void)static inline int tcheck (void ) {long cr; - asmvolatile ("tcheck 0" :"=r" (cr) : :"cr0" ); -return (cr >>28 ) &4 ; + asmvolatile ("tcheck 0;" +"mfcr %0;" + :"=r" (cr) + : + :"cr0" ); +return (cr >>28 ) &0xf ; }static inline bool tcheck_doomed (void )
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 769/1102 perf: Supply task information to sched_task()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kan Liang <[email protected]>
[ Upstream commit d57e94f5b891925e4f2796266eba31edd5a01903 ]
To save/restore LBR call stack data in system-wide mode, the task_struct information is required.
Extend the parameters of sched_task() to supply task_struct information.
When schedule in, the LBR call stack data for new task will be restored. When schedule out, the LBR call stack data for old task will be saved. Only need to pass the required task_struct information.
Stable-dep-of: 59e63416f515 ("perf/core: Allow list_del during perf_event_overflow()")
--- arch/powerpc/perf/core-book3s.c | 8 ++++++-- arch/s390/kernel/perf_pai_crypto.c | 3 ++- arch/s390/kernel/perf_pai_ext.c | 3 ++- arch/x86/events/amd/brs.c | 3 ++- arch/x86/events/amd/lbr.c | 3 ++- arch/x86/events/core.c | 5 +++-- arch/x86/events/intel/core.c | 4 ++-- arch/x86/events/intel/lbr.c | 3 ++- arch/x86/events/perf_event.h | 14 +++++++++----- include/linux/perf_event.h | 2 +- kernel/events/core.c | 20 +++++++++++--------- 11 files changed, 42 insertions(+), 26 deletions(-)
arch/powerpc/perf/core-book3s.c +6 -2
@@ -132,7 +132,10 @@ static unsigned long ebb_switch_in(bool ebb, struct cpu_hw_events *cpuhw)static inline void power_pmu_bhrb_enable (struct perf_event *event) {}static inline void power_pmu_bhrb_disable (struct perf_event *event) {} -static void power_pmu_sched_task (struct perf_event_pmu_context *pmu_ctx,bool sched_in) {} +static void power_pmu_sched_task (struct perf_event_pmu_context *pmu_ctx, +struct task_struct *task,bool sched_in) +{ +}static inline void power_pmu_bhrb_read (struct perf_event *event,struct cpu_hw_events *cpuhw) {}static void pmao_restore_workaround (bool ebb) { }#endif /* CONFIG_PPC32 */
@@ -444,7 +447,8 @@ static void power_pmu_bhrb_disable(struct perf_event *event)/* Called from ctxsw to prevent one process's branch entries to * mingle with the other process's entries during context switch. */ -static void power_pmu_sched_task (struct perf_event_pmu_context *pmu_ctx,bool sched_in) +static void power_pmu_sched_task (struct perf_event_pmu_context *pmu_ctx, +struct task_struct *task,bool sched_in) {if (!ppmu->bhrb_nr)return ;
arch/s390/kernel/perf_pai_crypto.c +2 -1
@@ -518,7 +518,8 @@ static void paicrypt_have_samples(void)/* Called on schedule-in and schedule-out. No access to event structure, * but for sampling only event CRYPTO_ALL is allowed. */ -static void paicrypt_sched_task (struct perf_event_pmu_context *pmu_ctx,bool sched_in) +static void paicrypt_sched_task (struct perf_event_pmu_context *pmu_ctx, +struct task_struct *task,bool sched_in) {/* We started with a clean page on event installation. So read out * results on schedule_out and if page was dirty, save old values.
arch/s390/kernel/perf_pai_ext.c +2 -1
@@ -542,7 +542,8 @@ static void paiext_have_samples(void)/* Called on schedule-in and schedule-out. No access to event structure, * but for sampling only event NNPA_ALL is allowed. */ -static void paiext_sched_task (struct perf_event_pmu_context *pmu_ctx,bool sched_in) +static void paiext_sched_task (struct perf_event_pmu_context *pmu_ctx, +struct task_struct *task,bool sched_in) {/* We started with a clean page on event installation. So read out * results on schedule_out and if page was dirty, save old values.
arch/x86/events/amd/brs.c +2 -1
@@ -381,7 +381,8 @@ static void amd_brs_poison_buffer(void)* On ctxswin, sched_in = true, called after the PMU has started * On ctxswout, sched_in = false, called before the PMU is stopped */ -void amd_pmu_brs_sched_task (struct perf_event_pmu_context *pmu_ctx,bool sched_in) +void amd_pmu_brs_sched_task (struct perf_event_pmu_context *pmu_ctx, +struct task_struct *task,bool sched_in) {struct cpu_hw_events *cpuc =this_cpu_ptr (&cpu_hw_events);
arch/x86/events/amd/lbr.c +2 -1
@@ -372,7 +372,8 @@ void amd_pmu_lbr_del(struct perf_event *event)perf_sched_cb_dec (event->pmu); } -void amd_pmu_lbr_sched_task (struct perf_event_pmu_context *pmu_ctx,bool sched_in) +void amd_pmu_lbr_sched_task (struct perf_event_pmu_context *pmu_ctx, +struct task_struct *task,bool sched_in) {struct cpu_hw_events *cpuc =this_cpu_ptr (&cpu_hw_events);
arch/x86/events/core.c +3 -2
@@ -2626,9 +2626,10 @@ static const struct attribute_group *x86_pmu_attr_groups[] = { NULL, }; -static void x86_pmu_sched_task (struct perf_event_pmu_context *pmu_ctx,bool sched_in) +static void x86_pmu_sched_task (struct perf_event_pmu_context *pmu_ctx, +struct task_struct *task,bool sched_in) { -static_call_cond (x86_pmu_sched_task)(pmu_ctx, sched_in); +static_call_cond (x86_pmu_sched_task)(pmu_ctx, task, sched_in); } static void x86_pmu_swap_task_ctx(struct perf_event_pmu_context *prev_epc,
arch/x86/events/intel/core.c +2 -2
@@ -5211,10 +5211,10 @@ static void intel_pmu_cpu_dead(int cpu) }static void intel_pmu_sched_task (struct perf_event_pmu_context *pmu_ctx, -bool sched_in) +struct task_struct *task,bool sched_in) {intel_pmu_pebs_sched_task (pmu_ctx, sched_in); -intel_pmu_lbr_sched_task (pmu_ctx, sched_in); +intel_pmu_lbr_sched_task (pmu_ctx, task, sched_in); } static void intel_pmu_swap_task_ctx(struct perf_event_pmu_context *prev_epc,
arch/x86/events/intel/lbr.c +2 -1
@@ -539,7 +539,8 @@ void intel_pmu_lbr_swap_task_ctx(struct perf_event_pmu_context *prev_epc, task_context_opt(next_ctx_data)->lbr_callstack_users); } -void intel_pmu_lbr_sched_task (struct perf_event_pmu_context *pmu_ctx,bool sched_in) +void intel_pmu_lbr_sched_task (struct perf_event_pmu_context *pmu_ctx, +struct task_struct *task,bool sched_in) {struct cpu_hw_events *cpuc =this_cpu_ptr (&cpu_hw_events);void *task_ctx;
arch/x86/events/perf_event.h +9 -5
@@ -861,7 +861,7 @@ struct x86_pmu {void (*check_microcode)(void );void (*sched_task)(struct perf_event_pmu_context *pmu_ctx, -bool sched_in); +struct task_struct *task,bool sched_in);/* * Intel Arch Perfmon v2+
@@ -1387,7 +1387,8 @@ void amd_pmu_lbr_reset(void);void amd_pmu_lbr_read (void );void amd_pmu_lbr_add (struct perf_event *event);void amd_pmu_lbr_del (struct perf_event *event); -void amd_pmu_lbr_sched_task (struct perf_event_pmu_context *pmu_ctx,bool sched_in); +void amd_pmu_lbr_sched_task (struct perf_event_pmu_context *pmu_ctx, +struct task_struct *task,bool sched_in);void amd_pmu_lbr_enable_all (void );void amd_pmu_lbr_disable_all (void );int amd_pmu_lbr_hw_config (struct perf_event *event);
@@ -1441,7 +1442,8 @@ static inline void amd_pmu_brs_del(struct perf_event *event) perf_sched_cb_dec(event->pmu); } -void amd_pmu_brs_sched_task (struct perf_event_pmu_context *pmu_ctx,bool sched_in); +void amd_pmu_brs_sched_task (struct perf_event_pmu_context *pmu_ctx, +struct task_struct *task,bool sched_in);#else static inline int amd_brs_init (void ) {
@@ -1466,7 +1468,8 @@ static inline void amd_pmu_brs_del(struct perf_event *event) { } -static inline void amd_pmu_brs_sched_task (struct perf_event_pmu_context *pmu_ctx,bool sched_in) +static inline void amd_pmu_brs_sched_task (struct perf_event_pmu_context *pmu_ctx, +struct task_struct *task,bool sched_in) { }
@@ -1656,7 +1659,8 @@ void intel_pmu_lbr_save_brstack(struct perf_sample_data *data,void intel_pmu_lbr_swap_task_ctx (struct perf_event_pmu_context *prev_epc,struct perf_event_pmu_context *next_epc); -void intel_pmu_lbr_sched_task (struct perf_event_pmu_context *pmu_ctx,bool sched_in); +void intel_pmu_lbr_sched_task (struct perf_event_pmu_context *pmu_ctx, +struct task_struct *task,bool sched_in);u64 lbr_from_signext_quirk_wr (u64 val);
include/linux/perf_event.h +1 -1
@@ -495,7 +495,7 @@ struct pmu {* context-switches callback */ void (*sched_task) (struct perf_event_pmu_context *pmu_ctx, -bool sched_in); +struct task_struct *task,bool sched_in);/* * Kmem cache of PMU specific data
kernel/events/core.c +11 -9
@@ -3594,7 +3594,8 @@ static void perf_event_swap_task_ctx_data(struct perf_event_context *prev_ctx, } } -static void perf_ctx_sched_task_cb (struct perf_event_context *ctx,bool sched_in) +static void perf_ctx_sched_task_cb (struct perf_event_context *ctx, +struct task_struct *task,bool sched_in) {struct perf_event_pmu_context *pmu_ctx;struct perf_cpu_pmu_context *cpc;
@@ -3603,7 +3604,7 @@ static void perf_ctx_sched_task_cb(struct perf_event_context *ctx, bool sched_in cpc =this_cpu_ptr (pmu_ctx->pmu->cpu_pmu_context);if (cpc->sched_cb_usage && pmu_ctx->pmu->sched_task) - pmu_ctx->pmu->sched_task (pmu_ctx, sched_in); + pmu_ctx->pmu->sched_task (pmu_ctx, task, sched_in); } }
@@ -3666,7 +3667,7 @@ perf_event_context_sched_out(struct task_struct *task, struct task_struct *next)WRITE_ONCE (ctx->task, next);WRITE_ONCE (next_ctx->task, task); -perf_ctx_sched_task_cb (ctx, false); +perf_ctx_sched_task_cb (ctx, task, false);perf_event_swap_task_ctx_data (ctx, next_ctx);perf_ctx_enable (ctx, false);
@@ -3696,7 +3697,7 @@ perf_event_context_sched_out(struct task_struct *task, struct task_struct *next)perf_ctx_disable (ctx, false); inside_switch: -perf_ctx_sched_task_cb (ctx, false); +perf_ctx_sched_task_cb (ctx, task, false);task_ctx_sched_out (ctx,NULL ,EVENT_ALL );perf_ctx_enable (ctx, false);
@@ -3738,7 +3739,8 @@ void perf_sched_cb_inc(struct pmu *pmu)* PEBS requires this to provide PID/TID information. This requires we flush * all queued PEBS records before we context switch to a new task. */ -static void __perf_pmu_sched_task (struct perf_cpu_pmu_context *cpc,bool sched_in) +static void __perf_pmu_sched_task (struct perf_cpu_pmu_context *cpc, +struct task_struct *task,bool sched_in) {struct perf_cpu_context *cpuctx =this_cpu_ptr (&perf_cpu_context);struct pmu *pmu;
@@ -3752,7 +3754,7 @@ static void __perf_pmu_sched_task(struct perf_cpu_pmu_context *cpc, bool sched_iperf_ctx_lock (cpuctx, cpuctx->task_ctx);perf_pmu_disable (pmu); - pmu->sched_task (cpc->task_epc, sched_in); + pmu->sched_task (cpc->task_epc, task, sched_in);perf_pmu_enable (pmu);perf_ctx_unlock (cpuctx, cpuctx->task_ctx);
@@ -3770,7 +3772,7 @@ static void perf_pmu_sched_task(struct task_struct *prev,return ;list_for_each_entry (cpc,this_cpu_ptr (&sched_cb_list), sched_cb_entry) -__perf_pmu_sched_task (cpc, sched_in); +__perf_pmu_sched_task (cpc, sched_in ? next : prev, sched_in); } static void perf_event_switch(struct task_struct *task,
@@ -4072,7 +4074,7 @@ static void perf_event_context_sched_in(struct task_struct *task)perf_ctx_lock (cpuctx, ctx);perf_ctx_disable (ctx, false); -perf_ctx_sched_task_cb (ctx, true); +perf_ctx_sched_task_cb (ctx, task, true);perf_ctx_enable (ctx, false);perf_ctx_unlock (cpuctx, ctx);
@@ -4103,7 +4105,7 @@ static void perf_event_context_sched_in(struct task_struct *task)perf_event_sched_in (cpuctx, ctx,NULL ); -perf_ctx_sched_task_cb (cpuctx->task_ctx, true); +perf_ctx_sched_task_cb (cpuctx->task_ctx, task, true);if (!RB_EMPTY_ROOT (&ctx->pinned_groups.tree))perf_ctx_enable (&cpuctx->ctx, false);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 770/1102 perf/core: Allow list_del during perf_event_overflow()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Richter <[email protected]>
[ Upstream commit 59e63416f5153e7d58652c616fbdcb7d5e01fff7 ]
A PMU might use perf_sched_cb_inc() and perf_sched_cb_dec() interface to get the PMU call back function pmu::sched_task invoked at schedule in and schedule out. This is achieved by walking along the list anchored by sched_cb_list.
The following scenario might lead to a list corruption.
perf_pmu_sched_task()
for_each_list_entry(..., &sched_cb_list)
+--> __perf_pmu_sched_task()
+--> event->pmu->sched_task())
+--> PMU_push_sample()
+--> perf_event_overflow()
+--> __perf_event_overflow()
+--> pmu->stop()
+--> perf_sched_cb_dec()
remove entry from sched_cb_list
while list node in use.This happens when ioctl(fd, PERF_EVENT_IOC_REFRESH, xxx) has been invoked and perf_event::event_limit hits zero.
Prevent the list corruption and convert for_each_list_entry() to for_each_list_entry_safe().
--- kernel/events/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
kernel/events/core.c +2 -2
@@ -3765,13 +3765,13 @@ static void perf_pmu_sched_task(struct task_struct *prev, bool sched_in) {struct perf_cpu_context *cpuctx =this_cpu_ptr (&perf_cpu_context); -struct perf_cpu_pmu_context *cpc; +struct perf_cpu_pmu_context *cpc, *cpc2;/* cpuctx->task_ctx will be handled in perf_event_context_sched_in/out */ if (prev == next || cpuctx->task_ctx)return ; -list_for_each_entry (cpc,this_cpu_ptr (&sched_cb_list), sched_cb_entry) +list_for_each_entry_safe (cpc, cpc2,this_cpu_ptr (&sched_cb_list), sched_cb_entry)__perf_pmu_sched_task (cpc, sched_in ? next : prev, sched_in); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 771/1102 perf/core: Check sample_type in perf_sample_save_callchain
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yabin Cui <[email protected]>
[ Upstream commit f226805bc5f60adf03783d8e4cbfe303ccecd64e ]
Check sample_type in perf_sample_save_callchain() to prevent saving callchain data when it isn't required.
Stable-dep-of: 88aa1223bfff ("perf/x86/intel: Correct pt_regs->flags update for PEBS path")
--- arch/x86/events/amd/ibs.c | 3 +-- arch/x86/events/intel/ds.c | 6 ++---- include/linux/perf_event.h | 5 +++++ 3 files changed, 8 insertions(+), 6 deletions(-)
arch/x86/events/amd/ibs.c +1 -2
@@ -1138,8 +1138,7 @@ static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs)* recorded as part of interrupt regs. Thus we need to use rip from * interrupt regs while unwinding call stack. */ -if (event->attr.sample_type &PERF_SAMPLE_CALLCHAIN ) -perf_sample_save_callchain (&data, event, iregs); +perf_sample_save_callchain (&data, event, iregs); throttle =perf_event_overflow (event, &data, ®s); out:
arch/x86/events/intel/ds.c +2 -4
@@ -1771,8 +1771,7 @@ static void setup_pebs_fixed_sample_data(struct perf_event *event,* previous PMI context or an (I)RET happened between the record and * PMI. */ -if (sample_type &PERF_SAMPLE_CALLCHAIN ) -perf_sample_save_callchain (data, event, iregs); +perf_sample_save_callchain (data, event, iregs);/* * We use the interrupt regs as a base because the PEBS record does not
@@ -1939,8 +1938,7 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event,* previous PMI context or an (I)RET happened between the record and * PMI. */ -if (sample_type &PERF_SAMPLE_CALLCHAIN ) -perf_sample_save_callchain (data, event, iregs); +perf_sample_save_callchain (data, event, iregs); *regs = *iregs;/* The ip in basic is EventingIP */
include/linux/perf_event.h +5 -0
@@ -1286,6 +1286,11 @@ static inline void perf_sample_save_callchain(struct perf_sample_data *data, {int size =1 ; +if (!(event->attr.sample_type &PERF_SAMPLE_CALLCHAIN )) +return ; +if (WARN_ON_ONCE (data->sample_flags &PERF_SAMPLE_CALLCHAIN )) +return ; + data->callchain =perf_callchain (event, regs); size += data->callchain->nr;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 772/1102 perf/x86/intel/ds: Clarify adaptive PEBS processing
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kan Liang <[email protected]>
[ Upstream commit 7087bfb0adc9a12ec3b463b1d38072c5efce5d6c ]
Modify the pebs_basic and pebs_meminfo structs to make the bitfields more explicit to ease readability of the code.
Stable-dep-of: 88aa1223bfff ("perf/x86/intel: Correct pt_regs->flags update for PEBS path")
--- arch/x86/events/intel/ds.c | 43 ++++++++++++++----------------- arch/x86/include/asm/perf_event.h | 16 ++++++++++-- 2 files changed, 34 insertions(+), 25 deletions(-)
arch/x86/events/intel/ds.c +20 -23
@@ -1898,8 +1898,6 @@ static void adaptive_pebs_save_regs(struct pt_regs *regs, }#define PEBS_LATENCY_MASK 0xffff -#define PEBS_CACHE_LATENCY_OFFSET 32 -#define PEBS_RETIRE_LATENCY_OFFSET 32/* * With adaptive PEBS the layout depends on what fields are configured.
@@ -1913,8 +1911,7 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event,struct cpu_hw_events *cpuc =this_cpu_ptr (&cpu_hw_events);struct pebs_basic *basic = __pebs;void *next_record = basic +1 ; -u64 sample_type; -u64 format_size; +u64 sample_type, format_group;struct pebs_meminfo *meminfo =NULL ;struct pebs_gprs *gprs =NULL ;struct x86_perf_regs *perf_regs;
@@ -1926,7 +1923,7 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event, perf_regs->xmm_regs =NULL ; sample_type = event->attr.sample_type; - format_size = basic->format_size; + format_group = basic->format_group;perf_sample_data_init (data,0 , event->hw.last_period); data->period = event->hw.last_period;
@@ -1947,7 +1944,7 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event,if (sample_type &PERF_SAMPLE_WEIGHT_STRUCT ) {if (x86_pmu.flags &PMU_FL_RETIRE_LATENCY ) - data->weight.var3_w = format_size >>PEBS_RETIRE_LATENCY_OFFSET &PEBS_LATENCY_MASK ; + data->weight.var3_w = basic->retire_latency;else data->weight.var3_w =0 ; }
@@ -1957,12 +1954,12 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event,* But PERF_SAMPLE_TRANSACTION needs gprs->ax. * Save the pointer here but process later. */ -if (format_size &PEBS_DATACFG_MEMINFO ) { +if (format_group &PEBS_DATACFG_MEMINFO ) { meminfo = next_record; next_record = meminfo +1 ; } -if (format_size &PEBS_DATACFG_GP ) { +if (format_group &PEBS_DATACFG_GP ) { gprs = next_record; next_record = gprs +1 ;
@@ -1975,14 +1972,13 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event,adaptive_pebs_save_regs (regs, gprs); } -if (format_size &PEBS_DATACFG_MEMINFO ) { +if (format_group &PEBS_DATACFG_MEMINFO ) {if (sample_type &PERF_SAMPLE_WEIGHT_TYPE ) { -u64 weight = meminfo->latency; +u64 latency = x86_pmu.flags &PMU_FL_INSTR_LATENCY ? + meminfo->cache_latency : meminfo->mem_latency; -if (x86_pmu.flags &PMU_FL_INSTR_LATENCY ) { - data->weight.var2_w = weight &PEBS_LATENCY_MASK ; - weight >>=PEBS_CACHE_LATENCY_OFFSET ; - } +if (x86_pmu.flags &PMU_FL_INSTR_LATENCY ) + data->weight.var2_w = meminfo->instr_latency;/* * Although meminfo::latency is defined as a u64,
@@ -1990,12 +1986,13 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event,* in practice on Ice Lake and earlier platforms. */ if (sample_type &PERF_SAMPLE_WEIGHT ) { - data->weight.full = weight ?: + data->weight.full = latency ?:intel_get_tsx_weight (meminfo->tsx_tuning); }else { - data->weight.var1_dw = (u32)(weight &PEBS_LATENCY_MASK ) ?: + data->weight.var1_dw = (u32 )latency ?:intel_get_tsx_weight (meminfo->tsx_tuning); } + data->sample_flags |=PERF_SAMPLE_WEIGHT_TYPE ; }
@@ -2016,16 +2013,16 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event, } } -if (format_size &PEBS_DATACFG_XMMS ) { +if (format_group &PEBS_DATACFG_XMMS ) {struct pebs_xmm *xmm = next_record; next_record = xmm +1 ; perf_regs->xmm_regs = xmm->xmm; } -if (format_size &PEBS_DATACFG_LBRS ) { +if (format_group &PEBS_DATACFG_LBRS ) {struct lbr_entry *lbr = next_record; -int num_lbr = ((format_size >>PEBS_DATACFG_LBR_SHIFT ) +int num_lbr = ((format_group >>PEBS_DATACFG_LBR_SHIFT ) &0xff ) +1 ; next_record = next_record + num_lbr *sizeof (struct lbr_entry );
@@ -2035,11 +2032,11 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event, } } -WARN_ONCE (next_record != __pebs + (format_size >>48 ), -"PEBS record size %llu, expected %llu, config %llx\n" , - format_size >>48 , +WARN_ONCE (next_record != __pebs + basic->format_size, +"PEBS record size %u, expected %llu, config %llx\n" , + basic->format_size, (u64)(next_record - __pebs), - basic->format_size); + format_group); } static inline void *
arch/x86/include/asm/perf_event.h +14 -2
@@ -448,7 +448,9 @@ static inline bool is_topdown_idx(int idx)*/ struct pebs_basic { -u64 format_size; +u64 format_group:32 , + retire_latency:16 , + format_size:16 ;u64 ip;u64 applicable_counters;u64 tsc;
@@ -457,7 +459,17 @@ struct pebs_basic {struct pebs_meminfo {u64 address;u64 aux; -u64 latency; +union { +/* pre Alder Lake */ +u64 mem_latency; +/* Alder Lake and later */ +struct { +u64 instr_latency:16 ; +u64 pad2:16 ; +u64 cache_latency:16 ; +u64 pad3:16 ; + }; + };u64 tsx_tuning; };
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 773/1102 perf/x86/intel/ds: Remove redundant assignments to sample.period
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Changbin Du <[email protected]>
[ Upstream commit 75a9001bab36f0456f6aae1ab0aa487db456464a ]
The perf_sample_data_init() has already set the period of sample, so no need to do it again.
Stable-dep-of: 88aa1223bfff ("perf/x86/intel: Correct pt_regs->flags update for PEBS path")
--- arch/x86/events/intel/ds.c | 3 --- 1 file changed, 3 deletions(-)
arch/x86/events/intel/ds.c +0 -3
@@ -1747,8 +1747,6 @@ static void setup_pebs_fixed_sample_data(struct perf_event *event,perf_sample_data_init (data,0 , event->hw.last_period); - data->period = event->hw.last_period; -/* * Use latency for weight (only avail with PEBS-LL) */
@@ -1925,7 +1923,6 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event, sample_type = event->attr.sample_type; format_group = basic->format_group;perf_sample_data_init (data,0 , event->hw.last_period); - data->period = event->hw.last_period;setup_pebs_time (event, data, basic->tsc);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 774/1102 perf/x86/intel/ds: Factor out PEBS group processing code to functions
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dapeng Mi <[email protected]>
[ Upstream commit 167cde7dc9b36b7a88f3c29d836fabce13023327 ]
Adaptive PEBS and arch-PEBS share lots of same code to process these PEBS groups, like basic, GPR and meminfo groups. Extract these shared code to generic functions to avoid duplicated code.
Stable-dep-of: 88aa1223bfff ("perf/x86/intel: Correct pt_regs->flags update for PEBS path")
--- arch/x86/events/intel/ds.c | 170 +++++++++++++++++++++++-------------- 1 file changed, 104 insertions(+), 66 deletions(-)
arch/x86/events/intel/ds.c +104 -66
@@ -1897,6 +1897,90 @@ static void adaptive_pebs_save_regs(struct pt_regs *regs,#define PEBS_LATENCY_MASK 0xffff +static inline void __setup_perf_sample_data (struct perf_event *event, +struct pt_regs *iregs, +struct perf_sample_data *data) +{ +perf_sample_data_init (data,0 , event->hw.last_period); + +/* +* We must however always use iregs for the unwinder to stay sane; the +* record BP,SP,IP can point into thin air when the record is from a +* previous PMI context or an (I)RET happened between the record and +* PMI. +*/ +perf_sample_save_callchain (data, event, iregs); +} + +static inline void __setup_pebs_basic_group (struct perf_event *event, +struct pt_regs *regs, +struct perf_sample_data *data, +u64 sample_type,u64 ip, +u64 tsc,u16 retire) +{ +/* The ip in basic is EventingIP */ +set_linear_ip (regs, ip); + regs->flags =PERF_EFLAGS_EXACT ; +setup_pebs_time (event, data, tsc); + +if (sample_type &PERF_SAMPLE_WEIGHT_STRUCT ) + data->weight.var3_w = retire; +} + +static inline void __setup_pebs_gpr_group (struct perf_event *event, +struct pt_regs *regs, +struct pebs_gprs *gprs, +u64 sample_type) +{ +if (event->attr.precise_ip <2 ) { +set_linear_ip (regs, gprs->ip); + regs->flags &= ~PERF_EFLAGS_EXACT ; + } + +if (sample_type & (PERF_SAMPLE_REGS_INTR |PERF_SAMPLE_REGS_USER )) +adaptive_pebs_save_regs (regs, gprs); +} + +static inline void __setup_pebs_meminfo_group (struct perf_event *event, +struct perf_sample_data *data, +u64 sample_type,u64 latency, +u16 instr_latency,u64 address, +u64 aux,u64 tsx_tuning,u64 ax) +{ +if (sample_type &PERF_SAMPLE_WEIGHT_TYPE ) { +u64 tsx_latency =intel_get_tsx_weight (tsx_tuning); + + data->weight.var2_w = instr_latency; + +/* +* Although meminfo::latency is defined as a u64, +* only the lower 32 bits include the valid data +* in practice on Ice Lake and earlier platforms. +*/ +if (sample_type &PERF_SAMPLE_WEIGHT ) + data->weight.full = latency ?: tsx_latency; +else + data->weight.var1_dw = (u32 )latency ?: tsx_latency; + + data->sample_flags |=PERF_SAMPLE_WEIGHT_TYPE ; + } + +if (sample_type &PERF_SAMPLE_DATA_SRC ) { + data->data_src.val =get_data_src (event, aux); + data->sample_flags |=PERF_SAMPLE_DATA_SRC ; + } + +if (sample_type &PERF_SAMPLE_ADDR_TYPE ) { + data->addr = address; + data->sample_flags |=PERF_SAMPLE_ADDR ; + } + +if (sample_type &PERF_SAMPLE_TRANSACTION ) { + data->txn =intel_get_tsx_transaction (tsx_tuning, ax); + data->sample_flags |=PERF_SAMPLE_TRANSACTION ; + } +} +/* * With adaptive PEBS the layout depends on what fields are configured. */
@@ -1907,12 +1991,14 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event, struct pt_regs *regs) {struct cpu_hw_events *cpuc =this_cpu_ptr (&cpu_hw_events); +u64 sample_type = event->attr.sample_type;struct pebs_basic *basic = __pebs;void *next_record = basic +1 ; -u64 sample_type, format_group;struct pebs_meminfo *meminfo =NULL ;struct pebs_gprs *gprs =NULL ;struct x86_perf_regs *perf_regs; +u64 format_group; +u16 retire;if (basic ==NULL )return ;
@@ -1920,31 +2006,17 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event, perf_regs = container_of(regs, struct x86_perf_regs, regs); perf_regs->xmm_regs =NULL ; - sample_type = event->attr.sample_type; format_group = basic->format_group; -perf_sample_data_init (data,0 , event->hw.last_period); -setup_pebs_time (event, data, basic->tsc); - -/* -* We must however always use iregs for the unwinder to stay sane; the -* record BP,SP,IP can point into thin air when the record is from a -* previous PMI context or an (I)RET happened between the record and -* PMI. -*/ -perf_sample_save_callchain (data, event, iregs); +__setup_perf_sample_data (event, iregs, data); *regs = *iregs; -/* The ip in basic is EventingIP */ -set_linear_ip (regs, basic->ip); - regs->flags =PERF_EFLAGS_EXACT ; -if (sample_type &PERF_SAMPLE_WEIGHT_STRUCT ) { -if (x86_pmu.flags &PMU_FL_RETIRE_LATENCY ) - data->weight.var3_w = basic->retire_latency; -else - data->weight.var3_w =0 ; - } +/* basic group */ + retire = x86_pmu.flags &PMU_FL_RETIRE_LATENCY ? + basic->retire_latency :0 ; +__setup_pebs_basic_group (event, regs, data, sample_type, + basic->ip, basic->tsc, retire);/* * The record for MEMINFO is in front of GP
@@ -1960,54 +2032,20 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event, gprs = next_record; next_record = gprs +1 ; -if (event->attr.precise_ip <2 ) { -set_linear_ip (regs, gprs->ip); - regs->flags &= ~PERF_EFLAGS_EXACT ; - } - -if (sample_type & (PERF_SAMPLE_REGS_INTR |PERF_SAMPLE_REGS_USER )) -adaptive_pebs_save_regs (regs, gprs); +__setup_pebs_gpr_group (event, regs, gprs, sample_type); }if (format_group &PEBS_DATACFG_MEMINFO ) { -if (sample_type &PERF_SAMPLE_WEIGHT_TYPE ) { -u64 latency = x86_pmu.flags &PMU_FL_INSTR_LATENCY ? - meminfo->cache_latency : meminfo->mem_latency; - -if (x86_pmu.flags &PMU_FL_INSTR_LATENCY ) - data->weight.var2_w = meminfo->instr_latency; - -/* -* Although meminfo::latency is defined as a u64, -* only the lower 32 bits include the valid data -* in practice on Ice Lake and earlier platforms. -*/ -if (sample_type &PERF_SAMPLE_WEIGHT ) { - data->weight.full = latency ?: -intel_get_tsx_weight (meminfo->tsx_tuning); - }else { - data->weight.var1_dw = (u32 )latency ?: -intel_get_tsx_weight (meminfo->tsx_tuning); - } - - data->sample_flags |=PERF_SAMPLE_WEIGHT_TYPE ; - } - -if (sample_type &PERF_SAMPLE_DATA_SRC ) { - data->data_src.val =get_data_src (event, meminfo->aux); - data->sample_flags |=PERF_SAMPLE_DATA_SRC ; - } - -if (sample_type &PERF_SAMPLE_ADDR_TYPE ) { - data->addr = meminfo->address; - data->sample_flags |=PERF_SAMPLE_ADDR ; - } - -if (sample_type &PERF_SAMPLE_TRANSACTION ) { - data->txn =intel_get_tsx_transaction (meminfo->tsx_tuning, - gprs ? gprs->ax :0 ); - data->sample_flags |=PERF_SAMPLE_TRANSACTION ; - } +u64 latency = x86_pmu.flags &PMU_FL_INSTR_LATENCY ? + meminfo->cache_latency : meminfo->mem_latency; +u64 instr_latency = x86_pmu.flags &PMU_FL_INSTR_LATENCY ? + meminfo->instr_latency :0 ; +u64 ax = gprs ? gprs->ax :0 ; + +__setup_pebs_meminfo_group (event, data, sample_type, latency, + instr_latency, meminfo->address, + meminfo->aux, meminfo->tsx_tuning, + ax); }if (format_group &PEBS_DATACFG_XMMS ) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 775/1102 perf/x86/intel: Correct pt_regs->flags update for PEBS path
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dapeng Mi <[email protected]>
[ Upstream commit 88aa1223bfffb1a0a98c639e9e1f71058f0d9178 ]
pt_regs->flags holds the saved CPU FLAGS register. In the PEBS path, it was incorrectly set to PERF_EFLAGS_EXACT instead of being populated from the PEBS flags snapshot.
Update pt_regs->flags from PEBS GPR flags if GPRs group is present.
--- arch/x86/events/intel/ds.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
arch/x86/events/intel/ds.c +9 -1
@@ -1920,7 +1920,7 @@ static inline void __setup_pebs_basic_group(struct perf_event *event, {/* The ip in basic is EventingIP */ set_linear_ip (regs, ip); - regs->flags =PERF_EFLAGS_EXACT ; + regs->flags |=PERF_EFLAGS_EXACT ;setup_pebs_time (event, data, tsc);if (sample_type &PERF_SAMPLE_WEIGHT_STRUCT )
@@ -1932,9 +1932,17 @@ static inline void __setup_pebs_gpr_group(struct perf_event *event, struct pebs_gprs *gprs, u64 sample_type) { +/* +* Update flags with PEBS data. PERF_EFLAGS_EXACT must be set +* in previous basic group handling. +*/ + regs->flags = gprs->flags |PERF_EFLAGS_EXACT ; +if (event->attr.precise_ip <2 ) {set_linear_ip (regs, gprs->ip); regs->flags &= ~PERF_EFLAGS_EXACT ; + }else if (regs->flags &X86_VM_MASK ) { + regs->flags ^= (PERF_EFLAGS_VM |X86_VM_MASK ); }if (sample_type & (PERF_SAMPLE_REGS_INTR |PERF_SAMPLE_REGS_USER ))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 776/1102 sched/fair: Fix EEVDF entity placement bug causing scheduling lag
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Peter Zijlstra <[email protected]>
[ Upstream commit 6d71a9c6160479899ee744d2c6d6602a191deb1f ]
I noticed this in my traces today:
turbostat-1222 [006] d..2. 311.935649: reweight_entity: (ffff888108f13e00-ffff88885ef38440-6)
{ weight: 1048576 avg_vruntime: 3184159639071 vruntime: 3184159640194 (-1123) deadline: 3184162621107 } ->
{ weight: 2 avg_vruntime: 3184177463330 vruntime: 3184748414495 (-570951165) deadline: 4747605329439 }
turbostat-1222 [006] d..2. 311.935651: reweight_entity: (ffff888108f13e00-ffff88885ef38440-6)
{ weight: 2 avg_vruntime: 3184177463330 vruntime: 3184748414495 (-570951165) deadline: 4747605329439 } ->
{ weight: 1048576 avg_vruntime: 3184176414812 vruntime: 3184177464419 (-1049607) deadline: 3184180445332 }Which is a weight transition: 1048576 -> 2 -> 1048576.
One would expect the lag to shoot out *AND* come back, notably:
-1123*1048576/2 = -588775424
-588775424*2/1048576 = -1123
Except the trace shows it is all off. Worse, subsequent cycles shoot it out further and further.
This made me have a very hard look at reweight_entity(), and specifically the ->on_rq case, which is more prominent with DELAY_DEQUEUE.
And indeed, it is all sorts of broken. While the computation of the new lag is correct, the computation for the new vruntime, using the new lag is broken for it does not consider the logic set out in place_entity().
With the below patch, I now see things like:
migration/12-55 [012] d..3. 309.006650: reweight_entity: (ffff8881e0e6f600-ffff88885f235f40-12)
{ weight: 977582 avg_vruntime: 4860513347366 vruntime: 4860513347908 (-542) deadline: 4860516552475 } ->
{ weight: 2 avg_vruntime: 4860528915984 vruntime: 4860793840706 (-264924722) deadline: 6427157349203 }
migration/14-62 [014] d..3. 309.006698: reweight_entity: (ffff8881e0e6cc00-ffff88885f3b5f40-15)
{ weight: 2 avg_vruntime: 4874472992283 vruntime: 4939833828823 (-65360836540) deadline: 6316614641111 } ->
{ weight: 967149 avg_vruntime: 4874217684324 vruntime: 4874217688559 (-4235) deadline: 4874220535650 }Which isn't perfect yet, but much closer.
Stable-dep-of: 51b0e68cfa0a ("sched/eevdf: Fix rb augmented with multi fields")
--- kernel/sched/fair.c | 145 ++++++-------------------------------------- 1 file changed, 18 insertions(+), 127 deletions(-)
kernel/sched/fair.c +18 -127
@@ -726,21 +726,16 @@ u64 avg_vruntime(struct cfs_rq *cfs_rq)* * XXX could add max_slice to the augmented data to track this. */ -static s64 entity_lag (u64 avruntime,struct sched_entity *se) +static void update_entity_lag (struct cfs_rq *cfs_rq,struct sched_entity *se) {s64 vlag, limit; - vlag = avruntime - se->vruntime; - limit =calc_delta_fair (max_t (u64,2 *se->slice,TICK_NSEC ), se); - -return clamp (vlag, -limit, limit); -} - -static void update_entity_lag (struct cfs_rq *cfs_rq,struct sched_entity *se) -{SCHED_WARN_ON (!se->on_rq); - se->vlag =entity_lag (avg_vruntime (cfs_rq), se); + vlag =avg_vruntime (cfs_rq) - se->vruntime; + limit =calc_delta_fair (max_t (u64,2 *se->slice,TICK_NSEC ), se); + + se->vlag =clamp (vlag, -limit, limit); }/*
@@ -3789,137 +3784,32 @@ static inline void dequeue_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) { }#endif -static void reweight_eevdf (struct sched_entity *se,u64 avruntime, -unsigned long weight) -{ -unsigned long old_weight = se->load.weight; -s64 vlag, vslice; - -/* -* VRUNTIME -* -------- -* -* COROLLARY #1: The virtual runtime of the entity needs to be -* adjusted if re-weight at !0-lag point. -* -* Proof: For contradiction assume this is not true, so we can -* re-weight without changing vruntime at !0-lag point. -* -* Weight VRuntime Avg-VRuntime -* before w v V -* after w' v' V' -* -* Since lag needs to be preserved through re-weight: -* -* lag = (V - v)*w = (V'- v')*w', where v = v' -* ==> V' = (V - v)*w/w' + v (1) -* -* Let W be the total weight of the entities before reweight, -* since V' is the new weighted average of entities: -* -* V' = (WV + w'v - wv) / (W + w' - w) (2) -* -* by using (1) & (2) we obtain: -* -* (WV + w'v - wv) / (W + w' - w) = (V - v)*w/w' + v -* ==> (WV-Wv+Wv+w'v-wv)/(W+w'-w) = (V - v)*w/w' + v -* ==> (WV - Wv)/(W + w' - w) + v = (V - v)*w/w' + v -* ==> (V - v)*W/(W + w' - w) = (V - v)*w/w' (3) -* -* Since we are doing at !0-lag point which means V != v, we -* can simplify (3): -* -* ==> W / (W + w' - w) = w / w' -* ==> Ww' = Ww + ww' - ww -* ==> W * (w' - w) = w * (w' - w) -* ==> W = w (re-weight indicates w' != w) -* -* So the cfs_rq contains only one entity, hence vruntime of -* the entity @v should always equal to the cfs_rq's weighted -* average vruntime @V, which means we will always re-weight -* at 0-lag point, thus breach assumption. Proof completed. -* -* -* COROLLARY #2: Re-weight does NOT affect weighted average -* vruntime of all the entities. -* -* Proof: According to corollary #1, Eq. (1) should be: -* -* (V - v)*w = (V' - v')*w' -* ==> v' = V' - (V - v)*w/w' (4) -* -* According to the weighted average formula, we have: -* -* V' = (WV - wv + w'v') / (W - w + w') -* = (WV - wv + w'(V' - (V - v)w/w')) / (W - w + w') -* = (WV - wv + w'V' - Vw + wv) / (W - w + w') -* = (WV + w'V' - Vw) / (W - w + w') -* -* ==> V'*(W - w + w') = WV + w'V' - Vw -* ==> V' * (W - w) = (W - w) * V (5) -* -* If the entity is the only one in the cfs_rq, then reweight -* always occurs at 0-lag point, so V won't change. Or else -* there are other entities, hence W != w, then Eq. (5) turns -* into V' = V. So V won't change in either case, proof done. -* -* -* So according to corollary #1 & #2, the effect of re-weight -* on vruntime should be: -* -* v' = V' - (V - v) * w / w' (4) -* = V - (V - v) * w / w' -* = V - vl * w / w' -* = V - vl' -*/ -if (avruntime != se->vruntime) { - vlag =entity_lag (avruntime, se); - vlag =div_s64 (vlag * old_weight, weight); - se->vruntime = avruntime - vlag; - } - -/* -* DEADLINE -* -------- -* -* When the weight changes, the virtual time slope changes and -* we should adjust the relative virtual deadline accordingly. -* -* d' = v' + (d - v)*w/w' -* = V' - (V - v)*w/w' + (d - v)*w/w' -* = V - (V - v)*w/w' + (d - v)*w/w' -* = V + (d - V)*w/w' -*/ - vslice = (s64)(se->deadline - avruntime); - vslice =div_s64 (vslice * old_weight, weight); - se->deadline = avruntime + vslice; -} +static void place_entity (struct cfs_rq *cfs_rq,struct sched_entity *se,int flags);static void reweight_entity (struct cfs_rq *cfs_rq,struct sched_entity *se,unsigned long weight) {bool curr = cfs_rq->curr == se; -u64 avruntime;if (se->on_rq) {/* commit outstanding execution time */ update_curr (cfs_rq); - avruntime =avg_vruntime (cfs_rq); +update_entity_lag (cfs_rq, se); + se->deadline -= se->vruntime; + se->rel_deadline =1 ;if (!curr)__dequeue_entity (cfs_rq, se);update_load_sub (&cfs_rq->load, se->load.weight); }dequeue_load_avg (cfs_rq, se); -if (se->on_rq) { -reweight_eevdf (se, avruntime, weight); - }else { -/* -* Because we keep se->vlag = V - v_i, while: lag_i = w_i*(V - v_i), -* we need to scale se->vlag when w_i changes. -*/ - se->vlag =div_s64 (se->vlag * se->load.weight, weight); - } +/* +* Because we keep se->vlag = V - v_i, while: lag_i = w_i*(V - v_i), +* we need to scale se->vlag when w_i changes. +*/ + se->vlag =div_s64 (se->vlag * se->load.weight, weight); +if (se->rel_deadline) + se->deadline =div_s64 (se->deadline * se->load.weight, weight);update_load_set (&se->load, weight);
@@ -3934,6 +3824,7 @@ static void reweight_entity(struct cfs_rq *cfs_rq, struct sched_entity *se,enqueue_load_avg (cfs_rq, se);if (se->on_rq) {update_load_add (&cfs_rq->load, se->load.weight); +place_entity (cfs_rq, se,0 );if (!curr)__enqueue_entity (cfs_rq, se); }
@@ -5375,7 +5266,7 @@ place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) se->vruntime = vruntime - lag; -if (sched_feat (PLACE_REL_DEADLINE ) && se->rel_deadline) { +if (se->rel_deadline) { se->deadline += se->vruntime; se->rel_deadline =0 ;return ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 777/1102 sched/fair: Fix lag clamp
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Peter Zijlstra <[email protected]>
[ Upstream commit 6e3c0a4e1ad1e0455b7880fad02b3ee179f56c09 ]
Vincent reported that he was seeing undue lag clamping in a mixed slice workload. Implement the max_slice tracking as per the todo comment.
Stable-dep-of: 51b0e68cfa0a ("sched/eevdf: Fix rb augmented with multi fields")
--- include/linux/sched.h | 1 + kernel/sched/fair.c | 39 +++++++++++++++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 4 deletions(-)
include/linux/sched.h +1 -0
@@ -545,6 +545,7 @@ struct sched_entity {u64 deadline;u64 min_vruntime;u64 min_slice; +u64 max_slice;struct list_head group_node;unsigned char on_rq;
kernel/sched/fair.c +35 -4
@@ -710,6 +710,8 @@ u64 avg_vruntime(struct cfs_rq *cfs_rq)return cfs_rq->zero_vruntime; } +static inline u64 cfs_rq_max_slice (struct cfs_rq *cfs_rq); +/* * lag_i = S - s_i = w_i * (V - v_i) *
@@ -723,17 +725,16 @@ u64 avg_vruntime(struct cfs_rq *cfs_rq)* EEVDF gives the following limit for a steady state system: * * -r_max < lag < max(r_max, q) -* -* XXX could add max_slice to the augmented data to track this. */ static void update_entity_lag (struct cfs_rq *cfs_rq,struct sched_entity *se) { +u64 max_slice =cfs_rq_max_slice (cfs_rq) +TICK_NSEC ;s64 vlag, limit;SCHED_WARN_ON (!se->on_rq); vlag =avg_vruntime (cfs_rq) - se->vruntime; - limit =calc_delta_fair (max_t (u64,2 *se->slice,TICK_NSEC ), se); + limit =calc_delta_fair (max_slice, se); se->vlag =clamp (vlag, -limit, limit); }
@@ -791,6 +792,21 @@ static inline u64 cfs_rq_min_slice(struct cfs_rq *cfs_rq)return min_slice; } +static inline u64 cfs_rq_max_slice (struct cfs_rq *cfs_rq) +{ +struct sched_entity *root =__pick_root_entity (cfs_rq); +struct sched_entity *curr = cfs_rq->curr; +u64 max_slice =0ULL ; + +if (curr && curr->on_rq) + max_slice = curr->slice; + +if (root) + max_slice =max (max_slice, root->max_slice); + +return max_slice; +} +static inline bool __entity_less (struct rb_node *a,const struct rb_node *b) {return entity_before (__node_2_se (a),__node_2_se (b));
@@ -816,6 +832,15 @@ static inline void __min_slice_update(struct sched_entity *se, struct rb_node *n } } +static inline void __max_slice_update (struct sched_entity *se,struct rb_node *node) +{ +if (node) { +struct sched_entity *rse =__node_2_se (node); +if (rse->max_slice > se->max_slice) + se->max_slice = rse->max_slice; + } +} +/* * se->min_vruntime = min(se->vruntime, {left,right}->min_vruntime) */
@@ -823,6 +848,7 @@ static inline bool min_vruntime_update(struct sched_entity *se, bool exit) {u64 old_min_vruntime = se->min_vruntime;u64 old_min_slice = se->min_slice; +u64 old_max_slice = se->max_slice;struct rb_node *node = &se->run_node; se->min_vruntime = se->vruntime;
@@ -833,8 +859,13 @@ static inline bool min_vruntime_update(struct sched_entity *se, bool exit)__min_slice_update (se, node->rb_right);__min_slice_update (se, node->rb_left); + se->max_slice = se->slice; +__max_slice_update (se, node->rb_right); +__max_slice_update (se, node->rb_left); +return se->min_vruntime == old_min_vruntime && - se->min_slice == old_min_slice; + se->min_slice == old_min_slice && + se->max_slice == old_max_slice; } RB_DECLARE_CALLBACKS(static, min_vruntime_cb, struct sched_entity,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 778/1102 sched/eevdf: Fix rb augmented with multi fields
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vincent Guittot <[email protected]>
[ Upstream commit 51b0e68cfa0ac69e3c3ea9d6753af7e15dfaab22 ]
The eevdf rb tree maintains 3 augmented fields but only one is currently copied when balancing the tree.
Add a more generic define that can be used when there are several augmented fields. In this case, we provide a function that takes care of copying all fields.
--- include/linux/rbtree_augmented.h | 35 +++++++++++++++++++++++++------- kernel/sched/fair.c | 12 +++++++++-- 2 files changed, 38 insertions(+), 9 deletions(-)
include/linux/rbtree_augmented.h +28 -7
@@ -87,18 +87,18 @@ rb_add_augmented_cached(struct rb_node *node, struct rb_root_cached *tree, }/* -* Template for declaring augmented rbtree callbacks (generic case) +* Template for declaring augmented rbtree callbacks (generic multi fields) * * RBSTATIC: 'static' or empty * RBNAME: name of the rb_augment_callbacks structure * RBSTRUCT: struct type of the tree nodes * RBFIELD: name of struct rb_node field within RBSTRUCT -* RBAUGMENTED: name of field within RBSTRUCT holding data for subtree -* RBCOMPUTE: name of function that recomputes the RBAUGMENTED data +* RBCOPY: name of function that copies the RBAUGMENTED datas +* RBCOMPUTE: name of function that recomputes the RBAUGMENTED datas */ -#define RB_DECLARE_CALLBACKS (RBSTATIC ,RBNAME , \ -RBSTRUCT ,RBFIELD ,RBAUGMENTED ,RBCOMPUTE ) \ +#define RB_DECLARE_CALLBACKS_MULTI (RBSTATIC ,RBNAME , \ +RBSTRUCT ,RBFIELD ,RBCOPY ,RBCOMPUTE ) \ static inline void \ RBNAME ## _propagate(struct rb_node *rb, struct rb_node *stop) \ { \
@@ -114,14 +114,14 @@ RBNAME ## _copy(struct rb_node *rb_old, struct rb_node *rb_new) \ { \RBSTRUCT *old =rb_entry (rb_old,RBSTRUCT ,RBFIELD ); \RBSTRUCT *new =rb_entry (rb_new,RBSTRUCT ,RBFIELD ); \ - new->RBAUGMENTED = old->RBAUGMENTED; \ +RBCOPY (new, old); \ } \static void \ RBNAME ## _rotate(struct rb_node *rb_old, struct rb_node *rb_new) \ { \RBSTRUCT *old =rb_entry (rb_old,RBSTRUCT ,RBFIELD ); \RBSTRUCT *new =rb_entry (rb_new,RBSTRUCT ,RBFIELD ); \ - new->RBAUGMENTED = old->RBAUGMENTED; \ +RBCOPY (new, old); \RBCOMPUTE (old, false); \ } \ RBSTATIC const struct rb_augment_callbacks RBNAME = { \
@@ -130,6 +130,27 @@ RBSTATIC const struct rb_augment_callbacks RBNAME = { \ .rotate = RBNAME ## _rotate \ }; +/* +* Template for declaring augmented rbtree callbacks (generic single field) +* +* RBSTATIC: 'static' or empty +* RBNAME: name of the rb_augment_callbacks structure +* RBSTRUCT: struct type of the tree nodes +* RBFIELD: name of struct rb_node field within RBSTRUCT +* RBAUGMENTED: name of field within RBSTRUCT holding data for subtree +* RBCOMPUTE: name of function that recomputes the RBAUGMENTED data +*/ + +#define RB_DECLARE_CALLBACKS (RBSTATIC ,RBNAME , \ +RBSTRUCT ,RBFIELD ,RBAUGMENTED ,RBCOMPUTE ) \ +static inline void \ +RBNAME ## _copy_single(RBSTRUCT *new, RBSTRUCT *old) \ +{ \ + new->RBAUGMENTED = old->RBAUGMENTED; \ +} \ +RB_DECLARE_CALLBACKS_MULTI(RBSTATIC, RBNAME, \ + RBSTRUCT, RBFIELD, RBNAME ## _copy_single, RBCOMPUTE) +/* * Template for declaring augmented rbtree callbacks, * computing RBAUGMENTED scalar as max(RBCOMPUTE(node)) for all subtree nodes.
kernel/sched/fair.c +10 -2
@@ -841,6 +841,13 @@ static inline void __max_slice_update(struct sched_entity *se, struct rb_node *n } } +static inline void min_vruntime_copy (struct sched_entity *new,struct sched_entity *old) +{ + new->min_vruntime = old->min_vruntime; + new->min_slice = old->min_slice; + new->max_slice = old->max_slice; +} +/* * se->min_vruntime = min(se->vruntime, {left,right}->min_vruntime) */
@@ -868,8 +875,9 @@ static inline bool min_vruntime_update(struct sched_entity *se, bool exit) se->max_slice == old_max_slice; } -RB_DECLARE_CALLBACKS(static, min_vruntime_cb, struct sched_entity, - run_node, min_vruntime, min_vruntime_update); + +RB_DECLARE_CALLBACKS_MULTI(static, min_vruntime_cb, struct sched_entity, + run_node, min_vruntime_copy, min_vruntime_update);/* * Enqueue an entity into the rb-tree:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 779/1102 net/sched: cls_route: free emptied bucket on filter move
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Victor Nogueira <[email protected]>
[ Upstream commit 1853f30cf5c84971f99788a76207c6f745380896 ]
route4_change can move an existing filter to a different top-level
bucket: route4_set_parms recomputes the handle from TCA_ROUTE4_TO/ FROM/IIF, and the handle-mismatch check is gated on the 'new' flag, so for an existing filter the new handle may differ from the old one and land in a different bucket. When this happens, the filter is unlinked from the old bucket, but the bucket itself is never freed once it goes empty. The stale empty bucket remains in head->table[], causing route4_delete to report *last=false even after the last live filter is gone. That pins the empty tcf_proto and causes a leak.
Fix this by refcounting the filters linked to a bucket and freeing the bucket when the count drops to zero. The existing scan in route4_delete goes away with it.
The count is updated at all sites that link or unlink a filter during add, change and delete, and the bucket is dropped from head->table[] as soon as it reaches zero.
Conditions to recreate the bug:
CONFIG_NET_CLS_ROUTE4=y, CONFIG_NET_SCH_INGRESS=y, CONFIG_NET_CLS_ACT=y.
tc qdisc replace dev lo clsact
tc filter add dev lo ingress protocol ip pref 100 route from 1 to 1 tc filter change dev lo ingress protocol ip pref 100 handle 0x10001 \
route from 1 to 2
tc filter del dev lo ingress protocol ip pref 100 handle 0x10002 \
route from 1 to 2
tc filter show dev lo ingress | grep -c 'pref 100 route chain 0 '
--- net/sched/cls_route.c | 45 +++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 23 deletions(-)
net/sched/cls_route.c +22 -23
@@ -11,6 +11,7 @@#include <linux/kernel.h> #include <linux/string.h> #include <linux/errno.h> +#include <linux/refcount.h> #include <linux/skbuff.h> #include <net/dst.h> #include <net/route.h>
@@ -41,6 +42,7 @@ struct route4_head {struct route4_bucket {/* 16 FROM buckets + 16 IIF buckets + 1 wildcard bucket */ struct route4_filter __rcu *ht[16 +16 +1 ]; +refcount_t filters_ref;struct rcu_head rcu; };
@@ -336,7 +338,7 @@ static int route4_delete(struct tcf_proto *tp, void *arg, bool *last,struct route4_filter *nf;struct route4_bucket *b;unsigned int h =0 ; -int i, h1; +int h1;if (!head || !f)return -EINVAL ;
@@ -362,23 +364,14 @@ static int route4_delete(struct tcf_proto *tp, void *arg, bool *last,tcf_exts_get_net (&f->exts);tcf_queue_work (&f->rwork, route4_delete_filter_work); -/* Strip RTNL protected tree */ -for (i =0 ; i <=32 ; i++) { -struct route4_filter *rt; - - rt =rtnl_dereference (b->ht[i]); -if (rt) - goto out; +if (refcount_dec_and_test (&b->filters_ref)) { +RCU_INIT_POINTER (head->table[to_hash (h)],NULL ); +kfree_rcu (b, rcu); } - -/* OK, session has no flows */ -RCU_INIT_POINTER (head->table[to_hash (h)],NULL ); -kfree_rcu (b, rcu);break ; } } -out: *last = true;for (h1 =0 ; h1 <=256 ; h1++) {if (rcu_access_pointer (head->table[h1])) {
@@ -459,6 +452,7 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp,if (b ==NULL )return -ENOBUFS ; +refcount_set (&b->filters_ref,1 );rcu_assign_pointer (head->table[h1], b); }else {unsigned int h2 =from_hash (nhandle >>16 );
@@ -468,6 +462,8 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp, fp = rtnl_dereference(fp->next))if (fp->handle == f->handle)return -EEXIST ; + +refcount_inc (&b->filters_ref); }if (tb[TCA_ROUTE4_TO ])
@@ -500,7 +496,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,struct route4_filter *fold, *f1, *pfp, *f =NULL ;struct route4_bucket *b;struct nlattr *tb[TCA_ROUTE4_MAX +1 ]; -unsigned int h, th; +unsigned int h;int err;bool new = true;
@@ -560,17 +556,20 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,rcu_assign_pointer (*fp, f);if (fold) { - th =to_hash (fold->handle); + b = fold->bkt; h =from_hash (fold->handle >>16 ); - b =rtnl_dereference (head->table[th]); -if (b) { - fp = &b->ht[h]; -for (pfp =rtnl_dereference (*fp); pfp; - fp = &pfp->next, pfp =rtnl_dereference (*fp)) { -if (pfp == fold) { -rcu_assign_pointer (*fp, fold->next); -break ; + fp = &b->ht[h]; +for (pfp =rtnl_dereference (*fp); pfp; + fp = &pfp->next, pfp =rtnl_dereference (*fp)) { +if (pfp == fold) { +rcu_assign_pointer (*fp, fold->next); +if (refcount_dec_and_test (&b->filters_ref)) { +unsigned int th =to_hash (fold->handle); + +RCU_INIT_POINTER (head->table[th],NULL ); +kfree_rcu (b, rcu); } +break ; } } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 780/1102 net/sched: cls_route: Reject handle aliasing
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Victor Nogueira <[email protected]>
[ Upstream commit b74a8455a2f271f54695b6a8ec1f113824a46c0e ]
route4_set_parms() rejects a duplicate by scanning the destination chain for f->handle, but f->handle is the handle the filter has before the update, not the one it is about to be linked under. The comparison and the insertion therefore use different handles, which causes breakage.
When a change moves the filter to a chain that already holds nhandle, the scan looks for the old handle instead, misses the collision and links a second filter with the same handle:
tc filter add dev lo ingress protocol ip pref 100 \
route from 1 to 1 classid 1:1 action ok
tc filter add dev lo ingress protocol ip pref 100 \
route from 2 to 2 classid 1:2 action drop
tc filter change dev lo ingress protocol ip pref 100 handle 0x10001 \
route from 2 to 2 classid 1:1 action ok
tc filter show dev lo ingress
... fh 0x00020002 flowid 1:2 to 2 from 2
... fh 0x00020002 flowid 1:1 to 2 from 2
The newcomer is appended after the incumbent, and both end up with the same f->id. route4_get() returns the first match, so the second filter can no longer be addressed by handle, and route4_classify() stops at the first filter whose f->id matches. The second filter is dumped but is effectively dead.
Fix this by comparing against nhandle.
--- net/sched/cls_route.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
net/sched/cls_route.c +5 -1
@@ -460,8 +460,12 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp,for (fp =rtnl_dereference (b->ht[h2]); fp; fp =rtnl_dereference (fp->next)) -if (fp->handle == f->handle) +if (fp->handle == nhandle) { +NL_SET_ERR_MSG_FMT (extack, +"Handle %x is already in use" , + nhandle);return -EEXIST ; + }refcount_inc (&b->filters_ref); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 781/1102 net/sched: cls_route: Fix in-place replace
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Victor Nogueira <[email protected]>
[ Upstream commit 41e85e54e5649a1617698438b0ce64c6f9d83d69 ]
Building on the previous patch, route4_set_parms rejects a duplicate by scanning the destination chain for nhandle, but the scan doesn't exclude the older version it is replacing, so an in-place replace will match the older version's handle and fail.
Fix this by passing the older filter as a parameter to route4_set_parms (replacing "new") and skipping it in the scan.
Excluding the older version is not enough on its own. nhandle is built out of TCA_ROUTE4_TO, TCA_ROUTE4_FROM and TCA_ROUTE4_IIF alone, while the 0x7F00 bits, which only tell apart filters sharing one key, are folded in on the create path. Letting the replace through would therefore rename the filter it replaces: replacing handle 0x10101 stored it back as 0x10001, and a sibling at 0x10201 could then no longer be replaced at all, since its own nhandle collided with the renamed filter.
tc filter add ... handle 0x10101 route from 1 to 1 classid 1:1 tc filter add ... handle 0x10201 route from 1 to 1 classid 1:2 tc filter replace ... handle 0x10101 route from 1 to 1 classid 1:9 ... fh 0x00010001 flowid 1:9 to 1 from 1 ... fh 0x00010201 flowid 1:2 to 1 from 1 tc filter replace ... handle 0x10201 route from 1 to 1 classid 1:8 Error: Handle 10001 is already in use.
So carry those bits over when the key the request builds is the key the older filter already has. An in-place replace then keeps the handle userspace named the filter by, while a request that does change the key still renames it, as it did before.
--- net/sched/cls_route.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-)
net/sched/cls_route.c +13 -14
@@ -393,8 +393,9 @@ static const struct nla_policy route4_policy[TCA_ROUTE4_MAX + 1] = {static int route4_set_parms (struct net *net,struct tcf_proto *tp,unsigned long base,struct route4_filter *f,u32 handle,struct route4_head *head, -struct nlattr **tb,struct nlattr *est,int new, -u32 flags,struct netlink_ext_ack *extack) +struct nlattr **tb,struct nlattr *est, +struct route4_filter *fold,u32 flags, +struct netlink_ext_ack *extack) {u32 id =0 , to =0 , nhandle =0x8000 ;struct route4_filter *fp;
@@ -407,7 +408,7 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp,return err;if (tb[TCA_ROUTE4_TO ]) { -if (new && handle &0x8000 ) { +if (!fold && handle &0x8000 ) {NL_SET_ERR_MSG (extack,"Invalid handle" );return -EINVAL ; }
@@ -430,14 +431,14 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp, }else nhandle |=0xFFFF <<16 ; -if (handle && new) { +if (handle && (!fold || nhandle == (handle & ~0x7F00 ))) nhandle |= handle &0x7F00 ; -if (nhandle != handle) { -NL_SET_ERR_MSG_FMT (extack, -"Handle mismatch constructed: %x (expected: %x)" , - handle, nhandle); -return -EINVAL ; - } + +if (handle && !fold && nhandle != handle) { +NL_SET_ERR_MSG_FMT (extack, +"Handle mismatch constructed: %x (expected: %x)" , + handle, nhandle); +return -EINVAL ; }if (!nhandle) {
@@ -460,7 +461,7 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp,for (fp =rtnl_dereference (b->ht[h2]); fp; fp =rtnl_dereference (fp->next)) -if (fp->handle == nhandle) { +if (fp != fold && fp->handle == nhandle) {NL_SET_ERR_MSG_FMT (extack,"Handle %x is already in use" , nhandle);
@@ -502,7 +503,6 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,struct nlattr *tb[TCA_ROUTE4_MAX +1 ];unsigned int h;int err; -bool new = true;if (!handle) {NL_SET_ERR_MSG (extack,"Creating with handle of 0 is invalid" );
@@ -539,11 +539,10 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, f->tp = fold->tp; f->bkt = fold->bkt; - new = false; } err =route4_set_parms (net, tp, base, f, handle, head, tb, - tca[TCA_RATE ], new, flags, extack); + tca[TCA_RATE ], fold, flags, extack);if (err <0 ) goto errout;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 782/1102 net/sched: cls_api: Dont replay RTM_GETCHAIN in tc_ctl_chain().
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit dff39930ad5e53d202bfdfb14687d1d2fd753b4d ]
If a netlink socket sends RTM_GETCHAIN requests repeatedly without recv()ing the responses, tc_ctl_chain() hogs CPU and triggers Hung Task splat. [0]
As caught in the stack trace, netlink_attachskb() could confuse tc_ctl_chain() by returning -EAGAIN when the userspace netlink socket's receive buffer is full.
The replay: label exists since commit 32a4f5ecd738 ("net: sched:
introduce chain object to uapi") but was not used initially.
Since commit 9f407f1768d3 ("net: sched: introduce chain templates"), the label is needed for RTM_NEWCHAIN because tcf_proto_lookup_ops() may release RTNL to call request_module().
However, the replay logic is unnecessary for RTM_GETCHAIN.
Let's apply the replay logic only for RTM_NEWCHAIN.
[0]:
INFO: task repro:1018 is blocked on a mutex likely owned by task repro:1022. task:repro state:R running task stack:14096 pid:1022 tgid:1014 ppid:961 task_flags:0x400040 flags:0x00080000 Call Trace: <TASK> ? clockevents_program_event (kernel/time/clockevents.c:372) ? pskb_expand_head (net/core/skbuff.c:615) ? skb_release_data (net/core/skbuff.c:1122) ? netlink_attachskb (./include/linux/skbuff.h:1323 ./include/linux/skbuff.h:1332 net/netlink/af_netlink.c:1232) ? __netlink_lookup (./include/linux/rcupdate.h:882 ./include/linux/rhashtable.h:711 net/netlink/af_netlink.c:499) ? tc_chain_notify (net/sched/cls_api.c:3045) ? tc_chain_notify (./include/linux/skbuff.h:1384 net/sched/cls_api.c:3041) ? netlink_unicast (net/netlink/af_netlink.c:1335) ? rtnl_unicast (./include/net/netlink.h:1198 net/core/rtnetlink.c:985) ? tc_ctl_chain (net/sched/cls_api.c:3242) ? rtnetlink_rcv_msg (net/core/rtnetlink.c:7146) ? netlink_unicast (net/netlink/af_netlink.c:1354) ? __pfx_rtnetlink_rcv_msg (net/core/rtnetlink.c:7177) ? netlink_rcv_skb (net/netlink/af_netlink.c:2556) ? netlink_unicast (net/netlink/af_netlink.c:1319) ? netlink_sendmsg (net/netlink/af_netlink.c:1900) ? __sock_sendmsg (net/socket.c:800) ? __sys_sendto (net/socket.c:2281) ? __x64_sys_sendto (net/socket.c:2288 net/socket.c:2284 net/socket.c:2284) ? do_syscall_64 (arch/x86/entry/syscall_64.c:61 arch/x86/entry/syscall_64.c:84) ? entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) </TASK>
--- net/sched/cls_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/sched/cls_api.c +1 -1
@@ -3243,7 +3243,7 @@ static int tc_ctl_chain(struct sk_buff *skb, struct nlmsghdr *n,tcf_chain_put (chain); errout_block:tcf_block_release (q, block, true); -if (err == -EAGAIN ) +if (err == -EAGAIN && n->nlmsg_type ==RTM_NEWCHAIN )/* Replay the request. */ goto replay;return err;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 783/1102 net: sun4i-emac: fix missing of_node_put() for phy_node
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Li Youhong <[email protected]>
[ Upstream commit af406abfecad2f48d8f1fc646d3994f0982bac62 ]
of_parse_phandle() returns a node pointer with an elevated refcount. Add the missing of_node_put() on the probe error path after register_netdev() fails and in emac_remove().
--- drivers/net/ethernet/allwinner/sun4i-emac.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/net/ethernet/allwinner/sun4i-emac.c +2 -0
@@ -1067,6 +1067,7 @@ static int emac_probe(struct platform_device *pdev)return 0 ; out_release_sram: +of_node_put (db->phy_node);sunxi_sram_release (&pdev->dev); out_clk_disable_unprepare:clk_disable_unprepare (db->clk);
@@ -1094,6 +1095,7 @@ static void emac_remove(struct platform_device *pdev) }unregister_netdev (ndev); +of_node_put (db->phy_node);sunxi_sram_release (&pdev->dev);clk_disable_unprepare (db->clk);irq_dispose_mapping (ndev->irq);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 784/1102 net: hinic: fix mailbox segment buffer overflow
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aamir Ahmed <[email protected]>
[ Upstream commit 5d4d985957434867bbe85e4fa5e638f3e48ad522 ]
check_mbox_seq_id_and_seg_len() validates that seq_id does not exceed SEQ_ID_MAX_VAL (42) and seg_len does not exceed MBOX_SEG_LEN (48). However, this allows the last segment (seq_id=42) to carry a full 48-byte payload, writing to offset 42*48=2016 for 48 bytes (ending at byte 2064). The receive buffer is only MBOX_MAX_BUF_SZ (2048) bytes, resulting in a 16-byte heap buffer overflow.
The hinic3 driver already handles this correctly by defining MBOX_LAST_SEG_MAX_LEN and rejecting the last segment when it exceeds the remaining buffer space. Apply the same fix to the hinic driver.
--- drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c +3 -1
@@ -128,6 +128,7 @@ enum hinic_mbox_tx_status {#define SEQ_ID_START_VAL 0#define SEQ_ID_MAX_VAL 42 +#define MBOX_LAST_SEG_MAX_LEN (MBOX_MAX_BUF_SZ - SEQ_ID_MAX_VAL * MBOX_SEG_LEN)#define NO_DMA_ATTRIBUTE_VAL 0
@@ -372,7 +373,8 @@ recv_pf_from_vf_mbox_handler(struct hinic_mbox_func_to_func *func_to_func,static bool check_mbox_seq_id_and_seg_len (struct hinic_recv_mbox *recv_mbox,u8 seq_id,u8 seg_len) { -if (seq_id >SEQ_ID_MAX_VAL || seg_len >MBOX_SEG_LEN ) +if (seq_id >SEQ_ID_MAX_VAL || seg_len >MBOX_SEG_LEN || + (seq_id ==SEQ_ID_MAX_VAL && seg_len >MBOX_LAST_SEG_MAX_LEN ))return false;if (seq_id ==0 ) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 785/1102 cxgb4: clip_tbl: Fix spelling mistake "wont" -> "wont"
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Colin Ian King <[email protected]>
[ Upstream commit c824deb1a89755f70156b5cdaf569fca80698719 ]
There are spelling mistakes in dev_err and dev_info messages. Fix them.
Stable-dep-of: ccbe7540e4aa ("net: phy: mediatek-ge: disable EEE on the MT7530 PHY")
--- drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c +2 -2
@@ -120,7 +120,7 @@ int cxgb4_clip_get(const struct net_device *dev, const u32 *lip, u8 v6)write_unlock_bh (&ctbl->lock);dev_err (adap->pdev_dev,"CLIP FW cmd failed with error %d, " -"Connections using %pI6c wont be " +"Connections using %pI6c won't be " "offloaded" , ret, ce->addr6.sin6_addr.s6_addr);return ret;
@@ -133,7 +133,7 @@ int cxgb4_clip_get(const struct net_device *dev, const u32 *lip, u8 v6) }else {write_unlock_bh (&ctbl->lock);dev_info (adap->pdev_dev,"CLIP table overflow, " -"Connections using %pI6c wont be offloaded" , +"Connections using %pI6c won't be offloaded" , (void *)lip);return -ENOMEM ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 786/1102 net: phy: mediatek: Re-organize MediaTek ethernet phy drivers
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: SkyLake.Huang <[email protected]>
[ Upstream commit 4c452f7ea86212934ae896842847d1671e13a18b ]
Re-organize MediaTek ethernet phy driver files and get ready to integrate some common functions and add new 2.5G phy driver.mtk-ge.c: MT7530 Gphy on MT7621 & MT7531 Gphymtk-ge-soc.c: Built-in Gphy on MT7981 & Built-in switch Gphy on MT7988 mtk-2p5ge.c: Planned for built-in 2.5G phy on MT7988
Stable-dep-of: ccbe7540e4aa ("net: phy: mediatek-ge: disable EEE on the MT7530 PHY")
---
MAINTAINERS | 4 ++--
drivers/net/phy/Kconfig | 17 +-------------
drivers/net/phy/Makefile | 3 +--
drivers/net/phy/mediatek/Kconfig | 22 +++++++++++++++++++
drivers/net/phy/mediatek/Makefile | 3 +++
.../mtk-ge-soc.c} | 0
.../phy/{mediatek-ge.c => mediatek/mtk-ge.c} | 0
7 files changed, 29 insertions(+), 20 deletions(-)
create mode 100644 drivers/net/phy/mediatek/Kconfig
create mode 100644 drivers/net/phy/mediatek/Makefile
rename drivers/net/phy/{mediatek-ge-soc.c => mediatek/mtk-ge-soc.c} (100%)
rename drivers/net/phy/{mediatek-ge.c => mediatek/mtk-ge.c} (100%)MAINTAINERS +2 -2
@@ -14433,8 +14433,8 @@ M: Qingfang Deng <[email protected]> M: SkyLake Huang <[email protected]> L: [email protected] S: Maintained -F: drivers/net/phy/mediatek-ge-soc.c -F: drivers/net/phy/mediatek-ge.c +F: drivers/net/phy/mediatek/mtk-ge-soc.c +F: drivers/net/phy/mediatek/mtk-ge.c F: drivers/phy/mediatek/phy-mtk-xfi-tphy.c MEDIATEK I2C CONTROLLER DRIVER
drivers/net/phy/Kconfig +1 -16
@@ -266,22 +266,7 @@ config MAXLINEAR_GPHY Support for the Maxlinear GPY115, GPY211, GPY212, GPY215, GPY241, GPY245 PHYs. -config MEDIATEK_GE_PHY - tristate "MediaTek Gigabit Ethernet PHYs" - help - Supports the MediaTek Gigabit Ethernet PHYs. - -config MEDIATEK_GE_SOC_PHY - tristate "MediaTek SoC Ethernet PHYs" - depends on (ARM64 && ARCH_MEDIATEK) || COMPILE_TEST - depends on NVMEM_MTK_EFUSE - help - Supports MediaTek SoC built-in Gigabit Ethernet PHYs. - - Include support for built-in Ethernet PHYs which are present in - the MT7981 and MT7988 SoCs. These PHYs need calibration data - present in the SoCs efuse and will dynamically calibrate VCM - (common-mode voltage) during startup. +source "drivers/net/phy/mediatek/Kconfig" config MICREL_PHY tristate "Micrel PHYs"
drivers/net/phy/Makefile +1 -2
@@ -74,8 +74,7 @@ obj-$(CONFIG_MARVELL_PHY) += marvell.o obj-$(CONFIG_MARVELL_88Q2XXX_PHY) += marvell-88q2xxx.o obj-$(CONFIG_MARVELL_88X2222_PHY) += marvell-88x2222.o obj-$(CONFIG_MAXLINEAR_GPHY) += mxl-gpy.o -obj-$(CONFIG_MEDIATEK_GE_PHY) += mediatek-ge.o -obj-$(CONFIG_MEDIATEK_GE_SOC_PHY) += mediatek-ge-soc.o +obj-y += mediatek/ obj-$(CONFIG_MESON_GXL_PHY) += meson-gxl.o obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o obj-$(CONFIG_MICREL_PHY) += micrel.o
drivers/net/phy/mediatek/Kconfig new file +22 -0
@@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0-only +config MEDIATEK_GE_PHY + tristate "MediaTek Gigabit Ethernet PHYs" + help + Supports the MediaTek non-built-in Gigabit Ethernet PHYs. + + Non-built-in Gigabit Ethernet PHYs include mt7530/mt7531. + You may find mt7530 inside mt7621. This driver shares some + common operations with MediaTek SoC built-in Gigabit + Ethernet PHYs. + +config MEDIATEK_GE_SOC_PHY + tristate "MediaTek SoC Ethernet PHYs" + depends on (ARM64 && ARCH_MEDIATEK) || COMPILE_TEST + depends on NVMEM_MTK_EFUSE + help + Supports MediaTek SoC built-in Gigabit Ethernet PHYs. + + Include support for built-in Ethernet PHYs which are present in + the MT7981 and MT7988 SoCs. These PHYs need calibration data + present in the SoCs efuse and will dynamically calibrate VCM + (common-mode voltage) during startup.
drivers/net/phy/mediatek/Makefile new file +3 -0
@@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_MEDIATEK_GE_PHY) += mtk-ge.o +obj-$(CONFIG_MEDIATEK_GE_SOC_PHY) += mtk-ge-soc.o
drivers/net/phy/mediatek/mtk-ge-soc.c renamed from drivers/net/phy/mediatek-ge-soc.c +0 -0
drivers/net/phy/mediatek/mtk-ge.c renamed from drivers/net/phy/mediatek-ge.c +0 -0
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 787/1102 net: phy: add phy_disable_eee
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Heiner Kallweit <[email protected]>
[ Upstream commit b55498ff14bd14860d48dc8d2a0b6889b218c408 ]
If a MAC driver doesn't support EEE, then the PHY shouldn't advertise it. Add phy_disable_eee() for this purpose.
Stable-dep-of: ccbe7540e4aa ("net: phy: mediatek-ge: disable EEE on the MT7530 PHY")
--- drivers/net/phy/phy_device.c | 16 ++++++++++++++++ include/linux/phy.h | 1 + 2 files changed, 17 insertions(+)
drivers/net/phy/phy_device.c +16 -0
@@ -3043,6 +3043,22 @@ void phy_support_eee(struct phy_device *phydev) }EXPORT_SYMBOL (phy_support_eee); +/** +* phy_disable_eee - Disable EEE for the PHY +* @phydev: Target phy_device struct +* +* This function is used by MAC drivers for MAC's which don't support EEE. +* It disables EEE on the PHY layer. +*/ +void phy_disable_eee (struct phy_device *phydev) +{ +linkmode_zero (phydev->supported_eee); +linkmode_zero (phydev->advertising_eee); + phydev->eee_cfg.tx_lpi_enabled = false; + phydev->eee_cfg.eee_enabled = false; +} +EXPORT_SYMBOL_GPL (phy_disable_eee); +/** * phy_support_sym_pause - Enable support of symmetrical pause * @phydev: target phy_device struct
include/linux/phy.h +1 -0
@@ -2040,6 +2040,7 @@ void phy_advertise_eee_all(struct phy_device *phydev);void phy_support_sym_pause (struct phy_device *phydev);void phy_support_asym_pause (struct phy_device *phydev);void phy_support_eee (struct phy_device *phydev); +void phy_disable_eee (struct phy_device *phydev);void phy_set_sym_pause (struct phy_device *phydev,bool rx,bool tx,bool autoneg);void phy_set_asym_pause (struct phy_device *phydev,bool rx,bool tx);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 788/1102 net: phy: mediatek-ge: disable EEE on the MT7530 PHY
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vladislav Karmanov <[email protected]>
[ Upstream commit ccbe7540e4aad0d1c3acc249697350b93ccb8025 ]
The MT7530 internal GE PHY advertises EEE by hardware default, but its EEE support is defective: with EEE advertised, some link partners fail to establish a stable link. On a 2-pair (4-wire) cable where both ends advertise gigabit, 1000BASE-T training cannot succeed, and instead of falling back to 100 Mbps the port loops, so no link or DHCP lease is ever obtained. MediaTek confirms the hardware is the root cause (Landen Chao, 2021): "EEE of the 10-year-old MT7530 internal gephy has many IOT problems, so it is recommended to disable its EEE."
mtk_gephy_config_init() used to clear the EEE advertisement early, but commit af3b4b0e59de ("net: phy: mediatek-ge: do not disable EEE advertisement") removed that on the rationale that the DSA subdriver already performs an early disable. That holds for MT7531, whose mt7531_setup() clears MDIO_AN_EEE_ADV on each switch PHY, but not for the MT7530 PHY: neither the MT7621 integrated switch nor the dedicated MT7530 IC ever had such a loop, so removing it left those boards without any working early EEE disable and the link flapping came back.
Since the broken hardware is the PHY, fix it in the PHY driver so it covers all users of this PHY, integrated in a switch or standalone:
- clear MDIO_AN_EEE_ADV in probe(), as early as possible, before
anything can negotiate EEE with the link partner;
- clear it again in config_init() and call phy_disable_eee() there. config_init() is what phy_init_hw() replays after a PHY reset, when the register is back at its EEE-advertising hardware default, and it runs after of_set_phy_eee_broken() in phy_probe(), so the eee_disabled_modes mask survives and neither phylib nor userspace can re-enable EEE. dp83867 disables broken EEE from config_init() the same way.
Auto-negotiation then falls back to a stable 100 Mbps link instead of looping at gigabit. Tested on ASUS RT-AX53U (MT7621): with a 2-pair cable on the WAN port, a single clean 100 Mbps link comes up and a DHCP lease is obtained, where the unpatched driver loops.
--- drivers/net/phy/mediatek/mtk-ge.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
drivers/net/phy/mediatek/mtk-ge.c +29 -0
@@ -40,10 +40,38 @@ static void mtk_gephy_config_init(struct phy_device *phydev)phy_write_mmd (phydev,MDIO_MMD_VEND1 ,0xa6 ,0x300 ); } +static int mt7530_phy_probe (struct phy_device *phydev) +{ +/* The MT7530 internal GE PHY has broken EEE: with EEE advertised, +* some link partners fail to establish a stable link (on a 2-pair +* cable, 1000BASE-T training fails and the port loops instead of +* falling back). MediaTek recommends disabling EEE on this PHY. +* Clear the advertisement as early as possible, before anything +* can negotiate EEE with the link partner. +*/ +return phy_write_mmd (phydev,MDIO_MMD_AN ,MDIO_AN_EEE_ADV ,0 ); +} +static int mt7530_phy_config_init (struct phy_device *phydev) { +int ret; +mtk_gephy_config_init (phydev); +/* The probe() clear alone is not durable: phy_init_hw() replays only +* ->config_init after a PHY reset, with the register back at its +* EEE-advertising hardware default, and phy_probe() zeroes +* eee_disabled_modes (of_set_phy_eee_broken()) after ->probe already +* ran. Clear the advertisement again and mark EEE disabled, so that +* neither phylib nor userspace can re-enable it; dp83867 disables +* broken EEE from config_init() the same way. +*/ + ret =phy_write_mmd (phydev,MDIO_MMD_AN ,MDIO_AN_EEE_ADV ,0 ); +if (ret) +return ret; + +phy_disable_eee (phydev); +/* Increase post_update_timer */ phy_write_paged (phydev,MTK_PHY_PAGE_EXTENDED_3 ,0x11 ,0x4b );
@@ -69,6 +97,7 @@ static struct phy_driver mtk_gephy_driver[] = { { PHY_ID_MATCH_EXACT(0x03a29412), .name = "MediaTek MT7530 PHY", + .probe = mt7530_phy_probe, .config_init = mt7530_phy_config_init,/* Interrupts are handled by the switch, not the PHY * itself.
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 789/1102 octeontx2-af: fix PF/CGX debugfs PCI bus lookup
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ratheesh Kannoth <[email protected]>
[ Upstream commit 4f4b743c2d2bbc336cb164d9d3d2ed6956ad8437 ]
rvu_dbg_rvu_pf_cgx_map_display() locates each RVU PF PCI device via pci_get_domain_bus_and_slot() when printing the PF-to-CGX map. It assumed PF0 always sits on PCI bus 1 and derived other PF bus numbers as pf + 1, but the AF device can be enumerated on a different bus.
Use rvu->pdev->bus->number as the base bus instead, so each PF lookup uses pf + start on systems where RVU functions are on contiguous buses but do not start at bus 1.
--- .../net/ethernet/marvell/octeontx2/af/rvu_debugfs.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c +8 -2
@@ -852,19 +852,25 @@ static int rvu_dbg_rvu_pf_cgx_map_display(struct seq_file *filp, void *unused)int pf, domain, blkid;u8 cgx_id, lmac_id;u16 pcifunc; +u8 start; - domain =2 ; + domain =pci_domain_nr (rvu->pdev->bus); mac_ops =get_mac_ops (rvu_first_cgx_pdata (rvu));/* There can be no CGX devices at all */ if (!mac_ops)return 0 ;seq_printf (filp,"PCI dev\t\tRVU PF Func\tNIX block\t%s\tLMAC\tCHAN\n" , mac_ops->name); + +/* All the PF devices are on contiguous PCI bus numbers, but the PF0(AF) +* may not start from 1 always. Hence get domain and bus from PCI device. +*/ + start = rvu->pdev->bus->number;for (pf =0 ; pf < rvu->hw->total_pfs; pf++) {if (!is_pf_cgxmapped (rvu, pf))continue ; - pdev =pci_get_domain_bus_and_slot (domain, pf +1 ,0 ); + pdev =pci_get_domain_bus_and_slot (domain, pf + start,0 );if (!pdev)continue ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 790/1102 net/rds: fix tcp stream corruption with large pages
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Greg Marsden <[email protected]>
[ Upstream commit 2ac09b5353fe6858411fdc8c6efa60d832e20f13 ]
rds_message_map_pages() assigns PAGE_SIZE bytes to every scatterlist entry, even when total_len ends in a partial page. The RDS congestion map is defined as 8192 bytes, so on systems with PAGE_SIZE greater than 8192 the scatterlist maps bytes beyond the end of the congestion map. RDS-TCP transmits the SG contents according to those lengths, so the extra bytes become part of the TCP RDS stream and are interpreted as subsequent RDS message headers, corrupting the stream.
Limit the final scatterlist mapping to the number of bytes remaining. This has no effect on systems with a 4K page size and allows RDS-TCP to be used on systems with 16K and larger page sizes.
The RDS selftest, which previously hung on 16K pages, now passes.
--- net/rds/message.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
net/rds/message.c +3 -1
@@ -365,7 +365,9 @@ struct rds_message *rds_message_map_pages(unsigned long *page_addrs, unsigned infor (i =0 ; i < rm->data.op_nents; ++i) {sg_set_page (&rm->data.op_sg[i],virt_to_page ((void *)page_addrs[i]), -PAGE_SIZE ,0 ); + i == rm->data.op_nents -1 + ? total_len - (i *PAGE_SIZE ) + :PAGE_SIZE ,0 ); }return rm;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 791/1102 net: stmmac: TSO: Simplify the code flow of DMA descriptor allocations
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Furong Xu <[email protected]>
[ Upstream commit 3569399994384f7e409a560910613edc2ad4a779 ]
The TCP Segmentation Offload (TSO) engine is an optional function in DWMAC cores, it is implemented for dwmac4 and dwxgmac2 only, ancient dwmac100 and dwmac1000 are not supported by hardware. Current driver code checks priv->dma_cap.tsoen which is read from MAC_HW_Feature1 register to determine if TSO is enabled in hardware configurations, if (!priv->dma_cap.tsoen) driver never sets NETIF_F_TSO for net_device.
This patch never affects dwmac100/dwmac1000 and their stmmac_desc_ops:
ndesc_ops/enh_desc_ops, since TSO is never supported by them two.
The DMA AXI address width of DWMAC cores can be configured to 32-bit/40-bit/48-bit, then the format of DMA transmit descriptors get a little different between 32-bit and 40-bit/48-bit. Current driver code checks priv->dma_cap.addr64 to use certain format with certain configuration.
This patch converts the format of DMA transmit descriptors on dwmac4 and dwxgmac2 that the DMA AXI address width is configured to 32-bit (as described by function comments of stmmac_tso_xmit() in current code) to a more generic format (see updated function comments after this patch) which is actually already used on 40-bit/48-bit platforms to provide better compatibility and make code flow cleaner in TSO TX routine.
Another interesting finding, struct stmmac_desc_ops is a common abstract interface to maintain descriptors, we should avoid the direct assignment of descriptor members (e.g. desc->des0), stmmac_set_desc_addr() is the proper method yet. This patch tries to improve this by the way.
Tested and verified on:
DWMAC CORE 5.00a with 32-bit DMA AXI address width DWMAC CORE 5.10a with 32-bit DMA AXI address width DWXGMAC CORE 3.20a with 40-bit DMA AXI address width
Stable-dep-of: 5e38d732ec67 ("net: stmmac: fix TX descriptor availability check for TSO traffic")
--- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 60 ++++++++----------- 1 file changed, 24 insertions(+), 36 deletions(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +24 -36
@@ -4108,11 +4108,7 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, dma_addr_t des, desc = &tx_q->dma_tx[tx_q->cur_tx]; curr_addr = des + (total_len - tmp_len); -if (priv->dma_cap.addr64 <=32 ) - desc->des0 =cpu_to_le32 (curr_addr); -else -stmmac_set_desc_addr (priv, desc, curr_addr); - +stmmac_set_desc_addr (priv, desc, curr_addr); buff_size = tmp_len >=TSO_MAX_BUFF_SIZE ?TSO_MAX_BUFF_SIZE : tmp_len;
@@ -4158,17 +4154,27 @@ static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue)* First Descriptor * -------- * | DES0 |---> buffer1 = L2/L3/L4 header -* | DES1 |---> TCP Payload (can continue on next descr...) -* | DES2 |---> buffer 1 and 2 len +* | DES1 |---> can be used as buffer2 for TCP Payload if the DMA AXI address +* | | width is 32-bit, but we never use it. +* | | Also can be used as the most-significant 8-bits or 16-bits of +* | | buffer1 address pointer if the DMA AXI address width is 40-bit +* | | or 48-bit, and we always use it. +* | DES2 |---> buffer1 len * | DES3 |---> must set TSE, TCP hdr len-> [22:19]. TCP payload len [17:0] * -------- +* -------- +* | DES0 |---> buffer1 = TCP Payload (can continue on next descr...) +* | DES1 |---> same as the First Descriptor +* | DES2 |---> buffer1 len +* | DES3 | +* -------- * | * ... * | * -------- -* | DES0 | --| Split TCP Payload on Buffers 1 and 2 -* | DES1 | --| -* | DES2 | --> buffer 1 and 2 len +* | DES0 |---> buffer1 = Split TCP Payload +* | DES1 |---> same as the First Descriptor +* | DES2 |---> buffer1 len * | DES3 | * -------- *
@@ -4178,15 +4184,14 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) {struct dma_desc *desc, *first, *mss_desc =NULL ;struct stmmac_priv *priv =netdev_priv (dev); -int tmp_pay_len =0 , first_tx, nfrags;unsigned int first_entry, tx_packets;struct stmmac_txq_stats *txq_stats;struct stmmac_tx_queue *tx_q;u32 pay_len, mss, queue; -dma_addr_t tso_des, des; +int i, first_tx, nfrags;u8 proto_hdr_len, hdr; +dma_addr_t des;bool set_ic; -int i;/* Always insert VLAN tag to SKB payload for TSO frames. *
@@ -4271,24 +4276,9 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)if (dma_mapping_error (priv->device, des)) goto dma_map_err; -if (priv->dma_cap.addr64 <=32 ) { - first->des0 =cpu_to_le32 (des); - -/* Fill start of payload in buff2 of first descriptor */ -if (pay_len) - first->des1 =cpu_to_le32 (des + proto_hdr_len); - -/* If needed take extra descriptors to fill the remaining payload */ - tmp_pay_len = pay_len -TSO_MAX_BUFF_SIZE ; - tso_des = des; - }else { -stmmac_set_desc_addr (priv, first, des); - tmp_pay_len = pay_len; - tso_des = des + proto_hdr_len; - pay_len =0 ; - } - -stmmac_tso_allocator (priv, tso_des, tmp_pay_len, (nfrags ==0 ), queue); +stmmac_set_desc_addr (priv, first, des); +stmmac_tso_allocator (priv, des + proto_hdr_len, pay_len, + (nfrags ==0 ), queue);/* In case two or more DMA transmit descriptors are allocated for this * non-paged SKB data, the DMA buffer address should be saved to
@@ -4390,11 +4380,9 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) }/* Complete the first descriptor before granting the DMA */ -stmmac_prepare_tso_tx_desc (priv, first,1 , - proto_hdr_len, - pay_len, -1 , tx_q->tx_skbuff_dma[first_entry].last_segment, - hdr /4 , (skb->len - proto_hdr_len)); +stmmac_prepare_tso_tx_desc (priv, first,1 , proto_hdr_len,0 ,1 , + tx_q->tx_skbuff_dma[first_entry].last_segment, + hdr /4 , (skb->len - proto_hdr_len));/* If context desc is used to change MSS */ if (mss_desc) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 792/1102 net: stmmac: fix TSO support when some channels have TBS available
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Russell King (Oracle) <[email protected]>
[ Upstream commit e32820264c2949a3e75bc5083e49c885e3bbaf62 ]
According to the STM32MP25xx manual, which is dwmac v5.3, TBS (time based scheduling) is not permitted for channels which have hardware TSO enabled. Intel's commit 5e6038b88a57 ("net: stmmac: fix TSO and TBS feature enabling during driver open") concurs with this, but it is incomplete.
This commit avoids enabling TSO support on the channels which have TBS available, which, as far as the hardware is concerned, means we do not set the TSE bit in the DMA channel's transmit control register.
However, the net device's features apply to all queues(channels), which means these channels may still be handed TSO skbs to transmit, and the driver will pass them to stmmac_tso_xmit(). This will generate the descriptors for TSO, even though the channel has the TSE bit clear.
Fix this by checking whether the queue(channel) has TBS available, and if it does, fall back to software GSO support.
Stable-dep-of: 5e38d732ec67 ("net: stmmac: fix TX descriptor availability check for TSO traffic")
--- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +28 -4
@@ -3367,6 +3367,17 @@ static void stmmac_safety_feat_configuration(struct stmmac_priv *priv) } } +/* STM32MP25xx (dwmac v5.3) states "Do not enable time-based scheduling for +* channels on which the TSO feature is enabled." If we have a skb for a +* channel which has TBS enabled, fall back to software GSO. +*/ +static bool stmmac_tso_channel_permitted (struct stmmac_priv *priv, +unsigned int chan) +{ +/* TSO and TBS cannot co-exist */ +return !(priv->dma_conf.tx_queue[chan].tbs &STMMAC_TBS_AVAIL ); +} +/** * stmmac_hw_setup - setup mac in a usable state. * @dev : pointer to the device structure.
@@ -3492,10 +3503,7 @@ static int stmmac_hw_setup(struct net_device *dev, bool ptp_register)/* Enable TSO */ if (priv->tso) {for (chan =0 ; chan < tx_cnt; chan++) { -struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; - -/* TSO and TBS cannot co-exist */ -if (tx_q->tbs &STMMAC_TBS_AVAIL ) +if (!stmmac_tso_channel_permitted (priv, chan))continue ;stmmac_enable_tso (priv, priv->ioaddr,1 , chan);
@@ -4708,6 +4716,21 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)return NETDEV_TX_OK ; } +static netdev_features_t stmmac_features_check (struct sk_buff *skb, +struct net_device *dev, +netdev_features_t features) +{ +u16 queue; + +if (skb_is_gso (skb)) { + queue =skb_get_queue_mapping (skb); +if (!stmmac_tso_channel_permitted (netdev_priv (dev), queue)) + features &= ~NETIF_F_GSO_MASK ; + } + +return vlan_features_check (skb, features); +} +static void stmmac_rx_vlan (struct net_device *dev,struct sk_buff *skb) {struct vlan_ethhdr *veth =skb_vlan_eth_hdr (skb);
@@ -7086,6 +7109,7 @@ static void stmmac_get_stats64(struct net_device *dev, struct rtnl_link_stats64static const struct net_device_ops stmmac_netdev_ops = { .ndo_open = stmmac_open, .ndo_start_xmit = stmmac_xmit, + .ndo_features_check = stmmac_features_check, .ndo_stop = stmmac_release, .ndo_change_mtu = stmmac_change_mtu, .ndo_fix_features = stmmac_fix_features,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 793/1102 net: stmmac: add stmmac_tso_header_size()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Russell King (Oracle) <[email protected]>
[ Upstream commit f799b5dab9c98a4b52a93d1c357916e0f0b7e663 ]
We will need to compute the size of the protocol headers in two places, so move this into a separate function.
Stable-dep-of: 5e38d732ec67 ("net: stmmac: fix TX descriptor availability check for TSO traffic")
--- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +15 -5
@@ -4151,6 +4151,18 @@ static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue)stmmac_set_tx_tail_ptr (priv, priv->ioaddr, tx_q->tx_tail_addr, queue); } +static size_t stmmac_tso_header_size (struct sk_buff *skb) +{ +size_t size; + +if (skb_shinfo (skb)->gso_type &SKB_GSO_UDP_L4 ) + size =skb_transport_offset (skb) +sizeof (struct udphdr ); +else + size =skb_tcp_all_headers (skb); + +return size; +} +/** * stmmac_tso_xmit - Tx entry point of the driver for oversized frames (TSO) * @skb : the socket buffer
@@ -4222,13 +4234,11 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) first_tx = tx_q->cur_tx;/* Compute header lengths */ -if (skb_shinfo (skb)->gso_type &SKB_GSO_UDP_L4 ) { - proto_hdr_len =skb_transport_offset (skb) +sizeof (struct udphdr ); + proto_hdr_len =stmmac_tso_header_size (skb); +if (skb_shinfo (skb)->gso_type &SKB_GSO_UDP_L4 ) hdr =sizeof (struct udphdr ); - }else { - proto_hdr_len =skb_tcp_all_headers (skb); +else hdr =tcp_hdrlen (skb); - }/* Desc availability based on threshold should be enough safe */ if (unlikely(stmmac_tx_avail(priv, queue) <
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 794/1102 net: stmmac: add TSO check for header length
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Russell King (Oracle) <[email protected]>
[ Upstream commit 6732e474f880468435a79d05811c8c814c5d71f8 ]
According to the STM32MP151 documentation which covers dwmac v4.2, the hardware TSO feature can handle header lengths up to a maximum of 1023 bytes.
Add a .ndo_features_check() method implementation to check the header length meets these requirements, otherwise fall back to software GSO.
Stable-dep-of: 5e38d732ec67 ("net: stmmac: fix TX descriptor availability check for TSO traffic")
--- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +16 -1
@@ -4163,6 +4163,20 @@ static size_t stmmac_tso_header_size(struct sk_buff *skb)return size; } +/* STM32MP151 (dwmac v4.2) and STM32MP25xx (dwmac v5.3) states for TDES2 normal +* (read format) descriptor that the maximum header length supported for the +* TSO feature is 1023 bytes. +* +* While IPv4 is limited to MAC+VLAN+IPv4+ext+TCP+ext = 138 bytes, the IPv6 +* extension headers aren't similarly limited. +*/ +static bool stmmac_tso_valid_packet (struct sk_buff *skb) +{ +size_t header_len =stmmac_tso_header_size (skb); + +return header_len <=1023 ; +} +/** * stmmac_tso_xmit - Tx entry point of the driver for oversized frames (TSO) * @skb : the socket buffer
@@ -4734,7 +4748,8 @@ static netdev_features_t stmmac_features_check(struct sk_buff *skb,if (skb_is_gso (skb)) { queue =skb_get_queue_mapping (skb); -if (!stmmac_tso_channel_permitted (netdev_priv (dev), queue)) +if (!stmmac_tso_channel_permitted (netdev_priv (dev), queue) || + !stmmac_tso_valid_packet (skb)) features &= ~NETIF_F_GSO_MASK ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 795/1102 net: stmmac: fix TX descriptor availability check for TSO traffic
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit 5e38d732ec67a5b1f9a56e6c73add480c4b6030a ]
stmmac_tso_xmit() estimates the number of free TX descriptors required by a TSO skb as:
(skb->len - proto_hdr_len) / TSO_MAX_BUFF_SIZE + 1
which assumes the payload is split into TSO_MAX_BUFF_SIZE chunks. This underestimates the descriptors actually consumed by stmmac_tso_allocator(), since each fragment is mapped individually and so it needs at least one descriptor regardless of its size. Moreover, one descriptor is used for the L2/L3/L4 headers and, when the MSS changes, one more is consumed for the MSS context descriptor.
For a highly fragmented TSO skb the check can therefore pass even when the ring has too few free slots. stmmac_tso_allocator() then writes past the available descriptors, overwriting descriptors still owned by the DMA engine, corrupting the TX ring.
Add stmmac_tso_get_num_desc() to compute the exact number of descriptors needed for the header, the linear payload and each fragment, plus the MSS context descriptor when required, and use it in the availability check.
--- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +25 -6
@@ -4177,6 +4177,26 @@ static bool stmmac_tso_valid_packet(struct sk_buff *skb)return header_len <=1023 ; } +static int stmmac_tso_get_num_desc (struct stmmac_tx_queue *tx_q, +struct sk_buff *skb,u32 pay_len) +{ +int i, ndesc =1 ; + +/* head payload */ + ndesc +=DIV_ROUND_UP (pay_len,TSO_MAX_BUFF_SIZE ); +/* frag payload */ +for (i =0 ; i <skb_shinfo (skb)->nr_frags; i++) { +const skb_frag_t *frag = &skb_shinfo (skb)->frags[i]; + + ndesc +=DIV_ROUND_UP (skb_frag_size (frag), +TSO_MAX_BUFF_SIZE ); + } +/* MSS update requires a new descriptor */ + ndesc += !!(skb_shinfo (skb)->gso_size != tx_q->mss); + +return ndesc; +} +/** * stmmac_tso_xmit - Tx entry point of the driver for oversized frames (TSO) * @skb : the socket buffer
@@ -4220,9 +4240,9 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)struct stmmac_priv *priv =netdev_priv (dev);unsigned int first_entry, tx_packets;struct stmmac_txq_stats *txq_stats; +int i, first_tx, nfrags, ndesc;struct stmmac_tx_queue *tx_q;u32 pay_len, mss, queue; -int i, first_tx, nfrags;u8 proto_hdr_len, hdr;dma_addr_t des;bool set_ic;
@@ -4249,14 +4269,15 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)/* Compute header lengths */ proto_hdr_len =stmmac_tso_header_size (skb); + pay_len =skb_headlen (skb) - proto_hdr_len;/* no frags */ +if (skb_shinfo (skb)->gso_type &SKB_GSO_UDP_L4 ) hdr =sizeof (struct udphdr );else hdr =tcp_hdrlen (skb); -/* Desc availability based on threshold should be enough safe */ -if (unlikely (stmmac_tx_avail (priv, queue) < - (((skb->len - proto_hdr_len) /TSO_MAX_BUFF_SIZE +1 )))) { + ndesc =stmmac_tso_get_num_desc (tx_q, skb, pay_len); +if (unlikely (stmmac_tx_avail (priv, queue) < ndesc)) {if (!netif_tx_queue_stopped (netdev_get_tx_queue (dev, queue))) {netif_tx_stop_queue (netdev_get_tx_queue (priv->dev, queue));
@@ -4268,8 +4289,6 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)return NETDEV_TX_BUSY ; } - pay_len =skb_headlen (skb) - proto_hdr_len;/* no frags */ - mss =skb_shinfo (skb)->gso_size;/* set new MSS value if needed */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 796/1102 net: hsr: enable promiscuous mode on interlink port with fwd offload
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: MD Danish Anwar <[email protected]>
[ Upstream commit a2dc179481d18f6df7274522571b64dd50f31e81 ]
hsr_portdev_setup() skips promiscuous mode on non-master ports when hsr->fwd_offloaded is set. fwd_offloaded is derived only from the ring slaves' NETIF_F_HW_HSR_FWD bit, so this also skips it for the interlink port, which never gets forwarding offload. Without promiscuous mode, the interlink NIC drops unicast frames addressed to hsr_dev's MAC (e.g. SAN traffic to the RedBox), breaking RedBox whenever the ring is HW-offloaded.
--- net/hsr/hsr_slave.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
net/hsr/hsr_slave.c +7 -4
@@ -148,9 +148,12 @@ static int hsr_portdev_setup(struct hsr_priv *hsr, struct net_device *dev,int res;/* Don't use promiscuous mode for offload since L2 frame forward -* happens at the offloaded hardware. +* happens at the offloaded hardware. The interlink port never +* gets forwarding offload (RedBox forwarding to/from it is done +* by this driver), so it still needs promiscuous mode to receive +* frames addressed to hsr_dev's MAC rather than its own. */ -if (!port->hsr->fwd_offloaded) { +if (!port->hsr->fwd_offloaded || port->type ==HSR_PT_INTERLINK ) { res =dev_set_promiscuity (dev,1 );if (res)return res;
@@ -173,7 +176,7 @@ static int hsr_portdev_setup(struct hsr_priv *hsr, struct net_device *dev, fail_rx_handler:netdev_upper_dev_unlink (dev, hsr_dev); fail_upper_dev_link: -if (!port->hsr->fwd_offloaded) +if (!port->hsr->fwd_offloaded || port->type ==HSR_PT_INTERLINK )dev_set_promiscuity (dev,-1 );return res;
@@ -236,7 +239,7 @@ void hsr_del_port(struct hsr_port *port)netdev_update_features (master->dev);dev_set_mtu (master->dev,hsr_get_max_mtu (hsr));netdev_rx_handler_unregister (port->dev); -if (!port->hsr->fwd_offloaded) +if (!port->hsr->fwd_offloaded || port->type ==HSR_PT_INTERLINK )dev_set_promiscuity (port->dev,-1 );if (port->type ==HSR_PT_SLAVE_A || port->type ==HSR_PT_SLAVE_B )vlan_vids_del_by_dev (port->dev, master->dev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 797/1102 openvswitch: fix wrong flag value in get_ipv6_ext_hdrs()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eelco Chaudron <[email protected]>
[ Upstream commit e184a4a6f423550a25adce867036cdb1ff471745 ]
The ESP and AH cases in get_ipv6_ext_hdrs() used IPPROTO_FRAGMENT instead of OFPIEH12_FRAG when checking for out-of-order extension headers, causing the fragment header to not be recognised as a valid predecessor.
The original code used IPPROTO_FRAGMENT (44) as a bitmask constant where OFPIEH12_FRAG (1 << 4 = 16) was intended. IPPROTO_FRAGMENT encodes bits 2, 3 and 5 (OFPIEH12_AUTH | OFPIEH12_DEST | OFPIEH12_ROUTER), but not bit 4 (OFPIEH12_FRAG). This caused incorrect OFPIEH12_UNSEQ verdicts in both the ESP and AH arms: the ESP arm failed to whitelist OFPIEH12_FRAG, while the AH arm accidentally whitelisted OFPIEH12_AUTH.
With the fix, a packet with two AH headers now also gets OFPIEH12_UNSEQ in addition to OFPIEH12_UNREP, matching the ESP arm which already sets UNSEQ on a repeat, which is the intended behavior.
--- net/openvswitch/flow.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/openvswitch/flow.c +2 -2
@@ -292,7 +292,7 @@ static void get_ipv6_ext_hdrs(struct sk_buff *skb, struct ipv6hdr *nh,if (*ext_hdrs &OFPIEH12_ESP ) *ext_hdrs |=OFPIEH12_UNREP ;if ((*ext_hdrs & ~(OFPIEH12_HOP |OFPIEH12_DEST | -OFPIEH12_ROUTER |IPPROTO_FRAGMENT | +OFPIEH12_ROUTER |OFPIEH12_FRAG |OFPIEH12_AUTH |OFPIEH12_UNREP )) || dest_options_header_count >=2 ) { *ext_hdrs |=OFPIEH12_UNSEQ ;
@@ -305,7 +305,7 @@ static void get_ipv6_ext_hdrs(struct sk_buff *skb, struct ipv6hdr *nh, *ext_hdrs |=OFPIEH12_UNREP ;if ((*ext_hdrs & ~(OFPIEH12_HOP |OFPIEH12_DEST |OFPIEH12_ROUTER | -IPPROTO_FRAGMENT |OFPIEH12_UNREP )) || +OFPIEH12_FRAG |OFPIEH12_UNREP )) || dest_options_header_count >=2 ) { *ext_hdrs |=OFPIEH12_UNSEQ ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 798/1102 sunvdc: unmap LDC cookies when the descriptor send fails
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Stian Halseth <[email protected]>
[ Upstream commit 0c6da21fa35e03fc74f09895433ccd6d4a9c3530 ]
__send_request() maps the request's pages into the LDC channel's map table (ldc_map_sg()), fills in the descriptor and marks it VIO_DESC_READY before ringing the doorbell via __vdc_tx_trigger(). When the trigger fails, the error path only prints a message: the descriptor stays READY and the cookies are never unmapped. The mapping is normally released in vdc_end_one() when the peer completes the descriptor - but a descriptor whose doorbell was never sent will never complete, and since dr->prod is not advanced on failure, the reset path (vdc_requeue_inflight(), which walks [cons, prod)) never visits it either. The map table entries are leaked permanently.
Since commit a11f6ca9aef9 ("sunvdc: Do not spin in an infinite loop when vio_ldc_send() returns EAGAIN") trigger failures occur in practice under load, so every resulting I/O error also leaks one request's worth of entries from the fixed-size (8192 entries per channel) map table. Because the allocator hands out contiguous ranges, fragmentation makes large multi-segment requests fail first as the table drains, until ldc_map_sg() fails permanently and the disk is dead until reboot.
It also makes any retry-based recovery unusable: requeuing the request on -EAGAIN remaps the pages on every attempt, overwriting desc->cookies and orphaning the previous mapping, so the table drains at the retry rate. This is the memory exhaustion observed when the requeue approach was first tested in October 2025.
Roll back on failure: unmap the cookies, mark the descriptor FREE again and clear the request entry. If the trigger failed with -ENOTCONN, __vdc_tx_trigger() has already reset the port, which tears down and reallocates both the dring and the LDC channel including its map table - nothing to roll back, and the stale descriptor must not be touched.
--- drivers/block/sunvdc.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
drivers/block/sunvdc.c +17 -0
@@ -526,6 +526,23 @@ static int __send_request(struct request *req) err =__vdc_tx_trigger (port);if (err <0 ) { printk(KERN_ERR PFX "vdc_tx_trigger() failure, err=%d\n", err); +/* +* If the port was reset (-ENOTCONN), the dring and the +* LDC channel including all of its mappings are already +* torn down and reallocated - there is nothing to undo +* and @desc must not be touched. +* +* For any other failure the descriptor was never handed +* to the peer: unmap the cookies and free the descriptor +* again, so that a later retry of the request does not +* leak LDC map table entries. +*/ +if (err != -ENOTCONN ) { +ldc_unmap (port->vio.lp, desc->cookies, + desc->ncookies); + desc->hdr.state =VIO_DESC_FREE ; + rqe->req =NULL ; + } }else { port->req_id++; dr->prod =vio_dring_next (dr, dr->prod);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 799/1102 scripts/mksysmap: fix escape of $ in the __pi_ pattern
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Stoakes (ARM) <[email protected]>
[ Upstream commit 59351365ac271b5e0eb180f211c531476a36221f ]
Commit b18b047002b7 ("kbuild: change scripts/mksysmap into sed script") converted scripts/mksysmap from a shell script to a sed script.
However an error was made - escaping of '$' required \\ escaping in shell but only \ in a sed script.
This was mostly corrected in commit 7a6c355b55c0 ("scripts/mksysmap: Fix escape chars '$'"), but this fix missed arm64 PIE namespace local symbols like __pi_$x and __pi_$d which appear in System.map and /proc/kallsyms:
$ grep __pi_\\$ /proc/kallsyms | sort -u 0000000000000000 d __pi_$d 0000000000000000 t __pi_$x
Fix the escaping properly.
--- scripts/mksysmap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
scripts/mksysmap +1 -1
@@ -35,7 +35,7 @@ / __efistub_/d # arm64 local symbols in PIE namespace -/ __pi_\\$/d +/ __pi_\$/d / __pi_\.L/d # arm64 local symbols in non-VHE KVM namespace
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 800/1102 scsi: target: iscsi: Fix hang for aborted WRITE_PENDING commands
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maurizio Lombardi <[email protected]>
[ Upstream commit d5869dae5080e976d4b03cc33eb7ceb527f242bf ]
When a LUN_RESET aborts a WRITE command that is in the TRANSPORT_WRITE_PENDING state, the target core sets CMD_T_ABORTED and waits for the frontend to finish processing.
If the initiator subsequently sends the remaining dataout PDUs, __iscsit_check_dataout_hdr() catches the payload, stops the dataout timer if the sequence is final and finally dumps the data. However, the iSCSI target doesn't trigger the completion process for these aborted commands. Because of this, the abort path hangs indefinitely in target_put_cmd_and_wait(), leading to a deadlocked target worker thread.
Fix this by explicitly calling target_complete_cmd() when the final dataout PDU is received for an aborted WRITE command. target_complete_cmd() detects the CMD_T_ABORTED flag and cleanly routes the command into target_abort_work, allowing the abort completion to successfully unblock.
--- drivers/target/iscsi/iscsi_target.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/target/iscsi/iscsi_target.c +3 -1
@@ -1561,8 +1561,10 @@ __iscsit_check_dataout_hdr(struct iscsit_conn *conn, void *buf,*/ if (se_cmd->transport_state &CMD_T_ABORTED ) {if (hdr->flags &ISCSI_FLAG_CMD_FINAL && - --cmd->outstanding_r2ts <1 ) + --cmd->outstanding_r2ts <1 ) {iscsit_stop_dataout_timer (cmd); +target_complete_cmd (se_cmd,SAM_STAT_TASK_ABORTED ); + }return iscsit_dump_data_payload (conn, payload_length,1 ); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 801/1102 ksmbd: prevent out-of-bounds reads in share config responses
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
[ Upstream commit f25e93768fcc5d8287e50b1ec52a42e4c276df34 ]
Validate IPC share configuration payload sizes before consuming variable-length fields. Bound veto list parsing and account for the separator byte when deriving the path length.
--- fs/smb/server/mgmt/share_config.c | 38 +++++++++++++++++++++++-------- fs/smb/server/transport_ipc.c | 21 +++++++++++------ 2 files changed, 42 insertions(+), 17 deletions(-)
fs/smb/server/mgmt/share_config.c +28 -10
@@ -88,9 +88,9 @@ static struct ksmbd_share_config *__share_lookup(const char *name)static int parse_veto_list (struct ksmbd_share_config *share,char *veto_list, -int veto_list_sz) +size_t veto_list_sz) { -int sz =0 ; +size_t sz;if (!veto_list_sz)return 0 ;
@@ -98,7 +98,7 @@ static int parse_veto_list(struct ksmbd_share_config *share,while (veto_list_sz >0 ) {struct ksmbd_veto_pattern *p; - sz =strlen (veto_list); + sz =strnlen (veto_list, veto_list_sz);if (!sz)break ;
@@ -106,7 +106,7 @@ static int parse_veto_list(struct ksmbd_share_config *share,if (!p)return -ENOMEM ; - p->pattern =kstrdup (veto_list,KSMBD_DEFAULT_GFP ); + p->pattern =kstrndup (veto_list, sz,KSMBD_DEFAULT_GFP );if (!p->pattern) {kfree (p);return -ENOMEM ;
@@ -114,6 +114,9 @@ static int parse_veto_list(struct ksmbd_share_config *share,list_add (&p->list, &share->veto_list); +if (sz == veto_list_sz) +break ; + veto_list += sz +1 ; veto_list_sz -= (sz +1 ); }
@@ -165,13 +168,27 @@ static struct ksmbd_share_config *share_config_request(struct ksmbd_work *work, }if (!test_share_config_flag (share,KSMBD_SHARE_FLAG_PIPE )) { -int path_len =PATH_MAX ; +size_t path_len; -if (resp->payload_sz) +if (resp->payload_sz <= resp->veto_list_sz) { + ret = -EINVAL ; + }else { path_len = resp->payload_sz - resp->veto_list_sz; - - share->path =kstrndup (ksmbd_share_config_path (resp), path_len, -KSMBD_DEFAULT_GFP ); +if (resp->veto_list_sz) + path_len--; + +if (!path_len) { + ret = -EINVAL ; + }else { + share->path =kstrndup ( +ksmbd_share_config_path (resp), + path_len,KSMBD_DEFAULT_GFP ); +if (!share->path) + ret = -ENOMEM ; +else + ret =0 ; + } + }if (share->path) { share->path_sz =strlen (share->path); while (share->path_sz > 1 &&
@@ -184,7 +201,8 @@ static struct ksmbd_share_config *share_config_request(struct ksmbd_work *work, share->force_directory_mode = resp->force_directory_mode; share->force_uid = resp->force_uid; share->force_gid = resp->force_gid; - ret =parse_veto_list (share, +if (!ret) + ret =parse_veto_list (share,KSMBD_SHARE_CONFIG_VETO_LIST (resp), resp->veto_list_sz);if (!ret && share->path) {
fs/smb/server/transport_ipc.c +14 -7
@@ -524,14 +524,21 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry)if (entry->msg_sz <sizeof (struct ksmbd_share_config_response ))return -EINVAL ; -if (resp->payload_sz) { -if (resp->payload_sz < resp->veto_list_sz) -return -EINVAL ; +if (strnlen (resp->share_name,sizeof (resp->share_name)) == +sizeof (resp->share_name)) +return -EINVAL ; -if (check_add_overflow (sizeof (struct ksmbd_share_config_response ), - resp->payload_sz, &msg_sz)) -return -EINVAL ; - } +if (resp->veto_list_sz > resp->payload_sz) +return -EINVAL ; + +if (resp->flags !=KSMBD_SHARE_FLAG_INVALID && + !(resp->flags &KSMBD_SHARE_FLAG_PIPE ) && + resp->payload_sz <= resp->veto_list_sz) +return -EINVAL ; + +if (check_add_overflow (sizeof (struct ksmbd_share_config_response ), + resp->payload_sz, &msg_sz)) +return -EINVAL ;break ; }case KSMBD_EVENT_LOGIN_REQUEST_EXT :
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 802/1102 powerpc/ps3: Fix repository.c build failure
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thorsten Blum <[email protected]>
commit 5ba79d37403d86082ab4083b0f51ec3008a942cb upstream.
GCC fails to build ps3_defconfig with the following errors:
arch/powerpc/platforms/ps3/repository.c: In function ‘make_first_field.constprop’:
arch/powerpc/platforms/ps3/repository.c:78:9: error: ‘strnlen’ specified bound 8 exceeds source size 3 [-Werror=stringop-overread]
78 | memcpy((char *)&n, text, strnlen(text, sizeof(n)));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
arch/powerpc/platforms/ps3/repository.c: In function ‘make_first_field.constprop’:
arch/powerpc/platforms/ps3/repository.c:78:9: error: ‘strnlen’ specified bound 8 exceeds source size 4 [-Werror=stringop-overread]
78 | memcpy((char *)&n, text, strnlen(text, sizeof(n)));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~The current use of strnlen(text, sizeof(n)) triggers -Wstringop-overread when text is a short string literal that is smaller than sizeof(n), such as "bi" or "bus". Use strlen(text) instead and clamp the copy length to sizeof(n) before memcpy().
Drop the redundant char * cast while at it.
--- arch/powerpc/platforms/ps3/repository.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
arch/powerpc/platforms/ps3/repository.c +4 -1
@@ -6,6 +6,8 @@* Copyright 2006 Sony Corp. */ +#include <linux/minmax.h> +#include <asm/lv1call.h> #include "platform.h"
@@ -74,8 +76,9 @@ static void _dump_node(unsigned int lparstatic u64 make_first_field (const char *text,u64 index) {u64 n =0 ; +size_t len =min (strlen (text),sizeof (n)); -memcpy ((char *)&n, text,strnlen (text,sizeof (n))); +memcpy (&n, text, len);return PS3_VENDOR_ID_NONE + (n >>32 ) + index; }
Greg Kroah-Hartman
maintainerPATCH 803/1102 powerpc/eeh: Fix recursive locking on devices without EEH sensitive driver
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shivaprasad G Bhat <[email protected]>
commit c5e68706527968282e49de205cc2b935823cb88a upstream.
The commit 1010b4c012b0 ("powerpc/eeh: Make EEH driver device hotplug safe") refactored the EEH code such that the pci_rescan_remove_lock is held at the beginning of eeh_handle_normal_event() and the eeh_reset_device() is called with that lock being held. Looks like the commit missed to remove the existing lock/unlock inside eeh_rmv_device() which is no longer necessary. This is causing the eehd to hang on the lock which it actually holds when that code path is taken.
[<0>] 0xc00000011c78f870
[<0>] __switch_to+0xfc/0x1a0 [<0>] pci_lock_rescan_remove+0x30/0x44 [<0>] eeh_rmv_device+0x290/0x2e0 [<0>] eeh_pe_dev_traverse+0x80/0x130 [<0>] eeh_reset_device+0xcc/0x23c [<0>] eeh_handle_normal_event+0x830/0xa80 [<0>] eeh_event_handler+0xf8/0x190 [<0>] kthread+0x194/0x1b0 [<0>] start_kernel_thread+0x14/0x18
The issue is seen for cases where the errors are detected on the PHB directly AND|OR for devices where the driver error_detected() returns PCI_ERS_RESULT_NEED_RESET, and driver being not EEH sensitive(i.e no error handlers like slot_reset(), resume() etc defined).
--- arch/powerpc/kernel/eeh_driver.c | 2 -- 1 file changed, 2 deletions(-)
arch/powerpc/kernel/eeh_driver.c +0 -2
@@ -533,9 +533,7 @@ static void eeh_rmv_device(struct eeh_deif (rmv_data)list_add (&edev->rmv_entry, &rmv_data->removed_vf_list); }else { -pci_lock_rescan_remove ();pci_stop_and_remove_bus_device (dev); -pci_unlock_rescan_remove (); } }
Greg Kroah-Hartman
maintainerPATCH 804/1102 crypto: x86/aria - add missing vzeroupper in AVX2 code
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Biggers <[email protected]>
commit ac53977611428db3bc0b4ac0225e19c3e08ae50b upstream.
Since the AVX2 optimized ARIA code uses YMM registers, execute vzeroupper before returning from it. This is needed to avoid degrading the performance of any later SSE code that may happen to be executed.
--- arch/x86/crypto/aria-aesni-avx2-asm_64.S | 6 ++++++ 1 file changed, 6 insertions(+)
arch/x86/crypto/aria-aesni-avx2-asm_64.S +6 -0
@@ -988,6 +988,7 @@ SYM_TYPED_FUNC_START(aria_aesni_avx2_enc %ymm8, %ymm9, %ymm10, %ymm11, %ymm12, %ymm13, %ymm14, %ymm15, %rax); + vzeroupper; FRAME_END RET; SYM_FUNC_END(aria_aesni_avx2_encrypt_32way)
@@ -1013,6 +1014,7 @@ SYM_TYPED_FUNC_START(aria_aesni_avx2_dec %ymm8, %ymm9, %ymm10, %ymm11, %ymm12, %ymm13, %ymm14, %ymm15, %rax); + vzeroupper; FRAME_END RET; SYM_FUNC_END(aria_aesni_avx2_decrypt_32way)
@@ -1215,6 +1217,7 @@ SYM_TYPED_FUNC_START(aria_aesni_avx2_ctr %ymm8, %ymm9, %ymm10, %ymm11, %ymm12, %ymm13, %ymm14, %ymm15, %r10); + vzeroupper; FRAME_END RET; SYM_FUNC_END(aria_aesni_avx2_ctr_crypt_32way)
@@ -1366,6 +1369,7 @@ SYM_TYPED_FUNC_START(aria_aesni_avx2_gfn %ymm8, %ymm9, %ymm10, %ymm11, %ymm12, %ymm13, %ymm14, %ymm15, %rax); + vzeroupper; FRAME_END RET; SYM_FUNC_END(aria_aesni_avx2_gfni_encrypt_32way)
@@ -1391,6 +1395,7 @@ SYM_TYPED_FUNC_START(aria_aesni_avx2_gfn %ymm8, %ymm9, %ymm10, %ymm11, %ymm12, %ymm13, %ymm14, %ymm15, %rax); + vzeroupper; FRAME_END RET; SYM_FUNC_END(aria_aesni_avx2_gfni_decrypt_32way)
@@ -1435,6 +1440,7 @@ SYM_TYPED_FUNC_START(aria_aesni_avx2_gfn %ymm8, %ymm9, %ymm10, %ymm11, %ymm12, %ymm13, %ymm14, %ymm15, %r10); + vzeroupper; FRAME_END RET; SYM_FUNC_END(aria_aesni_avx2_gfni_ctr_crypt_32way)
Greg Kroah-Hartman
maintainerPATCH 805/1102 crypto: x86/aria - add missing vzeroupper in AVX-512 code
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Biggers <[email protected]>
commit 60892a384aa1e65d0e703e1c513417bdf0c80777 upstream.
Since the AVX-512 optimized ARIA code uses ZMM registers, execute vzeroupper before returning from it. This is needed to avoid degrading the performance of any later SSE code that may happen to be executed.
--- arch/x86/crypto/aria-gfni-avx512-asm_64.S | 3 +++ 1 file changed, 3 insertions(+)
arch/x86/crypto/aria-gfni-avx512-asm_64.S +3 -0
@@ -800,6 +800,7 @@ SYM_TYPED_FUNC_START(aria_gfni_avx512_en %zmm9, %zmm8, %zmm11, %zmm10, %zmm12, %zmm13, %zmm14, %zmm15, %rax); + vzeroupper; FRAME_END RET; SYM_FUNC_END(aria_gfni_avx512_encrypt_64way)
@@ -825,6 +826,7 @@ SYM_TYPED_FUNC_START(aria_gfni_avx512_de %zmm9, %zmm8, %zmm11, %zmm10, %zmm12, %zmm13, %zmm14, %zmm15, %rax); + vzeroupper; FRAME_END RET; SYM_FUNC_END(aria_gfni_avx512_decrypt_64way)
@@ -966,6 +968,7 @@ SYM_TYPED_FUNC_START(aria_gfni_avx512_ct %zmm9, %zmm8, %zmm11, %zmm10, %zmm12, %zmm13, %zmm14, %zmm15, %r10); + vzeroupper; FRAME_END RET; SYM_FUNC_END(aria_gfni_avx512_ctr_crypt_64way)
Greg Kroah-Hartman
maintainerPATCH 806/1102 x86/mm: Fix user-space data loss with MADV_FREE and THP
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vernon Yang <[email protected]>
commit f7491d7c81db0e7c304a7bd757a76d2fbeaff80e upstream.
Some of users of Polars (a data analytics library) have lost production data from this bug. They seem to have just the right combination of huge pages, MADV_FREE and heavy reclaim pressure.
pmd_modify() masks the old value with (_HPAGE_CHG_MASK & ~_PAGE_DIRTY), silently discarding the hardware dirty bit. The subsequent pmd_mksaveddirty() call is supposed to transfer _PAGE_DIRTY into _PAGE_SAVED_DIRTY when write-protecting, but the dirty bit was already stripped from the value, so there is nothing left to transfer.
Contrast with pte_modify(), which keeps _PAGE_DIRTY_BITS in its mask, and pud_modify(), which keeps _HPAGE_CHG_MASK untouched: pmd_modify() is the odd one out. Any pmd_modify() on a writable, dirty PMD loses the dirty state.
One visible consequence is data loss with MADV_FREE on PMD-mapped THP:
memset(buf, 0x5A, size); // PMD-mapped THP, PMD dirty
madvise(buf, size, MADV_FREE); // PMD cleaned but left writable,
// folio marked lazyfree
memset(buf, 0x5A, size); // hardware sets _PAGE_DIRTY again
mprotect(buf, size, PROT_READ); // pmd_modify() drops the dirty bit
mprotect(buf, size, PROT_READ|PROT_WRITE);
// ... memory pressure ...Reclaim (e.g. under memcg pressure) then finds the lazyfree folio with no dirty bit set anywhere and frees it in __discard_anon_folio_pmd_locked(), even though the data was rewritten after MADV_FREE; subsequent reads fault in fresh zero pages. NUMA hinting alone can trigger the same loss, as do_huge_pmd_numa_page() restores the PMD through pmd_modify() as well.
PMD-mapped file THPs are affected too: mprotect()/NUMA hinting dropping the dirty bit means rewritten data is never written back.
Fix it by keeping _PAGE_DIRTY in the preserved mask, exactly like pte_modify() and pud_modify() do. The existing pmd_mksaveddirty()/pmd_clear_saveddirty() pair then performs the hardware-dirty <-> saved-dirty transition based on the write bit, preserving the shadow-stack encoding rules.
--- arch/x86/include/asm/pgtable.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/x86/include/asm/pgtable.h +1 -1
@@ -857,7 +857,7 @@ static inline pmd_t pmd_modify(pmd_t pmdpmdval_t val =pmd_val (pmd), oldval = val;pmd_t pmd_result; - val &= (_HPAGE_CHG_MASK & ~_PAGE_DIRTY); + val &= _HPAGE_CHG_MASK; val |=check_pgprot (newprot) & ~_HPAGE_CHG_MASK; val =flip_protnone_guard (oldval, val,PHYSICAL_PMD_PAGE_MASK );
Greg Kroah-Hartman
maintainerPATCH 807/1102 ipv6: fix fib6 walker UAF on seq stop
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zihan Xi <[email protected]>
commit 19b4ed644d68098cc62ab612727f40d30f43476c upstream.
ipv6_route_iter_active() treats a walker in FWS_U at the table root as already unlinked. fib6_del_route() can move a still-linked walker into that same state when the current leaf is the last route at the root, so ipv6_route_native_seq_stop() skips fib6_walker_unlink(). The seq private object can then be freed while it remains on net->ipv6.fib6_walkers. A later route deletion walks the dangling list and uses the freed walker.
Use the list head as membership state and reinitialize it when unlinking. Keep the existing w->node check so a never-started iterator with a zeroed private object is not treated as linked.
The same stop helper is used by /proc/net/ipv6_route and by the BPF ipv6_route iterator. The BPF show path only widens the race.
--- net/ipv6/ip6_fib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/ipv6/ip6_fib.c +2 -2
@@ -85,7 +85,7 @@ static void fib6_walker_link(struct netstatic void fib6_walker_unlink (struct net *net,struct fib6_walker *w) {write_lock_bh (&net->ipv6.fib6_walker_lock); -list_del (&w->lh); +list_del_init (&w->lh);write_unlock_bh (&net->ipv6.fib6_walker_lock); }
@@ -2717,7 +2717,7 @@ static void *ipv6_route_seq_start(structstatic bool ipv6_route_iter_active (struct ipv6_route_iter *iter) {struct fib6_walker *w = &iter->w; -return w->node && !(w->state ==FWS_U && w->node == w->root); +return w->node && !list_empty (&w->lh); }static void ipv6_route_native_seq_stop (struct seq_file *seq,void *v)
Greg Kroah-Hartman
maintainerPATCH 808/1102 tracing: Fix memory corruption from the histogram stacktrace modifier
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Donggeun Yoo <[email protected]>
commit a5e70ba87ca8ebc79b4e63de302d03b0625fe153 upstream.
parse_field() sets HIST_FIELD_FL_STACKTRACE from the ".stacktrace" modifier before it looks the field name up, and nothing afterwards checks that the name resolved to a field which holds a stacktrace. create_hist_field() picks HIST_FIELD_FN_STACK on the strength of the field pointer alone, which reads a __data_loc word from the record and follows its low 16 bits as an offset into the same record. event_hist_trigger() takes the first word there as an entry count and copies that many longs into a 31 entry array:
n_entries = *stack; memcpy(entries, ++stack, n_entries * sizeof(unsigned long));
Neither end of that copy is bounded, and the count is whatever the event holds at the offset, so any field will do:
# cd /sys/kernel/tracing/events/sched/sched_process_fork # echo 'hist:keys=parent_pid.stacktrace' > trigger # (true)
BUG: kernel NULL pointer dereference, address: 0000000000000008 RIP: 0010:rb_insert_color+0x18/0x130 timerqueue_linked_add+0x7e/0xd0 enqueue_hrtimer+0x39/0xb0 __hrtimer_run_queues+0x10f/0x1f0 </IRQ> RIP: 0010:memcpy+0xc/0x30 event_hist_trigger+0x165/0x690
The timer interrupt landed on the rbtree the copy had already run over. No debug options are needed for this; KASAN reports the same write as an out-of-bounds read of 13835058055416381440 bytes.
Documentation/trace/histogram.rst already states the rule, "must be a long[] type", so enforce it once the name has been resolved. Names which resolve to no field at all, "hitcount.stacktrace" and the common_* pseudo-fields, are refused for the same reason: they hold no stacktrace to read.
--- kernel/trace/trace_events_hist.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
kernel/trace/trace_events_hist.c +10 -2
@@ -2307,6 +2307,7 @@ parse_field(struct hist_trigger_data *histruct ftrace_event_field *field =NULL ;char *field_name, *modifier, *str;struct trace_array *tr = file->tr; +bool stack_modifier = false; modifier = str =kstrdup (field_str,GFP_KERNEL );if (!modifier)
@@ -2329,9 +2330,10 @@ parse_field(struct hist_trigger_data *hi *flags |=HIST_FIELD_FL_EXECNAME ; else if (strcmp(modifier, "syscall") == 0) *flags |= HIST_FIELD_FL_SYSCALL; - else if (strcmp(modifier, "stacktrace") == 0) + else if (strcmp(modifier, "stacktrace") == 0) { *flags |= HIST_FIELD_FL_STACKTRACE; - else if (strcmp(modifier, "log2") == 0) + stack_modifier = true; + } else if (strcmp(modifier, "log2") == 0) *flags |= HIST_FIELD_FL_LOG2; else if (strcmp(modifier, "usecs") == 0) *flags |= HIST_FIELD_FL_TIMESTAMP_USECS;
@@ -2398,6 +2400,12 @@ parse_field(struct hist_trigger_data *hi } } } + +if (stack_modifier && + (!field || field->filter_type !=FILTER_STACKTRACE )) { +hist_err (tr,HIST_ERR_BAD_FIELD_MODIFIER ,errpos (field_str)); + field =ERR_PTR (-EINVAL ); + } out:kfree (str);
Greg Kroah-Hartman
maintainerPATCH 809/1102 rust: allow `unknown_lints` in generated bindings for Rust < 1.88
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Miguel Ojeda <[email protected]>
commit f4c3e38111fd84c2c7ae5785755f4a4d476e1cba upstream.
Starting with bindgen 0.73.2 [1], #[allow(unnecessary_transmutes)] are used, even when --rust-target 1.85 is passed.
However, the lint was introduced in Rust 1.88.0. Thus building with older Rust versions warns like:
error: unknown lint: `unnecessary_transmutes`
--> rust/uapi/uapi_generated.rs:26294:13
|
26294 | #[allow(unnecessary_transmutes)]
| ^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D unknown-lints` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(unknown_lints)]`Thus allow unknown_lints in the generated bindings -- only when building with older Rust versions.
I have asked upstream if this is intentional [1], i.e. if we are supposed to always allow unknown lints in case bindgen uses such attributes, or whether it is an oversight.
[ Emilio said it wasn't intentional -- we will work around it for now
on the kernel side. - Miguel ]
[ Removed the cfg for allow(unnecessary_transmutes) as suggested by
Gary. - Miguel ]
--- rust/bindings/lib.rs | 3 ++- rust/uapi/lib.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-)
rust/bindings/lib.rs +2 -1
@@ -26,7 +26,8 @@#[allow(dead_code)] #[allow(clippy::undocumented_unsafe_blocks)] -#[cfg_attr( CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES , allow(unnecessary_transmutes))] +#[cfg_attr(not( CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES ), allow(unknown_lints))] +#[allow(unnecessary_transmutes)] #[cfg_attr( CONFIG_RUSTC_HAS_SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS, allow(suspicious_runtime_symbol_definitions)
rust/uapi/lib.rs +2 -1
@@ -24,7 +24,8 @@ unreachable_pub, unsafe_op_in_unsafe_fn )] -#![cfg_attr( CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES , allow(unnecessary_transmutes))] +#![cfg_attr(not( CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES ), allow(unknown_lints))] +#![allow(unnecessary_transmutes)] #![cfg_attr( CONFIG_RUSTC_HAS_SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS, allow(suspicious_runtime_symbol_definitions)
Greg Kroah-Hartman
maintainerPATCH 810/1102 ALSA: ctxfi: Fix CA20K2 S/PDIF passthrough
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Roman Prucha <[email protected]>
commit b26a7a80e6bbf8dd17dacb127d12435d79375cf2 upstream.
dao_rsc_init() encodes the DAIO configuration as
conf = (desc->msr & 0x7) | (desc->passthru << 3);
S/PDIF passthrough uses msr=1 and passthru=1, resulting in conf=9.
daio_mgr_dao_init() masks conf with 0xf, but handles only values 1, 2, 4 and 8 when programming ATXCTL_NUC. As a result, conf=9 falls through to the default case and leaves NUC at its previous setting.
On a Creative X-Fi Titanium HD SB1270 (CA20K2), this breaks AC3 IEC61937 passthrough when snd_ctxfi runs with reference_rate=48000,multiple=2. The receiver detects a non-audio stream but cannot decode the AC3 payload.
With the unmodified driver, multiple=1 makes the same stream work. Handle conf=9 through the same NUC=0 path as conf=1.
The change was runtime tested on the SB1270 with multiple=2 using IEC958 stereo PCM, pre-encoded AC3 IEC61937 passthrough and ALSA A52 live 5.1 encoding.
--- sound/pci/ctxfi/cthw20k2.c | 1 + 1 file changed, 1 insertion(+)
sound/pci/ctxfi/cthw20k2.c +1 -0
@@ -993,6 +993,7 @@ static int daio_mgr_dao_init(void *blk,/* S/PDIF output */ switch ((conf &0xf )) {case 1 : +case 9 :set_field (&ctl->txctl[idx],ATXCTL_NUC ,0 );break ;case 2 :
Greg Kroah-Hartman
maintainerPATCH 811/1102 ALSA: usbusx2y: fix in04_last array size mismatch with in04_buf
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tristan Madani <[email protected]>
commit 861111a14740e12c36d363e9830f8daa734279c9 upstream.
The in04_last array in struct usx2ydev is declared as char[24], but in04_buf is allocated as sizeof(struct us428_ctls) which is 21 bytes. In i_usx2y_in04_int(), when ctl_snapshot_last == -2 (initialization path):
memcpy(usx2y->in04_last, usx2y->in04_buf, sizeof(usx2y->in04_last));
This copies 24 bytes from a 21-byte slab allocation, reading 3 bytes past the end of the source object.
Introduce a USX2Y_IN04_SIZE constant defined as sizeof(struct us428_ctls) and use it consistently for the in04_last array, the in04_buf allocation, the URB transfer length, and the comparison loop, replacing the bare 24 and 21 literals throughout.
--- sound/usb/usx2y/usbusx2y.c | 6 +++--- sound/usb/usx2y/usbusx2y.h | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-)
sound/usb/usx2y/usbusx2y.c +3 -3
@@ -196,7 +196,7 @@ static void i_usx2y_in04_int(struct urbmemcpy (usx2y->in04_last, usx2y->in04_buf,sizeof (usx2y->in04_last)); us428ctls->ctl_snapshot_last =-1 ; }else { -for (i =0 ; i <21 ; i++) { +for (i =0 ; i <USX2Y_IN04_SIZE ; i++) {if (usx2y->in04_last[i] != ((char *)usx2y->in04_buf)[i]) {if (diff <0 ) diff = i;
@@ -305,7 +305,7 @@ int usx2y_in04_init(struct usx2ydev *usx goto error; } - usx2y->in04_buf =kmalloc (21 ,GFP_KERNEL ); + usx2y->in04_buf =kmalloc (USX2Y_IN04_SIZE ,GFP_KERNEL );if (!usx2y->in04_buf) { err = -ENOMEM ; goto error;
@@ -313,7 +313,7 @@ int usx2y_in04_init(struct usx2ydev *usxinit_waitqueue_head (&usx2y->in04_wait_queue);usb_fill_int_urb (usx2y->in04_urb, usx2y->dev,usb_rcvintpipe (usx2y->dev,0x4 ), - usx2y->in04_buf,21 , + usx2y->in04_buf,USX2Y_IN04_SIZE , i_usx2y_in04_int, usx2y,10 );if (usb_urb_ep_type_check (usx2y->in04_urb)) {
sound/usb/usx2y/usbusx2y.h +3 -1
@@ -5,6 +5,8 @@#include "../midi.h" #include "usbus428ctldefs.h" +#define USX2Y_IN04_SIZE sizeof(struct us428_ctls) +#define NRURBS 2/* Default value used for nr of packs per urb.
@@ -55,7 +57,7 @@ struct usx2ydev {int stride;struct urb *in04_urb;void *in04_buf; -char in04_last[24 ]; +char in04_last[USX2Y_IN04_SIZE ];unsigned int in04_int_calls;struct snd_usx2y_urb_seq *us04;wait_queue_head_t in04_wait_queue;
Greg Kroah-Hartman
maintainerPATCH 812/1102 ALSA: usbusx2y: validate URB actual_length in interrupt callback
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tristan Madani <[email protected]>
commit 8f5ef203abda9dd36b2af473c7b737d544f807bd upstream.
i_usx2y_in04_int() processes the interrupt URB data without checking urb->actual_length. A short transfer from a malfunctioning device would cause the handler to process uninitialized heap data from the kmalloc-allocated in04_buf, which is then copied to the mmap-accessible ctl_snapshot[] array.
Fix by using kzalloc() for in04_buf to zero-initialize the buffer, and adding an actual_length check to skip processing on short transfers while still resubmitting the URB.
--- sound/usb/usx2y/usbusx2y.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
sound/usb/usx2y/usbusx2y.c +5 -1
@@ -189,6 +189,9 @@ static void i_usx2y_in04_int(struct urbreturn ; } +if (urb->actual_length <USX2Y_IN04_SIZE ) + goto resubmit; +if (us428ctls) { diff =-1 ;if (us428ctls->ctl_snapshot_last ==-2 ) {
@@ -253,6 +256,7 @@ static void i_usx2y_in04_int(struct urbif (err)dev_err (&urb->dev->dev,"in04_int() usb_submit_urb err=%i\n" , err); +resubmit: urb->dev = usx2y->dev;usb_submit_urb (urb,GFP_ATOMIC ); }
@@ -305,7 +309,7 @@ int usx2y_in04_init(struct usx2ydev *usx goto error; } - usx2y->in04_buf =kmalloc (USX2Y_IN04_SIZE ,GFP_KERNEL ); + usx2y->in04_buf =kzalloc (USX2Y_IN04_SIZE ,GFP_KERNEL );if (!usx2y->in04_buf) { err = -ENOMEM ; goto error;
Greg Kroah-Hartman
maintainerPATCH 813/1102 dm/amdgpu: fix malformed link_settings debugfs output
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Harry Wentland <[email protected]>
commit 622b4e8505aa7453a53d17fa3a288871f270fc8b upstream.
[Why]dp_link_settings_read() passed strlen() of each format string as the size argument to snprintf() and then advanced rd_buf_ptr by that same fixed amount. The format-string length has no relation to the formatted output length, so snprintf() truncated each field at a NUL it wrote inside the buffer while the pointer was advanced past it. The result is a buffer peppered with embedded NUL bytes and fields that are silently cut short, so the data read back from the debugfs node does not reflect the actual link settings.
[How]
Use scnprintf() with the real remaining buffer size (rd_buf_size - (rd_buf_ptr - rd_buf)) and advance rd_buf_ptr by its return value, which is the number of characters actually written. This both bounds each write to the space left in rd_buf and keeps the output a single, properly terminated string. The now-unused str_len local is removed.
--- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c | 20 +++++--------- 1 file changed, 8 insertions(+), 12 deletions(-)
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c +8 -12
@@ -192,7 +192,6 @@ static ssize_t dp_link_settings_read(strchar *rd_buf_ptr =NULL ;const uint32_t rd_buf_size =100 ;uint32_t result =0 ; -uint8_t str_len =0 ;int r;if (*pos &3 || size &3 )
@@ -204,29 +203,26 @@ static ssize_t dp_link_settings_read(str rd_buf_ptr = rd_buf; - str_len =strlen ("Current: %d 0x%x %d " ); -snprintf (rd_buf_ptr, str_len,"Current: %d 0x%x %d " , + rd_buf_ptr +=scnprintf (rd_buf_ptr, rd_buf_size - (rd_buf_ptr - rd_buf), +"Current: %d 0x%x %d " , link->cur_link_settings.lane_count, link->cur_link_settings.link_rate, link->cur_link_settings.link_spread); - rd_buf_ptr += str_len; - str_len =strlen ("Verified: %d 0x%x %d " ); -snprintf (rd_buf_ptr, str_len,"Verified: %d 0x%x %d " , + rd_buf_ptr +=scnprintf (rd_buf_ptr, rd_buf_size - (rd_buf_ptr - rd_buf), +"Verified: %d 0x%x %d " , link->verified_link_cap.lane_count, link->verified_link_cap.link_rate, link->verified_link_cap.link_spread); - rd_buf_ptr += str_len; - str_len =strlen ("Reported: %d 0x%x %d " ); -snprintf (rd_buf_ptr, str_len,"Reported: %d 0x%x %d " , + rd_buf_ptr +=scnprintf (rd_buf_ptr, rd_buf_size - (rd_buf_ptr - rd_buf), +"Reported: %d 0x%x %d " , link->reported_link_cap.lane_count, link->reported_link_cap.link_rate, link->reported_link_cap.link_spread); - rd_buf_ptr += str_len; - str_len =strlen ("Preferred: %d 0x%x %d " ); -snprintf (rd_buf_ptr, str_len,"Preferred: %d 0x%x %d\n" , + rd_buf_ptr +=scnprintf (rd_buf_ptr, rd_buf_size - (rd_buf_ptr - rd_buf), +"Preferred: %d 0x%x %d\n" , link->preferred_link_setting.lane_count, link->preferred_link_setting.link_rate, link->preferred_link_setting.link_spread);
Greg Kroah-Hartman
maintainerPATCH 814/1102 watchdog: sunxi_wdt: preserve boot-enabled watchdog
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: James Hilliard <[email protected]>
commit aab55360fa11a2c054798a484ac67ad606f563e4 upstream.
sunxi_wdt_probe() unconditionally stops the watchdog even when firmware left it running. This opens an unprotected interval during boot and prevents CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED from taking over the active watchdog.
Detect an enabled watchdog and decode its programmed interval. Preserve representable timeouts, and round the 0.5-second interval up to the minimum representable one-second timeout. Use the configured timeout for reserved interval encodings. Set the Linux reset mode and ping the watchdog without clearing its enable bit, then mark it hardware-running before registration so the watchdog core services it until userspace takes control. Leave disabled watchdogs untouched.
--- drivers/watchdog/sunxi_wdt.c | 45 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-)
drivers/watchdog/sunxi_wdt.c +44 -1
@@ -128,6 +128,38 @@ static int sunxi_wdt_ping(struct watchdoreturn 0 ; } +static bool sunxi_wdt_is_running (struct watchdog_device *wdt_dev) +{ +struct sunxi_wdt_dev *sunxi_wdt =watchdog_get_drvdata (wdt_dev); +const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs; + +return readl (sunxi_wdt->wdt_base + regs->wdt_mode) &WDT_MODE_EN ; +} + +static unsigned int sunxi_wdt_get_timeout (struct watchdog_device *wdt_dev) +{ +struct sunxi_wdt_dev *sunxi_wdt =watchdog_get_drvdata (wdt_dev); +const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs; +unsigned int timeout; +u32 interval; + + interval =readl (sunxi_wdt->wdt_base + regs->wdt_mode); + interval >>= regs->wdt_timeout_shift; + interval &=WDT_TIMEOUT_MASK ; +/* Round the 0.5-second interval up to the minimum representable timeout. */ +if (!interval) +return WDT_MIN_TIMEOUT ; + +for (timeout =WDT_MIN_TIMEOUT ; + timeout <ARRAY_SIZE (wdt_timeout_map); timeout++) { +if (wdt_timeout_map[timeout] == interval) +return timeout; + } + +/* Reserved interval encoding. */ +return 0 ; +} +static int sunxi_wdt_set_timeout (struct watchdog_device *wdt_dev,unsigned int timeout) {
@@ -248,6 +280,7 @@ static int sunxi_wdt_probe(struct platfo {struct device *dev = &pdev->dev;struct sunxi_wdt_dev *sunxi_wdt; +unsigned int running_timeout;int err; sunxi_wdt =devm_kzalloc (dev,sizeof (*sunxi_wdt),GFP_KERNEL );
@@ -275,7 +308,17 @@ static int sunxi_wdt_probe(struct platfowatchdog_set_drvdata (&sunxi_wdt->wdt_dev, sunxi_wdt); -sunxi_wdt_stop (&sunxi_wdt->wdt_dev); +if (sunxi_wdt_is_running (&sunxi_wdt->wdt_dev)) { + running_timeout =sunxi_wdt_get_timeout (&sunxi_wdt->wdt_dev); +if (running_timeout) + sunxi_wdt->wdt_dev.timeout = running_timeout; + + err =sunxi_wdt_start (&sunxi_wdt->wdt_dev); +if (err) +return err; + +set_bit (WDOG_HW_RUNNING , &sunxi_wdt->wdt_dev.status); + }watchdog_stop_on_reboot (&sunxi_wdt->wdt_dev); err =devm_watchdog_register_device (dev, &sunxi_wdt->wdt_dev);
Greg Kroah-Hartman
maintainerPATCH 815/1102 ufs: create the root dentry after loading cylinder metadata
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ali Ahmet Memis <[email protected]>
commit 55a4c98abb9694b067c6a031d11501f06b6b523c upstream.
ufs_fill_super() installed sb->s_root before it loaded the cylinder group structures for a writable mount:
sb->s_root = d_make_root(inode); ... if (!sb_rdonly(sb)) if (!ufs_read_cylinder_structures(sb)) goto failed;
When ufs_read_cylinder_structures() failed, the error path freed the in-core superblock information and set sb->s_fs_info to NULL while sb->s_root stayed installed. get_tree_bdev() then reached deactivate_locked_super(), and because s_root was present, generic_shutdown_super() called sync_filesystem() and the put_super operation. Both dereference UFS_SB(sb), which is now NULL, so a mount that fails only while reading the cylinder groups oopses during teardown. A crafted image whose first cylinder group cannot be read reaches this path.
Load the cylinder group metadata first and create the root dentry last, so the superblock is published to the VFS only once it is fully set up. ufs_setup_cstotal() and ufs_read_cylinder_structures() take only the super_block and do not use the root inode, so the reordering is safe.
--- fs/ufs/super.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-)
fs/ufs/super.c +9 -8
@@ -1269,6 +1269,15 @@ magic_found: sb->s_maxbytes =ufs_max_bytes (sb); sb->s_max_links =UFS_LINK_MAX ; +ufs_setup_cstotal (sb); +/* +* Read cylinder group structures +*/ +if (!sb_rdonly (sb)) +if (!ufs_read_cylinder_structures (sb)) + goto failed; + +/* create the root dentry last, once UFS_SB(sb) is fully set up */ inode =ufs_iget (sb,UFS_ROOTINO );if (IS_ERR (inode)) { ret =PTR_ERR (inode);
@@ -1280,14 +1289,6 @@ magic_found: goto failed; } -ufs_setup_cstotal (sb); -/* -* Read cylinder group structures -*/ -if (!sb_rdonly (sb)) -if (!ufs_read_cylinder_structures (sb)) - goto failed; -UFSD ("EXIT\n" );return 0 ;
Greg Kroah-Hartman
maintainerPATCH 816/1102 ufs: validate cylinder group metadata before caching it
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ali Ahmet Memis <[email protected]>
commit c9d263be26806d388129fab8c6904bed197fc6af upstream.
ufs_read_cylinder() copies the cylinder group index and the rotor positions straight from the on-disk group and caches them without any check:
ucpi->c_cgx = fs32_to_cpu(sb, ucg->cg_cgx); ucpi->c_rotor = fs32_to_cpu(sb, ucg->cg_rotor); ucpi->c_frotor = fs32_to_cpu(sb, ucg->cg_frotor); ucpi->c_irotor = fs32_to_cpu(sb, ucg->cg_irotor);
They are then used as indices during allocation and free:
- c_cgx indexes the cylinder summary array as UFS_SB(sb)->fs_cs(ucpi->c_cgx), so a value past s_ncg writes a 32 bit count outside the s_csp allocation.
- c_frotor becomes a bitmap scan start, start = c_frotor >> 3, and then length = ((s_fpg + 7) >> 3) - start. A start beyond the block bitmap wraps the unsigned length to a huge value, so ubh_scanc() walks far past the cylinder group buffers. c_irotor drives the inode bitmap the same way.
A crafted image can set any of these freely, turning an ordinary allocation into an out of bounds access.
Reject a cylinder group whose recorded index does not match the group being read, or whose rotors fall outside the group, before the metadata is cached. Valid filesystems keep cg_cgx equal to the group number and the rotors within the group, so only malformed images are rejected.
--- fs/ufs/cylinder.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
fs/ufs/cylinder.c +10 -0
@@ -66,6 +66,16 @@ static void ufs_read_cylinder (struct su ucpi->c_clustersumoff =fs32_to_cpu (sb, ucg->cg_u.cg_44.cg_clustersumoff); ucpi->c_clusteroff =fs32_to_cpu (sb, ucg->cg_u.cg_44.cg_clusteroff); ucpi->c_nclusterblks =fs32_to_cpu (sb, ucg->cg_u.cg_44.cg_nclusterblks); + +/* these on-disk values become array and bitmap indices */ +if (ucpi->c_cgx != cgno || + ucpi->c_rotor >= uspi->s_fpg || + ucpi->c_frotor >= uspi->s_fpg || + ucpi->c_irotor >= uspi->s_ipg) { +ufs_error (sb, __func__, +"inconsistent metadata in cylinder group %u\n" , cgno); + goto failed; + }UFSD ("EXIT\n" );return ;
Greg Kroah-Hartman
maintainerPATCH 817/1102 tunnels: Drop stale dst when building an ICMP error for PMTUD
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ido Schimmel <[email protected]>
commit b58d749633203d92c265317b45fccee555090352 upstream.
Bridged UDP tunnels such as VXLAN and GENEVE build an ICMP error packet around an overlay packet if the packet is going to exceed the underlay path MTU. The ICMP error packet is then injected back into the Rx path with the source and destination addresses swapped, so that it will be delivered to the overlay source.
If the overlay packet was routed to the UDP tunnel or locally generated, then it is already carrying a valid dst entry and this entry is not dropped when transforming the packet to an ICMP error packet. This causes the IP layer to reuse the dst entry, leading to the ICMP error packet being dropped or routed out of the UDP tunnel interface in case of forwarding.
Prior to the blamed commit this could not happen, as skb_tunnel_check_pmtu() did not build ICMP errors for PACKET_HOST packets. Such packets were instead encapsulated and, unless the DF bit was set in the outer header, fragmented by the underlay.
Fix this by making sure that the ICMP error packet does not have a valid dst entry, thereby forcing the IP layer to perform a route lookup.
Adjust the bridged PMTU exception selftests accordingly. When the local sender in ns_a pings the overlay destination with a deadline (-w), ping exits on the first socket error before any reply is received and returns a non-zero exit code. The test therefore only passed because the ICMP error was never delivered. Use a packet count (-c) like the ns_c line above it, so that the ICMP error counts against the packet budget and the exit code depends on whether echo replies were received. This passes with and without the fix.
--- net/ipv4/ip_tunnel_core.c | 6 ++++++ tools/testing/selftests/net/pmtu.sh | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-)
net/ipv4/ip_tunnel_core.c +6 -0
@@ -266,6 +266,9 @@ static int iptunnel_pmtud_build_icmp(streth_header (skb, skb->dev,ntohs (eh.h_proto), eh.h_source, eh.h_dest,0 );skb_reset_mac_header (skb); +if (skb_valid_dst (skb)) +skb_dst_drop (skb); +return skb->len; }
@@ -369,6 +372,9 @@ static int iptunnel_pmtud_build_icmpv6(seth_header (skb, skb->dev,ntohs (eh.h_proto), eh.h_source, eh.h_dest,0 );skb_reset_mac_header (skb); +if (skb_valid_dst (skb)) +skb_dst_drop (skb); +return skb->len; }
tools/testing/selftests/net/pmtu.sh +1 -1
@@ -1457,7 +1457,7 @@ test_pmtu_ipvX_over_bridged_vxlanY_or_ge mtu "${ns_b}" ${type}_b $((${ll_mtu} + 1000)) run_cmd ${ns_c} ${ping} -q -M want -i 0.1 -c 10 -s $((${ll_mtu} + 500)) ${dst} || return 1 - run_cmd ${ns_a} ${ping} -q -M want -i 0.1 -w 1 -s $((${ll_mtu} + 500)) ${dst} || return 1 + run_cmd ${ns_a} ${ping} -q -M want -i 0.1 -c 10 -s $((${ll_mtu} + 500)) ${dst} || return 1 # Check that exceptions were created pmtu="$(route_get_dst_pmtu_from_exception "${ns_c}" ${dst})"
Greg Kroah-Hartman
maintainerPATCH 818/1102 tick/broadcast: Plug clockevents replacement race
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Gleixner <[email protected]>
commit 113a9796effe3376d2ec5aabcca1fef4fef4cd62 upstream.
朱恺乾 reported and decoded the following race condition when a broadcast device is replaced:
CPUA CPUB
__tick_broadcast_oneshot_control()
bc = tick_broadcast_device.evtdev;
tick_install_broadcast_device(dev)
clockevents_exchange_device(cur, dev)
shutdown(cur);
detach(cur);
cur->handler = noop;
tick_broadcast_device.evtdev = dev; tick_broadcast_set_event(bc, next_event); <- FAIL: arms a detached device.
If the original broadcast device has a restricted interrupt affinity mask and the last CPU in that mask goes offline then the BUG() in tick_cleanup_dead_cpu() triggers because the clockevent device is not in detached state.
The reason for this is that tick_install_broadcast_device() is not serialized vs. tick broadcast operations.
The obvious cure is to serialize tick_install_broadcast_device() with tick_broadcast_lock against a concurrent tick broadcast operation.
That requires to split clockevents_exchange_device() into two parts, one which does the exchange, shutdown and detach operation and the other which drops the module reference count. This is required because the module reference cannot be dropped while holding tick_broadcast_lock.
Let clockevents_exchange_device() do both operations as before, but let the broadcast device code take the two step approach and do the device exchange under tick_broadcast_lock and drop the module reference count after releasing it.
--- kernel/time/clockevents.c | 33 ++++++++++++++++++++------------- kernel/time/tick-broadcast.c | 36 ++++++++++++++++++++++-------------- kernel/time/tick-internal.h | 2 ++ 3 files changed, 44 insertions(+), 27 deletions(-)
kernel/time/clockevents.c +20 -13
@@ -557,34 +557,41 @@ void clockevents_handle_noop(struct cloc { } -/** -* clockevents_exchange_device - release and request clock devices -* @old: device to release (can be NULL) -* @new: device to request (can be NULL) -* -* Called from various tick functions with clockevents_lock held and -* interrupts disabled. -*/ -void clockevents_exchange_device (struct clock_event_device *old, -struct clock_event_device *new) +void __clockevents_exchange_device (struct clock_event_device *old, +struct clock_event_device *new) {/* * Caller releases a clock event device. We queue it into the * released list and do a notify add later. */ if (old) { -module_put (old->owner);clockevents_switch_state (old,CLOCK_EVT_STATE_DETACHED );list_move (&old->list, &clockevents_released); }if (new) { -BUG_ON (!clockevent_state_detached (new)); +WARN_ON (!clockevent_state_detached (new));clockevents_shutdown (new); } }/** +* clockevents_exchange_device - release and request clock devices +* @old: device to release (can be NULL) +* @new: device to request (can be NULL) +* +* Called from various tick functions with clockevents_lock held and +* interrupts disabled. +*/ +void clockevents_exchange_device (struct clock_event_device *old, +struct clock_event_device *new) +{ +__clockevents_exchange_device (old, new); +if (old) +module_put (old->owner); +} + +/** * clockevents_suspend - suspend clock devices */ void clockevents_suspend (void )
@@ -650,7 +657,7 @@ void tick_cleanup_dead_cpu(int cpu)if (cpumask_test_cpu (cpu, dev->cpumask) &&cpumask_weight (dev->cpumask) ==1 && !tick_is_broadcast_device (dev)) { -BUG_ON (!clockevent_state_detached (dev)); +WARN_ON (!clockevent_state_detached (dev));list_del (&dev->list); } }
kernel/time/tick-broadcast.c +22 -14
@@ -162,23 +162,31 @@ static bool tick_set_oneshot_wakeup_devi*/ void tick_install_broadcast_device (struct clock_event_device *dev,int cpu) { -struct clock_event_device *cur = tick_broadcast_device.evtdev; +struct clock_event_device *cur; -if (tick_set_oneshot_wakeup_device (dev, cpu)) -return ; + scoped_guard(raw_spinlock_irqsave, &tick_broadcast_lock) { -if (!tick_check_broadcast_device (cur, dev)) -return ; +if (tick_set_oneshot_wakeup_device (dev, cpu)) +return ; -if (!try_module_get (dev->owner)) -return ; + cur = tick_broadcast_device.evtdev; +if (!tick_check_broadcast_device (cur, dev)) +return ; -clockevents_exchange_device (cur, dev); +if (!try_module_get (dev->owner)) +return ; + +__clockevents_exchange_device (cur, dev); +if (cur) + cur->event_handler = clockevents_handle_noop; +WRITE_ONCE (tick_broadcast_device.evtdev, dev); +if (!cpumask_empty (tick_broadcast_mask)) +tick_broadcast_start_periodic (dev); + } + +/* Module release must be outside of the lock */ if (cur) - cur->event_handler = clockevents_handle_noop; - tick_broadcast_device.evtdev = dev; -if (!cpumask_empty (tick_broadcast_mask)) -tick_broadcast_start_periodic (dev); +module_put (cur->owner);if (!(dev->features &CLOCK_EVT_FEAT_ONESHOT ))return ;
@@ -1209,7 +1217,7 @@ int tick_broadcast_oneshot_active(void)*/ bool tick_broadcast_oneshot_available (void ) { -struct clock_event_device *bc = tick_broadcast_device.evtdev; +struct clock_event_device *bc =READ_ONCE (tick_broadcast_device.evtdev);return bc ? bc->features &CLOCK_EVT_FEAT_ONESHOT : false; }
@@ -1217,7 +1225,7 @@ bool tick_broadcast_oneshot_available(vo#else int __tick_broadcast_oneshot_control (enum tick_broadcast_state state) { -struct clock_event_device *bc = tick_broadcast_device.evtdev; +struct clock_event_device *bc =READ_ONCE (tick_broadcast_device.evtdev);if (!bc || (bc->features &CLOCK_EVT_FEAT_HRTIMER ))return -EBUSY ;
kernel/time/tick-internal.h +2 -0
@@ -53,6 +53,8 @@ static inline void clockevent_set_state( }extern void clockevents_shutdown (struct clock_event_device *dev); +extern void __clockevents_exchange_device (struct clock_event_device *old, +struct clock_event_device *new);extern void clockevents_exchange_device (struct clock_event_device *old,struct clock_event_device *new); extern void clockevents_switch_state(struct clock_event_device *dev,
Greg Kroah-Hartman
maintainerPATCH 819/1102 tracing/user_events: Dont destroy fields when event removal fails
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Henry Martin <[email protected]>
commit 2deb753127d7b7035e893955c5e91875e767d1f8 upstream.
destroy_user_event() destroys the event's fields before attempting to remove the trace event call. If user_event_set_call_visible() fails, e.g. because the event is still enabled and trace_remove_event_call() returns -EBUSY, the event is left registered with an irreversibly destroyed field list. Any subsequent interaction with the event then operates on an empty field list while it is still fully visible in tracefs.
Move the field destruction after the call removal, and splice the field list back onto the event when the removal fails so the event remains in a consistent state.
--- kernel/trace/trace_events_user.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-)
kernel/trace/trace_events_user.c +20 -6
@@ -1122,10 +1122,9 @@ static void user_event_destroy_validator } } -static void user_event_destroy_fields (struct user_event *user) +static void user_event_destroy_fields (struct list_head *head) {struct ftrace_event_field *field, *next; -struct list_head *head = &user->fields;list_for_each_entry_safe (field, next, head, link) {list_del (&field->link);
@@ -1512,17 +1511,32 @@ static int user_event_set_call_visible(sstatic int destroy_user_event (struct user_event *user) { +LIST_HEAD (fields);int ret =0 ;lockdep_assert_held (&event_mutex); -/* Must destroy fields before call removal */ -user_event_destroy_fields (user); +/* +* Detach the fields before removing the call. Removing the event +* frees the field list memory (trace_destroy_fields() is run on +* successful removal and kmem_cache_free()s the fields), but the +* fields here are allocated and owned by user_events. Destroy +* them separately once removal has succeeded. +*/ +list_splice_init (&user->fields, &fields); ret =user_event_set_call_visible (user, false); -if (ret) +if (ret) { +/* +* Removal failed and the event stays registered, recover +* the fields so it is left in a consistent state. +*/ +list_splice (&fields, &user->fields);return ret; + } + +user_event_destroy_fields (&fields);dyn_event_remove (&user->devent);hash_del (&user->node);
@@ -2222,7 +2236,7 @@ static int user_event_parse(struct user_ put_user_lock:mutex_unlock (&event_mutex); put_user: -user_event_destroy_fields (user); +user_event_destroy_fields (&user->fields);user_event_destroy_validators (user);kfree (user->call.print_fmt);
Greg Kroah-Hartman
maintainerPATCH 820/1102 tracing: Free histogram the var ref when its initialization fails
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Donggeun Yoo <[email protected]>
commit 516001d53e6b2ea95a251ee2ef54a1a689a3fd58 upstream.
create_var_ref() allocates a VAR_REF hist_field and then calls init_var_ref() to fill it in. When that fails the field is leaked.
commit 656fe2ba85e8 ("tracing: Use hist trigger's var_ref array to destroy var_refs") made destroy_hist_field() return early for HIST_FIELD_FL_VAR_REF, since var refs are freed by walking the trigger's var_refs[] array instead. create_var_ref() adds the field to that array only after init_var_ref() has succeeded, so on this path the field is in neither place and nothing frees it. The call was correct when it was written, before var refs were taken out of destroy_hist_field().
init_var_ref() cannot free it either. The caller owns the field, so init_var_ref() undoes only its own string allocations and leaves the field alone. Freeing it there would leave create_var_ref() passing freed memory to destroy_hist_field(), which reads its flags.
Call __destroy_hist_field(), which frees the field without consulting the flag.
--- kernel/trace/trace_events_hist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/trace_events_hist.c +1 -1
@@ -2211,7 +2211,7 @@ static struct hist_field *create_var_ref ref_field =create_hist_field (var_field->hist_data,NULL , flags,NULL );if (ref_field) {if (init_var_ref (ref_field, var_field, system, event_name)) { -destroy_hist_field (ref_field,0 ); +__destroy_hist_field (ref_field);return NULL ; }
Greg Kroah-Hartman
maintainerPATCH 821/1102 tracing: Free histogram var refs regardless of how often they are referenced
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Donggeun Yoo <[email protected]>
commit 4bddcb346a6cf4615ca77f69a589623b877ca267 upstream.
Using the same variable three or more times in one hist trigger leaks the variable reference and its strings when the trigger is removed.
commit 656fe2ba85e8 ("tracing: Use hist trigger's var_ref array to destroy var_refs") made a trigger's var_refs[] array the only owner of a var ref:destroy_hist_field() returns early for HIST_FIELD_FL_VAR_REF, so the field expressions never destroy one. One entry, freed once, no count needed.
commit 8bcebc77e85f ("tracing: Fix histogram code when expression has same var as value") then made repeated references share one object and added a count of them. Only the increment side exists, since those expressions still return early and never drop a reference, so __destroy_hist_field() sees how many references were created rather than how many are left. It frees when the decremented count is 0 or 1, so two references work and three or more leak.
Sharing kept one array entry per object, and create_var_ref() searches and appends within a single trigger, so nothing outside it holds the object. Removing a trigger whose variables are still referenced is already refused by check_var_refs() with -EBUSY. Drop the count and free unconditionally.
--- kernel/trace/trace_events_hist.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-)
kernel/trace/trace_events_hist.c +1 -15
@@ -163,7 +163,6 @@ struct hist_field {struct hist_field *operands[HIST_FIELD_OPERANDS_MAX ];struct hist_trigger_data *hist_data;enum hist_field_fn fn_num; -unsigned int ref;unsigned int size;unsigned int offset;unsigned int is_signed;
@@ -1895,16 +1894,8 @@ out:return field_op; } -static void get_hist_field (struct hist_field *hist_field) -{ - hist_field->ref++; -} -static void __destroy_hist_field (struct hist_field *hist_field) { -if (--hist_field->ref >1 ) -return ; -kfree (hist_field->var.name);kfree (hist_field->name);
@@ -1951,8 +1942,6 @@ static struct hist_field *create_hist_fiif (!hist_field)return NULL ; - hist_field->ref =1 ; - hist_field->hist_data = hist_data;if (flags &HIST_FIELD_FL_EXPR || flags &HIST_FIELD_FL_ALIAS )
@@ -2200,10 +2189,8 @@ static struct hist_field *create_var_reffor (i =0 ; i < hist_data->n_var_refs; i++) { ref_field = hist_data->var_refs[i];if (ref_field->var.idx == var_field->var.idx && - ref_field->var.hist_data == var_field->hist_data) { -get_hist_field (ref_field); + ref_field->var.hist_data == var_field->hist_data)return ref_field; - } }/* Sanity check to avoid out-of-bound write on 'hist_data->var_refs' */ if (hist_data->n_var_refs >=TRACING_MAP_VARS_MAX )
@@ -3242,7 +3229,6 @@ static struct hist_field *create_var(str goto out; } - var->ref =1 ; var->flags =HIST_FIELD_FL_VAR ; var->var.idx = idx; var->var.hist_data = var->hist_data = hist_data;
Greg Kroah-Hartman
maintainerPATCH 822/1102 tracing: Free histogram the field rejected for a bad modifier
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Donggeun Yoo <[email protected]>
commit 230234d12ce42ab04132a32c3a848f07a5d27a71 upstream.
Writing a hist trigger whose value or variable carries a modifier that is not allowed there leaks the fields that were built for it.
__create_val_field() takes the field from parse_expr() and stores it in hist_data->fields[] only after the modifier checks have run:
hist_field = parse_expr(hist_data, file, field_str, flags, var_name,
&n_subexprs);
...
if (hist_field->flags & HIST_FIELD_FL_VAR) {
if (hist_field->flags & (...))
goto err;
} else {
if (hist_field->flags & (...))
goto err;
}
hist_data->fields[val_idx] = hist_field;Both checks jump past that store, and the err label returns without freeing anything. The error unwinds to create_hist_data(), which calls destroy_hist_data() -> destroy_hist_fields(), and that reaches a field only by walking fields[]. A field that never got there is unreachable.
commit e0213434fe3e ("tracing: Do not let histogram values have some modifiers") set ret to -EINVAL and fell through to the store, which left the field owned by fields[] and freed along with the rest of hist_data. Splitting the check into a value case and a variable case replaced that fall-through with a goto that skips it.
With CONFIG_DEBUG_KMEMLEAK, 200 writes of
# echo 'hist:keys=prev_pid:vals=next_pid.log2' > \ events/sched/sched_switch/trigger
each correctly rejected with -EINVAL, leave 332 unreferenced objects (63744 bytes) reported at create_hist_field(); 200 install and remove cycles of a valid trigger leave none. A '.log2' field is two allocations, since create_hist_field() puts the plain field in operands[0] of the log2 field, and both are reported.
Use destroy_hist_field() rather than __destroy_hist_field() so that operands[0] is freed as well. It returns early for HIST_FIELD_FL_VAR_REF, which is what an operand owned by hist_data->var_refs[] needs; the rejected field itself is never a var ref, because a var ref never carries a modifier flag.
--- kernel/trace/trace_events_hist.c | 1 + 1 file changed, 1 insertion(+)
kernel/trace/trace_events_hist.c +1 -0
@@ -4283,6 +4283,7 @@ static int __create_val_field(struct hisreturn ret; err:hist_err (file->tr,HIST_ERR_BAD_FIELD_MODIFIER ,errpos (field_str)); +destroy_hist_field (hist_field,0 );return -EINVAL ; }
Greg Kroah-Hartman
maintainerPATCH 823/1102 tracing: Let histogram values keep the percent and graph modifiers
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Donggeun Yoo <[email protected]>
commit 3d617bfd79330ae3acf94862c18bb3ccf5f5a0f9 upstream.
The .percent and .graph modifiers exist only for histogram values, but a value carrying either of them has been rejected since v6.3. The example in Documentation/trace/histogram.rst,
# echo 'hist:keys=prev_comm:vals=hitcount.percent:nohitcount' > \ events/sched/sched_switch/trigger
returns -EINVAL.
parse_field() sets the two flags only when the field is neither a key nor a variable, that is, only on a value:
} else if (strncmp(modifier, "percent", 7) == 0) {
if (*flags & (HIST_FIELD_FL_VAR | HIST_FIELD_FL_KEY))
goto error;
*flags |= HIST_FIELD_FL_PERCENT;__create_val_field() then rejects a value for carrying them, so no field can reach hist_trigger_print_val(), where both are implemented.
commit e0213434fe3e ("tracing: Do not let histogram values have some modifiers") added the check after a value with .buckets oopsed in hist_field_name(). That happens because .buckets and .log2 make create_hist_field() build a nested field in operands[0] which hist_field_name() then walks into. The percent and graph flags do not create an operand and are not read by hist_field_name(); they are only used when printing a value.
Stop rejecting the two flags on a value. The check for variables is left alone, where they are unreachable anyway because parse_field() rejects a variable carrying them first.
With the two flags removed, the trigger above installs and prints as documented:
{ prev_comm: rcu_preempt } hitcount (%): 0.00
{ prev_comm: init } hitcount (%): 99.98
Totals:
Hits: 237896--- kernel/trace/trace_events_hist.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
kernel/trace/trace_events_hist.c +1 -2
@@ -4265,8 +4265,7 @@ static int __create_val_field(struct his goto err; }else {/* Value */ -if (hist_field->flags & (HIST_FIELD_FL_GRAPH |HIST_FIELD_FL_PERCENT | -HIST_FIELD_FL_BUCKET |HIST_FIELD_FL_LOG2 | +if (hist_field->flags & (HIST_FIELD_FL_BUCKET |HIST_FIELD_FL_LOG2 |HIST_FIELD_FL_SYM |HIST_FIELD_FL_SYM_OFFSET |HIST_FIELD_FL_SYSCALL |HIST_FIELD_FL_STACKTRACE )) goto err;
Greg Kroah-Hartman
maintainerPATCH 824/1102 tracing: Keep the entry count when the histogram stats allocation fails
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Donggeun Yoo <[email protected]>
commit 06f5634ec5584954177f9a22e36b3bfb398a971b upstream.
print_entries() uses n_entries both as the number of sort entries and as its own return value, so the -ENOMEM it stores when the stats allocation fails overwrites the count that the cleanup still needs:
n_entries = tracing_map_sort_entries(map, ...);
if (n_entries < 0)
return n_entries;
...
if (!stats) {
n_entries = -ENOMEM;
goto out;
}
...
out:
tracing_map_destroy_sort_entries(sort_entries, n_entries);tracing_map_destroy_sort_entries() takes an unsigned int and loops up to it, so -ENOMEM arrives as 4294967284. It walks an array of at most map->max_elts pointers and calls destroy_sort_entry(), which dereferences and frees, on whatever lies past the end.
Reading the hist file of a trigger with a .percent value, with that allocation forced to fail:
BUG: KASAN: vmalloc-out-of-bounds in tracing_map_destroy_sort_entries+0xa0/0xb0 Read of size 8 at addr ffffc90000045000 by task init/1 tracing_map_destroy_sort_entries+0xa0/0xb0 hist_show+0x6f7/0x1df0 seq_read_iter+0x2b8/0x1190 vfs_read+0x176/0xa40 The buggy address belongs to a 4-page vmalloc region starting at ffffc90000041000 allocated at tracing_map_sort_entries+0x5c/0xd50
A few pages further the fault is fatal. The registers at the oops confirm the bound: the loop's end pointer less the array start, over the pointer size, is 4294967284.
Return the error in a separate variable and leave n_entries holding the count, the way tracing_map_sort_entries() does on its own error path.
The stats block is only entered for a value carrying .percent or .graph, which __create_val_field() has rejected since v6.3, so this cannot be reached in mainline as it stands. It becomes reachable again with "tracing: hist: let values keep the percent and graph modifiers", so it should be applied first.
--- kernel/trace/trace_events_hist.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
kernel/trace/trace_events_hist.c +5 -3
@@ -5637,7 +5637,7 @@ static int print_entries(struct seq_file {struct tracing_map_sort_entry **sort_entries =NULL ;struct tracing_map *map = hist_data->map; -int i, j, n_entries; +int i, j, n_entries, ret;struct hist_val_stat *stats =NULL ;u64 val;
@@ -5647,6 +5647,8 @@ static int print_entries(struct seq_fileif (n_entries <0 )return n_entries; + ret = n_entries; +/* Calculate the max and the total for each field if needed. */ for (j =0 ; j < hist_data->n_vals; j++) { if (!(hist_data->fields[j]->flags &
@@ -5656,7 +5658,7 @@ static int print_entries(struct seq_file stats =kcalloc (hist_data->n_vals,sizeof (*stats),GFP_KERNEL );if (!stats) { - n_entries = -ENOMEM ; + ret = -ENOMEM ; goto out; } }
@@ -5677,7 +5679,7 @@ static int print_entries(struct seq_file out:tracing_map_destroy_sort_entries (sort_entries, n_entries); -return n_entries; +return ret; } static void hist_trigger_show(struct seq_file *m,
Greg Kroah-Hartman
maintainerPATCH 825/1102 accel/ivpu: Validate full buffer range in ivpu_to_cpu_addr
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Magdalena Schulfer <[email protected]>
commit 3837c3f29fbc3b8c12bebf5c62741e2befe3482a upstream.
Add a size parameter to ivpu_to_cpu_addr() and validate that the whole [vpu_addr, vpu_addr + size) range stays within the BO.
--- drivers/accel/ivpu/ivpu_gem.h | 14 +++++++++++--- drivers/accel/ivpu/ivpu_ipc.c | 7 ++++--- 2 files changed, 15 insertions(+), 6 deletions(-)
drivers/accel/ivpu/ivpu_gem.h +11 -3
@@ -73,15 +73,23 @@ static inline bool ivpu_bo_is_snooped(streturn ivpu_bo_cache_mode (bo) ==DRM_IVPU_BO_CACHED ; } -static inline void *ivpu_to_cpu_addr (struct ivpu_bo *bo,u32 vpu_addr) +static inline void *ivpu_to_cpu_addr (struct ivpu_bo *bo,u64 vpu_addr,u64 size) { +u64 bo_size =ivpu_bo_size (bo); +u64 offset; +if (vpu_addr < bo->vpu_addr)return NULL ; -if (vpu_addr >= (bo->vpu_addr +ivpu_bo_size (bo))) +if (size > bo_size) +return NULL ; + + offset = vpu_addr - bo->vpu_addr; + +if (offset > bo_size - size)return NULL ; -return ivpu_bo_vaddr (bo) + (vpu_addr - bo->vpu_addr); +return ivpu_bo_vaddr (bo) + offset; }static inline u32 cpu_to_vpu_addr (struct ivpu_bo *bo,void *cpu_addr)
drivers/accel/ivpu/ivpu_ipc.c +4 -3
@@ -78,7 +78,7 @@ ivpu_ipc_tx_prepare(struct ivpu_device *return -ENOMEM ; } - tx_buf =ivpu_to_cpu_addr (ipc->mem_tx, tx_buf_vpu_addr); + tx_buf =ivpu_to_cpu_addr (ipc->mem_tx, tx_buf_vpu_addr,sizeof (*tx_buf));if (drm_WARN_ON (&vdev->drm, !tx_buf)) {gen_pool_free (ipc->mm_tx, tx_buf_vpu_addr,sizeof (*tx_buf));return -EIO ;
@@ -414,7 +414,7 @@ void ivpu_ipc_irq_handler(struct ivpu_dereturn ; } - ipc_hdr =ivpu_to_cpu_addr (ipc->mem_rx, vpu_addr); + ipc_hdr =ivpu_to_cpu_addr (ipc->mem_rx, vpu_addr,sizeof (*ipc_hdr));if (!ipc_hdr) {ivpu_warn_ratelimited (vdev,"IPC msg 0x%x out of range\n" , vpu_addr);continue ;
@@ -423,7 +423,8 @@ void ivpu_ipc_irq_handler(struct ivpu_de jsm_msg =NULL ;if (ipc_hdr->channel !=IVPU_IPC_CHAN_BOOT_MSG ) { - jsm_msg =ivpu_to_cpu_addr (ipc->mem_rx, ipc_hdr->data_addr); + jsm_msg =ivpu_to_cpu_addr (ipc->mem_rx, ipc_hdr->data_addr, +sizeof (*jsm_msg));if (!jsm_msg) {ivpu_warn_ratelimited (vdev,"JSM msg 0x%x out of range\n" , ipc_hdr->data_addr);
Greg Kroah-Hartman
maintainerPATCH 826/1102 accel/ivpu: Validate firmware log buffer metadata
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Magdalena Schulfer <[email protected]>
commit 0724afc55c77c36c7feb9a7264b02aa7593c5c2d upstream.
The tracing log headers parsed by fw_log_print_buffer() reside in DMA-shared BOs that the NPU firmware can write to.
fw_log_from_bo() validated log->header_size and log->size, but fw_log_print_buffer() re-read those same fields from shared memory afterwards, allowing a TOCTOU where firmware changes them between the check and the use, and making the host dereference out-of-bounds addresses while printing logs.
Snapshot the validated values once with READ_ONCE() and pass them down explicitly in a new struct ivpu_fw_log_desc instead of re-reading them from the shared struct.
--- drivers/accel/ivpu/ivpu_fw_log.c | 76 +++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 31 deletions(-)
drivers/accel/ivpu/ivpu_fw_log.c +45 -31
@@ -26,10 +26,17 @@ MODULE_PARM_DESC(fw_log_level, " error=" __stringify(IVPU_FW_LOG_ERROR) " fatal=" __stringify(IVPU_FW_LOG_FATAL)); +struct ivpu_fw_log_desc { +struct vpu_tracing_buffer_header *log; +u32 header_size; +u32 size; +}; +static int fw_log_from_bo (struct ivpu_device *vdev,struct ivpu_bo *bo,u32 *offset, -struct vpu_tracing_buffer_header **out_log) +struct ivpu_fw_log_desc *desc) {struct vpu_tracing_buffer_header *log; +u32 header_size, size;if ((*offset +sizeof (*log)) >ivpu_bo_size (bo))return -EINVAL ;
@@ -39,26 +46,32 @@ static int fw_log_from_bo(struct ivpu_deif (log->vpu_canary_start !=VPU_TRACING_BUFFER_CANARY )return -EINVAL ; -if (log->header_size <sizeof (*log) || log->header_size >1024 ) { -ivpu_dbg (vdev,FW_BOOT ,"Invalid header size 0x%x\n" , log->header_size); + header_size =READ_ONCE (log->header_size); + size =READ_ONCE (log->size); + +if (header_size <sizeof (*log) || header_size >1024 ) { +ivpu_dbg (vdev,FW_BOOT ,"Invalid header size 0x%x\n" , header_size);return -EINVAL ; } -if (log->size < log->header_size) { -ivpu_dbg (vdev,FW_BOOT ,"Invalid log size 0x%x\n" , log->size); +if ((char *)log + size > (char *)ivpu_bo_vaddr (bo) +ivpu_bo_size (bo)) { +ivpu_dbg (vdev,FW_BOOT ,"Invalid log size 0x%x\n" , size);return -EINVAL ; } -if ((char *)log + log->size > (char *)ivpu_bo_vaddr (bo) +ivpu_bo_size (bo)) { -ivpu_dbg (vdev,FW_BOOT ,"Invalid log size 0x%x\n" , log->size); +if (size < header_size) { +ivpu_dbg (vdev,FW_BOOT ,"Invalid log size 0x%x < header size 0x%x\n" , + size, header_size);return -EINVAL ; } - *out_log = log; - *offset += log->size; + desc->log = log; + desc->header_size = header_size; + desc->size = size; + *offset += size;ivpu_dbg (vdev,FW_BOOT ,"FW log name \"%s\", write offset 0x%x size 0x%x, wrap count %d, hdr version %d size %d format %d, alignment %d" , - log->name, log->write_index, log->size, log->wrap_count, log->header_version, - log->header_size, log->format, log->alignment); + log->name, log->write_index, size, log->wrap_count, log->header_version, + header_size, log->format, log->alignment);return 0 ; }
@@ -94,11 +107,12 @@ static void fw_log_print_lines(char *bufdrm_printf (p,"%s" , line); } -static void fw_log_print_buffer (struct vpu_tracing_buffer_header *log,const char *prefix, +static void fw_log_print_buffer (struct ivpu_fw_log_desc *desc,const char *prefix,bool only_new_msgs,struct drm_printer *p) { -char *log_data = (void *)log + log->header_size; -u32 data_size = log->size - log->header_size; +struct vpu_tracing_buffer_header *log = desc->log; +char *log_data = (void *)log + desc->header_size; +u32 data_size = desc->size - desc->header_size;u32 log_start = only_new_msgs ?READ_ONCE (log->read_index) :0 ;u32 log_end =READ_ONCE (log->write_index);
@@ -134,11 +148,11 @@ static void fw_log_print_all_in_bo(struct ivpu_device *vdev, const char *name, struct ivpu_bo *bo, bool only_new_msgs, struct drm_printer *p) { - struct vpu_tracing_buffer_header *log; + struct ivpu_fw_log_desc desc;u32 next =0 ; -while (fw_log_from_bo (vdev, bo, &next, &log) ==0 ) -fw_log_print_buffer (log, name, only_new_msgs, p); +while (fw_log_from_bo (vdev, bo, &next, &desc) ==0 ) +fw_log_print_buffer (&desc, name, only_new_msgs, p); }void ivpu_fw_log_print (struct ivpu_device *vdev,bool only_new_msgs,struct drm_printer *p)
@@ -149,36 +163,36 @@ void ivpu_fw_log_print(struct ivpu_devicvoid ivpu_fw_log_mark_read (struct ivpu_device *vdev) { -struct vpu_tracing_buffer_header *log; +struct ivpu_fw_log_desc desc;u32 next; next =0 ; -while (fw_log_from_bo (vdev, vdev->fw->mem_log_crit, &next, &log) ==0 ) { - log->read_index =READ_ONCE (log->write_index); - log->read_wrap_count =READ_ONCE (log->wrap_count); +while (fw_log_from_bo (vdev, vdev->fw->mem_log_crit, &next, &desc) ==0 ) { + desc.log->read_index =READ_ONCE (desc.log->write_index); + desc.log->read_wrap_count =READ_ONCE (desc.log->wrap_count); } next =0 ; -while (fw_log_from_bo (vdev, vdev->fw->mem_log_verb, &next, &log) ==0 ) { - log->read_index =READ_ONCE (log->write_index); - log->read_wrap_count =READ_ONCE (log->wrap_count); +while (fw_log_from_bo (vdev, vdev->fw->mem_log_verb, &next, &desc) ==0 ) { + desc.log->read_index =READ_ONCE (desc.log->write_index); + desc.log->read_wrap_count =READ_ONCE (desc.log->wrap_count); } }void ivpu_fw_log_reset (struct ivpu_device *vdev) { -struct vpu_tracing_buffer_header *log; +struct ivpu_fw_log_desc desc;u32 next; next =0 ; -while (fw_log_from_bo (vdev, vdev->fw->mem_log_crit, &next, &log) ==0 ) { - log->read_index =0 ; - log->read_wrap_count =0 ; +while (fw_log_from_bo (vdev, vdev->fw->mem_log_crit, &next, &desc) ==0 ) { + desc.log->read_index =0 ; + desc.log->read_wrap_count =0 ; } next =0 ; -while (fw_log_from_bo (vdev, vdev->fw->mem_log_verb, &next, &log) ==0 ) { - log->read_index =0 ; - log->read_wrap_count =0 ; +while (fw_log_from_bo (vdev, vdev->fw->mem_log_verb, &next, &desc) ==0 ) { + desc.log->read_index =0 ; + desc.log->read_wrap_count =0 ; } }
Greg Kroah-Hartman
maintainerPATCH 827/1102 accel/ivpu: Limit firmware log name prints to field size
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dawid Osuchowski <[email protected]>
commit 95bf070f3225dc7175725438c916ad321d42fe45 upstream.
The name in struct vpu_tracing_buffer_header is a fixed-size array populated by the NPU firmware. It is expected to be NUL-terminated, but nothing on the host side enforces this, so printing it with an unbounded string conversion would read past the field if the terminator is ever missing and expose adjacent bytes of the shared tracing BO through dmesg and the debugfs FW log output.
Print at most as many characters as the name field holds, so the output never runs past it even if the string is not NUL-terminated.
--- drivers/accel/ivpu/ivpu_fw_log.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
drivers/accel/ivpu/ivpu_fw_log.c +9 -6
@@ -69,9 +69,9 @@ static int fw_log_from_bo(struct ivpu_de *offset += size;ivpu_dbg (vdev,FW_BOOT , -"FW log name \"%s\", write offset 0x%x size 0x%x, wrap count %d, hdr version %d size %d format %d, alignment %d" , - log->name, log->write_index, size, log->wrap_count, log->header_version, - header_size, log->format, log->alignment); +"FW log name \"%.*s\", write offset 0x%x size 0x%x, wrap count %d, hdr version %d size %d format %d, alignment %d" , + (int )ARRAY_SIZE (log->name), log->name, log->write_index, size, log->wrap_count, + log->header_version, header_size, log->format, log->alignment);return 0 ; }
@@ -123,7 +123,8 @@ static void fw_log_print_buffer(struct iif (log->wrap_count == log->read_wrap_count) {if (log_end <= log_start) { -drm_printf (p,"==== %s \"%s\" log empty ====\n" , prefix, log->name); +drm_printf (p,"==== %s \"%.*s\" log empty ====\n" , prefix, + (int )ARRAY_SIZE (log->name), log->name);return ; } }else if (log->wrap_count == log->read_wrap_count +1 ) {
@@ -133,7 +134,8 @@ static void fw_log_print_buffer(struct i log_start = log_end; } -drm_printf (p,"==== %s \"%s\" log start ====\n" , prefix, log->name); +drm_printf (p,"==== %s \"%.*s\" log start ====\n" , prefix, (int )ARRAY_SIZE (log->name), + log->name);if (log_end > log_start) {fw_log_print_lines (log_data + log_start, log_end - log_start, p); }else {
@@ -141,7 +143,8 @@ static void fw_log_print_buffer(struct ifw_log_print_lines (log_data, log_end, p); }drm_printf (p,"\n\x1b[0m" );/* add new line and clear formatting */ -drm_printf (p,"==== %s \"%s\" log end ====\n" , prefix, log->name); +drm_printf (p,"==== %s \"%.*s\" log end ====\n" , prefix, (int )ARRAY_SIZE (log->name), + log->name); } static void
Greg Kroah-Hartman
maintainerPATCH 828/1102 ASoC: sprd: validate compress buffer sizes against fixed allocations
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tianchu Chen <[email protected]>
commit 7a4ce92d150b9e7ecf1a710a34d8cdeb590d3751 upstream.
sprd_platform_compr_open() allocates the stage 0 IRAM buffer (32K data area) and the stage 1 DDR buffer (2M data area) with fixed sizes, but sprd_platform_compr_copy() derives all copy lengths from the user controlled runtime->fragment_size and the write() count, never comparing them against the physical buffer sizes. The compress core only checks fragment_size * fragments for an u32 overflow in snd_compress_check_input(), so a local user can configure a logical buffer of up to ~4GB via SNDRV_COMPRESS_SET_PARAMS, far exceeding the fixed allocations.
A fragment_size larger than the 32K IRAM data area makes the stage 0 copy_from_user() overflow past the IRAM allocation, and a buffer_size larger than the 2M DDR buffer makes the wrapping copy at the end of sprd_platform_compr_copy() write fully user controlled data past the buffer. No SNDRV_PCM_TRIGGER_START is needed, a write() in SETUP state reaches the copy callback directly.
Reject parameters that do not fit into the fixed buffers in set_params(), and fix the advertised max fragment size: 128K never fitted into the 32K IRAM buffer. The caps values may have been carried over from the qdsp6 driver, which allocates its buffers according to the advertised maxima, unlike this driver. With 32K as max fragment size the advertised limits are self-consistent: 32K * 64 = 2M equals the DDR buffer size.
Discovered by Atuin - Automated Vulnerability Discovery Engine.
--- sound/soc/sprd/sprd-pcm-compress.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-)
sound/soc/sprd/sprd-pcm-compress.c +14 -1
@@ -17,7 +17,7 @@/* Default values if userspace does not set */ #define SPRD_COMPR_MIN_FRAGMENT_SIZE SZ_8K -#define SPRD_COMPR_MAX_FRAGMENT_SIZE SZ_128K +#define SPRD_COMPR_MAX_FRAGMENT_SIZE SZ_32K#define SPRD_COMPR_MIN_NUM_FRAGMENTS 4#define SPRD_COMPR_MAX_NUM_FRAGMENTS 64
@@ -272,6 +272,19 @@ static int sprd_platform_compr_set_paramint ret;/* +* The stage 0 IRAM buffer and the stage 1 DDR buffer are allocated +* with fixed sizes at open time, so the requested fragment size and +* fragments must fit into them, otherwise sprd_platform_compr_copy() +* would overflow the buffers. Note the compress core only checks the +* fragment size and fragments against an u32 overflow, not against +* the buffer sizes advertised by get_caps. +*/ +if (params->buffer.fragment_size >SPRD_COMPR_IRAM_BUF_SIZE || + (u64 )params->buffer.fragment_size * params->buffer.fragments > +SPRD_COMPR_AREA_BUF_SIZE ) +return -EINVAL ; + +/* * Configure the DMA engine 2-stage transfer mode. Channel 1 set as the * destination channel, and channel 0 set as the source channel, that * means once the source channel's transaction is done, it will trigger
Greg Kroah-Hartman
maintainerPATCH 829/1102 ASoC: sti: initialize IRQ lock before requesting IRQ
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Runyu Xiao <[email protected]>
commit 04405aeef4f8d7bcac6dcb1947acafdb4420c2c3 upstream.
uni_reader_init() registers the shared IRQ before initializing reader->irq_lock. A pending interrupt can invoke the handler while the lock is still uninitialized.
Initialize the lock before registering the IRQ so the interrupt path always sees valid lock state.
--- sound/soc/sti/uniperif_reader.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
sound/soc/sti/uniperif_reader.c +2 -2
@@ -422,6 +422,8 @@ int uni_reader_init(struct platform_devielse reader->hw = &uni_reader_pcm_hw; +spin_lock_init (&reader->irq_lock); + ret =devm_request_irq (&pdev->dev, reader->irq, uni_reader_irq_handler,IRQF_SHARED ,dev_name (&pdev->dev), reader);
@@ -430,8 +432,6 @@ int uni_reader_init(struct platform_devireturn -EBUSY ; } -spin_lock_init (&reader->irq_lock); -return 0 ; }EXPORT_SYMBOL_GPL (uni_reader_init);
Greg Kroah-Hartman
maintainerPATCH 830/1102 Bluetooth: btqcomsmd: destroy RPMsg endpoints before freeing hci_dev
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xu Rao <[email protected]>
commit f5a427b16e45210dee656b0860728f3d496dee85 upstream.
The command and ACL RPMsg endpoints store struct btqcomsmd as their callback private data. The receive callbacks dereference btq->hdev without taking an hci_dev reference.
The current teardown order frees the hci_dev before destroying the RPMsg endpoints in both the hci_register_dev() error path and the driver remove path. If WCNSS delivers data in that window, the endpoint callback can run with an already freed hci_dev and pass it to the Bluetooth core.
For qcom_smd endpoints, rpmsg_destroy_ept() closes the channel and clears the callback under the channel recv_lock. The receive path holds the same lock while invoking the callback, so destroying the endpoints first both prevents new callbacks and serializes with any callback already running.
Destroy the command and ACL endpoints before hci_free_dev(). Keep hci_unregister_dev() first during remove so the HCI core stops issuing operations before the transport endpoints are shut down. In the full registration-error cleanup path, return directly after freeing the hci_dev to avoid falling through to the partial-construction labels and destroying the endpoints twice.
--- drivers/bluetooth/btqcomsmd.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/bluetooth/btqcomsmd.c +5 -1
@@ -188,7 +188,10 @@ static int btqcomsmd_probe(struct platforeturn 0 ; hci_free_dev: +rpmsg_destroy_ept (btq->cmd_channel); +rpmsg_destroy_ept (btq->acl_channel);hci_free_dev (hdev); +return ret; destroy_cmd_channel:rpmsg_destroy_ept (btq->cmd_channel); destroy_acl_channel:
@@ -202,10 +205,11 @@ static void btqcomsmd_remove(struct platstruct btqcomsmd *btq =platform_get_drvdata (pdev);hci_unregister_dev (btq->hdev); -hci_free_dev (btq->hdev);rpmsg_destroy_ept (btq->cmd_channel);rpmsg_destroy_ept (btq->acl_channel); + +hci_free_dev (btq->hdev); }static const struct of_device_id btqcomsmd_of_match[] = {
Greg Kroah-Hartman
maintainerPATCH 831/1102 Bluetooth: btrtl: Dont leak return code when parsing firmware format v2
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rong Zhang <[email protected]>
commit 83e3e515fd261600ed8491fb0a8bcdfb115c904e upstream.
When key_id from chip is zero, rtlbt_parse_firmware_v2() intentionally ignores all security headers. However, the implementation simply breaks from a switch statement and leaks uninitialized return code rc' (if the first section is a security one) or the previous section's rc'.
Fix it by really skipping a loop with `continue'. For consistency and readability, also do the same for the default case.
--- drivers/bluetooth/btrtl.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
drivers/bluetooth/btrtl.c +2 -3
@@ -591,7 +591,7 @@ static int rtlbt_parse_firmware_v2(struc* headers. */ if (!key_id) -break ; +continue ; rc =btrtl_parse_section (hdev, btrtl_dev, opcode, ptr, section_len);break ;
@@ -600,8 +600,7 @@ static int rtlbt_parse_firmware_v2(struc ptr, section_len);break ;default : - rc =0 ; -break ; +continue ; }if (rc <0 ) { rtl_dev_err(hdev, "RTL: Parse section (%u) err %d",
Greg Kroah-Hartman
maintainerPATCH 832/1102 cpufreq: zero-initialize policy cpumask before sysfs publication
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhongqiu Han <[email protected]>
commit 54d37bcf2f497140b9207968557ddb484058e749 upstream.
cpufreq_policy_alloc() allocates policy->cpus with alloc_cpumask_var(), i.e. without __GFP_ZERO, unlike the sibling related_cpus and real_cpus masks. With CONFIG_CPUMASK_OFFSTACK=y the mask is a separate kmalloc_node() allocation, so its bitmap holds whatever the slab allocator left behind:
cpufreq_online()
cpufreq_policy_alloc()
alloc_cpumask_var(&policy->cpus) /* bitmap is uninitialized */
kobject_init_and_add() /* policy%u/ appears in sysfs */
cpufreq_policy_online()
cpumask_copy(policy->cpus, cpumask_of(cpu)) /* first valid value */This leaves a window in which the sysfs attributes are already reachable while policy->cpus is still garbage. show()/store() gate on policy_is_inactive(), i.e. cpumask_empty(policy->cpus), so a non-zero bitmap makes them run the attribute callbacks on a policy that is not initialized yet.
Fix this by using zalloc_cpumask_var() for policy->cpus.
--- drivers/cpufreq/cpufreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/cpufreq/cpufreq.c +1 -1
@@ -1252,7 +1252,7 @@ static struct cpufreq_policy *cpufreq_poif (!policy)return NULL ; -if (!alloc_cpumask_var (&policy->cpus,GFP_KERNEL )) +if (!zalloc_cpumask_var (&policy->cpus,GFP_KERNEL )) goto err_free_policy;if (!zalloc_cpumask_var (&policy->related_cpus,GFP_KERNEL ))
Greg Kroah-Hartman
maintainerPATCH 833/1102 cpufreq: initialize policy rwsem before sysfs publication
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Runyu Xiao <[email protected]>
commit 3e5d1bf4bd687beb2cb4e32a07af695455925588 upstream.
cpufreq_policy_alloc() initializes policy->rwsem after kobject_init_and_add() has created the policy sysfs directory and its default attributes. A sysfs access can therefore reach a policy callback before the semaphore has been initialized.
Initialize policy->rwsem before publishing the policy kobject so sysfs callbacks always see an initialized semaphore.
--- drivers/cpufreq/cpufreq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/cpufreq/cpufreq.c +2 -2
@@ -1261,6 +1261,8 @@ static struct cpufreq_policy *cpufreq_poif (!zalloc_cpumask_var (&policy->real_cpus,GFP_KERNEL )) goto err_free_rcpumask; +init_rwsem (&policy->rwsem); +init_completion (&policy->kobj_unregister); ret =kobject_init_and_add (&policy->kobj, &ktype_cpufreq, cpufreq_global_kobject,"policy%u" , cpu);
@@ -1275,8 +1277,6 @@ static struct cpufreq_policy *cpufreq_po goto err_free_real_cpus; } -init_rwsem (&policy->rwsem); -freq_constraints_init (&policy->constraints); policy->nb_min.notifier_call = cpufreq_notifier_min;
Greg Kroah-Hartman
maintainerPATCH 834/1102 exec: do_close_on_exec() before taking exec_update_lock
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jann Horn <[email protected]>
commit e780259b54e618ceb4763fbc21314acf3565e813 upstream.
do_close_on_exec() currently happens while holding the exec_update_lock, which is used in a lot of places that access process state to synchronize access checks.
I recently added another such use of exec_update_lock, causing a regression.
do_close_on_exec() can block waiting for a reply from a filesystem. That means a hung filesystem can block codepaths that use exec_update_lock; and it also means that a FUSE filesystem which attempts to inspect the calling process can deadlock.
To avoid such problems, move do_close_on_exec() before the exec_update_lock is taken, but after the FD table has been copied if necessary.
I have looked through all the calls between the old and new position of the do_close_on_exec() call; there seems to be no file descriptor table access in between.
--- fs/exec.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-)
fs/exec.c +14 -8
@@ -1263,6 +1263,20 @@ int begin_new_exec(struct linux_binprm * goto out;/* +* We have to apply CLOEXEC before we change whether the process is +* dumpable (in setup_new_exec) to avoid a race with a process in userspace +* trying to access the should-be-closed file descriptors of a process +* undergoing exec(2). +* +* This can block on filesystem ->flush() handlers, including waiting +* for FUSE daemons, so do it before exec_mmap takes the +* exec_update_lock. +* This must happen after the point of no return, and after unsharing +* the FD table. +*/ +do_close_on_exec (me->files); + +/* * Must be called _before_ exec_mmap() as bprm->mm is * not visible until then. Doing it here also ensures * we don't race against replace_mm_exe_file().
@@ -1312,14 +1326,6 @@ int begin_new_exec(struct linux_binprm *clear_syscall_work_syscall_user_dispatch (me); -/* -* We have to apply CLOEXEC before we change whether the process is -* dumpable (in setup_new_exec) to avoid a race with a process in userspace -* trying to access the should-be-closed file descriptors of a process -* undergoing exec(2). -*/ -do_close_on_exec (me->files); -if (bprm->secureexec) {/* Make sure parent cannot signal privileged process. */ me->pdeath_signal =0 ;
Greg Kroah-Hartman
maintainerPATCH 835/1102 fs: dont return -EINVAL for successful nested thaw
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Moritz Tanner <[email protected]>
commit fe967191e5851ea79818c5fe4e781c3882139218 upstream.
Commit 7366f8b6fc6a ("fs: handle freezing from multiple devices") replaced the freeze_holders bitmask with per-holder counters to allow nested freezes. In the bitmask version, a thaw that released a shared hold while another holder remained returned 0. Since the rework, thaw_super_locked() drops the freeze reference via freeze_dec() but then returns -EINVAL when other freezers remain, misinforming the
caller: the thaw did succeed, the superblock just stays frozen for the remaining holders.
This breaks bdev-initiated freezing. When a filesystem is frozen with FIFREEZE and additionally frozen via bdev_freeze() -- which nests by design, see fs_bdev_freeze() -- the subsequent bdev_thaw() receives -EINVAL from the holder op although its freeze reference was dropped, and therefore keeps bd_fsfreeze_count elevated. Then device-mapper's unlock_fs() ignores bdev_thaw()'s return value, so nothing rebalances the count. After the user's FITHAW and umount, the block device can never be mounted again:
dm-1: Can't mount, blockdev is frozen
There is no way for userspace to drop the leaked count; only destroying the block device (or a reboot) recovers the device.
Reproducer (any kernel since v6.8):
dmsetup create dut --table "0 $(blockdev --getsz "$DEV") linear $DEV 0"
mkfs.ext4 /dev/mapper/dut
mount /dev/mapper/dut /mnt
fsfreeze --freeze /mnt # freeze_ucount == 1
dmsetup suspend dut # bd_fsfreeze_count == 1, ucount == 2
dmsetup resume dut # ucount 2 -> 1, but thaw_super()
# returns -EINVAL, so bdev_thaw()
# keeps bd_fsfreeze_count at 1
fsfreeze --unfreeze /mnt # filesystem thaws fine
umount /mnt
mount /dev/mapper/dut /mnt # EBUSY, foreverThe same happens with fsfreeze held across an LVM snapshot of the origin volume.
fs_bdev_thaw()'s documentation already describes the intended
semantics: "If this function returns zero it doesn't mean that the filesystem is unfrozen as it may have been frozen multiple times". Restore them by returning 0 when a nested thaw drops its hold while other freezers remain. Thawing without holding a freeze still fails with -EINVAL as may_unfreeze() rejects that case before the reference count is touched.
--- fs/super.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
fs/super.c +6 -3
@@ -2163,11 +2163,14 @@ static int thaw_super_locked(struct supe goto out_unlock;/* -* All freezers share a single active reference. -* So just unlock in case there are any left. +* All freezers share a single active reference. If other freezers +* remain, drop our hold and report success; the superblock stays +* frozen until the last holder thaws it. */ -if (freeze_dec (sb, who)) +if (freeze_dec (sb, who)) { + error =0 ; goto out_unlock; + }if (sb_rdonly (sb)) { sb->s_writers.frozen =SB_UNFROZEN ;
Greg Kroah-Hartman
maintainerPATCH 836/1102 drm/drm_exec: fix up contended obj when num_objects is 0
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sunil Khatri <[email protected]>
commit 159720704d9d652b64390c11fb971e15b0a78d23 upstream.
drm_exec_prepare_array() silently returns success without calling drm_exec_lock_contended() when num_objects is zero. This breaks the invariant upheld by drm_exec_lock_obj(), where every entry point into the locking sequence must first attempt to lock any previously contended object before proceeding.
Drivers that chain multiple drm_exec_prepare_array() calls per drm_exec_until_all_locked() iteration (e.g. amdgpu's userq signal/wait ioctls, which prepare separate read and write BO arrays) can pass an empty array for one of the two calls. If contention is hit while preparing the non-empty array, exec->contended is set and the loop retries; on retry, the empty-array call preceding it is a no-op that never clears exec->contended, so drm_exec_retry_on_contention() immediately jumps back to the top of the loop without ever reaching the call that would resolve the contention. This spins forever.
Fix it by having drm_exec_prepare_array() call drm_exec_lock_contended() directly when num_objects is zero, so a pending contended object dont loop infinitely.
--- drivers/gpu/drm/drm_exec.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
drivers/gpu/drm/drm_exec.c +13 -0
@@ -323,6 +323,19 @@ int drm_exec_prepare_array(struct drm_ex {int ret; +/* +* Make sure to lock a contended object even when no objects are +* given, otherwise drm_exec_retry_on_contention() would loop +* forever on patterns like: +* +* ret = drm_exec_prepare_array(exec, objs, num_objects, ...); +* drm_exec_retry_on_contention(exec); +* +* with num_objects == 0. +*/ +if (!num_objects) +return drm_exec_lock_contended (exec); +for (unsigned int i =0 ; i < num_objects; ++i) { ret =drm_exec_prepare_obj (exec, objects[i], num_fences);if (unlikely (ret))
Greg Kroah-Hartman
maintainerPATCH 837/1102 drm/i915: Fix memory leak in query_perf_config_list()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thorsten Blum <[email protected]>
commit cbd3dafc2003db679ccd2f6c6a2551db79657049 upstream.
When krealloc() fails, free the original oa_config_ids before returning to avoid a memory leak.
--- drivers/gpu/drm/i915/i915_query.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/gpu/drm/i915/i915_query.c +3 -1
@@ -401,8 +401,10 @@ static int query_perf_config_list(struct ids =krealloc (oa_config_ids, n_configs *sizeof (*oa_config_ids),GFP_KERNEL ); -if (!ids) +if (!ids) { +kfree (oa_config_ids);return -ENOMEM ; + } alloc =fetch_and_zero (&n_configs);
Greg Kroah-Hartman
maintainerPATCH 838/1102 io_uring/net: let io_recv_buf_select return the length of the buffer region
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gabriel Krisman Bertazi <[email protected]>
commit dcbd1c054848848a1937ca0768ce2bdbc31ae621 upstream.
In preparation to using this field as an upper limit to truncation, return the size of the allocated region.
--- io_uring/net.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
io_uring/net.c +6 -4
@@ -1104,6 +1104,7 @@ static int io_recv_buf_select(struct io_ struct io_br_sel *sel, unsigned int issue_flags) { struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg); +size_t len;int ret;/*
@@ -1145,13 +1146,14 @@ static int io_recv_buf_select(struct io_/* special case 1 vec, can be a fast path */ if (ret ==1 ) { sr->buf = arg.iovs[0 ].iov_base; - sr->len = arg.iovs[0 ].iov_len; + len = sr->len = arg.iovs[0 ].iov_len; goto map_ubuf; }iov_iter_init (&kmsg->msg.msg_iter,ITER_DEST , arg.iovs, ret, - arg.out_len); + arg.out_len); + len = arg.out_len; }else { -size_t len = sel->val; + len = sel->val; *sel =io_buffer_select (req, &len, issue_flags);if (!sel->addr)
@@ -1165,7 +1167,7 @@ map_ubuf:return ret; } -return 0 ; +return len; }int io_recv (struct io_kiocb *req,unsigned int issue_flags)
Greg Kroah-Hartman
maintainerPATCH 839/1102 ring-buffer: Acquire the lock with irqsave in rb_wake_up_waiters()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sebastian Andrzej Siewior <[email protected]>
commit 815e07c8fe885a87751c2496a30ae0dcd4118210 upstream.
rb_wake_up_waiters() is a irq_work callback which is initialized with init_irq_work(). As such it will be invoked in thread context on PREEMPT_RT. Invoking the callback in IRQ context on PREEMPT_RT is not an option due its usage of wake_up_all(). Since this callback may run in thread context, it needs to acquire ring_buffer_per_cpu::reader_lock with disabling interrupts and may not assume that they are disabled.
Use raw_spinlock_irqsave() to acquire ring_buffer_per_cpu::reader_lock.
--- kernel/trace/ring_buffer.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-)
kernel/trace/ring_buffer.c +6 -7
@@ -837,14 +837,13 @@ static void rb_wake_up_waiters(struct irstruct ring_buffer_per_cpu *cpu_buffer = container_of(rbwork, struct ring_buffer_per_cpu, irq_work); -/* Called from interrupt context */ -raw_spin_lock (&cpu_buffer->reader_lock); - rbwork->wakeup_full = false; - rbwork->full_waiters_pending = false; + scoped_guard(raw_spinlock_irqsave, &cpu_buffer->reader_lock) { + rbwork->wakeup_full = false; + rbwork->full_waiters_pending = false; -/* Waking up all waiters, they will reset the shortest full */ - cpu_buffer->shortest_full =0 ; -raw_spin_unlock (&cpu_buffer->reader_lock); +/* Waking up all waiters, they will reset the shortest full */ + cpu_buffer->shortest_full =0 ; + }wake_up_all (&rbwork->full_waiters); }
Greg Kroah-Hartman
maintainerPATCH 840/1102 ring-buffer: Check resize_disabled before publishing the new subbuf order
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Carlier <[email protected]>
commit d860c67c051685abb0460b593b193f0f45f4fa92 upstream.
ring_buffer_subbuf_order_set() stores the new order and only then walks the CPUs, returning -EBUSY if any of them has resizing disabled. A user mapped buffer has resizing disabled, and __rb_map_vma() reads buffer->subbuf_order without buffer->mutex, so an mmap of an already mapped CPU racing the failing order change sizes the mapping with the new order and inserts pages past the sub-buffer into the VMA.
Check the CPUs before storing the new order.
--- kernel/trace/ring_buffer.c | 8 ++++++++ 1 file changed, 8 insertions(+)
kernel/trace/ring_buffer.c +8 -0
@@ -6778,6 +6778,14 @@ int ring_buffer_subbuf_order_set(struct old_capacity =rb_subbuf_capacity (buffer); +/* The mmap fast path reads subbuf_order without buffer->mutex. */ +for_each_buffer_cpu (buffer, cpu) { +if (!cpumask_test_cpu (cpu, buffer->cpumask)) +continue ; +if (atomic_read (&buffer->buffers[cpu]->resize_disabled)) +return -EBUSY ; + } +atomic_inc (&buffer->record_disabled);/* Make sure all commits have finished */
Greg Kroah-Hartman
maintainerPATCH 841/1102 net/sched: act_api: release all action references on NEWACTION failure
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xuanqiang Luo <[email protected]>
commit 478eb5abb51931a152abab068f8a717b7ff480fd upstream.
When a batched RTM_NEWACTION request replaces an existing action, tcf_idr_check_alloc() takes a temporary reference on it. If a later action fails to initialize, tcf_action_destroy() uses strict release semantics to clean up the actions initialized so far. For an action bound to a filter, the strict check returns -EPERM without dropping the temporary reference.
This error also makes tcf_action_destroy() return before releasing subsequent entries. Any new action initialized between the bound action and the failing entry is leaked together with its reserved IDR slot, preventing reuse of its index.
Use tcf_idr_release() to drop each reference held by the batch without rejecting bound actions. This allows cleanup to continue through all initialized entries and preserves the module reference release when an action is destroyed. Explicit action deletion and flushing retain their separate bind-count checks.
--- net/sched/act_api.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-)
net/sched/act_api.c +2 -7
@@ -1147,18 +1147,13 @@ EXPORT_SYMBOL(tcf_action_exec);int tcf_action_destroy (struct tc_action *actions[],int bind) { -const struct tc_action_ops *ops;struct tc_action *a;int ret =0 , i;tcf_act_for_each_action (i, a, actions) { actions[i] =NULL ; - ops = a->ops; - ret =__tcf_idr_release (a, bind, true); -if (ret ==ACT_P_DELETED ) -module_put (ops->owner); -else if (ret <0 ) -return ret; +/* Drop our reference even if the action is still bound to a filter. */ + ret =tcf_idr_release (a, bind); }return ret; }
Greg Kroah-Hartman
maintainerPATCH 842/1102 net: hso: fix TIOCMIWAIT race
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 00f9fbc12320253bfc576fb7539d860029c82d0f upstream.
The task state must be updated before checking the wakeup condition to avoid missing a racing modem status update.
--- drivers/net/usb/hso.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/usb/hso.c +1 -1
@@ -1549,10 +1549,10 @@ hso_wait_modem_status(struct hso_serialspin_unlock_irq (&serial->serial_lock);add_wait_queue (&tiocmget->waitq, &wait);for (;;) { +set_current_state (TASK_INTERRUPTIBLE );spin_lock_irq (&serial->serial_lock);memcpy (&cnow, &tiocmget->icount,sizeof (struct uart_icount ));spin_unlock_irq (&serial->serial_lock); -set_current_state (TASK_INTERRUPTIBLE );if (((arg &TIOCM_RNG ) && (cnow.rng != cprev.rng)) || ((arg &TIOCM_DSR ) && (cnow.dsr != cprev.dsr)) || ((arg &TIOCM_CD ) && (cnow.dcd != cprev.dcd))) {
Greg Kroah-Hartman
maintainerPATCH 843/1102 net: mana: Reserve extra CQ slot for the fence completion CQE
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sahil Chandna <[email protected]>
commit 80dd7e754b3aa9637a0758ad93fa209f9650ec48 upstream.
The RX completion queue is sized to hold exactly one CQE per posted RX WQE. MANA_FENCE_RQ makes hardware post an additional CQE_RX_OBJECT_FENCE after the packet CQEs. The current sizing reserves no extra slot for it and in rare cases, CQ has no guaranteed slot for the fence CQE when it is full of packet CQEs. This can lead to dropping the fence completion while the driver waits holding RTNL lock throughout the timeout duration. Reserve one extra CQE slot for CQE_RX_OBJECT_FENCE. mana_gd_alloc_memory() requires queue_size to be a power-of-two and at least MANA_PAGE_SIZE; the reservation pushes cq_size past a power-of-two, so round up the CQ size in mana_create_rxq().
--- drivers/net/ethernet/microsoft/mana/mana_en.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/net/ethernet/microsoft/mana/mana_en.c +5 -1
@@ -2145,6 +2145,10 @@ static int mana_alloc_rx_wqe(struct mana *cq_size +=COMP_ENTRY_SIZE ; } +/* Reserve an extra slot for Fence completion +* event (CQE_RX_OBJECT_FENCE) in case RX CQ is full. +*/ + *cq_size +=COMP_ENTRY_SIZE ;return 0 ; }
@@ -2230,7 +2234,7 @@ static struct mana_rxq *mana_create_rxq( goto out; rq_size =MANA_PAGE_ALIGN (rq_size); - cq_size =MANA_PAGE_ALIGN (cq_size); + cq_size =MANA_PAGE_ALIGN (roundup_pow_of_two (cq_size));/* Create RQ */ memset (&spec,0 ,sizeof (spec));
Greg Kroah-Hartman
maintainerPATCH 844/1102 net: mpls: clear inner_protocol when the last label is popped
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fourie Zhang <[email protected]>
commit 78a86d75a70e1e227711c72865c59b1422d0a5ae upstream.
skb_mpls_push() records the pre-encapsulation network header once, gated on !skb->inner_protocol. skb_mpls_pop() never clears that record, so it outlives the encapsulation it describes.
Open vSwitch can then re-push MPLS onto a packet whose inner_network_header still points at the older, deeper offset: push a label, pop every label, recirculate (ovs_flow_key_update() re-derives key->eth.type and resets network_header, but leaves inner_*), then push again. ovs_fragment() trusts the record:
skb->network_header = skb->inner_network_header;
so skb_network_offset() goes negative. The bound check is signed:
if (skb_network_offset(skb) > MAX_L2_LEN)
a negative offset passes it, and prepare_frag() widens the value:
unsigned int hlen = skb_network_offset(skb); memcpy(&data->l2_data, skb->data, hlen);
which is a ~4GiB memcpy out of a 30-byte per-CPU buffer.
Reproduced on v7.3-rc1. RDX is the truncated length, (unsigned int)(-8):
BUG: unable to handle page fault for address: ffffe8ffffc16000 #PF: supervisor write access in kernel mode Oops: 0002 [#1] SMP KASAN NOPTI RIP: 0010:memcpy+0x8/0x20 RDX: 00000000fffffff8 RSI: ffff888105d732db RDI: ffffe8ffffc16000 prepare_frag+0x3df/0x4e0 ovs_fragment+0x589/0x7e0 do_output+0x4ce/0x5e0 do_execute_actions+0x55d2/0x7b30 ovs_execute_actions+0xea/0x450
Same root-cause shape as commit 975b5b067f52 ("ipv6: sr: restore network header before routing and forwarding"): a stale network header offset reaching a consumer that widens it. Here it originates in the MPLS push/pop path.
Clear inner_protocol once the packet is no longer MPLS, so a later push re-records the current header. net/sched/act_mpls.c is the only other skb_mpls_pop() caller and gets the same fix; sch_frag.c saves and restores inner_protocol around fragmentation in the same way OVS does.
--- net/core/skbuff.c | 7 +++++++ 1 file changed, 7 insertions(+)
net/core/skbuff.c +7 -0
@@ -6554,6 +6554,13 @@ int skb_mpls_pop(struct sk_buff *skb, __ } skb->protocol = next_proto; +/* The last label is gone, so the inner header recorded by +* skb_mpls_push() no longer describes this packet. Drop it, or a +* later push keeps the stale offset. +*/ +if (!eth_p_mpls (next_proto)) + skb->inner_protocol =0 ; +return 0 ; }EXPORT_SYMBOL_GPL (skb_mpls_pop);
Greg Kroah-Hartman
maintainerPATCH 845/1102 net: openvswitch: fix use-after-free of the flow table mask array
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Norbert Szetei <[email protected]>
commit ba4ba11ed6eb8972c69070417fc27b48deb002e8 upstream.
tbl_mask_array_realloc() retires the old mask_array before it stops being reachable:
old = ovsl_dereference(tbl->mask_array);
if (old) {
...
call_rcu(&old->rcu, mask_array_rcu_cb);
}
rcu_assign_pointer(tbl->mask_array, new);call_rcu() only waits for read-side critical sections already in flight. tbl->mask_array still points at old between the call_rcu() and the rcu_assign_pointer(), so a reader entering ovs_flow_tbl_lookup_stats() in that window picks up old in a fresh critical section that the pending grace period does not cover.
tbl_mask_array_realloc() runs in process context under ovs_mutex, so the window is preemptible and can outlast the grace period. Then mask_array_rcu_cb() frees old before the swap runs:
BUG: KASAN: slab-use-after-free in flow_lookup.constprop.0+0x2bf/0x2f0 Read of size 8 at addr ffff888020b3e018 by task poc/741 flow_lookup.constprop.0+0x2bf/0x2f0 ovs_flow_tbl_lookup_stats+0x4a3/0x5c0 ovs_dp_process_packet+0x19c/0x710 ovs_vport_receive+0x243/0x390 internal_dev_xmit+0x81/0x170 Freed by task 728: kfree+0x16a/0x4e0 rcu_core+0x853/0x1030
Publish the new array before retiring the old one. The kfree_rcu() that call_rcu() replaced ran after the swap.
--- net/openvswitch/flow_table.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
net/openvswitch/flow_table.c +3 -1
@@ -259,11 +259,13 @@ static int tbl_mask_array_realloc(structif (ovsl_dereference (old->masks[i])) new->masks[new->count++] = old->masks[i]; } -call_rcu (&old->rcu, mask_array_rcu_cb); }rcu_assign_pointer (tbl->mask_array, new); +if (old) +call_rcu (&old->rcu, mask_array_rcu_cb); +return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 846/1102 netfilter: nf_log: unregister loggers before per-net teardown
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chengfeng Ye <[email protected]>
commit 2c018cc4842c33f0c732962e2ab58635e8ae5823 upstream.
nf_log_syslog and nfnetlink_log unregister their per-network namespace operations before unregistering their global logger backends. This leaves a window where a sysctl or netlink writer can rebind the still- registered logger after the per-net pre-exit callback cleared the old selection.
The race looks like this:
CPU 0 CPU 1
---- ----
unregister_pernet_subsys()
nf_log_unset(net, logger)
net->nf.nf_loggers[pf] = NULL
lock nf_log_mutex
find logger in loggers[][]
net->nf.nf_loggers[pf] = logger
unlock nf_log_mutex nf_log_unregister(logger)
lock nf_log_mutex
loggers[pf][type] = NULL
unlock nf_log_mutex
synchronize_rcu()
module exit returns
module core frees backend memory
Later, a sysctl read or packet logging operation can dereference the stale per-net logger pointer.
Fix this by unregistering the global logger backends before tearing down per-net state. Once the global registrations are gone, later writers can no longer rebind the logger. unregister_pernet_subsys() already waits for an RCU grace period after the pre-exit callback clears the per-net selection, while nf_log_unregister() continues to cover readers of the global logger table.
Apply this ordering fix to both nf_log backends that combine per-net teardown with global logger registration.
--- net/netfilter/nf_log_syslog.c | 2 +- net/netfilter/nfnetlink_log.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
net/netfilter/nf_log_syslog.c +1 -1
@@ -1063,12 +1063,12 @@ err1: static void __exit nf_log_syslog_exit(void) { -unregister_pernet_subsys (&nf_log_syslog_net_ops);nf_log_unregister (&nf_ip_logger);nf_log_unregister (&nf_arp_logger);nf_log_unregister (&nf_ip6_logger);nf_log_unregister (&nf_netdev_logger);nf_log_unregister (&nf_bridge_logger); +unregister_pernet_subsys (&nf_log_syslog_net_ops); }module_init (nf_log_syslog_init);
net/netfilter/nfnetlink_log.c +1 -1
@@ -1228,8 +1228,8 @@ static void __exit nfnetlink_log_fini(vo {nfnetlink_subsys_unregister (&nfulnl_subsys);netlink_unregister_notifier (&nfulnl_rtnl_notifier); -unregister_pernet_subsys (&nfnl_log_net_ops);nf_log_unregister (&nfulnl_logger); +unregister_pernet_subsys (&nfnl_log_net_ops); }MODULE_DESCRIPTION ("netfilter userspace logging" );
Greg Kroah-Hartman
maintainerPATCH 847/1102 netfilter: report NLM_F_DUMP_FILTERED when all is filtered out
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilya Maximets <[email protected]>
commit 7a099b347fef536a84068076e2d384f044e5cfc5 upstream.
NLM_F_DUMP_FILTERED is only set on data elements in the conntrack dump. But when everything is filtered out it is confusing for the user space, since the flag is not reported anymore and it looks like the table was empty, which may or may not be the case.
'answer_flags' were introduced precisely for this use case, and the conntrack dump should set the flag in there in case the filtering was applied.
This is important, for example, to be able to tell if the filters are supported or not by the kernel without modifying the kernel state.
With the proper reporting of NLM_F_DUMP_FILTERED on NLMSG_DONE, an application in user space can just try and dump with an arbitrary filter without worrying that there could be no matching entry. The reported flag will signal that the filtering was applied and therefore supported.
--- net/netfilter/nf_conntrack_netlink.c | 2 ++ 1 file changed, 2 insertions(+)
net/netfilter/nf_conntrack_netlink.c +2 -0
@@ -1053,6 +1053,8 @@ static int ctnetlink_start(struct netlin } cb->data = filter; +if (filter) + cb->answer_flags =NLM_F_DUMP_FILTERED ;return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 848/1102 vdpa: ifcvf: Put device on unsupported feature error
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiong Weimin <[email protected]>
commit 4d470be71196ca0ce302e6623454533dc31b465b upstream.
Route unsupported provisioned features through the common error path after vdpa_alloc_device() so the allocated device and adapter pointer are released consistently.
Message-ID: <[email protected]>
--- drivers/vdpa/ifcvf/ifcvf_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/vdpa/ifcvf/ifcvf_main.c +2 -1
@@ -723,7 +723,8 @@ static int ifcvf_vdpa_dev_add(struct vdpif (config->device_features & ~device_features) {IFCVF_ERR (pdev,"The provisioned features 0x%llx are not supported by this device with features 0x%llx\n" , config->device_features, device_features); -return -EINVAL ; + ret = -EINVAL ; + goto err; } device_features &= config->device_features; }
Greg Kroah-Hartman
maintainerPATCH 849/1102 vdpa: solidrun: Free IRQs after request failure
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiong Weimin <[email protected]>
commit e847542ab0545c73354849126150206c29d83929 upstream.
Unwind IRQs already requested by snet_request_irqs() before returning a VQ IRQ request error so a later DRIVER_OK retry starts from a clean state. The IRQs are requested and freed while the PCI device remains bound, so the driver cannot wait for devres cleanup at detach time.
Message-ID: <[email protected]>
--- drivers/vdpa/solidrun/snet_main.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/vdpa/solidrun/snet_main.c +5 -1
@@ -418,11 +418,15 @@ static int snet_request_irqs(struct pci_ snet->vqs[i]->irq_name, snet->vqs[i]);if (ret) {SNET_ERR (pdev,"Failed to request IRQ\n" ); -return ret; + goto err_free_irqs; } snet->vqs[i]->irq = irq; }return 0 ; + +err_free_irqs: +snet_free_irqs (snet); +return ret; }static void snet_set_status (struct vdpa_device *vdev,u8 status)
Greg Kroah-Hartman
maintainerPATCH 850/1102 scripts/sorttable: Mark long_size as __maybe_unused
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nathan Chancellor <[email protected]>
commit 4f73462856576797b8f3c55564a9be99f76dc67b upstream.
When building in a kernel tree prior to commit b055f4c431e3 ("sorttable:
Move ELF parsing into scripts/elf-parse.[ch]") with clang-23 or newer, which implements a new warning under -Wunused-but-set-variable for static global variable, there is a warning from sorttable because long_size is unused when MCOUNT_SORT_ENABLED is not set:
scripts/sorttable.c:452:12: error: variable 'long_size' set but not used [-Werror,-Wunused-but-set-global]
452 | static int long_size;
| ^Mark long_size as __maybe_unused to avoid inserting more ugly #ifdef directives while insuring the warning does not reappear, as the aforementioned change does not alter the uses of long_size, so it appears to be coincidence that the warning disappears after this refactoring.
--- scripts/sorttable.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
scripts/sorttable.c +1 -1
@@ -449,7 +449,7 @@ static inline void *get_index(void *star }static int extable_ent_size; -static int long_size; +static int long_size__maybe_unused ;#define ERRSTR_MAXSZ 256
Greg Kroah-Hartman
maintainerPATCH 851/1102 fs: autofs: fix memory leak in autofs_fill_super()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeffin Philip <[email protected]>
commit 5ab54837fce04a1c9923d0bfd3d5de51fdc768b3 upstream.
In autofs_fill_super(), we create a new inode using autofs_new_ino(), however, if we fail to create root_inode, (that is, root_inode failure path), we return -ENOMEM without freeing the new inode(ino) that we created causing a memory leak. Fix this by adding autofs_free_ino() to free the inode we created in root_inode failure path before returning ENOMEM.
--- fs/autofs/inode.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
fs/autofs/inode.c +3 -1
@@ -322,8 +322,10 @@ static int autofs_fill_super(struct supereturn -ENOMEM ; root_inode =autofs_get_inode (s,S_IFDIR |0755 ); -if (!root_inode) +if (!root_inode) { +autofs_free_ino (ino);return -ENOMEM ; + } root_inode->i_uid = ctx->uid; root_inode->i_gid = ctx->gid;
Greg Kroah-Hartman
maintainerPATCH 852/1102 erofs: preserve LZMA decoders on resize failure
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nikhil Gurudasani <[email protected]>
commit 617d0d8d199ba1790c94310fd75a22d01c97a8d6 upstream.
The pool-resize path frees each stream's old decoder before allocating its replacement. If an allocation fails after some streams have already been replaced, the failed stream is put back on the list with state == NULL. z_erofs_lzma_max_dictsize is still advanced as if the whole pool had been resized.
An existing LZMA mount can select the broken stream and pass NULL to xz_dec_microlzma_reset(). A retry at the same size also skip another resize attempt. Since the global maximum was advanced, thus, the invalid state is left unrepaired.
Allocate each replacement before freeing the old decoder, temporarily retaining one old decoder during allocation. Stop at the first failure and advance z_erofs_lzma_max_dictsize only after all streams satisfy the request.
Record each stream's dictionary capacity so retries can skip streams already enlarged before a partial failure.
--- fs/erofs/decompressor_lzma.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-)
fs/erofs/decompressor_lzma.c +14 -4
@@ -5,6 +5,7 @@struct z_erofs_lzma {struct z_erofs_lzma *next;struct xz_dec_microlzma *state; +unsigned int dict_size;u8 bounce[PAGE_SIZE ]; };
@@ -128,11 +129,19 @@ again: err =0 ;/* 2. walk each isolated stream and grow max dict_size if needed */ for (strm = head; strm; strm = strm->next) { +struct xz_dec_microlzma *state; + +if (strm->dict_size >= dict_size) +continue ; + state =xz_dec_microlzma_alloc (XZ_PREALLOC , dict_size); +if (!state) { + err = -ENOMEM ; +break ; + }if (strm->state)xz_dec_microlzma_end (strm->state); - strm->state =xz_dec_microlzma_alloc (XZ_PREALLOC , dict_size); -if (!strm->state) - err = -ENOMEM ; + strm->state = state; + strm->dict_size = dict_size; }/* 3. push back all to the global list and update max dict_size */
@@ -142,7 +151,8 @@ again:spin_unlock (&z_erofs_lzma_lock);wake_up_all (&z_erofs_lzma_wq); - z_erofs_lzma_max_dictsize = dict_size; +if (!err) + z_erofs_lzma_max_dictsize = dict_size;mutex_unlock (&lzma_resize_mutex);return err; }
Greg Kroah-Hartman
maintainerPATCH 853/1102 fbdev: vfb: defer cleanup until the last reference
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
commit a0a34a40ed299c9c7cff6af163a5b883ee9d6d73 upstream.
FBIOGETCMAP takes a shallow snapshot of info->cmap and performs the usercopy after dropping info->lock. vfb_remove() frees the colormap immediately after unregistering the framebuffer, even when an open file still holds a reference to fb_info. A concurrent driver unbind can therefore free the colormap while the ioctl copies it to userspace.
KASAN reports:
BUG: KASAN: slab-use-after-free in _copy_to_user Read of size 512 by task poc/125 _copy_to_user (./include/linux/instrumented.h:129 ./include/linux/uaccess.h:201 lib/usercopy.c:24) fb_cmap_to_user (./include/linux/uaccess.h:230 drivers/video/fbdev/core/fbcmap.c:211) do_fb_ioctl (drivers/video/fbdev/core/fb_chrdev.c:114) Allocated by task 1: fb_alloc_cmap_gfp (./include/linux/slab.h:973 ./include/linux/slab.h:1290 drivers/video/fbdev/core/fbcmap.c:108) vfb_probe (drivers/video/fbdev/vfb.c:459) Freed by task 124: fb_dealloc_cmap (drivers/video/fbdev/core/fbcmap.c:151) vfb_remove (drivers/video/fbdev/vfb.c:489)
unregister_framebuffer() drops the registration reference, and fbdev calls fb_destroy after the last put_fb_info(). Move the registered framebuffer's cleanup into an fb_destroy callback so its colormap and screen buffer stay alive until all file references have been released.
--- drivers/video/fbdev/vfb.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
drivers/video/fbdev/vfb.c +8 -3
@@ -78,6 +78,13 @@ static int vfb_pan_display(struct fb_varstatic int vfb_mmap (struct fb_info *info,struct vm_area_struct *vma); +static void vfb_destroy (struct fb_info *info) +{ +vfree (info->screen_buffer); +fb_dealloc_cmap (&info->cmap); +framebuffer_release (info); +} +static const struct fb_ops vfb_ops = { .owner =THIS_MODULE , __FB_DEFAULT_SYSMEM_OPS_RDWR,
@@ -87,6 +94,7 @@ static const struct fb_ops vfb_ops = { .fb_pan_display = vfb_pan_display, __FB_DEFAULT_SYSMEM_OPS_DRAW, .fb_mmap = vfb_mmap, + .fb_destroy = vfb_destroy, };/*
@@ -485,9 +493,6 @@ static void vfb_remove(struct platform_dif (info) {unregister_framebuffer (info); -vfree (videomemory); -fb_dealloc_cmap (&info->cmap); -framebuffer_release (info); } }
Greg Kroah-Hartman
maintainerPATCH 854/1102 inet: frags: invalidate queues before flushing them
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yilin Zhang <[email protected]>
commit b824476c56a153934c67c9e0f873e1fd967743d6 upstream.
fqdir_pre_exit() flushes the skbs from incomplete queues without changing their completion state. A fragment which found a queue before high_thresh was cleared can then acquire the queue lock and reuse stale reassembly metadata. A queue concurrently killed after fqdir->dead is set can instead become INET_FRAG_COMPLETE|INET_FRAG_HASH_DEAD while still holding its old skbs; skipping it because it is complete leaves those references behind until asynchronous fqdir teardown.
For IPv6, stale metadata can make ip6_frag_reasm() use the old nhoffset with a new skb and access memory out of bounds. The resulting heap corruption can be leveraged for local privilege escalation when unprivileged network namespaces are available. Unflushed fragments can also keep conntrack references alive after the conntrack per-net cleanup point.
Kill each incomplete queue, then flush every queue still owned by the dying rhashtable. HASH_DEAD identifies that ownership, while complete queues without it are already owned by another destroy path and must be left alone. Releasing a timer reference removed by inet_frag_kill() is deferred to inet_frag_putn(), after the queue lock is dropped.
KASAN report:
BUG: KASAN: slab-out-of-bounds in ipv6_frag_rcv (net/ipv6/reassembly.c:289 (discriminator 2) net/ipv6/reassembly.c:229 (discriminator 2) net/ipv6/reassembly.c:391 (discriminator 2)) Write of size 1 at addr ff110001039c6e00 by task poc/771 Call Trace: ? ipv6_frag_rcv (net/ipv6/reassembly.c:289 (discriminator 2) net/ipv6/reassembly.c:229 (discriminator 2) net/ipv6/reassembly.c:391 (discriminator 2)) ipv6_frag_rcv (net/ipv6/reassembly.c:289 (discriminator 2) net/ipv6/reassembly.c:229 (discriminator 2) net/ipv6/reassembly.c:391 (discriminator 2)) ip6_protocol_deliver_rcu (net/ipv6/ip6_input.c:479 (discriminator 5)) ip6_input_finish (net/ipv6/ip6_input.c:534) ipv6_rcv (include/net/dst.h:480 (discriminator 3) net/ipv6/ip6_input.c:119 (discriminator 3) net/ipv6/ip6_input.c:109 (discriminator 3) include/linux/netfilter.h:325 (discriminator 3) include/linux/netfilter.h:319 (discriminator 3) net/ipv6/ip6_input.c:351 (discriminator 3)) packet_sendmsg (net/packet/af_packet.c:3110 net/packet/af_packet.c:3142) __x64_sys_sendmmsg (net/socket.c:2883 net/socket.c:2880 net/socket.c:2880) The buggy address belongs to the object at ff110001039c6b40 which belongs to the cache skbuff_small_head of size 704 The buggy address is located 0 bytes to the right of allocated 704-byte region [ff110001039c6b40, ff110001039c6e00) BUG: KASAN: slab-out-of-bounds in ip6_protocol_deliver_rcu (net/ipv6/ip6_input.c:423 (discriminator 1)) Read of size 1 at addr ff110001039c6e08 by task poc/771 Call Trace: ? ip6_protocol_deliver_rcu (net/ipv6/ip6_input.c:423 (discriminator 1)) ip6_protocol_deliver_rcu (net/ipv6/ip6_input.c:423 (discriminator 1)) ip6_input_finish (net/ipv6/ip6_input.c:534) ipv6_rcv (include/net/dst.h:480 (discriminator 3) net/ipv6/ip6_input.c:119 (discriminator 3) net/ipv6/ip6_input.c:109 (discriminator 3) include/linux/netfilter.h:325 (discriminator 3) include/linux/netfilter.h:319 (discriminator 3) net/ipv6/ip6_input.c:351 (discriminator 3)) packet_sendmsg (net/packet/af_packet.c:3110 net/packet/af_packet.c:3142) __x64_sys_sendmmsg (net/socket.c:2883 net/socket.c:2880 net/socket.c:2880) packet_sendmsg (net/packet/af_packet.c:2959 net/packet/af_packet.c:3053 net/packet/af_packet.c:3142) __x64_sys_sendmmsg (net/socket.c:2883 net/socket.c:2880 net/socket.c:2880) The buggy address belongs to the object at ff110001039c6b40 which belongs to the cache skbuff_small_head of size 704 The buggy address is located 8 bytes to the right of allocated 704-byte region [ff110001039c6b40, ff110001039c6e00)
--- net/ipv4/inet_fragment.c | 6 ++++++ 1 file changed, 6 insertions(+)
net/ipv4/inet_fragment.c +6 -0
@@ -237,6 +237,8 @@ void fqdir_pre_exit(struct fqdir *fqdir)rhashtable_walk_start (&hti);while ((fq =rhashtable_walk_next (&hti))) { +int refs =0 ; +if (IS_ERR (fq)) {if (PTR_ERR (fq) != -EAGAIN )break ;
@@ -244,8 +246,12 @@ void fqdir_pre_exit(struct fqdir *fqdir) }spin_lock_bh (&fq->lock);if (!(fq->flags &INET_FRAG_COMPLETE )) +inet_frag_kill (fq, &refs); + +if (fq->flags &INET_FRAG_HASH_DEAD )inet_frag_queue_flush (fq,0 );spin_unlock_bh (&fq->lock); +inet_frag_putn (fq, refs); }rhashtable_walk_stop (&hti);
Greg Kroah-Hartman
maintainerPATCH 855/1102 ieee802154: 6lowpan: fix NULL dereference in lowpan_newlink
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhiling Zou <[email protected]>
commit bf79662bc85e820ac3b846e2f347da29fbf6ac95 upstream.
TUNSETLINK allows a TUN device to change its link-layer type to ARPHRD_IEEE802154 without initializing ieee802154_ptr. lowpan_newlink() checks only the device type before dereferencing the pointer, so an RTM_NEWLINK request can trigger a NULL pointer dereference.
Reject devices without ieee802154_ptr along with devices of the wrong type.
--- net/ieee802154/6lowpan/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/ieee802154/6lowpan/core.c +1 -1
@@ -146,7 +146,7 @@ static int lowpan_newlink(struct net *sr wdev =dev_get_by_index (dev_net (ldev),nla_get_u32 (tb[IFLA_LINK ]));if (!wdev)return -ENODEV ; -if (wdev->type !=ARPHRD_IEEE802154 ) { +if (wdev->type !=ARPHRD_IEEE802154 || !wdev->ieee802154_ptr) {dev_put (wdev);return -EINVAL ; }
Greg Kroah-Hartman
maintainerPATCH 856/1102 ieee802154: cc2520: fix FIFOP work use-after-free
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
commit ff5891b266a7fc6a062710836be84f1cc19338b5 upstream.
The FIFOP interrupt handler queues cc2520_fifop_irqwork. On removal, cc2520_remove() only flushes the work. The devm-managed FIFOP IRQ remains active until after ->remove() returns and can queue the work again after that flush, allowing it to run after the private data is released.
Disable the work with disable_work_sync() instead of flushing it, so the handler can no longer queue it once removal begins. Destroy the buffer mutex last, since the worker and the stop callback invoked through ieee802154_unregister_hw() both take it.
Found by an in-house static analysis tool.
--- drivers/net/ieee802154/cc2520.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
drivers/net/ieee802154/cc2520.c +2 -3
@@ -1156,11 +1156,10 @@ static void cc2520_remove(struct spi_dev {struct cc2520_private *priv =spi_get_drvdata (spi); -mutex_destroy (&priv->buffer_mutex); -flush_work (&priv->fifop_irqwork); - +disable_work_sync (&priv->fifop_irqwork);ieee802154_unregister_hw (priv->hw);ieee802154_free_hw (priv->hw); +mutex_destroy (&priv->buffer_mutex); }static const struct spi_device_id cc2520_ids[] = {
Greg Kroah-Hartman
maintainerPATCH 857/1102 ieee802154: hwsim: serialize pib updates to fix double-free
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Carlier <[email protected]>
commit 979d5b8de8ed4e1f997aef12da5694b99be7b871 upstream.
hwsim_update_pib() does an unserialized read-swap-free of phy->pib:
pib_old = rtnl_dereference(phy->pib); ... rcu_assign_pointer(phy->pib, pib); kfree_rcu(pib_old, rcu);
It assumes the RTNL is held, but ->set_channel is not always called under it: the mac802154 scan worker changes channels via drv_set_channel() without the RTNL. Such an update can race an RTNL-held one on the same phy; both read the same pib_old and both kfree_rcu() it, double-freeing the object. With SLUB percpu sheaves batching kfree_rcu(), this surfaces as a KASAN invalid-free in rcu_free_sheaf().
struct hwsim_phy has no lock for pib. Add one and make the swap atomic with rcu_replace_pointer() under it, dropping the misleading rtnl_dereference().
--- drivers/net/ieee802154/mac802154_hwsim.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
drivers/net/ieee802154/mac802154_hwsim.c +7 -3
@@ -72,6 +72,8 @@ struct hwsim_phy {struct ieee802154_hw *hw;u32 idx; +/* Serializes phy->pib_updates. */ +spinlock_t pib_lock;struct hwsim_pib __rcu *pib;bool suspended;
@@ -102,8 +104,6 @@ static int hwsim_update_pib(struct ieee8if (!pib)return -ENOMEM ; - pib_old =rtnl_dereference (phy->pib); - pib->page = page; pib->channel = channel; pib->filt.short_addr = filt->short_addr;
@@ -112,7 +112,10 @@ static int hwsim_update_pib(struct ieee8 pib->filt.pan_coord = filt->pan_coord; pib->filt_level = filt_level; -rcu_assign_pointer (phy->pib, pib); +spin_lock_bh (&phy->pib_lock); + pib_old =rcu_replace_pointer (phy->pib, pib, +lockdep_is_held (&phy->pib_lock)); +spin_unlock_bh (&phy->pib_lock);kfree_rcu (pib_old, rcu);return 0 ; }
@@ -952,6 +955,7 @@ static int hwsim_add_one(struct genl_inf goto err_pib; } +spin_lock_init (&phy->pib_lock); pib->channel =13 ; pib->filt.short_addr =cpu_to_le16 (IEEE802154_ADDR_BROADCAST ); pib->filt.pan_id =cpu_to_le16 (IEEE802154_PANID_BROADCAST );
Greg Kroah-Hartman
maintainerPATCH 858/1102 ipv4: fib: bound automatic table ID allocation
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zihan Xi <[email protected]>
commit efdfb1e27a3328085b79540dfe781d537b576ea1 upstream.
fib_empty_table() probes every table ID from 1 until it finds a free one. IPv4 tables are stored in a 256-bucket hash table, so a dense set of IDs makes each probe walk a growing hash chain while RTNL is held.
Automatic table assignment ("ip rule ... table 0") is an IPv4-only legacy path. Bound the automatically allocated ID to 4096 so the RTNL hold stays bounded, without changing lookups of explicitly specified table IDs.
This changes user-visible behavior. A table-0 rule previously received the lowest free ID in 1..RT_TABLE_MAX (0xFFFFFFFF). After this patch the search stops at 4096 and the rule add fails with ENOBUFS if that range is fully occupied. Explicit table IDs above 4096 remain usable.
The automatic path is unused in practice: it is IPv4-only, not documented by ip-rule, uncovered by kernel selftests, and both NetworkManager and systemd refuse table 0.
--- net/ipv4/fib_rules.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
net/ipv4/fib_rules.c +3 -1
@@ -212,6 +212,8 @@ INDIRECT_CALLABLE_SCOPE int fib4_rule_mareturn 1 ; } +#define FIB_MAX_AUTO_TABLE_ID 4096 +static struct fib_table *fib_empty_table (struct net *net) {u32 id =1 ;
@@ -220,7 +222,7 @@ static struct fib_table *fib_empty_tableif (!fib_get_table (net, id))return fib_new_table (net, id); -if (id++ ==RT_TABLE_MAX ) +if (id++ ==FIB_MAX_AUTO_TABLE_ID )break ; }return NULL ;
Greg Kroah-Hartman
maintainerPATCH 859/1102 ipvs: reject invalid states in connection template sync records
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kyle Zeng <[email protected]>
commit 74cb39735b6cd0aff4b5584158f09376fd97aadf upstream.
IPVS sync receivers validate protocol states before creating or updating a connection. For connection templates, however, they only log states outside the template state range and still store the value in the connection.
A template can be returned by ordinary connection lookup. TCP and SCTP then use the invalid state as an index into their transition tables.
Reject invalid template states in both sync protocol versions before looking up or modifying a connection. The version 1 path handles both IPv4 and IPv6 records.
--- net/netfilter/ipvs/ip_vs_sync.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)
net/netfilter/ipvs/ip_vs_sync.c +8 -8
@@ -1000,10 +1000,10 @@ static void ip_vs_process_message_v0(str pp->name, state);continue ; } - }else { -if (state >=IP_VS_CTPL_S_LAST ) -IP_VS_DBG (7 ,"BACKUP v0, Invalid tpl state %u\n" , - state); + }else if (state >=IP_VS_CTPL_S_LAST ) { +IP_VS_DBG (7 ,"BACKUP v0, Invalid tpl state %u\n" , + state); +continue ; } ip_vs_conn_fill_param(ipvs, AF_INET, s->protocol,
@@ -1160,10 +1160,10 @@ static inline int ip_vs_proc_sync_conn(s retc =40 ; goto out; } - }else { -if (state >=IP_VS_CTPL_S_LAST ) -IP_VS_DBG (7 ,"BACKUP, Invalid tpl state %u\n" , - state); + }else if (state >=IP_VS_CTPL_S_LAST ) { +IP_VS_DBG (7 ,"BACKUP, Invalid tpl state %u\n" , state); + retc =40 ; + goto out; }if (ip_vs_conn_fill_param_sync (ipvs, af, s, ¶m, pe_data, pe_data_len, pe_name, pe_name_len)) {
Greg Kroah-Hartman
maintainerPATCH 860/1102 mac802154: fix use-after-free of sdata via queued RX frames
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ibrahim Hashimov <[email protected]>
commit 2f37fba846c9fdff5fc15b6d93656057ccd13031 upstream.
The RX softirq producer ieee802154_subif_frame() queues received beacon and MAC-command frames onto local->rx_beacon_list / rx_mac_cmd_list and schedules a process-context worker, storing a raw mac_pkt->sdata (and skb->dev == sdata->dev) with neither a reference nor any locking:
- the lists have no lock: the softirq producer list_add_tail()s while the mac_wq worker list_del()s, so sibling interfaces on the same phy corrupt the list;
- the workers dereference the interface after it may have been freed. mac802154_rx_mac_cmd_worker() touches mac_pkt->sdata directly, and mac802154_rx_beacon_worker() -> mac802154_process_beacon() dereferences skb->dev (== sdata->dev). Removing an interface frees its sdata (netdev_priv) while a queued frame still points at it, so a later worker run is a use-after-free.
Reproduced under KASAN by flooding a victim interface with MAC command frames and removing it (the beacon path is the same class via skb->dev):
BUG: KASAN: slab-use-after-free in mac802154_rx_mac_cmd_worker+0x463/0x630 [mac802154] Read of size 4 at addr ffff888002f9ea18 by task kworker/u8:1/31 Workqueue: phy0-mac-cmds mac802154_rx_mac_cmd_worker [mac802154] Call Trace: mac802154_rx_mac_cmd_worker+0x463/0x630 [mac802154] process_one_work+0x611/0xe80 worker_thread+0x52e/0xdc0 kthread+0x30c/0x630 ret_from_fork+0x2fd/0x3e0
Fix both lists together:
- add local->rx_lock and take it around every list access: the softirq producer (plain spin_lock, softirq context) and the workers and flush (spin_lock_bh, process context);
- pin the interface for the lifetime of a queued frame with netdev_hold()/netdev_put(), so the worker can safely dereference sdata / skb->dev even while the interface is being removed;
- dequeue under the lock at the head and loop-drain the whole list in the workers (they previously processed one frame per run and relied on a later enqueue to drain the rest);
- drop not-yet-started frames of an interface before it is unregistered, from ieee802154_if_remove() (after the RCU grace period) and from the ieee802154_remove_interfaces() loop -- the latter is the whole-phy teardown path, which does not go through ieee802154_if_remove().
An in-flight worker that already dequeued a frame keeps its own netdev reference; unregister_netdevice() then waits it out in netdev_run_todo(), which runs at rtnl_unlock() (rtnl released) and after the interface has been closed, so it does not pin rtnl. A worker blocked in an association TX only delays that one interface's unregister (the usual "waiting for %s to become free"), it does not hold rtnl. netdev_hold() is used for this reason instead of a cancel_work_sync() under rtnl, which would block on the worker's unbounded MLME TX wait via ieee802154_sync_queue().
The mac-command worker additionally skips processing for a stopped interface (ieee802154_sdata_running()), avoiding a needless association response during teardown.
--- include/net/cfg802154.h | 1 net/mac802154/ieee802154_i.h | 8 ++ net/mac802154/iface.c | 6 ++ net/mac802154/main.c | 1 net/mac802154/rx.c | 120 +++++++++++++++++++++++++++++++++++-------- net/mac802154/scan.c | 10 +-- 6 files changed, 117 insertions(+), 29 deletions(-)
include/net/cfg802154.h +1 -0
@@ -376,6 +376,7 @@ struct cfg802154_mac_pkt {struct list_head node;struct sk_buff *skb;struct ieee802154_sub_if_data *sdata; +netdevice_tracker dev_tracker;u8 page;u8 channel; };
net/mac802154/ieee802154_i.h +8 -0
@@ -74,6 +74,10 @@ struct ieee802154_local {struct work_struct rx_beacon_work;struct list_head rx_mac_cmd_list;struct work_struct rx_mac_cmd_work; +/* Serializes rx_beacon_list and rx_mac_cmd_list against the RX +* softirq producer, the mac_wq workers and the teardown flush. +*/ +spinlock_t rx_lock;/* Association */ struct ieee802154_pan_device *assoc_dev;
@@ -303,6 +307,10 @@ static inline bool mac802154_is_beaconin }void mac802154_rx_mac_cmd_worker (struct work_struct *work); +void mac802154_flush_list (struct list_head *list, +struct ieee802154_sub_if_data *sdata); +void mac802154_flush_queued_pkts (struct ieee802154_local *local, +struct ieee802154_sub_if_data *sdata); int mac802154_perform_association(struct ieee802154_sub_if_data *sdata, struct ieee802154_pan_device *coord,
net/mac802154/iface.c +6 -0
@@ -694,6 +694,7 @@ void ieee802154_if_remove(struct ieee802mutex_unlock (&sdata->local->iflist_mtx);synchronize_rcu (); +mac802154_flush_queued_pkts (sdata->local, sdata);unregister_netdevice (sdata->dev); }
@@ -705,6 +706,11 @@ void ieee802154_remove_interfaces(structlist_for_each_entry_safe (sdata, tmp, &local->interfaces, list) {list_del_rcu (&sdata->list); +/* Best-effort: a frame the RX softirq queues for this sdata +* after the flush still pins the netdev, so the +* unregister_netdevice() below waits it out. +*/ +mac802154_flush_queued_pkts (local, sdata);unregister_netdevice (sdata->dev); }mutex_unlock (&local->iflist_mtx);
net/mac802154/main.c +1 -0
@@ -91,6 +91,7 @@ ieee802154_alloc_hw(size_t priv_data_lenINIT_LIST_HEAD (&local->interfaces);INIT_LIST_HEAD (&local->rx_beacon_list);INIT_LIST_HEAD (&local->rx_mac_cmd_list); +spin_lock_init (&local->rx_lock);mutex_init (&local->iflist_mtx);tasklet_setup (&local->tasklet, ieee802154_tasklet_handler);
net/mac802154/rx.c +98 -22
161 lines not on this page · raw message
net/mac802154/scan.c +3 -7
@@ -104,13 +104,9 @@ static unsigned int mac802154_scan_get_cstatic void mac802154_flush_queued_beacons (struct ieee802154_local *local) { -struct cfg802154_mac_pkt *mac_pkt, *tmp; - -list_for_each_entry_safe (mac_pkt, tmp, &local->rx_beacon_list, node) { -list_del (&mac_pkt->node); -kfree_skb (mac_pkt->skb); -kfree (mac_pkt); - } +spin_lock_bh (&local->rx_lock); +mac802154_flush_list (&local->rx_beacon_list,NULL ); +spin_unlock_bh (&local->rx_lock); } static void
Greg Kroah-Hartman
maintainerPATCH 861/1102 KVM: PPC: Book3S HV: Set irqfd->producer only on success
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: leixiang <[email protected]>
commit 1144454ea22290d7c6998a2af6239e5995476afc upstream.
Set irqfd->producer only after kvmppc_set_passthru_irq() succeeds to avoid leaving a dangling pointer on failure. The bypass manager does not register a failed producer, so the pointer is never cleared.
--- arch/powerpc/kvm/book3s_hv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/powerpc/kvm/book3s_hv.c +2 -2
@@ -6069,12 +6069,12 @@ static int kvmppc_irq_bypass_add_producestruct kvm_kernel_irqfd *irqfd = container_of(cons, struct kvm_kernel_irqfd, consumer); - irqfd->producer = prod; - ret =kvmppc_set_passthru_irq (irqfd->kvm, prod->irq, irqfd->gsi);if (ret)pr_info ("kvmppc_set_passthru_irq (irq %d, gsi %d) fails: %d\n" , prod->irq, irqfd->gsi, ret); +else + irqfd->producer = prod;return ret; }
Greg Kroah-Hartman
maintainerPATCH 862/1102 s390/qeth: allow bridgeport queries despite OS_MISMATCH
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nagamani PV <[email protected]>
commit 74f27fc8642b7e8d139796f8c18ee46df393c2b2 upstream.
When HiperSockets interfaces on the same VCHID span different OS families, reads of the sysfs attributes bridge_role and bridge_state fail with -EPERM if bridge port ownership belongs to another OS family.
As a result, userspace tools such as 'lszdev -ii' cannot retrieve bridge_role and bridge_state, even though firmware returns valid bridge port data for QUERY_BRIDGE_PORTS requests.
The firmware reports IPA_RC_SBP_IQD_OS_MISMATCH (0x0010) to indicate that bridge port ownership belongs to a different OS family. For QUERY_BRIDGE_PORTS operations, firmware still returns valid bridge port data (role=none, state=inactive) together with a primary return code of 0x0000 (success).
Allow QUERY_BRIDGE_PORTS requests to return the bridge port data provided by the firmware despite OS_MISMATCH. To make the OS family mismatch visible to userspace, represent the firmware-reported role "none" as "none (OS family mismatch)" while preserving the reported bridge_state.
The behavior for non-QUERY bridge port commands is unchanged; SET operations continue to return -EPERM when another OS family owns the bridge port.
This restores readability of bridge_role and bridge_state.
--- drivers/s390/net/qeth_l2.h | 3 ++- drivers/s390/net/qeth_l2_main.c | 26 ++++++++++++++++++++++---- drivers/s390/net/qeth_l2_sys.c | 7 ++++++- 3 files changed, 30 insertions(+), 6 deletions(-)
drivers/s390/net/qeth_l2.h +2 -1
@@ -13,7 +13,8 @@ extern const struct attribute_group *qetint qeth_bridgeport_query_ports (struct qeth_card *card,enum qeth_sbp_roles *role, -enum qeth_sbp_states *state); +enum qeth_sbp_states *state, +bool *os_mismatch);int qeth_bridgeport_setrole (struct qeth_card *card,enum qeth_sbp_roles role);int qeth_bridgeport_an_set (struct qeth_card *card,int enable);
drivers/s390/net/qeth_l2_main.c +22 -4
@@ -1157,7 +1157,7 @@ static void qeth_l2_setup_bridgeport_attqeth_bridgeport_setrole (card, card->options.sbp.role);/* Let the callback function refresh the stored role value. */ qeth_bridgeport_query_ports (card, &card->options.sbp.role, -NULL ); +NULL ,NULL ); }if (card->options.sbp.hostnotification) {if (qeth_bridgeport_an_set (card,1 ))
@@ -1544,6 +1544,7 @@ struct _qeth_sbp_cbctl {struct {enum qeth_sbp_roles *role;enum qeth_sbp_states *state; +bool *os_mismatch; } qports; } data; };
@@ -1720,10 +1721,19 @@ static int qeth_bridgeport_query_ports_cstruct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;struct _qeth_sbp_cbctl *cbctl = (struct _qeth_sbp_cbctl *)reply->param;struct qeth_sbp_port_data *qports; +u16 sbp_rc;int rc;QETH_CARD_TEXT (card,2 ,"brqprtcb" ); - rc =qeth_bridgeport_makerc (card, cmd); + sbp_rc = cmd->data.sbp.hdr.return_code; + +/* on OS family mismatch, query still returns valid port data; +* treat as success +*/ +if (sbp_rc ==IPA_RC_SBP_IQD_OS_MISMATCH && !cmd->hdr.return_code) + rc =0 ; +else + rc =qeth_bridgeport_makerc (card, cmd);if (rc)return rc;
@@ -1739,6 +1749,9 @@ static int qeth_bridgeport_query_ports_cif (cbctl->data.qports.state) *cbctl->data.qports.state = qports->entry[0 ].state; } +if (cbctl->data.qports.os_mismatch) + *cbctl->data.qports.os_mismatch = + (sbp_rc ==IPA_RC_SBP_IQD_OS_MISMATCH );return 0 ; }
@@ -1747,13 +1760,17 @@ static int qeth_bridgeport_query_ports_c* @card: qeth_card structure pointer. * @role: Role of the port: 0-none, 1-primary, 2-secondary. * @state: State of the port: 0-inactive, 1-standby, 2-active. +* @os_mismatch: if non-NULL, set to true when firmware reports +* OS family mismatch. * * Returns negative errno-compatible error indication or 0 on success. * -* 'role' and 'state' are not updated in case of hardware operation failure. +* 'role', 'state' and 'os_mismatch' are not updated in case of +* hardware operation failure. */ int qeth_bridgeport_query_ports (struct qeth_card *card, -enum qeth_sbp_roles *role,enum qeth_sbp_states *state) +enum qeth_sbp_roles *role,enum qeth_sbp_states *state, +bool *os_mismatch) {struct qeth_cmd_buffer *iob;struct _qeth_sbp_cbctl cbctl = {
@@ -1761,6 +1778,7 @@ int qeth_bridgeport_query_ports(struct q .qports = { .role = role, .state = state, + .os_mismatch = os_mismatch, }, }, };
drivers/s390/net/qeth_l2_sys.c +6 -1
@@ -15,6 +15,7 @@ static ssize_t qeth_bridge_port_role_sta {struct qeth_card *card =dev_get_drvdata (dev);enum qeth_sbp_states state =QETH_SBP_STATE_INACTIVE ; +bool os_mismatch = false;int rc =0 ;char *word;
@@ -25,7 +26,7 @@ static ssize_t qeth_bridge_port_role_staif (qeth_card_hw_is_reachable (card) && card->options.sbp.supported_funcs) rc =qeth_bridgeport_query_ports (card, - &card->options.sbp.role, &state); + &card->options.sbp.role, &state, &os_mismatch);if (!rc) {if (show_state)switch (state) {
@@ -52,6 +53,10 @@ static ssize_t qeth_bridge_port_role_staif (rc)QETH_CARD_TEXT_ (card,2 ,"SBP%02x:%02x" , card->options.sbp.role, state); +else if (!show_state && + card->options.sbp.role ==QETH_SBP_ROLE_NONE && + os_mismatch) + rc =sysfs_emit (buf,"%s (OS family mismatch)\n" , word);else rc =sysfs_emit (buf,"%s\n" , word); }
Greg Kroah-Hartman
maintainerPATCH 863/1102 s390/crypto: Fix skcipher_walk return code handling in aes_s390
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Harald Freudenberger <[email protected]>
commit 15fa028589c3a2545f0bff355eff06d5844196bf upstream.
The return codes from skcipher_walk_virt() were not properly checked before entering the processing loops in ecb_aes_crypt() and ctr_aes_crypt(). If skcipher_walk_virt() fails, the walk structure may be in an undefined state, and attempting to process data could lead to incorrect behavior or accessing uninitialized memory.
Add proper return code checking to ensure correct handling of the walk initialization and walk advance and eventually return to the caller with that return code.
--- arch/s390/crypto/aes_s390.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
arch/s390/crypto/aes_s390.c +6 -6
@@ -236,7 +236,7 @@ static int ecb_aes_crypt(struct skcipherreturn fallback_skcipher_crypt (sctx, req, modifier); ret =skcipher_walk_virt (&walk, req, false); -while ((nbytes = walk.nbytes) !=0 ) { +while (!ret && ((nbytes = walk.nbytes) !=0 )) {/* only use complete blocks */ n = nbytes & ~(AES_BLOCK_SIZE -1 ); cpacf_km(sctx->fc | modifier, sctx->key,
@@ -340,7 +340,7 @@ static int cbc_aes_crypt(struct skcipherreturn ret;memcpy (param.iv, walk.iv,AES_BLOCK_SIZE );memcpy (param.key, sctx->key, sctx->key_len); -while ((nbytes = walk.nbytes) !=0 ) { +while (!ret && ((nbytes = walk.nbytes) !=0 )) {/* only use complete blocks */ n = nbytes & ~(AES_BLOCK_SIZE -1 ); cpacf_kmc(sctx->fc | modifier, ¶m,
@@ -466,7 +466,7 @@ static int xts_aes_crypt(struct skciphermemcpy (xts_param.key + offset, xts_ctx->key, xts_ctx->key_len);memcpy (xts_param.init, pcc_param.xts,16 ); -while ((nbytes = walk.nbytes) !=0 ) { +while (!ret && ((nbytes = walk.nbytes) !=0 )) {/* only use complete blocks */ n = nbytes & ~(AES_BLOCK_SIZE -1 ); cpacf_km(xts_ctx->fc | modifier, xts_param.key + offset,
@@ -594,7 +594,7 @@ static int fullxts_aes_crypt(struct skcimemcpy (fxts_param.tweak, req->iv,AES_BLOCK_SIZE ); fxts_param.nap[0 ] =0x01 ;/* initial alpha power (1, little-endian) */ -while ((nbytes = walk.nbytes) !=0 ) { +while (!ret && ((nbytes = walk.nbytes) !=0 )) {/* only use complete blocks */ n = nbytes & ~(AES_BLOCK_SIZE -1 ); cpacf_km(xts_ctx->fc | modifier, fxts_param.key + offset,
@@ -684,7 +684,7 @@ static int ctr_aes_crypt(struct skcipher locked =mutex_trylock (&ctrblk_lock); ret =skcipher_walk_virt (&walk, req, false); -while ((nbytes = walk.nbytes) >=AES_BLOCK_SIZE ) { +while (!ret && ((nbytes = walk.nbytes) >=AES_BLOCK_SIZE )) { n =AES_BLOCK_SIZE ;if (nbytes >=2 *AES_BLOCK_SIZE && locked)
@@ -703,7 +703,7 @@ static int ctr_aes_crypt(struct skcipher/* * final block may be < AES_BLOCK_SIZE, copy only nbytes */ -if (nbytes) { +if (!ret && nbytes) {memset (buf,0 ,AES_BLOCK_SIZE );memcpy (buf, walk.src.virt.addr, nbytes); cpacf_kmctr(sctx->fc, sctx->key, buf, buf,
Greg Kroah-Hartman
maintainerPATCH 864/1102 s390/crypto: Fix use of mutex in atomic context
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Harald Freudenberger <[email protected]>
commit d1c44a7d085473173bb360b7218a43699c3f56c7 upstream.
The AES CTR implementation used a mutex to lock one page of exclusive memory for fast CTR processing. Unfortunately a mutex is not save to use in atomic or interrupt context. So use a binary semaphore instead which is save to use in such environments.
Furthermore rework the code to get rid of conditional locking. So restructure the AES CRT code by extracting the main loop into a separate function and just give in information about the (locked) page can be used or not (is not locked).
--- arch/s390/crypto/aes_s390.c | 63 +++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 24 deletions(-)
arch/s390/crypto/aes_s390.c +39 -24
@@ -28,14 +28,14 @@#include <linux/module.h> #include <linux/cpufeature.h> #include <linux/init.h> -#include <linux/mutex.h> #include <linux/fips.h> +#include <linux/semaphore.h> #include <linux/string.h> #include <crypto/xts.h> #include <asm/cpacf.h> static u8 *ctrblk; -static DEFINE_MUTEX (ctrblk_lock); +static DEFINE_SEMAPHORE(ctrblk_sem, 1);static cpacf_mask_t km_functions, kmc_functions, kmctr_functions, kma_functions;
@@ -669,46 +669,61 @@ static unsigned int __ctrblk_init(u8 *ctreturn n; } +static int __ctr_aes_crypt (struct s390_aes_ctx *sctx, +struct skcipher_walk *walk,bool locked) +{ +unsigned int n, nbytes; +int ret =0 ; +u8 *ctrptr; + +while (!ret && ((nbytes = walk->nbytes) >=AES_BLOCK_SIZE )) { + n =AES_BLOCK_SIZE ; +if (nbytes >=2 *AES_BLOCK_SIZE && locked) + n =__ctrblk_init (ctrblk, walk->iv, nbytes); + ctrptr = (n >AES_BLOCK_SIZE ) ? ctrblk : walk->iv; +cpacf_kmctr (sctx->fc, sctx->key, walk->dst.virt.addr, + walk->src.virt.addr, n, ctrptr); +if (ctrptr == ctrblk) +memcpy (walk->iv, ctrptr + n -AES_BLOCK_SIZE , +AES_BLOCK_SIZE ); +crypto_inc (walk->iv,AES_BLOCK_SIZE ); + ret =skcipher_walk_done (walk, nbytes - n); + } + +return ret; +} +static int ctr_aes_crypt (struct skcipher_request *req) {struct crypto_skcipher *tfm =crypto_skcipher_reqtfm (req);struct s390_aes_ctx *sctx =crypto_skcipher_ctx (tfm); -u8 buf[AES_BLOCK_SIZE ], *ctrptr;struct skcipher_walk walk; -unsigned int n, nbytes; -int ret, locked; +u8 buf[AES_BLOCK_SIZE ]; +int ret;if (unlikely (!sctx->fc))return fallback_skcipher_crypt (sctx, req,0 ); - locked =mutex_trylock (&ctrblk_lock); - ret =skcipher_walk_virt (&walk, req, false); -while (!ret && ((nbytes = walk.nbytes) >=AES_BLOCK_SIZE )) { - n =AES_BLOCK_SIZE ; +if (ret) +return ret; -if (nbytes >=2 *AES_BLOCK_SIZE && locked) - n =__ctrblk_init (ctrblk, walk.iv, nbytes); - ctrptr = (n >AES_BLOCK_SIZE ) ? ctrblk : walk.iv; -cpacf_kmctr (sctx->fc, sctx->key, walk.dst.virt.addr, - walk.src.virt.addr, n, ctrptr); -if (ctrptr == ctrblk) -memcpy (walk.iv, ctrptr + n -AES_BLOCK_SIZE , -AES_BLOCK_SIZE ); -crypto_inc (walk.iv,AES_BLOCK_SIZE ); - ret =skcipher_walk_done (&walk, nbytes - n); +if (down_trylock (&ctrblk_sem) ==0 ) { + ret =__ctr_aes_crypt (sctx, &walk, true); +up (&ctrblk_sem); + }else { + ret =__ctr_aes_crypt (sctx, &walk, false); } -if (locked) -mutex_unlock (&ctrblk_lock); +/* * final block may be < AES_BLOCK_SIZE, copy only nbytes */ -if (!ret && nbytes) { +if (!ret && walk.nbytes >0 ) {memset (buf,0 ,AES_BLOCK_SIZE ); -memcpy (buf, walk.src.virt.addr, nbytes); +memcpy (buf, walk.src.virt.addr, walk.nbytes);cpacf_kmctr (sctx->fc, sctx->key, buf, buf,AES_BLOCK_SIZE , walk.iv); -memcpy (walk.dst.virt.addr, buf, nbytes); +memcpy (walk.dst.virt.addr, buf, walk.nbytes);crypto_inc (walk.iv,AES_BLOCK_SIZE ); ret =skcipher_walk_done (&walk,0 ); }
Greg Kroah-Hartman
maintainerPATCH 865/1102 s390/crypto: Fix missing scrub of temp buffers with AES ctr and gcm algorithm
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Harald Freudenberger <[email protected]>
commit 8b7c3b6914f19caf648d05726a86af6326d3c2c6 upstream.
In function ctr_aes_crypt() there is a buffer used to process remaining bytes < AES_BLOCK_SIZE. This buffer was not scrubbed and thus could lead to expose of unwanted data. When the buffer is used explicitly scrub it at the end of the code block to avoid exposure of maybe sensitive data.
In a similar way the function gcm_aes_crypt() hat an error path where the CPACF param block was not scrubbed. Instead of return early now these error paths go to end of function where explicit scrubbing is done. Similar with the buffers which are part of the gcm_sg_walk structs from the variables gw_in and gw_out.
--- arch/s390/crypto/aes_s390.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-)
arch/s390/crypto/aes_s390.c +12 -4
@@ -726,6 +726,7 @@ static int ctr_aes_crypt(struct skciphermemcpy (walk.dst.virt.addr, buf, walk.nbytes);crypto_inc (walk.iv,AES_BLOCK_SIZE ); ret =skcipher_walk_done (&walk,0 ); +memzero_explicit (buf,sizeof (buf)); }return ret;
@@ -1027,10 +1028,14 @@ static int gcm_aes_crypt(struct aead_req gw_in.ptr, aad_bytes); n = aad_bytes + pc_bytes; -if (gcm_in_walk_done (&gw_in, n) != n) -return -ENOMEM ; -if (gcm_out_walk_done (&gw_out, n) != n) -return -ENOMEM ; +if (gcm_in_walk_done (&gw_in, n) != n) { + ret = -ENOMEM ; + goto out; + } +if (gcm_out_walk_done (&gw_out, n) != n) { + ret = -ENOMEM ; + goto out; + } aadlen -= aad_bytes; pclen -= pc_bytes; }while (aadlen + pclen >0 );
@@ -1042,7 +1047,10 @@ static int gcm_aes_crypt(struct aead_req }else scatterwalk_map_and_copy (param.t, req->dst, len, taglen,1 ); +out:memzero_explicit (¶m,sizeof (param)); +memzero_explicit (gw_in.buf,sizeof (gw_in.buf)); +memzero_explicit (gw_out.buf,sizeof (gw_out.buf));return ret; }
Greg Kroah-Hartman
maintainerPATCH 866/1102 perf: RISC-V: store available counter mask as bitmap
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xixin Liu <[email protected]>
commit 6809da6e9c08ccc9a09cb0a48c61471679274aaa upstream.
The available-counter mask was a single unsigned long, but iteration uses RISCV_MAX_COUNTERS, which is 64. On RV32 that reads past the object. Filling with an unsigned-long bit at index 32 and above is also wrong.
Use DECLARE_BITMAP and set_bit/bitmap helpers. Walk each bitmap word into CFG_MATCH when checking events, when allocating an index, and when stopping all counters. Set the counter base to i times BITS_PER_LONG. Share the CFG_MATCH ecall through a small helper so the 32-bit argument split is not duplicated. On qemu-system-riscv32 the probe bitmap has bits above XLEN set, so the first word alone is not enough.
--- drivers/perf/riscv_pmu_legacy.c | 5 +- drivers/perf/riscv_pmu_sbi.c | 88 ++++++++++++++++++++++++++++------------ include/linux/perf/riscv_pmu.h | 2 3 files changed, 66 insertions(+), 29 deletions(-)
drivers/perf/riscv_pmu_legacy.c +3 -2
@@ -111,8 +111,9 @@ static void pmu_legacy_init(struct riscv {pr_info ("Legacy PMU implementation is available\n" ); - pmu->cmask =BIT (RISCV_PMU_LEGACY_CYCLE ) | -BIT (RISCV_PMU_LEGACY_INSTRET ); +bitmap_zero (pmu->cmask,RISCV_MAX_COUNTERS ); +set_bit (RISCV_PMU_LEGACY_CYCLE , pmu->cmask); +set_bit (RISCV_PMU_LEGACY_INSTRET , pmu->cmask); pmu->ctr_start = pmu_legacy_ctr_start; pmu->ctr_stop =NULL ; pmu->event_map = pmu_legacy_event_map;
drivers/perf/riscv_pmu_sbi.c +62 -26
157 lines not on this page · raw message
include/linux/perf/riscv_pmu.h +1 -1
@@ -55,7 +55,7 @@ struct riscv_pmu {irqreturn_t (*handle_irq)(int irq_num,void *dev); -unsigned long cmask; +DECLARE_BITMAP (cmask,RISCV_MAX_COUNTERS );u64 (*ctr_read)(struct perf_event *event);int (*ctr_get_idx)(struct perf_event *event);int (*ctr_get_width)(int idx);
Greg Kroah-Hartman
maintainerPATCH 867/1102 perf: RISC-V: use BIT_ULL for u64 overflow masks
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xixin Liu <[email protected]>
commit 6693171c8c540b3a54e671992f0561613076fc3b upstream.
Overflow status and restart masks are u64, but bits were built with BIT(). On RV32 that is an unsigned long shift, so indices >= 32 truncate or wrap and corrupt the mask.
Use BIT_ULL() for those u64 bitops.
--- drivers/perf/riscv_pmu_sbi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/perf/riscv_pmu_sbi.c +3 -3
@@ -946,7 +946,7 @@ static inline void pmu_sbi_start_ovf_ctrstruct riscv_pmu_snapshot_data *sdata = cpu_hw_evt->snapshot_addr;for_each_set_bit (idx, cpu_hw_evt->used_hw_ctrs,RISCV_MAX_COUNTERS ) { -if (ctr_ovf_mask &BIT (idx)) { +if (ctr_ovf_mask &BIT_ULL (idx)) { event = cpu_hw_evt->events[idx]; hwc = &event->hw; max_period =riscv_pmu_ctr_get_width_mask (event);
@@ -1055,14 +1055,14 @@ static irqreturn_t pmu_sbi_ovf_handler(i hidx = info->csr -CSR_CYCLE ;/* check if the corresponding bit is set in scountovf or overflow mask in shmem */ -if (!(overflow &BIT (hidx))) +if (!(overflow &BIT_ULL (hidx)))continue ;/* * Keep a track of overflowed counters so that they can be started * with updated initial value. */ - overflowed_ctrs |=BIT (lidx); + overflowed_ctrs |=BIT_ULL (lidx); hw_evt = &event->hw;/* Update the event states here so that we know the state while reading */ hw_evt->state |=PERF_HES_STOPPED ;
Greg Kroah-Hartman
maintainerPATCH 868/1102 afs: Fix incorrect free in candidate cleanup in afs_lookup_server()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
commit 044d596094af4b769fb8e1173dff0d08bd68db6c upstream.
Fix afs_lookup_server() to not free an existing server's endpoint state when cleaning up a candidate server. The candidate record doesn't have an endpoint state yet at this point, so the free for that can just be removed.
--- fs/afs/server.c | 1 - 1 file changed, 1 deletion(-)
fs/afs/server.c +0 -1
@@ -241,7 +241,6 @@ struct afs_server *afs_lookup_server(str out:afs_put_addrlist (alist, afs_alist_trace_put_server_create);if (candidate) { -kfree (rcu_access_pointer (server->endpoint_state));kfree (candidate);afs_dec_servers_outstanding (cell->net); }
Greg Kroah-Hartman
maintainerPATCH 869/1102 afs: Clear stale peer app data after address list changes
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chengfeng Ye <[email protected]>
commit ba0623fc19a424f4745394c499f9f28a8d88d397 upstream.
afs_fs_probe_fileserver() fetches the current endpoint state under server->fs_lock, but leaves old_alist as NULL. Consequently, afs_set_peer_appdata() treats every address list replacement as initial setup and only binds the new peers; it never unbinds peers removed from the old list.
An address refresh can therefore proceed as follows. CPU 0 replaces server S's list and drops Pold without clearing Pold->app_data. The server destroyer then clears only S's current peers and lets S reach its RCU callback. After the callback frees S, CPU 1 handles a callback through an RxRPC connection that still pins Pold, reads Pold->app_data, and calls afs_use_server() on the freed object.
KASAN reported:
BUG: KASAN: slab-use-after-free in afs_find_server+0x3c/0xa0 Read of size 4 at addr ffff8881013e1af0 by task krxrpcio/7001/74 Call Trace: afs_find_server+0x3c/0xa0 afs_rx_new_call+0x15c/0x390 rxrpc_new_incoming_call+0x97c/0x1730 rxrpc_input_packet.constprop.0+0xd03/0xec0 rxrpc_io_thread+0x967/0x1640 Allocated by task 93: afs_lookup_server+0x1a7/0x14c0 afs_alloc_server_list+0x43f/0xb60 afs_create_volume+0x923/0x1490 afs_get_tree+0x1c6/0x10a0 Freed by task 0: kfree+0x131/0x3c0 rcu_core+0x50a/0x1850 Last potentially related work creation: __call_rcu_common.constprop.0+0x71/0xa10 afs_put_server+0x213/0x2b0
Preserve old->addresses for the peer app-data update so that removed peers are cleared before the endpoint state is replaced. Also advance both cursors when the old and new lists share a peer; activating the old/new comparison without this would otherwise loop forever on the shared entry.
--- fs/afs/addr_list.c | 5 ++++- fs/afs/fs_probe.c | 1 + 2 files changed, 5 insertions(+), 1 deletion(-)
fs/afs/addr_list.c +4 -1
@@ -394,8 +394,11 @@ void afs_set_peer_appdata(struct afs_server *server,struct rxrpc_peer *pn = new_alist->addrs[n].peer;struct rxrpc_peer *po = old_alist->addrs[o].peer; -if (pn == po) +if (pn == po) { + n++; + o++;continue ; + }if (pn < po) {rxrpc_kernel_set_peer_data (pn, data); n++;
fs/afs/fs_probe.c +1 -0
@@ -258,6 +258,7 @@ int afs_fs_probe_fileserver(struct afs_net *net, struct afs_server *server, lockdep_is_held(&server->fs_lock));if (old) { estate->responsive_set = old->responsive_set; + old_alist = old->addresses;if (!new_alist) new_alist = old->addresses; }
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 870/1102 hwmon: (applesmc) fix key backlight workqueue leak on register failure
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cong Nguyen <[email protected]>
commit 5a0aacaa2d593d7582ecfe289529b937b6dc5d3c upstream.
applesmc_create_key_backlight() allocates applesmc_led_wq before calling led_classdev_register(). When register fails, the error is returned to applesmc_init(), which jumps to out_light_sysfs and skips applesmc_release_key_backlight(), leaking the workqueue.
Destroy the workqueue on the register failure path. The bug was introduced when the inline init block was refactored into a helper that returns errors directly, dropping the old out_light_wq unwind label.
--- drivers/hwmon/applesmc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/hwmon/applesmc.c +6 -1
@@ -1249,12 +1249,17 @@ static void applesmc_release_light_sensostatic int applesmc_create_key_backlight (void ) { +int ret; +if (!smcreg.has_key_backlight)return 0 ; applesmc_led_wq =create_singlethread_workqueue ("applesmc-led" );if (!applesmc_led_wq)return -ENOMEM ; -return led_classdev_register (&pdev->dev, &applesmc_backlight); + ret =led_classdev_register (&pdev->dev, &applesmc_backlight); +if (ret) +destroy_workqueue (applesmc_led_wq); +return ret; }static void applesmc_release_key_backlight (void )
Greg Kroah-Hartman
maintainerPATCH 871/1102 hwmon: (chipcap2) fix channels in humidity alarm notifications
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Javier Carrasco <[email protected]>
commit 286b175bb03893949f24621f356abd9d20368b0a upstream.
hwmon_notify_event() expects the channel number as its last argument, taken into account with the type parameter that it is a humidity sensor type. Given that this device only provides one humidity channel, 0 must be passed. The custom construct to enumerate the channels makes wrong assumptions by listing all types together (temperature and humidity).
Remove the custom channel enumeration and pass the right channel to hwmon_notify_event() for hwmon_humidity_min_alarm and hwmon_humidity_max_alarm.
--- drivers/hwmon/chipcap2.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-)
drivers/hwmon/chipcap2.c +2 -7
@@ -89,11 +89,6 @@ struct cc2_data {bool process_irqs; }; -enum cc2_chan_addr { -CC2_CHAN_TEMP =0 , -CC2_CHAN_HUMIDITY , -}; -/* %RH as a per cent mille from a register value */ static long cc2_rh_convert (u16 data) {
@@ -492,7 +487,7 @@ static irqreturn_t cc2_low_interrupt(intif (cc2->process_irqs) {hwmon_notify_event (cc2->hwmon, hwmon_humidity, - hwmon_humidity_min_alarm,CC2_CHAN_HUMIDITY ); + hwmon_humidity_min_alarm,0 ); cc2->rh_alarm.low_alarm = true; }
@@ -505,7 +500,7 @@ static irqreturn_t cc2_high_interrupt(inif (cc2->process_irqs) {hwmon_notify_event (cc2->hwmon, hwmon_humidity, - hwmon_humidity_max_alarm,CC2_CHAN_HUMIDITY ); + hwmon_humidity_max_alarm,0 ); cc2->rh_alarm.high_alarm = true; }
Greg Kroah-Hartman
maintainerPATCH 872/1102 hwmon: (gpio-fan) Fix use-after-free in alarm work
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
commit a2471ed17b0e6ff7bfb6b2ea8e6e5b04c309d293 upstream.
fan_alarm_irq_handler() queues fan_data->alarm_work, but nothing cancels it. fan_alarm_notify() dereferences fan_data and its hwmon device. On unbind, devres frees the interrupt, which only waits for the handler itself, and then releases the hwmon device and fan_data, so a pending fan_alarm_notify() can run after those frees.
Replace INIT_WORK() with devm_work_autocancel(), registered before devm_request_irq(). The devres cleanup then frees the interrupt first, so no new work can be queued, and cancels the work while fan_data and the hwmon device are still alive.
This issue was found by an in-house static analysis tool.
--- drivers/hwmon/gpio-fan.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/hwmon/gpio-fan.c +7 -1
@@ -12,6 +12,7 @@#include <linux/slab.h> #include <linux/interrupt.h> #include <linux/irq.h> +#include <linux/devm-helpers.h> #include <linux/platform_device.h> #include <linux/err.h> #include <linux/kstrtox.h>
@@ -80,6 +81,7 @@ static DEVICE_ATTR_RO(fan1_alarm);static int fan_alarm_init (struct gpio_fan_data *fan_data) {int alarm_irq; +int err;struct device *dev = fan_data->dev;/*
@@ -90,7 +92,11 @@ static int fan_alarm_init(struct gpio_faif (alarm_irq <=0 )return 0 ; -INIT_WORK (&fan_data->alarm_work, fan_alarm_notify); + err =devm_work_autocancel (dev, &fan_data->alarm_work, + fan_alarm_notify); +if (err) +return err; +irq_set_irq_type (alarm_irq,IRQ_TYPE_EDGE_BOTH );return devm_request_irq (dev, alarm_irq, fan_alarm_irq_handler,IRQF_SHARED ,"GPIO fan alarm" , fan_data);