mirror of
				https://github.com/Ysurac/openmptcprouter.git
				synced 2025-03-09 15:40:20 +00:00 
			
		
		
		
	Add NSS Acceleration Support on Qualcomm devices
This commit is contained in:
		
							parent
							
								
									359074adc6
								
							
						
					
					
						commit
						e9d1e0219b
					
				
					 54 changed files with 12395 additions and 0 deletions
				
			
		|  | @ -0,0 +1,311 @@ | |||
| From 6504bc9edeb1a2a54d813f4bb5d0267e7bf827f9 Mon Sep 17 00:00:00 2001 | ||||
| From: Praveenkumar I <ipkumar@codeaurora.org> | ||||
| Date: Thu, 6 Feb 2020 17:35:42 +0530 | ||||
| Subject: [PATCH 4/8] clk: ipq8074: Support added for necessary clocks and | ||||
|  reset | ||||
| 
 | ||||
| Change-Id: I21a76a44185f766e9b6dcba274392ea8e599718b | ||||
| Signed-off-by: Praveenkumar I <ipkumar@codeaurora.org> | ||||
| Signed-off-by: Rajkumar Ayyasamy <arajkuma@codeaurora.org> | ||||
| ---
 | ||||
|  drivers/clk/qcom/gcc-ipq8074.c               | 238 ++++++++++++++++++- | ||||
|  include/dt-bindings/clock/qcom,gcc-ipq8074.h |  35 ++- | ||||
|  2 files changed, 258 insertions(+), 15 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/clk/qcom/gcc-ipq8074.c
 | ||||
| +++ b/drivers/clk/qcom/gcc-ipq8074.c
 | ||||
| @@ -49,6 +49,22 @@ enum {
 | ||||
|  	P_UNIPHY2_TX, | ||||
|  }; | ||||
|   | ||||
| +static const char * const gcc_xo_gpll4_gpll0_gpll6_gpll0_div2[] = {
 | ||||
| +	"xo",
 | ||||
| +	"gpll4",
 | ||||
| +	"gpll0",
 | ||||
| +	"gpll6",
 | ||||
| +	"gpll0_out_main_div2",
 | ||||
| +};
 | ||||
| +
 | ||||
| +static const struct parent_map gcc_xo_gpll4_gpll0_gpll6_gpll0_div2_map[] = {
 | ||||
| +	{ P_XO, 0 },
 | ||||
| +	{ P_GPLL4, 1 },
 | ||||
| +	{ P_GPLL0, 2 },
 | ||||
| +	{ P_GPLL6, 3 },
 | ||||
| +	{ P_GPLL0_DIV2, 4 },
 | ||||
| +};
 | ||||
| +
 | ||||
|  static struct clk_alpha_pll gpll0_main = { | ||||
|  	.offset = 0x21000, | ||||
|  	.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], | ||||
| @@ -630,6 +646,12 @@ static const struct freq_tbl ftbl_pcie_a
 | ||||
|  	{ } | ||||
|  }; | ||||
|   | ||||
| +struct freq_tbl ftbl_pcie_rchng_clk_src[] = {
 | ||||
| +	F(19200000, P_XO, 1, 0, 0),
 | ||||
| +	F(100000000, P_GPLL0, 8, 0, 0),
 | ||||
| +	{ }
 | ||||
| +};
 | ||||
| +
 | ||||
|  static struct clk_rcg2 pcie0_axi_clk_src = { | ||||
|  	.cmd_rcgr = 0x75054, | ||||
|  	.freq_tbl = ftbl_pcie_axi_clk_src, | ||||
| @@ -2030,6 +2052,78 @@ static struct clk_rcg2 gp3_clk_src = {
 | ||||
|  	}, | ||||
|  }; | ||||
|   | ||||
| +struct freq_tbl ftbl_qdss_tsctr_clk_src[] = {
 | ||||
| +	F(160000000, P_GPLL0_DIV2, 2.5, 0, 0),
 | ||||
| +	F(320000000, P_GPLL0, 2.5, 0, 0),
 | ||||
| +	F(600000000, P_GPLL6, 2, 0, 0),
 | ||||
| +	{ }
 | ||||
| +};
 | ||||
| +
 | ||||
| +struct clk_rcg2 qdss_tsctr_clk_src = {
 | ||||
| +	.cmd_rcgr = 0x29064,
 | ||||
| +	.freq_tbl = ftbl_qdss_tsctr_clk_src,
 | ||||
| +	.hid_width = 5,
 | ||||
| +	.parent_map = gcc_xo_gpll4_gpll0_gpll6_gpll0_div2_map,
 | ||||
| +	.clkr.hw.init = &(struct clk_init_data){
 | ||||
| +		.name = "qdss_tsctr_clk_src",
 | ||||
| +		.parent_names = gcc_xo_gpll4_gpll0_gpll6_gpll0_div2,
 | ||||
| +		.num_parents = 5,
 | ||||
| +		.ops = &clk_rcg2_ops,
 | ||||
| +	},
 | ||||
| +};
 | ||||
| +
 | ||||
| +static struct clk_fixed_factor qdss_dap_sync_clk_src = {
 | ||||
| +	.mult = 1,
 | ||||
| +	.div = 4,
 | ||||
| +	.hw.init = &(struct clk_init_data){
 | ||||
| +		.name = "qdss_dap_sync_clk_src",
 | ||||
| +		.parent_names = (const char *[]){
 | ||||
| +			"qdss_tsctr_clk_src"
 | ||||
| +		},
 | ||||
| +		.num_parents = 1,
 | ||||
| +		.ops = &clk_fixed_factor_ops,
 | ||||
| +	},
 | ||||
| +};
 | ||||
| +
 | ||||
| +struct freq_tbl ftbl_qdss_at_clk_src[] = {
 | ||||
| +	F(66670000, P_GPLL0_DIV2, 6, 0, 0),
 | ||||
| +	F(240000000, P_GPLL6, 6, 0, 0),
 | ||||
| +	{ }
 | ||||
| +};
 | ||||
| +
 | ||||
| +struct clk_rcg2 qdss_at_clk_src = {
 | ||||
| +	.cmd_rcgr = 0x2900c,
 | ||||
| +	.freq_tbl = ftbl_qdss_at_clk_src,
 | ||||
| +	.hid_width = 5,
 | ||||
| +	.parent_map = gcc_xo_gpll4_gpll0_gpll6_gpll0_div2_map,
 | ||||
| +	.clkr.hw.init = &(struct clk_init_data){
 | ||||
| +		.name = "qdss_at_clk_src",
 | ||||
| +		.parent_names = gcc_xo_gpll4_gpll0_gpll6_gpll0_div2,
 | ||||
| +		.num_parents = 5,
 | ||||
| +		.ops = &clk_rcg2_ops,
 | ||||
| +	},
 | ||||
| +};
 | ||||
| +
 | ||||
| +
 | ||||
| +struct freq_tbl ftbl_adss_pwm_clk_src[] = {
 | ||||
| +	F(19200000, P_XO, 1, 0, 0),
 | ||||
| +	F(200000000, P_GPLL0, 4, 0, 0),
 | ||||
| +	{ }
 | ||||
| +};
 | ||||
| +
 | ||||
| +struct clk_rcg2 adss_pwm_clk_src = {
 | ||||
| +	.cmd_rcgr = 0x1c008,
 | ||||
| +	.freq_tbl = ftbl_adss_pwm_clk_src,
 | ||||
| +	.hid_width = 5,
 | ||||
| +	.parent_map = gcc_xo_gpll0_map,
 | ||||
| +	.clkr.hw.init = &(struct clk_init_data){
 | ||||
| +		.name = "adss_pwm_clk_src",
 | ||||
| +		.parent_data = gcc_xo_gpll0,
 | ||||
| +		.num_parents = 2,
 | ||||
| +		.ops = &clk_rcg2_ops,
 | ||||
| +	},
 | ||||
| +};
 | ||||
| +
 | ||||
|  static struct clk_branch gcc_blsp1_ahb_clk = { | ||||
|  	.halt_reg = 0x01008, | ||||
|  	.clkr = { | ||||
| @@ -4225,13 +4319,7 @@ static struct clk_branch gcc_gp3_clk = {
 | ||||
|  	}, | ||||
|  }; | ||||
|   | ||||
| -static const struct freq_tbl ftbl_pcie_rchng_clk_src[] = {
 | ||||
| -	F(19200000, P_XO, 1, 0, 0),
 | ||||
| -	F(100000000, P_GPLL0, 8, 0, 0),
 | ||||
| -	{ }
 | ||||
| -};
 | ||||
| -
 | ||||
| -static struct clk_rcg2 pcie0_rchng_clk_src = {
 | ||||
| +struct clk_rcg2 pcie0_rchng_clk_src = {
 | ||||
|  	.cmd_rcgr = 0x75070, | ||||
|  	.freq_tbl = ftbl_pcie_rchng_clk_src, | ||||
|  	.hid_width = 5, | ||||
| @@ -4323,6 +4411,114 @@ static const struct alpha_pll_config nss
 | ||||
|  	.alpha_en_mask = BIT(24), | ||||
|  }; | ||||
|   | ||||
| +static struct clk_branch gcc_snoc_bus_timeout2_ahb_clk = {
 | ||||
| +	.halt_reg = 0x4700c,
 | ||||
| +	.halt_bit = 31,
 | ||||
| +	.clkr = {
 | ||||
| +		.enable_reg = 0x4700c,
 | ||||
| +		.enable_mask = BIT(0),
 | ||||
| +		.hw.init = &(struct clk_init_data){
 | ||||
| +			.name = "gcc_snoc_bus_timeout2_ahb_clk",
 | ||||
| +			.parent_names = (const char *[]){
 | ||||
| +				"usb0_master_clk_src"
 | ||||
| +			},
 | ||||
| +			.num_parents = 1,
 | ||||
| +			.flags = CLK_SET_RATE_PARENT,
 | ||||
| +			.ops = &clk_branch2_ops,
 | ||||
| +		},
 | ||||
| +	},
 | ||||
| +};
 | ||||
| +
 | ||||
| +static struct clk_branch gcc_snoc_bus_timeout3_ahb_clk = {
 | ||||
| +	.halt_reg = 0x47014,
 | ||||
| +	.halt_bit = 31,
 | ||||
| +	.clkr = {
 | ||||
| +		.enable_reg = 0x47014,
 | ||||
| +		.enable_mask = BIT(0),
 | ||||
| +		.hw.init = &(struct clk_init_data){
 | ||||
| +			.name = "gcc_snoc_bus_timeout3_ahb_clk",
 | ||||
| +			.parent_names = (const char *[]){
 | ||||
| +				"usb1_master_clk_src"
 | ||||
| +			},
 | ||||
| +			.num_parents = 1,
 | ||||
| +			.flags = CLK_SET_RATE_PARENT,
 | ||||
| +			.ops = &clk_branch2_ops,
 | ||||
| +		},
 | ||||
| +	},
 | ||||
| +};
 | ||||
| +
 | ||||
| +static struct clk_branch gcc_dcc_clk = {
 | ||||
| +	.halt_reg = 0x77004,
 | ||||
| +	.halt_bit = 31,
 | ||||
| +	.clkr = {
 | ||||
| +		.enable_reg = 0x77004,
 | ||||
| +		.enable_mask = BIT(0),
 | ||||
| +		.hw.init = &(struct clk_init_data){
 | ||||
| +			.name = "gcc_dcc_clk",
 | ||||
| +			.parent_names = (const char *[]){
 | ||||
| +				"pcnoc_clk_src"
 | ||||
| +			},
 | ||||
| +			.num_parents = 1,
 | ||||
| +			.flags = CLK_SET_RATE_PARENT,
 | ||||
| +			.ops = &clk_branch2_ops,
 | ||||
| +		},
 | ||||
| +	},
 | ||||
| +};
 | ||||
| +
 | ||||
| +static struct clk_branch gcc_qdss_at_clk = {
 | ||||
| +	.halt_reg = 0x29024,
 | ||||
| +	.halt_bit = 31,
 | ||||
| +	.clkr = {
 | ||||
| +		.enable_reg = 0x29024,
 | ||||
| +		.enable_mask = BIT(0),
 | ||||
| +		.hw.init = &(struct clk_init_data){
 | ||||
| +			.name = "gcc_qdss_at_clk",
 | ||||
| +			.parent_names = (const char *[]){
 | ||||
| +				"qdss_at_clk_src"
 | ||||
| +			},
 | ||||
| +			.num_parents = 1,
 | ||||
| +			.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
 | ||||
| +			.ops = &clk_branch2_ops,
 | ||||
| +		},
 | ||||
| +	},
 | ||||
| +};
 | ||||
| +
 | ||||
| +static struct clk_branch gcc_qdss_dap_clk = {
 | ||||
| +	.halt_reg = 0x29084,
 | ||||
| +	.halt_bit = 31,
 | ||||
| +	.clkr = {
 | ||||
| +		.enable_reg = 0x29084,
 | ||||
| +		.enable_mask = BIT(0),
 | ||||
| +		.hw.init = &(struct clk_init_data){
 | ||||
| +			.name = "gcc_qdss_dap_clk",
 | ||||
| +			.parent_names = (const char *[]){
 | ||||
| +				"qdss_dap_sync_clk_src"
 | ||||
| +			},
 | ||||
| +			.num_parents = 1,
 | ||||
| +			.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
 | ||||
| +			.ops = &clk_branch2_ops,
 | ||||
| +		},
 | ||||
| +	},
 | ||||
| +};
 | ||||
| +
 | ||||
| +static struct clk_branch gcc_adss_pwm_clk = {
 | ||||
| +	.halt_reg = 0x1c020,
 | ||||
| +	.halt_bit = 31,
 | ||||
| +	.clkr = {
 | ||||
| +		.enable_reg = 0x1c020,
 | ||||
| +		.enable_mask = BIT(0),
 | ||||
| +		.hw.init = &(struct clk_init_data){
 | ||||
| +			.name = "gcc_adss_pwm_clk",
 | ||||
| +			.parent_names = (const char *[]){
 | ||||
| +				"adss_pwm_clk_src"
 | ||||
| +			},
 | ||||
| +			.num_parents = 1,
 | ||||
| +			.flags = CLK_SET_RATE_PARENT,
 | ||||
| +			.ops = &clk_branch2_ops,
 | ||||
| +		},
 | ||||
| +	},
 | ||||
| +};
 | ||||
| +
 | ||||
|  static struct clk_hw *gcc_ipq8074_hws[] = { | ||||
|  	&gpll0_out_main_div2.hw, | ||||
|  	&gpll6_out_main_div2.hw, | ||||
| @@ -4331,6 +4527,7 @@ static struct clk_hw *gcc_ipq8074_hws[]
 | ||||
|  	&gcc_xo_div4_clk_src.hw, | ||||
|  	&nss_noc_clk_src.hw, | ||||
|  	&nss_ppe_cdiv_clk_src.hw, | ||||
| +	&qdss_dap_sync_clk_src.hw,
 | ||||
|  }; | ||||
|   | ||||
|  static struct clk_regmap *gcc_ipq8074_clks[] = { | ||||
| @@ -4562,6 +4759,15 @@ static struct clk_regmap *gcc_ipq8074_cl
 | ||||
|  	[GCC_PCIE0_RCHNG_CLK] = &gcc_pcie0_rchng_clk.clkr, | ||||
|  	[GCC_PCIE0_AXI_S_BRIDGE_CLK] = &gcc_pcie0_axi_s_bridge_clk.clkr, | ||||
|  	[GCC_CRYPTO_PPE_CLK] = &gcc_crypto_ppe_clk.clkr, | ||||
| +    [GCC_SNOC_BUS_TIMEOUT2_AHB_CLK] = &gcc_snoc_bus_timeout2_ahb_clk.clkr,
 | ||||
| +    [GCC_SNOC_BUS_TIMEOUT3_AHB_CLK] = &gcc_snoc_bus_timeout3_ahb_clk.clkr,
 | ||||
| +    [GCC_DCC_CLK] = &gcc_dcc_clk.clkr,
 | ||||
| +    [QDSS_TSCTR_CLK_SRC] = &qdss_tsctr_clk_src.clkr,
 | ||||
| +    [QDSS_AT_CLK_SRC] = &qdss_at_clk_src.clkr,
 | ||||
| +    [GCC_QDSS_AT_CLK] = &gcc_qdss_at_clk.clkr,
 | ||||
| +    [GCC_QDSS_DAP_CLK] = &gcc_qdss_dap_clk.clkr,
 | ||||
| +    [ADSS_PWM_CLK_SRC] = &adss_pwm_clk_src.clkr,
 | ||||
| +    [GCC_ADSS_PWM_CLK] = &gcc_adss_pwm_clk.clkr,
 | ||||
|  }; | ||||
|   | ||||
|  static const struct qcom_reset_map gcc_ipq8074_resets[] = { | ||||
| --- a/include/dt-bindings/clock/qcom,gcc-ipq8074.h
 | ||||
| +++ b/include/dt-bindings/clock/qcom,gcc-ipq8074.h
 | ||||
| @@ -230,10 +230,19 @@
 | ||||
|  #define GCC_GP1_CLK				221 | ||||
|  #define GCC_GP2_CLK				222 | ||||
|  #define GCC_GP3_CLK				223 | ||||
| -#define GCC_PCIE0_AXI_S_BRIDGE_CLK		224
 | ||||
| -#define GCC_PCIE0_RCHNG_CLK_SRC			225
 | ||||
| -#define GCC_PCIE0_RCHNG_CLK			226
 | ||||
| -#define GCC_CRYPTO_PPE_CLK			227
 | ||||
| +#define GCC_CRYPTO_PPE_CLK             224
 | ||||
| +#define GCC_PCIE0_RCHNG_CLK_SRC        225
 | ||||
| +#define GCC_PCIE0_RCHNG_CLK            226
 | ||||
| +#define GCC_PCIE0_AXI_S_BRIDGE_CLK         227
 | ||||
| +#define GCC_SNOC_BUS_TIMEOUT2_AHB_CLK      228
 | ||||
| +#define GCC_SNOC_BUS_TIMEOUT3_AHB_CLK      229
 | ||||
| +#define GCC_DCC_CLK                230
 | ||||
| +#define ADSS_PWM_CLK_SRC           231
 | ||||
| +#define GCC_ADSS_PWM_CLK           232
 | ||||
| +#define QDSS_TSCTR_CLK_SRC         233
 | ||||
| +#define QDSS_AT_CLK_SRC            234
 | ||||
| +#define GCC_QDSS_AT_CLK            235
 | ||||
| +#define GCC_QDSS_DAP_CLK           236
 | ||||
|   | ||||
|  #define GCC_BLSP1_BCR				0 | ||||
|  #define GCC_BLSP1_QUP1_BCR			1 | ||||
|  | @ -0,0 +1,44 @@ | |||
| From 462aa0c53397ec5bf78e3e7f68aa8a3ca300f4ba Mon Sep 17 00:00:00 2001 | ||||
| From: Selvam Sathappan Periakaruppan <speriaka@codeaurora.org> | ||||
| Date: Tue, 24 Mar 2020 19:09:38 +0530 | ||||
| Subject: [PATCH 5/8] clk: qcom: ipq8074: Fix gcc_snoc_bus_timeout_ahb_clk | ||||
|  offset | ||||
| 
 | ||||
| By default, the ipq8074 V2 clks are provided in the gcc driver. | ||||
| Updating the gcc_snoc_bus_timeout_ahb_clk offsets also as needed | ||||
| in ipq8074 V2. | ||||
| 
 | ||||
| Change-Id: I5a6e98d002f5c3354a804e55dd9ebb1f83f7f974 | ||||
| Signed-off-by: Selvam Sathappan Periakaruppan <speriaka@codeaurora.org> | ||||
| ---
 | ||||
|  drivers/clk/qcom/gcc-ipq8074.c | 8 ++++---- | ||||
|  1 file changed, 4 insertions(+), 4 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/clk/qcom/gcc-ipq8074.c
 | ||||
| +++ b/drivers/clk/qcom/gcc-ipq8074.c
 | ||||
| @@ -4412,10 +4412,10 @@ static const struct alpha_pll_config nss
 | ||||
|  }; | ||||
|   | ||||
|  static struct clk_branch gcc_snoc_bus_timeout2_ahb_clk = { | ||||
| -	.halt_reg = 0x4700c,
 | ||||
| +	.halt_reg = 0x47014,
 | ||||
|  	.halt_bit = 31, | ||||
|  	.clkr = { | ||||
| -		.enable_reg = 0x4700c,
 | ||||
| +		.enable_reg = 0x47014,
 | ||||
|  		.enable_mask = BIT(0), | ||||
|  		.hw.init = &(struct clk_init_data){ | ||||
|  			.name = "gcc_snoc_bus_timeout2_ahb_clk", | ||||
| @@ -4430,10 +4430,10 @@ static struct clk_branch gcc_snoc_bus_ti
 | ||||
|  }; | ||||
|   | ||||
|  static struct clk_branch gcc_snoc_bus_timeout3_ahb_clk = { | ||||
| -	.halt_reg = 0x47014,
 | ||||
| +	.halt_reg = 0x4701C,
 | ||||
|  	.halt_bit = 31, | ||||
|  	.clkr = { | ||||
| -		.enable_reg = 0x47014,
 | ||||
| +		.enable_reg = 0x4701C,
 | ||||
|  		.enable_mask = BIT(0), | ||||
|  		.hw.init = &(struct clk_init_data){ | ||||
|  			.name = "gcc_snoc_bus_timeout3_ahb_clk", | ||||
|  | @ -0,0 +1,41 @@ | |||
| From 52315bec6ed633b6a71f28b746029602f8bd70b9 Mon Sep 17 00:00:00 2001 | ||||
| From: Balaji Prakash J <bjagadee@codeaurora.org> | ||||
| Date: Wed, 22 Apr 2020 20:35:30 +0530 | ||||
| Subject: [PATCH] clk: ipq8074: fix gcc_blsp1_ahb_clk properties | ||||
| 
 | ||||
| All the voting enabled clocks does not support the enable | ||||
| from CBCR register. So, updated gcc_blsp1_ahb_clk enable | ||||
| register and mask to enable bit in APCS_CLOCK_BRANCH_ENA_VOTE. | ||||
| 
 | ||||
| Also, the voting controlled clocks are shared among multiple | ||||
| components like APSS, RPM, NSS, TZ, etc. So, turning the | ||||
| voting off from APSS does not make the clock off if it has | ||||
| been voted from another component. Added the flag | ||||
| BRANCH_HALT_VOTED in order to skip checking the clock | ||||
| disable status. | ||||
| 
 | ||||
| This change is referred from the below commits, | ||||
| 1. 246b4fb3af9bd65d8af794aac2f0e7b1ed9cc2dd | ||||
| 2. c8374157d5ae91d3b3e0d513d62808a798b32d3a | ||||
| 
 | ||||
| Signed-off-by: Balaji Prakash J <bjagadee@codeaurora.org> | ||||
| Change-Id: I505cb560b31ad27a02c165fbe13bb33a2fc7d230 | ||||
| ---
 | ||||
|  drivers/clk/qcom/gcc-ipq8074.c | 5 +++-- | ||||
|  1 file changed, 3 insertions(+), 2 deletions(-) | ||||
| 
 | ||||
| --- a/drivers/clk/qcom/gcc-ipq8074.c
 | ||||
| +++ b/drivers/clk/qcom/gcc-ipq8074.c
 | ||||
| @@ -2126,9 +2126,10 @@ struct clk_rcg2 adss_pwm_clk_src = {
 | ||||
|   | ||||
|  static struct clk_branch gcc_blsp1_ahb_clk = { | ||||
|  	.halt_reg = 0x01008, | ||||
| +	.halt_check = BRANCH_HALT_VOTED,
 | ||||
|  	.clkr = { | ||||
| -		.enable_reg = 0x01008,
 | ||||
| -		.enable_mask = BIT(0),
 | ||||
| +		.enable_reg = 0x0b004,
 | ||||
| +		.enable_mask = BIT(10),
 | ||||
|  		.hw.init = &(struct clk_init_data){ | ||||
|  			.name = "gcc_blsp1_ahb_clk", | ||||
|  			.parent_hws = (const struct clk_hw *[]){ | ||||
|  | @ -0,0 +1,878 @@ | |||
| --- a/include/linux/if_bridge.h
 | ||||
| +++ b/include/linux/if_bridge.h
 | ||||
| @@ -69,6 +69,9 @@ void brioctl_set(int (*hook)(struct net
 | ||||
|  			     void __user *uarg)); | ||||
|  int br_ioctl_call(struct net *net, struct net_bridge *br, unsigned int cmd, | ||||
|  		  struct ifreq *ifr, void __user *uarg); | ||||
| +extern void br_dev_update_stats(struct net_device *dev,
 | ||||
| +				struct rtnl_link_stats64 *nlstats);
 | ||||
| +extern bool br_is_hairpin_enabled(struct net_device *dev);
 | ||||
|   | ||||
|  #if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING) | ||||
|  int br_multicast_list_adjacent(struct net_device *dev, | ||||
| @@ -211,4 +214,42 @@ static inline clock_t br_get_ageing_time
 | ||||
|  } | ||||
|  #endif | ||||
|   | ||||
| +/* QCA NSS ECM support - Start */
 | ||||
| +extern struct net_device *br_port_dev_get(struct net_device *dev,
 | ||||
| +					  unsigned char *addr,
 | ||||
| +					  struct sk_buff *skb,
 | ||||
| +					  unsigned int cookie);
 | ||||
| +extern void br_refresh_fdb_entry(struct net_device *dev, const char *addr);
 | ||||
| +extern void br_fdb_entry_refresh(struct net_device *dev, const char *addr, __u16 vid);
 | ||||
| +extern struct net_bridge_fdb_entry *br_fdb_has_entry(struct net_device *dev,
 | ||||
| +						     const char *addr,
 | ||||
| +						     __u16 vid);
 | ||||
| +extern void br_fdb_update_register_notify(struct notifier_block *nb);
 | ||||
| +extern void br_fdb_update_unregister_notify(struct notifier_block *nb);
 | ||||
| +
 | ||||
| +typedef struct net_bridge_port *br_port_dev_get_hook_t(struct net_device *dev,
 | ||||
| +						       struct sk_buff *skb,
 | ||||
| +						       unsigned char *addr,
 | ||||
| +						       unsigned int cookie);
 | ||||
| +extern br_port_dev_get_hook_t __rcu *br_port_dev_get_hook;
 | ||||
| +
 | ||||
| +#define BR_FDB_EVENT_ADD     0x01
 | ||||
| +#define BR_FDB_EVENT_DEL     0x02
 | ||||
| +
 | ||||
| +struct br_fdb_event {
 | ||||
| +	struct net_device *dev;
 | ||||
| +	unsigned char      addr[6];
 | ||||
| +	unsigned char      is_local;
 | ||||
| +	struct net_bridge *br;
 | ||||
| +	struct net_device *orig_dev;
 | ||||
| +};
 | ||||
| +extern void br_fdb_register_notify(struct notifier_block *nb);
 | ||||
| +extern void br_fdb_unregister_notify(struct notifier_block *nb);
 | ||||
| +
 | ||||
| +typedef struct net_bridge_port *br_get_dst_hook_t(
 | ||||
| +		const struct net_bridge_port *src,
 | ||||
| +		struct sk_buff **skb);
 | ||||
| +extern br_get_dst_hook_t __rcu *br_get_dst_hook;
 | ||||
| +/* QCA NSS ECM support - End */
 | ||||
| +
 | ||||
|  #endif | ||||
| --- a/include/linux/if_vlan.h
 | ||||
| +++ b/include/linux/if_vlan.h
 | ||||
| @@ -143,7 +143,10 @@ extern struct net_device *__vlan_find_de
 | ||||
|  extern int vlan_for_each(struct net_device *dev, | ||||
|  			 int (*action)(struct net_device *dev, int vid, | ||||
|  				       void *arg), void *arg); | ||||
| +extern void __vlan_dev_update_accel_stats(struct net_device *dev,
 | ||||
| +				    struct rtnl_link_stats64 *stats); /* QCA NSS ECM support */
 | ||||
|  extern struct net_device *vlan_dev_real_dev(const struct net_device *dev); | ||||
| +extern struct net_device *vlan_dev_next_dev(const struct net_device *dev); /* QCA NSS ECM support */
 | ||||
|  extern u16 vlan_dev_vlan_id(const struct net_device *dev); | ||||
|  extern __be16 vlan_dev_vlan_proto(const struct net_device *dev); | ||||
|   | ||||
| @@ -236,6 +239,12 @@ extern void vlan_vids_del_by_dev(struct
 | ||||
|  extern bool vlan_uses_dev(const struct net_device *dev); | ||||
|   | ||||
|  #else | ||||
| +static inline void __vlan_dev_update_accel_stats(struct net_device *dev,
 | ||||
| +					   struct rtnl_link_stats64 *stats)
 | ||||
| +{
 | ||||
| +
 | ||||
| +} /* QCA NSS ECM support - End */
 | ||||
| +
 | ||||
|  static inline struct net_device * | ||||
|  __vlan_find_dev_deep_rcu(struct net_device *real_dev, | ||||
|  		     __be16 vlan_proto, u16 vlan_id) | ||||
| --- a/include/linux/netdevice.h
 | ||||
| +++ b/include/linux/netdevice.h
 | ||||
| @@ -2855,6 +2855,10 @@ enum netdev_cmd {
 | ||||
|  	NETDEV_OFFLOAD_XSTATS_DISABLE, | ||||
|  	NETDEV_OFFLOAD_XSTATS_REPORT_USED, | ||||
|  	NETDEV_OFFLOAD_XSTATS_REPORT_DELTA, | ||||
| +	/* QCA NSS ECM Support - Start */
 | ||||
| +	NETDEV_BR_JOIN,
 | ||||
| +	NETDEV_BR_LEAVE,
 | ||||
| +	/* QCA NSS ECM Support - End */
 | ||||
|  }; | ||||
|  const char *netdev_cmd_to_name(enum netdev_cmd cmd); | ||||
|   | ||||
| --- a/include/net/ip6_route.h
 | ||||
| +++ b/include/net/ip6_route.h
 | ||||
| @@ -211,6 +211,11 @@ void rt6_multipath_rebalance(struct fib6
 | ||||
|  void rt6_uncached_list_add(struct rt6_info *rt); | ||||
|  void rt6_uncached_list_del(struct rt6_info *rt); | ||||
|   | ||||
| +/* QCA NSS ECM support - Start */
 | ||||
| +int rt6_register_notifier(struct notifier_block *nb);
 | ||||
| +int rt6_unregister_notifier(struct notifier_block *nb);
 | ||||
| +/* QCA NSS ECM support - End */
 | ||||
| +
 | ||||
|  static inline const struct rt6_info *skb_rt6_info(const struct sk_buff *skb) | ||||
|  { | ||||
|  	const struct dst_entry *dst = skb_dst(skb); | ||||
| --- a/include/net/neighbour.h
 | ||||
| +++ b/include/net/neighbour.h
 | ||||
| @@ -249,6 +249,13 @@ static inline int neigh_parms_family(str
 | ||||
|  	return p->tbl->family; | ||||
|  } | ||||
|   | ||||
| +/* QCA NSS ECM support - Start */
 | ||||
| +struct neigh_mac_update {
 | ||||
| +	unsigned char old_mac[ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))];
 | ||||
| +	unsigned char update_mac[ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))];
 | ||||
| +};
 | ||||
| +/* QCA NSS ECM support - End */
 | ||||
| +
 | ||||
|  #define NEIGH_PRIV_ALIGN	sizeof(long long) | ||||
|  #define NEIGH_ENTRY_SIZE(size)	ALIGN((size), NEIGH_PRIV_ALIGN) | ||||
|   | ||||
| @@ -397,6 +404,11 @@ int neigh_xmit(int fam, struct net_devic
 | ||||
|  void pneigh_for_each(struct neigh_table *tbl, | ||||
|  		     void (*cb)(struct pneigh_entry *)); | ||||
|   | ||||
| +/* QCA NSS ECM support - Start */
 | ||||
| +extern void neigh_mac_update_register_notify(struct notifier_block *nb);
 | ||||
| +extern void neigh_mac_update_unregister_notify(struct notifier_block *nb);
 | ||||
| +/* QCA NSS ECM support - End */
 | ||||
| +
 | ||||
|  struct neigh_seq_state { | ||||
|  	struct seq_net_private p; | ||||
|  	struct neigh_table *tbl; | ||||
| @@ -602,4 +614,5 @@ static inline void neigh_update_is_route
 | ||||
|  		*notify = 1; | ||||
|  	} | ||||
|  } | ||||
| +
 | ||||
|  #endif | ||||
| --- a/include/net/route.h
 | ||||
| +++ b/include/net/route.h
 | ||||
| @@ -240,6 +240,11 @@ struct rtable *rt_dst_alloc(struct net_d
 | ||||
|  			    unsigned int flags, u16 type, bool noxfrm); | ||||
|  struct rtable *rt_dst_clone(struct net_device *dev, struct rtable *rt); | ||||
|   | ||||
| +/* QCA NSS ECM support - Start */
 | ||||
| +int ip_rt_register_notifier(struct notifier_block *nb);
 | ||||
| +int ip_rt_unregister_notifier(struct notifier_block *nb);
 | ||||
| +/* QCA NSS ECM support - End */
 | ||||
| +
 | ||||
|  struct in_ifaddr; | ||||
|  void fib_add_ifaddr(struct in_ifaddr *); | ||||
|  void fib_del_ifaddr(struct in_ifaddr *, struct in_ifaddr *); | ||||
| --- a/net/bridge/br_private.h
 | ||||
| +++ b/net/bridge/br_private.h
 | ||||
| @@ -2172,4 +2172,9 @@ void br_do_proxy_suppress_arp(struct sk_
 | ||||
|  void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br, | ||||
|  		       u16 vid, struct net_bridge_port *p, struct nd_msg *msg); | ||||
|  struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb, struct nd_msg *m); | ||||
| +
 | ||||
| +/* QCA NSS ECM support - Start */
 | ||||
| +#define __br_get(__hook, __default, __args ...) \
 | ||||
| +		(__hook ? (__hook(__args)) : (__default))
 | ||||
| +/* QCA NSS ECM support - End */
 | ||||
|  #endif | ||||
| --- a/net/8021q/vlan_core.c
 | ||||
| +++ b/net/8021q/vlan_core.c
 | ||||
| @@ -72,6 +72,28 @@ bool vlan_do_receive(struct sk_buff **sk
 | ||||
|  	return true; | ||||
|  } | ||||
|   | ||||
| +/* QCA NSS ECM support - Start */
 | ||||
| +/* Update the VLAN device with statistics from network offload engines */
 | ||||
| +void __vlan_dev_update_accel_stats(struct net_device *dev,
 | ||||
| +				   struct rtnl_link_stats64 *nlstats)
 | ||||
| +{
 | ||||
| +	struct vlan_pcpu_stats *stats;
 | ||||
| +
 | ||||
| +	if (!is_vlan_dev(dev))
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +	stats = per_cpu_ptr(vlan_dev_priv(dev)->vlan_pcpu_stats, 0);
 | ||||
| +
 | ||||
| +	u64_stats_update_begin(&stats->syncp);
 | ||||
| +	u64_stats_add(&stats->rx_packets, nlstats->rx_packets);
 | ||||
| +	u64_stats_add(&stats->rx_bytes, nlstats->rx_bytes);
 | ||||
| +	u64_stats_add(&stats->tx_packets, nlstats->tx_packets);
 | ||||
| +	u64_stats_add(&stats->tx_bytes, nlstats->tx_bytes);
 | ||||
| +	u64_stats_update_end(&stats->syncp);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(__vlan_dev_update_accel_stats);
 | ||||
| +/* QCA NSS ECM support - End */
 | ||||
| +
 | ||||
|  /* Must be invoked with rcu_read_lock. */ | ||||
|  struct net_device *__vlan_find_dev_deep_rcu(struct net_device *dev, | ||||
|  					__be16 vlan_proto, u16 vlan_id) | ||||
| @@ -110,6 +132,15 @@ struct net_device *vlan_dev_real_dev(con
 | ||||
|  } | ||||
|  EXPORT_SYMBOL(vlan_dev_real_dev); | ||||
|   | ||||
| +/* QCA NSS ECM support - Start */
 | ||||
| +/* Caller is responsible to hold the reference of the returned device */
 | ||||
| +struct net_device *vlan_dev_next_dev(const struct net_device *dev)
 | ||||
| +{
 | ||||
| +	return vlan_dev_priv(dev)->real_dev;
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(vlan_dev_next_dev);
 | ||||
| +/* QCA NSS ECM support - End */
 | ||||
| +
 | ||||
|  u16 vlan_dev_vlan_id(const struct net_device *dev) | ||||
|  { | ||||
|  	return vlan_dev_priv(dev)->vlan_id; | ||||
| --- a/net/bridge/br_fdb.c
 | ||||
| +++ b/net/bridge/br_fdb.c
 | ||||
| @@ -33,6 +33,20 @@ static const struct rhashtable_params br
 | ||||
|   | ||||
|  static struct kmem_cache *br_fdb_cache __read_mostly; | ||||
|   | ||||
| +ATOMIC_NOTIFIER_HEAD(br_fdb_notifier_list);
 | ||||
| +
 | ||||
| +void br_fdb_register_notify(struct notifier_block *nb)
 | ||||
| +{
 | ||||
| +	atomic_notifier_chain_register(&br_fdb_notifier_list, nb);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL_GPL(br_fdb_register_notify);
 | ||||
| +
 | ||||
| +void br_fdb_unregister_notify(struct notifier_block *nb)
 | ||||
| +{
 | ||||
| +	atomic_notifier_chain_unregister(&br_fdb_notifier_list, nb);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL_GPL(br_fdb_unregister_notify);
 | ||||
| +
 | ||||
|  int __init br_fdb_init(void) | ||||
|  { | ||||
|  	br_fdb_cache = kmem_cache_create("bridge_fdb_cache", | ||||
| @@ -188,6 +202,25 @@ static void fdb_notify(struct net_bridge
 | ||||
|  	if (swdev_notify) | ||||
|  		br_switchdev_fdb_notify(br, fdb, type); | ||||
|   | ||||
| +	/* QCA NSS ECM support - Start */
 | ||||
| +	if (fdb->dst) {
 | ||||
| +		int event;
 | ||||
| +		struct br_fdb_event fdb_event;
 | ||||
| +
 | ||||
| +		if (type == RTM_NEWNEIGH)
 | ||||
| +			event = BR_FDB_EVENT_ADD;
 | ||||
| +		else
 | ||||
| +			event = BR_FDB_EVENT_DEL;
 | ||||
| +
 | ||||
| +		fdb_event.dev = fdb->dst->dev;
 | ||||
| +		ether_addr_copy(fdb_event.addr, fdb->key.addr.addr);
 | ||||
| +		fdb_event.is_local = test_bit(BR_FDB_LOCAL, &fdb->flags);
 | ||||
| +		atomic_notifier_call_chain(&br_fdb_notifier_list,
 | ||||
| +					   event,
 | ||||
| +					   (void *)&fdb_event);
 | ||||
| +	}
 | ||||
| +	/* QCA NSS ECM support - End */
 | ||||
| +
 | ||||
|  	skb = nlmsg_new(fdb_nlmsg_size(), GFP_ATOMIC); | ||||
|  	if (skb == NULL) | ||||
|  		goto errout; | ||||
| @@ -512,6 +545,22 @@ out:
 | ||||
|  	spin_unlock_bh(&br->hash_lock); | ||||
|  } | ||||
|   | ||||
| +/* QCA NSS ECM support - Start */
 | ||||
| +ATOMIC_NOTIFIER_HEAD(br_fdb_update_notifier_list);
 | ||||
| +
 | ||||
| +void br_fdb_update_register_notify(struct notifier_block *nb)
 | ||||
| +{
 | ||||
| +	atomic_notifier_chain_register(&br_fdb_update_notifier_list, nb);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL_GPL(br_fdb_update_register_notify);
 | ||||
| +
 | ||||
| +void br_fdb_update_unregister_notify(struct notifier_block *nb)
 | ||||
| +{
 | ||||
| +	atomic_notifier_chain_unregister(&br_fdb_update_notifier_list, nb);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL_GPL(br_fdb_update_unregister_notify);
 | ||||
| +/* QCA NSS ECM support - End */
 | ||||
| +
 | ||||
|  void br_fdb_cleanup(struct work_struct *work) | ||||
|  { | ||||
|  	struct net_bridge *br = container_of(work, struct net_bridge, | ||||
| @@ -520,6 +569,7 @@ void br_fdb_cleanup(struct work_struct *
 | ||||
|  	unsigned long delay = hold_time(br); | ||||
|  	unsigned long work_delay = delay; | ||||
|  	unsigned long now = jiffies; | ||||
| +	u8 mac_addr[6]; /* QCA NSS ECM support */
 | ||||
|   | ||||
|  	/* this part is tricky, in order to avoid blocking learning and | ||||
|  	 * consequently forwarding, we rely on rcu to delete objects with | ||||
| @@ -546,8 +596,15 @@ void br_fdb_cleanup(struct work_struct *
 | ||||
|  			work_delay = min(work_delay, this_timer - now); | ||||
|  		} else { | ||||
|  			spin_lock_bh(&br->hash_lock); | ||||
| -			if (!hlist_unhashed(&f->fdb_node))
 | ||||
| +			if (!hlist_unhashed(&f->fdb_node)) {
 | ||||
| +				ether_addr_copy(mac_addr, f->key.addr.addr);
 | ||||
|  				fdb_delete(br, f, true); | ||||
| +				/* QCA NSS ECM support - Start */
 | ||||
| +				atomic_notifier_call_chain(
 | ||||
| +					&br_fdb_update_notifier_list, 0,
 | ||||
| +					(void *)mac_addr);
 | ||||
| +				/* QCA NSS ECM support - End */
 | ||||
| +			}
 | ||||
|  			spin_unlock_bh(&br->hash_lock); | ||||
|  		} | ||||
|  	} | ||||
| @@ -879,6 +936,12 @@ void br_fdb_update(struct net_bridge *br
 | ||||
|  						      &fdb->flags))) | ||||
|  					clear_bit(BR_FDB_ADDED_BY_EXT_LEARN, | ||||
|  						  &fdb->flags); | ||||
| +
 | ||||
| +				/* QCA NSS ECM support - Start */
 | ||||
| +				atomic_notifier_call_chain(
 | ||||
| +					&br_fdb_update_notifier_list,
 | ||||
| +					0, (void *)addr);
 | ||||
| +				/* QCA NSS ECM support - End */
 | ||||
|  			} | ||||
|   | ||||
|  			if (unlikely(test_bit(BR_FDB_ADDED_BY_USER, &flags))) | ||||
| @@ -902,6 +965,64 @@ void br_fdb_update(struct net_bridge *br
 | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| +/* QCA NSS ECM support - Start */
 | ||||
| +/* Refresh FDB entries for bridge packets being forwarded by offload engines */
 | ||||
| +void br_refresh_fdb_entry(struct net_device *dev, const char *addr)
 | ||||
| +{
 | ||||
| +	struct net_bridge_port *p = br_port_get_rcu(dev);
 | ||||
| +
 | ||||
| +	if (!p || p->state == BR_STATE_DISABLED)
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +	if (!is_valid_ether_addr(addr)) {
 | ||||
| +		pr_info("bridge: Attempt to refresh with invalid ether address %pM\n",
 | ||||
| +			addr);
 | ||||
| +		return;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	rcu_read_lock();
 | ||||
| +	br_fdb_update(p->br, p, addr, 0, true);
 | ||||
| +	rcu_read_unlock();
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL_GPL(br_refresh_fdb_entry);
 | ||||
| +
 | ||||
| +/* Update timestamp of FDB entries for bridge packets being forwarded by offload engines */
 | ||||
| +void br_fdb_entry_refresh(struct net_device *dev, const char *addr, __u16 vid)
 | ||||
| +{
 | ||||
| +	struct net_bridge_fdb_entry *fdb;
 | ||||
| +	struct net_bridge_port *p = br_port_get_rcu(dev);
 | ||||
| +
 | ||||
| +	if (!p || p->state == BR_STATE_DISABLED)
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +	rcu_read_lock();
 | ||||
| +	fdb = fdb_find_rcu(&p->br->fdb_hash_tbl, addr, vid);
 | ||||
| +	if (likely(fdb)) {
 | ||||
| +		fdb->updated = jiffies;
 | ||||
| +	}
 | ||||
| +	rcu_read_unlock();
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL_GPL(br_fdb_entry_refresh);
 | ||||
| +
 | ||||
| +/* Look up the MAC address in the device's bridge fdb table */
 | ||||
| +struct net_bridge_fdb_entry *br_fdb_has_entry(struct net_device *dev,
 | ||||
| +					      const char *addr, __u16 vid)
 | ||||
| +{
 | ||||
| +	struct net_bridge_port *p = br_port_get_rcu(dev);
 | ||||
| +	struct net_bridge_fdb_entry *fdb;
 | ||||
| +
 | ||||
| +	if (!p || p->state == BR_STATE_DISABLED)
 | ||||
| +		return NULL;
 | ||||
| +
 | ||||
| +	rcu_read_lock();
 | ||||
| +	fdb = fdb_find_rcu(&p->br->fdb_hash_tbl, addr, vid);
 | ||||
| +	rcu_read_unlock();
 | ||||
| +
 | ||||
| +	return fdb;
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL_GPL(br_fdb_has_entry);
 | ||||
| +
 | ||||
| +/* QCA NSS ECM support - End */
 | ||||
|  /* Dump information about entries, in response to GETNEIGH */ | ||||
|  int br_fdb_dump(struct sk_buff *skb, | ||||
|  		struct netlink_callback *cb, | ||||
| --- a/net/bridge/br_if.c
 | ||||
| +++ b/net/bridge/br_if.c
 | ||||
| @@ -26,6 +26,12 @@
 | ||||
|   | ||||
|  #include "br_private.h" | ||||
|   | ||||
| +/* QCA NSS ECM support - Start */
 | ||||
| +/* Hook for external forwarding logic */
 | ||||
| +br_port_dev_get_hook_t __rcu *br_port_dev_get_hook __read_mostly;
 | ||||
| +EXPORT_SYMBOL_GPL(br_port_dev_get_hook);
 | ||||
| +/* QCA NSS ECM support - End */
 | ||||
| +
 | ||||
|  /* | ||||
|   * Determine initial path cost based on speed. | ||||
|   * using recommendations from 802.1d standard | ||||
| @@ -697,6 +703,8 @@ int br_add_if(struct net_bridge *br, str
 | ||||
|   | ||||
|  	kobject_uevent(&p->kobj, KOBJ_ADD); | ||||
|   | ||||
| +	call_netdevice_notifiers(NETDEV_BR_JOIN, dev); /* QCA NSS ECM support */
 | ||||
| +
 | ||||
|  	return 0; | ||||
|   | ||||
|  err6: | ||||
| @@ -732,6 +740,8 @@ int br_del_if(struct net_bridge *br, str
 | ||||
|  	if (!p || p->br != br) | ||||
|  		return -EINVAL; | ||||
|   | ||||
| +	call_netdevice_notifiers(NETDEV_BR_LEAVE, dev); /* QCA NSS ECM support */
 | ||||
| +
 | ||||
|  	/* Since more than one interface can be attached to a bridge, | ||||
|  	 * there still maybe an alternate path for netconsole to use; | ||||
|  	 * therefore there is no reason for a NETDEV_RELEASE event. | ||||
| @@ -775,3 +785,97 @@ bool br_port_flag_is_set(const struct ne
 | ||||
|  	return p->flags & flag; | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(br_port_flag_is_set); | ||||
| +
 | ||||
| +/* br_port_dev_get()
 | ||||
| + *      If a skb is provided, and the br_port_dev_get_hook_t hook exists,
 | ||||
| + *      use that to try and determine the egress port for that skb.
 | ||||
| + *      If not, or no egress port could be determined, use the given addr
 | ||||
| + *      to identify the port to which it is reachable,
 | ||||
| + *	returing a reference to the net device associated with that port.
 | ||||
| + *
 | ||||
| + * NOTE: Return NULL if given dev is not a bridge or the mac has no
 | ||||
| + * associated port.
 | ||||
| + */
 | ||||
| +struct net_device *br_port_dev_get(struct net_device *dev, unsigned char *addr,
 | ||||
| +				   struct sk_buff *skb,
 | ||||
| +				   unsigned int cookie)
 | ||||
| +{
 | ||||
| +	struct net_bridge_fdb_entry *fdbe;
 | ||||
| +	struct net_bridge *br;
 | ||||
| +	struct net_device *netdev = NULL;
 | ||||
| +
 | ||||
| +	/* Is this a bridge? */
 | ||||
| +	if (!(dev->priv_flags & IFF_EBRIDGE))
 | ||||
| +		return NULL;
 | ||||
| +
 | ||||
| +	rcu_read_lock();
 | ||||
| +
 | ||||
| +	/* If the hook exists and the skb isn't NULL, try and get the port */
 | ||||
| +	if (skb) {
 | ||||
| +		br_port_dev_get_hook_t *port_dev_get_hook;
 | ||||
| +
 | ||||
| +		port_dev_get_hook = rcu_dereference(br_port_dev_get_hook);
 | ||||
| +		if (port_dev_get_hook) {
 | ||||
| +			struct net_bridge_port *pdst =
 | ||||
| +				__br_get(port_dev_get_hook, NULL, dev, skb,
 | ||||
| +					 addr, cookie);
 | ||||
| +			if (pdst) {
 | ||||
| +				dev_hold(pdst->dev);
 | ||||
| +				netdev = pdst->dev;
 | ||||
| +				goto out;
 | ||||
| +			}
 | ||||
| +		}
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	/* Either there is no hook, or can't
 | ||||
| +	 * determine the port to use - fall back to using FDB
 | ||||
| +	 */
 | ||||
| +
 | ||||
| +	br = netdev_priv(dev);
 | ||||
| +
 | ||||
| +	/* Lookup the fdb entry and get reference to the port dev */
 | ||||
| +	fdbe = br_fdb_find_rcu(br, addr, 0);
 | ||||
| +	if (fdbe && fdbe->dst) {
 | ||||
| +		netdev = fdbe->dst->dev; /* port device */
 | ||||
| +		dev_hold(netdev);
 | ||||
| +	}
 | ||||
| +out:
 | ||||
| +	rcu_read_unlock();
 | ||||
| +	return netdev;
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL_GPL(br_port_dev_get);
 | ||||
| +
 | ||||
| +/* Update bridge statistics for bridge packets processed by offload engines */
 | ||||
| +void br_dev_update_stats(struct net_device *dev,
 | ||||
| +			 struct rtnl_link_stats64 *nlstats)
 | ||||
| +{
 | ||||
| +	struct pcpu_sw_netstats *tstats;
 | ||||
| +
 | ||||
| +	/* Is this a bridge? */
 | ||||
| +	if (!(dev->priv_flags & IFF_EBRIDGE))
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +	tstats = this_cpu_ptr(dev->tstats);
 | ||||
| +
 | ||||
| +	u64_stats_update_begin(&tstats->syncp);
 | ||||
| +	u64_stats_add(&tstats->rx_packets, nlstats->rx_packets);
 | ||||
| +	u64_stats_add(&tstats->rx_bytes, nlstats->rx_bytes);
 | ||||
| +	u64_stats_add(&tstats->tx_packets, nlstats->tx_packets);
 | ||||
| +	u64_stats_add(&tstats->tx_bytes, nlstats->tx_bytes);
 | ||||
| +	u64_stats_update_end(&tstats->syncp);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL_GPL(br_dev_update_stats);
 | ||||
| +
 | ||||
| +/* QCA NSS ECM support - Start */
 | ||||
| +/* API to know if hairpin feature is enabled/disabled on this bridge port */
 | ||||
| +bool br_is_hairpin_enabled(struct net_device *dev)
 | ||||
| +{
 | ||||
| +	struct net_bridge_port *port = br_port_get_check_rcu(dev);
 | ||||
| +
 | ||||
| +	if (likely(port))
 | ||||
| +		return port->flags & BR_HAIRPIN_MODE;
 | ||||
| +	return false;
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL_GPL(br_is_hairpin_enabled);
 | ||||
| +
 | ||||
| +/* QCA NSS ECM support - End */
 | ||||
| --- a/net/core/neighbour.c
 | ||||
| +++ b/net/core/neighbour.c
 | ||||
| @@ -1275,6 +1275,22 @@ static void neigh_update_hhs(struct neig
 | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| +/* QCA NSS ECM support - Start */
 | ||||
| +ATOMIC_NOTIFIER_HEAD(neigh_mac_update_notifier_list);
 | ||||
| +
 | ||||
| +void neigh_mac_update_register_notify(struct notifier_block *nb)
 | ||||
| +{
 | ||||
| +	atomic_notifier_chain_register(&neigh_mac_update_notifier_list, nb);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL_GPL(neigh_mac_update_register_notify);
 | ||||
| +
 | ||||
| +void neigh_mac_update_unregister_notify(struct notifier_block *nb)
 | ||||
| +{
 | ||||
| +	atomic_notifier_chain_unregister(&neigh_mac_update_notifier_list, nb);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL_GPL(neigh_mac_update_unregister_notify);
 | ||||
| +/* QCA NSS ECM support - End */
 | ||||
| +
 | ||||
|  /* Generic update routine. | ||||
|     -- lladdr is new lladdr or NULL, if it is not supplied. | ||||
|     -- new    is new state. | ||||
| @@ -1303,6 +1319,7 @@ static int __neigh_update(struct neighbo
 | ||||
|  	struct net_device *dev; | ||||
|  	int err, notify = 0; | ||||
|  	u8 old; | ||||
| +   struct neigh_mac_update nmu; /* QCA NSS ECM support */
 | ||||
|   | ||||
|  	trace_neigh_update(neigh, lladdr, new, flags, nlmsg_pid); | ||||
|   | ||||
| @@ -1317,7 +1334,10 @@ static int __neigh_update(struct neighbo
 | ||||
|  		new = old; | ||||
|  		goto out; | ||||
|  	} | ||||
| -	if (!(flags & NEIGH_UPDATE_F_ADMIN) &&
 | ||||
| +
 | ||||
| +	memset(&nmu, 0, sizeof(struct neigh_mac_update)); /* QCA NSS ECM support */
 | ||||
| +
 | ||||
| +   if (!(flags & NEIGH_UPDATE_F_ADMIN) &&
 | ||||
|  	    (old & (NUD_NOARP | NUD_PERMANENT))) | ||||
|  		goto out; | ||||
|   | ||||
| @@ -1354,7 +1374,12 @@ static int __neigh_update(struct neighbo
 | ||||
|  		   - compare new & old | ||||
|  		   - if they are different, check override flag | ||||
|  		 */ | ||||
| -		if ((old & NUD_VALID) &&
 | ||||
| +       /* QCA NSS ECM update - Start */
 | ||||
| +		memcpy(nmu.old_mac, neigh->ha, dev->addr_len);
 | ||||
| +		memcpy(nmu.update_mac, lladdr, dev->addr_len);
 | ||||
| +		/* QCA NSS ECM update - End */
 | ||||
| +
 | ||||
| +       if ((old & NUD_VALID) &&
 | ||||
|  		    !memcmp(lladdr, neigh->ha, dev->addr_len)) | ||||
|  			lladdr = neigh->ha; | ||||
|  	} else { | ||||
| @@ -1476,8 +1501,11 @@ out:
 | ||||
|  		neigh_update_gc_list(neigh); | ||||
|  	if (managed_update) | ||||
|  		neigh_update_managed_list(neigh); | ||||
| -	if (notify)
 | ||||
| +	if (notify) {
 | ||||
|  		neigh_update_notify(neigh, nlmsg_pid); | ||||
| +		atomic_notifier_call_chain(&neigh_mac_update_notifier_list, 0,
 | ||||
| +					   (struct neigh_mac_update *)&nmu); /* QCA NSS ECM support */
 | ||||
| +    }
 | ||||
|  	trace_neigh_update_done(neigh, err); | ||||
|  	return err; | ||||
|  } | ||||
| --- a/net/ipv4/fib_trie.c
 | ||||
| +++ b/net/ipv4/fib_trie.c
 | ||||
| @@ -1211,6 +1211,9 @@ static bool fib_valid_key_len(u32 key, u
 | ||||
|  static void fib_remove_alias(struct trie *t, struct key_vector *tp, | ||||
|  			     struct key_vector *l, struct fib_alias *old); | ||||
|   | ||||
| +/* Define route change notification chain. */
 | ||||
| +static BLOCKING_NOTIFIER_HEAD(iproute_chain);	/* QCA NSS ECM support */
 | ||||
| +
 | ||||
|  /* Caller must hold RTNL. */ | ||||
|  int fib_table_insert(struct net *net, struct fib_table *tb, | ||||
|  		     struct fib_config *cfg, struct netlink_ext_ack *extack) | ||||
| @@ -1404,6 +1407,9 @@ int fib_table_insert(struct net *net, st
 | ||||
|  	rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, new_fa->tb_id, | ||||
|  		  &cfg->fc_nlinfo, nlflags); | ||||
|  succeeded: | ||||
| +	blocking_notifier_call_chain(&iproute_chain,
 | ||||
| +				     RTM_NEWROUTE, fi);
 | ||||
| +
 | ||||
|  	return 0; | ||||
|   | ||||
|  out_remove_new_fa: | ||||
| @@ -1775,6 +1781,9 @@ int fib_table_delete(struct net *net, st
 | ||||
|  	if (fa_to_delete->fa_state & FA_S_ACCESSED) | ||||
|  		rt_cache_flush(cfg->fc_nlinfo.nl_net); | ||||
|   | ||||
| +	blocking_notifier_call_chain(&iproute_chain,
 | ||||
| +				     RTM_DELROUTE, fa_to_delete->fa_info);
 | ||||
| +
 | ||||
|  	fib_release_info(fa_to_delete->fa_info); | ||||
|  	alias_free_mem_rcu(fa_to_delete); | ||||
|  	return 0; | ||||
| @@ -2407,6 +2416,20 @@ void __init fib_trie_init(void)
 | ||||
|  					   0, SLAB_PANIC | SLAB_ACCOUNT, NULL); | ||||
|  } | ||||
|   | ||||
| +/* QCA NSS ECM support - Start */
 | ||||
| +int ip_rt_register_notifier(struct notifier_block *nb)
 | ||||
| +{
 | ||||
| +	return blocking_notifier_chain_register(&iproute_chain, nb);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(ip_rt_register_notifier);
 | ||||
| +
 | ||||
| +int ip_rt_unregister_notifier(struct notifier_block *nb)
 | ||||
| +{
 | ||||
| +	return blocking_notifier_chain_unregister(&iproute_chain, nb);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(ip_rt_unregister_notifier);
 | ||||
| +/* QCA NSS ECM support - End */
 | ||||
| +
 | ||||
|  struct fib_table *fib_trie_table(u32 id, struct fib_table *alias) | ||||
|  { | ||||
|  	struct fib_table *tb; | ||||
| --- a/net/ipv6/ndisc.c
 | ||||
| +++ b/net/ipv6/ndisc.c
 | ||||
| @@ -666,6 +666,7 @@ void ndisc_send_ns(struct net_device *de
 | ||||
|  	if (skb) | ||||
|  		ndisc_send_skb(skb, daddr, saddr); | ||||
|  } | ||||
| +EXPORT_SYMBOL(ndisc_send_ns);
 | ||||
|   | ||||
|  void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, | ||||
|  		   const struct in6_addr *daddr) | ||||
| --- a/net/ipv6/route.c
 | ||||
| +++ b/net/ipv6/route.c
 | ||||
| @@ -197,6 +197,9 @@ static void rt6_uncached_list_flush_dev(
 | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| +/* Define route change notification chain. */
 | ||||
| +ATOMIC_NOTIFIER_HEAD(ip6route_chain);	/* QCA NSS ECM support */
 | ||||
| +
 | ||||
|  static inline const void *choose_neigh_daddr(const struct in6_addr *p, | ||||
|  					     struct sk_buff *skb, | ||||
|  					     const void *daddr) | ||||
| @@ -3865,6 +3868,10 @@ int ip6_route_add(struct fib6_config *cf
 | ||||
|  		return PTR_ERR(rt); | ||||
|   | ||||
|  	err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, extack); | ||||
| +	if (!err)
 | ||||
| +		atomic_notifier_call_chain(&ip6route_chain,
 | ||||
| +					   RTM_NEWROUTE, rt);
 | ||||
| +
 | ||||
|  	fib6_info_release(rt); | ||||
|   | ||||
|  	return err; | ||||
| @@ -3886,6 +3893,9 @@ static int __ip6_del_rt(struct fib6_info
 | ||||
|  	err = fib6_del(rt, info); | ||||
|  	spin_unlock_bh(&table->tb6_lock); | ||||
|   | ||||
| +	if (!err)
 | ||||
| +		atomic_notifier_call_chain(&ip6route_chain,
 | ||||
| +					   RTM_DELROUTE, rt);
 | ||||
|  out: | ||||
|  	fib6_info_release(rt); | ||||
|  	return err; | ||||
| @@ -6339,6 +6349,20 @@ static int ip6_route_dev_notify(struct n
 | ||||
|  	return NOTIFY_OK; | ||||
|  } | ||||
|   | ||||
| +/* QCA NSS ECM support - Start */
 | ||||
| +int rt6_register_notifier(struct notifier_block *nb)
 | ||||
| +{
 | ||||
| +	return atomic_notifier_chain_register(&ip6route_chain, nb);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(rt6_register_notifier);
 | ||||
| +
 | ||||
| +int rt6_unregister_notifier(struct notifier_block *nb)
 | ||||
| +{
 | ||||
| +	return atomic_notifier_chain_unregister(&ip6route_chain, nb);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(rt6_unregister_notifier);
 | ||||
| +/* QCA NSS ECM support - End */
 | ||||
| +
 | ||||
|  /* | ||||
|   *	/proc | ||||
|   */ | ||||
| --- a/net/core/dev.c
 | ||||
| +++ b/net/core/dev.c
 | ||||
| @@ -1639,6 +1639,7 @@ const char *netdev_cmd_to_name(enum netd
 | ||||
|  	N(SVLAN_FILTER_PUSH_INFO) N(SVLAN_FILTER_DROP_INFO) | ||||
|  	N(PRE_CHANGEADDR) N(OFFLOAD_XSTATS_ENABLE) N(OFFLOAD_XSTATS_DISABLE) | ||||
|  	N(OFFLOAD_XSTATS_REPORT_USED) N(OFFLOAD_XSTATS_REPORT_DELTA) | ||||
| +	N(BR_JOIN) N(BR_LEAVE)
 | ||||
|  	} | ||||
|  #undef N | ||||
|  	return "UNKNOWN_NETDEV_EVENT"; | ||||
| --- a/net/ipv6/addrconf.c
 | ||||
| +++ b/net/ipv6/addrconf.c
 | ||||
| @@ -1002,6 +1002,7 @@ void inet6_ifa_finish_destroy(struct ine
 | ||||
|   | ||||
|  	kfree_rcu(ifp, rcu); | ||||
|  } | ||||
| +EXPORT_SYMBOL(inet6_ifa_finish_destroy);
 | ||||
|   | ||||
|  static void | ||||
|  ipv6_link_dev_addr(struct inet6_dev *idev, struct inet6_ifaddr *ifp) | ||||
| --- a/include/net/vxlan.h
 | ||||
| +++ b/include/net/vxlan.h
 | ||||
| @@ -432,6 +432,15 @@ static inline __be32 vxlan_compute_rco(u
 | ||||
|  	return vni_field; | ||||
|  } | ||||
|   | ||||
| +/*
 | ||||
| + * vxlan_get_vni()
 | ||||
| + *      Returns the vni corresponding to tunnel
 | ||||
| + */
 | ||||
| +static inline u32 vxlan_get_vni(struct vxlan_dev *vxlan_tun)
 | ||||
| +{
 | ||||
| +    return be32_to_cpu(vxlan_tun->cfg.vni);
 | ||||
| +}
 | ||||
| +
 | ||||
|  static inline unsigned short vxlan_get_sk_family(struct vxlan_sock *vs) | ||||
|  { | ||||
|  	return vs->sock->sk->sk_family; | ||||
| --- a/include/uapi/linux/in.h
 | ||||
| +++ b/include/uapi/linux/in.h
 | ||||
| @@ -63,6 +63,8 @@ enum {
 | ||||
|  #define IPPROTO_MTP		IPPROTO_MTP | ||||
|    IPPROTO_BEETPH = 94,		/* IP option pseudo header for BEET	*/ | ||||
|  #define IPPROTO_BEETPH		IPPROTO_BEETPH | ||||
| +  IPPROTO_ETHERIP = 97,		/* ETHERIP protocol number		*/
 | ||||
| +#define IPPROTO_ETHERIP		IPPROTO_ETHERIP
 | ||||
|    IPPROTO_ENCAP = 98,		/* Encapsulation Header			*/ | ||||
|  #define IPPROTO_ENCAP		IPPROTO_ENCAP | ||||
|    IPPROTO_PIM = 103,		/* Protocol Independent Multicast	*/ | ||||
| @@ -327,7 +329,7 @@ struct sockaddr_in {
 | ||||
|  #endif | ||||
|   | ||||
|  /* <asm/byteorder.h> contains the htonl type stuff.. */ | ||||
| -#include <asm/byteorder.h> 
 | ||||
| +#include <asm/byteorder.h>
 | ||||
|   | ||||
|   | ||||
|  #endif /* _UAPI_LINUX_IN_H */ | ||||
| --- a/tools/include/uapi/linux/in.h
 | ||||
| +++ b/tools/include/uapi/linux/in.h
 | ||||
| @@ -63,6 +63,8 @@ enum {
 | ||||
|  #define IPPROTO_MTP		IPPROTO_MTP | ||||
|    IPPROTO_BEETPH = 94,		/* IP option pseudo header for BEET	*/ | ||||
|  #define IPPROTO_BEETPH		IPPROTO_BEETPH | ||||
| +  IPPROTO_ETHERIP = 97,		/* ETHERIP protocol number		*/
 | ||||
| +#define IPPROTO_ETHERIP		IPPROTO_ETHERIP
 | ||||
|    IPPROTO_ENCAP = 98,		/* Encapsulation Header			*/ | ||||
|  #define IPPROTO_ENCAP		IPPROTO_ENCAP | ||||
|    IPPROTO_PIM = 103,		/* Protocol Independent Multicast	*/ | ||||
| @@ -327,7 +329,7 @@ struct sockaddr_in {
 | ||||
|  #endif | ||||
|   | ||||
|  /* <asm/byteorder.h> contains the htonl type stuff.. */ | ||||
| -#include <asm/byteorder.h> 
 | ||||
| +#include <asm/byteorder.h>
 | ||||
|   | ||||
|   | ||||
|  #endif /* _UAPI_LINUX_IN_H */ | ||||
| --- a/net/netfilter/nf_conntrack_ecache.c
 | ||||
| +++ b/net/netfilter/nf_conntrack_ecache.c
 | ||||
| @@ -266,7 +266,6 @@ void nf_conntrack_register_notifier(stru
 | ||||
|  	mutex_lock(&nf_ct_ecache_mutex); | ||||
|  	notify = rcu_dereference_protected(net->ct.nf_conntrack_event_cb, | ||||
|  					   lockdep_is_held(&nf_ct_ecache_mutex)); | ||||
| -	WARN_ON_ONCE(notify);
 | ||||
|  	rcu_assign_pointer(net->ct.nf_conntrack_event_cb, new); | ||||
|  	mutex_unlock(&nf_ct_ecache_mutex); | ||||
|  } | ||||
| --- a/include/net/netns/conntrack.h
 | ||||
| +++ b/include/net/netns/conntrack.h
 | ||||
| @@ -26,6 +26,7 @@ struct nf_tcp_net {
 | ||||
|  	unsigned int timeouts[TCP_CONNTRACK_TIMEOUT_MAX]; | ||||
|  	u8 tcp_loose; | ||||
|  	u8 tcp_be_liberal; | ||||
| +	u8 tcp_no_window_check;
 | ||||
|  	u8 tcp_max_retrans; | ||||
|  	u8 tcp_ignore_invalid_rst; | ||||
|  #if IS_ENABLED(CONFIG_NF_FLOW_TABLE) | ||||
| --- a/net/netfilter/nf_conntrack_proto_tcp.c
 | ||||
| +++ b/net/netfilter/nf_conntrack_proto_tcp.c
 | ||||
| @@ -513,11 +513,15 @@ tcp_in_window(struct nf_conn *ct, enum ip_conntrack_dir dir,
 | ||||
|  	struct ip_ct_tcp *state = &ct->proto.tcp; | ||||
|  	struct ip_ct_tcp_state *sender = &state->seen[dir]; | ||||
|  	struct ip_ct_tcp_state *receiver = &state->seen[!dir]; | ||||
| +	const struct nf_tcp_net *tn = nf_tcp_pernet(nf_ct_net(ct));
 | ||||
|  	__u32 seq, ack, sack, end, win, swin; | ||||
|  	bool in_recv_win, seq_ok; | ||||
|  	s32 receiver_offset; | ||||
|  	u16 win_raw; | ||||
|   | ||||
| +	if (tn->tcp_no_window_check)
 | ||||
| +		return NFCT_TCP_ACCEPT;
 | ||||
| +
 | ||||
|  	/* | ||||
|  	 * Get the required data from the packet. | ||||
|  	 */ | ||||
| @@ -1257,7 +1261,7 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct,
 | ||||
|  		 IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED && | ||||
|  		 timeouts[new_state] > timeouts[TCP_CONNTRACK_UNACK]) | ||||
|  		timeout = timeouts[TCP_CONNTRACK_UNACK]; | ||||
| -	else if (ct->proto.tcp.last_win == 0 &&
 | ||||
| +	else if (!tn->tcp_no_window_check && ct->proto.tcp.last_win == 0 &&
 | ||||
|  		 timeouts[new_state] > timeouts[TCP_CONNTRACK_RETRANS]) | ||||
|  		timeout = timeouts[TCP_CONNTRACK_RETRANS]; | ||||
|  	else | ||||
| @@ -1573,6 +1577,9 @@ void nf_conntrack_tcp_init_net(struct net *net)
 | ||||
|  	 */ | ||||
|  	tn->tcp_be_liberal = 0; | ||||
|   | ||||
| +	/* Skip Windows Check */
 | ||||
| +	tn->tcp_no_window_check = 0;
 | ||||
| +
 | ||||
|  	/* If it's non-zero, we turn off RST sequence number check */ | ||||
|  	tn->tcp_ignore_invalid_rst = 0; | ||||
|   | ||||
| --- a/net/netfilter/nf_conntrack_standalone.c
 | ||||
| +++ b/net/netfilter/nf_conntrack_standalone.c
 | ||||
| @@ -637,6 +637,7 @@ enum nf_ct_sysctl_index {
 | ||||
|  #endif | ||||
|  	NF_SYSCTL_CT_PROTO_TCP_LOOSE, | ||||
|  	NF_SYSCTL_CT_PROTO_TCP_LIBERAL, | ||||
| +	NF_SYSCTL_CT_PROTO_TCP_NO_WINDOW_CHECK,
 | ||||
|  	NF_SYSCTL_CT_PROTO_TCP_IGNORE_INVALID_RST, | ||||
|  	NF_SYSCTL_CT_PROTO_TCP_MAX_RETRANS, | ||||
|  	NF_SYSCTL_CT_PROTO_TIMEOUT_UDP, | ||||
| @@ -844,6 +845,14 @@ static struct ctl_table nf_ct_sysctl_table[] = {
 | ||||
|  		.extra1 	= SYSCTL_ZERO, | ||||
|  		.extra2 	= SYSCTL_ONE, | ||||
|  	}, | ||||
| +	[NF_SYSCTL_CT_PROTO_TCP_NO_WINDOW_CHECK] = {
 | ||||
| +		.procname       = "nf_conntrack_tcp_no_window_check",
 | ||||
| +		.maxlen         = sizeof(u8),
 | ||||
| +		.mode           = 0644,
 | ||||
| +		.proc_handler	= proc_dou8vec_minmax,
 | ||||
| +		.extra1 	= SYSCTL_ZERO,
 | ||||
| +		.extra2 	= SYSCTL_ONE,
 | ||||
| +	},
 | ||||
|  	[NF_SYSCTL_CT_PROTO_TCP_IGNORE_INVALID_RST] = { | ||||
|  		.procname	= "nf_conntrack_tcp_ignore_invalid_rst", | ||||
|  		.maxlen		= sizeof(u8), | ||||
| @@ -1054,6 +1063,7 @@ static void nf_conntrack_standalone_init_tcp_sysctl(struct net *net,
 | ||||
|   | ||||
|  	XASSIGN(LOOSE, &tn->tcp_loose); | ||||
|  	XASSIGN(LIBERAL, &tn->tcp_be_liberal); | ||||
| +	XASSIGN(NO_WINDOW_CHECK, &tn->tcp_no_window_check);
 | ||||
|  	XASSIGN(MAX_RETRANS, &tn->tcp_max_retrans); | ||||
|  	XASSIGN(IGNORE_INVALID_RST, &tn->tcp_ignore_invalid_rst); | ||||
|  #undef XASSIGN | ||||
|  | @ -0,0 +1,600 @@ | |||
| --- a/drivers/net/ppp/ppp_generic.c
 | ||||
| +++ b/drivers/net/ppp/ppp_generic.c
 | ||||
| @@ -48,6 +48,7 @@
 | ||||
|  #include <net/slhc_vj.h> | ||||
|  #include <linux/atomic.h> | ||||
|  #include <linux/refcount.h> | ||||
| +#include <linux/if_pppox.h>
 | ||||
|   | ||||
|  #include <linux/nsproxy.h> | ||||
|  #include <net/net_namespace.h> | ||||
| @@ -254,6 +255,25 @@ struct ppp_net {
 | ||||
|  #define seq_before(a, b)	((s32)((a) - (b)) < 0) | ||||
|  #define seq_after(a, b)		((s32)((a) - (b)) > 0) | ||||
|   | ||||
| +
 | ||||
| +/*
 | ||||
| + * Registration/Unregistration methods
 | ||||
| + * for PPP channel connect and disconnect event notifications.
 | ||||
| + */
 | ||||
| +RAW_NOTIFIER_HEAD(ppp_channel_connection_notifier_list);
 | ||||
| +
 | ||||
| +void ppp_channel_connection_register_notify(struct notifier_block *nb)
 | ||||
| +{
 | ||||
| +	raw_notifier_chain_register(&ppp_channel_connection_notifier_list, nb);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL_GPL(ppp_channel_connection_register_notify);
 | ||||
| +
 | ||||
| +void ppp_channel_connection_unregister_notify(struct notifier_block *nb)
 | ||||
| +{
 | ||||
| +	raw_notifier_chain_unregister(&ppp_channel_connection_notifier_list, nb);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL_GPL(ppp_channel_connection_unregister_notify);
 | ||||
| +
 | ||||
|  /* Prototypes. */ | ||||
|  static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf, | ||||
|  			struct file *file, unsigned int cmd, unsigned long arg); | ||||
| @@ -3453,7 +3473,10 @@ ppp_connect_channel(struct channel *pch,
 | ||||
|  	struct ppp_net *pn; | ||||
|  	int ret = -ENXIO; | ||||
|  	int hdrlen; | ||||
| +	int ppp_proto;
 | ||||
| +	int version;
 | ||||
|   | ||||
| +	int notify = 0;
 | ||||
|  	pn = ppp_pernet(pch->chan_net); | ||||
|   | ||||
|  	mutex_lock(&pn->all_ppp_mutex); | ||||
| @@ -3485,13 +3508,40 @@ ppp_connect_channel(struct channel *pch,
 | ||||
|  	++ppp->n_channels; | ||||
|  	pch->ppp = ppp; | ||||
|  	refcount_inc(&ppp->file.refcnt); | ||||
| +
 | ||||
| +	/* Set the netdev priv flag if the prototype
 | ||||
| +	 * is L2TP or PPTP. Return success in all cases
 | ||||
| +	 */
 | ||||
| +	if (!pch->chan)
 | ||||
| +		goto out2;
 | ||||
| +
 | ||||
| +	ppp_proto = ppp_channel_get_protocol(pch->chan);
 | ||||
| +	if (ppp_proto == PX_PROTO_PPTP) {
 | ||||
| +		ppp->dev->priv_flags_ext |= IFF_EXT_PPP_PPTP;
 | ||||
| +	} else if (ppp_proto == PX_PROTO_OL2TP) {
 | ||||
| +		version = ppp_channel_get_proto_version(pch->chan);
 | ||||
| +		if (version == 2)
 | ||||
| +			ppp->dev->priv_flags_ext |= IFF_EXT_PPP_L2TPV2;
 | ||||
| +		else if (version == 3)
 | ||||
| +			ppp->dev->priv_flags_ext |= IFF_EXT_PPP_L2TPV3;
 | ||||
| +	}
 | ||||
| +	notify = 1;
 | ||||
| +
 | ||||
| + out2:
 | ||||
|  	ppp_unlock(ppp); | ||||
|  	ret = 0; | ||||
| -
 | ||||
|   outl: | ||||
|  	write_unlock_bh(&pch->upl); | ||||
|   out: | ||||
|  	mutex_unlock(&pn->all_ppp_mutex); | ||||
| +
 | ||||
| +	if (notify && ppp && ppp->dev) {
 | ||||
| +		dev_hold(ppp->dev);
 | ||||
| +		raw_notifier_call_chain(&ppp_channel_connection_notifier_list,
 | ||||
| +					   PPP_CHANNEL_CONNECT, ppp->dev);
 | ||||
| +		dev_put(ppp->dev);
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| @@ -3509,6 +3559,13 @@ ppp_disconnect_channel(struct channel *p
 | ||||
|  	pch->ppp = NULL; | ||||
|  	write_unlock_bh(&pch->upl); | ||||
|  	if (ppp) { | ||||
| +		if (ppp->dev) {
 | ||||
| +			dev_hold(ppp->dev);
 | ||||
| +			raw_notifier_call_chain(&ppp_channel_connection_notifier_list,
 | ||||
| +					   PPP_CHANNEL_DISCONNECT, ppp->dev);
 | ||||
| +			dev_put(ppp->dev);
 | ||||
| +		}
 | ||||
| +
 | ||||
|  		/* remove it from the ppp unit's list */ | ||||
|  		ppp_lock(ppp); | ||||
|  		list_del(&pch->clist); | ||||
| @@ -3588,6 +3645,222 @@ static void *unit_find(struct idr *p, in
 | ||||
|  	return idr_find(p, n); | ||||
|  } | ||||
|   | ||||
| +/* Updates the PPP interface statistics. */
 | ||||
| +void ppp_update_stats(struct net_device *dev, unsigned long rx_packets,
 | ||||
| +		      unsigned long rx_bytes, unsigned long tx_packets,
 | ||||
| +		      unsigned long tx_bytes, unsigned long rx_errors,
 | ||||
| +		      unsigned long tx_errors, unsigned long rx_dropped,
 | ||||
| +		      unsigned long tx_dropped)
 | ||||
| +{
 | ||||
| +	struct ppp *ppp;
 | ||||
| +
 | ||||
| +	if (!dev)
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +	if (dev->type != ARPHRD_PPP)
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +	ppp = netdev_priv(dev);
 | ||||
| +
 | ||||
| +	ppp_xmit_lock(ppp);
 | ||||
| +	ppp->stats64.tx_packets += tx_packets;
 | ||||
| +	ppp->stats64.tx_bytes += tx_bytes;
 | ||||
| +	ppp->dev->stats.tx_errors += tx_errors;
 | ||||
| +	ppp->dev->stats.tx_dropped += tx_dropped;
 | ||||
| +	if (tx_packets)
 | ||||
| +		ppp->last_xmit = jiffies;
 | ||||
| +	ppp_xmit_unlock(ppp);
 | ||||
| +
 | ||||
| +	ppp_recv_lock(ppp);
 | ||||
| +	ppp->stats64.rx_packets += rx_packets;
 | ||||
| +	ppp->stats64.rx_bytes += rx_bytes;
 | ||||
| +	ppp->dev->stats.rx_errors += rx_errors;
 | ||||
| +	ppp->dev->stats.rx_dropped += rx_dropped;
 | ||||
| +	if (rx_packets)
 | ||||
| +		ppp->last_recv = jiffies;
 | ||||
| +	ppp_recv_unlock(ppp);
 | ||||
| +}
 | ||||
| +
 | ||||
| +/* Returns >0 if the device is a multilink PPP netdevice, 0 if not or < 0 if
 | ||||
| + * the device is not PPP.
 | ||||
| + */
 | ||||
| +int ppp_is_multilink(struct net_device *dev)
 | ||||
| +{
 | ||||
| +	struct ppp *ppp;
 | ||||
| +	unsigned int flags;
 | ||||
| +
 | ||||
| +	if (!dev)
 | ||||
| +		return -1;
 | ||||
| +
 | ||||
| +	if (dev->type != ARPHRD_PPP)
 | ||||
| +		return -1;
 | ||||
| +
 | ||||
| +	ppp = netdev_priv(dev);
 | ||||
| +	ppp_lock(ppp);
 | ||||
| +	flags = ppp->flags;
 | ||||
| +	ppp_unlock(ppp);
 | ||||
| +
 | ||||
| +	if (flags & SC_MULTILINK)
 | ||||
| +		return 1;
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(ppp_is_multilink);
 | ||||
| +
 | ||||
| +/* ppp_channel_get_protocol()
 | ||||
| + *	Call this to obtain the underlying protocol of the PPP channel,
 | ||||
| + *	e.g. PX_PROTO_OE
 | ||||
| + *
 | ||||
| + * NOTE: Some channels do not use PX sockets so the protocol value may be very
 | ||||
| + * different for them.
 | ||||
| + * NOTE: -1 indicates failure.
 | ||||
| + * NOTE: Once you know the channel protocol you may then either cast 'chan' to
 | ||||
| + * its sub-class or use the channel protocol specific API's as provided by that
 | ||||
| + * channel sub type.
 | ||||
| + */
 | ||||
| +int ppp_channel_get_protocol(struct ppp_channel *chan)
 | ||||
| +{
 | ||||
| +	if (!chan->ops->get_channel_protocol)
 | ||||
| +		return -1;
 | ||||
| +
 | ||||
| +	return chan->ops->get_channel_protocol(chan);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(ppp_channel_get_protocol);
 | ||||
| +
 | ||||
| +/* ppp_channel_get_proto_version()
 | ||||
| + *	Call this to get channel protocol version
 | ||||
| + */
 | ||||
| +int ppp_channel_get_proto_version(struct ppp_channel *chan)
 | ||||
| +{
 | ||||
| +	if (!chan->ops->get_channel_protocol_ver)
 | ||||
| +		return -1;
 | ||||
| +
 | ||||
| +	return chan->ops->get_channel_protocol_ver(chan);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(ppp_channel_get_proto_version);
 | ||||
| +
 | ||||
| +/* ppp_channel_hold()
 | ||||
| + *	Call this to hold a channel.
 | ||||
| + *
 | ||||
| + * Returns true on success or false if the hold could not happen.
 | ||||
| + *
 | ||||
| + * NOTE: chan must be protected against destruction during this call -
 | ||||
| + * either by correct locking etc. or because you already have an implicit
 | ||||
| + * or explicit hold to the channel already and this is an additional hold.
 | ||||
| + */
 | ||||
| +bool ppp_channel_hold(struct ppp_channel *chan)
 | ||||
| +{
 | ||||
| +	if (!chan->ops->hold)
 | ||||
| +		return false;
 | ||||
| +
 | ||||
| +	chan->ops->hold(chan);
 | ||||
| +	return true;
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(ppp_channel_hold);
 | ||||
| +
 | ||||
| +/* ppp_channel_release()
 | ||||
| + *	Call this to release a hold you have upon a channel
 | ||||
| + */
 | ||||
| +void ppp_channel_release(struct ppp_channel *chan)
 | ||||
| +{
 | ||||
| +	chan->ops->release(chan);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(ppp_channel_release);
 | ||||
| +
 | ||||
| +/* Check if ppp xmit lock is on hold */
 | ||||
| +bool ppp_is_xmit_locked(struct net_device *dev)
 | ||||
| +{
 | ||||
| +	struct ppp *ppp;
 | ||||
| +
 | ||||
| +	if (!dev)
 | ||||
| +		return false;
 | ||||
| +
 | ||||
| +	if (dev->type != ARPHRD_PPP)
 | ||||
| +		return false;
 | ||||
| +
 | ||||
| +	ppp = netdev_priv(dev);
 | ||||
| +	if (!ppp)
 | ||||
| +		return false;
 | ||||
| +
 | ||||
| +	if (spin_is_locked(&(ppp)->wlock))
 | ||||
| +		return true;
 | ||||
| +
 | ||||
| +	return false;
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(ppp_is_xmit_locked);
 | ||||
| +
 | ||||
| +/* ppp_hold_channels()
 | ||||
| + *	Returns the PPP channels of the PPP device, storing each one into
 | ||||
| + *	channels[].
 | ||||
| + *
 | ||||
| + * channels[] has chan_sz elements.
 | ||||
| + * This function returns the number of channels stored, up to chan_sz.
 | ||||
| + * It will return < 0 if the device is not PPP.
 | ||||
| + *
 | ||||
| + * You MUST release the channels using ppp_release_channels().
 | ||||
| + */
 | ||||
| +int ppp_hold_channels(struct net_device *dev, struct ppp_channel *channels[],
 | ||||
| +		      unsigned int chan_sz)
 | ||||
| +{
 | ||||
| +	struct ppp *ppp;
 | ||||
| +	int c;
 | ||||
| +	struct channel *pch;
 | ||||
| +
 | ||||
| +	if (!dev)
 | ||||
| +		return -1;
 | ||||
| +
 | ||||
| +	if (dev->type != ARPHRD_PPP)
 | ||||
| +		return -1;
 | ||||
| +
 | ||||
| +	ppp = netdev_priv(dev);
 | ||||
| +
 | ||||
| +	c = 0;
 | ||||
| +	ppp_lock(ppp);
 | ||||
| +	list_for_each_entry(pch, &ppp->channels, clist) {
 | ||||
| +		struct ppp_channel *chan;
 | ||||
| +
 | ||||
| +		if (!pch->chan) {
 | ||||
| +			/* Channel is going / gone away */
 | ||||
| +			continue;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		if (c == chan_sz) {
 | ||||
| +			/* No space to record channel */
 | ||||
| +			ppp_unlock(ppp);
 | ||||
| +			return c;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		/* Hold the channel, if supported */
 | ||||
| +		chan = pch->chan;
 | ||||
| +		if (!chan->ops->hold)
 | ||||
| +			continue;
 | ||||
| +
 | ||||
| +		chan->ops->hold(chan);
 | ||||
| +
 | ||||
| +		 /* Record the channel */
 | ||||
| +		channels[c++] = chan;
 | ||||
| +	}
 | ||||
| +	ppp_unlock(ppp);
 | ||||
| +	return c;
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(ppp_hold_channels);
 | ||||
| +
 | ||||
| +/* ppp_release_channels()
 | ||||
| + *	Releases channels
 | ||||
| + */
 | ||||
| +void ppp_release_channels(struct ppp_channel *channels[], unsigned int chan_sz)
 | ||||
| +{
 | ||||
| +	unsigned int c;
 | ||||
| +
 | ||||
| +	for (c = 0; c < chan_sz; ++c) {
 | ||||
| +		struct ppp_channel *chan;
 | ||||
| +
 | ||||
| +		chan = channels[c];
 | ||||
| +		chan->ops->release(chan);
 | ||||
| +	}
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(ppp_release_channels);
 | ||||
| +
 | ||||
|  /* Module/initialization stuff */ | ||||
|   | ||||
|  module_init(ppp_init); | ||||
| @@ -3604,6 +3877,7 @@ EXPORT_SYMBOL(ppp_input_error);
 | ||||
|  EXPORT_SYMBOL(ppp_output_wakeup); | ||||
|  EXPORT_SYMBOL(ppp_register_compressor); | ||||
|  EXPORT_SYMBOL(ppp_unregister_compressor); | ||||
| +EXPORT_SYMBOL(ppp_update_stats);
 | ||||
|  MODULE_LICENSE("GPL"); | ||||
|  MODULE_ALIAS_CHARDEV(PPP_MAJOR, 0); | ||||
|  MODULE_ALIAS_RTNL_LINK("ppp"); | ||||
| --- a/drivers/net/ppp/pppoe.c
 | ||||
| +++ b/drivers/net/ppp/pppoe.c
 | ||||
| @@ -62,6 +62,7 @@
 | ||||
|  #include <linux/inetdevice.h> | ||||
|  #include <linux/etherdevice.h> | ||||
|  #include <linux/skbuff.h> | ||||
| +#include <linux/if_arp.h>
 | ||||
|  #include <linux/init.h> | ||||
|  #include <linux/if_ether.h> | ||||
|  #include <linux/if_pppox.h> | ||||
| @@ -87,7 +88,7 @@
 | ||||
|  static int __pppoe_xmit(struct sock *sk, struct sk_buff *skb); | ||||
|   | ||||
|  static const struct proto_ops pppoe_ops; | ||||
| -static const struct ppp_channel_ops pppoe_chan_ops;
 | ||||
| +static const struct pppoe_channel_ops pppoe_chan_ops;
 | ||||
|   | ||||
|  /* per-net private data for this module */ | ||||
|  static unsigned int pppoe_net_id __read_mostly; | ||||
| @@ -692,7 +693,7 @@ static int pppoe_connect(struct socket *
 | ||||
|   | ||||
|  		po->chan.mtu = dev->mtu - sizeof(struct pppoe_hdr) - 2; | ||||
|  		po->chan.private = sk; | ||||
| -		po->chan.ops = &pppoe_chan_ops;
 | ||||
| +		po->chan.ops = (struct ppp_channel_ops *)&pppoe_chan_ops;
 | ||||
|   | ||||
|  		error = ppp_register_net_channel(dev_net(dev), &po->chan); | ||||
|  		if (error) { | ||||
| @@ -995,9 +996,80 @@ static int pppoe_fill_forward_path(struc
 | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| -static const struct ppp_channel_ops pppoe_chan_ops = {
 | ||||
| -	.start_xmit = pppoe_xmit,
 | ||||
| -	.fill_forward_path = pppoe_fill_forward_path,
 | ||||
| +/************************************************************************
 | ||||
| + *
 | ||||
| + * function called by generic PPP driver to hold channel
 | ||||
| + *
 | ||||
| + ***********************************************************************/
 | ||||
| +static void pppoe_hold_chan(struct ppp_channel *chan)
 | ||||
| +{
 | ||||
| +	struct sock *sk = (struct sock *)chan->private;
 | ||||
| +
 | ||||
| +	sock_hold(sk);
 | ||||
| +}
 | ||||
| +
 | ||||
| +/************************************************************************
 | ||||
| + *
 | ||||
| + * function called by generic PPP driver to release channel
 | ||||
| + *
 | ||||
| + ***********************************************************************/
 | ||||
| +static void pppoe_release_chan(struct ppp_channel *chan)
 | ||||
| +{
 | ||||
| +	struct sock *sk = (struct sock *)chan->private;
 | ||||
| +
 | ||||
| +	sock_put(sk);
 | ||||
| +}
 | ||||
| +
 | ||||
| +/************************************************************************
 | ||||
| + *
 | ||||
| + * function called to get the channel protocol type
 | ||||
| + *
 | ||||
| + ***********************************************************************/
 | ||||
| +static int pppoe_get_channel_protocol(struct ppp_channel *chan)
 | ||||
| +{
 | ||||
| +	return PX_PROTO_OE;
 | ||||
| +}
 | ||||
| +
 | ||||
| +/************************************************************************
 | ||||
| + *
 | ||||
| + * function called to get the PPPoE channel addressing
 | ||||
| + * NOTE: This function returns a HOLD to the netdevice
 | ||||
| + *
 | ||||
| + ***********************************************************************/
 | ||||
| +static int pppoe_get_addressing(struct ppp_channel *chan,
 | ||||
| +				 struct pppoe_opt *addressing)
 | ||||
| +{
 | ||||
| +	struct sock *sk = (struct sock *)chan->private;
 | ||||
| +	struct pppox_sock *po = pppox_sk(sk);
 | ||||
| +	int err = 0;
 | ||||
| +
 | ||||
| +	*addressing = po->proto.pppoe;
 | ||||
| +	if (!addressing->dev)
 | ||||
| +		return -ENODEV;
 | ||||
| +
 | ||||
| +	dev_hold(addressing->dev);
 | ||||
| +	return err;
 | ||||
| +}
 | ||||
| +
 | ||||
| +/* pppoe_channel_addressing_get()
 | ||||
| + *	Return PPPoE channel specific addressing information.
 | ||||
| + */
 | ||||
| +int pppoe_channel_addressing_get(struct ppp_channel *chan,
 | ||||
| +				  struct pppoe_opt *addressing)
 | ||||
| +{
 | ||||
| +	return pppoe_get_addressing(chan, addressing);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(pppoe_channel_addressing_get);
 | ||||
| +
 | ||||
| +static const struct pppoe_channel_ops pppoe_chan_ops = {
 | ||||
| +	/* PPPoE specific channel ops */
 | ||||
| +	.get_addressing = pppoe_get_addressing,
 | ||||
| +	/* General ppp channel ops */
 | ||||
| +	.ops.start_xmit = pppoe_xmit,
 | ||||
| +	.ops.get_channel_protocol = pppoe_get_channel_protocol,
 | ||||
| +	.ops.hold = pppoe_hold_chan,
 | ||||
| +	.ops.release = pppoe_release_chan,
 | ||||
| +	.ops.fill_forward_path = pppoe_fill_forward_path,
 | ||||
|  }; | ||||
|   | ||||
|  static int pppoe_recvmsg(struct socket *sock, struct msghdr *m, | ||||
| --- a/include/linux/if_pppox.h
 | ||||
| +++ b/include/linux/if_pppox.h
 | ||||
| @@ -91,4 +91,17 @@ enum {
 | ||||
|      PPPOX_DEAD		= 16  /* dead, useless, please clean me up!*/ | ||||
|  }; | ||||
|   | ||||
| +/*
 | ||||
| + * PPPoE Channel specific operations
 | ||||
| + */
 | ||||
| +struct pppoe_channel_ops {
 | ||||
| +	/* Must be first - general to all PPP channels */
 | ||||
| +	struct ppp_channel_ops ops;
 | ||||
| +	int (*get_addressing)(struct ppp_channel *, struct pppoe_opt *);
 | ||||
| +};
 | ||||
| +
 | ||||
| +/* Return PPPoE channel specific addressing information */
 | ||||
| +extern int pppoe_channel_addressing_get(struct ppp_channel *chan,
 | ||||
| +					 struct pppoe_opt *addressing);
 | ||||
| +
 | ||||
|  #endif /* !(__LINUX_IF_PPPOX_H) */ | ||||
| --- a/include/linux/netdevice.h
 | ||||
| +++ b/include/linux/netdevice.h
 | ||||
| @@ -1762,6 +1762,36 @@ enum netdev_priv_flags {
 | ||||
|  	IFF_NO_IP_ALIGN			= BIT_ULL(34), | ||||
|  }; | ||||
|   | ||||
| +/**
 | ||||
| + * enum netdev_priv_flags_ext - &struct net_device priv_flags_ext
 | ||||
| + *
 | ||||
| + * These flags are used to check for device type and can be
 | ||||
| + * set and used by the drivers
 | ||||
| + *
 | ||||
| + * @IFF_EXT_TUN_TAP: device is a TUN/TAP device
 | ||||
| + * @IFF_EXT_PPP_L2TPV2: device is a L2TPV2 device
 | ||||
| + * @IFF_EXT_PPP_L2TPV3: device is a L2TPV3 device
 | ||||
| + * @IFF_EXT_PPP_PPTP: device is a PPTP device
 | ||||
| + * @IFF_EXT_GRE_V4_TAP: device is a GRE IPv4 TAP device
 | ||||
| + * @IFF_EXT_GRE_V6_TAP: device is a GRE IPv6 TAP device
 | ||||
| + * @IFF_EXT_IFB: device is an IFB device
 | ||||
| + * @IFF_EXT_MAPT: device is an MAPT device
 | ||||
| + * @IFF_EXT_HW_NO_OFFLOAD: device is an NON Offload device
 | ||||
| + * @IFF_EXT_L2TPV3: device is a L2TPV3 Ethernet device
 | ||||
| + */
 | ||||
| +enum netdev_priv_flags_ext {
 | ||||
| +	IFF_EXT_TUN_TAP			= 1<<0,
 | ||||
| +	IFF_EXT_PPP_L2TPV2		= 1<<1,
 | ||||
| +	IFF_EXT_PPP_L2TPV3		= 1<<2,
 | ||||
| +	IFF_EXT_PPP_PPTP		= 1<<3,
 | ||||
| +	IFF_EXT_GRE_V4_TAP		= 1<<4,
 | ||||
| +	IFF_EXT_GRE_V6_TAP		= 1<<5,
 | ||||
| +	IFF_EXT_IFB			= 1<<6,
 | ||||
| +	IFF_EXT_MAPT			= 1<<7,
 | ||||
| +	IFF_EXT_HW_NO_OFFLOAD		= 1<<8,
 | ||||
| +	IFF_EXT_ETH_L2TPV3		= 1<<9,
 | ||||
| +};
 | ||||
| +
 | ||||
|  #define IFF_802_1Q_VLAN			IFF_802_1Q_VLAN | ||||
|  #define IFF_EBRIDGE			IFF_EBRIDGE | ||||
|  #define IFF_BONDING			IFF_BONDING | ||||
| @@ -2075,6 +2103,7 @@ struct net_device {
 | ||||
|  	/* Read-mostly cache-line for fast-path access */ | ||||
|  	unsigned int		flags; | ||||
|  	unsigned long long	priv_flags; | ||||
| +	unsigned int		priv_flags_ext;
 | ||||
|  	const struct net_device_ops *netdev_ops; | ||||
|  	int			ifindex; | ||||
|  	unsigned short		gflags; | ||||
| --- a/include/linux/ppp_channel.h
 | ||||
| +++ b/include/linux/ppp_channel.h
 | ||||
| @@ -19,6 +19,10 @@
 | ||||
|  #include <linux/skbuff.h> | ||||
|  #include <linux/poll.h> | ||||
|  #include <net/net_namespace.h> | ||||
| +#include <linux/notifier.h>
 | ||||
| +
 | ||||
| +#define PPP_CHANNEL_DISCONNECT	0
 | ||||
| +#define PPP_CHANNEL_CONNECT	1
 | ||||
|   | ||||
|  struct net_device_path; | ||||
|  struct net_device_path_ctx; | ||||
| @@ -30,9 +34,19 @@ struct ppp_channel_ops {
 | ||||
|  	int	(*start_xmit)(struct ppp_channel *, struct sk_buff *); | ||||
|  	/* Handle an ioctl call that has come in via /dev/ppp. */ | ||||
|  	int	(*ioctl)(struct ppp_channel *, unsigned int, unsigned long); | ||||
| +	/* Get channel protocol type, one of PX_PROTO_XYZ or specific to
 | ||||
| +	 * the channel subtype
 | ||||
| +	 */
 | ||||
| +	int (*get_channel_protocol)(struct ppp_channel *);
 | ||||
| +	/* Get channel protocol version */
 | ||||
| +	int (*get_channel_protocol_ver)(struct ppp_channel *);
 | ||||
| +	/* Hold the channel from being destroyed */
 | ||||
| +	void (*hold)(struct ppp_channel *);
 | ||||
| +	/* Release hold on the channel */
 | ||||
| +	void (*release)(struct ppp_channel *);
 | ||||
|  	int	(*fill_forward_path)(struct net_device_path_ctx *, | ||||
| -				     struct net_device_path *,
 | ||||
| -				     const struct ppp_channel *);
 | ||||
| +				struct net_device_path *,
 | ||||
| +				const struct ppp_channel *);
 | ||||
|  }; | ||||
|   | ||||
|  struct ppp_channel { | ||||
| @@ -76,6 +90,51 @@ extern int ppp_unit_number(struct ppp_ch
 | ||||
|  /* Get the device name associated with a channel, or NULL if none */ | ||||
|  extern char *ppp_dev_name(struct ppp_channel *); | ||||
|   | ||||
| +/* Call this to obtain the underlying protocol of the PPP channel,
 | ||||
| + * e.g. PX_PROTO_OE
 | ||||
| + */
 | ||||
| +extern int ppp_channel_get_protocol(struct ppp_channel *);
 | ||||
| +
 | ||||
| +/* Call this get protocol version */
 | ||||
| +extern int ppp_channel_get_proto_version(struct ppp_channel *);
 | ||||
| +
 | ||||
| +/* Call this to hold a channel */
 | ||||
| +extern bool ppp_channel_hold(struct ppp_channel *);
 | ||||
| +
 | ||||
| +/* Call this to release a hold you have upon a channel */
 | ||||
| +extern void ppp_channel_release(struct ppp_channel *);
 | ||||
| +
 | ||||
| +/* Release hold on PPP channels */
 | ||||
| +extern void ppp_release_channels(struct ppp_channel *channels[],
 | ||||
| +				 unsigned int chan_sz);
 | ||||
| +
 | ||||
| +/* Hold PPP channels for the PPP device */
 | ||||
| +extern int ppp_hold_channels(struct net_device *dev,
 | ||||
| +				struct ppp_channel *channels[],
 | ||||
| +				unsigned int chan_sz);
 | ||||
| +
 | ||||
| +/* Test if ppp xmit lock is locked */
 | ||||
| +extern bool ppp_is_xmit_locked(struct net_device *dev);
 | ||||
| +
 | ||||
| +/* Test if the ppp device is a multi-link ppp device */
 | ||||
| +extern int ppp_is_multilink(struct net_device *dev);
 | ||||
| +
 | ||||
| +/* Register the PPP channel connect notifier */
 | ||||
| +extern void ppp_channel_connection_register_notify(struct notifier_block *nb);
 | ||||
| +
 | ||||
| +/* Unregister the PPP channel connect notifier */
 | ||||
| +extern void ppp_channel_connection_unregister_notify(struct notifier_block *nb);
 | ||||
| +
 | ||||
| +/* Update statistics of the PPP net_device by incrementing related
 | ||||
| + * statistics field value with corresponding parameter
 | ||||
| + */
 | ||||
| +extern void ppp_update_stats(struct net_device *dev, unsigned long rx_packets,
 | ||||
| +			     unsigned long rx_bytes, unsigned long tx_packets,
 | ||||
| +			     unsigned long tx_bytes, unsigned long rx_errors,
 | ||||
| +			     unsigned long tx_errors, unsigned long rx_dropped,
 | ||||
| +			     unsigned long tx_dropped);
 | ||||
| +
 | ||||
| +
 | ||||
|  /* | ||||
|   * SMP locking notes: | ||||
|   * The channel code must ensure that when it calls ppp_unregister_channel, | ||||
|  | @ -0,0 +1,49 @@ | |||
| --- a/drivers/net/bonding/bond_main.c
 | ||||
| +++ b/drivers/net/bonding/bond_main.c
 | ||||
| @@ -208,6 +208,7 @@ atomic_t netpoll_block_tx = ATOMIC_INIT(
 | ||||
|  #endif | ||||
|   | ||||
|  unsigned int bond_net_id __read_mostly; | ||||
| +static unsigned long bond_id_mask = 0xFFFFFFF0; /* QCA NSS ECM bonding support */
 | ||||
|   | ||||
|  static const struct flow_dissector_key flow_keys_bonding_keys[] = { | ||||
|  	{ | ||||
| @@ -5793,8 +5794,14 @@ static void bond_destructor(struct net_d
 | ||||
|  	if (bond->wq) | ||||
|  		destroy_workqueue(bond->wq); | ||||
|   | ||||
| +	/* QCA NSS ECM bonding support - Start */
 | ||||
| +	if (bond->id != (~0U))
 | ||||
| +		clear_bit(bond->id, &bond_id_mask);
 | ||||
| +	/* QCA NSS ECM bonding support - End */
 | ||||
| +
 | ||||
|  	if (bond->rr_tx_counter) | ||||
|  		free_percpu(bond->rr_tx_counter); | ||||
| +
 | ||||
|  } | ||||
|   | ||||
|  void bond_setup(struct net_device *bond_dev) | ||||
| @@ -6358,6 +6365,13 @@ int bond_create(struct net *net, const c
 | ||||
|   | ||||
|  	bond_work_init_all(bond); | ||||
|   | ||||
| +	/* QCA NSS ECM bonding support - Start */
 | ||||
| +	bond->id = ~0U;
 | ||||
| +	if (bond_id_mask != (~0UL)) {
 | ||||
| +		bond->id = (u32)ffz(bond_id_mask);
 | ||||
| +		set_bit(bond->id, &bond_id_mask);
 | ||||
| +	}
 | ||||
| +	/* QCA NSS ECM bonding support - End */
 | ||||
|  out: | ||||
|  	rtnl_unlock(); | ||||
|  	return res; | ||||
| --- a/include/net/bonding.h
 | ||||
| +++ b/include/net/bonding.h
 | ||||
| @@ -265,6 +265,7 @@ struct bonding {
 | ||||
|  	spinlock_t ipsec_lock; | ||||
|  #endif /* CONFIG_XFRM_OFFLOAD */ | ||||
|  	struct bpf_prog *xdp_prog; | ||||
| +	u32 id;/* QCA NSS ECM bonding support */
 | ||||
|  }; | ||||
|   | ||||
|  #define bond_slave_get_rcu(dev) \ | ||||
|  | @ -0,0 +1,685 @@ | |||
| --- a/drivers/net/bonding/bond_3ad.c
 | ||||
| +++ b/drivers/net/bonding/bond_3ad.c
 | ||||
| @@ -115,6 +115,40 @@ static void ad_marker_response_received(
 | ||||
|  					struct port *port); | ||||
|  static void ad_update_actor_keys(struct port *port, bool reset); | ||||
|   | ||||
| +/* QCA NSS ECM bonding support - Start */
 | ||||
| +struct bond_cb __rcu *bond_cb;
 | ||||
| +
 | ||||
| +int bond_register_cb(struct bond_cb *cb)
 | ||||
| +{
 | ||||
| +	struct bond_cb *lag_cb;
 | ||||
| +
 | ||||
| +	lag_cb = kzalloc(sizeof(*lag_cb), GFP_ATOMIC | __GFP_NOWARN);
 | ||||
| +	if (!lag_cb) {
 | ||||
| +		return -1;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	memcpy((void *)lag_cb, (void *)cb, sizeof(*cb));
 | ||||
| +
 | ||||
| +	rcu_read_lock();
 | ||||
| +	rcu_assign_pointer(bond_cb, lag_cb);
 | ||||
| +	rcu_read_unlock();
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(bond_register_cb);
 | ||||
| +
 | ||||
| +void bond_unregister_cb(void)
 | ||||
| +{
 | ||||
| +	struct bond_cb *lag_cb_main;
 | ||||
| +
 | ||||
| +	rcu_read_lock();
 | ||||
| +	lag_cb_main = rcu_dereference(bond_cb);
 | ||||
| +	rcu_assign_pointer(bond_cb, NULL);
 | ||||
| +	rcu_read_unlock();
 | ||||
| +
 | ||||
| +	kfree(lag_cb_main);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(bond_unregister_cb);
 | ||||
| +/* QCA NSS ECM bonding support - End */
 | ||||
|   | ||||
|  /* ================= api to bonding and kernel code ================== */ | ||||
|   | ||||
| @@ -1064,7 +1098,31 @@ static void ad_mux_machine(struct port *
 | ||||
|  			ad_disable_collecting_distributing(port, | ||||
|  							   update_slave_arr); | ||||
|  			port->ntt = true; | ||||
| +
 | ||||
| +		/* QCA NSS ECM bonding support - Start */
 | ||||
| +			/* Send a notificaton about change in state of this
 | ||||
| +			* port. We only want to handle case where port moves
 | ||||
| +			* from AD_MUX_COLLECTING_DISTRIBUTING ->
 | ||||
| +			* AD_MUX_ATTACHED.
 | ||||
| +			*/
 | ||||
| +			if (bond_slave_is_up(port->slave) &&
 | ||||
| +			    (last_state == AD_MUX_COLLECTING_DISTRIBUTING)) {
 | ||||
| +				struct bond_cb *lag_cb_main;
 | ||||
| +
 | ||||
| +				rcu_read_lock();
 | ||||
| +				lag_cb_main = rcu_dereference(bond_cb);
 | ||||
| +				if (lag_cb_main &&
 | ||||
| +				    lag_cb_main->bond_cb_link_down) {
 | ||||
| +					struct net_device *dev;
 | ||||
| +
 | ||||
| +					dev = port->slave->dev;
 | ||||
| +					lag_cb_main->bond_cb_link_down(dev);
 | ||||
| +				}
 | ||||
| +				rcu_read_unlock();
 | ||||
| +			}
 | ||||
| +
 | ||||
|  			break; | ||||
| +		/* QCA NSS ECM bonding support - End */
 | ||||
|  		case AD_MUX_COLLECTING_DISTRIBUTING: | ||||
|  			port->actor_oper_port_state |= LACP_STATE_COLLECTING; | ||||
|  			port->actor_oper_port_state |= LACP_STATE_DISTRIBUTING; | ||||
| @@ -1908,6 +1966,7 @@ static void ad_enable_collecting_distrib
 | ||||
|  					      bool *update_slave_arr) | ||||
|  { | ||||
|  	if (port->aggregator->is_active) { | ||||
| +		struct bond_cb *lag_cb_main; /* QCA NSS ECM bonding support */
 | ||||
|  		slave_dbg(port->slave->bond->dev, port->slave->dev, | ||||
|  			  "Enabling port %d (LAG %d)\n", | ||||
|  			  port->actor_port_number, | ||||
| @@ -1915,6 +1974,16 @@ static void ad_enable_collecting_distrib
 | ||||
|  		__enable_port(port); | ||||
|  		/* Slave array needs update */ | ||||
|  		*update_slave_arr = true; | ||||
| +
 | ||||
| +		/* QCA NSS ECM bonding support - Start */
 | ||||
| +		rcu_read_lock();
 | ||||
| +		lag_cb_main = rcu_dereference(bond_cb);
 | ||||
| +
 | ||||
| +		if (lag_cb_main && lag_cb_main->bond_cb_link_up)
 | ||||
| +			lag_cb_main->bond_cb_link_up(port->slave->dev);
 | ||||
| +
 | ||||
| +		rcu_read_unlock();
 | ||||
| +		/* QCA NSS ECM bonding support - End */
 | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| @@ -2674,6 +2743,104 @@ int bond_3ad_get_active_agg_info(struct
 | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| +/* QCA NSS ECM bonding support - Start */
 | ||||
| +/* bond_3ad_get_tx_dev - Calculate egress interface for a given packet,
 | ||||
| + * for a LAG that is configured in 802.3AD mode
 | ||||
| + * @skb: pointer to skb to be egressed
 | ||||
| + * @src_mac: pointer to source L2 address
 | ||||
| + * @dst_mac: pointer to destination L2 address
 | ||||
| + * @src: pointer to source L3 address
 | ||||
| + * @dst: pointer to destination L3 address
 | ||||
| + * @protocol: L3 protocol id from L2 header
 | ||||
| + * @bond_dev: pointer to bond master device
 | ||||
| + *
 | ||||
| + * If @skb is NULL, bond_xmit_hash is used to calculate hash using L2/L3
 | ||||
| + * addresses.
 | ||||
| + *
 | ||||
| + * Returns: Either valid slave device, or NULL otherwise
 | ||||
| + */
 | ||||
| +struct net_device *bond_3ad_get_tx_dev(struct sk_buff *skb, u8 *src_mac,
 | ||||
| +				       u8 *dst_mac, void *src,
 | ||||
| +				       void *dst, u16 protocol,
 | ||||
| +				       struct net_device *bond_dev,
 | ||||
| +				       __be16 *layer4hdr)
 | ||||
| +{
 | ||||
| +	struct bonding *bond = netdev_priv(bond_dev);
 | ||||
| +	struct aggregator *agg;
 | ||||
| +	struct ad_info ad_info;
 | ||||
| +	struct list_head *iter;
 | ||||
| +	struct slave *slave;
 | ||||
| +	struct slave *first_ok_slave = NULL;
 | ||||
| +	u32 hash = 0;
 | ||||
| +	int slaves_in_agg;
 | ||||
| +	int slave_agg_no = 0;
 | ||||
| +	int agg_id;
 | ||||
| +
 | ||||
| +	if (__bond_3ad_get_active_agg_info(bond, &ad_info)) {
 | ||||
| +		pr_debug("%s: Error: __bond_3ad_get_active_agg_info failed\n",
 | ||||
| +			 bond_dev->name);
 | ||||
| +		return NULL;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	slaves_in_agg = ad_info.ports;
 | ||||
| +	agg_id = ad_info.aggregator_id;
 | ||||
| +
 | ||||
| +	if (slaves_in_agg == 0) {
 | ||||
| +		pr_debug("%s: Error: active aggregator is empty\n",
 | ||||
| +			 bond_dev->name);
 | ||||
| +		return NULL;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	if (skb) {
 | ||||
| +		hash = bond_xmit_hash(bond, skb);
 | ||||
| +		slave_agg_no = hash % slaves_in_agg;
 | ||||
| +	} else {
 | ||||
| +		if (bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER23 &&
 | ||||
| +		    bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER2 &&
 | ||||
| +		    bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER34) {
 | ||||
| +			pr_debug("%s: Error: Unsupported hash policy for 802.3AD fast path\n",
 | ||||
| +				 bond_dev->name);
 | ||||
| +			return NULL;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		hash = bond_xmit_hash_without_skb(src_mac, dst_mac,
 | ||||
| +						  src, dst, protocol,
 | ||||
| +						  bond_dev, layer4hdr);
 | ||||
| +		slave_agg_no = hash % slaves_in_agg;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	bond_for_each_slave_rcu(bond, slave, iter) {
 | ||||
| +		agg = SLAVE_AD_INFO(slave)->port.aggregator;
 | ||||
| +		if (!agg || agg->aggregator_identifier != agg_id)
 | ||||
| +			continue;
 | ||||
| +
 | ||||
| +		if (slave_agg_no >= 0) {
 | ||||
| +			if (!first_ok_slave && bond_slave_can_tx(slave))
 | ||||
| +				first_ok_slave = slave;
 | ||||
| +			slave_agg_no--;
 | ||||
| +			continue;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		if (bond_slave_can_tx(slave))
 | ||||
| +			return slave->dev;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	if (slave_agg_no >= 0) {
 | ||||
| +		pr_err("%s: Error: Couldn't find a slave to tx on for aggregator ID %d\n",
 | ||||
| +		       bond_dev->name, agg_id);
 | ||||
| +		return NULL;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	/* we couldn't find any suitable slave after the agg_no, so use the
 | ||||
| +	 * first suitable found, if found.
 | ||||
| +	 */
 | ||||
| +	if (first_ok_slave)
 | ||||
| +		return first_ok_slave->dev;
 | ||||
| +
 | ||||
| +	return NULL;
 | ||||
| +}
 | ||||
| +/* QCA NSS ECM bonding support - End */
 | ||||
| +
 | ||||
|  int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond, | ||||
|  			 struct slave *slave) | ||||
|  { | ||||
| --- a/drivers/net/bonding/bond_main.c
 | ||||
| +++ b/drivers/net/bonding/bond_main.c
 | ||||
| @@ -286,6 +286,21 @@ const char *bond_mode_name(int mode)
 | ||||
|  	return names[mode]; | ||||
|  } | ||||
|   | ||||
| +/* QCA NSS ECM bonding support */
 | ||||
| +int bond_get_id(struct net_device *bond_dev)
 | ||||
| +{
 | ||||
| +	struct bonding *bond;
 | ||||
| +
 | ||||
| +	if (!((bond_dev->priv_flags & IFF_BONDING) &&
 | ||||
| +	      (bond_dev->flags & IFF_MASTER)))
 | ||||
| +		return -EINVAL;
 | ||||
| +
 | ||||
| +	bond = netdev_priv(bond_dev);
 | ||||
| +	return bond->id;
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(bond_get_id);
 | ||||
| +/* QCA NSS ECM bonding support */
 | ||||
| +
 | ||||
|  /** | ||||
|   * bond_dev_queue_xmit - Prepare skb for xmit. | ||||
|   * | ||||
| @@ -1185,6 +1200,23 @@ void bond_change_active_slave(struct bon
 | ||||
|  			if (BOND_MODE(bond) == BOND_MODE_8023AD) | ||||
|  				bond_3ad_handle_link_change(new_active, BOND_LINK_UP); | ||||
|   | ||||
| +           /* QCA NSS ECM bonding support - Start */
 | ||||
| +			if (bond->params.mode == BOND_MODE_XOR) {
 | ||||
| +				struct bond_cb *lag_cb_main;
 | ||||
| +
 | ||||
| +				rcu_read_lock();
 | ||||
| +				lag_cb_main = rcu_dereference(bond_cb);
 | ||||
| +				if (lag_cb_main &&
 | ||||
| +				    lag_cb_main->bond_cb_link_up) {
 | ||||
| +					struct net_device *dev;
 | ||||
| +
 | ||||
| +					dev = new_active->dev;
 | ||||
| +					lag_cb_main->bond_cb_link_up(dev);
 | ||||
| +				}
 | ||||
| +				rcu_read_unlock();
 | ||||
| +			}
 | ||||
| +           /* QCA NSS ECM bonding support - End */
 | ||||
| +
 | ||||
|  			if (bond_is_lb(bond)) | ||||
|  				bond_alb_handle_link_change(bond, new_active, BOND_LINK_UP); | ||||
|  		} else { | ||||
| @@ -1808,6 +1840,7 @@ int bond_enslave(struct net_device *bond
 | ||||
|  	const struct net_device_ops *slave_ops = slave_dev->netdev_ops; | ||||
|  	struct slave *new_slave = NULL, *prev_slave; | ||||
|  	struct sockaddr_storage ss; | ||||
| +	struct bond_cb *lag_cb_main; /* QCA NSS ECM bonding support */
 | ||||
|  	int link_reporting; | ||||
|  	int res = 0, i; | ||||
|   | ||||
| @@ -2251,6 +2284,15 @@ int bond_enslave(struct net_device *bond
 | ||||
|  		   bond_is_active_slave(new_slave) ? "an active" : "a backup", | ||||
|  		   new_slave->link != BOND_LINK_DOWN ? "an up" : "a down"); | ||||
|   | ||||
| +	/* QCA NSS ECM bonding support - Start */
 | ||||
| +	rcu_read_lock();
 | ||||
| +	lag_cb_main = rcu_dereference(bond_cb);
 | ||||
| +	if (lag_cb_main && lag_cb_main->bond_cb_enslave)
 | ||||
| +		lag_cb_main->bond_cb_enslave(slave_dev);
 | ||||
| +
 | ||||
| +	rcu_read_unlock();
 | ||||
| +	/* QCA NSS ECM bonding support - End */
 | ||||
| +
 | ||||
|  	/* enslave is successful */ | ||||
|  	bond_queue_slave_event(new_slave); | ||||
|  	return 0; | ||||
| @@ -2316,6 +2358,15 @@ err_undo_flags:
 | ||||
|  		} | ||||
|  	} | ||||
|   | ||||
| +	/* QCA NSS ECM bonding support - Start */
 | ||||
| +	rcu_read_lock();
 | ||||
| +	lag_cb_main = rcu_dereference(bond_cb);
 | ||||
| +	if (lag_cb_main && lag_cb_main->bond_cb_enslave)
 | ||||
| +		lag_cb_main->bond_cb_enslave(slave_dev);
 | ||||
| +
 | ||||
| +	rcu_read_unlock();
 | ||||
| +	/* QCA NSS ECM bonding support - End */
 | ||||
| +
 | ||||
|  	return res; | ||||
|  } | ||||
|   | ||||
| @@ -2337,6 +2388,7 @@ static int __bond_release_one(struct net
 | ||||
|  	struct bonding *bond = netdev_priv(bond_dev); | ||||
|  	struct slave *slave, *oldcurrent; | ||||
|  	struct sockaddr_storage ss; | ||||
| +	struct bond_cb *lag_cb_main; /* QCA NSS ECM bonding support */
 | ||||
|  	int old_flags = bond_dev->flags; | ||||
|  	netdev_features_t old_features = bond_dev->features; | ||||
|   | ||||
| @@ -2359,6 +2411,15 @@ static int __bond_release_one(struct net
 | ||||
|   | ||||
|  	bond_set_slave_inactive_flags(slave, BOND_SLAVE_NOTIFY_NOW); | ||||
|   | ||||
| +	/* QCA NSS ECM bonding support - Start */
 | ||||
| +	rcu_read_lock();
 | ||||
| +	lag_cb_main = rcu_dereference(bond_cb);
 | ||||
| +	if (lag_cb_main && lag_cb_main->bond_cb_release)
 | ||||
| +		lag_cb_main->bond_cb_release(slave_dev);
 | ||||
| +
 | ||||
| +	rcu_read_unlock();
 | ||||
| +	/* QCA NSS ECM bonding support - End */
 | ||||
| +
 | ||||
|  	bond_sysfs_slave_del(slave); | ||||
|   | ||||
|  	/* recompute stats just before removing the slave */ | ||||
| @@ -2678,6 +2739,8 @@ static void bond_miimon_commit(struct bo
 | ||||
|  	struct slave *slave, *primary, *active; | ||||
|  	bool do_failover = false; | ||||
|  	struct list_head *iter; | ||||
| +	struct net_device *slave_dev = NULL; /* QCA NSS ECM bonding support */
 | ||||
| +	struct bond_cb *lag_cb_main; /* QCA NSS ECM bonding support */
 | ||||
|   | ||||
|  	ASSERT_RTNL(); | ||||
|   | ||||
| @@ -2717,6 +2780,12 @@ static void bond_miimon_commit(struct bo
 | ||||
|  				bond_set_active_slave(slave); | ||||
|  			} | ||||
|   | ||||
| +			/* QCA NSS ECM bonding support - Start */
 | ||||
| +			if ((bond->params.mode == BOND_MODE_XOR) &&
 | ||||
| +			    (!slave_dev))
 | ||||
| +				slave_dev = slave->dev;
 | ||||
| +			/* QCA NSS ECM bonding support - End */
 | ||||
| +
 | ||||
|  			slave_info(bond->dev, slave->dev, "link status definitely up, %u Mbps %s duplex\n", | ||||
|  				   slave->speed == SPEED_UNKNOWN ? 0 : slave->speed, | ||||
|  				   slave->duplex ? "full" : "half"); | ||||
| @@ -2765,6 +2834,16 @@ static void bond_miimon_commit(struct bo
 | ||||
|  		unblock_netpoll_tx(); | ||||
|  	} | ||||
|   | ||||
| +	/* QCA NSS ECM bonding support - Start */
 | ||||
| +	rcu_read_lock();
 | ||||
| +	lag_cb_main = rcu_dereference(bond_cb);
 | ||||
| +
 | ||||
| +	if (slave_dev && lag_cb_main && lag_cb_main->bond_cb_link_up)
 | ||||
| +		lag_cb_main->bond_cb_link_up(slave_dev);
 | ||||
| +
 | ||||
| +	rcu_read_unlock();
 | ||||
| +	/* QCA NSS ECM bonding support - End */
 | ||||
| +
 | ||||
|  	bond_set_carrier(bond); | ||||
|  } | ||||
|   | ||||
| @@ -4012,8 +4091,219 @@ static inline u32 bond_eth_hash(struct s
 | ||||
|  		return 0; | ||||
|   | ||||
|  	ep = (struct ethhdr *)(data + mhoff); | ||||
| -	return ep->h_dest[5] ^ ep->h_source[5] ^ be16_to_cpu(ep->h_proto);
 | ||||
| +	return ep->h_dest[5] ^ ep->h_source[5]; /* QCA NSS ECM bonding support */
 | ||||
| +}
 | ||||
| +
 | ||||
| +/* QCA NSS ECM bonding support - Start */
 | ||||
| +/* Extract the appropriate headers based on bond's xmit policy */
 | ||||
| +static bool bond_flow_dissect_without_skb(struct bonding *bond,
 | ||||
| +					  u8 *src_mac, u8 *dst_mac,
 | ||||
| +					  void *psrc, void *pdst,
 | ||||
| +					  u16 protocol, __be16 *layer4hdr,
 | ||||
| +					  struct flow_keys *fk)
 | ||||
| +{
 | ||||
| +	u32 *src = NULL;
 | ||||
| +	u32 *dst = NULL;
 | ||||
| +
 | ||||
| +	fk->ports.ports = 0;
 | ||||
| +	src = (uint32_t *)psrc;
 | ||||
| +	dst = (uint32_t *)pdst;
 | ||||
| +
 | ||||
| +	if (protocol == htons(ETH_P_IP)) {
 | ||||
| +       /* V4 addresses and address type*/
 | ||||
| +		fk->addrs.v4addrs.src = src[0];
 | ||||
| +		fk->addrs.v4addrs.dst = dst[0];
 | ||||
| +       fk->control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
 | ||||
| +	} else if (protocol == htons(ETH_P_IPV6)) {
 | ||||
| +       /* V6 addresses and address type*/
 | ||||
| +		memcpy(&fk->addrs.v6addrs.src, src, sizeof(struct in6_addr));
 | ||||
| +		memcpy(&fk->addrs.v6addrs.dst, dst, sizeof(struct in6_addr));
 | ||||
| +       fk->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
 | ||||
| +	} else {
 | ||||
| +		return false;
 | ||||
| +	}
 | ||||
| +	if ((bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER34) &&
 | ||||
| +	    (layer4hdr))
 | ||||
| +		fk->ports.ports = *layer4hdr;
 | ||||
| +
 | ||||
| +	return true;
 | ||||
| +}
 | ||||
| +
 | ||||
| +/* bond_xmit_hash_without_skb - Applies load balancing algorithm for a packet,
 | ||||
| + * to calculate hash for a given set of L2/L3 addresses. Does not
 | ||||
| + * calculate egress interface.
 | ||||
| + */
 | ||||
| +uint32_t bond_xmit_hash_without_skb(u8 *src_mac, u8 *dst_mac,
 | ||||
| +				    void *psrc, void *pdst, u16 protocol,
 | ||||
| +				    struct net_device *bond_dev,
 | ||||
| +				    __be16 *layer4hdr)
 | ||||
| +{
 | ||||
| +	struct bonding *bond = netdev_priv(bond_dev);
 | ||||
| +	struct flow_keys flow;
 | ||||
| +	u32 hash = 0;
 | ||||
| +
 | ||||
| +	if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER2 ||
 | ||||
| +	    !bond_flow_dissect_without_skb(bond, src_mac, dst_mac, psrc,
 | ||||
| +					   pdst, protocol, layer4hdr, &flow))
 | ||||
| +		return (dst_mac[5] ^ src_mac[5]);
 | ||||
| +
 | ||||
| +	if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER23)
 | ||||
| +		hash = dst_mac[5] ^ src_mac[5];
 | ||||
| +	else if (layer4hdr)
 | ||||
| +		hash = (__force u32)flow.ports.ports;
 | ||||
| +
 | ||||
| +	hash ^= (__force u32)flow_get_u32_dst(&flow) ^
 | ||||
| +		(__force u32)flow_get_u32_src(&flow);
 | ||||
| +	hash ^= (hash >> 16);
 | ||||
| +	hash ^= (hash >> 8);
 | ||||
| +
 | ||||
| +	return hash;
 | ||||
| +}
 | ||||
| +
 | ||||
| +/* bond_xor_get_tx_dev - Calculate egress interface for a given packet for a LAG
 | ||||
| + * that is configured in balance-xor mode
 | ||||
| + * @skb: pointer to skb to be egressed
 | ||||
| + * @src_mac: pointer to source L2 address
 | ||||
| + * @dst_mac: pointer to destination L2 address
 | ||||
| + * @src: pointer to source L3 address in network order
 | ||||
| + * @dst: pointer to destination L3 address in network order
 | ||||
| + * @protocol: L3 protocol
 | ||||
| + * @bond_dev: pointer to bond master device
 | ||||
| + *
 | ||||
| + * If @skb is NULL, bond_xmit_hash_without_skb is used to calculate hash using
 | ||||
| + * L2/L3 addresses.
 | ||||
| + *
 | ||||
| + * Returns: Either valid slave device, or NULL otherwise
 | ||||
| + */
 | ||||
| +static struct net_device *bond_xor_get_tx_dev(struct sk_buff *skb,
 | ||||
| +					      u8 *src_mac, u8 *dst_mac,
 | ||||
| +					      void *src, void *dst,
 | ||||
| +					      u16 protocol,
 | ||||
| +					      struct net_device *bond_dev,
 | ||||
| +					      __be16 *layer4hdr)
 | ||||
| +{
 | ||||
| +	struct bonding *bond = netdev_priv(bond_dev);
 | ||||
| +	int slave_cnt = READ_ONCE(bond->slave_cnt);
 | ||||
| +	int slave_id = 0, i = 0;
 | ||||
| +	u32 hash;
 | ||||
| +	struct list_head *iter;
 | ||||
| +	struct slave *slave;
 | ||||
| +
 | ||||
| +	if (slave_cnt == 0) {
 | ||||
| +		pr_debug("%s: Error: No slave is attached to the interface\n",
 | ||||
| +			 bond_dev->name);
 | ||||
| +		return NULL;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	if (skb) {
 | ||||
| +		hash = bond_xmit_hash(bond, skb);
 | ||||
| +		slave_id = hash % slave_cnt;
 | ||||
| +	} else {
 | ||||
| +		if (bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER23 &&
 | ||||
| +		    bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER2	&&
 | ||||
| +		    bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER34) {
 | ||||
| +			pr_debug("%s: Error: Unsupported hash policy for balance-XOR fast path\n",
 | ||||
| +				 bond_dev->name);
 | ||||
| +			return NULL;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		hash = bond_xmit_hash_without_skb(src_mac, dst_mac, src,
 | ||||
| +						  dst, protocol, bond_dev,
 | ||||
| +						  layer4hdr);
 | ||||
| +		slave_id = hash % slave_cnt;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	i = slave_id;
 | ||||
| +
 | ||||
| +	/* Here we start from the slave with slave_id */
 | ||||
| +	bond_for_each_slave_rcu(bond, slave, iter) {
 | ||||
| +		if (--i < 0) {
 | ||||
| +			if (bond_slave_can_tx(slave))
 | ||||
| +				return slave->dev;
 | ||||
| +		}
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	/* Here we start from the first slave up to slave_id */
 | ||||
| +	i = slave_id;
 | ||||
| +	bond_for_each_slave_rcu(bond, slave, iter) {
 | ||||
| +		if (--i < 0)
 | ||||
| +			break;
 | ||||
| +		if (bond_slave_can_tx(slave))
 | ||||
| +			return slave->dev;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	return NULL;
 | ||||
| +}
 | ||||
| +
 | ||||
| +/* bond_get_tx_dev - Calculate egress interface for a given packet.
 | ||||
| + *
 | ||||
| + * Supports 802.3AD and balance-xor modes
 | ||||
| + *
 | ||||
| + * @skb: pointer to skb to be egressed, if valid
 | ||||
| + * @src_mac: pointer to source L2 address
 | ||||
| + * @dst_mac: pointer to destination L2 address
 | ||||
| + * @src: pointer to source L3 address in network order
 | ||||
| + * @dst: pointer to destination L3 address in network order
 | ||||
| + * @protocol: L3 protocol id from L2 header
 | ||||
| + * @bond_dev: pointer to bond master device
 | ||||
| + *
 | ||||
| + * Returns: Either valid slave device, or NULL for un-supported LAG modes
 | ||||
| + */
 | ||||
| +struct net_device *bond_get_tx_dev(struct sk_buff *skb, uint8_t *src_mac,
 | ||||
| +				   u8 *dst_mac, void *src,
 | ||||
| +				   void *dst, u16 protocol,
 | ||||
| +				   struct net_device *bond_dev,
 | ||||
| +				   __be16 *layer4hdr)
 | ||||
| +{
 | ||||
| +	struct bonding *bond;
 | ||||
| +
 | ||||
| +	if (!bond_dev)
 | ||||
| +		return NULL;
 | ||||
| +
 | ||||
| +	if (!((bond_dev->priv_flags & IFF_BONDING) &&
 | ||||
| +	      (bond_dev->flags & IFF_MASTER)))
 | ||||
| +		return NULL;
 | ||||
| +
 | ||||
| +	bond = netdev_priv(bond_dev);
 | ||||
| +
 | ||||
| +	switch (bond->params.mode) {
 | ||||
| +	case BOND_MODE_XOR:
 | ||||
| +		return bond_xor_get_tx_dev(skb, src_mac, dst_mac,
 | ||||
| +					   src, dst, protocol,
 | ||||
| +					   bond_dev, layer4hdr);
 | ||||
| +	case BOND_MODE_8023AD:
 | ||||
| +		return bond_3ad_get_tx_dev(skb, src_mac, dst_mac,
 | ||||
| +					   src, dst, protocol,
 | ||||
| +					   bond_dev, layer4hdr);
 | ||||
| +	default:
 | ||||
| +		return NULL;
 | ||||
| +	}
 | ||||
|  } | ||||
| +EXPORT_SYMBOL(bond_get_tx_dev);
 | ||||
| +
 | ||||
| +/* In bond_xmit_xor() , we determine the output device by using a pre-
 | ||||
| + * determined xmit_hash_policy(), If the selected device is not enabled,
 | ||||
| + * find the next active slave.
 | ||||
| + */
 | ||||
| +static int bond_xmit_xor(struct sk_buff *skb, struct net_device *dev)
 | ||||
| +{
 | ||||
| +	struct bonding *bond = netdev_priv(dev);
 | ||||
| +	struct net_device *outdev;
 | ||||
| +
 | ||||
| +	outdev = bond_xor_get_tx_dev(skb, NULL, NULL, NULL,
 | ||||
| +				     NULL, 0, dev, NULL);
 | ||||
| +	if (!outdev)
 | ||||
| +		goto out;
 | ||||
| +
 | ||||
| +	bond_dev_queue_xmit(bond, skb, outdev);
 | ||||
| +	goto final;
 | ||||
| +out:
 | ||||
| +	/* no suitable interface, frame not sent */
 | ||||
| +	dev_kfree_skb(skb);
 | ||||
| +final:
 | ||||
| +	return NETDEV_TX_OK;
 | ||||
| +}
 | ||||
| +/* QCA NSS ECM bonding support - End */
 | ||||
|   | ||||
|  static bool bond_flow_ip(struct sk_buff *skb, struct flow_keys *fk, const void *data, | ||||
|  			 int hlen, __be16 l2_proto, int *nhoff, int *ip_proto, bool l34) | ||||
| @@ -5192,15 +5482,18 @@ static netdev_tx_t bond_3ad_xor_xmit(str
 | ||||
|  				     struct net_device *dev) | ||||
|  { | ||||
|  	struct bonding *bond = netdev_priv(dev); | ||||
| -	struct bond_up_slave *slaves;
 | ||||
| -	struct slave *slave;
 | ||||
| +	/* QCA NSS ECM bonding support - Start */
 | ||||
| +	struct net_device *outdev = NULL;
 | ||||
|   | ||||
| -	slaves = rcu_dereference(bond->usable_slaves);
 | ||||
| -	slave = bond_xmit_3ad_xor_slave_get(bond, skb, slaves);
 | ||||
| -	if (likely(slave))
 | ||||
| -		return bond_dev_queue_xmit(bond, skb, slave->dev);
 | ||||
| +	outdev = bond_3ad_get_tx_dev(skb, NULL, NULL, NULL,
 | ||||
| +				     NULL, 0, dev, NULL);
 | ||||
| +	if (!outdev) {
 | ||||
| +		dev_kfree_skb(skb);
 | ||||
| +		return NETDEV_TX_OK;
 | ||||
| +	}
 | ||||
|   | ||||
| -	return bond_tx_drop(dev, skb);
 | ||||
| +	return bond_dev_queue_xmit(bond, skb, outdev);
 | ||||
| +	/* QCA NSS ECM bonding support - End */
 | ||||
|  } | ||||
|   | ||||
|  /* in broadcast mode, we send everything to all usable interfaces. */ | ||||
| @@ -5450,8 +5743,9 @@ static netdev_tx_t __bond_start_xmit(str
 | ||||
|  		return bond_xmit_roundrobin(skb, dev); | ||||
|  	case BOND_MODE_ACTIVEBACKUP: | ||||
|  		return bond_xmit_activebackup(skb, dev); | ||||
| -	case BOND_MODE_8023AD:
 | ||||
|  	case BOND_MODE_XOR: | ||||
| +		return bond_xmit_xor(skb, dev); /* QCA NSS ECM bonding support */
 | ||||
| +	case BOND_MODE_8023AD:
 | ||||
|  		return bond_3ad_xor_xmit(skb, dev); | ||||
|  	case BOND_MODE_BROADCAST: | ||||
|  		return bond_xmit_broadcast(skb, dev); | ||||
| --- a/include/net/bond_3ad.h
 | ||||
| +++ b/include/net/bond_3ad.h
 | ||||
| @@ -303,8 +303,15 @@ int bond_3ad_lacpdu_recv(const struct sk
 | ||||
|  int bond_3ad_set_carrier(struct bonding *bond); | ||||
|  void bond_3ad_update_lacp_active(struct bonding *bond); | ||||
|  void bond_3ad_update_lacp_rate(struct bonding *bond); | ||||
| +/* QCA NSS ECM bonding support */
 | ||||
| +struct net_device *bond_3ad_get_tx_dev(struct sk_buff *skb, uint8_t *src_mac,
 | ||||
| +				       uint8_t *dst_mac, void *src,
 | ||||
| +				       void *dst, uint16_t protocol,
 | ||||
| +				       struct net_device *bond_dev,
 | ||||
| +				       __be16 *layer4hdr);
 | ||||
| +/* QCA NSS ECM bonding support */
 | ||||
| +
 | ||||
|  void bond_3ad_update_ad_actor_settings(struct bonding *bond); | ||||
|  int bond_3ad_stats_fill(struct sk_buff *skb, struct bond_3ad_stats *stats); | ||||
|  size_t bond_3ad_stats_size(void); | ||||
|  #endif /* _NET_BOND_3AD_H */ | ||||
| -
 | ||||
| --- a/include/net/bonding.h
 | ||||
| +++ b/include/net/bonding.h
 | ||||
| @@ -94,6 +94,8 @@
 | ||||
|   | ||||
|  #define BOND_TLS_FEATURES (NETIF_F_HW_TLS_TX | NETIF_F_HW_TLS_RX) | ||||
|   | ||||
| +extern struct bond_cb __rcu *bond_cb; /* QCA NSS ECM bonding support */
 | ||||
| +
 | ||||
|  #ifdef CONFIG_NET_POLL_CONTROLLER | ||||
|  extern atomic_t netpoll_block_tx; | ||||
|   | ||||
| @@ -659,6 +661,7 @@ struct bond_net {
 | ||||
|   | ||||
|  int bond_rcv_validate(const struct sk_buff *skb, struct bonding *bond, struct slave *slave); | ||||
|  netdev_tx_t bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *slave_dev); | ||||
| +int bond_get_id(struct net_device *bond_dev); /* QCA NSS ECM bonding support */
 | ||||
|  int bond_create(struct net *net, const char *name); | ||||
|  int bond_create_sysfs(struct bond_net *net); | ||||
|  void bond_destroy_sysfs(struct bond_net *net); | ||||
| @@ -689,6 +692,13 @@ struct bond_vlan_tag *bond_verify_device
 | ||||
|  					      int level); | ||||
|  int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave); | ||||
|  void bond_slave_arr_work_rearm(struct bonding *bond, unsigned long delay); | ||||
| +/* QCA NSS ECM bonding support - Start */
 | ||||
| +uint32_t bond_xmit_hash_without_skb(uint8_t *src_mac, uint8_t *dst_mac,
 | ||||
| +				    void *psrc, void *pdst, uint16_t protocol,
 | ||||
| +				    struct net_device *bond_dev,
 | ||||
| +				    __be16 *layer4hdr);
 | ||||
| +/* QCA NSS ECM bonding support - End */
 | ||||
| +
 | ||||
|  void bond_work_init_all(struct bonding *bond); | ||||
|   | ||||
|  #ifdef CONFIG_PROC_FS | ||||
| @@ -793,4 +803,18 @@ static inline netdev_tx_t bond_tx_drop(s
 | ||||
|  	return NET_XMIT_DROP; | ||||
|  } | ||||
|   | ||||
| +/* QCA NSS ECM bonding support - Start */
 | ||||
| +struct bond_cb {
 | ||||
| +	void (*bond_cb_link_up)(struct net_device *slave);
 | ||||
| +	void (*bond_cb_link_down)(struct net_device *slave);
 | ||||
| +	void (*bond_cb_enslave)(struct net_device *slave);
 | ||||
| +	void (*bond_cb_release)(struct net_device *slave);
 | ||||
| +	void (*bond_cb_delete_by_slave)(struct net_device *slave);
 | ||||
| +	void (*bond_cb_delete_by_mac)(uint8_t *mac_addr);
 | ||||
| +};
 | ||||
| +
 | ||||
| +extern int bond_register_cb(struct bond_cb *cb);
 | ||||
| +extern void bond_unregister_cb(void);
 | ||||
| +/* QCA NSS ECM bonding support - End */
 | ||||
| +
 | ||||
|  #endif /* _NET_BONDING_H */ | ||||
|  | @ -0,0 +1,96 @@ | |||
| --- a/include/linux/if_macvlan.h
 | ||||
| +++ b/include/linux/if_macvlan.h
 | ||||
| @@ -15,6 +15,13 @@ struct macvlan_port;
 | ||||
|  #define MACVLAN_MC_FILTER_BITS	8 | ||||
|  #define MACVLAN_MC_FILTER_SZ	(1 << MACVLAN_MC_FILTER_BITS) | ||||
|   | ||||
| +/* QCA NSS ECM Support - Start */
 | ||||
| +/*
 | ||||
| + * Callback for updating interface statistics for macvlan flows offloaded from host CPU.
 | ||||
| + */
 | ||||
| +typedef void (*macvlan_offload_stats_update_cb_t)(struct net_device *dev, struct rtnl_link_stats64 *stats, bool update_mcast_rx_stats);
 | ||||
| +/* QCA NSS ECM Support - End */
 | ||||
| +
 | ||||
|  struct macvlan_dev { | ||||
|  	struct net_device	*dev; | ||||
|  	struct list_head	list; | ||||
| @@ -35,6 +42,7 @@ struct macvlan_dev {
 | ||||
|  #ifdef CONFIG_NET_POLL_CONTROLLER | ||||
|  	struct netpoll		*netpoll; | ||||
|  #endif | ||||
| +	macvlan_offload_stats_update_cb_t	offload_stats_update; /* QCA NSS ECM support */
 | ||||
|  }; | ||||
|   | ||||
|  static inline void macvlan_count_rx(const struct macvlan_dev *vlan, | ||||
| @@ -107,4 +115,26 @@ static inline int macvlan_release_l2fw_o
 | ||||
|  	macvlan->accel_priv = NULL; | ||||
|  	return dev_uc_add(macvlan->lowerdev, dev->dev_addr); | ||||
|  } | ||||
| +
 | ||||
| +/* QCA NSS ECM Support - Start */
 | ||||
| +#if IS_ENABLED(CONFIG_MACVLAN)
 | ||||
| +static inline void
 | ||||
| +macvlan_offload_stats_update(struct net_device *dev,
 | ||||
| +			     struct rtnl_link_stats64 *stats,
 | ||||
| +			     bool update_mcast_rx_stats)
 | ||||
| +{
 | ||||
| +	struct macvlan_dev *macvlan = netdev_priv(dev);
 | ||||
| +
 | ||||
| +	macvlan->offload_stats_update(dev, stats, update_mcast_rx_stats);
 | ||||
| +}
 | ||||
| +
 | ||||
| +static inline enum
 | ||||
| +macvlan_mode macvlan_get_mode(struct net_device *dev)
 | ||||
| +{
 | ||||
| +	struct macvlan_dev *macvlan = netdev_priv(dev);
 | ||||
| +
 | ||||
| +	return macvlan->mode;
 | ||||
| +}
 | ||||
| +#endif
 | ||||
| +/* QCA NSS ECM Support - End */
 | ||||
|  #endif /* _LINUX_IF_MACVLAN_H */ | ||||
| --- a/drivers/net/macvlan.c
 | ||||
| +++ b/drivers/net/macvlan.c
 | ||||
| @@ -933,6 +933,34 @@ static void macvlan_uninit(struct net_de
 | ||||
|  		macvlan_port_destroy(port->dev); | ||||
|  } | ||||
|   | ||||
| +/* QCA NSS ECM Support - Start */
 | ||||
| +/* Update macvlan statistics processed by offload engines */
 | ||||
| +static void macvlan_dev_update_stats(struct net_device *dev,
 | ||||
| +				     struct rtnl_link_stats64 *offl_stats,
 | ||||
| +				     bool update_mcast_rx_stats)
 | ||||
| +{
 | ||||
| +	struct vlan_pcpu_stats *stats;
 | ||||
| +	struct macvlan_dev *macvlan;
 | ||||
| +
 | ||||
| +	/* Is this a macvlan? */
 | ||||
| +	if (!netif_is_macvlan(dev))
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +	macvlan = netdev_priv(dev);
 | ||||
| +	stats = this_cpu_ptr(macvlan->pcpu_stats);
 | ||||
| +	u64_stats_update_begin(&stats->syncp);
 | ||||
| +	u64_stats_add(&stats->rx_packets, offl_stats->rx_packets);
 | ||||
| +	u64_stats_add(&stats->rx_bytes, offl_stats->rx_bytes);
 | ||||
| +	u64_stats_add(&stats->tx_packets, offl_stats->tx_packets);
 | ||||
| +	u64_stats_add(&stats->tx_bytes, offl_stats->tx_bytes);
 | ||||
| +	/* Update multicast statistics */
 | ||||
| +	if (unlikely(update_mcast_rx_stats)) {
 | ||||
| +		u64_stats_add(&stats->rx_multicast, offl_stats->rx_packets);
 | ||||
| +	}
 | ||||
| +	u64_stats_update_end(&stats->syncp);
 | ||||
| +}
 | ||||
| +/* QCA NSS ECM Support - End */
 | ||||
| +
 | ||||
|  static void macvlan_dev_get_stats64(struct net_device *dev, | ||||
|  				    struct rtnl_link_stats64 *stats) | ||||
|  { | ||||
| @@ -1477,6 +1505,7 @@ int macvlan_common_newlink(struct net *s
 | ||||
|  	vlan->dev      = dev; | ||||
|  	vlan->port     = port; | ||||
|  	vlan->set_features = MACVLAN_FEATURES; | ||||
| +	vlan->offload_stats_update = macvlan_dev_update_stats; /* QCA NSS ECM Support */
 | ||||
|   | ||||
|  	vlan->mode     = MACVLAN_MODE_VEPA; | ||||
|  	if (data && data[IFLA_MACVLAN_MODE]) | ||||
|  | @ -0,0 +1,154 @@ | |||
| --- a/net/netfilter/Kconfig
 | ||||
| +++ b/net/netfilter/Kconfig
 | ||||
| @@ -175,6 +175,13 @@ config NF_CONNTRACK_TIMEOUT
 | ||||
|   | ||||
|  	  If unsure, say `N'. | ||||
|   | ||||
| +config NF_CONNTRACK_DSCPREMARK_EXT
 | ||||
| +	bool  'Connection tracking extension for dscp remark target'
 | ||||
| +	depends on NETFILTER_ADVANCED
 | ||||
| +	help
 | ||||
| +	  This option enables support for connection tracking extension
 | ||||
| +	  for dscp remark.
 | ||||
| +
 | ||||
|  config NF_CONNTRACK_TIMESTAMP | ||||
|  	bool  'Connection tracking timestamping' | ||||
|  	depends on NETFILTER_ADVANCED | ||||
| --- a/include/net/netfilter/nf_conntrack_extend.h
 | ||||
| +++ b/include/net/netfilter/nf_conntrack_extend.h
 | ||||
| @@ -31,6 +31,10 @@ enum nf_ct_ext_id {
 | ||||
|  #if IS_ENABLED(CONFIG_NET_ACT_CT) | ||||
|  	NF_CT_EXT_ACT_CT, | ||||
|  #endif | ||||
| +#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT
 | ||||
| +	NF_CT_EXT_DSCPREMARK,   /* QCA NSS ECM support */
 | ||||
| +#endif
 | ||||
| +
 | ||||
|  	NF_CT_EXT_NUM, | ||||
|  }; | ||||
|   | ||||
| --- a/net/netfilter/nf_conntrack_extend.c
 | ||||
| +++ b/net/netfilter/nf_conntrack_extend.c
 | ||||
| @@ -23,6 +23,7 @@
 | ||||
|  #include <net/netfilter/nf_conntrack_labels.h> | ||||
|  #include <net/netfilter/nf_conntrack_synproxy.h> | ||||
|  #include <net/netfilter/nf_conntrack_act_ct.h> | ||||
| +#include <net/netfilter/nf_conntrack_dscpremark_ext.h>
 | ||||
|  #include <net/netfilter/nf_nat.h> | ||||
|   | ||||
|  #define NF_CT_EXT_PREALLOC	128u /* conntrack events are on by default */ | ||||
| @@ -54,6 +55,9 @@ static const u8 nf_ct_ext_type_len[NF_CT
 | ||||
|  #if IS_ENABLED(CONFIG_NET_ACT_CT) | ||||
|  	[NF_CT_EXT_ACT_CT] = sizeof(struct nf_conn_act_ct_ext), | ||||
|  #endif | ||||
| +#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT
 | ||||
| +	[NF_CT_EXT_DSCPREMARK] = sizeof(struct nf_ct_dscpremark_ext),
 | ||||
| +#endif
 | ||||
|  }; | ||||
|   | ||||
|  static __always_inline unsigned int total_extension_size(void) | ||||
| @@ -86,6 +90,9 @@ static __always_inline unsigned int tota
 | ||||
|  #if IS_ENABLED(CONFIG_NET_ACT_CT) | ||||
|  		+ sizeof(struct nf_conn_act_ct_ext) | ||||
|  #endif | ||||
| +#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT
 | ||||
| +	    + sizeof(struct nf_ct_dscpremark_ext)
 | ||||
| +#endif
 | ||||
|  	; | ||||
|  } | ||||
|   | ||||
| --- a/net/netfilter/Makefile
 | ||||
| +++ b/net/netfilter/Makefile
 | ||||
| @@ -14,6 +14,7 @@ nf_conntrack-$(CONFIG_NF_CONNTRACK_LABEL
 | ||||
|  nf_conntrack-$(CONFIG_NF_CT_PROTO_DCCP) += nf_conntrack_proto_dccp.o | ||||
|  nf_conntrack-$(CONFIG_NF_CT_PROTO_SCTP) += nf_conntrack_proto_sctp.o | ||||
|  nf_conntrack-$(CONFIG_NF_CT_PROTO_GRE) += nf_conntrack_proto_gre.o | ||||
| +nf_conntrack-$(CONFIG_NF_CONNTRACK_DSCPREMARK_EXT) += nf_conntrack_dscpremark_ext.o
 | ||||
|  ifeq ($(CONFIG_NF_CONNTRACK),m) | ||||
|  nf_conntrack-$(CONFIG_DEBUG_INFO_BTF_MODULES) += nf_conntrack_bpf.o | ||||
|  else ifeq ($(CONFIG_NF_CONNTRACK),y) | ||||
| --- a/net/netfilter/nf_conntrack_core.c
 | ||||
| +++ b/net/netfilter/nf_conntrack_core.c
 | ||||
| @@ -45,6 +45,9 @@
 | ||||
|  #include <net/netfilter/nf_conntrack_zones.h> | ||||
|  #include <net/netfilter/nf_conntrack_timestamp.h> | ||||
|  #include <net/netfilter/nf_conntrack_timeout.h> | ||||
| +#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT
 | ||||
| +#include <net/netfilter/nf_conntrack_dscpremark_ext.h>
 | ||||
| +#endif
 | ||||
|  #include <net/netfilter/nf_conntrack_labels.h> | ||||
|  #include <net/netfilter/nf_conntrack_synproxy.h> | ||||
|  #include <net/netfilter/nf_nat.h> | ||||
| @@ -1781,6 +1784,9 @@ init_conntrack(struct net *net, struct n
 | ||||
|  	nf_ct_acct_ext_add(ct, GFP_ATOMIC); | ||||
|  	nf_ct_tstamp_ext_add(ct, GFP_ATOMIC); | ||||
|  	nf_ct_labels_ext_add(ct); | ||||
| +#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT
 | ||||
| +	nf_ct_dscpremark_ext_add(ct, GFP_ATOMIC);
 | ||||
| +#endif
 | ||||
|   | ||||
|  #ifdef CONFIG_NF_CONNTRACK_EVENTS | ||||
|  	ecache = tmpl ? nf_ct_ecache_find(tmpl) : NULL; | ||||
| --- a/net/netfilter/xt_DSCP.c
 | ||||
| +++ b/net/netfilter/xt_DSCP.c
 | ||||
| @@ -15,6 +15,9 @@
 | ||||
|   | ||||
|  #include <linux/netfilter/x_tables.h> | ||||
|  #include <linux/netfilter/xt_DSCP.h> | ||||
| +#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT
 | ||||
| +#include <net/netfilter/nf_conntrack_dscpremark_ext.h>
 | ||||
| +#endif
 | ||||
|   | ||||
|  MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); | ||||
|  MODULE_DESCRIPTION("Xtables: DSCP/TOS field modification"); | ||||
| @@ -31,6 +34,10 @@ dscp_tg(struct sk_buff *skb, const struc
 | ||||
|  { | ||||
|  	const struct xt_DSCP_info *dinfo = par->targinfo; | ||||
|  	u_int8_t dscp = ipv4_get_dsfield(ip_hdr(skb)) >> XT_DSCP_SHIFT; | ||||
| +#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT
 | ||||
| +	struct nf_conn *ct;
 | ||||
| +	enum ip_conntrack_info ctinfo;
 | ||||
| +#endif
 | ||||
|   | ||||
|  	if (dscp != dinfo->dscp) { | ||||
|  		if (skb_ensure_writable(skb, sizeof(struct iphdr))) | ||||
| @@ -39,6 +46,13 @@ dscp_tg(struct sk_buff *skb, const struc
 | ||||
|  		ipv4_change_dsfield(ip_hdr(skb), XT_DSCP_ECN_MASK, | ||||
|  				    dinfo->dscp << XT_DSCP_SHIFT); | ||||
|   | ||||
| +#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT
 | ||||
| +		ct = nf_ct_get(skb, &ctinfo);
 | ||||
| +		if (!ct)
 | ||||
| +			return XT_CONTINUE;
 | ||||
| +
 | ||||
| +		nf_conntrack_dscpremark_ext_set_dscp_rule_valid(ct);
 | ||||
| +#endif
 | ||||
|  	} | ||||
|  	return XT_CONTINUE; | ||||
|  } | ||||
| @@ -48,13 +62,24 @@ dscp_tg6(struct sk_buff *skb, const stru
 | ||||
|  { | ||||
|  	const struct xt_DSCP_info *dinfo = par->targinfo; | ||||
|  	u_int8_t dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> XT_DSCP_SHIFT; | ||||
| -
 | ||||
| +#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT
 | ||||
| +	struct nf_conn *ct;
 | ||||
| +	enum ip_conntrack_info ctinfo;
 | ||||
| +#endif
 | ||||
|  	if (dscp != dinfo->dscp) { | ||||
|  		if (skb_ensure_writable(skb, sizeof(struct ipv6hdr))) | ||||
|  			return NF_DROP; | ||||
|   | ||||
|  		ipv6_change_dsfield(ipv6_hdr(skb), XT_DSCP_ECN_MASK, | ||||
|  				    dinfo->dscp << XT_DSCP_SHIFT); | ||||
| +
 | ||||
| +#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT
 | ||||
| +		ct = nf_ct_get(skb, &ctinfo);
 | ||||
| +		if (!ct)
 | ||||
| +			return XT_CONTINUE;
 | ||||
| +
 | ||||
| +		nf_conntrack_dscpremark_ext_set_dscp_rule_valid(ct);
 | ||||
| +#endif
 | ||||
|  	} | ||||
|  	return XT_CONTINUE; | ||||
|  } | ||||
|  | @ -0,0 +1,89 @@ | |||
| From ce18a6fdff6a39a01111d74f513d2ef66142047c Mon Sep 17 00:00:00 2001 | ||||
| From: Murat Sezgin <msezgin@codeaurora.org> | ||||
| Date: Wed, 5 Aug 2020 13:21:27 -0700 | ||||
| Subject: [PATCH 246/281] net:ipv6: Fix IPv6 user route change event calls | ||||
| 
 | ||||
| These events should be called only when the route table is | ||||
| changed by the userspace. So, we should call them in the | ||||
| ioctl and the netlink message handler function. | ||||
| 
 | ||||
| Change-Id: If7ec615014cfc79d5fa72878e49eaf99c2560c32 | ||||
| Signed-off-by: Murat Sezgin <msezgin@codeaurora.org> | ||||
| ---
 | ||||
| 	net/ipv6/route.c | 31 +++++++++++++++++++++---------- | ||||
| 	1 file changed, 21 insertions(+), 10 deletions(-) | ||||
| 
 | ||||
| diff --git a/net/ipv6/route.c b/net/ipv6/route.c
 | ||||
| index df82117..4fb8247 100644
 | ||||
| --- a/net/ipv6/route.c
 | ||||
| +++ b/net/ipv6/route.c
 | ||||
| @@ -3868,10 +3868,6 @@ int ip6_route_add(struct fib6_config *cfg, gfp_t gfp_flags,
 | ||||
|  		return PTR_ERR(rt); | ||||
|   | ||||
|  	err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, extack); | ||||
| -	if (!err)
 | ||||
| -		atomic_notifier_call_chain(&ip6route_chain,
 | ||||
| -					   RTM_NEWROUTE, rt);
 | ||||
| -
 | ||||
|  	fib6_info_release(rt); | ||||
|   | ||||
|  	return err; | ||||
| @@ -3893,9 +3889,6 @@ static int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info)
 | ||||
|  	err = fib6_del(rt, info); | ||||
|  	spin_unlock_bh(&table->tb6_lock); | ||||
|   | ||||
| -	if (!err)
 | ||||
| -		atomic_notifier_call_chain(&ip6route_chain,
 | ||||
| -					   RTM_DELROUTE, rt);
 | ||||
|  out: | ||||
|  	fib6_info_release(rt); | ||||
|  	return err; | ||||
| @@ -4501,6 +4494,10 @@ int ipv6_route_ioctl(struct net *net, unsigned int cmd, struct in6_rtmsg *rtmsg)
 | ||||
|  		break; | ||||
|  	} | ||||
|  	rtnl_unlock(); | ||||
| +	if (!err)
 | ||||
| +		atomic_notifier_call_chain(&ip6route_chain,
 | ||||
| +					   (cmd == SIOCADDRT) ? RTM_NEWROUTE : RTM_DELROUTE, &cfg);
 | ||||
| +
 | ||||
|  	return err; | ||||
|  } | ||||
|   | ||||
| @@ -5528,11 +5525,17 @@ static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh,
 | ||||
|  	} | ||||
|   | ||||
|  	if (cfg.fc_mp) | ||||
| -		return ip6_route_multipath_del(&cfg, extack);
 | ||||
| +		err = ip6_route_multipath_del(&cfg, extack);
 | ||||
|  	else { | ||||
|  		cfg.fc_delete_all_nh = 1; | ||||
| -		return ip6_route_del(&cfg, extack);
 | ||||
| +		err = ip6_route_del(&cfg, extack);
 | ||||
|  	} | ||||
| +
 | ||||
| +	if (!err)
 | ||||
| +		atomic_notifier_call_chain(&ip6route_chain,
 | ||||
| +					   RTM_DELROUTE, &cfg);
 | ||||
| +
 | ||||
| +	return err;
 | ||||
|  } | ||||
|   | ||||
|  static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, | ||||
| @@ -5549,9 +5552,15 @@ static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
 | ||||
|  		cfg.fc_metric = IP6_RT_PRIO_USER; | ||||
|   | ||||
|  	if (cfg.fc_mp) | ||||
| -		return ip6_route_multipath_add(&cfg, extack);
 | ||||
| +		err = ip6_route_multipath_add(&cfg, extack);
 | ||||
|  	else | ||||
| -		return ip6_route_add(&cfg, GFP_KERNEL, extack);
 | ||||
| +		err = ip6_route_add(&cfg, GFP_KERNEL, extack);
 | ||||
| +
 | ||||
| +	if (!err)
 | ||||
| +		atomic_notifier_call_chain(&ip6route_chain,
 | ||||
| +					   RTM_NEWROUTE, &cfg);
 | ||||
| +
 | ||||
| +	return err;
 | ||||
|  } | ||||
|   | ||||
|  /* add the overhead of this fib6_nh to nexthop_len */ | ||||
|  | @ -0,0 +1,92 @@ | |||
| From 3c17a0e1112be70071e98d5208da5b55dcec20a6 Mon Sep 17 00:00:00 2001 | ||||
| From: Simon Casey <simon501098c@gmail.com> | ||||
| Date: Wed, 2 Feb 2022 19:37:29 +0100 | ||||
| Subject: [PATCH] Update 607-qca-add-add-nss-bridge-mgr-support.patch for kernel 5.15 | ||||
| 
 | ||||
| ---
 | ||||
|  include/linux/if_bridge.h |  4 ++++ | ||||
|  net/bridge/br_fdb.c       | 25 +++++++++++++++++++++---- | ||||
|  2 files changed, 25 insertions(+), 4 deletions(-) | ||||
| 
 | ||||
| --- a/include/linux/if_bridge.h
 | ||||
| +++ b/include/linux/if_bridge.h
 | ||||
| @@ -252,4 +252,8 @@ typedef struct net_bridge_port *br_get_d
 | ||||
|  extern br_get_dst_hook_t __rcu *br_get_dst_hook; | ||||
|  /* QCA NSS ECM support - End */ | ||||
|   | ||||
| +/* QCA NSS bridge-mgr support - Start */
 | ||||
| +extern struct net_device *br_fdb_bridge_dev_get_and_hold(struct net_bridge *br);
 | ||||
| +/* QCA NSS bridge-mgr support - End */
 | ||||
| +
 | ||||
|  #endif | ||||
| --- a/net/bridge/br_fdb.c
 | ||||
| +++ b/net/bridge/br_fdb.c
 | ||||
| @@ -569,7 +569,7 @@ void br_fdb_cleanup(struct work_struct *
 | ||||
|  	unsigned long delay = hold_time(br); | ||||
|  	unsigned long work_delay = delay; | ||||
|  	unsigned long now = jiffies; | ||||
| -	u8 mac_addr[6]; /* QCA NSS ECM support */
 | ||||
| +	struct br_fdb_event fdb_event; /* QCA NSS bridge-mgr support */
 | ||||
|   | ||||
|  	/* this part is tricky, in order to avoid blocking learning and | ||||
|  	 * consequently forwarding, we rely on rcu to delete objects with | ||||
| @@ -597,12 +597,13 @@ void br_fdb_cleanup(struct work_struct *
 | ||||
|  		} else { | ||||
|  			spin_lock_bh(&br->hash_lock); | ||||
|  			if (!hlist_unhashed(&f->fdb_node)) { | ||||
| -				ether_addr_copy(mac_addr, f->key.addr.addr);
 | ||||
| +				memset(&fdb_event, 0, sizeof(fdb_event));
 | ||||
| +				ether_addr_copy(fdb_event.addr, f->key.addr.addr);
 | ||||
|  				fdb_delete(br, f, true); | ||||
|  				/* QCA NSS ECM support - Start */ | ||||
|  				atomic_notifier_call_chain( | ||||
|  					&br_fdb_update_notifier_list, 0, | ||||
| -					(void *)mac_addr);
 | ||||
| +					(void *)&fdb_event);
 | ||||
|  				/* QCA NSS ECM support - End */ | ||||
|  			} | ||||
|  			spin_unlock_bh(&br->hash_lock); | ||||
| @@ -900,10 +901,21 @@ static bool __fdb_mark_active(struct net
 | ||||
|  		  test_and_clear_bit(BR_FDB_NOTIFY_INACTIVE, &fdb->flags)); | ||||
|  } | ||||
|   | ||||
| +/* QCA NSS bridge-mgr support - Start */
 | ||||
| +/* Get the bridge device */
 | ||||
| +struct net_device *br_fdb_bridge_dev_get_and_hold(struct net_bridge *br)
 | ||||
| +{
 | ||||
| +	dev_hold(br->dev);
 | ||||
| +	return br->dev;
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL_GPL(br_fdb_bridge_dev_get_and_hold);
 | ||||
| +/* QCA NSS bridge-mgr support - End */
 | ||||
| +
 | ||||
|  void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, | ||||
|  		   const unsigned char *addr, u16 vid, unsigned long flags) | ||||
|  { | ||||
|  	struct net_bridge_fdb_entry *fdb; | ||||
| +	struct br_fdb_event fdb_event; /* QCA NSS bridge-mgr support */
 | ||||
|   | ||||
|  	/* some users want to always flood. */ | ||||
|  	if (hold_time(br) == 0) | ||||
| @@ -929,6 +941,12 @@ void br_fdb_update(struct net_bridge *br
 | ||||
|  			if (unlikely(source != READ_ONCE(fdb->dst) && | ||||
|  				     !test_bit(BR_FDB_STICKY, &fdb->flags))) { | ||||
|  				br_switchdev_fdb_notify(br, fdb, RTM_DELNEIGH); | ||||
| +				/* QCA NSS bridge-mgr support - Start */
 | ||||
| +				ether_addr_copy(fdb_event.addr, addr);
 | ||||
| +				fdb_event.br = br;
 | ||||
| +				fdb_event.orig_dev = fdb->dst->dev;
 | ||||
| +				fdb_event.dev = source->dev;
 | ||||
| +				/* QCA NSS bridge-mgr support - End */
 | ||||
|  				WRITE_ONCE(fdb->dst, source); | ||||
|  				fdb_modified = true; | ||||
|  				/* Take over HW learned entry */ | ||||
| @@ -940,7 +958,7 @@ void br_fdb_update(struct net_bridge *br
 | ||||
|  				/* QCA NSS ECM support - Start */ | ||||
|  				atomic_notifier_call_chain( | ||||
|  					&br_fdb_update_notifier_list, | ||||
| -					0, (void *)addr);
 | ||||
| +					0, (void *)&fdb_event);
 | ||||
|  				/* QCA NSS ECM support - End */ | ||||
|  			} | ||||
|   | ||||
|  | @ -0,0 +1,44 @@ | |||
| --- a/include/linux/skbuff.h
 | ||||
| +++ b/include/linux/skbuff.h
 | ||||
| @@ -773,6 +773,7 @@ typedef unsigned char *sk_buff_data_t;
 | ||||
|   *	@offload_fwd_mark: Packet was L2-forwarded in hardware | ||||
|   *	@offload_l3_fwd_mark: Packet was L3-forwarded in hardware | ||||
|   *	@tc_skip_classify: do not classify packet. set by IFB device | ||||
| + *	@tc_skip_classify_offload: do not classify packet set by offload IFB device
 | ||||
|   *	@tc_at_ingress: used within tc_classify to distinguish in/egress | ||||
|   *	@redirected: packet was redirected by packet classifier | ||||
|   *	@from_ingress: packet was redirected from the ingress path | ||||
| @@ -968,6 +969,8 @@ struct sk_buff {
 | ||||
|  #ifdef CONFIG_NET_CLS_ACT | ||||
|  	__u8			tc_skip_classify:1; | ||||
|  	__u8			tc_at_ingress:1;	/* See TC_AT_INGRESS_MASK */ | ||||
| +   __u8			tc_skip_classify_offload:1;
 | ||||
| +	__u16			tc_verd_qca_nss; /* QCA NSS Qdisc Support */
 | ||||
|  #endif | ||||
|  #ifdef CONFIG_IPV6_NDISC_NODETYPE | ||||
|  	__u8			ndisc_nodetype:2; | ||||
| --- a/include/uapi/linux/pkt_cls.h
 | ||||
| +++ b/include/uapi/linux/pkt_cls.h
 | ||||
| @@ -139,6 +139,7 @@ enum tca_id {
 | ||||
|  	TCA_ID_MPLS, | ||||
|  	TCA_ID_CT, | ||||
|  	TCA_ID_GATE, | ||||
| +	TCA_ID_MIRRED_NSS, /* QCA NSS Qdisc IGS Support */
 | ||||
|  	/* other actions go here */ | ||||
|  	__TCA_ID_MAX = 255 | ||||
|  }; | ||||
| @@ -801,4 +802,14 @@ enum {
 | ||||
|  	TCF_EM_OPND_LT | ||||
|  }; | ||||
|   | ||||
| +/* QCA NSS Qdisc Support - Start */
 | ||||
| +#define _TC_MAKE32(x)		((x))
 | ||||
| +#define _TC_MAKEMASK1(n)	(_TC_MAKE32(1) << _TC_MAKE32(n))
 | ||||
| +
 | ||||
| +#define TC_NCLS                 _TC_MAKEMASK1(8)
 | ||||
| +#define TC_NCLS_NSS		_TC_MAKEMASK1(12)
 | ||||
| +#define SET_TC_NCLS_NSS(v)	( TC_NCLS_NSS | ((v) & ~TC_NCLS_NSS))
 | ||||
| +#define CLR_TC_NCLS_NSS(v)	( (v) & ~TC_NCLS_NSS)
 | ||||
| +/* QCA NSS Qdisc Support - End */
 | ||||
| +
 | ||||
|  #endif | ||||
|  | @ -0,0 +1,441 @@ | |||
| --- a/include/linux/timer.h
 | ||||
| +++ b/include/linux/timer.h
 | ||||
| @@ -17,6 +17,7 @@ struct timer_list {
 | ||||
|  	unsigned long		expires; | ||||
|  	void			(*function)(struct timer_list *); | ||||
|  	u32			flags; | ||||
| +	unsigned long		cust_data;
 | ||||
|   | ||||
|  #ifdef CONFIG_LOCKDEP | ||||
|  	struct lockdep_map	lockdep_map; | ||||
| --- a/drivers/net/ifb.c
 | ||||
| +++ b/drivers/net/ifb.c
 | ||||
| @@ -151,6 +151,31 @@ resched:
 | ||||
|   | ||||
|  } | ||||
|   | ||||
| +void ifb_update_offload_stats(struct net_device *dev, struct pcpu_sw_netstats *offload_stats)
 | ||||
| +{
 | ||||
| +	struct ifb_dev_private *dp;
 | ||||
| +   struct ifb_q_private *txp;
 | ||||
| +
 | ||||
| +   if (!dev || !offload_stats) {
 | ||||
| +   	return;
 | ||||
| +   }
 | ||||
| +
 | ||||
| +   if (!(dev->priv_flags_ext & IFF_EXT_IFB)) {
 | ||||
| +       return;
 | ||||
| +   }
 | ||||
| +
 | ||||
| +   dp = netdev_priv(dev);
 | ||||
| +   txp = dp->tx_private;
 | ||||
| +
 | ||||
| +   u64_stats_update_begin(&txp->rx_stats.sync);
 | ||||
| +   txp->rx_stats.packets += u64_stats_read(&offload_stats->rx_packets);
 | ||||
| +   txp->rx_stats.bytes += u64_stats_read(&offload_stats->rx_bytes);
 | ||||
| +   txp->tx_stats.packets += u64_stats_read(&offload_stats->tx_packets);
 | ||||
| +   txp->tx_stats.bytes += u64_stats_read(&offload_stats->tx_bytes);
 | ||||
| +   u64_stats_update_end(&txp->rx_stats.sync);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(ifb_update_offload_stats);
 | ||||
| +
 | ||||
|  static void ifb_stats64(struct net_device *dev, | ||||
|  			struct rtnl_link_stats64 *stats) | ||||
|  { | ||||
| @@ -326,6 +351,7 @@ static void ifb_setup(struct net_device
 | ||||
|  	dev->flags |= IFF_NOARP; | ||||
|  	dev->flags &= ~IFF_MULTICAST; | ||||
|  	dev->priv_flags &= ~IFF_TX_SKB_SHARING; | ||||
| +	dev->priv_flags_ext |= IFF_EXT_IFB;     /* Mark the device as an IFB device. */
 | ||||
|  	netif_keep_dst(dev); | ||||
|  	eth_hw_addr_random(dev); | ||||
|  	dev->needs_free_netdev = true; | ||||
| --- a/include/linux/netdevice.h
 | ||||
| +++ b/include/linux/netdevice.h
 | ||||
| @@ -4588,6 +4588,15 @@ void dev_uc_flush(struct net_device *dev
 | ||||
|  void dev_uc_init(struct net_device *dev); | ||||
|   | ||||
|  /** | ||||
| + *  ifb_update_offload_stats - Update the IFB interface stats
 | ||||
| + *  @dev: IFB device to update the stats
 | ||||
| + *  @offload_stats: per CPU stats structure
 | ||||
| + *
 | ||||
| + *  Allows update of IFB stats when flows are offloaded to an accelerator.
 | ||||
| + **/
 | ||||
| +void ifb_update_offload_stats(struct net_device *dev, struct pcpu_sw_netstats *offload_stats);
 | ||||
| +
 | ||||
| +/**
 | ||||
|   *  __dev_uc_sync - Synchonize device's unicast list | ||||
|   *  @dev:  device to sync | ||||
|   *  @sync: function to call if address should be added | ||||
| @@ -5133,6 +5142,11 @@ static inline bool netif_is_failover_sla
 | ||||
|  	return dev->priv_flags & IFF_FAILOVER_SLAVE; | ||||
|  } | ||||
|   | ||||
| +static inline bool netif_is_ifb_dev(const struct net_device *dev)
 | ||||
| +{
 | ||||
| +	return dev->priv_flags_ext & IFF_EXT_IFB;
 | ||||
| +}
 | ||||
| +
 | ||||
|  /* This device needs to keep skb dst for qdisc enqueue or ndo_start_xmit() */ | ||||
|  static inline void netif_keep_dst(struct net_device *dev) | ||||
|  { | ||||
| --- a/include/uapi/linux/pkt_sched.h
 | ||||
| +++ b/include/uapi/linux/pkt_sched.h
 | ||||
| @@ -1278,4 +1278,248 @@ enum {
 | ||||
|   | ||||
|  #define TCA_ETS_MAX (__TCA_ETS_MAX - 1) | ||||
|   | ||||
| +/* QCA NSS Clients Support - Start */
 | ||||
| +enum {
 | ||||
| +	TCA_NSS_ACCEL_MODE_NSS_FW,
 | ||||
| +	TCA_NSS_ACCEL_MODE_PPE,
 | ||||
| +	TCA_NSS_ACCEL_MODE_MAX
 | ||||
| +};
 | ||||
| +
 | ||||
| +/* NSSFIFO section */
 | ||||
| +
 | ||||
| +enum {
 | ||||
| +	TCA_NSSFIFO_UNSPEC,
 | ||||
| +	TCA_NSSFIFO_PARMS,
 | ||||
| +	__TCA_NSSFIFO_MAX
 | ||||
| +};
 | ||||
| +
 | ||||
| +#define TCA_NSSFIFO_MAX	(__TCA_NSSFIFO_MAX - 1)
 | ||||
| +
 | ||||
| +struct tc_nssfifo_qopt {
 | ||||
| +	__u32	limit;		/* Queue length: bytes for bfifo, packets for pfifo */
 | ||||
| +	__u8	set_default;	/* Sets qdisc to be the default qdisc for enqueue */
 | ||||
| +	__u8	accel_mode;	/* Dictates which data plane offloads the qdisc */
 | ||||
| +};
 | ||||
| +
 | ||||
| +/* NSSWRED section */
 | ||||
| +
 | ||||
| +enum {
 | ||||
| +	TCA_NSSWRED_UNSPEC,
 | ||||
| +	TCA_NSSWRED_PARMS,
 | ||||
| +	__TCA_NSSWRED_MAX
 | ||||
| +};
 | ||||
| +
 | ||||
| +#define TCA_NSSWRED_MAX (__TCA_NSSWRED_MAX - 1)
 | ||||
| +#define NSSWRED_CLASS_MAX 6
 | ||||
| +struct tc_red_alg_parameter {
 | ||||
| +	__u32	min;	/* qlen_avg < min: pkts are all enqueued */
 | ||||
| +	__u32	max;	/* qlen_avg > max: pkts are all dropped */
 | ||||
| +	__u32	probability;/* Drop probability at qlen_avg = max */
 | ||||
| +	__u32	exp_weight_factor;/* exp_weight_factor for calculate qlen_avg */
 | ||||
| +};
 | ||||
| +
 | ||||
| +struct tc_nsswred_traffic_class {
 | ||||
| +	__u32 limit;			/* Queue length */
 | ||||
| +	__u32 weight_mode_value;	/* Weight mode value */
 | ||||
| +	struct tc_red_alg_parameter rap;/* Parameters for RED alg */
 | ||||
| +};
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * Weight modes for WRED
 | ||||
| + */
 | ||||
| +enum tc_nsswred_weight_modes {
 | ||||
| +	TC_NSSWRED_WEIGHT_MODE_DSCP = 0,/* Weight mode is DSCP */
 | ||||
| +	TC_NSSWRED_WEIGHT_MODES,	/* Must be last */
 | ||||
| +};
 | ||||
| +
 | ||||
| +struct tc_nsswred_qopt {
 | ||||
| +	__u32	limit;			/* Queue length */
 | ||||
| +	enum tc_nsswred_weight_modes weight_mode;
 | ||||
| +					/* Weight mode */
 | ||||
| +	__u32	traffic_classes;	/* How many traffic classes: DPs */
 | ||||
| +	__u32	def_traffic_class;	/* Default traffic if no match: def_DP */
 | ||||
| +	__u32	traffic_id;		/* The traffic id to be configured: DP */
 | ||||
| +	__u32	weight_mode_value;	/* Weight mode value */
 | ||||
| +	struct tc_red_alg_parameter rap;/* RED algorithm parameters */
 | ||||
| +	struct tc_nsswred_traffic_class tntc[NSSWRED_CLASS_MAX];
 | ||||
| +					/* Traffic settings for dumpping */
 | ||||
| +	__u8	ecn;			/* Setting ECN bit or dropping */
 | ||||
| +	__u8	set_default;		/* Sets qdisc to be the default for enqueue */
 | ||||
| +	__u8	accel_mode;		/* Dictates which data plane offloads the qdisc */
 | ||||
| +};
 | ||||
| +
 | ||||
| +/* NSSCODEL section */
 | ||||
| +
 | ||||
| +enum {
 | ||||
| +	TCA_NSSCODEL_UNSPEC,
 | ||||
| +	TCA_NSSCODEL_PARMS,
 | ||||
| +	__TCA_NSSCODEL_MAX
 | ||||
| +};
 | ||||
| +
 | ||||
| +#define TCA_NSSCODEL_MAX	(__TCA_NSSCODEL_MAX - 1)
 | ||||
| +
 | ||||
| +struct tc_nsscodel_qopt {
 | ||||
| +	__u32	target;		/* Acceptable queueing delay */
 | ||||
| +	__u32	limit;		/* Max number of packets that can be held in the queue */
 | ||||
| +	__u32	interval;	/* Monitoring interval */
 | ||||
| +	__u32	flows;		/* Number of flow buckets */
 | ||||
| +	__u32	quantum;	/* Weight (in bytes) used for DRR of flow buckets */
 | ||||
| +	__u8	ecn;		/* 0 - disable ECN, 1 - enable ECN */
 | ||||
| +	__u8	set_default;	/* Sets qdisc to be the default qdisc for enqueue */
 | ||||
| +	__u8	accel_mode;	/* Dictates which data plane offloads the qdisc */
 | ||||
| +};
 | ||||
| +
 | ||||
| +struct tc_nsscodel_xstats {
 | ||||
| +	__u32 peak_queue_delay;	/* Peak delay experienced by a dequeued packet */
 | ||||
| +	__u32 peak_drop_delay;	/* Peak delay experienced by a dropped packet */
 | ||||
| +};
 | ||||
| +
 | ||||
| +/* NSSFQ_CODEL section */
 | ||||
| +
 | ||||
| +struct tc_nssfq_codel_xstats {
 | ||||
| +	__u32 new_flow_count;	/* Total number of new flows seen */
 | ||||
| +	__u32 new_flows_len;	/* Current number of new flows */
 | ||||
| +	__u32 old_flows_len;	/* Current number of old flows */
 | ||||
| +	__u32 ecn_mark;		/* Number of packets marked with ECN */
 | ||||
| +	__u32 drop_overlimit;	/* Number of packets dropped due to overlimit */
 | ||||
| +	__u32 maxpacket;	/* The largest packet seen so far in the queue */
 | ||||
| +};
 | ||||
| +
 | ||||
| +/* NSSTBL section */
 | ||||
| +
 | ||||
| +enum {
 | ||||
| +	TCA_NSSTBL_UNSPEC,
 | ||||
| +	TCA_NSSTBL_PARMS,
 | ||||
| +	__TCA_NSSTBL_MAX
 | ||||
| +};
 | ||||
| +
 | ||||
| +#define TCA_NSSTBL_MAX	(__TCA_NSSTBL_MAX - 1)
 | ||||
| +
 | ||||
| +struct tc_nsstbl_qopt {
 | ||||
| +	__u32	burst;		/* Maximum burst size */
 | ||||
| +	__u32	rate;		/* Limiting rate of TBF */
 | ||||
| +	__u32	peakrate;	/* Maximum rate at which TBF is allowed to send */
 | ||||
| +	__u32	mtu;		/* Max size of packet, or minumim burst size */
 | ||||
| +	__u8	accel_mode;	/* Dictates which data plane offloads the qdisc */
 | ||||
| +};
 | ||||
| +
 | ||||
| +/* NSSPRIO section */
 | ||||
| +
 | ||||
| +#define TCA_NSSPRIO_MAX_BANDS 256
 | ||||
| +
 | ||||
| +enum {
 | ||||
| +	TCA_NSSPRIO_UNSPEC,
 | ||||
| +	TCA_NSSPRIO_PARMS,
 | ||||
| +	__TCA_NSSPRIO_MAX
 | ||||
| +};
 | ||||
| +
 | ||||
| +#define TCA_NSSPRIO_MAX	(__TCA_NSSPRIO_MAX - 1)
 | ||||
| +
 | ||||
| +struct tc_nssprio_qopt {
 | ||||
| +	__u32	bands;		/* Number of bands */
 | ||||
| +	__u8	accel_mode;	/* Dictates which data plane offloads the qdisc */
 | ||||
| +};
 | ||||
| +
 | ||||
| +/* NSSBF section */
 | ||||
| +
 | ||||
| +enum {
 | ||||
| +	TCA_NSSBF_UNSPEC,
 | ||||
| +	TCA_NSSBF_CLASS_PARMS,
 | ||||
| +	TCA_NSSBF_QDISC_PARMS,
 | ||||
| +	__TCA_NSSBF_MAX
 | ||||
| +};
 | ||||
| +
 | ||||
| +#define TCA_NSSBF_MAX	(__TCA_NSSBF_MAX - 1)
 | ||||
| +
 | ||||
| +struct tc_nssbf_class_qopt {
 | ||||
| +	__u32	burst;		/* Maximum burst size */
 | ||||
| +	__u32	rate;		/* Allowed bandwidth for this class */
 | ||||
| +	__u32	mtu;		/* MTU of the associated interface */
 | ||||
| +	__u32	quantum;	/* Quantum allocation for DRR */
 | ||||
| +};
 | ||||
| +
 | ||||
| +struct tc_nssbf_qopt {
 | ||||
| +	__u16	defcls;		/* Default class value */
 | ||||
| +	__u8	accel_mode;	/* Dictates which data plane offloads the qdisc */
 | ||||
| +};
 | ||||
| +
 | ||||
| +/* NSSWRR section */
 | ||||
| +
 | ||||
| +enum {
 | ||||
| +	TCA_NSSWRR_UNSPEC,
 | ||||
| +	TCA_NSSWRR_CLASS_PARMS,
 | ||||
| +	TCA_NSSWRR_QDISC_PARMS,
 | ||||
| +	__TCA_NSSWRR_MAX
 | ||||
| +};
 | ||||
| +
 | ||||
| +#define TCA_NSSWRR_MAX	(__TCA_NSSWRR_MAX - 1)
 | ||||
| +
 | ||||
| +struct tc_nsswrr_class_qopt {
 | ||||
| +	__u32	quantum;	/* Weight associated to this class */
 | ||||
| +};
 | ||||
| +
 | ||||
| +struct tc_nsswrr_qopt {
 | ||||
| +	__u8	accel_mode;	/* Dictates which data plane offloads the qdisc */
 | ||||
| +};
 | ||||
| +
 | ||||
| +/* NSSWFQ section */
 | ||||
| +
 | ||||
| +enum {
 | ||||
| +	TCA_NSSWFQ_UNSPEC,
 | ||||
| +	TCA_NSSWFQ_CLASS_PARMS,
 | ||||
| +	TCA_NSSWFQ_QDISC_PARMS,
 | ||||
| +	__TCA_NSSWFQ_MAX
 | ||||
| +};
 | ||||
| +
 | ||||
| +#define TCA_NSSWFQ_MAX	(__TCA_NSSWFQ_MAX - 1)
 | ||||
| +
 | ||||
| +struct tc_nsswfq_class_qopt {
 | ||||
| +	__u32	quantum;	/* Weight associated to this class */
 | ||||
| +};
 | ||||
| +
 | ||||
| +struct tc_nsswfq_qopt {
 | ||||
| +	__u8	accel_mode;	/* Dictates which data plane offloads the qdisc */
 | ||||
| +};
 | ||||
| +
 | ||||
| +/* NSSHTB section */
 | ||||
| +
 | ||||
| +enum {
 | ||||
| +	TCA_NSSHTB_UNSPEC,
 | ||||
| +	TCA_NSSHTB_CLASS_PARMS,
 | ||||
| +	TCA_NSSHTB_QDISC_PARMS,
 | ||||
| +	__TCA_NSSHTB_MAX
 | ||||
| +};
 | ||||
| +
 | ||||
| +#define TCA_NSSHTB_MAX	(__TCA_NSSHTB_MAX - 1)
 | ||||
| +
 | ||||
| +struct tc_nsshtb_class_qopt {
 | ||||
| +	__u32	burst;		/* Allowed burst size */
 | ||||
| +	__u32	rate;		/* Allowed bandwidth for this class */
 | ||||
| +	__u32	cburst;		/* Maximum burst size */
 | ||||
| +	__u32	crate;		/* Maximum bandwidth for this class */
 | ||||
| +	__u32	quantum;	/* Quantum allocation for DRR */
 | ||||
| +	__u32	priority;	/* Priority value associated with this class */
 | ||||
| +	__u32	overhead;	/* Overhead in bytes per packet */
 | ||||
| +};
 | ||||
| +
 | ||||
| +struct tc_nsshtb_qopt {
 | ||||
| +	__u32	r2q;		/* Rate to quantum ratio */
 | ||||
| +	__u8	accel_mode;	/* Dictates which data plane offloads the qdisc */
 | ||||
| +};
 | ||||
| +
 | ||||
| +/* NSSBLACKHOLE section */
 | ||||
| +
 | ||||
| +enum {
 | ||||
| +	TCA_NSSBLACKHOLE_UNSPEC,
 | ||||
| +	TCA_NSSBLACKHOLE_PARMS,
 | ||||
| +	__TCA_NSSBLACKHOLE_MAX
 | ||||
| +};
 | ||||
| +
 | ||||
| +#define TCA_NSSBLACKHOLE_MAX	(__TCA_NSSBLACKHOLE_MAX - 1)
 | ||||
| +
 | ||||
| +struct tc_nssblackhole_qopt {
 | ||||
| +	__u8	set_default;	/* Sets qdisc to be the default qdisc for enqueue */
 | ||||
| +	__u8	accel_mode;	/* Dictates which data plane offloads the qdisc */
 | ||||
| +};
 | ||||
| +/* QCA NSS Clients Support - End */
 | ||||
|  #endif | ||||
| --- a/net/sched/sch_api.c
 | ||||
| +++ b/net/sched/sch_api.c
 | ||||
| @@ -313,6 +313,7 @@ struct Qdisc *qdisc_lookup(struct net_de
 | ||||
|  out: | ||||
|  	return q; | ||||
|  } | ||||
| +EXPORT_SYMBOL(qdisc_lookup);
 | ||||
|   | ||||
|  struct Qdisc *qdisc_lookup_rcu(struct net_device *dev, u32 handle) | ||||
|  { | ||||
| @@ -2386,4 +2387,26 @@ static int __init pktsched_init(void)
 | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +/* QCA NSS Qdisc Support - Start */
 | ||||
| +bool tcf_destroy(struct tcf_proto *tp, bool force)
 | ||||
| +{
 | ||||
| +	tp->ops->destroy(tp, force, NULL);
 | ||||
| +	module_put(tp->ops->owner);
 | ||||
| +	kfree_rcu(tp, rcu);
 | ||||
| +
 | ||||
| +	return true;
 | ||||
| +}
 | ||||
| +
 | ||||
| +void tcf_destroy_chain(struct tcf_proto __rcu **fl)
 | ||||
| +{
 | ||||
| +	struct tcf_proto *tp;
 | ||||
| +
 | ||||
| +	while ((tp = rtnl_dereference(*fl)) != NULL) {
 | ||||
| +		RCU_INIT_POINTER(*fl, tp->next);
 | ||||
| +		tcf_destroy(tp, true);
 | ||||
| +	}
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(tcf_destroy_chain);
 | ||||
| +/* QCA NSS Qdisc Support - End */
 | ||||
| +
 | ||||
|  subsys_initcall(pktsched_init); | ||||
| --- a/net/sched/sch_generic.c
 | ||||
| +++ b/net/sched/sch_generic.c
 | ||||
| @@ -1069,6 +1069,7 @@ static void __qdisc_destroy(struct Qdisc
 | ||||
|   | ||||
|  	call_rcu(&qdisc->rcu, qdisc_free_cb); | ||||
|  } | ||||
| +EXPORT_SYMBOL(qdisc_destroy);
 | ||||
|   | ||||
|  void qdisc_destroy(struct Qdisc *qdisc) | ||||
|  { | ||||
| --- a/include/net/sch_generic.h
 | ||||
| +++ b/include/net/sch_generic.h
 | ||||
| @@ -94,6 +94,7 @@ struct Qdisc {
 | ||||
|  #define TCQ_F_INVISIBLE		0x80 /* invisible by default in dump */ | ||||
|  #define TCQ_F_NOLOCK		0x100 /* qdisc does not require locking */ | ||||
|  #define TCQ_F_OFFLOADED		0x200 /* qdisc is offloaded to HW */ | ||||
| +#define TCQ_F_NSS		0x1000 /* NSS qdisc flag. */
 | ||||
|  	u32			limit; | ||||
|  	const struct Qdisc_ops	*ops; | ||||
|  	struct qdisc_size_table	__rcu *stab; | ||||
| @@ -719,6 +720,40 @@ static inline bool skb_skip_tc_classify(
 | ||||
|  	return false; | ||||
|  } | ||||
|   | ||||
| +/*
 | ||||
| + * Set skb classify bit field.
 | ||||
| + */
 | ||||
| +static inline void skb_set_tc_classify_offload(struct sk_buff *skb)
 | ||||
| +{
 | ||||
| +#ifdef CONFIG_NET_CLS_ACT
 | ||||
| +	skb->tc_skip_classify_offload = 1;
 | ||||
| +#endif
 | ||||
| +}
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * Clear skb classify bit field.
 | ||||
| + */
 | ||||
| +static inline void skb_clear_tc_classify_offload(struct sk_buff *skb)
 | ||||
| +{
 | ||||
| +#ifdef CONFIG_NET_CLS_ACT
 | ||||
| +	skb->tc_skip_classify_offload = 0;
 | ||||
| +#endif
 | ||||
| +}
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * Skip skb processing if sent from ifb dev.
 | ||||
| + */
 | ||||
| +static inline bool skb_skip_tc_classify_offload(struct sk_buff *skb)
 | ||||
| +{
 | ||||
| +#ifdef CONFIG_NET_CLS_ACT
 | ||||
| +	if (skb->tc_skip_classify_offload) {
 | ||||
| +		skb_clear_tc_classify_offload(skb);
 | ||||
| +		return true;
 | ||||
| +	}
 | ||||
| +#endif
 | ||||
| +	return false;
 | ||||
| +}
 | ||||
| +
 | ||||
|  /* Reset all TX qdiscs greater than index of a device.  */ | ||||
|  static inline void qdisc_reset_all_tx_gt(struct net_device *dev, unsigned int i) | ||||
|  { | ||||
| @@ -1305,4 +1340,9 @@ static inline void qdisc_synchronize(con
 | ||||
|  		msleep(1); | ||||
|  } | ||||
|   | ||||
| +/* QCA NSS Qdisc Support - Start */
 | ||||
| +void qdisc_destroy(struct Qdisc *qdisc);
 | ||||
| +void tcf_destroy_chain(struct tcf_proto __rcu **fl);
 | ||||
| +/* QCA NSS Qdisc Support - End */
 | ||||
| +
 | ||||
|  #endif | ||||
|  | @ -0,0 +1,46 @@ | |||
| --- a/net/l2tp/l2tp_core.c
 | ||||
| +++ b/net/l2tp/l2tp_core.c
 | ||||
| @@ -398,6 +398,31 @@ err_tlock:
 | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(l2tp_session_register); | ||||
|   | ||||
| +void l2tp_stats_update(struct l2tp_tunnel *tunnel,
 | ||||
| +                       struct l2tp_session *session,
 | ||||
| +                       struct l2tp_stats *stats)
 | ||||
| +{
 | ||||
| +        atomic_long_add(atomic_long_read(&stats->rx_packets),
 | ||||
| +                        &tunnel->stats.rx_packets);
 | ||||
| +        atomic_long_add(atomic_long_read(&stats->rx_bytes),
 | ||||
| +                        &tunnel->stats.rx_bytes);
 | ||||
| +        atomic_long_add(atomic_long_read(&stats->tx_packets),
 | ||||
| +                        &tunnel->stats.tx_packets);
 | ||||
| +        atomic_long_add(atomic_long_read(&stats->tx_bytes),
 | ||||
| +                        &tunnel->stats.tx_bytes);
 | ||||
| +
 | ||||
| +        atomic_long_add(atomic_long_read(&stats->rx_packets),
 | ||||
| +                        &session->stats.rx_packets);
 | ||||
| +        atomic_long_add(atomic_long_read(&stats->rx_bytes),
 | ||||
| +                        &session->stats.rx_bytes);
 | ||||
| +        atomic_long_add(atomic_long_read(&stats->tx_packets),
 | ||||
| +                        &session->stats.tx_packets);
 | ||||
| +        atomic_long_add(atomic_long_read(&stats->tx_bytes),
 | ||||
| +                        &session->stats.tx_bytes);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL_GPL(l2tp_stats_update);
 | ||||
| +
 | ||||
| +
 | ||||
|  /***************************************************************************** | ||||
|   * Receive data handling | ||||
|   *****************************************************************************/ | ||||
| --- a/net/l2tp/l2tp_core.h
 | ||||
| +++ b/net/l2tp/l2tp_core.h
 | ||||
| @@ -232,6 +232,9 @@ struct l2tp_session *l2tp_session_get_nt
 | ||||
|  struct l2tp_session *l2tp_session_get_by_ifname(const struct net *net, | ||||
|  						const char *ifname); | ||||
|   | ||||
| +void l2tp_stats_update(struct l2tp_tunnel *tunnel, struct l2tp_session *session,
 | ||||
| +                       struct l2tp_stats *stats);
 | ||||
| +
 | ||||
|  /* Tunnel and session lifetime management. | ||||
|   * Creation of a new instance is a two-step process: create, then register. | ||||
|   * Destruction is triggered using the *_delete functions, and completes asynchronously. | ||||
|  | @ -0,0 +1,478 @@ | |||
| --- a/include/linux/if_pppox.h
 | ||||
| +++ b/include/linux/if_pppox.h
 | ||||
| @@ -36,6 +36,7 @@ struct pptp_opt {
 | ||||
|  	u32 ack_sent, ack_recv; | ||||
|  	u32 seq_sent, seq_recv; | ||||
|  	int ppp_flags; | ||||
| +	bool pptp_offload_mode;
 | ||||
|  }; | ||||
|  #include <net/sock.h> | ||||
|   | ||||
| @@ -100,8 +101,40 @@ struct pppoe_channel_ops {
 | ||||
|  	int (*get_addressing)(struct ppp_channel *, struct pppoe_opt *); | ||||
|  }; | ||||
|   | ||||
| +/* PPTP client callback */
 | ||||
| +typedef int (*pptp_gre_seq_offload_callback_t)(struct sk_buff *skb,
 | ||||
| +					       struct net_device *pptp_dev);
 | ||||
| +
 | ||||
|  /* Return PPPoE channel specific addressing information */ | ||||
|  extern int pppoe_channel_addressing_get(struct ppp_channel *chan, | ||||
|  					 struct pppoe_opt *addressing); | ||||
|   | ||||
| +/* Lookup PPTP session info and return PPTP session using sip, dip and local call id */
 | ||||
| +extern int pptp_session_find_by_src_callid(struct pptp_opt *opt, __be16 src_call_id,
 | ||||
| +			 __be32 daddr, __be32 saddr);
 | ||||
| +
 | ||||
| +/* Lookup PPTP session info and return PPTP session using dip and peer call id */
 | ||||
| +extern int pptp_session_find(struct pptp_opt *opt, __be16 peer_call_id,
 | ||||
| +			     __be32 peer_ip_addr);
 | ||||
| +
 | ||||
| +/* Return PPTP session information given the channel */
 | ||||
| +extern void pptp_channel_addressing_get(struct pptp_opt *opt,
 | ||||
| +					struct ppp_channel *chan);
 | ||||
| +
 | ||||
| +/* Enable the PPTP session offload flag */
 | ||||
| +extern int pptp_session_enable_offload_mode(__be16 peer_call_id,
 | ||||
| +					    __be32 peer_ip_addr);
 | ||||
| +
 | ||||
| +/* Disable the PPTP session offload flag */
 | ||||
| +extern int pptp_session_disable_offload_mode(__be16 peer_call_id,
 | ||||
| +					     __be32 peer_ip_addr);
 | ||||
| +
 | ||||
| +/* Register the PPTP GRE packets sequence number offload callback */
 | ||||
| +extern int
 | ||||
| +pptp_register_gre_seq_offload_callback(pptp_gre_seq_offload_callback_t
 | ||||
| +				       pptp_client_cb);
 | ||||
| +
 | ||||
| +/* Unregister the PPTP GRE packets sequence number offload callback */
 | ||||
| +extern void pptp_unregister_gre_seq_offload_callback(void);
 | ||||
| +
 | ||||
|  #endif /* !(__LINUX_IF_PPPOX_H) */ | ||||
| --- a/drivers/net/ppp/ppp_generic.c
 | ||||
| +++ b/drivers/net/ppp/ppp_generic.c
 | ||||
| @@ -2973,6 +2973,20 @@ char *ppp_dev_name(struct ppp_channel *c
 | ||||
|  	return name; | ||||
|  } | ||||
|   | ||||
| +/* Return the PPP net device index */
 | ||||
| +int ppp_dev_index(struct ppp_channel *chan)
 | ||||
| +{
 | ||||
| +	struct channel *pch = chan->ppp;
 | ||||
| +	int ifindex = 0;
 | ||||
| +
 | ||||
| +	if (pch) {
 | ||||
| +		read_lock_bh(&pch->upl);
 | ||||
| +		if (pch->ppp && pch->ppp->dev)
 | ||||
| +			ifindex = pch->ppp->dev->ifindex;
 | ||||
| +		read_unlock_bh(&pch->upl);
 | ||||
| +	}
 | ||||
| +	return ifindex;
 | ||||
| +}
 | ||||
|   | ||||
|  /* | ||||
|   * Disconnect a channel from the generic layer. | ||||
| @@ -3681,6 +3695,28 @@ void ppp_update_stats(struct net_device
 | ||||
|  	ppp_recv_unlock(ppp); | ||||
|  } | ||||
|   | ||||
| +/* Returns true if Compression is enabled on PPP device
 | ||||
| + */
 | ||||
| +bool ppp_is_cp_enabled(struct net_device *dev)
 | ||||
| +{
 | ||||
| +	struct ppp *ppp;
 | ||||
| +	bool flag = false;
 | ||||
| +
 | ||||
| +	if (!dev)
 | ||||
| +		return false;
 | ||||
| +
 | ||||
| +	if (dev->type != ARPHRD_PPP)
 | ||||
| +		return false;
 | ||||
| +
 | ||||
| +	ppp = netdev_priv(dev);
 | ||||
| +	ppp_lock(ppp);
 | ||||
| +	flag = !!(ppp->xstate & SC_COMP_RUN) || !!(ppp->rstate & SC_DECOMP_RUN);
 | ||||
| +	ppp_unlock(ppp);
 | ||||
| +
 | ||||
| +	return flag;
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(ppp_is_cp_enabled);
 | ||||
| +
 | ||||
|  /* Returns >0 if the device is a multilink PPP netdevice, 0 if not or < 0 if | ||||
|   * the device is not PPP. | ||||
|   */ | ||||
| @@ -3872,6 +3908,7 @@ EXPORT_SYMBOL(ppp_unregister_channel);
 | ||||
|  EXPORT_SYMBOL(ppp_channel_index); | ||||
|  EXPORT_SYMBOL(ppp_unit_number); | ||||
|  EXPORT_SYMBOL(ppp_dev_name); | ||||
| +EXPORT_SYMBOL(ppp_dev_index);
 | ||||
|  EXPORT_SYMBOL(ppp_input); | ||||
|  EXPORT_SYMBOL(ppp_input_error); | ||||
|  EXPORT_SYMBOL(ppp_output_wakeup); | ||||
| --- a/include/linux/ppp_channel.h
 | ||||
| +++ b/include/linux/ppp_channel.h
 | ||||
| @@ -84,6 +84,9 @@ extern void ppp_unregister_channel(struc
 | ||||
|  /* Get the channel number for a channel */ | ||||
|  extern int ppp_channel_index(struct ppp_channel *); | ||||
|   | ||||
| +/* Get the device index  associated with a channel, or 0, if none */
 | ||||
| +extern int ppp_dev_index(struct ppp_channel *);
 | ||||
| +
 | ||||
|  /* Get the unit number associated with a channel, or -1 if none */ | ||||
|  extern int ppp_unit_number(struct ppp_channel *); | ||||
|   | ||||
| @@ -116,6 +119,7 @@ extern int ppp_hold_channels(struct net_
 | ||||
|  /* Test if ppp xmit lock is locked */ | ||||
|  extern bool ppp_is_xmit_locked(struct net_device *dev); | ||||
|   | ||||
| +bool ppp_is_cp_enabled(struct net_device *dev);
 | ||||
|  /* Test if the ppp device is a multi-link ppp device */ | ||||
|  extern int ppp_is_multilink(struct net_device *dev); | ||||
|   | ||||
| --- a/drivers/net/ppp/pptp.c
 | ||||
| +++ b/drivers/net/ppp/pptp.c
 | ||||
| @@ -50,6 +50,8 @@ static struct proto pptp_sk_proto __read
 | ||||
|  static const struct ppp_channel_ops pptp_chan_ops; | ||||
|  static const struct proto_ops pptp_ops; | ||||
|   | ||||
| +static pptp_gre_seq_offload_callback_t __rcu pptp_gre_offload_xmit_cb;
 | ||||
| +
 | ||||
|  static struct pppox_sock *lookup_chan(u16 call_id, __be32 s_addr) | ||||
|  { | ||||
|  	struct pppox_sock *sock; | ||||
| @@ -91,6 +93,79 @@ static int lookup_chan_dst(u16 call_id,
 | ||||
|  	return i < MAX_CALLID; | ||||
|  } | ||||
|   | ||||
| +/* Search a pptp session based on local call id, local and remote ip address */
 | ||||
| +static int lookup_session_src(struct pptp_opt *opt, u16 call_id, __be32 daddr, __be32 saddr)
 | ||||
| +{
 | ||||
| +	struct pppox_sock *sock;
 | ||||
| +	int i = 1;
 | ||||
| +
 | ||||
| +	rcu_read_lock();
 | ||||
| +	for_each_set_bit_from(i, callid_bitmap, MAX_CALLID) {
 | ||||
| +		sock = rcu_dereference(callid_sock[i]);
 | ||||
| +		if (!sock)
 | ||||
| +			continue;
 | ||||
| +
 | ||||
| +		if (sock->proto.pptp.src_addr.call_id == call_id &&
 | ||||
| +		    sock->proto.pptp.dst_addr.sin_addr.s_addr == daddr &&
 | ||||
| +		    sock->proto.pptp.src_addr.sin_addr.s_addr == saddr) {
 | ||||
| +			sock_hold(sk_pppox(sock));
 | ||||
| +			memcpy(opt, &sock->proto.pptp, sizeof(struct pptp_opt));
 | ||||
| +			sock_put(sk_pppox(sock));
 | ||||
| +			rcu_read_unlock();
 | ||||
| +			return 0;
 | ||||
| +		}
 | ||||
| +	}
 | ||||
| +	rcu_read_unlock();
 | ||||
| +	return -EINVAL;
 | ||||
| +}
 | ||||
| +
 | ||||
| +/* Search a pptp session based on peer call id and peer ip address */
 | ||||
| +static int lookup_session_dst(struct pptp_opt *opt, u16 call_id, __be32 d_addr)
 | ||||
| +{
 | ||||
| +	struct pppox_sock *sock;
 | ||||
| +	int i = 1;
 | ||||
| +
 | ||||
| +	rcu_read_lock();
 | ||||
| +	for_each_set_bit_from(i, callid_bitmap, MAX_CALLID) {
 | ||||
| +		sock = rcu_dereference(callid_sock[i]);
 | ||||
| +		if (!sock)
 | ||||
| +			continue;
 | ||||
| +
 | ||||
| +		if (sock->proto.pptp.dst_addr.call_id == call_id &&
 | ||||
| +		    sock->proto.pptp.dst_addr.sin_addr.s_addr == d_addr) {
 | ||||
| +			sock_hold(sk_pppox(sock));
 | ||||
| +			memcpy(opt, &sock->proto.pptp, sizeof(struct pptp_opt));
 | ||||
| +			sock_put(sk_pppox(sock));
 | ||||
| +			rcu_read_unlock();
 | ||||
| +			return 0;
 | ||||
| +		}
 | ||||
| +	}
 | ||||
| +	rcu_read_unlock();
 | ||||
| +	return -EINVAL;
 | ||||
| +}
 | ||||
| +
 | ||||
| +/* If offload mode set then this function sends all packets to
 | ||||
| + * offload module instead of network stack
 | ||||
| + */
 | ||||
| +static int pptp_client_skb_xmit(struct sk_buff *skb,
 | ||||
| +				struct net_device *pptp_dev)
 | ||||
| +{
 | ||||
| +	pptp_gre_seq_offload_callback_t pptp_gre_offload_cb_f;
 | ||||
| +	int ret;
 | ||||
| +
 | ||||
| +	rcu_read_lock();
 | ||||
| +	pptp_gre_offload_cb_f = rcu_dereference(pptp_gre_offload_xmit_cb);
 | ||||
| +
 | ||||
| +	if (!pptp_gre_offload_cb_f) {
 | ||||
| +		rcu_read_unlock();
 | ||||
| +		return -1;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	ret = pptp_gre_offload_cb_f(skb, pptp_dev);
 | ||||
| +	rcu_read_unlock();
 | ||||
| +	return ret;
 | ||||
| +}
 | ||||
| +
 | ||||
|  static int add_chan(struct pppox_sock *sock, | ||||
|  		    struct pptp_addr *sa) | ||||
|  { | ||||
| @@ -136,7 +211,7 @@ static struct rtable *pptp_route_output(
 | ||||
|  	struct net *net; | ||||
|   | ||||
|  	net = sock_net(sk); | ||||
| -	flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark, 0,
 | ||||
| +	flowi4_init_output(fl4, 0, sk->sk_mark, 0,
 | ||||
|  			   RT_SCOPE_UNIVERSE, IPPROTO_GRE, 0, | ||||
|  			   po->proto.pptp.dst_addr.sin_addr.s_addr, | ||||
|  			   po->proto.pptp.src_addr.sin_addr.s_addr, | ||||
| @@ -163,8 +238,11 @@ static int pptp_xmit(struct ppp_channel
 | ||||
|   | ||||
|  	struct rtable *rt; | ||||
|  	struct net_device *tdev; | ||||
| +	struct net_device *pptp_dev;
 | ||||
|  	struct iphdr  *iph; | ||||
|  	int    max_headroom; | ||||
| +	int    pptp_ifindex;
 | ||||
| +	int    ret;
 | ||||
|   | ||||
|  	if (sk_pppox(po)->sk_state & PPPOX_DEAD) | ||||
|  		goto tx_error; | ||||
| @@ -258,7 +336,32 @@ static int pptp_xmit(struct ppp_channel
 | ||||
|  	ip_select_ident(net, skb, NULL); | ||||
|  	ip_send_check(iph); | ||||
|   | ||||
| -	ip_local_out(net, skb->sk, skb);
 | ||||
| +	pptp_ifindex = ppp_dev_index(chan);
 | ||||
| +
 | ||||
| +	/* set incoming interface as the ppp interface */
 | ||||
| +	if (skb->skb_iif)
 | ||||
| +		skb->skb_iif = pptp_ifindex;
 | ||||
| +
 | ||||
| +	/* If the PPTP GRE seq number offload module is not enabled yet
 | ||||
| +	 * then sends all PPTP GRE packets through linux network stack
 | ||||
| +	 */
 | ||||
| +	if (!opt->pptp_offload_mode) {
 | ||||
| +		ip_local_out(net, skb->sk, skb);
 | ||||
| +		return 1;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	pptp_dev = dev_get_by_index(&init_net, pptp_ifindex);
 | ||||
| +	if (!pptp_dev)
 | ||||
| +		goto tx_error;
 | ||||
| +
 | ||||
| +	 /* If PPTP offload module is enabled then forward all PPTP GRE
 | ||||
| +	  * packets to PPTP GRE offload module
 | ||||
| +	  */
 | ||||
| +	ret = pptp_client_skb_xmit(skb, pptp_dev);
 | ||||
| +	dev_put(pptp_dev);
 | ||||
| +	if (ret < 0)
 | ||||
| +		goto tx_error;
 | ||||
| +
 | ||||
|  	return 1; | ||||
|   | ||||
|  tx_error: | ||||
| @@ -314,6 +417,13 @@ static int pptp_rcv_core(struct sock *sk
 | ||||
|  		goto drop; | ||||
|   | ||||
|  	payload = skb->data + headersize; | ||||
| +
 | ||||
| +	 /* If offload is enabled, we expect the offload module
 | ||||
| +	  * to handle PPTP GRE sequence number checks
 | ||||
| +	  */
 | ||||
| +	if (opt->pptp_offload_mode)
 | ||||
| +		goto allow_packet;
 | ||||
| +
 | ||||
|  	/* check for expected sequence number */ | ||||
|  	if (seq < opt->seq_recv + 1 || WRAPPED(opt->seq_recv, seq)) { | ||||
|  		if ((payload[0] == PPP_ALLSTATIONS) && (payload[1] == PPP_UI) && | ||||
| @@ -371,6 +481,7 @@ static int pptp_rcv(struct sk_buff *skb)
 | ||||
|  	if (po) { | ||||
|  		skb_dst_drop(skb); | ||||
|  		nf_reset_ct(skb); | ||||
| +		skb->skb_iif = ppp_dev_index(&po->chan);
 | ||||
|  		return sk_receive_skb(sk_pppox(po), skb, 0); | ||||
|  	} | ||||
|  drop: | ||||
| @@ -473,7 +584,7 @@ static int pptp_connect(struct socket *s
 | ||||
|   | ||||
|  	opt->dst_addr = sp->sa_addr.pptp; | ||||
|  	sk->sk_state |= PPPOX_CONNECTED; | ||||
| -
 | ||||
| +	opt->pptp_offload_mode = false;
 | ||||
|   end: | ||||
|  	release_sock(sk); | ||||
|  	return error; | ||||
| @@ -603,9 +714,169 @@ static int pptp_ppp_ioctl(struct ppp_cha
 | ||||
|  	return err; | ||||
|  } | ||||
|   | ||||
| +/* pptp_channel_addressing_get()
 | ||||
| + *	Return PPTP channel specific addressing information.
 | ||||
| + */
 | ||||
| +void pptp_channel_addressing_get(struct pptp_opt *opt, struct ppp_channel *chan)
 | ||||
| +{
 | ||||
| +	struct sock *sk;
 | ||||
| +	struct pppox_sock *po;
 | ||||
| +
 | ||||
| +	if (!opt)
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +	sk = (struct sock *)chan->private;
 | ||||
| +	if (!sk)
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +	sock_hold(sk);
 | ||||
| +
 | ||||
| +	/* This is very unlikely, but check the socket is connected state */
 | ||||
| +	if (unlikely(sock_flag(sk, SOCK_DEAD) ||
 | ||||
| +		     !(sk->sk_state & PPPOX_CONNECTED))) {
 | ||||
| +		sock_put(sk);
 | ||||
| +		return;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	po = pppox_sk(sk);
 | ||||
| +	memcpy(opt, &po->proto.pptp, sizeof(struct pptp_opt));
 | ||||
| +	sock_put(sk);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(pptp_channel_addressing_get);
 | ||||
| +
 | ||||
| +/* pptp_session_find()
 | ||||
| + *	Search and return a PPTP session info based on peer callid and IP
 | ||||
| + *	address. The function accepts the parameters in network byte order.
 | ||||
| + */
 | ||||
| +int pptp_session_find(struct pptp_opt *opt, __be16 peer_call_id,
 | ||||
| +		      __be32 peer_ip_addr)
 | ||||
| +{
 | ||||
| +	if (!opt)
 | ||||
| +		return -EINVAL;
 | ||||
| +
 | ||||
| +	return lookup_session_dst(opt, ntohs(peer_call_id), peer_ip_addr);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(pptp_session_find);
 | ||||
| +
 | ||||
| +/* pptp_session_find_by_src_callid()
 | ||||
| + *	Search and return a PPTP session info based on src callid and IP
 | ||||
| + *	address. The function accepts the parameters in network byte order.
 | ||||
| + */
 | ||||
| +int pptp_session_find_by_src_callid(struct pptp_opt *opt, __be16 src_call_id,
 | ||||
| +		      __be32 daddr, __be32 saddr)
 | ||||
| +{
 | ||||
| +	if (!opt)
 | ||||
| +		return -EINVAL;
 | ||||
| +
 | ||||
| +	return lookup_session_src(opt, ntohs(src_call_id), daddr, saddr);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(pptp_session_find_by_src_callid);
 | ||||
| +
 | ||||
| + /* Function to change the offload mode true/false for a PPTP session */
 | ||||
| +static int pptp_set_offload_mode(bool accel_mode,
 | ||||
| +				 __be16 peer_call_id, __be32 peer_ip_addr)
 | ||||
| +{
 | ||||
| +	struct pppox_sock *sock;
 | ||||
| +	int i = 1;
 | ||||
| +
 | ||||
| +	rcu_read_lock();
 | ||||
| +	for_each_set_bit_from(i, callid_bitmap, MAX_CALLID) {
 | ||||
| +		sock = rcu_dereference(callid_sock[i]);
 | ||||
| +		if (!sock)
 | ||||
| +			continue;
 | ||||
| +
 | ||||
| +		if (sock->proto.pptp.dst_addr.call_id == peer_call_id &&
 | ||||
| +		    sock->proto.pptp.dst_addr.sin_addr.s_addr == peer_ip_addr) {
 | ||||
| +			sock_hold(sk_pppox(sock));
 | ||||
| +			sock->proto.pptp.pptp_offload_mode = accel_mode;
 | ||||
| +			sock_put(sk_pppox(sock));
 | ||||
| +			rcu_read_unlock();
 | ||||
| +			return 0;
 | ||||
| +		}
 | ||||
| +	}
 | ||||
| +	rcu_read_unlock();
 | ||||
| +	return -EINVAL;
 | ||||
| +}
 | ||||
| +
 | ||||
| +/* Enable the PPTP session offload flag */
 | ||||
| +int pptp_session_enable_offload_mode(__be16 peer_call_id, __be32 peer_ip_addr)
 | ||||
| +{
 | ||||
| +	return pptp_set_offload_mode(true, peer_call_id, peer_ip_addr);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(pptp_session_enable_offload_mode);
 | ||||
| +
 | ||||
| +/* Disable the PPTP session offload flag */
 | ||||
| +int pptp_session_disable_offload_mode(__be16 peer_call_id, __be32 peer_ip_addr)
 | ||||
| +{
 | ||||
| +	return pptp_set_offload_mode(false, peer_call_id, peer_ip_addr);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(pptp_session_disable_offload_mode);
 | ||||
| +
 | ||||
| +/* Register the offload callback function on behalf of the module which
 | ||||
| + * will own the sequence and acknowledgment number updates for all
 | ||||
| + * PPTP GRE packets. All PPTP GRE packets are then transmitted to this
 | ||||
| + * module after encapsulation in order to ensure the correct seq/ack
 | ||||
| + * fields are set in the packets before transmission. This is required
 | ||||
| + * when PPTP flows are offloaded to acceleration engines, in-order to
 | ||||
| + * ensure consistency in sequence and ack numbers between PPTP control
 | ||||
| + * (PPP LCP) and data packets
 | ||||
| + */
 | ||||
| +int pptp_register_gre_seq_offload_callback(pptp_gre_seq_offload_callback_t
 | ||||
| +					   pptp_gre_offload_cb)
 | ||||
| +{
 | ||||
| +	pptp_gre_seq_offload_callback_t pptp_gre_offload_cb_f;
 | ||||
| +
 | ||||
| +	rcu_read_lock();
 | ||||
| +	pptp_gre_offload_cb_f = rcu_dereference(pptp_gre_offload_xmit_cb);
 | ||||
| +
 | ||||
| +	if (pptp_gre_offload_cb_f) {
 | ||||
| +		rcu_read_unlock();
 | ||||
| +		return -1;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	rcu_assign_pointer(pptp_gre_offload_xmit_cb, pptp_gre_offload_cb);
 | ||||
| +	rcu_read_unlock();
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(pptp_register_gre_seq_offload_callback);
 | ||||
| +
 | ||||
| +/* Unregister the PPTP GRE packets sequence number offload callback */
 | ||||
| +void pptp_unregister_gre_seq_offload_callback(void)
 | ||||
| +{
 | ||||
| +	rcu_assign_pointer(pptp_gre_offload_xmit_cb, NULL);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(pptp_unregister_gre_seq_offload_callback);
 | ||||
| +
 | ||||
| +/* pptp_hold_chan() */
 | ||||
| +static void pptp_hold_chan(struct ppp_channel *chan)
 | ||||
| +{
 | ||||
| +	struct sock *sk = (struct sock *)chan->private;
 | ||||
| +
 | ||||
| +	sock_hold(sk);
 | ||||
| +}
 | ||||
| +
 | ||||
| +/* pptp_release_chan() */
 | ||||
| +static void pptp_release_chan(struct ppp_channel *chan)
 | ||||
| +{
 | ||||
| +	struct sock *sk = (struct sock *)chan->private;
 | ||||
| +
 | ||||
| +	sock_put(sk);
 | ||||
| +}
 | ||||
| +
 | ||||
| +/* pptp_get_channel_protocol()
 | ||||
| + *     Return the protocol type of the PPTP over PPP protocol
 | ||||
| + */
 | ||||
| +static int pptp_get_channel_protocol(struct ppp_channel *chan)
 | ||||
| +{
 | ||||
| +	return PX_PROTO_PPTP;
 | ||||
| +}
 | ||||
| +
 | ||||
|  static const struct ppp_channel_ops pptp_chan_ops = { | ||||
|  	.start_xmit = pptp_xmit, | ||||
|  	.ioctl      = pptp_ppp_ioctl, | ||||
| +	.get_channel_protocol = pptp_get_channel_protocol,
 | ||||
| +	.hold = pptp_hold_chan,
 | ||||
| +	.release = pptp_release_chan,
 | ||||
|  }; | ||||
|   | ||||
|  static struct proto pptp_sk_proto __read_mostly = { | ||||
|  | @ -0,0 +1,77 @@ | |||
| --- a/include/net/ip6_tunnel.h
 | ||||
| +++ b/include/net/ip6_tunnel.h
 | ||||
| @@ -36,6 +36,7 @@ struct __ip6_tnl_parm {
 | ||||
|  	__u8 proto;		/* tunnel protocol */ | ||||
|  	__u8 encap_limit;	/* encapsulation limit for tunnel */ | ||||
|  	__u8 hop_limit;		/* hop limit for tunnel */ | ||||
| +	__u8 draft03;		/* FMR using draft03 of map-e - QCA NSS Clients Support */
 | ||||
|  	bool collect_md; | ||||
|  	__be32 flowinfo;	/* traffic class and flowlabel for tunnel */ | ||||
|  	__u32 flags;		/* tunnel flags */ | ||||
| --- a/include/net/ip_tunnels.h
 | ||||
| +++ b/include/net/ip_tunnels.h
 | ||||
| @@ -553,4 +553,9 @@ static inline void ip_tunnel_info_opts_s
 | ||||
|   | ||||
|  #endif /* CONFIG_INET */ | ||||
|   | ||||
| +/* QCA NSS Clients Support - Start */
 | ||||
| +void ipip6_update_offload_stats(struct net_device *dev, void *ptr);
 | ||||
| +void ip6_update_offload_stats(struct net_device *dev, void *ptr);
 | ||||
| +/* QCA NSS Clients Support - End */
 | ||||
| +
 | ||||
|  #endif /* __NET_IP_TUNNELS_H */ | ||||
| --- a/net/ipv6/ip6_tunnel.c
 | ||||
| +++ b/net/ipv6/ip6_tunnel.c
 | ||||
| @@ -2398,6 +2398,26 @@ nla_put_failure:
 | ||||
|  	return -EMSGSIZE; | ||||
|  } | ||||
|   | ||||
| +/* QCA NSS Client Support - Start */
 | ||||
| +/*
 | ||||
| + * Update offload stats
 | ||||
| + */
 | ||||
| +void ip6_update_offload_stats(struct net_device *dev, void *ptr)
 | ||||
| +{
 | ||||
| +	struct pcpu_sw_netstats *tstats = per_cpu_ptr(dev->tstats, 0);
 | ||||
| +	const struct pcpu_sw_netstats *offload_stats =
 | ||||
| +					(struct pcpu_sw_netstats *)ptr;
 | ||||
| +
 | ||||
| +	u64_stats_update_begin(&tstats->syncp);
 | ||||
| +	u64_stats_add(&tstats->tx_packets, u64_stats_read(&offload_stats->tx_packets));
 | ||||
| +	u64_stats_add(&tstats->tx_bytes, u64_stats_read(&offload_stats->tx_bytes));
 | ||||
| +	u64_stats_add(&tstats->rx_packets, u64_stats_read(&offload_stats->rx_packets));
 | ||||
| +	u64_stats_add(&tstats->rx_bytes, u64_stats_read(&offload_stats->rx_bytes));
 | ||||
| +	u64_stats_update_end(&tstats->syncp);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(ip6_update_offload_stats);
 | ||||
| +/* QCA NSS Client Support - End */
 | ||||
| +
 | ||||
|  struct net *ip6_tnl_get_link_net(const struct net_device *dev) | ||||
|  { | ||||
|  	struct ip6_tnl *tunnel = netdev_priv(dev); | ||||
| --- a/net/ipv6/sit.c
 | ||||
| +++ b/net/ipv6/sit.c
 | ||||
| @@ -1733,6 +1733,23 @@ nla_put_failure:
 | ||||
|  	return -EMSGSIZE; | ||||
|  } | ||||
|   | ||||
| +/* QCA NSS Clients Support - Start */
 | ||||
| +void ipip6_update_offload_stats(struct net_device *dev, void *ptr)
 | ||||
| +{
 | ||||
| +	struct pcpu_sw_netstats *tstats = per_cpu_ptr(dev->tstats, 0);
 | ||||
| +	const struct pcpu_sw_netstats *offload_stats =
 | ||||
| +					(struct pcpu_sw_netstats *)ptr;
 | ||||
| +
 | ||||
| +	u64_stats_update_begin(&tstats->syncp);
 | ||||
| +	u64_stats_add(&tstats->tx_packets, u64_stats_read(&offload_stats->tx_packets));
 | ||||
| +	u64_stats_add(&tstats->tx_bytes, u64_stats_read(&offload_stats->tx_bytes));
 | ||||
| +	u64_stats_add(&tstats->rx_packets, u64_stats_read(&offload_stats->rx_packets));
 | ||||
| +	u64_stats_add(&tstats->rx_bytes, u64_stats_read(&offload_stats->rx_bytes));
 | ||||
| +	u64_stats_update_end(&tstats->syncp);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(ipip6_update_offload_stats);
 | ||||
| +/* QCA NSS Clients Support - End */
 | ||||
| +
 | ||||
|  static const struct nla_policy ipip6_policy[IFLA_IPTUN_MAX + 1] = { | ||||
|  	[IFLA_IPTUN_LINK]		= { .type = NLA_U32 }, | ||||
|  	[IFLA_IPTUN_LOCAL]		= { .type = NLA_U32 }, | ||||
|  | @ -0,0 +1,103 @@ | |||
| --- a/drivers/net/vxlan/vxlan_core.c
 | ||||
| +++ b/drivers/net/vxlan/vxlan_core.c
 | ||||
| @@ -71,6 +71,20 @@ static inline bool vxlan_collect_metadat
 | ||||
|  	       ip_tunnel_collect_metadata(); | ||||
|  } | ||||
|   | ||||
| +ATOMIC_NOTIFIER_HEAD(vxlan_fdb_notifier_list);
 | ||||
| +
 | ||||
| +void vxlan_fdb_register_notify(struct notifier_block *nb)
 | ||||
| +{
 | ||||
| +	atomic_notifier_chain_register(&vxlan_fdb_notifier_list, nb);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(vxlan_fdb_register_notify);
 | ||||
| +
 | ||||
| +void vxlan_fdb_unregister_notify(struct notifier_block *nb)
 | ||||
| +{
 | ||||
| +	atomic_notifier_chain_unregister(&vxlan_fdb_notifier_list, nb);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(vxlan_fdb_unregister_notify);
 | ||||
| +
 | ||||
|  #if IS_ENABLED(CONFIG_IPV6) | ||||
|  static int vxlan_nla_get_addr(union vxlan_addr *ip, struct nlattr *nla) | ||||
|  { | ||||
| @@ -307,6 +321,7 @@ static void __vxlan_fdb_notify(struct vx
 | ||||
|  { | ||||
|  	struct net *net = dev_net(vxlan->dev); | ||||
|  	struct sk_buff *skb; | ||||
| +   struct vxlan_fdb_event vfe;
 | ||||
|  	int err = -ENOBUFS; | ||||
|   | ||||
|  	skb = nlmsg_new(vxlan_nlmsg_size(), GFP_ATOMIC); | ||||
| @@ -322,6 +337,10 @@ static void __vxlan_fdb_notify(struct vx
 | ||||
|  	} | ||||
|   | ||||
|  	rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC); | ||||
| +	vfe.dev = vxlan->dev;
 | ||||
| +	vfe.rdst = rd;
 | ||||
| +	ether_addr_copy(vfe.eth_addr, fdb->eth_addr);
 | ||||
| +	atomic_notifier_call_chain(&vxlan_fdb_notifier_list, type, (void *)&vfe);
 | ||||
|  	return; | ||||
|  errout: | ||||
|  	if (err < 0) | ||||
| @@ -488,6 +507,18 @@ static struct vxlan_fdb *vxlan_find_mac(
 | ||||
|  	return f; | ||||
|  } | ||||
|   | ||||
| +/* Find and update age of fdb entry corresponding to MAC. */
 | ||||
| +void vxlan_fdb_update_mac(struct vxlan_dev *vxlan, const u8 *mac, uint32_t vni)
 | ||||
| +{
 | ||||
| +	u32 hash_index;
 | ||||
| +
 | ||||
| +	hash_index = fdb_head_index(vxlan, mac, vni);
 | ||||
| +	spin_lock_bh(&vxlan->hash_lock[hash_index]);
 | ||||
| +	vxlan_find_mac(vxlan, mac, vni);
 | ||||
| +	spin_unlock_bh(&vxlan->hash_lock[hash_index]);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(vxlan_fdb_update_mac);
 | ||||
| +
 | ||||
|  /* caller should hold vxlan->hash_lock */ | ||||
|  static struct vxlan_rdst *vxlan_fdb_find_rdst(struct vxlan_fdb *f, | ||||
|  					      union vxlan_addr *ip, __be16 port, | ||||
| @@ -2658,6 +2689,9 @@ static void vxlan_xmit_one(struct sk_buf
 | ||||
|  			goto out_unlock; | ||||
|  		} | ||||
|   | ||||
| +		/* Reset the skb_iif to Tunnels interface index */
 | ||||
| +		skb->skb_iif = dev->ifindex;
 | ||||
| +
 | ||||
|  		tos = ip_tunnel_ecn_encap(tos, old_iph, skb); | ||||
|  		ttl = ttl ? : ip4_dst_hoplimit(&rt->dst); | ||||
|  		err = vxlan_build_skb(skb, ndst, sizeof(struct iphdr), | ||||
| @@ -2729,6 +2763,9 @@ static void vxlan_xmit_one(struct sk_buf
 | ||||
|  		if (err < 0) | ||||
|  			goto tx_error; | ||||
|   | ||||
| +		/* Reset the skb_iif to Tunnels interface index */
 | ||||
| +		skb->skb_iif = dev->ifindex;
 | ||||
| +
 | ||||
|  		udp_tunnel6_xmit_skb(ndst, sock6->sock->sk, skb, dev, | ||||
|  				     &local_ip.sin6.sin6_addr, | ||||
|  				     &dst->sin6.sin6_addr, tos, ttl, | ||||
| --- a/include/net/vxlan.h
 | ||||
| +++ b/include/net/vxlan.h
 | ||||
| @@ -344,6 +344,19 @@ struct vxlan_dev {
 | ||||
|  					 VXLAN_F_COLLECT_METADATA  |	\ | ||||
|  					 VXLAN_F_VNIFILTER) | ||||
|   | ||||
| +/*
 | ||||
| + * Application data for fdb notifier event
 | ||||
| + */
 | ||||
| +struct vxlan_fdb_event {
 | ||||
| +	struct net_device *dev;
 | ||||
| +	struct vxlan_rdst *rdst;
 | ||||
| +	u8 eth_addr[ETH_ALEN];
 | ||||
| +};
 | ||||
| +
 | ||||
| +extern void vxlan_fdb_register_notify(struct notifier_block *nb);
 | ||||
| +extern void vxlan_fdb_unregister_notify(struct notifier_block *nb);
 | ||||
| +extern void vxlan_fdb_update_mac(struct vxlan_dev *vxlan, const u8 *mac, uint32_t vni);
 | ||||
| +
 | ||||
|  struct net_device *vxlan_dev_create(struct net *net, const char *name, | ||||
|  				    u8 name_assign_type, struct vxlan_config *conf); | ||||
|   | ||||
|  | @ -0,0 +1,368 @@ | |||
| --- a/include/linux/ppp_channel.h
 | ||||
| +++ b/include/linux/ppp_channel.h
 | ||||
| @@ -61,6 +61,51 @@ struct ppp_channel {
 | ||||
|  }; | ||||
|   | ||||
|  #ifdef __KERNEL__ | ||||
| +/* Call this to obtain the underlying protocol of the PPP channel,
 | ||||
| + * e.g. PX_PROTO_OE
 | ||||
| + */
 | ||||
| +extern int ppp_channel_get_protocol(struct ppp_channel *);
 | ||||
| +
 | ||||
| +/* Call this to hold a channel */
 | ||||
| +extern bool ppp_channel_hold(struct ppp_channel *);
 | ||||
| +
 | ||||
| +/* Call this to release a hold you have upon a channel */
 | ||||
| +extern void ppp_channel_release(struct ppp_channel *);
 | ||||
| +
 | ||||
| +/* Release hold on PPP channels */
 | ||||
| +extern void ppp_release_channels(struct ppp_channel *channels[],
 | ||||
| +                                 unsigned int chan_sz);
 | ||||
| +
 | ||||
| +/* Test if ppp xmit lock is locked */
 | ||||
| +extern bool ppp_is_xmit_locked(struct net_device *dev);
 | ||||
| +
 | ||||
| +/* Call this get protocol version */
 | ||||
| +extern int ppp_channel_get_proto_version(struct ppp_channel *);
 | ||||
| +
 | ||||
| +/* Get the device index  associated with a channel, or 0, if none */
 | ||||
| +extern int ppp_dev_index(struct ppp_channel *);
 | ||||
| +
 | ||||
| +/* Hold PPP channels for the PPP device */
 | ||||
| +extern int ppp_hold_channels(struct net_device *dev,
 | ||||
| +                             struct ppp_channel *channels[],
 | ||||
| +                             unsigned int chan_sz);
 | ||||
| +extern int __ppp_hold_channels(struct net_device *dev,
 | ||||
| + 				struct ppp_channel *channels[],
 | ||||
| + 				unsigned int chan_sz);
 | ||||
| +
 | ||||
| +/* Test if the ppp device is a multi-link ppp device */
 | ||||
| +extern int ppp_is_multilink(struct net_device *dev);
 | ||||
| +extern int __ppp_is_multilink(struct net_device *dev);
 | ||||
| +
 | ||||
| +/* Update statistics of the PPP net_device by incrementing related
 | ||||
| + * statistics field value with corresponding parameter
 | ||||
| + */
 | ||||
| +extern void ppp_update_stats(struct net_device *dev, unsigned long rx_packets,
 | ||||
| +                             unsigned long rx_bytes, unsigned long tx_packets,
 | ||||
| +                             unsigned long tx_bytes, unsigned long rx_errors,
 | ||||
| +                             unsigned long tx_errors, unsigned long rx_dropped,
 | ||||
| +                             unsigned long tx_dropped);
 | ||||
| +
 | ||||
|  /* Called by the channel when it can send some more data. */ | ||||
|  extern void ppp_output_wakeup(struct ppp_channel *); | ||||
|   | ||||
| @@ -148,5 +193,17 @@ extern void ppp_update_stats(struct net_
 | ||||
|   * that ppp_unregister_channel returns. | ||||
|   */ | ||||
|   | ||||
| +/* QCA NSS Clients Support - Start */
 | ||||
| +/* PPP channel connection event types */
 | ||||
| +#define PPP_CHANNEL_DISCONNECT  0
 | ||||
| +#define PPP_CHANNEL_CONNECT     1
 | ||||
| +
 | ||||
| +/* Register the PPP channel connect notifier */
 | ||||
| +extern void ppp_channel_connection_register_notify(struct notifier_block *nb);
 | ||||
| +
 | ||||
| +/* Unregister the PPP channel connect notifier */
 | ||||
| +extern void ppp_channel_connection_unregister_notify(struct notifier_block *nb);
 | ||||
| +/* QCA NSS Clients Support - End */
 | ||||
| +
 | ||||
|  #endif /* __KERNEL__ */ | ||||
|  #endif | ||||
| --- a/include/linux/if_pppol2tp.h
 | ||||
| +++ b/include/linux/if_pppol2tp.h
 | ||||
| @@ -12,4 +12,30 @@
 | ||||
|  #include <linux/in6.h> | ||||
|  #include <uapi/linux/if_pppol2tp.h> | ||||
|   | ||||
| +/* QCA NSS ECM support - Start */
 | ||||
| +/*
 | ||||
| + * Holds L2TP channel info
 | ||||
| + */
 | ||||
| +struct  pppol2tp_common_addr {
 | ||||
| +	int tunnel_version;				/* v2 or v3 */
 | ||||
| +	__u32 local_tunnel_id, remote_tunnel_id;	/* tunnel id */
 | ||||
| +	__u32 local_session_id, remote_session_id;	/* session id */
 | ||||
| +	struct sockaddr_in local_addr, remote_addr; /* ip address and port */
 | ||||
| +};
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * L2TP channel operations
 | ||||
| + */
 | ||||
| +struct pppol2tp_channel_ops {
 | ||||
| +	struct ppp_channel_ops ops; /* ppp channel ops */
 | ||||
| +};
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * exported function which calls pppol2tp channel's get addressing
 | ||||
| + * function
 | ||||
| + */
 | ||||
| +extern int pppol2tp_channel_addressing_get(struct ppp_channel *,
 | ||||
| +					   struct pppol2tp_common_addr *);
 | ||||
| +/* QCA NSS ECM support - End */
 | ||||
| +
 | ||||
|  #endif | ||||
| --- a/net/l2tp/l2tp_ppp.c
 | ||||
| +++ b/net/l2tp/l2tp_ppp.c
 | ||||
| @@ -123,9 +123,17 @@ struct pppol2tp_session {
 | ||||
|  }; | ||||
|   | ||||
|  static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb); | ||||
| -
 | ||||
| -static const struct ppp_channel_ops pppol2tp_chan_ops = {
 | ||||
| -	.start_xmit =  pppol2tp_xmit,
 | ||||
| +static int pppol2tp_get_channel_protocol(struct ppp_channel *);
 | ||||
| +static int pppol2tp_get_channel_protocol_ver(struct ppp_channel *);
 | ||||
| +static void pppol2tp_hold_chan(struct ppp_channel *);
 | ||||
| +static void pppol2tp_release_chan(struct ppp_channel *);
 | ||||
| +
 | ||||
| +static const struct pppol2tp_channel_ops pppol2tp_chan_ops = {
 | ||||
| +	.ops.start_xmit =  pppol2tp_xmit,
 | ||||
| +	.ops.get_channel_protocol = pppol2tp_get_channel_protocol,
 | ||||
| +	.ops.get_channel_protocol_ver = pppol2tp_get_channel_protocol_ver,
 | ||||
| +	.ops.hold = pppol2tp_hold_chan,
 | ||||
| +	.ops.release = pppol2tp_release_chan,
 | ||||
|  }; | ||||
|   | ||||
|  static const struct proto_ops pppol2tp_ops; | ||||
| @@ -373,6 +381,13 @@ static int pppol2tp_xmit(struct ppp_chan
 | ||||
|  	skb->data[0] = PPP_ALLSTATIONS; | ||||
|  	skb->data[1] = PPP_UI; | ||||
|   | ||||
| +	/* QCA NSS ECM support - start */
 | ||||
| +	/* set incoming interface as the ppp interface */
 | ||||
| +	if ((skb->protocol == htons(ETH_P_IP)) ||
 | ||||
| +	    (skb->protocol == htons(ETH_P_IPV6)))
 | ||||
| +		skb->skb_iif = ppp_dev_index(chan);
 | ||||
| +	/* QCA NSS ECM support - End */
 | ||||
| +
 | ||||
|  	local_bh_disable(); | ||||
|  	l2tp_xmit_skb(session, skb); | ||||
|  	local_bh_enable(); | ||||
| @@ -818,7 +833,7 @@ static int pppol2tp_connect(struct socke
 | ||||
|  	po->chan.hdrlen = PPPOL2TP_L2TP_HDR_SIZE_NOSEQ; | ||||
|   | ||||
|  	po->chan.private = sk; | ||||
| -	po->chan.ops	 = &pppol2tp_chan_ops;
 | ||||
| +	po->chan.ops	 = (struct ppp_channel_ops *)&pppol2tp_chan_ops.ops;
 | ||||
|  	po->chan.mtu	 = pppol2tp_tunnel_mtu(tunnel); | ||||
|   | ||||
|  	error = ppp_register_net_channel(sock_net(sk), &po->chan); | ||||
| @@ -1732,6 +1747,109 @@ static void __exit pppol2tp_exit(void)
 | ||||
|  	unregister_pernet_device(&pppol2tp_net_ops); | ||||
|  } | ||||
|   | ||||
| +/* QCA NSS ECM support - Start */
 | ||||
| +/* pppol2tp_hold_chan() */
 | ||||
| +static void pppol2tp_hold_chan(struct ppp_channel *chan)
 | ||||
| +{
 | ||||
| +	struct sock *sk = (struct sock *)chan->private;
 | ||||
| +
 | ||||
| +	sock_hold(sk);
 | ||||
| +}
 | ||||
| +
 | ||||
| +/* pppol2tp_release_chan() */
 | ||||
| +static void pppol2tp_release_chan(struct ppp_channel *chan)
 | ||||
| +{
 | ||||
| +	struct sock *sk = (struct sock *)chan->private;
 | ||||
| +
 | ||||
| +	sock_put(sk);
 | ||||
| +}
 | ||||
| +
 | ||||
| +/* pppol2tp_get_channel_protocol()
 | ||||
| + * Return the protocol type of the L2TP over PPP protocol
 | ||||
| + */
 | ||||
| +static int pppol2tp_get_channel_protocol(struct ppp_channel *chan)
 | ||||
| +{
 | ||||
| +	return PX_PROTO_OL2TP;
 | ||||
| +}
 | ||||
| +
 | ||||
| +/* pppol2tp_get_channel_protocol_ver()
 | ||||
| + * Return the protocol version of the L2TP over PPP protocol
 | ||||
| + */
 | ||||
| +static int pppol2tp_get_channel_protocol_ver(struct ppp_channel *chan)
 | ||||
| +{
 | ||||
| +	struct sock *sk;
 | ||||
| +	struct l2tp_session *session;
 | ||||
| +	struct l2tp_tunnel *tunnel;
 | ||||
| +	int version = 0;
 | ||||
| +
 | ||||
| +	if (chan && chan->private)
 | ||||
| +		sk = (struct sock *)chan->private;
 | ||||
| +	else
 | ||||
| +		return -1;
 | ||||
| +
 | ||||
| +	/* Get session and tunnel contexts from the socket */
 | ||||
| +	session = pppol2tp_sock_to_session(sk);
 | ||||
| +	if (!session)
 | ||||
| +		return -1;
 | ||||
| +
 | ||||
| +	tunnel = session->tunnel;
 | ||||
| +	if (!tunnel) {
 | ||||
| +		sock_put(sk);
 | ||||
| +		return -1;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	version = tunnel->version;
 | ||||
| +
 | ||||
| +	sock_put(sk);
 | ||||
| +
 | ||||
| +	return version;
 | ||||
| +}
 | ||||
| +
 | ||||
| +/* pppol2tp_get_addressing() */
 | ||||
| +static int pppol2tp_get_addressing(struct ppp_channel *chan,
 | ||||
| +				   struct pppol2tp_common_addr *addr)
 | ||||
| +{
 | ||||
| +	struct sock *sk = (struct sock *)chan->private;
 | ||||
| +	struct l2tp_session *session;
 | ||||
| +	struct l2tp_tunnel *tunnel;
 | ||||
| +	struct inet_sock *isk = NULL;
 | ||||
| +	int err = -ENXIO;
 | ||||
| +
 | ||||
| +	/* Get session and tunnel contexts from the socket */
 | ||||
| +	session = pppol2tp_sock_to_session(sk);
 | ||||
| +	if (!session)
 | ||||
| +		return err;
 | ||||
| +
 | ||||
| +	tunnel = session->tunnel;
 | ||||
| +	if (!tunnel) {
 | ||||
| +		sock_put(sk);
 | ||||
| +		return err;
 | ||||
| +	}
 | ||||
| +	isk = inet_sk(tunnel->sock);
 | ||||
| +
 | ||||
| +	addr->local_tunnel_id = tunnel->tunnel_id;
 | ||||
| +	addr->remote_tunnel_id = tunnel->peer_tunnel_id;
 | ||||
| +	addr->local_session_id = session->session_id;
 | ||||
| +	addr->remote_session_id = session->peer_session_id;
 | ||||
| +
 | ||||
| +	addr->local_addr.sin_port = isk->inet_sport;
 | ||||
| +	addr->remote_addr.sin_port = isk->inet_dport;
 | ||||
| +	addr->local_addr.sin_addr.s_addr = isk->inet_saddr;
 | ||||
| +	addr->remote_addr.sin_addr.s_addr = isk->inet_daddr;
 | ||||
| +
 | ||||
| +	sock_put(sk);
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
| +/* pppol2tp_channel_addressing_get() */
 | ||||
| +int pppol2tp_channel_addressing_get(struct ppp_channel *chan,
 | ||||
| +				    struct pppol2tp_common_addr *addr)
 | ||||
| +{
 | ||||
| +	return pppol2tp_get_addressing(chan, addr);
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(pppol2tp_channel_addressing_get);
 | ||||
| +/* QCA NSS ECM support - End */
 | ||||
| +
 | ||||
|  module_init(pppol2tp_init); | ||||
|  module_exit(pppol2tp_exit); | ||||
|   | ||||
| --- a/drivers/net/ppp/ppp_generic.c
 | ||||
| +++ b/drivers/net/ppp/ppp_generic.c
 | ||||
| @@ -3743,6 +3743,32 @@ int ppp_is_multilink(struct net_device *
 | ||||
|  } | ||||
|  EXPORT_SYMBOL(ppp_is_multilink); | ||||
|   | ||||
| +/* __ppp_is_multilink()
 | ||||
| + *      Returns >0 if the device is a multilink PPP netdevice, 0 if not or < 0
 | ||||
| + *      if the device is not PPP. Caller should acquire ppp_lock before calling
 | ||||
| + *      this function
 | ||||
| + */
 | ||||
| +int __ppp_is_multilink(struct net_device *dev)
 | ||||
| +{
 | ||||
| +	struct ppp *ppp;
 | ||||
| +	unsigned int flags;
 | ||||
| +
 | ||||
| +	if (!dev)
 | ||||
| +		return -1;
 | ||||
| +
 | ||||
| +	if (dev->type != ARPHRD_PPP)
 | ||||
| +		return -1;
 | ||||
| +
 | ||||
| +	ppp = netdev_priv(dev);
 | ||||
| +	flags = ppp->flags;
 | ||||
| +
 | ||||
| +	if (flags & SC_MULTILINK)
 | ||||
| +		return 1;
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(__ppp_is_multilink);
 | ||||
| +
 | ||||
|  /* ppp_channel_get_protocol() | ||||
|   *	Call this to obtain the underlying protocol of the PPP channel, | ||||
|   *	e.g. PX_PROTO_OE | ||||
| @@ -3881,6 +3907,59 @@ int ppp_hold_channels(struct net_device
 | ||||
|  } | ||||
|  EXPORT_SYMBOL(ppp_hold_channels); | ||||
|   | ||||
| +/* __ppp_hold_channels()
 | ||||
| + *	Returns the PPP channels of the PPP device, storing each one into
 | ||||
| + *	channels[].
 | ||||
| + *
 | ||||
| + * channels[] has chan_sz elements.
 | ||||
| + * This function returns the number of channels stored, up to chan_sz.
 | ||||
| + * It will return < 0 if the device is not PPP.
 | ||||
| + *
 | ||||
| + * You MUST release the channels using ppp_release_channels().
 | ||||
| + */
 | ||||
| +int __ppp_hold_channels(struct net_device *dev, struct ppp_channel *channels[],
 | ||||
| +		      unsigned int chan_sz)
 | ||||
| +{
 | ||||
| +	struct ppp *ppp;
 | ||||
| +	int c;
 | ||||
| +	struct channel *pch;
 | ||||
| +
 | ||||
| +	if (!dev)
 | ||||
| +		return -1;
 | ||||
| +
 | ||||
| +	if (dev->type != ARPHRD_PPP)
 | ||||
| +		return -1;
 | ||||
| +
 | ||||
| +	ppp = netdev_priv(dev);
 | ||||
| +
 | ||||
| +	c = 0;
 | ||||
| +	list_for_each_entry(pch, &ppp->channels, clist) {
 | ||||
| +		struct ppp_channel *chan;
 | ||||
| +
 | ||||
| +		if (!pch->chan) {
 | ||||
| +			/* Channel is going / gone away */
 | ||||
| +			continue;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		if (c == chan_sz) {
 | ||||
| +			/* No space to record channel */
 | ||||
| +			return c;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		/* Hold the channel, if supported */
 | ||||
| +		chan = pch->chan;
 | ||||
| +		if (!chan->ops->hold)
 | ||||
| +			continue;
 | ||||
| +
 | ||||
| +		chan->ops->hold(chan);
 | ||||
| +
 | ||||
| +		 /* Record the channel */
 | ||||
| +		channels[c++] = chan;
 | ||||
| +	}
 | ||||
| +	return c;
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(__ppp_hold_channels);
 | ||||
| +
 | ||||
|  /* ppp_release_channels() | ||||
|   *	Releases channels | ||||
|   */ | ||||
| --- a/net/l2tp/l2tp_core.h
 | ||||
| +++ b/net/l2tp/l2tp_core.h
 | ||||
| @@ -235,6 +235,9 @@ struct l2tp_session *l2tp_session_get_by
 | ||||
|  void l2tp_stats_update(struct l2tp_tunnel *tunnel, struct l2tp_session *session, | ||||
|                         struct l2tp_stats *stats); | ||||
|   | ||||
| +void l2tp_stats_update(struct l2tp_tunnel *tunnel, struct l2tp_session *session,
 | ||||
| +                       struct l2tp_stats *stats);
 | ||||
| +
 | ||||
|  /* Tunnel and session lifetime management. | ||||
|   * Creation of a new instance is a two-step process: create, then register. | ||||
|   * Destruction is triggered using the *_delete functions, and completes asynchronously. | ||||
|  | @ -0,0 +1,22 @@ | |||
| --- a/net/ipv6/ip6_tunnel.c
 | ||||
| +++ b/net/ipv6/ip6_tunnel.c
 | ||||
| @@ -2404,7 +2404,7 @@ nla_put_failure:
 | ||||
|   */ | ||||
|  void ip6_update_offload_stats(struct net_device *dev, void *ptr) | ||||
|  { | ||||
| -	struct pcpu_sw_netstats *tstats = per_cpu_ptr(dev->tstats, 0);
 | ||||
| +	struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);
 | ||||
|  	const struct pcpu_sw_netstats *offload_stats = | ||||
|  					(struct pcpu_sw_netstats *)ptr; | ||||
|   | ||||
| --- a/net/ipv6/sit.c
 | ||||
| +++ b/net/ipv6/sit.c
 | ||||
| @@ -1736,7 +1736,7 @@ nla_put_failure:
 | ||||
|  /* QCA NSS Clients Support - Start */ | ||||
|  void ipip6_update_offload_stats(struct net_device *dev, void *ptr) | ||||
|  { | ||||
| -	struct pcpu_sw_netstats *tstats = per_cpu_ptr(dev->tstats, 0);
 | ||||
| +	struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);
 | ||||
|  	const struct pcpu_sw_netstats *offload_stats = | ||||
|  					(struct pcpu_sw_netstats *)ptr; | ||||
|   | ||||
|  | @ -0,0 +1,24 @@ | |||
| --- /dev/null
 | ||||
| +++ b/include/uapi/linux/tlshdr.h
 | ||||
| @@ -0,0 +1,21 @@
 | ||||
| +#ifndef _UAPI_LINUX_TLSHDR_H
 | ||||
| +#define _UAPI_LINUX_TLSHDR_H
 | ||||
| +
 | ||||
| +#include <linux/types.h>
 | ||||
| +
 | ||||
| +struct tlshdr {
 | ||||
| +	__u8 type;
 | ||||
| +	__be16 version;
 | ||||
| +	__be16 len;
 | ||||
| +} __attribute__((packed));
 | ||||
| +
 | ||||
| +#define TLSHDR_REC_TYPE_CCS 20		/* TLS packet is change cipher specification */
 | ||||
| +#define TLSHDR_REC_TYPE_ALERT 21	/* TLS packet is Alert */
 | ||||
| +#define TLSHDR_REC_TYPE_HANDSHAKE 22	/* TLS packet is Handshake */
 | ||||
| +#define TLSHDR_REC_TYPE_DATA 23		/* TLS packet is Application data */
 | ||||
| +
 | ||||
| +#define TLSHDR_VERSION_1_1 0x0302 /* TLS Header Version(tls 1.1) */
 | ||||
| +#define TLSHDR_VERSION_1_2 0x0303 /* TLS Header Version(tls 1.2) */
 | ||||
| +#define TLSHDR_VERSION_1_3 0x0304 /* TLS Header Version(tls 1.3) */
 | ||||
| +
 | ||||
| +#endif /* _UAPI_LINUX_TLSHDR_H */
 | ||||
|  | @ -0,0 +1,876 @@ | |||
| --- a/include/linux/if_bridge.h
 | ||||
| +++ b/include/linux/if_bridge.h
 | ||||
| @@ -281,4 +281,17 @@ extern br_get_dst_hook_t __rcu *br_get_d
 | ||||
|  extern struct net_device *br_fdb_bridge_dev_get_and_hold(struct net_bridge *br); | ||||
|  /* QCA NSS bridge-mgr support - End */ | ||||
|   | ||||
| +/* QCA qca-mcs support - Start */
 | ||||
| +typedef struct net_bridge_port *br_get_dst_hook_t(const struct net_bridge_port *src,
 | ||||
| +		struct sk_buff **skb);
 | ||||
| +extern br_get_dst_hook_t __rcu *br_get_dst_hook;
 | ||||
| +
 | ||||
| +typedef int (br_multicast_handle_hook_t)(const struct net_bridge_port *src,
 | ||||
| +		struct sk_buff *skb);
 | ||||
| +extern br_multicast_handle_hook_t __rcu *br_multicast_handle_hook;
 | ||||
| +
 | ||||
| +typedef void (br_notify_hook_t)(int group, int event, const void *ptr);
 | ||||
| +extern br_notify_hook_t __rcu *br_notify_hook;
 | ||||
| +/* QCA qca-mcs support - End */
 | ||||
| +
 | ||||
|  #endif | ||||
| --- a/net/bridge/br_fdb.c
 | ||||
| +++ b/net/bridge/br_fdb.c
 | ||||
| @@ -232,6 +232,8 @@ static void fdb_notify(struct net_bridge
 | ||||
|  		kfree_skb(skb); | ||||
|  		goto errout; | ||||
|  	} | ||||
| +
 | ||||
| +	__br_notify(RTNLGRP_NEIGH, type, fdb); /* QCA qca-mcs support */
 | ||||
|  	rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC); | ||||
|  	return; | ||||
|  errout: | ||||
| @@ -298,6 +300,7 @@ struct net_bridge_fdb_entry *br_fdb_find
 | ||||
|  { | ||||
|  	return fdb_find_rcu(&br->fdb_hash_tbl, addr, vid); | ||||
|  } | ||||
| +EXPORT_SYMBOL_GPL(br_fdb_find_rcu); /* QCA qca-mcs support */
 | ||||
|   | ||||
|  /* When a static FDB entry is added, the mac address from the entry is | ||||
|   * added to the bridge private HW address list and all required ports | ||||
| --- a/net/bridge/br_private.h
 | ||||
| +++ b/net/bridge/br_private.h
 | ||||
| @@ -853,6 +853,7 @@ void br_manage_promisc(struct net_bridge
 | ||||
|  int nbp_backup_change(struct net_bridge_port *p, struct net_device *backup_dev); | ||||
|   | ||||
|  /* br_input.c */ | ||||
| +int br_pass_frame_up(struct sk_buff *skb); /* QCA qca-mcs support */
 | ||||
|  int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb); | ||||
|  rx_handler_func_t *br_get_rx_handler(const struct net_device *dev); | ||||
|   | ||||
| @@ -2178,4 +2179,14 @@ struct nd_msg *br_is_nd_neigh_msg(struct
 | ||||
|  #define __br_get(__hook, __default, __args ...) \ | ||||
|  		(__hook ? (__hook(__args)) : (__default)) | ||||
|  /* QCA NSS ECM support - End */ | ||||
| +
 | ||||
| +/* QCA qca-mcs support - Start */
 | ||||
| +static inline void __br_notify(int group, int type, const void *data)
 | ||||
| +{
 | ||||
| +	br_notify_hook_t *notify_hook = rcu_dereference(br_notify_hook);
 | ||||
| +
 | ||||
| +	if (notify_hook)
 | ||||
| +		notify_hook(group, type, data);
 | ||||
| +}
 | ||||
| +/* QCA qca-mcs support - End */
 | ||||
|  #endif | ||||
| --- a/net/bridge/br_netlink.c
 | ||||
| +++ b/net/bridge/br_netlink.c
 | ||||
| @@ -640,6 +640,7 @@ void br_info_notify(int event, const str
 | ||||
|  		kfree_skb(skb); | ||||
|  		goto errout; | ||||
|  	} | ||||
| +	__br_notify(RTNLGRP_LINK, event, port); /* QCA qca-mcs support */
 | ||||
|  	rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); | ||||
|  	return; | ||||
|  errout: | ||||
| --- a/net/bridge/br.c
 | ||||
| +++ b/net/bridge/br.c
 | ||||
| @@ -467,6 +467,12 @@ static void __exit br_deinit(void)
 | ||||
|  	br_fdb_fini(); | ||||
|  } | ||||
|   | ||||
| +/* QCA qca-mcs support - Start */
 | ||||
| +/* Hook for bridge event notifications */
 | ||||
| +br_notify_hook_t __rcu *br_notify_hook __read_mostly;
 | ||||
| +EXPORT_SYMBOL_GPL(br_notify_hook);
 | ||||
| +/* QCA qca-mcs support - End */
 | ||||
| +
 | ||||
|  module_init(br_init) | ||||
|  module_exit(br_deinit) | ||||
|  MODULE_LICENSE("GPL"); | ||||
| --- a/net/bridge/br_device.c
 | ||||
| +++ b/net/bridge/br_device.c
 | ||||
| @@ -82,6 +82,13 @@ netdev_tx_t br_dev_xmit(struct sk_buff *
 | ||||
|  	if (is_broadcast_ether_addr(dest)) { | ||||
|  		br_flood(br, skb, BR_PKT_BROADCAST, false, true); | ||||
|  	} else if (is_multicast_ether_addr(dest)) { | ||||
| +		/* QCA qca-mcs support - Start */
 | ||||
| +		br_multicast_handle_hook_t *multicast_handle_hook =
 | ||||
| +		  rcu_dereference(br_multicast_handle_hook);
 | ||||
| +		if (!__br_get(multicast_handle_hook, true, NULL, skb))
 | ||||
| +			goto out;
 | ||||
| +		/* QCA qca-mcs support - End */
 | ||||
| +
 | ||||
|  		if (unlikely(netpoll_tx_running(dev))) { | ||||
|  			br_flood(br, skb, BR_PKT_MULTICAST, false, true); | ||||
|  			goto out; | ||||
| --- a/net/bridge/br_input.c
 | ||||
| +++ b/net/bridge/br_input.c
 | ||||
| @@ -30,7 +30,17 @@ br_netif_receive_skb(struct net *net, st
 | ||||
|  	return netif_receive_skb(skb); | ||||
|  } | ||||
|   | ||||
| -static int br_pass_frame_up(struct sk_buff *skb)
 | ||||
| +/* QCA qca-mcs support - Start */
 | ||||
| +/* Hook for external Multicast handler */
 | ||||
| +br_multicast_handle_hook_t __rcu *br_multicast_handle_hook __read_mostly;
 | ||||
| +EXPORT_SYMBOL_GPL(br_multicast_handle_hook);
 | ||||
| +
 | ||||
| +/* Hook for external forwarding logic */
 | ||||
| +br_get_dst_hook_t __rcu *br_get_dst_hook __read_mostly;
 | ||||
| +EXPORT_SYMBOL_GPL(br_get_dst_hook);
 | ||||
| +/* QCA qca-mcs support - End */
 | ||||
| +
 | ||||
| +int br_pass_frame_up(struct sk_buff *skb)
 | ||||
|  { | ||||
|  	struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev; | ||||
|  	struct net_bridge *br = netdev_priv(brdev); | ||||
| @@ -69,6 +79,7 @@ static int br_pass_frame_up(struct sk_bu
 | ||||
|  		       dev_net(indev), NULL, skb, indev, NULL, | ||||
|  		       br_netif_receive_skb); | ||||
|  } | ||||
| +EXPORT_SYMBOL_GPL(br_pass_frame_up); /* QCA qca-mcs support */
 | ||||
|   | ||||
|  /* note: already called with rcu_read_lock */ | ||||
|  int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb) | ||||
| @@ -82,6 +93,11 @@ int br_handle_frame_finish(struct net *n
 | ||||
|  	struct net_bridge_mcast *brmctx; | ||||
|  	struct net_bridge_vlan *vlan; | ||||
|  	struct net_bridge *br; | ||||
| +	/* QCA qca-mcs support - Start */
 | ||||
| +	br_multicast_handle_hook_t *multicast_handle_hook;
 | ||||
| +	struct net_bridge_port *pdst = NULL;
 | ||||
| +	br_get_dst_hook_t *get_dst_hook = rcu_dereference(br_get_dst_hook);
 | ||||
| +	/* QCA qca-mcs support - End */
 | ||||
|  	u16 vid = 0; | ||||
|  	u8 state; | ||||
|   | ||||
| @@ -158,6 +174,12 @@ int br_handle_frame_finish(struct net *n
 | ||||
|   | ||||
|  	switch (pkt_type) { | ||||
|  	case BR_PKT_MULTICAST: | ||||
| +		/* QCA qca-mcs support - Start */
 | ||||
| +		multicast_handle_hook = rcu_dereference(br_multicast_handle_hook);
 | ||||
| +		if (!__br_get(multicast_handle_hook, true, p, skb))
 | ||||
| +			goto out;
 | ||||
| +		/* QCA qca-mcs support - End */
 | ||||
| +
 | ||||
|  		mdst = br_mdb_get(brmctx, skb, vid); | ||||
|  		if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) && | ||||
|  		    br_multicast_querier_exists(brmctx, eth_hdr(skb), mdst)) { | ||||
| @@ -173,8 +195,15 @@ int br_handle_frame_finish(struct net *n
 | ||||
|  		} | ||||
|  		break; | ||||
|  	case BR_PKT_UNICAST: | ||||
| -		dst = br_fdb_find_rcu(br, eth_hdr(skb)->h_dest, vid);
 | ||||
| -		break;
 | ||||
| +		/* QCA qca-mcs support - Start */
 | ||||
| +		pdst = __br_get(get_dst_hook, NULL, p, &skb);
 | ||||
| +		if (pdst) {
 | ||||
| +			if (!skb)
 | ||||
| +				goto out;
 | ||||
| +		} else {
 | ||||
| +		/* QCA qca-mcs support - End */
 | ||||
| +			dst = br_fdb_find_rcu(br, eth_hdr(skb)->h_dest, vid);
 | ||||
| +		}
 | ||||
|  	default: | ||||
|  		break; | ||||
|  	} | ||||
| @@ -189,6 +218,13 @@ int br_handle_frame_finish(struct net *n
 | ||||
|  			dst->used = now; | ||||
|  		br_forward(dst->dst, skb, local_rcv, false); | ||||
|  	} else { | ||||
| +	  /* QCA qca-mcs support - Start */
 | ||||
| +		if (pdst) {
 | ||||
| +			br_forward(pdst, skb, local_rcv, false);
 | ||||
| +			goto out;
 | ||||
| +		}
 | ||||
| +		/* QCA qca-mcs support - End */
 | ||||
| +
 | ||||
|  		if (!mcast_hit) | ||||
|  			br_flood(br, skb, pkt_type, local_rcv, false); | ||||
|  		else | ||||
| --- a/include/linux/mroute.h
 | ||||
| +++ b/include/linux/mroute.h
 | ||||
| @@ -85,4 +85,44 @@ struct rtmsg;
 | ||||
|  int ipmr_get_route(struct net *net, struct sk_buff *skb, | ||||
|  		   __be32 saddr, __be32 daddr, | ||||
|  		   struct rtmsg *rtm, u32 portid); | ||||
| +
 | ||||
| +/* QCA ECM qca-mcs support - Start */
 | ||||
| +#define IPMR_MFC_EVENT_UPDATE   1
 | ||||
| +#define IPMR_MFC_EVENT_DELETE   2
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * Callback to registered modules in the event of updates to a multicast group
 | ||||
| + */
 | ||||
| +typedef void (*ipmr_mfc_event_offload_callback_t)(__be32 origin, __be32 group,
 | ||||
| +						  u32 max_dest_dev,
 | ||||
| +						  u32 dest_dev_idx[],
 | ||||
| +						  u8 op);
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * Register the callback used to inform offload modules when updates occur to
 | ||||
| + * MFC. The callback is registered by offload modules
 | ||||
| + */
 | ||||
| +extern bool ipmr_register_mfc_event_offload_callback(
 | ||||
| +			ipmr_mfc_event_offload_callback_t mfc_offload_cb);
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * De-Register the callback used to inform offload modules when updates occur
 | ||||
| + * to MFC
 | ||||
| + */
 | ||||
| +extern void ipmr_unregister_mfc_event_offload_callback(void);
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * Find the destination interface list, given a multicast group and source
 | ||||
| + */
 | ||||
| +extern int ipmr_find_mfc_entry(struct net *net, __be32 origin, __be32 group,
 | ||||
| +				 u32 max_dst_cnt, u32 dest_dev[]);
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * Out-of-band multicast statistics update for flows that are offloaded from
 | ||||
| + * Linux
 | ||||
| + */
 | ||||
| +extern int ipmr_mfc_stats_update(struct net *net, __be32 origin, __be32 group,
 | ||||
| +				 u64 pkts_in, u64 bytes_in,
 | ||||
| +				 u64 pkts_out, u64 bytes_out);
 | ||||
| +/* QCA ECM qca-mcs support - End */
 | ||||
|  #endif | ||||
| --- a/include/linux/mroute6.h
 | ||||
| +++ b/include/linux/mroute6.h
 | ||||
| @@ -110,4 +110,47 @@ static inline int ip6mr_sk_done(struct s
 | ||||
|  	return 0; | ||||
|  } | ||||
|  #endif | ||||
| +
 | ||||
| +/* QCA qca-mcs support - Start */
 | ||||
| +#define IP6MR_MFC_EVENT_UPDATE   1
 | ||||
| +#define IP6MR_MFC_EVENT_DELETE   2
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * Callback to registered modules in the event of updates to a multicast group
 | ||||
| + */
 | ||||
| +typedef void (*ip6mr_mfc_event_offload_callback_t)(struct in6_addr *origin,
 | ||||
| +						   struct in6_addr *group,
 | ||||
| +						   u32 max_dest_dev,
 | ||||
| +						   u32 dest_dev_idx[],
 | ||||
| +						   uint8_t op);
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * Register the callback used to inform offload modules when updates occur
 | ||||
| + * to MFC. The callback is registered by offload modules
 | ||||
| + */
 | ||||
| +extern bool ip6mr_register_mfc_event_offload_callback(
 | ||||
| +			ip6mr_mfc_event_offload_callback_t mfc_offload_cb);
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * De-Register the callback used to inform offload modules when updates occur
 | ||||
| + * to MFC
 | ||||
| + */
 | ||||
| +extern void ip6mr_unregister_mfc_event_offload_callback(void);
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * Find the destination interface list given a multicast group and source
 | ||||
| + */
 | ||||
| +extern int ip6mr_find_mfc_entry(struct net *net, struct in6_addr *origin,
 | ||||
| +				struct in6_addr *group, u32 max_dst_cnt,
 | ||||
| +				u32 dest_dev[]);
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * Out-of-band multicast statistics update for flows that are offloaded from
 | ||||
| + * Linux
 | ||||
| + */
 | ||||
| +extern int ip6mr_mfc_stats_update(struct net *net, struct in6_addr *origin,
 | ||||
| +				  struct in6_addr *group, uint64_t pkts_in,
 | ||||
| +				  uint64_t bytes_in, uint64_t pkts_out,
 | ||||
| +				  uint64_t bytes_out);
 | ||||
| +/* QCA qca-mcs support - End */
 | ||||
|  #endif | ||||
| --- a/net/ipv4/ipmr.c
 | ||||
| +++ b/net/ipv4/ipmr.c
 | ||||
| @@ -89,6 +89,9 @@ static struct net_device *vif_dev_read(c
 | ||||
|  /* Special spinlock for queue of unresolved entries */ | ||||
|  static DEFINE_SPINLOCK(mfc_unres_lock); | ||||
|   | ||||
| +/* spinlock for offload */
 | ||||
| +static DEFINE_SPINLOCK(lock); /* QCA ECM qca-mcs support */
 | ||||
| +
 | ||||
|  /* We return to original Alan's scheme. Hash table of resolved | ||||
|   * entries is changed only in process context and protected | ||||
|   * with weak lock mrt_lock. Queue of unresolved entries is protected | ||||
| @@ -112,6 +115,9 @@ static void mroute_netlink_event(struct
 | ||||
|  static void igmpmsg_netlink_event(const struct mr_table *mrt, struct sk_buff *pkt); | ||||
|  static void mroute_clean_tables(struct mr_table *mrt, int flags); | ||||
|  static void ipmr_expire_process(struct timer_list *t); | ||||
| +static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt, __be32 origin,
 | ||||
| +					 __be32 mcastgrp);
 | ||||
| +static ipmr_mfc_event_offload_callback_t __rcu ipmr_mfc_event_offload_callback; /* QCA ECM qca-mcs support */
 | ||||
|   | ||||
|  #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES | ||||
|  #define ipmr_for_each_table(mrt, net)					\ | ||||
| @@ -223,6 +229,80 @@ static int ipmr_rule_fill(struct fib_rul
 | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +/* QCA ECM qca-mcs support - Start */
 | ||||
| +/* ipmr_sync_entry_update()
 | ||||
| + * Call the registered offload callback to report an update to a multicast
 | ||||
| + * route entry. The callback receives the list of destination interfaces and
 | ||||
| + * the interface count
 | ||||
| + */
 | ||||
| +static void ipmr_sync_entry_update(struct mr_table *mrt,
 | ||||
| +				   struct mfc_cache *cache)
 | ||||
| +{
 | ||||
| +	int vifi, dest_if_count = 0;
 | ||||
| +	u32 dest_dev[MAXVIFS];
 | ||||
| +	__be32  origin;
 | ||||
| +	__be32  group;
 | ||||
| +	ipmr_mfc_event_offload_callback_t offload_update_cb_f;
 | ||||
| +
 | ||||
| +	memset(dest_dev, 0, sizeof(dest_dev));
 | ||||
| +
 | ||||
| +	origin = cache->mfc_origin;
 | ||||
| +	group = cache->mfc_mcastgrp;
 | ||||
| +
 | ||||
| +	spin_lock(&mrt_lock);
 | ||||
| +	for (vifi = 0; vifi < cache->_c.mfc_un.res.maxvif; vifi++) {
 | ||||
| +		if (!((cache->_c.mfc_un.res.ttls[vifi] > 0) &&
 | ||||
| +		      (cache->_c.mfc_un.res.ttls[vifi] < 255))) {
 | ||||
| +			continue;
 | ||||
| +		}
 | ||||
| +		if (dest_if_count == MAXVIFS) {
 | ||||
| +			spin_unlock(&mrt_lock);
 | ||||
| +			return;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		if (!VIF_EXISTS(mrt, vifi)) {
 | ||||
| +			spin_unlock(&mrt_lock);
 | ||||
| +			return;
 | ||||
| +		}
 | ||||
| +		dest_dev[dest_if_count] = mrt->vif_table[vifi].dev->ifindex;
 | ||||
| +		dest_if_count++;
 | ||||
| +	}
 | ||||
| +	spin_unlock(&mrt_lock);
 | ||||
| +
 | ||||
| +	rcu_read_lock();
 | ||||
| +	offload_update_cb_f = rcu_dereference(ipmr_mfc_event_offload_callback);
 | ||||
| +
 | ||||
| +	if (!offload_update_cb_f) {
 | ||||
| +		rcu_read_unlock();
 | ||||
| +		return;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	offload_update_cb_f(group, origin, dest_if_count, dest_dev,
 | ||||
| +			    IPMR_MFC_EVENT_UPDATE);
 | ||||
| +	rcu_read_unlock();
 | ||||
| +}
 | ||||
| +
 | ||||
| +/* ipmr_sync_entry_delete()
 | ||||
| + * Call the registered offload callback to inform of a multicast route entry
 | ||||
| + * delete event
 | ||||
| + */
 | ||||
| +static void ipmr_sync_entry_delete(u32 origin, u32 group)
 | ||||
| +{
 | ||||
| +	ipmr_mfc_event_offload_callback_t offload_update_cb_f;
 | ||||
| +
 | ||||
| +	rcu_read_lock();
 | ||||
| +	offload_update_cb_f = rcu_dereference(ipmr_mfc_event_offload_callback);
 | ||||
| +
 | ||||
| +	if (!offload_update_cb_f) {
 | ||||
| +		rcu_read_unlock();
 | ||||
| +		return;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	offload_update_cb_f(group, origin, 0, NULL, IPMR_MFC_EVENT_DELETE);
 | ||||
| +	rcu_read_unlock();
 | ||||
| +}
 | ||||
| +/* QCA ECM qca-mcs support - End */
 | ||||
| +
 | ||||
|  static const struct fib_rules_ops __net_initconst ipmr_rules_ops_template = { | ||||
|  	.family		= RTNL_FAMILY_IPMR, | ||||
|  	.rule_size	= sizeof(struct ipmr_rule), | ||||
| @@ -236,6 +316,156 @@ static const struct fib_rules_ops __net_
 | ||||
|  	.owner		= THIS_MODULE, | ||||
|  }; | ||||
|   | ||||
| +/* QCA ECM qca-mcs support - Start */
 | ||||
| +/* ipmr_register_mfc_event_offload_callback()
 | ||||
| + * Register the IPv4 Multicast update offload callback with IPMR
 | ||||
| + */
 | ||||
| +bool ipmr_register_mfc_event_offload_callback(
 | ||||
| +		ipmr_mfc_event_offload_callback_t mfc_offload_cb)
 | ||||
| +{
 | ||||
| +	ipmr_mfc_event_offload_callback_t offload_update_cb_f;
 | ||||
| +
 | ||||
| +	rcu_read_lock();
 | ||||
| +	offload_update_cb_f = rcu_dereference(ipmr_mfc_event_offload_callback);
 | ||||
| +
 | ||||
| +	if (offload_update_cb_f) {
 | ||||
| +		rcu_read_unlock();
 | ||||
| +		return false;
 | ||||
| +	}
 | ||||
| +	rcu_read_unlock();
 | ||||
| +
 | ||||
| +	spin_lock(&lock);
 | ||||
| +	rcu_assign_pointer(ipmr_mfc_event_offload_callback, mfc_offload_cb);
 | ||||
| +	spin_unlock(&lock);
 | ||||
| +	synchronize_rcu();
 | ||||
| +	return true;
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(ipmr_register_mfc_event_offload_callback);
 | ||||
| +
 | ||||
| +/* ipmr_unregister_mfc_event_offload_callback()
 | ||||
| + * De-register the IPv4 Multicast update offload callback with IPMR
 | ||||
| + */
 | ||||
| +void ipmr_unregister_mfc_event_offload_callback(void)
 | ||||
| +{
 | ||||
| +	spin_lock(&lock);
 | ||||
| +	rcu_assign_pointer(ipmr_mfc_event_offload_callback, NULL);
 | ||||
| +	spin_unlock(&lock);
 | ||||
| +	synchronize_rcu();
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(ipmr_unregister_mfc_event_offload_callback);
 | ||||
| +
 | ||||
| +/* ipmr_find_mfc_entry()
 | ||||
| + * Returns destination interface list for a particular multicast flow, and
 | ||||
| + * the number of interfaces in the list
 | ||||
| + */
 | ||||
| +int ipmr_find_mfc_entry(struct net *net, __be32 origin, __be32 group,
 | ||||
| +			u32 max_dest_cnt, u32 dest_dev[])
 | ||||
| +{
 | ||||
| +	int vifi, dest_if_count = 0;
 | ||||
| +	struct mr_table *mrt;
 | ||||
| +	struct mfc_cache *cache;
 | ||||
| +
 | ||||
| +	mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
 | ||||
| +	if (!mrt)
 | ||||
| +		return -ENOENT;
 | ||||
| +
 | ||||
| +	rcu_read_lock();
 | ||||
| +	cache = ipmr_cache_find(mrt, origin, group);
 | ||||
| +	if (!cache) {
 | ||||
| +		rcu_read_unlock();
 | ||||
| +		return -ENOENT;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	spin_lock(&mrt_lock);
 | ||||
| +	for (vifi = 0; vifi < cache->_c.mfc_un.res.maxvif; vifi++) {
 | ||||
| +		if (!((cache->_c.mfc_un.res.ttls[vifi] > 0) &&
 | ||||
| +		      (cache->_c.mfc_un.res.ttls[vifi] < 255))) {
 | ||||
| +			continue;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		/* We have another valid destination interface entry. Check if
 | ||||
| +		 * the number of the destination interfaces for the route is
 | ||||
| +		 * exceeding the size of the array given to us
 | ||||
| +		 */
 | ||||
| +		if (dest_if_count == max_dest_cnt) {
 | ||||
| +			spin_unlock(&mrt_lock);
 | ||||
| +			rcu_read_unlock();
 | ||||
| +			return -EINVAL;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		if (!VIF_EXISTS(mrt, vifi)) {
 | ||||
| +			spin_unlock(&mrt_lock);
 | ||||
| +			rcu_read_unlock();
 | ||||
| +			return -EINVAL;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		dest_dev[dest_if_count] = mrt->vif_table[vifi].dev->ifindex;
 | ||||
| +		dest_if_count++;
 | ||||
| +	}
 | ||||
| +	spin_unlock(&mrt_lock);
 | ||||
| +	rcu_read_unlock();
 | ||||
| +
 | ||||
| +	return dest_if_count;
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(ipmr_find_mfc_entry);
 | ||||
| +
 | ||||
| +/* ipmr_mfc_stats_update()
 | ||||
| + * Update the MFC/VIF statistics for offloaded flows
 | ||||
| + */
 | ||||
| +int ipmr_mfc_stats_update(struct net *net, __be32 origin, __be32 group,
 | ||||
| +			  u64 pkts_in, u64 bytes_in,
 | ||||
| +			  u64 pkts_out, u64 bytes_out)
 | ||||
| +{
 | ||||
| +	int vif, vifi;
 | ||||
| +	struct mr_table *mrt;
 | ||||
| +	struct mfc_cache *cache;
 | ||||
| +
 | ||||
| +	mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
 | ||||
| +	if (!mrt)
 | ||||
| +		return -ENOENT;
 | ||||
| +
 | ||||
| +	rcu_read_lock();
 | ||||
| +	cache = ipmr_cache_find(mrt, origin, group);
 | ||||
| +	if (!cache) {
 | ||||
| +		rcu_read_unlock();
 | ||||
| +		return -ENOENT;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	vif = cache->_c.mfc_parent;
 | ||||
| +
 | ||||
| +	spin_lock(&mrt_lock);
 | ||||
| +	if (!VIF_EXISTS(mrt, vif)) {
 | ||||
| +		spin_unlock(&mrt_lock);
 | ||||
| +		rcu_read_unlock();
 | ||||
| +		return -EINVAL;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	mrt->vif_table[vif].pkt_in += pkts_in;
 | ||||
| +	mrt->vif_table[vif].bytes_in += bytes_in;
 | ||||
| +	cache->_c.mfc_un.res.pkt  += pkts_out;
 | ||||
| +	cache->_c.mfc_un.res.bytes += bytes_out;
 | ||||
| +
 | ||||
| +	for (vifi = cache->_c.mfc_un.res.minvif;
 | ||||
| +			vifi < cache->_c.mfc_un.res.maxvif; vifi++) {
 | ||||
| +		if ((cache->_c.mfc_un.res.ttls[vifi] > 0) &&
 | ||||
| +		    (cache->_c.mfc_un.res.ttls[vifi] < 255)) {
 | ||||
| +			if (!VIF_EXISTS(mrt, vifi)) {
 | ||||
| +				spin_unlock(&mrt_lock);
 | ||||
| +				rcu_read_unlock();
 | ||||
| +				return -EINVAL;
 | ||||
| +			}
 | ||||
| +			mrt->vif_table[vifi].pkt_out += pkts_out;
 | ||||
| +			mrt->vif_table[vifi].bytes_out += bytes_out;
 | ||||
| +		}
 | ||||
| +	}
 | ||||
| +	spin_unlock(&mrt_lock);
 | ||||
| +	rcu_read_unlock();
 | ||||
| +
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(ipmr_mfc_stats_update);
 | ||||
| +/* QCA ECM qca-mcs support - End */
 | ||||
| +
 | ||||
|  static int __net_init ipmr_rules_init(struct net *net) | ||||
|  { | ||||
|  	struct fib_rules_ops *ops; | ||||
| @@ -1191,6 +1421,10 @@ static int ipmr_mfc_delete(struct mr_tab
 | ||||
|  	call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, c, mrt->id); | ||||
|  	mroute_netlink_event(mrt, c, RTM_DELROUTE); | ||||
|  	mr_cache_put(&c->_c); | ||||
| +	/* QCA ECM qca-mcs support - Start */
 | ||||
| +	/* Inform offload modules of the delete event */
 | ||||
| +	ipmr_sync_entry_delete(c->mfc_origin, c->mfc_mcastgrp);
 | ||||
| +	/* QCA ECM qca-mcs support - End */
 | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| @@ -1221,6 +1455,10 @@ static int ipmr_mfc_add(struct net *net,
 | ||||
|  		call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE, c, | ||||
|  					      mrt->id); | ||||
|  		mroute_netlink_event(mrt, c, RTM_NEWROUTE); | ||||
| +		/* QCA ECM qca-mcs support - Start */
 | ||||
| +		/* Inform offload modules of the update event */
 | ||||
| +		ipmr_sync_entry_update(mrt, c);
 | ||||
| +		/* QCA ECM qca-mcs support - End */
 | ||||
|  		return 0; | ||||
|  	} | ||||
|   | ||||
| --- a/net/ipv6/ip6mr.c
 | ||||
| +++ b/net/ipv6/ip6mr.c
 | ||||
| @@ -74,6 +74,9 @@ static struct net_device *vif_dev_read(c
 | ||||
|  /* Special spinlock for queue of unresolved entries */ | ||||
|  static DEFINE_SPINLOCK(mfc_unres_lock); | ||||
|   | ||||
| +/* Spinlock for offload */
 | ||||
| +static DEFINE_SPINLOCK(lock); /* QCA qca-mcs support */
 | ||||
| +
 | ||||
|  /* We return to original Alan's scheme. Hash table of resolved | ||||
|     entries is changed only in process context and protected | ||||
|     with weak lock mrt_lock. Queue of unresolved entries is protected | ||||
| @@ -101,6 +104,13 @@ static int ip6mr_rtm_dumproute(struct sk
 | ||||
|  			       struct netlink_callback *cb); | ||||
|  static void mroute_clean_tables(struct mr_table *mrt, int flags); | ||||
|  static void ipmr_expire_process(struct timer_list *t); | ||||
| +/* QCA qca-mcs support - Start */
 | ||||
| +static struct mfc6_cache *ip6mr_cache_find(struct mr_table *mrt,
 | ||||
| +					   const struct in6_addr *origin,
 | ||||
| +					   const struct in6_addr *mcastgrp);
 | ||||
| +static ip6mr_mfc_event_offload_callback_t __rcu
 | ||||
| +				ip6mr_mfc_event_offload_callback;
 | ||||
| +/* QCA qca-mcs support - End */
 | ||||
|   | ||||
|  #ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES | ||||
|  #define ip6mr_for_each_table(mrt, net) \ | ||||
| @@ -375,6 +385,84 @@ static struct mfc6_cache_cmp_arg ip6mr_m
 | ||||
|  	.mf6c_mcastgrp = IN6ADDR_ANY_INIT, | ||||
|  }; | ||||
|   | ||||
| +/* QCA qca-mcs support - Start */
 | ||||
| +/* ip6mr_sync_entry_update()
 | ||||
| + * Call the registered offload callback to report an update to a multicast
 | ||||
| + * route entry. The callback receives the list of destination interfaces and
 | ||||
| + * the interface count
 | ||||
| + */
 | ||||
| +static void ip6mr_sync_entry_update(struct mr_table *mrt,
 | ||||
| +				    struct mfc6_cache *cache)
 | ||||
| +{
 | ||||
| +	int vifi, dest_if_count = 0;
 | ||||
| +	u32 dest_dev[MAXMIFS];
 | ||||
| +	struct in6_addr mc_origin, mc_group;
 | ||||
| +	ip6mr_mfc_event_offload_callback_t offload_update_cb_f;
 | ||||
| +
 | ||||
| +	memset(dest_dev, 0, sizeof(dest_dev));
 | ||||
| +
 | ||||
| +	spin_lock(&mrt_lock);
 | ||||
| +
 | ||||
| +	for (vifi = 0; vifi < cache->_c.mfc_un.res.maxvif; vifi++) {
 | ||||
| +		if (!((cache->_c.mfc_un.res.ttls[vifi] > 0) &&
 | ||||
| +		      (cache->_c.mfc_un.res.ttls[vifi] < 255))) {
 | ||||
| +			continue;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		if (dest_if_count == MAXMIFS) {
 | ||||
| +			spin_unlock(&mrt_lock);
 | ||||
| +			return;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		if (!VIF_EXISTS(mrt, vifi)) {
 | ||||
| +			spin_unlock(&mrt_lock);
 | ||||
| +			return;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		dest_dev[dest_if_count] = mrt->vif_table[vifi].dev->ifindex;
 | ||||
| +		dest_if_count++;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	memcpy(&mc_origin, &cache->mf6c_origin, sizeof(struct in6_addr));
 | ||||
| +	memcpy(&mc_group, &cache->mf6c_mcastgrp, sizeof(struct in6_addr));
 | ||||
| +	spin_unlock(&mrt_lock);
 | ||||
| +
 | ||||
| +	rcu_read_lock();
 | ||||
| +	offload_update_cb_f = rcu_dereference(ip6mr_mfc_event_offload_callback);
 | ||||
| +
 | ||||
| +	if (!offload_update_cb_f) {
 | ||||
| +		rcu_read_unlock();
 | ||||
| +		return;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	offload_update_cb_f(&mc_group, &mc_origin, dest_if_count, dest_dev,
 | ||||
| +			    IP6MR_MFC_EVENT_UPDATE);
 | ||||
| +	rcu_read_unlock();
 | ||||
| +}
 | ||||
| +
 | ||||
| +/* ip6mr_sync_entry_delete()
 | ||||
| + * Call the registered offload callback to inform of a multicast route entry
 | ||||
| + * delete event
 | ||||
| + */
 | ||||
| +static void ip6mr_sync_entry_delete(struct in6_addr *mc_origin,
 | ||||
| +				    struct in6_addr *mc_group)
 | ||||
| +{
 | ||||
| +	ip6mr_mfc_event_offload_callback_t offload_update_cb_f;
 | ||||
| +
 | ||||
| +	rcu_read_lock();
 | ||||
| +	offload_update_cb_f = rcu_dereference(ip6mr_mfc_event_offload_callback);
 | ||||
| +
 | ||||
| +	if (!offload_update_cb_f) {
 | ||||
| +		rcu_read_unlock();
 | ||||
| +		return;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	offload_update_cb_f(mc_group, mc_origin, 0, NULL,
 | ||||
| +			    IP6MR_MFC_EVENT_DELETE);
 | ||||
| +	rcu_read_unlock();
 | ||||
| +}
 | ||||
| +/* QCA qca-mcs support - End */
 | ||||
| +
 | ||||
|  static struct mr_table_ops ip6mr_mr_table_ops = { | ||||
|  	.rht_params = &ip6mr_rht_params, | ||||
|  	.cmparg_any = &ip6mr_mr_table_ops_cmparg_any, | ||||
| @@ -697,6 +785,151 @@ static int call_ip6mr_mfc_entry_notifier
 | ||||
|  				     &mfc->_c, tb_id, &net->ipv6.ipmr_seq); | ||||
|  } | ||||
|   | ||||
| +/* QCA qca-mcs support - Start */
 | ||||
| +/* ip6mr_register_mfc_event_offload_callback()
 | ||||
| + * Register the IPv6 multicast update callback for offload modules
 | ||||
| + */
 | ||||
| +bool ip6mr_register_mfc_event_offload_callback(
 | ||||
| +		ip6mr_mfc_event_offload_callback_t mfc_offload_cb)
 | ||||
| +{
 | ||||
| +	ip6mr_mfc_event_offload_callback_t offload_update_cb_f;
 | ||||
| +
 | ||||
| +	rcu_read_lock();
 | ||||
| +	offload_update_cb_f = rcu_dereference(ip6mr_mfc_event_offload_callback);
 | ||||
| +
 | ||||
| +	if (offload_update_cb_f) {
 | ||||
| +		rcu_read_unlock();
 | ||||
| +		return false;
 | ||||
| +	}
 | ||||
| +	rcu_read_unlock();
 | ||||
| +
 | ||||
| +	spin_lock(&lock);
 | ||||
| +	rcu_assign_pointer(ip6mr_mfc_event_offload_callback, mfc_offload_cb);
 | ||||
| +	spin_unlock(&lock);
 | ||||
| +	synchronize_rcu();
 | ||||
| +	return true;
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(ip6mr_register_mfc_event_offload_callback);
 | ||||
| +
 | ||||
| +/* ip6mr_unregister_mfc_event_offload_callback()
 | ||||
| + * De-register the IPv6 multicast update callback for offload modules
 | ||||
| + */
 | ||||
| +void ip6mr_unregister_mfc_event_offload_callback(void)
 | ||||
| +{
 | ||||
| +	spin_lock(&lock);
 | ||||
| +	rcu_assign_pointer(ip6mr_mfc_event_offload_callback, NULL);
 | ||||
| +	spin_unlock(&lock);
 | ||||
| +	synchronize_rcu();
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(ip6mr_unregister_mfc_event_offload_callback);
 | ||||
| +
 | ||||
| +/* ip6mr_find_mfc_entry()
 | ||||
| + * Return the destination interface list for a particular multicast flow, and
 | ||||
| + * the number of interfaces in the list
 | ||||
| + */
 | ||||
| +int ip6mr_find_mfc_entry(struct net *net, struct in6_addr *origin,
 | ||||
| +			 struct in6_addr *group, u32 max_dest_cnt,
 | ||||
| +			 u32 dest_dev[])
 | ||||
| +{
 | ||||
| +	int vifi, dest_if_count = 0;
 | ||||
| +	struct mr_table *mrt;
 | ||||
| +	struct mfc6_cache *cache;
 | ||||
| +
 | ||||
| +	mrt = ip6mr_get_table(net, RT6_TABLE_DFLT);
 | ||||
| +	if (!mrt)
 | ||||
| +		return -ENOENT;
 | ||||
| +
 | ||||
| +	spin_lock(&mrt_lock);
 | ||||
| +	cache = ip6mr_cache_find(mrt, origin, group);
 | ||||
| +	if (!cache) {
 | ||||
| +		spin_unlock(&mrt_lock);
 | ||||
| +		return -ENOENT;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	for (vifi = 0; vifi < cache->_c.mfc_un.res.maxvif; vifi++) {
 | ||||
| +		if (!((cache->_c.mfc_un.res.ttls[vifi] > 0) &&
 | ||||
| +		      (cache->_c.mfc_un.res.ttls[vifi] < 255))) {
 | ||||
| +			continue;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		/* We have another valid destination interface entry. Check if
 | ||||
| +		 * the number of the destination interfaces for the route is
 | ||||
| +		 * exceeding the size of the array given to us
 | ||||
| +		 */
 | ||||
| +		if (dest_if_count == max_dest_cnt) {
 | ||||
| +			spin_unlock(&mrt_lock);
 | ||||
| +			return -EINVAL;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		if (!VIF_EXISTS(mrt, vifi)) {
 | ||||
| +			spin_unlock(&mrt_lock);
 | ||||
| +			return -EINVAL;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		dest_dev[dest_if_count] = mrt->vif_table[vifi].dev->ifindex;
 | ||||
| +		dest_if_count++;
 | ||||
| +	}
 | ||||
| +	spin_unlock(&mrt_lock);
 | ||||
| +
 | ||||
| +	return dest_if_count;
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(ip6mr_find_mfc_entry);
 | ||||
| +
 | ||||
| +/* ip6mr_mfc_stats_update()
 | ||||
| + * Update the MFC/VIF statistics for offloaded flows
 | ||||
| + */
 | ||||
| +int ip6mr_mfc_stats_update(struct net *net, struct in6_addr *origin,
 | ||||
| +			   struct in6_addr *group, u64 pkts_in,
 | ||||
| +			   u64 bytes_in, uint64_t pkts_out,
 | ||||
| +			   u64 bytes_out)
 | ||||
| +{
 | ||||
| +	int vif, vifi;
 | ||||
| +	struct mr_table *mrt;
 | ||||
| +	struct mfc6_cache *cache;
 | ||||
| +
 | ||||
| +	mrt = ip6mr_get_table(net, RT6_TABLE_DFLT);
 | ||||
| +
 | ||||
| +	if (!mrt)
 | ||||
| +		return -ENOENT;
 | ||||
| +
 | ||||
| +	spin_lock(&mrt_lock);
 | ||||
| +	cache = ip6mr_cache_find(mrt, origin, group);
 | ||||
| +	if (!cache) {
 | ||||
| +		spin_unlock(&mrt_lock);
 | ||||
| +		return -ENOENT;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	vif = cache->_c.mfc_parent;
 | ||||
| +
 | ||||
| +	if (!VIF_EXISTS(mrt, vif)) {
 | ||||
| +		spin_unlock(&mrt_lock);
 | ||||
| +		return -EINVAL;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	mrt->vif_table[vif].pkt_in += pkts_in;
 | ||||
| +	mrt->vif_table[vif].bytes_in += bytes_in;
 | ||||
| +	cache->_c.mfc_un.res.pkt += pkts_out;
 | ||||
| +	cache->_c.mfc_un.res.bytes += bytes_out;
 | ||||
| +
 | ||||
| +	for (vifi = cache->_c.mfc_un.res.minvif;
 | ||||
| +			vifi < cache->_c.mfc_un.res.maxvif; vifi++) {
 | ||||
| +		if ((cache->_c.mfc_un.res.ttls[vifi] > 0) &&
 | ||||
| +		    (cache->_c.mfc_un.res.ttls[vifi] < 255)) {
 | ||||
| +			if (!VIF_EXISTS(mrt, vifi)) {
 | ||||
| +				spin_unlock(&mrt_lock);
 | ||||
| +				return -EINVAL;
 | ||||
| +			}
 | ||||
| +			mrt->vif_table[vifi].pkt_out += pkts_out;
 | ||||
| +			mrt->vif_table[vifi].bytes_out += bytes_out;
 | ||||
| +		}
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	spin_unlock(&mrt_lock);
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +EXPORT_SYMBOL(ip6mr_mfc_stats_update);
 | ||||
| +/* QCA qca-mcs support - End */
 | ||||
| +
 | ||||
|  /* Delete a VIF entry */ | ||||
|  static int mif6_delete(struct mr_table *mrt, int vifi, int notify, | ||||
|  		       struct list_head *head) | ||||
| @@ -1221,6 +1454,7 @@ static int ip6mr_mfc_delete(struct mr_ta
 | ||||
|  			    int parent) | ||||
|  { | ||||
|  	struct mfc6_cache *c; | ||||
| +	struct in6_addr mc_origin, mc_group; /* QCA qca-mcs support */
 | ||||
|   | ||||
|  	/* The entries are added/deleted only under RTNL */ | ||||
|  	rcu_read_lock(); | ||||
| @@ -1229,6 +1463,11 @@ static int ip6mr_mfc_delete(struct mr_ta
 | ||||
|  	rcu_read_unlock(); | ||||
|  	if (!c) | ||||
|  		return -ENOENT; | ||||
| +
 | ||||
| +	/* QCA qca-mcs support - Start */
 | ||||
| +	memcpy(&mc_origin, &c->mf6c_origin, sizeof(struct in6_addr));
 | ||||
| +	memcpy(&mc_group, &c->mf6c_mcastgrp, sizeof(struct in6_addr));
 | ||||
| +	/* QCA qca-mcs support - End */
 | ||||
|  	rhltable_remove(&mrt->mfc_hash, &c->_c.mnode, ip6mr_rht_params); | ||||
|  	list_del_rcu(&c->_c.list); | ||||
|   | ||||
| @@ -1236,6 +1475,11 @@ static int ip6mr_mfc_delete(struct mr_ta
 | ||||
|  				       FIB_EVENT_ENTRY_DEL, c, mrt->id); | ||||
|  	mr6_netlink_event(mrt, c, RTM_DELROUTE); | ||||
|  	mr_cache_put(&c->_c); | ||||
| +	/* QCA qca-mcs support - Start */
 | ||||
| +	/* Inform offload modules of the delete event */
 | ||||
| +	ip6mr_sync_entry_delete(&mc_origin, &mc_group);
 | ||||
| +	/* QCA qca-mcs support - End */
 | ||||
| +
 | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| @@ -1457,6 +1701,10 @@ static int ip6mr_mfc_add(struct net *net
 | ||||
|  		call_ip6mr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE, | ||||
|  					       c, mrt->id); | ||||
|  		mr6_netlink_event(mrt, c, RTM_NEWROUTE); | ||||
| +		/* QCA qca-mcs support - Start */
 | ||||
| +		/* Inform offload modules of the update event */
 | ||||
| +		ip6mr_sync_entry_update(mrt, c);
 | ||||
| +		/* QCA qca-mcs support - End */
 | ||||
|  		return 0; | ||||
|  	} | ||||
|   | ||||
|  | @ -0,0 +1,111 @@ | |||
| --- a/crypto/authenc.c
 | ||||
| +++ b/crypto/authenc.c
 | ||||
| @@ -417,6 +417,8 @@ static int crypto_authenc_create(struct
 | ||||
|  		     enc->base.cra_driver_name) >= CRYPTO_MAX_ALG_NAME) | ||||
|  		goto err_free_inst; | ||||
|   | ||||
| +	inst->alg.base.cra_flags |= (auth_base->cra_flags |
 | ||||
| +				    enc->base.cra_flags) & CRYPTO_ALG_NOSUPP_SG;
 | ||||
|  	inst->alg.base.cra_priority = enc->base.cra_priority * 10 + | ||||
|  				      auth_base->cra_priority; | ||||
|  	inst->alg.base.cra_blocksize = enc->base.cra_blocksize; | ||||
| --- a/include/linux/crypto.h
 | ||||
| +++ b/include/linux/crypto.h
 | ||||
| @@ -101,6 +101,11 @@
 | ||||
|  #define CRYPTO_NOLOAD			0x00008000 | ||||
|   | ||||
|  /* | ||||
| + * Set this flag if algorithm does not support SG list transforms
 | ||||
| + */
 | ||||
| +#define CRYPTO_ALG_NOSUPP_SG		0x0000c000
 | ||||
| +
 | ||||
| +/*
 | ||||
|   * The algorithm may allocate memory during request processing, i.e. during | ||||
|   * encryption, decryption, or hashing.  Users can request an algorithm with this | ||||
|   * flag unset if they can't handle memory allocation failures. | ||||
| --- a/net/ipv4/esp4.c
 | ||||
| +++ b/net/ipv4/esp4.c
 | ||||
| @@ -658,6 +658,7 @@ static int esp_output(struct xfrm_state
 | ||||
|  	struct ip_esp_hdr *esph; | ||||
|  	struct crypto_aead *aead; | ||||
|  	struct esp_info esp; | ||||
| +	bool nosupp_sg;
 | ||||
|   | ||||
|  	esp.inplace = true; | ||||
|   | ||||
| @@ -669,6 +670,11 @@ static int esp_output(struct xfrm_state
 | ||||
|  	aead = x->data; | ||||
|  	alen = crypto_aead_authsize(aead); | ||||
|   | ||||
| +	nosupp_sg = crypto_tfm_alg_type(&aead->base) & CRYPTO_ALG_NOSUPP_SG;
 | ||||
| +	if (nosupp_sg && skb_linearize(skb)) {
 | ||||
| +		return -ENOMEM;
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	esp.tfclen = 0; | ||||
|  	if (x->tfcpad) { | ||||
|  		struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb); | ||||
| @@ -890,6 +896,7 @@ static int esp_input(struct xfrm_state *
 | ||||
|  	u8 *iv; | ||||
|  	struct scatterlist *sg; | ||||
|  	int err = -EINVAL; | ||||
| +	bool nosupp_sg;
 | ||||
|   | ||||
|  	if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr) + ivlen)) | ||||
|  		goto out; | ||||
| @@ -897,6 +904,12 @@ static int esp_input(struct xfrm_state *
 | ||||
|  	if (elen <= 0) | ||||
|  		goto out; | ||||
|   | ||||
| +	nosupp_sg = crypto_tfm_alg_type(&aead->base) & CRYPTO_ALG_NOSUPP_SG;
 | ||||
| +	if (nosupp_sg && skb_linearize(skb)) {
 | ||||
| +		err = -ENOMEM;
 | ||||
| +		goto out;
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	assoclen = sizeof(struct ip_esp_hdr); | ||||
|  	seqhilen = 0; | ||||
|   | ||||
| --- a/net/ipv6/esp6.c
 | ||||
| +++ b/net/ipv6/esp6.c
 | ||||
| @@ -696,6 +696,7 @@ static int esp6_output(struct xfrm_state
 | ||||
|  	struct ip_esp_hdr *esph; | ||||
|  	struct crypto_aead *aead; | ||||
|  	struct esp_info esp; | ||||
| +	bool nosupp_sg;
 | ||||
|   | ||||
|  	esp.inplace = true; | ||||
|   | ||||
| @@ -707,6 +708,11 @@ static int esp6_output(struct xfrm_state
 | ||||
|  	aead = x->data; | ||||
|  	alen = crypto_aead_authsize(aead); | ||||
|   | ||||
| +	nosupp_sg = crypto_tfm_alg_type(&aead->base) & CRYPTO_ALG_NOSUPP_SG;
 | ||||
| +	if (nosupp_sg && skb_linearize(skb)) {
 | ||||
| +		return -ENOMEM;
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	esp.tfclen = 0; | ||||
|  	if (x->tfcpad) { | ||||
|  		struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb); | ||||
| @@ -934,6 +940,7 @@ static int esp6_input(struct xfrm_state
 | ||||
|  	__be32 *seqhi; | ||||
|  	u8 *iv; | ||||
|  	struct scatterlist *sg; | ||||
| +	bool nosupp_sg;
 | ||||
|   | ||||
|  	if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr) + ivlen)) { | ||||
|  		ret = -EINVAL; | ||||
| @@ -945,6 +952,12 @@ static int esp6_input(struct xfrm_state
 | ||||
|  		goto out; | ||||
|  	} | ||||
|   | ||||
| +	nosupp_sg = crypto_tfm_alg_type(&aead->base) & CRYPTO_ALG_NOSUPP_SG;
 | ||||
| +	if (nosupp_sg && skb_linearize(skb)) {
 | ||||
| +		ret = -ENOMEM;
 | ||||
| +		goto out;
 | ||||
| +	}
 | ||||
| +
 | ||||
|  	assoclen = sizeof(struct ip_esp_hdr); | ||||
|  	seqhilen = 0; | ||||
|   | ||||
|  | @ -0,0 +1,80 @@ | |||
| From eee3a7956b943dd3e23a74fbb5bfe89405eb0782 Mon Sep 17 00:00:00 2001 | ||||
| From: Andrea Righi <andrea.righi@canonical.com> | ||||
| Date: Mon, 6 Dec 2021 17:34:47 +0100 | ||||
| Subject: UBUNTU: SAUCE: ipv6: fix NULL pointer dereference in ip6_output() | ||||
| 
 | ||||
| It is possible to trigger a NULL pointer dereference by running the srv6 | ||||
| net kselftest (tools/testing/selftests/net/srv6_end_dt46_l3vpn_test.sh): | ||||
| 
 | ||||
| [  249.051216] BUG: kernel NULL pointer dereference, address: 0000000000000378 | ||||
| [  249.052331] #PF: supervisor read access in kernel mode | ||||
| [  249.053137] #PF: error_code(0x0000) - not-present page | ||||
| [  249.053960] PGD 0 P4D 0 | ||||
| [  249.054376] Oops: 0000 [#1] PREEMPT SMP NOPTI | ||||
| [  249.055083] CPU: 1 PID: 21 Comm: ksoftirqd/1 Tainted: G            E     5.16.0-rc4 #2 | ||||
| [  249.056328] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.14.0-2 04/01/2014 | ||||
| [  249.057632] RIP: 0010:ip6_forward+0x53c/0xab0 | ||||
| [  249.058354] Code: 49 c7 44 24 20 00 00 00 00 48 83 e0 fe 48 8b 40 30 48 3d 70 b2 b5 81 0f 85 b5 04 00 00 e8 7c f2 ff ff 41 89 c5 e9 17 01 00 00 <44> 8b 93 78 03 00 00 45 85 d2 0f 85 92 fb ff ff 49 8b 54 24 10 48 | ||||
| [  249.061274] RSP: 0018:ffffc900000cbb30 EFLAGS: 00010246 | ||||
| [  249.062042] RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffff8881051d3400 | ||||
| [  249.063141] RDX: ffff888104bda000 RSI: 00000000000002c0 RDI: 0000000000000000 | ||||
| [  249.064264] RBP: ffffc900000cbbc8 R08: 0000000000000000 R09: 0000000000000000 | ||||
| [  249.065376] R10: 0000000000000040 R11: 0000000000000000 R12: ffff888103409800 | ||||
| [  249.066498] R13: ffff8881051d3410 R14: ffff888102725280 R15: ffff888103525000 | ||||
| [  249.067619] FS:  0000000000000000(0000) GS:ffff88813bc80000(0000) knlGS:0000000000000000 | ||||
| [  249.068881] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033 | ||||
| [  249.069777] CR2: 0000000000000378 CR3: 0000000104980000 CR4: 0000000000750ee0 | ||||
| [  249.070907] PKRU: 55555554 | ||||
| [  249.071337] Call Trace: | ||||
| [  249.071730]  <TASK> | ||||
| [  249.072070]  ? debug_smp_processor_id+0x17/0x20 | ||||
| [  249.072807]  seg6_input_core+0x2bb/0x2d0 | ||||
| [  249.073436]  ? _raw_spin_unlock_irqrestore+0x29/0x40 | ||||
| [  249.074225]  seg6_input+0x3b/0x130 | ||||
| [  249.074768]  lwtunnel_input+0x5e/0xa0 | ||||
| [  249.075357]  ip_rcv+0x17b/0x190 | ||||
| [  249.075867]  ? update_load_avg+0x82/0x600 | ||||
| [  249.076514]  __netif_receive_skb_one_core+0x86/0xa0 | ||||
| [  249.077231]  __netif_receive_skb+0x15/0x60 | ||||
| [  249.077843]  process_backlog+0x97/0x160 | ||||
| [  249.078389]  __napi_poll+0x31/0x170 | ||||
| [  249.078912]  net_rx_action+0x229/0x270 | ||||
| [  249.079506]  __do_softirq+0xef/0x2ed | ||||
| [  249.080085]  run_ksoftirqd+0x37/0x50 | ||||
| [  249.080663]  smpboot_thread_fn+0x193/0x230 | ||||
| [  249.081312]  kthread+0x17a/0x1a0 | ||||
| [  249.081847]  ? smpboot_register_percpu_thread+0xe0/0xe0 | ||||
| [  249.082677]  ? set_kthread_struct+0x50/0x50 | ||||
| [  249.083340]  ret_from_fork+0x22/0x30 | ||||
| [  249.083926]  </TASK> | ||||
| [  249.090295] ---[ end trace 1998d7ba5965a365 ]--- | ||||
| 
 | ||||
| It looks like commit 0857d6f8c759 ("ipv6: When forwarding count rx stats | ||||
| on the orig netdev") tries to determine the right netdev to account the | ||||
| rx stats, but in this particular case it's failing and the netdev is | ||||
| NULL. | ||||
| 
 | ||||
| Fallback to the previous method of determining the netdev interface (via | ||||
| skb->dev) to account the rx stats when the orig netdev can't be | ||||
| determined. | ||||
| 
 | ||||
| Fixes: 0857d6f8c759 ("ipv6: When forwarding count rx stats on the orig netdev") | ||||
| Signed-off-by: Andrea Righi <andrea.righi@canonical.com> | ||||
| (cherry picked from https://lore.kernel.org/lkml/20211206163447.991402-1-andrea.righi@canonical.com/T/#u) | ||||
| Signed-off-by: Andrea Righi <andrea.righi@canonical.com> | ||||
| ---
 | ||||
|  net/ipv6/ip6_output.c | 3 +++ | ||||
|  1 file changed, 3 insertions(+) | ||||
| 
 | ||||
| --- a/net/ipv6/ip6_output.c
 | ||||
| +++ b/net/ipv6/ip6_output.c
 | ||||
| @@ -492,6 +492,9 @@ int ip6_forward(struct sk_buff *skb)
 | ||||
|  	u32 mtu; | ||||
|   | ||||
|  	idev = __in6_dev_get_safely(dev_get_by_index_rcu(net, IP6CB(skb)->iif)); | ||||
| +	if (unlikely(!idev))
 | ||||
| +		idev = __in6_dev_get_safely(skb->dev);
 | ||||
| +
 | ||||
|  	if (net->ipv6.devconf_all->forwarding == 0) | ||||
|  		goto error; | ||||
|   | ||||
|  | @ -0,0 +1,25 @@ | |||
| diff -uprN a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi
 | ||||
| --- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi	2023-06-22 18:11:32.910676000 -0700
 | ||||
| +++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi	2023-07-26 21:43:58.269612521 -0700
 | ||||
| @@ -750,6 +750,21 @@
 | ||||
|  			status = "disabled"; | ||||
|  		}; | ||||
|   | ||||
| +		blsp1_i2c4: i2c@78b8000 {
 | ||||
| +			compatible = "qcom,i2c-qup-v2.2.1";
 | ||||
| +			#address-cells = <1>;
 | ||||
| +			#size-cells = <0>;
 | ||||
| +			reg = <0x078b8000 0x600>;
 | ||||
| +			interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
 | ||||
| +			clocks = <&gcc GCC_BLSP1_QUP4_I2C_APPS_CLK>,
 | ||||
| +				 <&gcc GCC_BLSP1_AHB_CLK>;
 | ||||
| +			clock-names = "core", "iface";
 | ||||
| +			clock-frequency = <100000>;
 | ||||
| +			dmas = <&blsp_dma 18>, <&blsp_dma 19>;
 | ||||
| +			dma-names = "tx", "rx";
 | ||||
| +			status = "disabled";
 | ||||
| +		};
 | ||||
| +
 | ||||
|  		blsp1_i2c5: i2c@78b9000 { | ||||
|  			compatible = "qcom,i2c-qup-v2.2.1"; | ||||
|  			#address-cells = <1>; | ||||
|  | @ -0,0 +1,412 @@ | |||
| --- a/include/linux/cpuhotplug.h
 | ||||
| +++ b/include/linux/cpuhotplug.h
 | ||||
| @@ -94,6 +94,7 @@ enum cpuhp_state {
 | ||||
|  	CPUHP_RADIX_DEAD, | ||||
|  	CPUHP_PAGE_ALLOC, | ||||
|  	CPUHP_NET_DEV_DEAD, | ||||
| +	CPUHP_SKB_RECYCLER_DEAD,
 | ||||
|  	CPUHP_PCI_XGENE_DEAD, | ||||
|  	CPUHP_IOMMU_IOVA_DEAD, | ||||
|  	CPUHP_LUSTRE_CFS_DEAD, | ||||
| --- a/include/linux/skbuff.h
 | ||||
| +++ b/include/linux/skbuff.h
 | ||||
| @@ -1240,7 +1240,7 @@ static inline void kfree_skb_list(struct sk_buff *segs)
 | ||||
|  	kfree_skb_list_reason(segs, SKB_DROP_REASON_NOT_SPECIFIED); | ||||
|  } | ||||
|   | ||||
| -#ifdef CONFIG_TRACEPOINTS
 | ||||
| +#ifdef CONFIG_SKB_RECYCLER
 | ||||
|  void consume_skb(struct sk_buff *skb); | ||||
|  #else | ||||
|  static inline void consume_skb(struct sk_buff *skb) | ||||
| @@ -1252,6 +1252,8 @@ static inline void consume_skb(struct sk_buff *skb)
 | ||||
|  void __consume_stateless_skb(struct sk_buff *skb); | ||||
|  void  __kfree_skb(struct sk_buff *skb); | ||||
|  extern struct kmem_cache *skbuff_head_cache; | ||||
| +extern void kfree_skbmem(struct sk_buff *skb);
 | ||||
| +extern void skb_release_data(struct sk_buff *skb);
 | ||||
|   | ||||
|  void kfree_skb_partial(struct sk_buff *skb, bool head_stolen); | ||||
|  bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from, | ||||
| --- a/net/Kconfig
 | ||||
| +++ b/net/Kconfig
 | ||||
| @@ -332,6 +332,27 @@ config NET_FLOW_LIMIT
 | ||||
|  	  with many clients some protection against DoS by a single (spoofed) | ||||
|  	  flow that greatly exceeds average workload. | ||||
|   | ||||
| +config SKB_RECYCLER
 | ||||
| +	bool "Generic skb recycling"
 | ||||
| +	default y
 | ||||
| +	help
 | ||||
| +	  SKB_RECYCLER is used to implement RX-to-RX skb recycling.
 | ||||
| +	  This config enables the recycling scheme for bridging and
 | ||||
| +	  routing workloads. It can reduce skbuff freeing or
 | ||||
| +	  reallocation overhead.
 | ||||
| +
 | ||||
| +config SKB_RECYCLER_MULTI_CPU
 | ||||
| +	bool "Cross-CPU recycling for CPU-locked workloads"
 | ||||
| +	depends on SMP && SKB_RECYCLER
 | ||||
| +	default n
 | ||||
| +
 | ||||
| +config ALLOC_SKB_PAGE_FRAG_DISABLE
 | ||||
| +	bool "Disable page fragment based skbuff payload allocations"
 | ||||
| +	depends on !SKB_RECYCLER
 | ||||
| +	default n
 | ||||
| +	help
 | ||||
| +	 Disable page fragment based allocations for skbuff payloads.
 | ||||
| +
 | ||||
|  menu "Network testing" | ||||
|   | ||||
|  config NET_PKTGEN | ||||
| --- a/net/core/Makefile
 | ||||
| +++ b/net/core/Makefile
 | ||||
| @@ -40,3 +40,4 @@ obj-$(CONFIG_NET_SOCK_MSG) += skmsg.o
 | ||||
|  obj-$(CONFIG_BPF_SYSCALL) += sock_map.o | ||||
|  obj-$(CONFIG_BPF_SYSCALL) += bpf_sk_storage.o | ||||
|  obj-$(CONFIG_OF)	+= of_net.o | ||||
| +obj-$(CONFIG_SKB_RECYCLER) += skbuff_recycle.o
 | ||||
| --- a/net/core/dev.c
 | ||||
| +++ b/net/core/dev.c
 | ||||
| @@ -5974,10 +5974,16 @@ static int process_backlog(struct napi_struct *napi, int quota)
 | ||||
|   | ||||
|  	napi->weight = READ_ONCE(dev_rx_weight); | ||||
|  	while (again) { | ||||
| -		struct sk_buff *skb;
 | ||||
| +		struct sk_buff *skb, *next_skb;
 | ||||
|   | ||||
|  		while ((skb = __skb_dequeue(&sd->process_queue))) { | ||||
|  			rcu_read_lock(); | ||||
| +
 | ||||
| +			next_skb = skb_peek(&sd->process_queue);
 | ||||
| +			if (likely(next_skb)) {
 | ||||
| +				prefetch(next_skb->data);
 | ||||
| +			}
 | ||||
| +
 | ||||
|  			__netif_receive_skb(skb); | ||||
|  			rcu_read_unlock(); | ||||
|  			input_queue_head_incr(sd); | ||||
| --- a/net/core/skbuff.c
 | ||||
| +++ b/net/core/skbuff.c
 | ||||
| @@ -84,6 +84,33 @@
 | ||||
|  #include "dev.h" | ||||
|  #include "sock_destructor.h" | ||||
|   | ||||
| +struct kmem_cache *skb_data_cache;
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * For low memory profile, NSS_SKB_FIXED_SIZE_2K is enabled and
 | ||||
| + * CONFIG_SKB_RECYCLER is disabled. For premium and enterprise profile
 | ||||
| + * CONFIG_SKB_RECYCLER is enabled and NSS_SKB_FIXED_SIZE_2K is disabled.
 | ||||
| + * Irrespective of NSS_SKB_FIXED_SIZE_2K enabled/disabled, the
 | ||||
| + * CONFIG_SKB_RECYCLER and __LP64__ determines the value of SKB_DATA_CACHE_SIZE
 | ||||
| + */
 | ||||
| +#if defined(CONFIG_SKB_RECYCLER)
 | ||||
| +/*
 | ||||
| + * 2688 for 64bit arch, 2624 for 32bit arch
 | ||||
| + */
 | ||||
| +#define SKB_DATA_CACHE_SIZE (SKB_DATA_ALIGN(SKB_RECYCLE_SIZE + NET_SKB_PAD) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
 | ||||
| +#else
 | ||||
| +/*
 | ||||
| + * 2368 for 64bit arch, 2176 for 32bit arch
 | ||||
| + */
 | ||||
| +#if defined(__LP64__)
 | ||||
| +#define SKB_DATA_CACHE_SIZE ((SKB_DATA_ALIGN(1984 + NET_SKB_PAD)) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
 | ||||
| +#else
 | ||||
| +#define SKB_DATA_CACHE_SIZE ((SKB_DATA_ALIGN(1856 + NET_SKB_PAD)) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
 | ||||
| +#endif
 | ||||
| +#endif
 | ||||
| +
 | ||||
| +#include "skbuff_recycle.h"
 | ||||
| +
 | ||||
|  struct kmem_cache *skbuff_head_cache __ro_after_init; | ||||
|  static struct kmem_cache *skbuff_fclone_cache __ro_after_init; | ||||
|  #ifdef CONFIG_SKB_EXTENSIONS | ||||
| @@ -426,32 +453,46 @@ EXPORT_SYMBOL(napi_build_skb);
 | ||||
|   * memory is free | ||||
|   */ | ||||
|  static void *kmalloc_reserve(unsigned int *size, gfp_t flags, int node, | ||||
| -			     bool *pfmemalloc)
 | ||||
| -{
 | ||||
| -	bool ret_pfmemalloc = false;
 | ||||
| -	size_t obj_size;
 | ||||
| -	void *obj;
 | ||||
| + 			     bool *pfmemalloc)
 | ||||
| + {
 | ||||
| + 	void *obj;
 | ||||
| + 	bool ret_pfmemalloc = false;
 | ||||
| +	unsigned int obj_size = *size;
 | ||||
| +
 | ||||
| +	if (obj_size > SZ_2K && obj_size <= SKB_DATA_CACHE_SIZE) {
 | ||||
| +		obj = kmem_cache_alloc_node(skb_data_cache,
 | ||||
| +						flags | __GFP_NOMEMALLOC | __GFP_NOWARN,
 | ||||
| +						node);
 | ||||
| +		*size = SKB_DATA_CACHE_SIZE;
 | ||||
| +		if (obj || !(gfp_pfmemalloc_allowed(flags)))
 | ||||
| +			goto out;
 | ||||
|   | ||||
| -	obj_size = SKB_HEAD_ALIGN(*size);
 | ||||
| +		/* Try again but now we are using pfmemalloc reserves */
 | ||||
| +		ret_pfmemalloc = true;
 | ||||
| +		obj = kmem_cache_alloc_node(skb_data_cache, flags, node);
 | ||||
| +		goto out;
 | ||||
| +	}
 | ||||
|   | ||||
|  	obj_size = kmalloc_size_roundup(obj_size); | ||||
| -	/* The following cast might truncate high-order bits of obj_size, this
 | ||||
| +
 | ||||
| +	/*
 | ||||
| +	 * The following cast might truncate high-order bits of obj_size, this
 | ||||
|  	 * is harmless because kmalloc(obj_size >= 2^32) will fail anyway. | ||||
|  	 */ | ||||
|  	*size = (unsigned int)obj_size; | ||||
|   | ||||
| -	/*
 | ||||
| -	 * Try a regular allocation, when that fails and we're not entitled
 | ||||
| -	 * to the reserves, fail.
 | ||||
| -	 */
 | ||||
| + 	/*
 | ||||
| + 	 * Try a regular allocation, when that fails and we're not entitled
 | ||||
| + 	 * to the reserves, fail.
 | ||||
| + 	 */
 | ||||
|  	obj = kmalloc_node_track_caller(obj_size, | ||||
| -					flags | __GFP_NOMEMALLOC | __GFP_NOWARN,
 | ||||
| -					node);
 | ||||
| -	if (obj || !(gfp_pfmemalloc_allowed(flags)))
 | ||||
| -		goto out;
 | ||||
| +				flags | __GFP_NOMEMALLOC | __GFP_NOWARN,
 | ||||
| +				node);
 | ||||
| + 	if (obj || !(gfp_pfmemalloc_allowed(flags)))
 | ||||
| + 		goto out;
 | ||||
|   | ||||
| -	/* Try again but now we are using pfmemalloc reserves */
 | ||||
| -	ret_pfmemalloc = true;
 | ||||
| + 	/* Try again but now we are using pfmemalloc reserves */
 | ||||
| + 	ret_pfmemalloc = true;
 | ||||
|  	obj = kmalloc_node_track_caller(obj_size, flags, node); | ||||
|   | ||||
|  out: | ||||
| @@ -513,10 +554,12 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
 | ||||
|  	 * aligned memory blocks, unless SLUB/SLAB debug is enabled. | ||||
|  	 * Both skb->head and skb_shared_info are cache line aligned. | ||||
|  	 */ | ||||
| +	size = SKB_DATA_ALIGN(size);
 | ||||
| +	size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
 | ||||
|  	data = kmalloc_reserve(&size, gfp_mask, node, &pfmemalloc); | ||||
|  	if (unlikely(!data)) | ||||
|  		goto nodata; | ||||
| -	/* kmalloc_size_roundup() might give us more room than requested.
 | ||||
| +	/* kmalloc_reserve(size) might give us more room than requested.
 | ||||
|  	 * Put skb_shared_info exactly at the end of allocated zone, | ||||
|  	 * to allow max possible filling before reallocation. | ||||
|  	 */ | ||||
| @@ -551,7 +594,7 @@ EXPORT_SYMBOL(__alloc_skb);
 | ||||
|  /** | ||||
|   *	__netdev_alloc_skb - allocate an skbuff for rx on a specific device | ||||
|   *	@dev: network device to receive on | ||||
| - *	@len: length to allocate
 | ||||
| + *	@length: length to allocate
 | ||||
|   *	@gfp_mask: get_free_pages mask, passed to alloc_skb | ||||
|   * | ||||
|   *	Allocate a new &sk_buff and assign it a usage count of one. The | ||||
| @@ -561,29 +604,53 @@ EXPORT_SYMBOL(__alloc_skb);
 | ||||
|   * | ||||
|   *	%NULL is returned if there is no free memory. | ||||
|   */ | ||||
| -struct sk_buff *__netdev_alloc_skb(struct net_device *dev, unsigned int len,
 | ||||
| -				   gfp_t gfp_mask)
 | ||||
| +struct sk_buff *__netdev_alloc_skb(struct net_device *dev,
 | ||||
| +				   unsigned int length, gfp_t gfp_mask)
 | ||||
|  { | ||||
| -	struct page_frag_cache *nc;
 | ||||
|  	struct sk_buff *skb; | ||||
| +	unsigned int len = length;
 | ||||
| +
 | ||||
| +#ifdef CONFIG_SKB_RECYCLER
 | ||||
| +	skb = skb_recycler_alloc(dev, length);
 | ||||
| +	if (likely(skb))
 | ||||
| +		return skb;
 | ||||
| +
 | ||||
| +	len = SKB_RECYCLE_SIZE;
 | ||||
| +	if (unlikely(length > SKB_RECYCLE_SIZE))
 | ||||
| +		len = length;
 | ||||
| +
 | ||||
| +	skb = __alloc_skb(len + NET_SKB_PAD, gfp_mask,
 | ||||
| +			  SKB_ALLOC_RX, NUMA_NO_NODE);
 | ||||
| +	if (!skb)
 | ||||
| +		goto skb_fail;
 | ||||
| +	goto skb_success;
 | ||||
| +#else
 | ||||
| +	struct page_frag_cache *nc;
 | ||||
|  	bool pfmemalloc; | ||||
| +	bool page_frag_alloc_enable = true;
 | ||||
|  	void *data; | ||||
|   | ||||
|  	len += NET_SKB_PAD; | ||||
|   | ||||
| +
 | ||||
| +#ifdef CONFIG_ALLOC_SKB_PAGE_FRAG_DISABLE
 | ||||
| +	page_frag_alloc_enable = false;
 | ||||
| +#endif
 | ||||
|  	/* If requested length is either too small or too big, | ||||
|  	 * we use kmalloc() for skb->head allocation. | ||||
|  	 */ | ||||
|  	if (len <= SKB_WITH_OVERHEAD(1024) || | ||||
|  	    len > SKB_WITH_OVERHEAD(PAGE_SIZE) || | ||||
| -	    (gfp_mask & (__GFP_DIRECT_RECLAIM | GFP_DMA))) {
 | ||||
| +	    (gfp_mask & (__GFP_DIRECT_RECLAIM | GFP_DMA)) ||
 | ||||
| +	    !page_frag_alloc_enable) {
 | ||||
|  		skb = __alloc_skb(len, gfp_mask, SKB_ALLOC_RX, NUMA_NO_NODE); | ||||
|  		if (!skb) | ||||
|  			goto skb_fail; | ||||
|  		goto skb_success; | ||||
|  	} | ||||
|   | ||||
| -	len = SKB_HEAD_ALIGN(len);
 | ||||
| +	len += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
 | ||||
| +	len = SKB_DATA_ALIGN(len);
 | ||||
|   | ||||
|  	if (sk_memalloc_socks()) | ||||
|  		gfp_mask |= __GFP_MEMALLOC; | ||||
| @@ -612,6 +679,7 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev, unsigned int len,
 | ||||
|  	if (pfmemalloc) | ||||
|  		skb->pfmemalloc = 1; | ||||
|  	skb->head_frag = 1; | ||||
| +#endif
 | ||||
|   | ||||
|  skb_success: | ||||
|  	skb_reserve(skb, NET_SKB_PAD); | ||||
| @@ -682,7 +750,8 @@ struct sk_buff *__napi_alloc_skb(struct napi_struct *napi, unsigned int len,
 | ||||
|  		data = page_frag_alloc_1k(&nc->page_small, gfp_mask); | ||||
|  		pfmemalloc = NAPI_SMALL_PAGE_PFMEMALLOC(nc->page_small); | ||||
|  	} else { | ||||
| -		len = SKB_HEAD_ALIGN(len);
 | ||||
| +		len += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
 | ||||
| +		len = SKB_DATA_ALIGN(len);
 | ||||
|   | ||||
|  		data = page_frag_alloc(&nc->page, len, gfp_mask); | ||||
|  		pfmemalloc = nc->page.pfmemalloc; | ||||
| @@ -780,7 +849,7 @@ static void skb_free_head(struct sk_buff *skb)
 | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| -static void skb_release_data(struct sk_buff *skb)
 | ||||
| +void skb_release_data(struct sk_buff *skb)
 | ||||
|  { | ||||
|  	struct skb_shared_info *shinfo = skb_shinfo(skb); | ||||
|  	int i; | ||||
| @@ -822,7 +891,7 @@ static void skb_release_data(struct sk_buff *skb)
 | ||||
|  /* | ||||
|   *	Free an skbuff by memory without cleaning the state. | ||||
|   */ | ||||
| -static void kfree_skbmem(struct sk_buff *skb)
 | ||||
| +void kfree_skbmem(struct sk_buff *skb)
 | ||||
|  { | ||||
|  	struct sk_buff_fclones *fclones; | ||||
|   | ||||
| @@ -1034,7 +1103,6 @@ void skb_tx_error(struct sk_buff *skb)
 | ||||
|  } | ||||
|  EXPORT_SYMBOL(skb_tx_error); | ||||
|   | ||||
| -#ifdef CONFIG_TRACEPOINTS
 | ||||
|  /** | ||||
|   *	consume_skb - free an skbuff | ||||
|   *	@skb: buffer to free | ||||
| @@ -1043,13 +1111,50 @@ EXPORT_SYMBOL(skb_tx_error);
 | ||||
|   *	Functions identically to kfree_skb, but kfree_skb assumes that the frame | ||||
|   *	is being dropped after a failure and notes that | ||||
|   */ | ||||
| +#ifdef CONFIG_SKB_RECYCLER
 | ||||
|  void consume_skb(struct sk_buff *skb) | ||||
|  { | ||||
|  	if (!skb_unref(skb)) | ||||
|  		return; | ||||
|   | ||||
| +	prefetch(&skb->destructor);
 | ||||
| +
 | ||||
| +	/*Tian: Not sure if we need to continue using this since
 | ||||
| +	 * since unref does the work in 5.4
 | ||||
| +	 */
 | ||||
| +
 | ||||
| +	/*
 | ||||
| +	if (likely(atomic_read(&skb->users) == 1))
 | ||||
| +		smp_rmb();
 | ||||
| +	else if (likely(!atomic_dec_and_test(&skb->users)))
 | ||||
| +		return;
 | ||||
| +	*/
 | ||||
| +
 | ||||
| +	/* If possible we'd like to recycle any skb rather than just free it,
 | ||||
| +	 * but in order to do that we need to release any head state too.
 | ||||
| +	 * We don't want to do this later because we'll be in a pre-emption
 | ||||
| +	 * disabled state.
 | ||||
| +	 */
 | ||||
| +	skb_release_head_state(skb);
 | ||||
| +
 | ||||
| +	/* Can we recycle this skb?  If we can then it will be much faster
 | ||||
| +	 * for us to recycle this one later than to allocate a new one
 | ||||
| +	 * from scratch.
 | ||||
| +	 */
 | ||||
| +	if (likely(skb->head) && likely(skb_recycler_consume(skb)))
 | ||||
| +		return;
 | ||||
| +
 | ||||
| +#ifdef CONFIG_TRACEPOINTS
 | ||||
|  	trace_consume_skb(skb); | ||||
| -	__kfree_skb(skb);
 | ||||
| +#endif
 | ||||
| +	/* We're not recycling so now we need to do the rest of what we would
 | ||||
| +	 * have done in __kfree_skb (above and beyond the skb_release_head_state
 | ||||
| +	 * that we already did).
 | ||||
| +	 */
 | ||||
| +	if (likely(skb->head))
 | ||||
| +		skb_release_data(skb);
 | ||||
| +
 | ||||
| +	kfree_skbmem(skb);
 | ||||
|  } | ||||
|  EXPORT_SYMBOL(consume_skb); | ||||
|  #endif | ||||
| @@ -1856,6 +1961,8 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
 | ||||
|  	if (skb_pfmemalloc(skb)) | ||||
|  		gfp_mask |= __GFP_MEMALLOC; | ||||
|   | ||||
| +	size = SKB_DATA_ALIGN(size);
 | ||||
| +	size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
 | ||||
|  	data = kmalloc_reserve(&size, gfp_mask, NUMA_NO_NODE, NULL); | ||||
|  	if (!data) | ||||
|  		goto nodata; | ||||
| @@ -4557,6 +4664,11 @@ static void skb_extensions_init(void) {}
 | ||||
|   | ||||
|  void __init skb_init(void) | ||||
|  { | ||||
| +	skb_data_cache = kmem_cache_create_usercopy("skb_data_cache",
 | ||||
| +						SKB_DATA_CACHE_SIZE,
 | ||||
| +						0, SLAB_PANIC, 0, SKB_DATA_CACHE_SIZE,
 | ||||
| +						NULL);
 | ||||
| +
 | ||||
|  	skbuff_head_cache = kmem_cache_create_usercopy("skbuff_head_cache", | ||||
|  					      sizeof(struct sk_buff), | ||||
|  					      0, | ||||
| @@ -4570,6 +4682,7 @@ void __init skb_init(void)
 | ||||
|  						SLAB_HWCACHE_ALIGN|SLAB_PANIC, | ||||
|  						NULL); | ||||
|  	skb_extensions_init(); | ||||
| +	skb_recycler_init();
 | ||||
|  } | ||||
|   | ||||
|  static int | ||||
| @@ -6224,6 +6337,8 @@ static int pskb_carve_inside_header(struct sk_buff *skb, const u32 off,
 | ||||
|  	if (skb_pfmemalloc(skb)) | ||||
|  		gfp_mask |= __GFP_MEMALLOC; | ||||
|   | ||||
| +	size = SKB_DATA_ALIGN(size);
 | ||||
| +	size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
 | ||||
|  	data = kmalloc_reserve(&size, gfp_mask, NUMA_NO_NODE, NULL); | ||||
|  	if (!data) | ||||
|  		return -ENOMEM; | ||||
| @@ -6340,6 +6455,8 @@ static int pskb_carve_inside_nonlinear(struct sk_buff *skb, const u32 off,
 | ||||
|  	if (skb_pfmemalloc(skb)) | ||||
|  		gfp_mask |= __GFP_MEMALLOC; | ||||
|   | ||||
| +	size = SKB_DATA_ALIGN(size);
 | ||||
| +	size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
 | ||||
|  	data = kmalloc_reserve(&size, gfp_mask, NUMA_NO_NODE, NULL); | ||||
|  	if (!data) | ||||
|  		return -ENOMEM; | ||||
|  | @ -0,0 +1,10 @@ | |||
| --- a/crypto/algapi.c
 | ||||
| +++ b/crypto/algapi.c
 | ||||
| @@ -290,7 +290,6 @@ static struct crypto_larval *__crypto_re
 | ||||
|  		} | ||||
|   | ||||
|  		if (!strcmp(q->cra_driver_name, alg->cra_name) || | ||||
| -		    !strcmp(q->cra_driver_name, alg->cra_driver_name) ||
 | ||||
|  		    !strcmp(q->cra_name, alg->cra_driver_name)) | ||||
|  			goto err; | ||||
|  	} | ||||
|  | @ -0,0 +1,13 @@ | |||
| --- a/drivers/mtd/mtdblock.c
 | ||||
| +++ b/drivers/mtd/mtdblock.c
 | ||||
| @@ -261,10 +261,6 @@ static int mtdblock_open(struct mtd_blkt
 | ||||
|  		return 0; | ||||
|  	} | ||||
|   | ||||
| -	if (mtd_type_is_nand(mbd->mtd))
 | ||||
| -		pr_warn("%s: MTD device '%s' is NAND, please consider using UBI block devices instead.\n",
 | ||||
| -			mbd->tr->name, mbd->mtd->name);
 | ||||
| -
 | ||||
|  	/* OK, it's not open. Create cache info for it */ | ||||
|  	mtdblk->count = 1; | ||||
|  	mutex_init(&mtdblk->cache_mutex); | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue