ipq806x: NSS Hardware Offloading Target Core and ECM patches
This commit is contained in:
		| @@ -0,0 +1,215 @@ | |||||||
|  | From c70758d96b22e4421a6afd824cb59e350c6a8040 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Robert Marko <robert.marko@sartura.hr> | ||||||
|  | Date: Tue, 2 Jun 2020 22:09:15 +0200 | ||||||
|  | Subject: [PATCH] Regulator: Add NSS VOLT | ||||||
|  |  | ||||||
|  | Signed-off-by: Robert Marko <robert.marko@sartura.hr> | ||||||
|  | --- | ||||||
|  |  drivers/regulator/Kconfig  | 7 +++++++ | ||||||
|  |  drivers/regulator/Makefile | 1 + | ||||||
|  |  2 files changed, 8 insertions(+) | ||||||
|  |  | ||||||
|  | --- a/drivers/regulator/Kconfig | ||||||
|  | +++ b/drivers/regulator/Kconfig | ||||||
|  | @@ -1423,5 +1423,12 @@ config REGULATOR_QCOM_LABIBB | ||||||
|  |  	  boost regulator and IBB can be used as a negative boost regulator | ||||||
|  |  	  for LCD display panel. | ||||||
|  |   | ||||||
|  | +config REGULATOR_NSS_VOLT | ||||||
|  | +	bool "Qualcomm IPQ806X NSS Voltage regulator" | ||||||
|  | +	depends on ARCH_QCOM || COMPILE_TEST | ||||||
|  | +	help | ||||||
|  | +	  This driver provides support for the Qualcomm IPQ806X NSS Voltage | ||||||
|  | +	  regulator. | ||||||
|  | + | ||||||
|  |  endif | ||||||
|  |   | ||||||
|  | --- a/drivers/regulator/Makefile | ||||||
|  | +++ b/drivers/regulator/Makefile | ||||||
|  | @@ -171,5 +171,6 @@ obj-$(CONFIG_REGULATOR_WM831X) += wm831x | ||||||
|  |  obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o | ||||||
|  |  obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o | ||||||
|  |  obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o | ||||||
|  | +obj-$(CONFIG_REGULATOR_NSS_VOLT) += nss-volt-ipq806x.o | ||||||
|  |   | ||||||
|  |  ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/drivers/regulator/nss-volt-ipq806x.c | ||||||
|  | @@ -0,0 +1,146 @@ | ||||||
|  | +/* | ||||||
|  | + * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. | ||||||
|  | + * | ||||||
|  | + * Permission to use, copy, modify, and/or distribute this software for any | ||||||
|  | + * purpose with or without fee is hereby granted, provided that the above | ||||||
|  | + * copyright notice and this permission notice appear in all copies. | ||||||
|  | + * | ||||||
|  | + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||||
|  | + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||||
|  | + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||||
|  | + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||||
|  | + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||||
|  | + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||||
|  | + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||||
|  | + */ | ||||||
|  | + | ||||||
|  | +#include <linux/kernel.h> | ||||||
|  | +#include <linux/err.h> | ||||||
|  | +#include <linux/platform_device.h> | ||||||
|  | +#include <linux/module.h> | ||||||
|  | +#include <linux/of.h> | ||||||
|  | +#include <linux/of_device.h> | ||||||
|  | +#include <linux/regulator/nss-volt-ipq806x.h> | ||||||
|  | + | ||||||
|  | +struct nss_data { | ||||||
|  | +	struct regulator *nss_reg; | ||||||
|  | +	u32 nss_core_vdd_nominal; | ||||||
|  | +	u32 nss_core_vdd_high; | ||||||
|  | +	u32 nss_core_threshold_freq; | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +static struct nss_data *data; | ||||||
|  | + | ||||||
|  | +int nss_ramp_voltage(unsigned long rate, bool ramp_up) | ||||||
|  | +{ | ||||||
|  | +	int ret; | ||||||
|  | +	int curr_uV, uV; | ||||||
|  | +	struct regulator *reg; | ||||||
|  | + | ||||||
|  | +	if (!data) { | ||||||
|  | +		pr_err("NSS core regulator not init.\n"); | ||||||
|  | +		return -ENODEV; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	reg = data->nss_reg; | ||||||
|  | + | ||||||
|  | +	if (!reg) { | ||||||
|  | +		pr_err("NSS core regulator not found.\n"); | ||||||
|  | +		return -EINVAL; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	uV = data->nss_core_vdd_nominal; | ||||||
|  | +	if (rate >= data->nss_core_threshold_freq) | ||||||
|  | +		return data->nss_core_vdd_high; | ||||||
|  | + | ||||||
|  | +	curr_uV = regulator_get_voltage(reg); | ||||||
|  | + | ||||||
|  | +	if (ramp_up) { | ||||||
|  | +		if (uV <= curr_uV) | ||||||
|  | +			return 0; | ||||||
|  | +	} else { | ||||||
|  | +		if (uV >= curr_uV) | ||||||
|  | +			return 0; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	ret = regulator_set_voltage(reg, uV, data->nss_core_vdd_high); | ||||||
|  | +	if (ret) | ||||||
|  | +		pr_err("NSS volt scaling failed (%d)\n", uV); | ||||||
|  | + | ||||||
|  | +	return ret; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static const struct of_device_id nss_ipq806x_match_table[] = { | ||||||
|  | +	{ .compatible = "qcom,nss-common" }, | ||||||
|  | +	{} | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +static int nss_volt_ipq806x_probe(struct platform_device *pdev) | ||||||
|  | +{ | ||||||
|  | +	struct device_node *np = pdev->dev.of_node; | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	if (!np) | ||||||
|  | +		return -ENODEV; | ||||||
|  | + | ||||||
|  | +	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); | ||||||
|  | +	if (!data) | ||||||
|  | +		return -ENOMEM; | ||||||
|  | + | ||||||
|  | +	data->nss_reg = devm_regulator_get(&pdev->dev, "nss_core"); | ||||||
|  | +	ret = PTR_ERR_OR_ZERO(data->nss_reg); | ||||||
|  | +	if (ret) { | ||||||
|  | +		if (ret == -EPROBE_DEFER) | ||||||
|  | +			dev_dbg(&pdev->dev, | ||||||
|  | +				"nss_core regulator not ready, retry\n"); | ||||||
|  | +		else | ||||||
|  | +			dev_err(&pdev->dev, "no regulator for nss_core: %d\n", | ||||||
|  | +				ret); | ||||||
|  | + | ||||||
|  | +		return ret; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	if (of_property_read_u32(np, "nss_core_vdd_nominal", | ||||||
|  | +				 &data->nss_core_vdd_nominal)) { | ||||||
|  | +		pr_warn("NSS core vdd nominal not found. Using defaults...\n"); | ||||||
|  | +		data->nss_core_vdd_nominal = 1100000; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	if (of_property_read_u32(np, "nss_core_vdd_high", | ||||||
|  | +				 &data->nss_core_vdd_high)) { | ||||||
|  | +		pr_warn("NSS core vdd high not found. Using defaults...\n"); | ||||||
|  | +		data->nss_core_vdd_high = 1150000; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	if (of_property_read_u32(np, "nss_core_threshold_freq", | ||||||
|  | +				 &data->nss_core_threshold_freq)) { | ||||||
|  | +		pr_warn("NSS core thres freq not found. Using defaults...\n"); | ||||||
|  | +		data->nss_core_threshold_freq = 733000000; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	platform_set_drvdata(pdev, data); | ||||||
|  | + | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static struct platform_driver nss_ipq806x_driver = { | ||||||
|  | +	.probe          = nss_volt_ipq806x_probe, | ||||||
|  | +	.driver         = { | ||||||
|  | +		.name   = "nss-volt-ipq806x", | ||||||
|  | +		.owner  = THIS_MODULE, | ||||||
|  | +		.of_match_table = nss_ipq806x_match_table, | ||||||
|  | +	}, | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +static int __init nss_ipq806x_init(void) | ||||||
|  | +{ | ||||||
|  | +	return platform_driver_register(&nss_ipq806x_driver); | ||||||
|  | +} | ||||||
|  | +late_initcall(nss_ipq806x_init); | ||||||
|  | + | ||||||
|  | +static void __exit nss_ipq806x_exit(void) | ||||||
|  | +{ | ||||||
|  | +	platform_driver_unregister(&nss_ipq806x_driver); | ||||||
|  | +} | ||||||
|  | +module_exit(nss_ipq806x_exit); | ||||||
|  | + | ||||||
|  | --- a/include/linux/regulator/nss-volt-ipq806x.h | ||||||
|  | +++ b/include/linux/regulator/nss-volt-ipq806x.h | ||||||
|  | @@ -22,3 +22,28 @@ | ||||||
|  |  int nss_ramp_voltage(unsigned long rate, bool ramp_up); | ||||||
|  |   | ||||||
|  |  #endif | ||||||
|  | + | ||||||
|  | +/* | ||||||
|  | + * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. | ||||||
|  | + * | ||||||
|  | + * Permission to use, copy, modify, and/or distribute this software for any | ||||||
|  | + * purpose with or without fee is hereby granted, provided that the above | ||||||
|  | + * copyright notice and this permission notice appear in all copies. | ||||||
|  | + * | ||||||
|  | + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||||
|  | + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||||
|  | + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||||
|  | + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||||
|  | + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||||
|  | + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||||
|  | + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||||
|  | + */ | ||||||
|  | + | ||||||
|  | +#ifndef __QCOM_NSS_VOL_SCALING_H | ||||||
|  | +#define __QCOM_NSS_VOL_SCALING_H | ||||||
|  | + | ||||||
|  | +#include <linux/regulator/consumer.h> | ||||||
|  | + | ||||||
|  | +int nss_ramp_voltage(unsigned long rate, bool ramp_up); | ||||||
|  | + | ||||||
|  | +#endif | ||||||
| @@ -0,0 +1,208 @@ | |||||||
|  | --- a/include/dt-bindings/clock/qcom,gcc-ipq806x.h | ||||||
|  | +++ b/include/dt-bindings/clock/qcom,gcc-ipq806x.h | ||||||
|  | @@ -283,6 +283,7 @@ | ||||||
|  |  #define EBI2_AON_CLK				281 | ||||||
|  |  #define NSSTCM_CLK_SRC				282 | ||||||
|  |  #define NSSTCM_CLK				283 | ||||||
|  | +#define NSS_CORE_CLK				284 /* Virtual */ | ||||||
|  |  #define CE5_A_CLK_SRC				285 | ||||||
|  |  #define CE5_H_CLK_SRC				286 | ||||||
|  |  #define CE5_CORE_CLK_SRC			287 | ||||||
|  | --- a/drivers/clk/qcom/gcc-ipq806x.c | ||||||
|  | +++ b/drivers/clk/qcom/gcc-ipq806x.c | ||||||
|  | @@ -24,6 +24,10 @@ | ||||||
|  |  #include "clk-branch.h" | ||||||
|  |  #include "clk-hfpll.h" | ||||||
|  |  #include "reset.h" | ||||||
|  | +#include <linux/regulator/nss-volt-ipq806x.h> | ||||||
|  | + | ||||||
|  | +/* NSS safe parent index which will be used during NSS PLL rate change */ | ||||||
|  | +static int gcc_ipq806x_nss_safe_parent; | ||||||
|  |   | ||||||
|  |  static struct clk_pll pll0 = { | ||||||
|  |  	.l_reg = 0x30c4, | ||||||
|  | @@ -2995,6 +2999,139 @@ static struct clk_branch ce5_h_clk = { | ||||||
|  |  	}, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | +static int nss_core_clk_set_rate(struct clk_hw *hw, unsigned long rate, | ||||||
|  | +				 unsigned long parent_rate) | ||||||
|  | +{ | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	/* | ||||||
|  | +	 * When ramping up voltage, it needs to be done first. This ensures that | ||||||
|  | +	 * the volt required will be available when you step up the frequency. | ||||||
|  | +	 */ | ||||||
|  | +	ret = nss_ramp_voltage(rate, true); | ||||||
|  | +	if (ret) | ||||||
|  | +		return ret; | ||||||
|  | + | ||||||
|  | +	ret = clk_dyn_rcg_ops.set_rate(&ubi32_core1_src_clk.clkr.hw, rate, | ||||||
|  | +				    parent_rate); | ||||||
|  | +	if (ret) | ||||||
|  | +		return ret; | ||||||
|  | + | ||||||
|  | +	ret = clk_dyn_rcg_ops.set_rate(&ubi32_core2_src_clk.clkr.hw, rate, | ||||||
|  | +				    parent_rate); | ||||||
|  | + | ||||||
|  | +	if (ret) | ||||||
|  | +		return ret; | ||||||
|  | + | ||||||
|  | +	/* | ||||||
|  | +	 * When ramping down voltage, it needs to be set first. This ensures | ||||||
|  | +	 * that the volt required will be available until you step down the | ||||||
|  | +	 * frequency. | ||||||
|  | +	 */ | ||||||
|  | +	ret = nss_ramp_voltage(rate, false); | ||||||
|  | + | ||||||
|  | +	return ret; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int | ||||||
|  | +nss_core_clk_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, | ||||||
|  | +				 unsigned long parent_rate, u8 index) | ||||||
|  | +{ | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	/* | ||||||
|  | +	 * When ramping up voltage needs to be done first. This ensures that | ||||||
|  | +	 * the voltage required will be available when you step up the | ||||||
|  | +	 * frequency. | ||||||
|  | +	 */ | ||||||
|  | +	ret = nss_ramp_voltage(rate, true); | ||||||
|  | +	if (ret) | ||||||
|  | +		return ret; | ||||||
|  | + | ||||||
|  | +	ret = clk_dyn_rcg_ops.set_rate_and_parent( | ||||||
|  | +			&ubi32_core1_src_clk.clkr.hw, rate, parent_rate, index); | ||||||
|  | +	if (ret) | ||||||
|  | +		return ret; | ||||||
|  | + | ||||||
|  | +	ret = clk_dyn_rcg_ops.set_rate_and_parent( | ||||||
|  | +			&ubi32_core2_src_clk.clkr.hw, rate, parent_rate, index); | ||||||
|  | + | ||||||
|  | +	if (ret) | ||||||
|  | +		return ret; | ||||||
|  | + | ||||||
|  | +	/* | ||||||
|  | +	 * When ramping down voltage needs to be done last. This ensures that | ||||||
|  | +	 * the voltage required will be available when you step down the | ||||||
|  | +	 * frequency. | ||||||
|  | +	 */ | ||||||
|  | +	ret = nss_ramp_voltage(rate, false); | ||||||
|  | + | ||||||
|  | +	return ret; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int nss_core_clk_determine_rate(struct clk_hw *hw, | ||||||
|  | +					struct clk_rate_request *req) | ||||||
|  | +{ | ||||||
|  | +	return clk_dyn_rcg_ops.determine_rate(&ubi32_core1_src_clk.clkr.hw, | ||||||
|  | +						req); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static unsigned long | ||||||
|  | +nss_core_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) | ||||||
|  | +{ | ||||||
|  | +	return clk_dyn_rcg_ops.recalc_rate(&ubi32_core1_src_clk.clkr.hw, | ||||||
|  | +						 parent_rate); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static u8 nss_core_clk_get_parent(struct clk_hw *hw) | ||||||
|  | +{ | ||||||
|  | +	return clk_dyn_rcg_ops.get_parent(&ubi32_core1_src_clk.clkr.hw); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int nss_core_clk_set_parent(struct clk_hw *hw, u8 i) | ||||||
|  | +{ | ||||||
|  | +	int ret; | ||||||
|  | +	struct clk_dyn_rcg *rcg; | ||||||
|  | +	struct freq_tbl f = {  200000000, P_PLL0, 2,  1, 2 }; | ||||||
|  | + | ||||||
|  | +	/* P_PLL0 is 800 Mhz which needs to be divided for 200 Mhz */ | ||||||
|  | +	if (i == gcc_ipq806x_nss_safe_parent) { | ||||||
|  | +		rcg = to_clk_dyn_rcg(&ubi32_core1_src_clk.clkr.hw); | ||||||
|  | +		clk_dyn_configure_bank(rcg, &f); | ||||||
|  | + | ||||||
|  | +		rcg = to_clk_dyn_rcg(&ubi32_core2_src_clk.clkr.hw); | ||||||
|  | +		clk_dyn_configure_bank(rcg, &f); | ||||||
|  | + | ||||||
|  | +		return 0; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	ret = clk_dyn_rcg_ops.set_parent(&ubi32_core1_src_clk.clkr.hw, i); | ||||||
|  | +	if (ret) | ||||||
|  | +		return ret; | ||||||
|  | + | ||||||
|  | +	return clk_dyn_rcg_ops.set_parent(&ubi32_core2_src_clk.clkr.hw, i); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static const struct clk_ops clk_ops_nss_core = { | ||||||
|  | +	.set_rate = nss_core_clk_set_rate, | ||||||
|  | +	.set_rate_and_parent = nss_core_clk_set_rate_and_parent, | ||||||
|  | +	.determine_rate = nss_core_clk_determine_rate, | ||||||
|  | +	.recalc_rate = nss_core_clk_recalc_rate, | ||||||
|  | +	.get_parent = nss_core_clk_get_parent, | ||||||
|  | +	.set_parent = nss_core_clk_set_parent, | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +/* Virtual clock for nss core clocks */ | ||||||
|  | +static struct clk_regmap nss_core_clk = { | ||||||
|  | +	.hw.init = &(struct clk_init_data){ | ||||||
|  | +		.name = "nss_core_clk", | ||||||
|  | +		.ops = &clk_ops_nss_core, | ||||||
|  | +		.parent_names = gcc_pxo_pll8_pll14_pll18_pll0, | ||||||
|  | +		.num_parents = 5, | ||||||
|  | +		.flags = CLK_SET_RATE_PARENT, | ||||||
|  | +	}, | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  |  static struct clk_regmap *gcc_ipq806x_clks[] = { | ||||||
|  |  	[PLL0] = &pll0.clkr, | ||||||
|  |  	[PLL0_VOTE] = &pll0_vote, | ||||||
|  | @@ -3114,6 +3251,7 @@ static struct clk_regmap *gcc_ipq806x_cl | ||||||
|  |  	[UBI32_CORE2_CLK_SRC] = &ubi32_core2_src_clk.clkr, | ||||||
|  |  	[NSSTCM_CLK_SRC] = &nss_tcm_src.clkr, | ||||||
|  |  	[NSSTCM_CLK] = &nss_tcm_clk.clkr, | ||||||
|  | +	[NSS_CORE_CLK] = &nss_core_clk, | ||||||
|  |  	[PLL9] = &hfpll0.clkr, | ||||||
|  |  	[PLL10] = &hfpll1.clkr, | ||||||
|  |  	[PLL12] = &hfpll_l2.clkr, | ||||||
|  | @@ -3334,6 +3472,12 @@ static int gcc_ipq806x_probe(struct plat | ||||||
|  |  	if (!regmap) | ||||||
|  |  		return -ENODEV; | ||||||
|  |   | ||||||
|  | +	gcc_ipq806x_nss_safe_parent = qcom_find_src_index(&nss_core_clk.hw, | ||||||
|  | +					gcc_pxo_pll8_pll14_pll18_pll0_map, | ||||||
|  | +					P_PLL0); | ||||||
|  | +	if (gcc_ipq806x_nss_safe_parent < 0) | ||||||
|  | +		return gcc_ipq806x_nss_safe_parent; | ||||||
|  | + | ||||||
|  |  	/* Setup PLL18 static bits */ | ||||||
|  |  	regmap_update_bits(regmap, 0x31a4, 0xffffffc0, 0x40000400); | ||||||
|  |  	regmap_write(regmap, 0x31b0, 0x3080); | ||||||
|  | --- a/drivers/clk/qcom/clk-rcg.c | ||||||
|  | +++ b/drivers/clk/qcom/clk-rcg.c | ||||||
|  | @@ -805,6 +805,11 @@ static int clk_dyn_rcg_set_rate_and_pare | ||||||
|  |  	return __clk_dyn_rcg_set_rate(hw, rate); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | +void clk_dyn_configure_bank(struct clk_dyn_rcg *rcg, const struct freq_tbl *f) | ||||||
|  | +{ | ||||||
|  | +	configure_bank(rcg, f); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  |  const struct clk_ops clk_rcg_ops = { | ||||||
|  |  	.enable = clk_enable_regmap, | ||||||
|  |  	.disable = clk_disable_regmap, | ||||||
|  | --- a/drivers/clk/qcom/clk-rcg.h | ||||||
|  | +++ b/drivers/clk/qcom/clk-rcg.h | ||||||
|  | @@ -183,4 +183,7 @@ struct clk_rcg_dfs_data { | ||||||
|  |  extern int qcom_cc_register_rcg_dfs(struct regmap *regmap, | ||||||
|  |  				    const struct clk_rcg_dfs_data *rcgs, | ||||||
|  |  				    size_t len); | ||||||
|  | + | ||||||
|  | +extern void clk_dyn_configure_bank(struct clk_dyn_rcg *rcg, | ||||||
|  | +					const struct freq_tbl *f); | ||||||
|  |  #endif | ||||||
| @@ -0,0 +1,11 @@ | |||||||
|  | --- a/net/core/of_net.c | ||||||
|  | +++ b/net/core/of_net.c | ||||||
|  | @@ -39,7 +39,7 @@ int of_get_phy_mode(struct device_node * | ||||||
|  |  	for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) | ||||||
|  |  		if (!strcasecmp(pm, phy_modes(i))) { | ||||||
|  |  			*interface = i; | ||||||
|  | -			return 0; | ||||||
|  | +			return i; | ||||||
|  |  		} | ||||||
|  |   | ||||||
|  |  	return -ENODEV; | ||||||
| @@ -0,0 +1,759 @@ | |||||||
|  | --- a/include/linux/if_bridge.h | ||||||
|  | +++ b/include/linux/if_bridge.h | ||||||
|  | @@ -69,6 +69,7 @@ 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 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, | ||||||
|  | @@ -192,4 +193,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 | ||||||
|  | @@ -222,7 +222,12 @@ extern void vlan_vids_del_by_dev(struct | ||||||
|  |   | ||||||
|  |  extern bool vlan_uses_dev(const struct net_device *dev); | ||||||
|  |   | ||||||
|  | +/* QCA NSS ECM support - Start */ | ||||||
|  | +extern struct net_device *vlan_dev_next_dev(const struct net_device *dev); | ||||||
|  | +/* QCA NSS ECM support - End */ | ||||||
|  | + | ||||||
|  |  #else | ||||||
|  | + | ||||||
|  |  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 | ||||||
|  | @@ -1713,6 +1713,27 @@ enum netdev_ml_priv_type { | ||||||
|  |  	ML_PRIV_CAN, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | +/* QCA NSS ECM support - Start */ | ||||||
|  | +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, | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +#define IFF_EXT_TUN_TAP             IFF_EXT_TUN_TAP | ||||||
|  | +#define IFF_EXT_PPP_L2TPV2          IFF_EXT_PPP_L2TPV2 | ||||||
|  | +#define IFF_EXT_PPP_L2TPV3          IFF_EXT_PPP_L2TPV3 | ||||||
|  | +#define IFF_EXT_PPP_PPTP            IFF_EXT_PPP_PPTP | ||||||
|  | +#define IFF_EXT_GRE_V4_TAP          IFF_EXT_GRE_V4_TAP | ||||||
|  | +#define IFF_EXT_GRE_V6_TAP          IFF_EXT_GRE_V6_TAP | ||||||
|  | +#define IFF_EXT_IFB                 IFF_EXT_IFB | ||||||
|  | +/* QCA NSS ECM support - End */ | ||||||
|  | + | ||||||
|  | + | ||||||
|  |  /** | ||||||
|  |   *	struct net_device - The DEVICE structure. | ||||||
|  |   * | ||||||
|  | @@ -2848,6 +2869,10 @@ enum netdev_cmd { | ||||||
|  |  	NETDEV_CVLAN_FILTER_DROP_INFO, | ||||||
|  |  	NETDEV_SVLAN_FILTER_PUSH_INFO, | ||||||
|  |  	NETDEV_SVLAN_FILTER_DROP_INFO, | ||||||
|  | +	/* 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 | ||||||
|  | @@ -574,4 +574,15 @@ static inline void neigh_update_is_route | ||||||
|  |  		*notify = 1; | ||||||
|  |  	} | ||||||
|  |  } | ||||||
|  | + | ||||||
|  | +/* 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))]; | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +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 */ | ||||||
|  | + | ||||||
|  |  #endif | ||||||
|  | --- a/include/net/route.h | ||||||
|  | +++ b/include/net/route.h | ||||||
|  | @@ -234,6 +234,11 @@ struct rtable *rt_dst_alloc(struct net_d | ||||||
|  |  			     bool nopolicy, 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/8021q/vlan_core.c | ||||||
|  | +++ b/net/8021q/vlan_core.c | ||||||
|  | @@ -556,4 +556,12 @@ static int __init vlan_offload_init(void | ||||||
|  |  	return 0; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | +/* QCA NSS ECM support - Start */ | ||||||
|  | +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 */ | ||||||
|  | + | ||||||
|  |  fs_initcall(vlan_offload_init); | ||||||
|  | --- a/net/bridge/br_fdb.c | ||||||
|  | +++ b/net/bridge/br_fdb.c | ||||||
|  | @@ -38,6 +38,35 @@ static int fdb_insert(struct net_bridge | ||||||
|  |  static void fdb_notify(struct net_bridge *br, | ||||||
|  |  		       const struct net_bridge_fdb_entry *, int, bool); | ||||||
|  |   | ||||||
|  | +/* QCA NSS ECM support - Start */ | ||||||
|  | +ATOMIC_NOTIFIER_HEAD(br_fdb_notifier_list); | ||||||
|  | +ATOMIC_NOTIFIER_HEAD(br_fdb_update_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); | ||||||
|  | + | ||||||
|  | +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 */ | ||||||
|  | + | ||||||
|  |  int __init br_fdb_init(void) | ||||||
|  |  { | ||||||
|  |  	br_fdb_cache = kmem_cache_create("bridge_fdb_cache", | ||||||
|  | @@ -343,6 +372,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 | ||||||
|  | @@ -369,8 +399,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); | ||||||
|  |  		} | ||||||
|  |  	} | ||||||
|  | @@ -618,6 +655,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))) | ||||||
|  | @@ -799,6 +842,25 @@ static void fdb_notify(struct net_bridge | ||||||
|  |   | ||||||
|  |  	br_offload_fdb_update(fdb); | ||||||
|  |   | ||||||
|  | +	/* 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 */ | ||||||
|  | + | ||||||
|  |  	if (swdev_notify) | ||||||
|  |  		br_switchdev_fdb_notify(br, fdb, type); | ||||||
|  |   | ||||||
|  | @@ -1382,3 +1444,62 @@ void br_fdb_clear_offload(const struct n | ||||||
|  |  	spin_unlock_bh(&p->br->hash_lock); | ||||||
|  |  } | ||||||
|  |  EXPORT_SYMBOL_GPL(br_fdb_clear_offload); | ||||||
|  | + | ||||||
|  | +/* 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 */ | ||||||
|  | + | ||||||
|  | --- a/net/bridge/br_if.c | ||||||
|  | +++ b/net/bridge/br_if.c | ||||||
|  | @@ -18,6 +18,7 @@ | ||||||
|  |  #include <linux/rtnetlink.h> | ||||||
|  |  #include <linux/if_ether.h> | ||||||
|  |  #include <linux/slab.h> | ||||||
|  | +#include <linux/version.h> | ||||||
|  |  #include <net/dsa.h> | ||||||
|  |  #include <net/sock.h> | ||||||
|  |  #include <linux/if_vlan.h> | ||||||
|  | @@ -27,6 +28,12 @@ | ||||||
|  |  #include "br_private.h" | ||||||
|  |  #include "br_private_offload.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 | ||||||
|  | @@ -707,6 +714,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: | ||||||
|  | @@ -742,6 +751,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. | ||||||
|  | @@ -788,3 +799,75 @@ bool br_port_flag_is_set(const struct ne | ||||||
|  |  	return p->flags & flag; | ||||||
|  |  } | ||||||
|  |  EXPORT_SYMBOL_GPL(br_port_flag_is_set); | ||||||
|  | + | ||||||
|  | +/* 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); | ||||||
|  | + | ||||||
|  | +/* 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); | ||||||
|  | +/* QCA NSS ECM support - End */ | ||||||
|  | --- a/net/bridge/br_private.h | ||||||
|  | +++ b/net/bridge/br_private.h | ||||||
|  | @@ -2120,4 +2120,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/core/dev.c | ||||||
|  | +++ b/net/core/dev.c | ||||||
|  | @@ -1685,7 +1685,7 @@ const char *netdev_cmd_to_name(enum netd | ||||||
|  |  	N(UDP_TUNNEL_DROP_INFO) N(CHANGE_TX_QUEUE_LEN) | ||||||
|  |  	N(CVLAN_FILTER_PUSH_INFO) N(CVLAN_FILTER_DROP_INFO) | ||||||
|  |  	N(SVLAN_FILTER_PUSH_INFO) N(SVLAN_FILTER_DROP_INFO) | ||||||
|  | -	N(PRE_CHANGEADDR) | ||||||
|  | +	N(PRE_CHANGEADDR) N(BR_JOIN) N(BR_LEAVE) | ||||||
|  |  	} | ||||||
|  |  #undef N | ||||||
|  |  	return "UNKNOWN_NETDEV_EVENT"; | ||||||
|  | --- a/net/core/neighbour.c | ||||||
|  | +++ b/net/core/neighbour.c | ||||||
|  | @@ -1219,7 +1219,21 @@ 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. | ||||||
|  | @@ -1250,6 +1264,7 @@ static int __neigh_update(struct neighbo | ||||||
|  |  	int notify = 0; | ||||||
|  |  	struct net_device *dev; | ||||||
|  |  	int update_isrouter = 0; | ||||||
|  | +	struct neigh_mac_update nmu; /* QCA NSS ECM support */ | ||||||
|  |   | ||||||
|  |  	trace_neigh_update(neigh, lladdr, new, flags, nlmsg_pid); | ||||||
|  |   | ||||||
|  | @@ -1264,6 +1279,8 @@ static int __neigh_update(struct neighbo | ||||||
|  |  		new = old; | ||||||
|  |  		goto out; | ||||||
|  |  	} | ||||||
|  | +	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; | ||||||
|  | @@ -1301,6 +1318,11 @@ static int __neigh_update(struct neighbo | ||||||
|  |  		   - compare new & old | ||||||
|  |  		   - if they are different, check override flag | ||||||
|  |  		 */ | ||||||
|  | +		/* 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; | ||||||
|  | @@ -1423,8 +1445,11 @@ out: | ||||||
|  |  	if (((new ^ old) & NUD_PERMANENT) || ext_learn_change) | ||||||
|  |  		neigh_update_gc_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); | ||||||
|  |   | ||||||
|  | --- a/net/ipv4/fib_trie.c | ||||||
|  | +++ b/net/ipv4/fib_trie.c | ||||||
|  | @@ -1206,6 +1206,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) | ||||||
|  | @@ -1396,6 +1399,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: | ||||||
|  | @@ -1766,6 +1772,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; | ||||||
|  | @@ -2394,6 +2403,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/addrconf.c | ||||||
|  | +++ b/net/ipv6/addrconf.c | ||||||
|  | @@ -987,6 +987,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) | ||||||
|  | @@ -2048,6 +2049,7 @@ struct inet6_ifaddr *ipv6_get_ifaddr(str | ||||||
|  |   | ||||||
|  |  	return result; | ||||||
|  |  } | ||||||
|  | +EXPORT_SYMBOL(ipv6_get_ifaddr); | ||||||
|  |   | ||||||
|  |  /* Gets referenced address, destroys ifaddr */ | ||||||
|  |   | ||||||
|  | --- a/net/ipv6/ndisc.c | ||||||
|  | +++ b/net/ipv6/ndisc.c | ||||||
|  | @@ -649,6 +649,7 @@ void ndisc_send_ns(struct net_device *de | ||||||
|  |   | ||||||
|  |  	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 | ||||||
|  | @@ -3878,6 +3878,9 @@ out_free: | ||||||
|  |  	return ERR_PTR(err); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | +/* Define route change notification chain. */ | ||||||
|  | +ATOMIC_NOTIFIER_HEAD(ip6route_chain);	/* QCA NSS ECM support */ | ||||||
|  | + | ||||||
|  |  int ip6_route_add(struct fib6_config *cfg, gfp_t gfp_flags, | ||||||
|  |  		  struct netlink_ext_ack *extack) | ||||||
|  |  { | ||||||
|  | @@ -3889,6 +3892,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; | ||||||
|  | @@ -3910,6 +3917,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; | ||||||
|  | @@ -6352,6 +6362,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/ipv6/ip6_gre.c | ||||||
|  | +++ b/net/ipv6/ip6_gre.c | ||||||
|  | @@ -1934,6 +1934,7 @@ static void ip6gre_tap_setup(struct net_ | ||||||
|  |   | ||||||
|  |  	dev->priv_flags &= ~IFF_TX_SKB_SHARING; | ||||||
|  |  	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; | ||||||
|  | +	dev->priv_flags_ext |= IFF_EXT_GRE_V6_TAP; /* QCA NSS ECM Support */ | ||||||
|  |  	netif_keep_dst(dev); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | --- a/net/ipv4/ip_gre.c | ||||||
|  | +++ b/net/ipv4/ip_gre.c | ||||||
|  | @@ -1333,6 +1333,7 @@ static void ipgre_tap_setup(struct net_d | ||||||
|  |  	dev->netdev_ops	= &gre_tap_netdev_ops; | ||||||
|  |  	dev->priv_flags &= ~IFF_TX_SKB_SHARING; | ||||||
|  |  	dev->priv_flags	|= IFF_LIVE_ADDR_CHANGE; | ||||||
|  | +	dev->priv_flags_ext |= IFF_EXT_GRE_V4_TAP; /* QCA NSS ECM Support */ | ||||||
|  |  	ip_tunnel_setup(dev, gre_tap_net_id); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | --- a/include/linux/netfilter/nf_conntrack_proto_gre.h | ||||||
|  | +++ b/include/linux/netfilter/nf_conntrack_proto_gre.h | ||||||
|  | @@ -31,4 +31,36 @@ void nf_ct_gre_keymap_destroy(struct nf_ | ||||||
|  |   | ||||||
|  |  bool gre_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff, | ||||||
|  |  		      struct net *net, struct nf_conntrack_tuple *tuple); | ||||||
|  | + | ||||||
|  | +/* QCA NSS ECM Support - Start */ | ||||||
|  | +/* GRE is a mess: Four different standards */ | ||||||
|  | +struct gre_hdr { | ||||||
|  | +#if defined(__LITTLE_ENDIAN_BITFIELD) | ||||||
|  | +        __u16   rec:3, | ||||||
|  | +                srr:1, | ||||||
|  | +                seq:1, | ||||||
|  | +                key:1, | ||||||
|  | +                routing:1, | ||||||
|  | +                csum:1, | ||||||
|  | +                version:3, | ||||||
|  | +                reserved:4, | ||||||
|  | +                ack:1; | ||||||
|  | +#elif defined(__BIG_ENDIAN_BITFIELD) | ||||||
|  | +        __u16   csum:1, | ||||||
|  | +                routing:1, | ||||||
|  | +                key:1, | ||||||
|  | +                seq:1, | ||||||
|  | +                srr:1, | ||||||
|  | +                rec:3, | ||||||
|  | +                ack:1, | ||||||
|  | +                reserved:4, | ||||||
|  | +                version:3; | ||||||
|  | +#else | ||||||
|  | +#error "Adjust your <asm/byteorder.h> defines" | ||||||
|  | +#endif | ||||||
|  | +        __be16  protocol; | ||||||
|  | +}; | ||||||
|  | +/* QCA NSS ECM Support - End */ | ||||||
|  | + | ||||||
|  | + | ||||||
|  |  #endif /* _CONNTRACK_PROTO_GRE_H */ | ||||||
|  | --- a/include/net/netfilter/nf_conntrack_timeout.h | ||||||
|  | +++ b/include/net/netfilter/nf_conntrack_timeout.h | ||||||
|  | @@ -123,5 +123,6 @@ static inline void nf_ct_destroy_timeout | ||||||
|  |  extern struct nf_ct_timeout *(*nf_ct_timeout_find_get_hook)(struct net *net, const char *name); | ||||||
|  |  extern void (*nf_ct_timeout_put_hook)(struct nf_ct_timeout *timeout); | ||||||
|  |  #endif | ||||||
|  | +extern unsigned int *udp_get_timeouts(struct net *net); | ||||||
|  |   | ||||||
|  |  #endif /* _NF_CONNTRACK_TIMEOUT_H */ | ||||||
|  | --- a/net/netfilter/nf_conntrack_proto_udp.c | ||||||
|  | +++ b/net/netfilter/nf_conntrack_proto_udp.c | ||||||
|  | @@ -29,10 +29,11 @@ static const unsigned int udp_timeouts[U | ||||||
|  |  	[UDP_CT_REPLIED]	= 120*HZ, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | -static unsigned int *udp_get_timeouts(struct net *net) | ||||||
|  | +unsigned int *udp_get_timeouts(struct net *net) | ||||||
|  |  { | ||||||
|  |  	return nf_udp_pernet(net)->timeouts; | ||||||
|  |  } | ||||||
|  | +EXPORT_SYMBOL(udp_get_timeouts); | ||||||
|  |   | ||||||
|  |  static void udp_error_log(const struct sk_buff *skb, | ||||||
|  |  			  const struct nf_hook_state *state, | ||||||
| @@ -0,0 +1,207 @@ | |||||||
|  | --- a/include/net/netns/conntrack.h | ||||||
|  | +++ b/include/net/netns/conntrack.h | ||||||
|  | @@ -113,6 +113,9 @@ struct netns_ct { | ||||||
|  |   | ||||||
|  |  	struct ct_pcpu __percpu *pcpu_lists; | ||||||
|  |  	struct ip_conntrack_stat __percpu *stat; | ||||||
|  | +#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS | ||||||
|  | +	struct atomic_notifier_head nf_conntrack_chain; | ||||||
|  | +#endif | ||||||
|  |  	struct nf_ct_event_notifier __rcu *nf_conntrack_event_cb; | ||||||
|  |  	struct nf_ip_net	nf_ct_proto; | ||||||
|  |  #if defined(CONFIG_NF_CONNTRACK_LABELS) | ||||||
|  | --- a/include/net/netfilter/nf_conntrack_ecache.h | ||||||
|  | +++ b/include/net/netfilter/nf_conntrack_ecache.h | ||||||
|  | @@ -78,6 +78,11 @@ struct nf_exp_event { | ||||||
|  |  	int report; | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | +#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS | ||||||
|  | +extern int nf_conntrack_register_chain_notifier(struct net *net, struct notifier_block *nb); | ||||||
|  | +extern int nf_conntrack_unregister_chain_notifier(struct net *net, struct notifier_block *nb); | ||||||
|  | +#endif | ||||||
|  | + | ||||||
|  |  struct nf_ct_event_notifier { | ||||||
|  |  	int (*ct_event)(unsigned int events, const struct nf_ct_event *item); | ||||||
|  |  	int (*exp_event)(unsigned int events, const struct nf_exp_event *item); | ||||||
|  | @@ -111,11 +116,13 @@ static inline void | ||||||
|  |  nf_conntrack_event_cache(enum ip_conntrack_events event, struct nf_conn *ct) | ||||||
|  |  { | ||||||
|  |  #ifdef CONFIG_NF_CONNTRACK_EVENTS | ||||||
|  | -	struct net *net = nf_ct_net(ct); | ||||||
|  |  	struct nf_conntrack_ecache *e; | ||||||
|  | +#ifndef CONFIG_NF_CONNTRACK_CHAIN_EVENTS | ||||||
|  | +	struct net *net = nf_ct_net(ct); | ||||||
|  |   | ||||||
|  |  	if (!rcu_access_pointer(net->ct.nf_conntrack_event_cb)) | ||||||
|  |  		return; | ||||||
|  | +#endif | ||||||
|  |   | ||||||
|  |  	e = nf_ct_ecache_find(ct); | ||||||
|  |  	if (e == NULL) | ||||||
|  | @@ -130,10 +137,12 @@ nf_conntrack_event_report(enum ip_conntr | ||||||
|  |  			  u32 portid, int report) | ||||||
|  |  { | ||||||
|  |  #ifdef CONFIG_NF_CONNTRACK_EVENTS | ||||||
|  | +#ifndef CONFIG_NF_CONNTRACK_CHAIN_EVENTS | ||||||
|  |  	const struct net *net = nf_ct_net(ct); | ||||||
|  |   | ||||||
|  |  	if (!rcu_access_pointer(net->ct.nf_conntrack_event_cb)) | ||||||
|  |  		return 0; | ||||||
|  | +#endif | ||||||
|  |   | ||||||
|  |  	return nf_conntrack_eventmask_report(1 << event, ct, portid, report); | ||||||
|  |  #else | ||||||
|  | @@ -145,10 +154,12 @@ static inline int | ||||||
|  |  nf_conntrack_event(enum ip_conntrack_events event, struct nf_conn *ct) | ||||||
|  |  { | ||||||
|  |  #ifdef CONFIG_NF_CONNTRACK_EVENTS | ||||||
|  | +#ifndef CONFIG_NF_CONNTRACK_CHAIN_EVENTS | ||||||
|  |  	const struct net *net = nf_ct_net(ct); | ||||||
|  |   | ||||||
|  |  	if (!rcu_access_pointer(net->ct.nf_conntrack_event_cb)) | ||||||
|  |  		return 0; | ||||||
|  | +#endif | ||||||
|  |   | ||||||
|  |  	return nf_conntrack_eventmask_report(1 << event, ct, 0, 0); | ||||||
|  |  #else | ||||||
|  | --- a/net/netfilter/Kconfig | ||||||
|  | +++ b/net/netfilter/Kconfig | ||||||
|  | @@ -160,6 +160,21 @@ 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_CHAIN_EVENTS | ||||||
|  | +	bool "Register multiple callbacks to ct events" | ||||||
|  | +	depends on NF_CONNTRACK_EVENTS | ||||||
|  | +	help | ||||||
|  | +	  Support multiple registrations. | ||||||
|  | + | ||||||
|  | +	  If unsure, say `N'. | ||||||
|  | + | ||||||
|  |  config NF_CONNTRACK_TIMESTAMP | ||||||
|  |  	bool  'Connection tracking timestamping' | ||||||
|  |  	depends on NETFILTER_ADVANCED | ||||||
|  | --- a/net/netfilter/nf_conntrack_core.c | ||||||
|  | +++ b/net/netfilter/nf_conntrack_core.c | ||||||
|  | @@ -2877,6 +2877,9 @@ int nf_conntrack_init_net(struct net *ne | ||||||
|  |  	nf_conntrack_ecache_pernet_init(net); | ||||||
|  |  	nf_conntrack_helper_pernet_init(net); | ||||||
|  |  	nf_conntrack_proto_pernet_init(net); | ||||||
|  | +#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS | ||||||
|  | +	ATOMIC_INIT_NOTIFIER_HEAD(&net->ct.nf_conntrack_chain); | ||||||
|  | +#endif | ||||||
|  |   | ||||||
|  |  	return 0; | ||||||
|  |   | ||||||
|  | --- a/net/netfilter/nf_conntrack_ecache.c | ||||||
|  | +++ b/net/netfilter/nf_conntrack_ecache.c | ||||||
|  | @@ -17,6 +17,9 @@ | ||||||
|  |  #include <linux/stddef.h> | ||||||
|  |  #include <linux/err.h> | ||||||
|  |  #include <linux/percpu.h> | ||||||
|  | +#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS | ||||||
|  | +#include <linux/notifier.h> | ||||||
|  | +#endif | ||||||
|  |  #include <linux/kernel.h> | ||||||
|  |  #include <linux/netdevice.h> | ||||||
|  |  #include <linux/slab.h> | ||||||
|  | @@ -150,8 +153,15 @@ static int __nf_conntrack_eventmask_repo | ||||||
|  |  		rcu_read_unlock(); | ||||||
|  |  		return 0; | ||||||
|  |  	} | ||||||
|  | - | ||||||
|  | +#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS | ||||||
|  | +	ret = atomic_notifier_call_chain(&net->ct.nf_conntrack_chain, | ||||||
|  | +		events | missed, &item); | ||||||
|  | + | ||||||
|  | +	if (notify) | ||||||
|  | +	  ret = notify->ct_event(events | missed, item); | ||||||
|  | +#else | ||||||
|  |  	ret = notify->ct_event(events | missed, item); | ||||||
|  | +#endif | ||||||
|  |  	rcu_read_unlock(); | ||||||
|  |   | ||||||
|  |  	if (likely(ret >= 0 && missed == 0)) | ||||||
|  | @@ -245,7 +255,11 @@ void nf_ct_expect_event_report(enum ip_c | ||||||
|  |   | ||||||
|  |  	rcu_read_lock(); | ||||||
|  |  	notify = rcu_dereference(net->ct.nf_conntrack_event_cb); | ||||||
|  | +#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS | ||||||
|  | +	if (!notify && !rcu_dereference_raw(net->ct.nf_conntrack_chain.head)) | ||||||
|  | +#else | ||||||
|  |  	if (!notify) | ||||||
|  | +#endif | ||||||
|  |  		goto out_unlock; | ||||||
|  |   | ||||||
|  |  	e = nf_ct_ecache_find(exp->master); | ||||||
|  | @@ -264,6 +278,14 @@ out_unlock: | ||||||
|  |  	rcu_read_unlock(); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | +#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS | ||||||
|  | +int nf_conntrack_register_chain_notifier(struct net *net, struct notifier_block *nb) | ||||||
|  | +{ | ||||||
|  | +	return atomic_notifier_chain_register(&net->ct.nf_conntrack_chain, nb); | ||||||
|  | +} | ||||||
|  | +EXPORT_SYMBOL_GPL(nf_conntrack_register_chain_notifier); | ||||||
|  | +#endif | ||||||
|  | + | ||||||
|  |  void nf_conntrack_register_notifier(struct net *net, | ||||||
|  |  				    const struct nf_ct_event_notifier *new) | ||||||
|  |  { | ||||||
|  | @@ -278,6 +300,14 @@ void nf_conntrack_register_notifier(stru | ||||||
|  |  } | ||||||
|  |  EXPORT_SYMBOL_GPL(nf_conntrack_register_notifier); | ||||||
|  |   | ||||||
|  | +#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS | ||||||
|  | +int nf_conntrack_unregister_chain_notifier(struct net *net, struct notifier_block *nb) | ||||||
|  | +{ | ||||||
|  | +	return atomic_notifier_chain_unregister(&net->ct.nf_conntrack_chain, nb); | ||||||
|  | +} | ||||||
|  | +EXPORT_SYMBOL_GPL(nf_conntrack_unregister_chain_notifier); | ||||||
|  | +#endif | ||||||
|  | + | ||||||
|  |  void nf_conntrack_unregister_notifier(struct net *net) | ||||||
|  |  { | ||||||
|  |  	mutex_lock(&nf_ct_ecache_mutex); | ||||||
|  | --- a/include/net/netfilter/nf_conntrack_extend.h | ||||||
|  | +++ b/include/net/netfilter/nf_conntrack_extend.h | ||||||
|  | @@ -28,6 +28,10 @@ enum nf_ct_ext_id { | ||||||
|  |  #if IS_ENABLED(CONFIG_NETFILTER_SYNPROXY) | ||||||
|  |  	NF_CT_EXT_SYNPROXY, | ||||||
|  |  #endif | ||||||
|  | +#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT | ||||||
|  | +	NF_CT_EXT_DSCPREMARK,   /* QCA NSS ECM support */ | ||||||
|  | +#endif | ||||||
|  | + | ||||||
|  |  	NF_CT_EXT_NUM, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | @@ -40,6 +44,9 @@ enum nf_ct_ext_id { | ||||||
|  |  #define NF_CT_EXT_TIMEOUT_TYPE struct nf_conn_timeout | ||||||
|  |  #define NF_CT_EXT_LABELS_TYPE struct nf_conn_labels | ||||||
|  |  #define NF_CT_EXT_SYNPROXY_TYPE struct nf_conn_synproxy | ||||||
|  | +/* QCA NSS ECM support - Start */ | ||||||
|  | +#define NF_CT_EXT_DSCPREMARK_TYPE struct nf_ct_dscpremark_ext | ||||||
|  | +/* QCA NSS ECM support - End */ | ||||||
|  |   | ||||||
|  |  /* Extensions: optional stuff which isn't permanently in struct. */ | ||||||
|  |  struct nf_ct_ext { | ||||||
|  | --- 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 | ||||||
|  |   | ||||||
|  |  obj-$(CONFIG_NETFILTER) = netfilter.o | ||||||
|  |   | ||||||
| @@ -0,0 +1,577 @@ | |||||||
|  | From ac4b71aecf237fd07a29788706d198b4e36fa660 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Simon Casey <simon501098c@gmail.com> | ||||||
|  | Date: Wed, 2 Feb 2022 19:32:54 +0100 | ||||||
|  | Subject: [PATCH] Update 602-qca-add-pppoe-offload-support.patch for kernel | ||||||
|  |  5.15 | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  drivers/net/ppp/ppp_generic.c | 276 +++++++++++++++++++++++++++++++++- | ||||||
|  |  drivers/net/ppp/pppoe.c       |  82 +++++++++- | ||||||
|  |  include/linux/if_pppox.h      |  13 ++ | ||||||
|  |  include/linux/netdevice.h     |  19 +++ | ||||||
|  |  include/linux/ppp_channel.h   |  63 +++++++- | ||||||
|  |  5 files changed, 445 insertions(+), 8 deletions(-) | ||||||
|  |  | ||||||
|  | --- 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> | ||||||
|  | @@ -253,6 +254,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); | ||||||
|  | @@ -3450,7 +3470,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); | ||||||
|  | @@ -3482,13 +3505,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; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | @@ -3506,6 +3556,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); | ||||||
|  | @@ -3585,6 +3642,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); | ||||||
|  | @@ -3601,6 +3874,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 | ||||||
|  | @@ -93,4 +93,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 | ||||||
|  | @@ -2029,6 +2029,7 @@ struct net_device { | ||||||
|  |  	unsigned int		flags; | ||||||
|  |  	unsigned int		priv_flags; | ||||||
|  |  	unsigned int		extra_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 ppp_channel; | ||||||
|  |   | ||||||
|  | @@ -28,9 +32,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 { | ||||||
|  | @@ -74,6 +88,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,95 @@ | |||||||
|  | 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 605-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 | ||||||
|  | @@ -231,4 +231,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 | ||||||
|  | @@ -67,6 +67,15 @@ void br_fdb_update_unregister_notify(str | ||||||
|  |  EXPORT_SYMBOL_GPL(br_fdb_update_unregister_notify); | ||||||
|  |  /* QCA NSS ECM support - End */ | ||||||
|  |   | ||||||
|  | +/* QCA NSS bridge-mgr support - Start */ | ||||||
|  | +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 */ | ||||||
|  | + | ||||||
|  |  int __init br_fdb_init(void) | ||||||
|  |  { | ||||||
|  |  	br_fdb_cache = kmem_cache_create("bridge_fdb_cache", | ||||||
|  | @@ -372,7 +381,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 | ||||||
|  | @@ -400,12 +409,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); | ||||||
|  | @@ -623,6 +633,7 @@ void br_fdb_update(struct net_bridge *br | ||||||
|  |  		   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) | ||||||
|  | @@ -648,6 +659,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 = READ_ONCE(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 */ | ||||||
|  | @@ -659,7 +676,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,81 @@ | |||||||
|  | --- a/drivers/net/bonding/bond_main.c | ||||||
|  | +++ b/drivers/net/bonding/bond_main.c | ||||||
|  | @@ -249,6 +249,9 @@ static const struct flow_dissector_key f | ||||||
|  |  	}, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | +/* QCA NSS bonding support */ | ||||||
|  | +static unsigned long bond_id_mask = 0xFFFFFFF0; | ||||||
|  | + | ||||||
|  |  static struct flow_dissector flow_keys_bonding __read_mostly; | ||||||
|  |   | ||||||
|  |  /*-------------------------- Forward declarations ---------------------------*/ | ||||||
|  | @@ -4060,6 +4063,23 @@ static int bond_get_lowest_level_rcu(str | ||||||
|  |  } | ||||||
|  |  #endif | ||||||
|  |   | ||||||
|  | +/* QCA NSS bonding support */ | ||||||
|  | +int bond_get_id(struct net_device *bond_dev) | ||||||
|  | +{ | ||||||
|  | +    struct bonding *bond; | ||||||
|  | +    int bond_id = 0; | ||||||
|  | + | ||||||
|  | +    if (!((bond_dev->priv_flags & IFF_BONDING) && | ||||||
|  | +          (bond_dev->flags & IFF_MASTER))) | ||||||
|  | +        return -EINVAL; | ||||||
|  | + | ||||||
|  | +    bond = netdev_priv(bond_dev); | ||||||
|  | +    bond_id = bond->id; | ||||||
|  | + | ||||||
|  | +    return bond_id; | ||||||
|  | +} | ||||||
|  | +EXPORT_SYMBOL(bond_get_id); | ||||||
|  | + | ||||||
|  |  static void bond_get_stats(struct net_device *bond_dev, | ||||||
|  |  			   struct rtnl_link_stats64 *stats) | ||||||
|  |  { | ||||||
|  | @@ -5392,6 +5412,10 @@ static void bond_destructor(struct net_d | ||||||
|  |   | ||||||
|  |  	if (bond->rr_tx_counter) | ||||||
|  |  		free_percpu(bond->rr_tx_counter); | ||||||
|  | + | ||||||
|  | +    /* QCA NSS bonding support */ | ||||||
|  | +    if (bond->id != (~0U)) | ||||||
|  | +        clear_bit(bond->id, &bond_id_mask); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  void bond_setup(struct net_device *bond_dev) | ||||||
|  | @@ -5969,7 +5993,14 @@ int bond_create(struct net *net, const c | ||||||
|  |   | ||||||
|  |  	bond_work_init_all(bond); | ||||||
|  |   | ||||||
|  | -	rtnl_unlock(); | ||||||
|  | +    /* QCA NSS bonding support */ | ||||||
|  | +    bond->id = ~0U; | ||||||
|  | +    if (bond_id_mask != (~0UL)) { | ||||||
|  | +        bond->id = (u32)ffz(bond_id_mask); | ||||||
|  | +        set_bit(bond->id, &bond_id_mask);     | ||||||
|  | +    } | ||||||
|  | + | ||||||
|  | +    rtnl_unlock(); | ||||||
|  |  	return 0; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | --- a/include/net/bonding.h | ||||||
|  | +++ b/include/net/bonding.h | ||||||
|  | @@ -255,6 +255,7 @@ struct bonding { | ||||||
|  |  	spinlock_t ipsec_lock; | ||||||
|  |  #endif /* CONFIG_XFRM_OFFLOAD */ | ||||||
|  |  	struct bpf_prog *xdp_prog; | ||||||
|  | +    u32    id; /* QCA NSS bonding */ | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  #define bond_slave_get_rcu(dev) \ | ||||||
|  | @@ -629,6 +630,7 @@ struct bond_net { | ||||||
|  |   | ||||||
|  |  int bond_arp_rcv(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 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); | ||||||
| @@ -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; | ||||||
|  | @@ -34,6 +41,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, | ||||||
|  | @@ -106,4 +114,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 | ||||||
|  | @@ -930,6 +930,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); | ||||||
|  | +	stats->rx_packets += offl_stats->rx_packets; | ||||||
|  | +	stats->rx_bytes += offl_stats->rx_bytes; | ||||||
|  | +	stats->tx_packets += offl_stats->tx_packets; | ||||||
|  | +	stats->tx_bytes += offl_stats->tx_bytes; | ||||||
|  | +	/* Update multicast statistics */ | ||||||
|  | +	if (unlikely(update_mcast_rx_stats)) { | ||||||
|  | +		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) | ||||||
|  |  { | ||||||
|  | @@ -1465,6 +1493,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]) | ||||||
		Reference in New Issue
	
	Block a user
	 ACwifidude
					ACwifidude