diff --git a/target/linux/ipq806x/patches-6.6/990-000-Add-required-entries-in-dts-files-for-NSS-support.patch b/target/linux/ipq806x/patches-6.6/990-000-Add-required-entries-in-dts-files-for-NSS-support.patch new file mode 100644 index 0000000000..ab0a97c2a6 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-000-Add-required-entries-in-dts-files-for-NSS-support.patch @@ -0,0 +1,283 @@ +--- a/arch/arm/boot/dts/qcom/qcom-ipq8064-v2.0.dtsi ++++ b/arch/arm/boot/dts/qcom/qcom-ipq8064-v2.0.dtsi +@@ -22,6 +22,15 @@ + reg = <0x41200000 0x300000>; + no-map; + }; ++ ++ ramoops@42100000 { ++ compatible = "ramoops"; ++ reg = <0x42100000 0x40000>; ++ record-size = <0x4000>; ++ console-size = <0x4000>; ++ ftrace-size = <0x4000>; ++ pmsg-size = <0x4000>; ++ }; + }; + }; + +--- a/arch/arm/boot/dts/qcom/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom/qcom-ipq8064.dtsi +@@ -670,6 +670,20 @@ + }; + }; + ++ nss-common { ++ compatible = "qcom,nss-common"; ++ reg = <0x03000000 0x00001000>; ++ reg-names = "nss_fpb_base"; ++ clocks = <&gcc NSS_CORE_CLK>, <&gcc NSSTCM_CLK>, ++ <&rpmcc RPM_NSS_FABRIC_0_CLK>, <&rpmcc RPM_NSS_FABRIC_1_CLK>; ++ clock-names = "nss_core_clk", "nss_tcm_clk", ++ "nss-fab0-clk", "nss-fab1-clk"; ++ nss_core-supply = <&smb208_s1b>; ++ nss_core_vdd_nominal = <1100000>; ++ nss_core_vdd_high = <1150000>; ++ nss_core_threshold_freq = <733000000>; ++ }; ++ + sfpb_mutex: hwlock@1200600 { + compatible = "qcom,sfpb-mutex"; + reg = <0x01200600 0x100>; +@@ -856,6 +870,132 @@ + status = "disabled"; + }; + ++ nss0: nss@40000000 { ++ compatible = "qcom,nss"; ++ qcom,low-frequency = <733000000>; /* orig value 110000000 */ ++ qcom,mid-frequency = <733000000>; /* orig value 550000000 */ ++ qcom,max-frequency = <733000000>; ++ ++ interrupts = , ++ ; ++ reg = <0x36000000 0x1000 0x39000000 0x10000>; ++ reg-names = "nphys", "vphys"; ++ clocks = <&gcc NSS_CORE_CLK>, <&gcc NSSTCM_CLK_SRC>, ++ <&gcc NSSTCM_CLK>, <&rpmcc RPM_NSS_FABRIC_0_CLK>, ++ <&rpmcc RPM_NSS_FABRIC_1_CLK>; ++ clock-names = "nss-core-clk", "nss-tcm-src", ++ "nss-tcm-clk", "nss-fab0-clk", ++ "nss-fab1-clk"; ++ resets = <&gcc UBI32_CORE1_CLKRST_CLAMP_RESET>, ++ <&gcc UBI32_CORE1_CLAMP_RESET>, ++ <&gcc UBI32_CORE1_AHB_RESET>, ++ <&gcc UBI32_CORE1_AXI_RESET>; ++ reset-names = "clkrst-clamp", "clamp", "ahb", "axi"; ++ ++ qcom,id = <0>; ++ qcom,num-irq = <2>; ++ qcom,num-queue = <2>; ++ qcom,load-addr = <0x40000000>; ++ qcom,turbo-frequency; ++ ++ qcom,bridge-enabled; ++ qcom,gre-enabled; ++ qcom,gre-redir-enabled; ++ qcom,gre_tunnel_enabled; ++ qcom,ipv4-enabled; ++ qcom,ipv4-reasm-enabled; ++ qcom,ipv6-enabled; ++ qcom,ipv6-reasm-enabled; ++ qcom,l2tpv2-enabled; ++ qcom,map-t-enabled; ++ qcom,pppoe-enabled; ++ qcom,pptp-enabled; ++ qcom,portid-enabled; ++ qcom,shaping-enabled; ++ qcom,tun6rd-enabled; ++ qcom,tunipip6-enabled; ++ qcom,vlan-enabled; ++ qcom,wlan-dataplane-offload-enabled; ++ qcom,wlanredirect-enabled; ++ qcom,pxvlan-enabled; ++ qcom,vxlan-enabled; ++ qcom,match-enabled; ++ qcom,mirror-enabled; ++ qcom,rmnet-enabled; ++ qcom,clmap-enabled; ++ }; ++ ++ nss1: nss@40800000 { ++ compatible = "qcom,nss"; ++ qcom,low-frequency = <733000000>; /* orig value 110000000 */ ++ qcom,mid-frequency = <733000000>; /* orig value 550000000 */ ++ qcom,max-frequency = <733000000>; ++ ++ interrupts = , ++ ; ++ reg = <0x36400000 0x1000 0x39010000 0x10000>; ++ reg-names = "nphys", "vphys"; ++ resets = <&gcc UBI32_CORE2_CLKRST_CLAMP_RESET>, ++ <&gcc UBI32_CORE2_CLAMP_RESET>, ++ <&gcc UBI32_CORE2_AHB_RESET>, ++ <&gcc UBI32_CORE2_AXI_RESET>; ++ reset-names = "clkrst-clamp", "clamp", "ahb", "axi"; ++ ++ qcom,id = <1>; ++ qcom,num-irq = <2>; ++ qcom,load-addr = <0x40800000>; ++ qcom,num-queue = <2>; ++ qcom,turbo-frequency; ++ ++ qcom,capwap-enabled; ++ qcom,crypto-enabled; ++ qcom,dtls-enabled; ++ qcom,ipsec-enabled; ++ }; ++ ++ crypto1: crypto@38000000 { ++ compatible = "qcom,nss-crypto"; ++ reg = <0x38000000 0x20000>, <0x38004000 0x22000>; ++ reg-names = "crypto_pbase", "bam_base"; ++ clocks = <&gcc CE5_CORE_CLK>, <&gcc CE5_A_CLK>, <&gcc CE5_H_CLK>; ++ clock-names = "ce5_core", "ce5_aclk", "ce5_hclk"; ++ resets = <&gcc CRYPTO_ENG1_RESET>, <&gcc CRYPTO_AHB_RESET>; ++ reset-names = "rst_eng", "rst_ahb"; ++ qcom,id = <0>; ++ qcom,ee = <0>; ++ }; ++ ++ crypto2: crypto@38400000 { ++ compatible = "qcom,nss-crypto"; ++ reg = <0x38400000 0x20000>, <0x38404000 0x22000>; ++ reg-names = "crypto_pbase", "bam_base"; ++ resets = <&gcc CRYPTO_ENG2_RESET>; ++ reset-names = "rst_eng"; ++ qcom,id = <1>; ++ qcom,ee = <0>; ++ }; ++ ++ crypto3: crypto@38800000 { ++ compatible = "qcom,nss-crypto"; ++ reg = <0x38800000 0x20000>, <0x38804000 0x22000>; ++ reg-names = "crypto_pbase", "bam_base"; ++ resets = <&gcc CRYPTO_ENG3_RESET>; ++ reset-names = "rst_eng"; ++ qcom,id = <2>; ++ qcom,ee = <0>; ++ }; ++ ++ crypto4: crypto@38c00000 { ++ compatible = "qcom,nss-crypto"; ++ reg = <0x38c00000 0x20000>, <0x38c04000 0x22000>; ++ reg-names = "crypto_pbase", "bam_base"; ++ resets = <&gcc CRYPTO_ENG4_RESET>; ++ reset-names = "rst_eng"; ++ qcom,id = <3>; ++ qcom,ee = <0>; ++ }; ++ ++ + sdcc3bam: dma-controller@12182000 { + compatible = "qcom,bam-v1.3.0"; + reg = <0x12182000 0x8000>; +@@ -1462,6 +1602,12 @@ + status = "disabled"; + }; + ++ nss-gmac-common { ++ compatible = "qcom,nss-gmac-common"; ++ reg = <0x03000000 0x0000FFFF 0x1bb00000 0x0000FFFF 0x00900000 0x00004000>; ++ reg-names = "nss_reg_base", "qsgmii_reg_base", "clk_ctl_base"; ++ }; ++ + gmac0: ethernet@37000000 { + device_type = "network"; + compatible = "qcom,ipq806x-gmac", "snps,dwmac"; +@@ -1559,3 +1705,27 @@ + }; + }; + }; ++ ++ &gmac1 { ++ compatible = "qcom,nss-gmac"; ++ qcom,id = <0>; ++ qcom,pcs-chanid = <0>; ++ qcom,phy_mii_type = <0>; ++ qcom,emulation = <0>; ++ qcom,forced-speed = <1000>; ++ qcom,forced-duplex = <1>; ++ qcom,socver = <0>; ++ mdiobus = <&mdio0>; ++ }; ++ ++ &gmac2 { ++ compatible = "qcom,nss-gmac"; ++ qcom,id = <1>; ++ qcom,pcs-chanid = <1>; ++ qcom,phy_mii_type = <1>; ++ qcom,emulation = <0>; ++ qcom,forced-speed = <1000>; ++ qcom,forced-duplex = <1>; ++ qcom,socver = <0>; ++ mdiobus = <&mdio0>; ++ }; +--- a/arch/arm/boot/dts/qcom/qcom-ipq8065-nighthawk.dtsi ++++ b/arch/arm/boot/dts/qcom/qcom-ipq8065-nighthawk.dtsi +@@ -360,13 +360,23 @@ + &gmac1 { + status = "okay"; + ++ compatible = "qcom,nss-gmac"; ++ reg = <0x37200000 0x200000>; ++ interrupts = ; + phy-mode = "rgmii"; + qcom,id = <1>; ++ qcom,pcs-chanid = <0>; + qcom,phy_mdio_addr = <4>; + qcom,poll_required = <0>; + qcom,rgmii_delay = <1>; ++ qcom,phy-mdio-addr = <4>; ++ qcom,poll-required = <0>; ++ qcom,rgmii-delay = <1>; + qcom,phy_mii_type = <0>; + qcom,emulation = <0>; ++ qcom,forced-speed = <1000>; ++ qcom,forced-duplex = <1>; ++ qcom,socver = <0>; + qcom,irq = <255>; + mdiobus = <&mdio0>; + +@@ -385,13 +395,23 @@ + &gmac2 { + status = "okay"; + ++ compatible = "qcom,nss-gmac"; ++ reg = <0x37400000 0x200000>; ++ interrupts = ; + phy-mode = "sgmii"; + qcom,id = <2>; ++ qcom,pcs-chanid = <1>; + qcom,phy_mdio_addr = <0>; /* none */ + qcom,poll_required = <0>; /* no polling */ + qcom,rgmii_delay = <0>; ++ qcom,phy-mdio-addr = <0>; ++ qcom,poll-required = <0>; ++ qcom,rgmii-delay = <0>; + qcom,phy_mii_type = <1>; + qcom,emulation = <0>; ++ qcom,forced-speed = <1000>; ++ qcom,forced-duplex = <1>; ++ qcom,socver = <0>; + qcom,irq = <258>; + mdiobus = <&mdio0>; + +--- a/arch/arm/boot/dts/qcom/qcom-ipq8065.dtsi ++++ b/arch/arm/boot/dts/qcom/qcom-ipq8065.dtsi +@@ -95,3 +95,15 @@ + opp-level = <2>; + }; + }; ++ ++ &nss0 { ++ qcom,low-frequency = <110000000>; ++ qcom,mid-frequency = <600000000>; ++ qcom,max-frequency = <800000000>; ++ }; ++ ++ &nss1 { ++ qcom,low-frequency = <110000000>; ++ qcom,mid-frequency = <600000000>; ++ qcom,max-frequency = <800000000>; ++ }; diff --git a/target/linux/ipq806x/patches-6.6/990-001-Add-required-driver-files-for-NSS-support.patch b/target/linux/ipq806x/patches-6.6/990-001-Add-required-driver-files-for-NSS-support.patch new file mode 100644 index 0000000000..7c2839469c --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-001-Add-required-driver-files-for-NSS-support.patch @@ -0,0 +1,643 @@ +--- /dev/null ++++ b/include/net/netfilter/nf_conntrack_dscpremark_ext.h +@@ -0,0 +1,98 @@ ++/* ++ ************************************************************************** ++ * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. ++ * Copyright (c) 2023, Qualcomm Innovation Center, Inc. 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. ++ ************************************************************************** ++ */ ++ ++/* DSCP remark conntrack extension APIs. */ ++ ++#ifndef _NF_CONNTRACK_DSCPREMARK_H ++#define _NF_CONNTRACK_DSCPREMARK_H ++#include ++#include ++ ++/* Rule flags */ ++#define NF_CT_DSCPREMARK_EXT_DSCP_RULE_VALID 0x1 ++ ++/* Rule validity */ ++#define NF_CT_DSCPREMARK_EXT_RULE_VALID 0x1 ++#define NF_CT_DSCPREMARK_EXT_RULE_NOT_VALID 0x0 ++ ++/* Which QoS features are set flags */ ++#define NF_CT_DSCPREMARK_EXT_PRIO 0x1 ++#define NF_CT_DSCPREMARK_EXT_DSCP 0x2 ++#define NF_CT_DSCPREMARK_EXT_IGS_QOS 0x4 ++#define NF_CT_DSCPREMARK_EXT_MARK 0x8 ++#define NF_CT_DSCPREMARK_EXT_SAWF 0x10 ++ ++/* ++ * DSCP remark conntrack extension structure. ++ */ ++struct nf_ct_dscpremark_ext { ++ __u32 flow_priority; /* Original direction packet priority */ ++ __u32 reply_priority; /* Reply direction packet priority */ ++ __u32 flow_mark; /* Original direction packet mark */ ++ __u32 reply_mark; /* Reply direction packet mark */ ++ __u32 flow_sawf_meta; /* Original direction SAWF meta info */ ++ __u32 return_sawf_meta; /* Reply direction SAWF meta info */ ++ __u16 igs_flow_qos_tag; /* Original direction ingress packet priority */ ++ __u16 igs_reply_qos_tag; /* Reply direction ingress packet priority */ ++ __u8 flow_dscp; /* IP DSCP value for original direction */ ++ __u8 reply_dscp; /* IP DSCP value for reply direction */ ++ __u16 rule_flags; /* Rule Validity flags */ ++ __u16 flow_set_flags; /* Original direction set flags */ ++ __u16 return_set_flags; /* Reply direction set flags */ ++}; ++ ++/* ++ * nf_ct_dscpremark_ext_find() ++ * Finds the extension data of the conntrack entry if it exists. ++ */ ++static inline struct nf_ct_dscpremark_ext * ++nf_ct_dscpremark_ext_find(const struct nf_conn *ct) ++{ ++#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT ++ return nf_ct_ext_find(ct, NF_CT_EXT_DSCPREMARK); ++#else ++ return NULL; ++#endif ++} ++ ++/* ++ * nf_ct_dscpremark_ext_add() ++ * Adds the extension data to the conntrack entry. ++ */ ++static inline ++struct nf_ct_dscpremark_ext *nf_ct_dscpremark_ext_add(struct nf_conn *ct, ++ gfp_t gfp) ++{ ++#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT ++ struct nf_ct_dscpremark_ext *ncde; ++ ++ ncde = nf_ct_ext_add(ct, NF_CT_EXT_DSCPREMARK, gfp); ++ if (!ncde) ++ return NULL; ++ ++ return ncde; ++#else ++ return NULL; ++#endif ++}; ++ ++#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT ++extern int nf_conntrack_dscpremark_ext_set_dscp_rule_valid(struct nf_conn *ct); ++extern int nf_conntrack_dscpremark_ext_get_dscp_rule_validity(struct nf_conn *ct); ++#endif /* CONFIG_NF_CONNTRACK_DSCPREMARK_EXT */ ++ ++#endif /* _NF_CONNTRACK_DSCPREMARK_H */ +--- /dev/null ++++ b/net/netfilter/nf_conntrack_dscpremark_ext.c +@@ -0,0 +1,62 @@ ++/* ++ ************************************************************************** ++ * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. ++ * Copyright (c) 2023, Qualcomm Innovation Center, Inc. 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. ++ ************************************************************************** ++ */ ++ ++/* DSCP remark handling conntrack extension registration. */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* nf_conntrack_dscpremark_ext_set_dscp_rule_valid() ++ * Set DSCP rule validity flag in the extension ++ */ ++int nf_conntrack_dscpremark_ext_set_dscp_rule_valid(struct nf_conn *ct) ++{ ++ struct nf_ct_dscpremark_ext *ncde; ++ ++ ncde = nf_ct_dscpremark_ext_find(ct); ++ if (!ncde) ++ return -1; ++ ++ ncde->rule_flags = NF_CT_DSCPREMARK_EXT_DSCP_RULE_VALID; ++ return 0; ++} ++EXPORT_SYMBOL(nf_conntrack_dscpremark_ext_set_dscp_rule_valid); ++ ++/* nf_conntrack_dscpremark_ext_get_dscp_rule_validity() ++ * Check if the DSCP rule flag is valid from the extension ++ */ ++int nf_conntrack_dscpremark_ext_get_dscp_rule_validity(struct nf_conn *ct) ++{ ++ struct nf_ct_dscpremark_ext *ncde; ++ ++ ncde = nf_ct_dscpremark_ext_find(ct); ++ if (!ncde) ++ return NF_CT_DSCPREMARK_EXT_RULE_NOT_VALID; ++ ++ if (ncde->rule_flags & NF_CT_DSCPREMARK_EXT_DSCP_RULE_VALID) ++ return NF_CT_DSCPREMARK_EXT_RULE_VALID; ++ ++ return NF_CT_DSCPREMARK_EXT_RULE_NOT_VALID; ++} ++EXPORT_SYMBOL(nf_conntrack_dscpremark_ext_get_dscp_rule_validity); +--- /dev/null ++++ b/include/soc/qcom/socinfo.h +@@ -0,0 +1,435 @@ ++/* Copyright (c) 2009-2014, 2016, 2020, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef _ARCH_ARM_MACH_MSM_SOCINFO_H_ ++#define _ARCH_ARM_MACH_MSM_SOCINFO_H_ ++ ++#include ++ ++#define CPU_IPQ4018 272 ++#define CPU_IPQ4019 273 ++#define CPU_IPQ4028 287 ++#define CPU_IPQ4029 288 ++ ++#define CPU_IPQ8062 201 ++#define CPU_IPQ8064 202 ++#define CPU_IPQ8066 203 ++#define CPU_IPQ8065 280 ++#define CPU_IPQ8068 204 ++#define CPU_IPQ8069 281 ++ ++#define CPU_IPQ8074 323 ++#define CPU_IPQ8072 342 ++#define CPU_IPQ8076 343 ++#define CPU_IPQ8078 344 ++#define CPU_IPQ8070 375 ++#define CPU_IPQ8071 376 ++ ++#define CPU_IPQ8072A 389 ++#define CPU_IPQ8074A 390 ++#define CPU_IPQ8076A 391 ++#define CPU_IPQ8078A 392 ++#define CPU_IPQ8070A 395 ++#define CPU_IPQ8071A 396 ++ ++#define CPU_IPQ8172 397 ++#define CPU_IPQ8173 398 ++#define CPU_IPQ8174 399 ++ ++#define CPU_IPQ6018 402 ++#define CPU_IPQ6028 403 ++#define CPU_IPQ6000 421 ++#define CPU_IPQ6010 422 ++#define CPU_IPQ6005 453 ++ ++/* TBD the CHIP IDs */ ++#define CPU_IPQ5000 425 ++#define CPU_IPQ5010 426 ++#define CPU_IPQ5018 427 ++ ++static inline const int* read_ipq_soc_version_major(void) ++{ ++ const int *prop; ++ prop = of_get_property(of_find_node_by_path("/"), "soc_version_major", ++ NULL); ++ ++ return prop; ++} ++ ++static inline int read_ipq_cpu_type(void) ++{ ++ const int *prop; ++ prop = of_get_property(of_find_node_by_path("/"), "cpu_type", NULL); ++ /* ++ * Return Default CPU type if "cpu_type" property is not found in DTSI ++ */ ++ if (!prop) ++ return CPU_IPQ8064; ++ return *prop; ++} ++ ++static inline int cpu_is_ipq4018(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ4018; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq4019(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ4019; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq4028(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ4028; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq4029(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ4029; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq40xx(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq4018() || cpu_is_ipq4019() || ++ cpu_is_ipq4028() || cpu_is_ipq4029(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8062(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8062; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8064(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8064; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8066(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8066; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8068(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8068; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8065(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8065; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8069(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8069; ++#else ++ return 0; ++#endif ++} ++static inline int cpu_is_ipq806x(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq8062() || cpu_is_ipq8064() || ++ cpu_is_ipq8066() || cpu_is_ipq8068() || ++ cpu_is_ipq8065() || cpu_is_ipq8069(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8070(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8070; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8071(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8071; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8072(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8072; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8074(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8074; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8076(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8076; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8078(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8078; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8072a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8072A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8074a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8074A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8076a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8076A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8078a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8078A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8070a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8070A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8071a(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8071A; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8172(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8172; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8173(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8173; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq8174(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ8174; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq6018(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ6018; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq6028(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ6028; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq6000(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ6000; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq6010(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ6010; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq6005(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ6005; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq5000(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ5000; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq5010(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ5010; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq5018(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return read_ipq_cpu_type() == CPU_IPQ5018; ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq807x(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq8072() || cpu_is_ipq8074() || ++ cpu_is_ipq8076() || cpu_is_ipq8078() || ++ cpu_is_ipq8070() || cpu_is_ipq8071() || ++ cpu_is_ipq8072a() || cpu_is_ipq8074a() || ++ cpu_is_ipq8076a() || cpu_is_ipq8078a() || ++ cpu_is_ipq8070a() || cpu_is_ipq8071a() || ++ cpu_is_ipq8172() || cpu_is_ipq8173() || ++ cpu_is_ipq8174(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq60xx(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq6018() || cpu_is_ipq6028() || ++ cpu_is_ipq6000() || cpu_is_ipq6010() || ++ cpu_is_ipq6005(); ++#else ++ return 0; ++#endif ++} ++ ++static inline int cpu_is_ipq50xx(void) ++{ ++#ifdef CONFIG_ARCH_QCOM ++ return cpu_is_ipq5000() || cpu_is_ipq5010() || ++ cpu_is_ipq5018(); ++#else ++ return 0; ++#endif ++} ++ ++#endif /* _ARCH_ARM_MACH_MSM_SOCINFO_H_ */ +--- /dev/null ++++ b/include/uapi/linux/tc_act/tc_nss_mirred.h +@@ -0,0 +1,36 @@ ++#ifndef __LINUX_TC_NSS_MIRRED_H ++#define __LINUX_TC_NSS_MIRRED_H ++ ++#include ++ ++/* ++ * Type of nss mirred action. ++ */ ++#define TCA_ACT_MIRRED_NSS 17 ++ ++/* ++ * Types of parameters for nss mirred action. ++ */ ++enum { ++ TC_NSS_MIRRED_UNSPEC, ++ TC_NSS_MIRRED_TM, ++ TC_NSS_MIRRED_PARMS, ++ __TC_NSS_MIRRED_MAX ++}; ++#define TC_NSS_MIRRED_MAX (__TC_NSS_MIRRED_MAX - 1) ++ ++/* ++ * tc_nss_mirred ++ * tc command structure for nss mirred action. ++ */ ++struct tc_nss_mirred { ++ tc_gen; /* General tc structure. */ ++ __u32 from_ifindex; /* ifindex of the port from which traffic ++ * will be redirected. ++ */ ++ __u32 to_ifindex; /* ifindex of the port to which traffic ++ * will be redirected. ++ */ ++}; ++ ++#endif /* __LINUX_TC_NSS_MIRRED_H */ diff --git a/target/linux/ipq806x/patches-6.6/990-002-Fix-missing-smb208-v1.0-nss-regulator.patch b/target/linux/ipq806x/patches-6.6/990-002-Fix-missing-smb208-v1.0-nss-regulator.patch new file mode 100644 index 0000000000..2e6b9bc7ce --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-002-Fix-missing-smb208-v1.0-nss-regulator.patch @@ -0,0 +1,30 @@ +--- a/arch/arm/boot/dts/qcom/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom/qcom-ipq8064.dtsi +@@ -1706,6 +1706,27 @@ + }; + }; + ++ &rpm { ++ smb208_regulators: regulators { ++ compatible = "qcom,rpm-smb208-regulators"; ++ ++ smb208_s1a: s1a { ++ regulator-min-microvolt = <1050000>; ++ regulator-max-microvolt = <1150000>; ++ ++ qcom,switch-mode-frequency = <1200000>; ++ }; ++ ++ smb208_s1b: s1b { ++ regulator-min-microvolt = <1050000>; ++ regulator-max-microvolt = <1150000>; ++ ++ qcom,switch-mode-frequency = <1200000>; ++ }; ++ ++ }; ++ }; ++ + &gmac1 { + compatible = "qcom,nss-gmac"; + qcom,id = <0>; diff --git a/target/linux/ipq806x/patches-6.6/990-003-Regulator-Add-NSS-VOLT.patch b/target/linux/ipq806x/patches-6.6/990-003-Regulator-Add-NSS-VOLT.patch new file mode 100644 index 0000000000..c784ebc4f6 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-003-Regulator-Add-NSS-VOLT.patch @@ -0,0 +1,210 @@ +From c70758d96b22e4421a6afd824cb59e350c6a8040 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Tue, 2 Jun 2020 22:09:15 +0200 +Subject: [PATCH] Regulator: Add NSS VOLT + +Signed-off-by: Robert Marko +--- + drivers/regulator/Kconfig | 7 +++++++ + drivers/regulator/Makefile | 1 + + 2 files changed, 8 insertions(+) + +--- a/drivers/regulator/Kconfig ++++ b/drivers/regulator/Kconfig +@@ -1663,4 +1663,11 @@ 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 +@@ -195,5 +195,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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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; ++ } ++ ++ if (rate >= data->nss_core_threshold_freq) ++ uV = data->nss_core_vdd_high; ++ else ++ uV = data->nss_core_vdd_nominal; ++ ++ 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); +--- /dev/null ++++ b/include/linux/regulator/nss-volt-ipq806x.h +@@ -0,0 +1,24 @@ ++/* ++ * 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 ++ ++int nss_ramp_voltage(unsigned long rate, bool ramp_up); ++ ++#endif diff --git a/target/linux/ipq806x/patches-6.6/990-004-nss-core-and-crypto-clocks.patch b/target/linux/ipq806x/patches-6.6/990-004-nss-core-and-crypto-clocks.patch new file mode 100644 index 0000000000..9e15c400fd --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-004-nss-core-and-crypto-clocks.patch @@ -0,0 +1,237 @@ +refs: linux-msm +db30db7d342f +42924e528bb6 +--- 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 ++ ++/* NSS safe parent index which will be used during NSS PLL rate change */ ++static int gcc_ipq806x_nss_safe_parent; + + static const struct clk_parent_data gcc_pxo[] = { + { .fw_name = "pxo", .name = "pxo" }, +@@ -2825,7 +2829,8 @@ static struct clk_dyn_rcg ubi32_core1_sr + .parent_data = gcc_pxo_pll8_pll14_pll18_pll0, + .num_parents = ARRAY_SIZE(gcc_pxo_pll8_pll14_pll18_pll0), + .ops = &clk_dyn_rcg_ops, +- .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, ++ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE | ++ CLK_IGNORE_UNUSED, + }, + }, + }; +@@ -2878,7 +2883,8 @@ static struct clk_dyn_rcg ubi32_core2_sr + .parent_data = gcc_pxo_pll8_pll14_pll18_pll0, + .num_parents = ARRAY_SIZE(gcc_pxo_pll8_pll14_pll18_pll0), + .ops = &clk_dyn_rcg_ops, +- .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, ++ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE | ++ CLK_IGNORE_UNUSED, + }, + }, + }; +@@ -3063,6 +3069,145 @@ 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 struct clk_hw *nss_core_clk_get_safe_parent(struct clk_hw *hw) ++{ ++ return clk_hw_get_parent_by_index(hw, gcc_ipq806x_nss_safe_parent); ++} ++ ++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, ++ .get_safe_parent = nss_core_clk_get_safe_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_data = 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, +@@ -3182,6 +3327,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, +@@ -3402,6 +3548,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 +@@ -818,6 +818,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 +@@ -191,4 +191,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 diff --git a/target/linux/ipq806x/patches-6.6/990-005-CLK-Add-safe-switch-hook.patch b/target/linux/ipq806x/patches-6.6/990-005-CLK-Add-safe-switch-hook.patch new file mode 100644 index 0000000000..79bcb7df4c --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-005-CLK-Add-safe-switch-hook.patch @@ -0,0 +1,149 @@ +From a4851511e1d8e61a14c6f2345354c059cb964e6a Mon Sep 17 00:00:00 2001 +From: regae <37059068+regae@users.noreply.github.com> +Date: Sat, 15 Jun 2024 19:16:36 +0700 +Subject: [PATCH 10/13] OpenWrt: 136-clk-Add-safe-switch-hook.patch + +Sometimes clocks can't accept their parent source turning off +while the source is reprogrammed to a different rate. Most +notably CPU clocks require a way to switch away from the current +PLL they're running on, reprogram that PLL to a new rate, and +then switch back to the PLL with the new rate once they're done. +Add a hook that drivers can implement allowing them to return a +'safe parent' that they can switch their parent to while the +upstream source is reprogrammed to support this. + +--- + drivers/clk/clk.c | 61 +++++++++++++++++++++++++++++++----- + include/linux/clk-provider.h | 1 + + 2 files changed, 54 insertions(+), 8 deletions(-) + +--- a/drivers/clk/clk.c ++++ b/drivers/clk/clk.c +@@ -69,9 +69,12 @@ struct clk_core { + struct clk_parent_map *parents; + u8 num_parents; + u8 new_parent_index; ++ u8 safe_parent_index; + unsigned long rate; + unsigned long req_rate; ++ unsigned long old_rate; + unsigned long new_rate; ++ struct clk_core *safe_parent; + struct clk_core *new_parent; + struct clk_core *new_child; + unsigned long flags; +@@ -2240,7 +2243,8 @@ out: + static void clk_calc_subtree(struct clk_core *core, unsigned long new_rate, + struct clk_core *new_parent, u8 p_index) + { +- struct clk_core *child; ++ struct clk_core *child, *parent; ++ struct clk_hw *parent_hw; + + core->new_rate = new_rate; + core->new_parent = new_parent; +@@ -2250,6 +2254,18 @@ static void clk_calc_subtree(struct clk_ + if (new_parent && new_parent != core->parent) + new_parent->new_child = core; + ++ if (core->ops->get_safe_parent) { ++ parent_hw = core->ops->get_safe_parent(core->hw); ++ if (parent_hw) { ++ parent = parent_hw->core; ++ p_index = clk_fetch_parent_index(core, parent); ++ core->safe_parent_index = p_index; ++ core->safe_parent = parent; ++ } ++ } else { ++ core->safe_parent = NULL; ++ } ++ + hlist_for_each_entry(child, &core->children, child_node) { + child->new_rate = clk_recalc(child, new_rate); + clk_calc_subtree(child, child->new_rate, NULL, 0); +@@ -2351,14 +2367,43 @@ static struct clk_core *clk_propagate_ra + unsigned long event) + { + struct clk_core *child, *tmp_clk, *fail_clk = NULL; ++ struct clk_core *old_parent; + int ret = NOTIFY_DONE; + +- if (core->rate == core->new_rate) ++ if (core->rate == core->new_rate && event != POST_RATE_CHANGE) + return NULL; + ++ switch (event) { ++ case PRE_RATE_CHANGE: ++ if (core->safe_parent) ++ core->ops->set_parent(core->hw, core->safe_parent_index); ++ core->old_rate = core->rate; ++ break; ++ case POST_RATE_CHANGE: ++ if (core->safe_parent) { ++ old_parent = __clk_set_parent_before(core, ++ core->new_parent); ++ if (core->ops->set_rate_and_parent) { ++ core->ops->set_rate_and_parent(core->hw, ++ core->new_rate, ++ core->new_parent ? ++ core->new_parent->rate : 0, ++ core->new_parent_index); ++ } else if (core->ops->set_parent) { ++ core->ops->set_parent(core->hw, ++ core->new_parent_index); ++ } ++ __clk_set_parent_after(core, core->new_parent, ++ old_parent); ++ } ++ break; ++ } ++ + if (core->notifier_count) { +- ret = __clk_notify(core, event, core->rate, core->new_rate); +- if (ret & NOTIFY_STOP_MASK) ++ if (event != POST_RATE_CHANGE || core->old_rate != core->rate) ++ ret = __clk_notify(core, event, core->old_rate, ++ core->new_rate); ++ if (ret & NOTIFY_STOP_MASK && event != POST_RATE_CHANGE) + fail_clk = core; + } + +@@ -2413,7 +2458,8 @@ static void clk_change_rate(struct clk_c + clk_core_enable_lock(core); + } + +- if (core->new_parent && core->new_parent != core->parent) { ++ if (core->new_parent && core->new_parent != core->parent && ++ !core->safe_parent) { + old_parent = __clk_set_parent_before(core, core->new_parent); + trace_clk_set_parent(core, core->new_parent); + +@@ -2450,9 +2496,6 @@ static void clk_change_rate(struct clk_c + if (core->flags & CLK_OPS_PARENT_ENABLE) + clk_core_disable_unprepare(parent); + +- if (core->notifier_count && old_rate != core->rate) +- __clk_notify(core, POST_RATE_CHANGE, old_rate, core->rate); +- + if (core->flags & CLK_RECALC_NEW_RATES) + (void)clk_calc_new_rates(core, core->new_rate); + +@@ -2547,6 +2590,8 @@ static int clk_core_set_rate_nolock(stru + clk_change_rate(top); + + core->req_rate = req_rate; ++ clk_propagate_rate_change(top, POST_RATE_CHANGE); ++ + err: + clk_pm_runtime_put(core); + +--- a/include/linux/clk-provider.h ++++ b/include/linux/clk-provider.h +@@ -250,6 +250,7 @@ struct clk_ops { + struct clk_rate_request *req); + int (*set_parent)(struct clk_hw *hw, u8 index); + u8 (*get_parent)(struct clk_hw *hw); ++ struct clk_hw *(*get_safe_parent)(struct clk_hw *hw); + int (*set_rate)(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate); + int (*set_rate_and_parent)(struct clk_hw *hw, diff --git a/target/linux/ipq806x/patches-6.6/990-006-Revert-ARM-dma-mapping-remove-dmac_clean_range-and-dmac_inv_range.patch b/target/linux/ipq806x/patches-6.6/990-006-Revert-ARM-dma-mapping-remove-dmac_clean_range-and-dmac_inv_range.patch new file mode 100644 index 0000000000..48e5890125 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-006-Revert-ARM-dma-mapping-remove-dmac_clean_range-and-dmac_inv_range.patch @@ -0,0 +1,159 @@ +From 16c72f2199f57a4b2f0295b8d08881cc94ad7e0b Mon Sep 17 00:00:00 2001 +From: Kathiravan T +Date: Thu, 7 Feb 2013 12:15:11 -0800 +Subject: [PATCH 274/281] Revert "ARM: dma-mapping: remove dmac_clean_range and + dmac_inv_range" + +This partially reverts 'commit 702b94bff3c505 ("ARM: dma-mapping: +remove dmac_clean_range and dmac_inv_range")' + +Some MSM drivers still use the dmac_clean and dmac_inv_range APIs. +Bring back the defines and exports for v7 CPUs. + +Change-Id: I69017d73da1065a5eeb9c87c899b6a51be5ebfe6 +Signed-off-by: Rohit Vaswani +Signed-off-by: Abhimanyu Kapur +[sramana: resolved minor merge conflicts] +Signed-off-by: Srinivas Ramana +[akdwived: Made change for checkpatch error] +Signed-off-by: Avaneesh Kumar Dwivedi +Signed-off-by: Vijayanand Jitta +[qqzhou@codeaurora.org: Made change for compile error] +Signed-off-by: Qingqing Zhou + +(cherry picked from commit 432d2898642443b498747a8e8a42cb423bd726f1) +Signed-off-by: Kathiravan T + +Change-Id: If81758c05ae89333205b8021a73287920a8ee64d +Signed-off-by: Pavithra R +--- + arch/arm/include/asm/cacheflush.h | 21 +++++++++++++++++++++ + arch/arm/include/asm/glue-cache.h | 2 ++ + arch/arm/mm/cache-v7.S | 6 ++++-- + arch/arm/mm/proc-macros.S | 4 +++- + arch/arm/mm/proc-syms.c | 3 +++ + 5 files changed, 33 insertions(+), 3 deletions(-) + +--- a/arch/arm/include/asm/cacheflush.h ++++ b/arch/arm/include/asm/cacheflush.h +@@ -91,6 +91,21 @@ + * DMA Cache Coherency + * =================== + * ++ * dma_inv_range(start, end) ++ * ++ * Invalidate (discard) the specified virtual address range. ++ * May not write back any entries. If 'start' or 'end' ++ * are not cache line aligned, those lines must be written ++ * back. ++ * - start - virtual start address ++ * - end - virtual end address ++ * ++ * dma_clean_range(start, end) ++ * ++ * Clean (write back) the specified virtual address range. ++ * - start - virtual start address ++ * - end - virtual end address ++ * + * dma_flush_range(start, end) + * + * Clean and invalidate the specified virtual address range. +@@ -112,6 +127,8 @@ struct cpu_cache_fns { + void (*dma_map_area)(const void *, size_t, int); + void (*dma_unmap_area)(const void *, size_t, int); + ++ void (*dma_inv_range)(const void *, const void *); ++ void (*dma_clean_range)(const void *, const void *); + void (*dma_flush_range)(const void *, const void *); + } __no_randomize_layout; + +@@ -137,6 +154,8 @@ extern struct cpu_cache_fns cpu_cache; + * is visible to DMA, or data written by DMA to system memory is + * visible to the CPU. + */ ++#define dmac_inv_range cpu_cache.dma_inv_range ++#define dmac_clean_range cpu_cache.dma_clean_range + #define dmac_flush_range cpu_cache.dma_flush_range + + #else +@@ -156,6 +175,8 @@ extern void __cpuc_flush_dcache_area(voi + * is visible to DMA, or data written by DMA to system memory is + * visible to the CPU. + */ ++extern void dmac_inv_range(const void *, const void *); ++extern void dmac_clean_range(const void *, const void *); + extern void dmac_flush_range(const void *, const void *); + + #endif +--- a/arch/arm/include/asm/glue-cache.h ++++ b/arch/arm/include/asm/glue-cache.h +@@ -156,6 +156,8 @@ static inline void nop_dma_unmap_area(co + #define __cpuc_flush_dcache_area __glue(_CACHE,_flush_kern_dcache_area) + + #define dmac_flush_range __glue(_CACHE,_dma_flush_range) ++#define dmac_inv_range __glue(_CACHE, _dma_inv_range) ++#define dmac_clean_range __glue(_CACHE, _dma_clean_range) + #endif + + #endif +--- a/arch/arm/mm/cache-v7.S ++++ b/arch/arm/mm/cache-v7.S +@@ -361,7 +361,7 @@ ENDPROC(v7_flush_kern_dcache_area) + * - start - virtual start address of region + * - end - virtual end address of region + */ +-v7_dma_inv_range: ++ENTRY(v7_dma_inv_range) + dcache_line_size r2, r3 + sub r3, r2, #1 + tst r0, r3 +@@ -391,7 +391,7 @@ ENDPROC(v7_dma_inv_range) + * - start - virtual start address of region + * - end - virtual end address of region + */ +-v7_dma_clean_range: ++ENTRY(v7_dma_clean_range) + dcache_line_size r2, r3 + sub r3, r2, #1 + bic r0, r0, r3 +@@ -477,6 +477,8 @@ ENDPROC(v7_dma_unmap_area) + + globl_equ b15_dma_map_area, v7_dma_map_area + globl_equ b15_dma_unmap_area, v7_dma_unmap_area ++ globl_equ b15_dma_inv_range, v7_dma_inv_range ++ globl_equ b15_dma_clean_range, v7_dma_clean_range + globl_equ b15_dma_flush_range, v7_dma_flush_range + + define_cache_functions b15 +--- a/arch/arm/mm/proc-macros.S ++++ b/arch/arm/mm/proc-macros.S +@@ -326,7 +326,7 @@ ENTRY(\name\()_processor_functions) + ENTRY(\name\()_cache_fns) + .long \name\()_flush_icache_all + .long \name\()_flush_kern_cache_all +- .long \name\()_flush_kern_cache_louis ++ .long \name\()_flush_kern_cache_louis + .long \name\()_flush_user_cache_all + .long \name\()_flush_user_cache_range + .long \name\()_coherent_kern_range +@@ -334,6 +334,8 @@ ENTRY(\name\()_cache_fns) + .long \name\()_flush_kern_dcache_area + .long \name\()_dma_map_area + .long \name\()_dma_unmap_area ++ .long \name\()_dma_inv_range ++ .long \name\()_dma_clean_range + .long \name\()_dma_flush_range + .size \name\()_cache_fns, . - \name\()_cache_fns + .endm +--- a/arch/arm/mm/proc-syms.c ++++ b/arch/arm/mm/proc-syms.c +@@ -27,6 +27,9 @@ EXPORT_SYMBOL(__cpuc_flush_user_all); + EXPORT_SYMBOL(__cpuc_flush_user_range); + EXPORT_SYMBOL(__cpuc_coherent_kern_range); + EXPORT_SYMBOL(__cpuc_flush_dcache_area); ++EXPORT_SYMBOL(dmac_inv_range); ++EXPORT_SYMBOL(dmac_clean_range); ++EXPORT_SYMBOL(dmac_flush_range); + #else + EXPORT_SYMBOL(cpu_cache); + #endif diff --git a/target/linux/ipq806x/patches-6.6/990-007-arm-arm64-Add-new-APIs-to-perform-dma-cache-maintenance-operation-without-dsb.patch b/target/linux/ipq806x/patches-6.6/990-007-arm-arm64-Add-new-APIs-to-perform-dma-cache-maintenance-operation-without-dsb.patch new file mode 100644 index 0000000000..95bc2fdcdf --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-007-arm-arm64-Add-new-APIs-to-perform-dma-cache-maintenance-operation-without-dsb.patch @@ -0,0 +1,379 @@ +From fe56e8cb75f113577dbf3edb717ea967f3723e22 Mon Sep 17 00:00:00 2001 +From: Suman Ghosh +Date: Sat, 4 Sep 2021 01:09:20 +0530 +Subject: [PATCH 278/281] arm/arm64: Add new APIs to perform dma cache + maintenance operation without dsb. + +Change-Id: I511657af343c8dc668ab7280362b3cdd57579360 +Signed-off-by: Suman Ghosh +Signed-off-by: Tushar Ganatra +Signed-off-by: Pavithra R +Signed-off-by: Tushar Ganatra +--- + arch/arm/include/asm/cacheflush.h | 11 ++++ + arch/arm/include/asm/glue-cache.h | 3 + + arch/arm/mm/cache-v7.S | 98 +++++++++++++++++++++++++++-- + arch/arm/mm/proc-syms.c | 3 + + arch/arm64/include/asm/assembler.h | 39 ++++++++++++ + arch/arm64/include/asm/cacheflush.h | 9 +++ + arch/arm64/mm/cache.S | 58 +++++++++++++++++ + arch/arm64/mm/flush.c | 24 +++++++ + 8 files changed, 239 insertions(+), 6 deletions(-) + +--- a/arch/arm/include/asm/cacheflush.h ++++ b/arch/arm/include/asm/cacheflush.h +@@ -130,6 +130,10 @@ struct cpu_cache_fns { + void (*dma_inv_range)(const void *, const void *); + void (*dma_clean_range)(const void *, const void *); + void (*dma_flush_range)(const void *, const void *); ++ ++ void (*dma_inv_range_no_dsb)(const void *, const void *); ++ void (*dma_clean_range_no_dsb)(const void *, const void *); ++ void (*dma_flush_range_no_dsb)(const void *, const void *); + } __no_randomize_layout; + + /* +@@ -158,6 +162,10 @@ extern struct cpu_cache_fns cpu_cache; + #define dmac_clean_range cpu_cache.dma_clean_range + #define dmac_flush_range cpu_cache.dma_flush_range + ++#define dmac_inv_range_no_dsb cpu_cache.dma_inv_range_no_dsb ++#define dmac_clean_range_no_dsb cpu_cache.dma_clean_range_no_dsb ++#define dmac_flush_range_no_dsb cpu_cache.dma_flush_range_no_dsb ++ + #else + + extern void __cpuc_flush_icache_all(void); +@@ -178,6 +186,9 @@ extern void __cpuc_flush_dcache_area(voi + extern void dmac_inv_range(const void *, const void *); + extern void dmac_clean_range(const void *, const void *); + extern void dmac_flush_range(const void *, const void *); ++extern void dmac_inv_range_no_dsb(const void *, const void *); ++extern void dmac_clean_range_no_dsb(const void *, const void *); ++extern void dmac_flush_range_no_dsb(const void *, const void *); + + #endif + +--- a/arch/arm/include/asm/glue-cache.h ++++ b/arch/arm/include/asm/glue-cache.h +@@ -156,8 +156,11 @@ static inline void nop_dma_unmap_area(co + #define __cpuc_flush_dcache_area __glue(_CACHE,_flush_kern_dcache_area) + + #define dmac_flush_range __glue(_CACHE,_dma_flush_range) ++#define dmac_flush_range_no_dsb __glue(_CACHE,_dma_flush_range_no_dsb) + #define dmac_inv_range __glue(_CACHE, _dma_inv_range) ++#define dmac_inv_range_no_dsb __glue(_CACHE, _dma_inv_range_no_dsb) + #define dmac_clean_range __glue(_CACHE, _dma_clean_range) ++#define dmac_clean_range_no_dsb __glue(_CACHE, _dma_clean_range_no_dsb) + #endif + + #endif +--- a/arch/arm/mm/cache-v7.S ++++ b/arch/arm/mm/cache-v7.S +@@ -387,6 +387,42 @@ ENTRY(v7_dma_inv_range) + ENDPROC(v7_dma_inv_range) + + /* ++ * v7_dma_inv_range_no_dsb(start,end) ++ * ++ * Invalidate the data cache within the specified region; we will ++ * be performing a DMA operation in this region and we want to ++ * purge old data in the cache. This API does not do ++ * "data synchronization barrier". The caller is responsible to ++ * do dsb after the transaction. ++ * ++ * - start - virtual start address of region ++ * - end - virtual end address of region ++ */ ++ENTRY(v7_dma_inv_range_no_dsb) ++ dcache_line_size r2, r3 ++ sub r3, r2, #1 ++ tst r0, r3 ++ bic r0, r0, r3 ++#ifdef CONFIG_ARM_ERRATA_764369 ++ ALT_SMP(W(dsb)) ++ ALT_UP(W(nop)) ++#endif ++ mcrne p15, 0, r0, c7, c14, 1 @ clean & invalidate D / U line ++ addne r0, r0, r2 ++ ++ tst r1, r3 ++ bic r1, r1, r3 ++ mcrne p15, 0, r1, c7, c14, 1 @ clean & invalidate D / U line ++ cmp r0, r1 ++1: ++ mcrlo p15, 0, r0, c7, c6, 1 @ invalidate D / U line ++ addlo r0, r0, r2 ++ cmplo r0, r1 ++ blo 1b ++ ret lr ++ENDPROC(v7_dma_inv_range_no_dsb) ++ ++/* + * v7_dma_clean_range(start,end) + * - start - virtual start address of region + * - end - virtual end address of region +@@ -409,12 +445,16 @@ ENTRY(v7_dma_clean_range) + ENDPROC(v7_dma_clean_range) + + /* +- * v7_dma_flush_range(start,end) ++ * v7_dma_clean_range_no_dsb(start,end) ++ * ++ * This API does not do "data synchronization barrier". ++ * The caller is responsible to do dsb after the transaction. ++ * + * - start - virtual start address of region + * - end - virtual end address of region + */ +-ENTRY(v7_dma_flush_range) +- dcache_line_size r2, r3 ++ENTRY(v7_dma_clean_range_no_dsb) ++ dcache_line_size r2, r3 + sub r3, r2, #1 + bic r0, r0, r3 + #ifdef CONFIG_ARM_ERRATA_764369 +@@ -422,6 +462,27 @@ ENTRY(v7_dma_flush_range) + ALT_UP(W(nop)) + #endif + 1: ++ mcr p15, 0, r0, c7, c10, 1 @ clean D / U line ++ add r0, r0, r2 ++ cmp r0, r1 ++ blo 1b ++ ret lr ++ENDPROC(v7_dma_clean_range_no_dsb) ++ ++/* ++ * v7_dma_flush_range(start,end) ++ * - start - virtual start address of region ++ * - end - virtual end address of region ++ */ ++ENTRY(v7_dma_flush_range) ++ dcache_line_size r2, r3 ++ sub r3, r2, #1 ++ bic r0, r0, r3 ++#ifdef CONFIG_ARM_ERRATA_764369 ++ ALT_SMP(W(dsb)) ++ ALT_UP(W(nop)) ++#endif ++1: + mcr p15, 0, r0, c7, c14, 1 @ clean & invalidate D / U line + add r0, r0, r2 + cmp r0, r1 +@@ -431,10 +492,35 @@ ENTRY(v7_dma_flush_range) + ENDPROC(v7_dma_flush_range) + + /* ++ * v7_dma_flush_range_no_dsb(start,end) ++ * ++ * This API does not do "data synchronization barrier". ++ * The caller is responsible to do dsb after the transaction. ++ * ++ * - start - virtual start address of region ++ * - end - virtual end address of region ++ */ ++ENTRY(v7_dma_flush_range_no_dsb) ++ dcache_line_size r2, r3 ++ sub r3, r2, #1 ++ bic r0, r0, r3 ++#ifdef CONFIG_ARM_ERRATA_764369 ++ ALT_SMP(W(dsb)) ++ ALT_UP(W(nop)) ++#endif ++1: ++ mcr p15, 0, r0, c7, c14, 1 @ clean & invalidate D / U line ++ add r0, r0, r2 ++ cmp r0, r1 ++ blo 1b ++ ret lr ++ENDPROC(v7_dma_flush_range_no_dsb) ++ ++/* + * dma_map_area(start, size, dir) +- * - start - kernel virtual start address +- * - size - size of region +- * - dir - DMA direction ++ * - start - kernel virtual start address ++ * - size - size of region ++ * - dir - DMA direction + */ + ENTRY(v7_dma_map_area) + add r1, r1, r0 +--- a/arch/arm/mm/proc-syms.c ++++ b/arch/arm/mm/proc-syms.c +@@ -28,8 +28,11 @@ EXPORT_SYMBOL(__cpuc_flush_user_range); + EXPORT_SYMBOL(__cpuc_coherent_kern_range); + EXPORT_SYMBOL(__cpuc_flush_dcache_area); + EXPORT_SYMBOL(dmac_inv_range); ++EXPORT_SYMBOL(dmac_inv_range_no_dsb); + EXPORT_SYMBOL(dmac_clean_range); ++EXPORT_SYMBOL(dmac_clean_range_no_dsb); + EXPORT_SYMBOL(dmac_flush_range); ++EXPORT_SYMBOL(dmac_flush_range_no_dsb); + #else + EXPORT_SYMBOL(cpu_cache); + #endif +--- a/arch/arm64/include/asm/assembler.h ++++ b/arch/arm64/include/asm/assembler.h +@@ -428,6 +428,45 @@ alternative_endif + + /* + * Macro to perform a data cache maintenance for the interval ++ * [kaddr, kaddr + size) ++ * This macro does not do "data synchronization barrier". Caller should ++ * do "dsb" after transaction. ++ * ++ * op: operation passed to dc instruction ++ * kaddr: starting virtual address of the region ++ * size: size of the region ++ * Corrupts: kaddr, size, tmp1, tmp2 ++ */ ++ .macro dcache_by_line_op_no_dsb op, kaddr, size, tmp1, tmp2 ++ dcache_line_size \tmp1, \tmp2 ++ add \size, \kaddr, \size ++ sub \tmp2, \tmp1, #1 ++ bic \kaddr, \kaddr, \tmp2 ++9998: ++ .ifc \op, cvau ++ __dcache_op_workaround_clean_cache \op, \kaddr ++ .else ++ .ifc \op, cvac ++ __dcache_op_workaround_clean_cache \op, \kaddr ++ .else ++ .ifc \op, cvap ++ sys 3, c7, c12, 1, \kaddr // dc cvap ++ .else ++ .ifc \op, cvadp ++ sys 3, c7, c13, 1, \kaddr // dc cvadp ++ .else ++ dc \op, \kaddr ++ .endif ++ .endif ++ .endif ++ .endif ++ add \kaddr, \kaddr, \tmp1 ++ cmp \kaddr, \size ++ b.lo 9998b ++ .endm ++ ++/* ++ * Macro to perform a data cache maintenance for the interval + * [start, end) + * + * op: operation passed to dc instruction +--- a/arch/arm64/include/asm/cacheflush.h ++++ b/arch/arm64/include/asm/cacheflush.h +@@ -79,6 +79,15 @@ extern void dcache_clean_pou(unsigned lo + extern long caches_clean_inval_user_pou(unsigned long start, unsigned long end); + extern void sync_icache_aliases(unsigned long start, unsigned long end); + ++extern void dmac_inv_range(const void *start, const void *end); ++extern void __dma_flush_area_no_dsb(const void *start, size_t size); ++extern void __dma_inv_area_no_dsb(const void *start, size_t size); ++extern void __dma_clean_area_no_dsb(const void *start, size_t size); ++ ++extern void dmac_flush_range_no_dsb(const void *start, const void *end); ++extern void dmac_inv_range_no_dsb(const void *start, const void *end); ++extern void dmac_clean_range_no_dsb(const void *start, const void *end); ++ + static inline void flush_icache_range(unsigned long start, unsigned long end) + { + caches_clean_inval_pou(start, end); +--- a/arch/arm64/mm/cache.S ++++ b/arch/arm64/mm/cache.S +@@ -164,6 +164,64 @@ SYM_FUNC_END(__pi_dcache_inval_poc) + SYM_FUNC_ALIAS(dcache_inval_poc, __pi_dcache_inval_poc) + + /* ++ * __dma_inv_area_no_dsb(start, size) ++ * ++ * This macro does not do "data synchronization barrier". Caller should ++ * do "dsb" after transaction. ++ * ++ * start - virtual start address of region ++ * size - size in question ++ */ ++SYM_FUNC_START(__dma_inv_area_no_dsb) ++ add x1, x1, x0 ++ dcache_line_size x2, x3 ++ sub x3, x2, #1 ++ tst x1, x3 // end cache line aligned? ++ bic x1, x1, x3 ++ b.eq 1f ++ dc civac, x1 // clean & invalidate D / U line ++1: tst x0, x3 // start cache line aligned? ++ bic x0, x0, x3 ++ b.eq 2f ++ dc civac, x0 // clean & invalidate D / U line ++ b 3f ++2: dc ivac, x0 // invalidate D / U line ++3: add x0, x0, x2 ++ cmp x0, x1 ++ b.lo 2b ++ ret ++SYM_FUNC_END(__dma_inv_area_no_dsb) ++ ++/* ++ * __dma_clean_area_no_dsb(start, size) ++ * ++ * his macro does not do "data synchronization barrier". Caller should ++ * o "dsb" after transaction. ++ * ++ * start - virtual start address of region ++ * size - size in question ++ */ ++SYM_FUNC_START(__dma_clean_area_no_dsb) ++ dcache_by_line_op_no_dsb cvac, x0, x1, x2, x3 ++ ret ++SYM_FUNC_END(__dma_clean_area_no_dsb) ++ ++/* ++ * __dma_flush_area_no_dsb(start, size) ++ * ++ * clean & invalidate D / U line ++ * his macro does not do "data synchronization barrier". Caller should ++ * o "dsb" after transaction. ++ * ++ * start - virtual start address of region ++ * size - size in question ++ */ ++SYM_FUNC_START(__dma_flush_area_no_dsb) ++ dcache_by_line_op_no_dsb civac, x0, x1, x2, x3 ++ ret ++SYM_FUNC_END(__dma_flush_area_no_dsb) ++ ++/* + * dcache_clean_poc(start, end) + * + * Ensure that any D-cache lines for the interval [start, end) +--- a/arch/arm64/mm/flush.c ++++ b/arch/arm64/mm/flush.c +@@ -100,3 +100,27 @@ void arch_invalidate_pmem(void *addr, si + } + EXPORT_SYMBOL_GPL(arch_invalidate_pmem); + #endif ++ ++void dmac_flush_range_no_dsb(const void *start, const void *end) ++{ ++ __dma_flush_area_no_dsb(start, (void *)(end) - (void *)(start)); ++} ++EXPORT_SYMBOL(dmac_flush_range_no_dsb); ++ ++void dmac_inv_range(const void *start, const void *end) ++{ ++ dcache_inval_poc((unsigned long)start, (unsigned long)(end)); ++} ++EXPORT_SYMBOL(dmac_inv_range); ++ ++void dmac_inv_range_no_dsb(const void *start, const void *end) ++{ ++ __dma_inv_area_no_dsb(start, (void *)(end) - (void *)(start)); ++} ++EXPORT_SYMBOL(dmac_inv_range_no_dsb); ++ ++void dmac_clean_range_no_dsb(const void *start, const void *end) ++{ ++ __dma_clean_area_no_dsb(start, (void *)(end) - (void *)(start)); ++} ++EXPORT_SYMBOL(dmac_clean_range_no_dsb); diff --git a/target/linux/ipq806x/patches-6.6/990-008-net-Add-DSCP-remarking-feature-for-HW-acceleration.patch b/target/linux/ipq806x/patches-6.6/990-008-net-Add-DSCP-remarking-feature-for-HW-acceleration.patch new file mode 100644 index 0000000000..837dcb57f7 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-008-net-Add-DSCP-remarking-feature-for-HW-acceleration.patch @@ -0,0 +1,143 @@ +From 5f63a10caee09a5cec9a0da7f6b9cde39decf0c7 Mon Sep 17 00:00:00 2001 +From: Murat Sezgin +Date: Thu, 11 May 2023 11:54:05 -0700 +Subject: [PATCH 269/281] net: Add DSCP remarking feature for HW acceleration. + +DSCP remarking is an advanced QoS feature of acceleration +engine. With this feature it is possible to set the accelerated +eggress packets' DSCP values with iptables command. + +Change-Id: I7fa8d8a2a87b66b262d54e25f22e9eedd665c456 +Signed-off-by: Murat Sezgin +Signed-off-by: Murat Sezgin +--- + include/net/netfilter/nf_conntrack_extend.h | 3 +++ + net/netfilter/Kconfig | 7 ++++++ + net/netfilter/Makefile | 1 + + net/netfilter/nf_conntrack_core.c | 6 +++++ + net/netfilter/xt_DSCP.c | 27 ++++++++++++++++++++- + 5 files changed, 43 insertions(+), 1 deletion(-) + +--- a/include/net/netfilter/nf_conntrack_extend.h ++++ b/include/net/netfilter/nf_conntrack_extend.h +@@ -31,6 +31,9 @@ enum nf_ct_ext_id { + #if IS_ENABLED(CONFIG_NET_ACT_CT) + NF_CT_EXT_ACT_CT, + #endif ++#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT ++ NF_CT_EXT_DSCPREMARK, ++#endif + NF_CT_EXT_NUM, + }; + +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -174,6 +174,13 @@ config NF_CONNTRACK_TIMEOUT + + If unsure, say `N'. + ++config NF_CONNTRACK_DSCPREMARK_EXT ++ bool 'Connection tracking extension for dscp remark target' ++ depends on NETFILTER_ADVANCED ++ help ++ This option enables support for connection tracking extension ++ for dscp remark. ++ + config NF_CONNTRACK_TIMESTAMP + bool 'Connection tracking timestamping' + depends on NETFILTER_ADVANCED +--- a/net/netfilter/Makefile ++++ b/net/netfilter/Makefile +@@ -20,6 +20,7 @@ nf_conntrack-$(CONFIG_DEBUG_INFO_BTF_MOD + else ifeq ($(CONFIG_NF_CONNTRACK),y) + nf_conntrack-$(CONFIG_DEBUG_INFO_BTF) += nf_conntrack_bpf.o + endif ++nf_conntrack-$(CONFIG_NF_CONNTRACK_DSCPREMARK_EXT) += nf_conntrack_dscpremark_ext.o + + obj-$(CONFIG_NETFILTER) = netfilter.o + obj-$(CONFIG_NETFILTER_BPF_LINK) += nf_bpf_link.o +--- a/net/netfilter/nf_conntrack_core.c ++++ b/net/netfilter/nf_conntrack_core.c +@@ -45,6 +45,9 @@ + #include + #include + #include ++#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT ++#include ++#endif + #include + #include + #include +@@ -1740,6 +1743,9 @@ init_conntrack(struct net *net, struct n + nf_ct_acct_ext_add(ct, GFP_ATOMIC); + nf_ct_tstamp_ext_add(ct, GFP_ATOMIC); + nf_ct_labels_ext_add(ct); ++#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT ++ nf_ct_dscpremark_ext_add(ct, GFP_ATOMIC); ++#endif + + #ifdef CONFIG_NF_CONNTRACK_EVENTS + ecache = tmpl ? nf_ct_ecache_find(tmpl) : NULL; +--- a/net/netfilter/xt_DSCP.c ++++ b/net/netfilter/xt_DSCP.c +@@ -15,6 +15,9 @@ + + #include + #include ++#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT ++#include ++#endif + + MODULE_AUTHOR("Harald Welte "); + MODULE_DESCRIPTION("Xtables: DSCP/TOS field modification"); +@@ -31,6 +34,10 @@ dscp_tg(struct sk_buff *skb, const struc + { + const struct xt_DSCP_info *dinfo = par->targinfo; + u_int8_t dscp = ipv4_get_dsfield(ip_hdr(skb)) >> XT_DSCP_SHIFT; ++#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT ++ struct nf_conn *ct; ++ enum ip_conntrack_info ctinfo; ++#endif + + if (dscp != dinfo->dscp) { + if (skb_ensure_writable(skb, sizeof(struct iphdr))) +@@ -39,6 +46,13 @@ dscp_tg(struct sk_buff *skb, const struc + ipv4_change_dsfield(ip_hdr(skb), XT_DSCP_ECN_MASK, + dinfo->dscp << XT_DSCP_SHIFT); + ++#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT ++ ct = nf_ct_get(skb, &ctinfo); ++ if (!ct) ++ return XT_CONTINUE; ++ ++ nf_conntrack_dscpremark_ext_set_dscp_rule_valid(ct); ++#endif + } + return XT_CONTINUE; + } +@@ -48,13 +62,24 @@ dscp_tg6(struct sk_buff *skb, const stru + { + const struct xt_DSCP_info *dinfo = par->targinfo; + u_int8_t dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> XT_DSCP_SHIFT; +- ++#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT ++ struct nf_conn *ct; ++ enum ip_conntrack_info ctinfo; ++#endif + if (dscp != dinfo->dscp) { + if (skb_ensure_writable(skb, sizeof(struct ipv6hdr))) + return NF_DROP; + + ipv6_change_dsfield(ipv6_hdr(skb), XT_DSCP_ECN_MASK, + dinfo->dscp << XT_DSCP_SHIFT); ++ ++#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT ++ ct = nf_ct_get(skb, &ctinfo); ++ if (!ct) ++ return XT_CONTINUE; ++ ++ nf_conntrack_dscpremark_ext_set_dscp_rule_valid(ct); ++#endif + } + return XT_CONTINUE; + } diff --git a/target/linux/ipq806x/patches-6.6/990-009-net-conntrack-events-support-multiple-registrant.patch b/target/linux/ipq806x/patches-6.6/990-009-net-conntrack-events-support-multiple-registrant.patch new file mode 100644 index 0000000000..b0a3c97ac9 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-009-net-conntrack-events-support-multiple-registrant.patch @@ -0,0 +1,339 @@ +From 8daa619cff9acc2be5c0161019cd33e486171047 Mon Sep 17 00:00:00 2001 +From: Murat Sezgin +Date: Tue, 18 Apr 2017 15:48:01 -0700 +Subject: [PATCH 270/281] net: conntrack events, support multiple registrant + +Change-Id: Iebfb254590fb594f5baf232f849d1b7ae45ef757 +Signed-off-by: Zhi Chen +Signed-off-by: Murat Sezgin +--- + include/net/netfilter/nf_conntrack_ecache.h | 45 +++++++++++++- + include/net/netns/conntrack.h | 3 + + net/netfilter/Kconfig | 8 +++ + net/netfilter/nf_conntrack_core.c | 3 + + net/netfilter/nf_conntrack_ecache.c | 67 +++++++++++++++++++++ + net/netfilter/nf_conntrack_netlink.c | 25 +++++++- + 6 files changed, 148 insertions(+), 3 deletions(-) + +--- a/include/net/netfilter/nf_conntrack_ecache.h ++++ b/include/net/netfilter/nf_conntrack_ecache.h +@@ -65,13 +65,52 @@ struct nf_ct_event_notifier { + int (*exp_event)(unsigned int events, const struct nf_exp_event *item); + }; + ++#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS ++extern int nf_conntrack_register_notifier(struct net *net, ++ struct notifier_block *nb); ++extern int nf_conntrack_unregister_notifier(struct net *net, ++ struct notifier_block *nb); ++static inline int ++nf_conntrack_eventmask_report(unsigned int eventmask, ++ struct nf_conn *ct, ++ u32 portid, ++ int report) ++{ ++ struct nf_conntrack_ecache *e; ++ struct net *net = nf_ct_net(ct); ++ ++ e = nf_ct_ecache_find(ct); ++ if (e == NULL) ++ return 0; ++ ++ if (nf_ct_is_confirmed(ct)) { ++ struct nf_ct_event item = { ++ .ct = ct, ++ .portid = e->portid ? e->portid : portid, ++ .report = report ++ }; ++ /* This is a resent of a destroy event? If so, skip missed */ ++ unsigned long missed = e->portid ? 0 : e->missed; ++ ++ if (!((eventmask | missed) & e->ctmask)) ++ return 0; ++ ++ atomic_notifier_call_chain(&net->ct.nf_conntrack_chain, ++ eventmask | missed, &item); ++ } ++ ++ return 0; ++} ++#else + void nf_conntrack_register_notifier(struct net *net, + const struct nf_ct_event_notifier *nb); + void nf_conntrack_unregister_notifier(struct net *net); + +-void nf_ct_deliver_cached_events(struct nf_conn *ct); + int nf_conntrack_eventmask_report(unsigned int eventmask, struct nf_conn *ct, + u32 portid, int report); ++#endif ++ ++void nf_ct_deliver_cached_events(struct nf_conn *ct); + + bool nf_ct_ecache_ext_add(struct nf_conn *ct, u16 ctmask, u16 expmask, gfp_t gfp); + #else +@@ -98,11 +137,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) +--- a/include/net/netns/conntrack.h ++++ b/include/net/netns/conntrack.h +@@ -104,6 +104,9 @@ struct netns_ct { + u8 sysctl_checksum; + + 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/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -181,6 +181,14 @@ config NF_CONNTRACK_DSCPREMARK_EXT + 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 +@@ -2810,6 +2810,9 @@ int nf_conntrack_init_net(struct net *ne + nf_conntrack_ecache_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; + + err_expect: +--- a/net/netfilter/nf_conntrack_ecache.c ++++ b/net/netfilter/nf_conntrack_ecache.c +@@ -16,6 +16,9 @@ + #include + #include + #include ++#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS ++#include ++#endif + #include + #include + #include +@@ -124,6 +127,7 @@ static void ecache_work(struct work_stru + schedule_delayed_work(&cnet->ecache.dwork, delay); + } + ++#ifndef CONFIG_NF_CONNTRACK_CHAIN_EVENTS + static int __nf_conntrack_eventmask_report(struct nf_conntrack_ecache *e, + const u32 events, + const u32 missed, +@@ -198,9 +202,56 @@ int nf_conntrack_eventmask_report(unsign + return ret; + } + EXPORT_SYMBOL_GPL(nf_conntrack_eventmask_report); ++#endif + + /* deliver cached events and clear cache entry - must be called with locally + * disabled softirqs */ ++#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS ++void nf_ct_deliver_cached_events(struct nf_conn *ct) ++{ ++ unsigned long events, missed; ++ struct nf_conntrack_ecache *e; ++ struct nf_ct_event item; ++ struct net *net = nf_ct_net(ct); ++ int ret = 0; ++ ++ e = nf_ct_ecache_find(ct); ++ if (!e) ++ return; ++ ++ events = xchg(&e->cache, 0); ++ ++ if (!nf_ct_is_confirmed(ct) || nf_ct_is_dying(ct) || !events) ++ return; ++ ++ /* ++ * We make a copy of the missed event cache without taking ++ * the lock, thus we may send missed events twice. However, ++ * this does not harm and it happens very rarely. ++ */ ++ missed = e->missed; ++ ++ if (!((events | missed) & e->ctmask)) ++ return; ++ ++ item.ct = ct; ++ item.portid = 0; ++ item.report = 0; ++ ++ atomic_notifier_call_chain(&net->ct.nf_conntrack_chain, ++ events | missed, &item); ++ ++ if (likely(ret >= 0 && !missed)) ++ return; ++ ++ spin_lock_bh(&ct->lock); ++ if (ret < 0) ++ e->missed |= events; ++ else ++ e->missed &= ~missed; ++ spin_unlock_bh(&ct->lock); ++} ++#else + void nf_ct_deliver_cached_events(struct nf_conn *ct) + { + struct nf_conntrack_ecache *e; +@@ -226,6 +277,7 @@ void nf_ct_deliver_cached_events(struct + */ + __nf_conntrack_eventmask_report(e, events, e->missed, &item); + } ++#endif + EXPORT_SYMBOL_GPL(nf_ct_deliver_cached_events); + + void nf_ct_expect_event_report(enum ip_conntrack_expect_events event, +@@ -258,6 +310,12 @@ out_unlock: + rcu_read_unlock(); + } + ++#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS ++int nf_conntrack_register_notifier(struct net *net, struct notifier_block *nb) ++{ ++ return atomic_notifier_chain_register(&net->ct.nf_conntrack_chain, nb); ++} ++#else + void nf_conntrack_register_notifier(struct net *net, + const struct nf_ct_event_notifier *new) + { +@@ -270,8 +328,16 @@ void nf_conntrack_register_notifier(stru + rcu_assign_pointer(net->ct.nf_conntrack_event_cb, new); + mutex_unlock(&nf_ct_ecache_mutex); + } ++#endif + EXPORT_SYMBOL_GPL(nf_conntrack_register_notifier); + ++#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS ++int nf_conntrack_unregister_notifier(struct net *net, struct notifier_block *nb) ++{ ++ return atomic_notifier_chain_unregister(&net->ct.nf_conntrack_chain, ++ nb); ++} ++#else + void nf_conntrack_unregister_notifier(struct net *net) + { + mutex_lock(&nf_ct_ecache_mutex); +@@ -279,6 +345,7 @@ void nf_conntrack_unregister_notifier(st + mutex_unlock(&nf_ct_ecache_mutex); + /* synchronize_rcu() is called after netns pre_exit */ + } ++#endif + EXPORT_SYMBOL_GPL(nf_conntrack_unregister_notifier); + + void nf_conntrack_ecache_work(struct net *net, enum nf_ct_ecache_state state) +--- a/net/netfilter/nf_conntrack_netlink.c ++++ b/net/netfilter/nf_conntrack_netlink.c +@@ -28,6 +28,9 @@ + #include + #include + #include ++#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS ++#include ++#endif + #include + #include + +@@ -719,18 +722,26 @@ static size_t ctnetlink_nlmsg_size(const + ; + } + ++#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS ++static int ctnetlink_conntrack_event(struct notifier_block *this, ++ unsigned long events, void *ptr) ++#else + static int + ctnetlink_conntrack_event(unsigned int events, const struct nf_ct_event *item) ++#endif + { + const struct nf_conntrack_zone *zone; + struct net *net; + struct nlmsghdr *nlh; + struct nlattr *nest_parms; +- struct nf_conn *ct = item->ct; + struct sk_buff *skb; + unsigned int type; + unsigned int flags = 0, group; + int err; ++#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS ++ struct nf_ct_event *item = (struct nf_ct_event *)ptr; ++#endif ++ struct nf_conn *ct = item->ct; + + if (events & (1 << IPCT_DESTROY)) { + type = IPCTNL_MSG_CT_DELETE; +@@ -3089,6 +3100,7 @@ nla_put_failure: + } + + #ifdef CONFIG_NF_CONNTRACK_EVENTS ++#ifndef CONFIG_NF_CONNTRACK_CHAIN_EVENTS + static int + ctnetlink_expect_event(unsigned int events, const struct nf_exp_event *item) + { +@@ -3138,6 +3150,7 @@ errout: + return 0; + } + #endif ++#endif + static int ctnetlink_exp_done(struct netlink_callback *cb) + { + if (cb->args[1]) +@@ -3748,11 +3761,17 @@ static int ctnetlink_stat_exp_cpu(struct + } + + #ifdef CONFIG_NF_CONNTRACK_EVENTS ++#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS ++static struct notifier_block ctnl_notifier = { ++ .notifier_call = ctnetlink_conntrack_event, ++}; ++#else + static struct nf_ct_event_notifier ctnl_notifier = { + .ct_event = ctnetlink_conntrack_event, + .exp_event = ctnetlink_expect_event, + }; + #endif ++#endif + + static const struct nfnl_callback ctnl_cb[IPCTNL_MSG_MAX] = { + [IPCTNL_MSG_CT_NEW] = { +@@ -3851,8 +3870,12 @@ static int __net_init ctnetlink_net_init + static void ctnetlink_net_pre_exit(struct net *net) + { + #ifdef CONFIG_NF_CONNTRACK_EVENTS ++#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS ++ nf_conntrack_unregister_notifier(net, &ctnl_notifier); ++#else + nf_conntrack_unregister_notifier(net); + #endif ++#endif + } + + static struct pernet_operations ctnetlink_net_ops = { diff --git a/target/linux/ipq806x/patches-6.6/990-010-net-bonding-Added-acceleration-support-over-LAG-inte.patch b/target/linux/ipq806x/patches-6.6/990-010-net-bonding-Added-acceleration-support-over-LAG-inte.patch new file mode 100644 index 0000000000..256d069d28 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-010-net-bonding-Added-acceleration-support-over-LAG-inte.patch @@ -0,0 +1,720 @@ +From 691195eec4f741a2e73accae97092cf0a0203a54 Mon Sep 17 00:00:00 2001 +From: Ratheesh Kannoth +Date: Fri, 15 May 2020 12:36:11 +0530 +Subject: [PATCH] net/bonding: Added acceleration support over LAG interface + +The accel support is enabled for the following LAG modes, + 1. Balance-Xor (Static LAG) + 2. 802.3ad (Dynamic LAG) +Change-Id: I4524902ddec583a2963cb38731c062f216a5386d +Signed-off-by: Shyam Sunder + +net/bonding: Fix for Load Balancing within bonding (LAG) group + + New APIs (without skb) introduced in bonding driver to calculate hash on given + parameters (IP version, src IP, dst IP, src MAC, dst MAC) has issue in creating + flow_key. flow_get_u32_dst is returning 0 for given flow_key because addr_type + is not set correctly. Fix is added to populate addr_type. Test different flows + and verified that the flows are distributed. + +Change-Id: Ie54a40f590010ea2994ffc4ace8f4d050258a06f +Signed-off-by: Bhaskar Valaboju +Signed-off-by: Ratheesh Kannoth + +net: bonding: Relocate kernel bond functions + +header files included in uapi folder are the interface between +userspace and kernel space. This path declares new bonding +functions in kernel header file instead of uapi header file. + +Change-Id: I1b33f07015b79ec4ac702a9079da01f1c2a08bbb +Signed-off-by: Ratheesh Kannoth +--- + drivers/net/bonding/bond_3ad.c | 160 ++++++++++++++++- + drivers/net/bonding/bond_main.c | 306 ++++++++++++++++++++++++++++++-- + include/net/bond_3ad.h | 7 +- + include/net/bonding.h | 21 +++ + 4 files changed, 481 insertions(+), 13 deletions(-) + +--- a/drivers/net/bonding/bond_3ad.c ++++ b/drivers/net/bonding/bond_3ad.c +@@ -116,6 +116,38 @@ static void ad_marker_response_received( + struct port *port); + static void ad_update_actor_keys(struct port *port, bool reset); + ++struct bond_cb __rcu *bond_cb; ++ ++int bond_register_cb(struct bond_cb *cb) ++{ ++ struct bond_cb *lag_cb; ++ ++ lag_cb = kzalloc(sizeof(*lag_cb), GFP_ATOMIC | __GFP_NOWARN); ++ if (!lag_cb) { ++ return -1; ++ } ++ ++ memcpy((void *)lag_cb, (void *)cb, sizeof(*cb)); ++ ++ rcu_read_lock(); ++ rcu_assign_pointer(bond_cb, lag_cb); ++ rcu_read_unlock(); ++ return 0; ++} ++EXPORT_SYMBOL(bond_register_cb); ++ ++void bond_unregister_cb(void) ++{ ++ struct bond_cb *lag_cb_main; ++ ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ rcu_assign_pointer(bond_cb, NULL); ++ rcu_read_unlock(); ++ ++ kfree(lag_cb_main); ++} ++EXPORT_SYMBOL(bond_unregister_cb); + + /* ================= api to bonding and kernel code ================== */ + +@@ -1073,6 +1105,28 @@ static void ad_mux_machine(struct port * + ad_disable_collecting_distributing(port, + update_slave_arr); + port->ntt = true; ++ ++ /* Send a notificaton about change in state of this ++ * port. We only want to handle case where port moves ++ * from AD_MUX_COLLECTING_DISTRIBUTING -> ++ * AD_MUX_ATTACHED. ++ */ ++ if (bond_slave_is_up(port->slave) && ++ (last_state == AD_MUX_COLLECTING_DISTRIBUTING)) { ++ struct bond_cb *lag_cb_main; ++ ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ if (lag_cb_main && ++ lag_cb_main->bond_cb_link_down) { ++ struct net_device *dev; ++ ++ dev = port->slave->dev; ++ lag_cb_main->bond_cb_link_down(dev); ++ } ++ rcu_read_unlock(); ++ } ++ + break; + case AD_MUX_COLLECTING_DISTRIBUTING: + port->actor_oper_port_state |= LACP_STATE_COLLECTING; +@@ -1917,6 +1971,7 @@ static void ad_enable_collecting_distrib + bool *update_slave_arr) + { + if (port->aggregator->is_active) { ++ struct bond_cb *lag_cb_main; + slave_dbg(port->slave->bond->dev, port->slave->dev, + "Enabling port %d (LAG %d)\n", + port->actor_port_number, +@@ -1924,6 +1979,14 @@ static void ad_enable_collecting_distrib + __enable_port(port); + /* Slave array needs update */ + *update_slave_arr = true; ++ ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ ++ if (lag_cb_main && lag_cb_main->bond_cb_link_up) ++ lag_cb_main->bond_cb_link_up(port->slave->dev); ++ ++ rcu_read_unlock(); + } + } + +@@ -2683,6 +2746,102 @@ int bond_3ad_get_active_agg_info(struct + return ret; + } + ++/* bond_3ad_get_tx_dev - Calculate egress interface for a given packet, ++ * for a LAG that is configured in 802.3AD mode ++ * @skb: pointer to skb to be egressed ++ * @src_mac: pointer to source L2 address ++ * @dst_mac: pointer to destination L2 address ++ * @src: pointer to source L3 address ++ * @dst: pointer to destination L3 address ++ * @protocol: L3 protocol id from L2 header ++ * @bond_dev: pointer to bond master device ++ * ++ * If @skb is NULL, bond_xmit_hash is used to calculate hash using L2/L3 ++ * addresses. ++ * ++ * Returns: Either valid slave device, or NULL otherwise ++ */ ++struct net_device *bond_3ad_get_tx_dev(struct sk_buff *skb, u8 *src_mac, ++ u8 *dst_mac, void *src, ++ void *dst, u16 protocol, ++ struct net_device *bond_dev, ++ __be16 *layer4hdr) ++{ ++ struct bonding *bond = netdev_priv(bond_dev); ++ struct aggregator *agg; ++ struct ad_info ad_info; ++ struct list_head *iter; ++ struct slave *slave; ++ struct slave *first_ok_slave = NULL; ++ u32 hash = 0; ++ int slaves_in_agg; ++ int slave_agg_no = 0; ++ int agg_id; ++ ++ if (__bond_3ad_get_active_agg_info(bond, &ad_info)) { ++ pr_debug("%s: Error: __bond_3ad_get_active_agg_info failed\n", ++ bond_dev->name); ++ return NULL; ++ } ++ ++ slaves_in_agg = ad_info.ports; ++ agg_id = ad_info.aggregator_id; ++ ++ if (slaves_in_agg == 0) { ++ pr_debug("%s: Error: active aggregator is empty\n", ++ bond_dev->name); ++ return NULL; ++ } ++ ++ if (skb) { ++ hash = bond_xmit_hash(bond, skb); ++ slave_agg_no = hash % slaves_in_agg; ++ } else { ++ if (bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER23 && ++ bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER2 && ++ bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER34) { ++ pr_debug("%s: Error: Unsupported hash policy for 802.3AD fast path\n", ++ bond_dev->name); ++ return NULL; ++ } ++ ++ hash = bond_xmit_hash_without_skb(src_mac, dst_mac, ++ src, dst, protocol, ++ bond_dev, layer4hdr); ++ slave_agg_no = hash % slaves_in_agg; ++ } ++ ++ bond_for_each_slave_rcu(bond, slave, iter) { ++ agg = SLAVE_AD_INFO(slave)->port.aggregator; ++ if (!agg || agg->aggregator_identifier != agg_id) ++ continue; ++ ++ if (slave_agg_no >= 0) { ++ if (!first_ok_slave && bond_slave_can_tx(slave)) ++ first_ok_slave = slave; ++ slave_agg_no--; ++ continue; ++ } ++ ++ if (bond_slave_can_tx(slave)) ++ return slave->dev; ++ } ++ ++ if (slave_agg_no >= 0) { ++ pr_err("%s: Error: Couldn't find a slave to tx on for aggregator ID %d\n", ++ bond_dev->name, agg_id); ++ return NULL; ++ } ++ ++ /* we couldn't find any suitable slave after the agg_no, so use the ++ * first suitable found, if found. ++ */ ++ if (first_ok_slave) ++ return first_ok_slave->dev; ++ ++ return NULL; ++} ++ + int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond, + struct slave *slave) + { +--- a/drivers/net/bonding/bond_main.c ++++ b/drivers/net/bonding/bond_main.c +@@ -210,6 +210,7 @@ atomic_t netpoll_block_tx = ATOMIC_INIT( + #endif + + unsigned int bond_net_id __read_mostly; ++static unsigned long bond_id_mask = 0xFFFFFFF0; + + static const struct flow_dissector_key flow_keys_bonding_keys[] = { + { +@@ -287,6 +288,19 @@ const char *bond_mode_name(int mode) + return names[mode]; + } + ++int bond_get_id(struct net_device *bond_dev) ++{ ++ struct bonding *bond; ++ ++ if (!((bond_dev->priv_flags & IFF_BONDING) && ++ (bond_dev->flags & IFF_MASTER))) ++ return -EINVAL; ++ ++ bond = netdev_priv(bond_dev); ++ return bond->id; ++} ++EXPORT_SYMBOL(bond_get_id); ++ + /** + * bond_dev_queue_xmit - Prepare skb for xmit. + * +@@ -1231,6 +1245,21 @@ void bond_change_active_slave(struct bon + if (BOND_MODE(bond) == BOND_MODE_8023AD) + bond_3ad_handle_link_change(new_active, BOND_LINK_UP); + ++ if (bond->params.mode == BOND_MODE_XOR) { ++ struct bond_cb *lag_cb_main; ++ ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ if (lag_cb_main && ++ lag_cb_main->bond_cb_link_up) { ++ struct net_device *dev; ++ ++ dev = new_active->dev; ++ lag_cb_main->bond_cb_link_up(dev); ++ } ++ rcu_read_unlock(); ++ } ++ + if (bond_is_lb(bond)) + bond_alb_handle_link_change(bond, new_active, BOND_LINK_UP); + } else { +@@ -1876,6 +1905,7 @@ int bond_enslave(struct net_device *bond + const struct net_device_ops *slave_ops = slave_dev->netdev_ops; + struct slave *new_slave = NULL, *prev_slave; + struct sockaddr_storage ss; ++ struct bond_cb *lag_cb_main; + int link_reporting; + int res = 0, i; + +@@ -2325,6 +2355,13 @@ int bond_enslave(struct net_device *bond + bond_is_active_slave(new_slave) ? "an active" : "a backup", + new_slave->link != BOND_LINK_DOWN ? "an up" : "a down"); + ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ if (lag_cb_main && lag_cb_main->bond_cb_enslave) ++ lag_cb_main->bond_cb_enslave(slave_dev); ++ ++ rcu_read_unlock(); ++ + /* enslave is successful */ + bond_queue_slave_event(new_slave); + return 0; +@@ -2390,6 +2427,13 @@ err_undo_flags: + } + } + ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ if (lag_cb_main && lag_cb_main->bond_cb_enslave) ++ lag_cb_main->bond_cb_enslave(slave_dev); ++ ++ rcu_read_unlock(); ++ + return res; + } + +@@ -2411,6 +2455,7 @@ static int __bond_release_one(struct net + struct bonding *bond = netdev_priv(bond_dev); + struct slave *slave, *oldcurrent; + struct sockaddr_storage ss; ++ struct bond_cb *lag_cb_main; + int old_flags = bond_dev->flags; + netdev_features_t old_features = bond_dev->features; + +@@ -2433,6 +2478,13 @@ static int __bond_release_one(struct net + + bond_set_slave_inactive_flags(slave, BOND_SLAVE_NOTIFY_NOW); + ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ if (lag_cb_main && lag_cb_main->bond_cb_release) ++ lag_cb_main->bond_cb_release(slave_dev); ++ ++ rcu_read_unlock(); ++ + bond_sysfs_slave_del(slave); + + /* recompute stats just before removing the slave */ +@@ -2761,8 +2813,8 @@ static void bond_miimon_commit(struct bo + struct slave *slave, *primary, *active; + bool do_failover = false; + struct list_head *iter; +- +- ASSERT_RTNL(); ++ struct net_device *slave_dev = NULL; ++ struct bond_cb *lag_cb_main; + + bond_for_each_slave(bond, slave, iter) { + switch (slave->link_new_state) { +@@ -2800,6 +2852,10 @@ static void bond_miimon_commit(struct bo + bond_set_active_slave(slave); + } + ++ if ((bond->params.mode == BOND_MODE_XOR) && ++ (!slave_dev)) ++ slave_dev = slave->dev; ++ + slave_info(bond->dev, slave->dev, "link status definitely up, %u Mbps %s duplex\n", + slave->speed == SPEED_UNKNOWN ? 0 : slave->speed, + slave->duplex ? "full" : "half"); +@@ -2848,6 +2904,14 @@ static void bond_miimon_commit(struct bo + unblock_netpoll_tx(); + } + ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ ++ if (slave_dev && lag_cb_main && lag_cb_main->bond_cb_link_up) ++ lag_cb_main->bond_cb_link_up(slave_dev); ++ ++ rcu_read_unlock(); ++ + bond_set_carrier(bond); + } + +@@ -4100,7 +4164,216 @@ static inline u32 bond_eth_hash(struct s + return 0; + + ep = (struct ethhdr *)(data + mhoff); +- return ep->h_dest[5] ^ ep->h_source[5] ^ be16_to_cpu(ep->h_proto); ++ return ep->h_dest[5] ^ ep->h_source[5]; ++} ++ ++/* Extract the appropriate headers based on bond's xmit policy */ ++static bool bond_flow_dissect_without_skb(struct bonding *bond, ++ u8 *src_mac, u8 *dst_mac, ++ void *psrc, void *pdst, ++ u16 protocol, __be16 *layer4hdr, ++ struct flow_keys *fk) ++{ ++ u32 *src = NULL; ++ u32 *dst = NULL; ++ ++ fk->ports.ports = 0; ++ src = (uint32_t *)psrc; ++ dst = (uint32_t *)pdst; ++ ++ if (protocol == htons(ETH_P_IP)) { ++ /* V4 addresses and address type*/ ++ fk->addrs.v4addrs.src = src[0]; ++ fk->addrs.v4addrs.dst = dst[0]; ++ fk->control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; ++ } else if (protocol == htons(ETH_P_IPV6)) { ++ /* V6 addresses and address type*/ ++ memcpy(&fk->addrs.v6addrs.src, src, sizeof(struct in6_addr)); ++ memcpy(&fk->addrs.v6addrs.dst, dst, sizeof(struct in6_addr)); ++ fk->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; ++ } else { ++ return false; ++ } ++ if ((bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER34) && ++ (layer4hdr)) ++ fk->ports.ports = *layer4hdr; ++ ++ return true; ++} ++ ++/* bond_xmit_hash_without_skb - Applies load balancing algorithm for a packet, ++ * to calculate hash for a given set of L2/L3 addresses. Does not ++ * calculate egress interface. ++ */ ++uint32_t bond_xmit_hash_without_skb(u8 *src_mac, u8 *dst_mac, ++ void *psrc, void *pdst, u16 protocol, ++ struct net_device *bond_dev, ++ __be16 *layer4hdr) ++{ ++ struct bonding *bond = netdev_priv(bond_dev); ++ struct flow_keys flow; ++ u32 hash = 0; ++ ++ if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER2 || ++ !bond_flow_dissect_without_skb(bond, src_mac, dst_mac, psrc, ++ pdst, protocol, layer4hdr, &flow)) ++ return (dst_mac[5] ^ src_mac[5]); ++ ++ if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER23) ++ hash = dst_mac[5] ^ src_mac[5]; ++ else if (layer4hdr) ++ hash = (__force u32)flow.ports.ports; ++ ++ hash ^= (__force u32)flow_get_u32_dst(&flow) ^ ++ (__force u32)flow_get_u32_src(&flow); ++ hash ^= (hash >> 16); ++ hash ^= (hash >> 8); ++ ++ return hash; ++} ++ ++/* bond_xor_get_tx_dev - Calculate egress interface for a given packet for a LAG ++ * that is configured in balance-xor mode ++ * @skb: pointer to skb to be egressed ++ * @src_mac: pointer to source L2 address ++ * @dst_mac: pointer to destination L2 address ++ * @src: pointer to source L3 address in network order ++ * @dst: pointer to destination L3 address in network order ++ * @protocol: L3 protocol ++ * @bond_dev: pointer to bond master device ++ * ++ * If @skb is NULL, bond_xmit_hash_without_skb is used to calculate hash using ++ * L2/L3 addresses. ++ * ++ * Returns: Either valid slave device, or NULL otherwise ++ */ ++static struct net_device *bond_xor_get_tx_dev(struct sk_buff *skb, ++ u8 *src_mac, u8 *dst_mac, ++ void *src, void *dst, ++ u16 protocol, ++ struct net_device *bond_dev, ++ __be16 *layer4hdr) ++{ ++ struct bonding *bond = netdev_priv(bond_dev); ++ int slave_cnt = READ_ONCE(bond->slave_cnt); ++ int slave_id = 0, i = 0; ++ u32 hash; ++ struct list_head *iter; ++ struct slave *slave; ++ ++ if (slave_cnt == 0) { ++ pr_debug("%s: Error: No slave is attached to the interface\n", ++ bond_dev->name); ++ return NULL; ++ } ++ ++ if (skb) { ++ hash = bond_xmit_hash(bond, skb); ++ slave_id = hash % slave_cnt; ++ } else { ++ if (bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER23 && ++ bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER2 && ++ bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER34) { ++ pr_debug("%s: Error: Unsupported hash policy for balance-XOR fast path\n", ++ bond_dev->name); ++ return NULL; ++ } ++ ++ hash = bond_xmit_hash_without_skb(src_mac, dst_mac, src, ++ dst, protocol, bond_dev, ++ layer4hdr); ++ slave_id = hash % slave_cnt; ++ } ++ ++ i = slave_id; ++ ++ /* Here we start from the slave with slave_id */ ++ bond_for_each_slave_rcu(bond, slave, iter) { ++ if (--i < 0) { ++ if (bond_slave_can_tx(slave)) ++ return slave->dev; ++ } ++ } ++ ++ /* Here we start from the first slave up to slave_id */ ++ i = slave_id; ++ bond_for_each_slave_rcu(bond, slave, iter) { ++ if (--i < 0) ++ break; ++ if (bond_slave_can_tx(slave)) ++ return slave->dev; ++ } ++ ++ return NULL; ++} ++ ++/* bond_get_tx_dev - Calculate egress interface for a given packet. ++ * ++ * Supports 802.3AD and balance-xor modes ++ * ++ * @skb: pointer to skb to be egressed, if valid ++ * @src_mac: pointer to source L2 address ++ * @dst_mac: pointer to destination L2 address ++ * @src: pointer to source L3 address in network order ++ * @dst: pointer to destination L3 address in network order ++ * @protocol: L3 protocol id from L2 header ++ * @bond_dev: pointer to bond master device ++ * ++ * Returns: Either valid slave device, or NULL for un-supported LAG modes ++ */ ++struct net_device *bond_get_tx_dev(struct sk_buff *skb, uint8_t *src_mac, ++ u8 *dst_mac, void *src, ++ void *dst, u16 protocol, ++ struct net_device *bond_dev, ++ __be16 *layer4hdr) ++{ ++ struct bonding *bond; ++ ++ if (!bond_dev) ++ return NULL; ++ ++ if (!((bond_dev->priv_flags & IFF_BONDING) && ++ (bond_dev->flags & IFF_MASTER))) ++ return NULL; ++ ++ bond = netdev_priv(bond_dev); ++ ++ switch (bond->params.mode) { ++ case BOND_MODE_XOR: ++ return bond_xor_get_tx_dev(skb, src_mac, dst_mac, ++ src, dst, protocol, ++ bond_dev, layer4hdr); ++ case BOND_MODE_8023AD: ++ return bond_3ad_get_tx_dev(skb, src_mac, dst_mac, ++ src, dst, protocol, ++ bond_dev, layer4hdr); ++ default: ++ return NULL; ++ } ++} ++EXPORT_SYMBOL(bond_get_tx_dev); ++ ++/* In bond_xmit_xor() , we determine the output device by using a pre- ++ * determined xmit_hash_policy(), If the selected device is not enabled, ++ * find the next active slave. ++ */ ++static int bond_xmit_xor(struct sk_buff *skb, struct net_device *dev) ++{ ++ struct bonding *bond = netdev_priv(dev); ++ struct net_device *outdev; ++ ++ outdev = bond_xor_get_tx_dev(skb, NULL, NULL, NULL, ++ NULL, 0, dev, NULL); ++ if (!outdev) ++ goto out; ++ ++ bond_dev_queue_xmit(bond, skb, outdev); ++ goto final; ++out: ++ /* no suitable interface, frame not sent */ ++ dev_kfree_skb(skb); ++final: ++ return NETDEV_TX_OK; + } + + static bool bond_flow_ip(struct sk_buff *skb, struct flow_keys *fk, const void *data, +@@ -5230,15 +5503,16 @@ static netdev_tx_t bond_3ad_xor_xmit(str + struct net_device *dev) + { + struct bonding *bond = netdev_priv(dev); +- struct bond_up_slave *slaves; +- struct slave *slave; ++ struct net_device *outdev = NULL; + +- slaves = rcu_dereference(bond->usable_slaves); +- slave = bond_xmit_3ad_xor_slave_get(bond, skb, slaves); +- if (likely(slave)) +- return bond_dev_queue_xmit(bond, skb, slave->dev); ++ outdev = bond_3ad_get_tx_dev(skb, NULL, NULL, NULL, ++ NULL, 0, dev, NULL); ++ if (!outdev) { ++ dev_kfree_skb(skb); ++ return NETDEV_TX_OK; ++ } + +- return bond_tx_drop(dev, skb); ++ return bond_dev_queue_xmit(bond, skb, outdev); + } + + /* in broadcast mode, we send everything to all usable interfaces. */ +@@ -5488,8 +5762,9 @@ static netdev_tx_t __bond_start_xmit(str + return bond_xmit_roundrobin(skb, dev); + case BOND_MODE_ACTIVEBACKUP: + return bond_xmit_activebackup(skb, dev); +- case BOND_MODE_8023AD: + case BOND_MODE_XOR: ++ return bond_xmit_xor(skb, dev); ++ case BOND_MODE_8023AD: + return bond_3ad_xor_xmit(skb, dev); + case BOND_MODE_BROADCAST: + return bond_xmit_broadcast(skb, dev); +@@ -5926,6 +6201,9 @@ static void bond_destructor(struct net_d + if (bond->wq) + destroy_workqueue(bond->wq); + ++ if (bond->id != (~0U)) ++ clear_bit(bond->id, &bond_id_mask); ++ + free_percpu(bond->rr_tx_counter); + } + +@@ -6479,6 +6757,11 @@ int bond_create(struct net *net, const c + + bond_work_init_all(bond); + ++ bond->id = ~0U; ++ if (bond_id_mask != (~0UL)) { ++ bond->id = (u32)ffz(bond_id_mask); ++ set_bit(bond->id, &bond_id_mask); ++ } + out: + rtnl_unlock(); + return res; +--- a/include/net/bond_3ad.h ++++ b/include/net/bond_3ad.h +@@ -302,6 +302,12 @@ int bond_3ad_lacpdu_recv(const struct sk + struct slave *slave); + int bond_3ad_set_carrier(struct bonding *bond); + void bond_3ad_update_lacp_rate(struct bonding *bond); ++struct net_device *bond_3ad_get_tx_dev(struct sk_buff *skb, uint8_t *src_mac, ++ uint8_t *dst_mac, void *src, ++ void *dst, uint16_t protocol, ++ struct net_device *bond_dev, ++ __be16 *layer4hdr); ++ + void bond_3ad_update_ad_actor_settings(struct bonding *bond); + int bond_3ad_stats_fill(struct sk_buff *skb, struct bond_3ad_stats *stats); + size_t bond_3ad_stats_size(void); +--- a/include/net/bonding.h ++++ b/include/net/bonding.h +@@ -90,6 +90,8 @@ + #define BOND_XFRM_FEATURES (NETIF_F_HW_ESP | NETIF_F_HW_ESP_TX_CSUM | \ + NETIF_F_GSO_ESP) + ++extern struct bond_cb __rcu *bond_cb; ++ + #ifdef CONFIG_NET_POLL_CONTROLLER + extern atomic_t netpoll_block_tx; + +@@ -261,6 +263,7 @@ struct bonding { + struct mutex ipsec_lock; + #endif /* CONFIG_XFRM_OFFLOAD */ + struct bpf_prog *xdp_prog; ++ u32 id; + }; + + #define bond_slave_get_rcu(dev) \ +@@ -683,6 +686,11 @@ struct bond_vlan_tag *bond_verify_device + int level); + int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave); + void bond_slave_arr_work_rearm(struct bonding *bond, unsigned long delay); ++uint32_t bond_xmit_hash_without_skb(uint8_t *src_mac, uint8_t *dst_mac, ++ void *psrc, void *pdst, uint16_t protocol, ++ struct net_device *bond_dev, ++ __be16 *layer4hdr); ++ + void bond_work_init_all(struct bonding *bond); + + #ifdef CONFIG_PROC_FS +@@ -787,4 +795,17 @@ static inline netdev_tx_t bond_tx_drop(s + return NET_XMIT_DROP; + } + ++struct bond_cb { ++ void (*bond_cb_link_up)(struct net_device *slave); ++ void (*bond_cb_link_down)(struct net_device *slave); ++ void (*bond_cb_enslave)(struct net_device *slave); ++ void (*bond_cb_release)(struct net_device *slave); ++ void (*bond_cb_delete_by_slave)(struct net_device *slave); ++ void (*bond_cb_delete_by_mac)(uint8_t *mac_addr); ++}; ++ ++extern int bond_register_cb(struct bond_cb *cb); ++extern void bond_unregister_cb(void); ++extern int bond_get_id(struct net_device *bond_dev); ++ + #endif /* _NET_BONDING_H */ diff --git a/target/linux/ipq806x/patches-6.6/990-011-netfilter-nf_conntrack_ecache-Fix-NSS-ECM-BRK-kernel-panic.patch b/target/linux/ipq806x/patches-6.6/990-011-netfilter-nf_conntrack_ecache-Fix-NSS-ECM-BRK-kernel-panic.patch new file mode 100644 index 0000000000..c5e61d7ab7 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-011-netfilter-nf_conntrack_ecache-Fix-NSS-ECM-BRK-kernel-panic.patch @@ -0,0 +1,10 @@ +--- a/net/netfilter/nf_conntrack_ecache.c ++++ b/net/netfilter/nf_conntrack_ecache.c +@@ -324,7 +324,6 @@ void nf_conntrack_register_notifier(stru + mutex_lock(&nf_ct_ecache_mutex); + notify = rcu_dereference_protected(net->ct.nf_conntrack_event_cb, + lockdep_is_held(&nf_ct_ecache_mutex)); +- WARN_ON_ONCE(notify); + rcu_assign_pointer(net->ct.nf_conntrack_event_cb, new); + mutex_unlock(&nf_ct_ecache_mutex); + } diff --git a/target/linux/ipq806x/patches-6.6/990-012-bridge-fix-bridge-vlan-filter-toggle-issue.patch b/target/linux/ipq806x/patches-6.6/990-012-bridge-fix-bridge-vlan-filter-toggle-issue.patch new file mode 100644 index 0000000000..7884b70166 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-012-bridge-fix-bridge-vlan-filter-toggle-issue.patch @@ -0,0 +1,33 @@ +From 337dafaa0d7bf49e25b4ec74ff44ca697abd06bc Mon Sep 17 00:00:00 2001 +From: Ken Zhu +Date: Tue, 2 May 2023 21:39:54 -0700 +Subject: [PATCH 213/281] bridge: fix bridge vlan filter toggle issue + +when vlan filter is disabled, it could cause new bridge +creation failed if vlan filter setting existing in the +netlink creation message + +Change-Id: I02f010e746e94623fda544846d05908a83d14fa2 +Signed-off-by: Ken Zhu +--- + net/bridge/br_netlink.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/net/bridge/br_netlink.c ++++ b/net/bridge/br_netlink.c +@@ -1320,6 +1320,7 @@ static int br_changelink(struct net_devi + br_stp_set_bridge_priority(br, priority); + } + ++#ifdef CONFIG_BRIDGE_VLAN_FILTERING + if (data[IFLA_BR_VLAN_FILTERING]) { + u8 vlan_filter = nla_get_u8(data[IFLA_BR_VLAN_FILTERING]); + +@@ -1328,7 +1329,6 @@ static int br_changelink(struct net_devi + return err; + } + +-#ifdef CONFIG_BRIDGE_VLAN_FILTERING + if (data[IFLA_BR_VLAN_PROTOCOL]) { + __be16 vlan_proto = nla_get_be16(data[IFLA_BR_VLAN_PROTOCOL]); + diff --git a/target/linux/ipq806x/patches-6.6/990-013-bridge-Add-new-bridge-APIs-needed-for-network-HW-acc.patch b/target/linux/ipq806x/patches-6.6/990-013-bridge-Add-new-bridge-APIs-needed-for-network-HW-acc.patch new file mode 100644 index 0000000000..aae901253b --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-013-bridge-Add-new-bridge-APIs-needed-for-network-HW-acc.patch @@ -0,0 +1,165 @@ +From b5f89637414ada5125cbc9138f5b27cbfa8fe1b5 Mon Sep 17 00:00:00 2001 +From: Murat Sezgin +Date: Wed, 10 May 2023 13:15:19 -0700 +Subject: [PATCH 219/281] bridge: Add new bridge APIs needed for network HW + acceleration + +Bridge acceleration hardware needs to perform certain operations, + currently unsupported by the existing bridge code: + *given a certain bridge device and a MAC@, figure out on + which underlying slave port this MAC@ is located + *trigger an FDB entry refresh from outside the bridge code: packets will + not be seen anymore in the host, once a flow has been accelerated. + So the hardware accelerator driver needs to keep track of the accelerated + flows and refresh the MACs accordingly. + *update bridge interface statistics from outside the bridge code: + once acceleration is enabled on a connection, packets will not flow + through the host CPU, so we need the hardware accelerator driver to + maintain the statistics on the host and update them and add whatever + flows through the hardware. + These change adds the corresponding functions, and make it available + to other through EXPORT_SYMBOLS(). + +Change-Id: I67afb325796004053897d9916e2df91827b65139 +Signed-off-by: Murat Sezgin + +net: bridge: Rework APIs added for network HW acceleration + +This change is to rework the bridge APIs based on the feedback from the +chromium community + +Change-Id: I92a65f1eb26c6700d871f9deb40bccf302cf18eb +Signed-off-by: Murat Sezgin +Signed-off-by: Murat Sezgin +--- + include/linux/if_bridge.h | 8 ++++++ + net/bridge/br_fdb.c | 38 ++++++++++++++++++++++++++++ + net/bridge/br_if.c | 52 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 98 insertions(+) + +--- a/include/linux/if_bridge.h ++++ b/include/linux/if_bridge.h +@@ -71,6 +71,14 @@ 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 struct net_device *br_port_dev_get(struct net_device *dev, ++ unsigned char *addr); ++extern void br_refresh_fdb_entry(struct net_device *dev, const char *addr); ++extern void br_dev_update_stats(struct net_device *dev, ++ struct rtnl_link_stats64 *nlstats); ++extern struct net_bridge_fdb_entry *br_fdb_has_entry(struct net_device *dev, ++ const char *addr, ++ __u16 vid); + + #if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING) + int br_multicast_list_adjacent(struct net_device *dev, +--- a/net/bridge/br_fdb.c ++++ b/net/bridge/br_fdb.c +@@ -914,6 +914,44 @@ void br_fdb_update(struct net_bridge *br + } + } + ++/* 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); ++ ++/* 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); ++ + /* Dump information about entries, in response to GETNEIGH */ + int br_fdb_dump(struct sk_buff *skb, + struct netlink_callback *cb, +--- a/net/bridge/br_if.c ++++ b/net/bridge/br_if.c +@@ -775,3 +775,59 @@ bool br_port_flag_is_set(const struct ne + return p->flags & flag; + } + EXPORT_SYMBOL_GPL(br_port_flag_is_set); ++ ++/* br_port_dev_get() ++ * Using the given addr, 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 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; ++ ++ br = netdev_priv(dev); ++ ++ /* Lookup the fdb entry and get reference to the port dev */ ++ rcu_read_lock(); ++ fdbe = br_fdb_find_rcu(br, addr, 0); ++ if (fdbe && fdbe->dst) { ++ netdev = fdbe->dst->dev; /* port device */ ++ dev_hold(netdev); ++ } ++ rcu_read_unlock(); ++ return netdev; ++} ++EXPORT_SYMBOL_GPL(br_port_dev_get); ++ ++/* Update bridge statistics for bridge packets processed by offload engines */ ++void br_dev_update_stats(struct net_device *dev, ++ struct rtnl_link_stats64 *nlstats) ++{ ++ struct net_bridge *br; ++ struct pcpu_sw_netstats *stats; ++ ++ /* Is this a bridge? */ ++ if (!(dev->priv_flags & IFF_EBRIDGE)) ++ return; ++ ++ br = netdev_priv(dev); ++ stats = this_cpu_ptr(dev->tstats); ++ ++ local_bh_disable(); ++ u64_stats_update_begin(&stats->syncp); ++ u64_stats_add(&stats->rx_packets, nlstats->rx_packets); ++ u64_stats_add(&stats->rx_bytes, nlstats->rx_bytes); ++ u64_stats_add(&stats->tx_packets, nlstats->tx_packets); ++ u64_stats_add(&stats->tx_bytes, nlstats->tx_bytes); ++ u64_stats_update_end(&stats->syncp); ++ local_bh_enable(); ++} ++EXPORT_SYMBOL_GPL(br_dev_update_stats); diff --git a/target/linux/ipq806x/patches-6.6/990-014-net-bridge-Added-bridge-fdb-update-notify-chain.patch b/target/linux/ipq806x/patches-6.6/990-014-net-bridge-Added-bridge-fdb-update-notify-chain.patch new file mode 100644 index 0000000000..243fc4bb73 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-014-net-bridge-Added-bridge-fdb-update-notify-chain.patch @@ -0,0 +1,99 @@ +From ae56f1dceb55a3e8e5fadf9bc109270436e3d8fa Mon Sep 17 00:00:00 2001 +From: Murat Sezgin +Date: Thu, 18 May 2023 21:11:09 -0700 +Subject: [PATCH 220/281] net: bridge: Added bridge fdb update notify chain + +Registered modules are notified based on two events: +1. Dst port changes for any existing bridge fdb entry +2. Bridge fdb entry ages out/deleted + +Change-Id: Iaba8fba6859bcc8e638df4f2e9900c131b1a6d34 +Signed-off-by: ratheesh kannoth +Signed-off-by: Murat Sezgin + +net: bridge : delete fdb entry before callbacks + +Ageing timer expires and deletes corresponding fdb entry. Registered +callbacks for br_fdb_update_notifier chains are called one by one. +Since callback invocation is before the deletion of fdb entry, +it may lead to race condition. Fix is to reorder it. + +Change-Id: Idefce6d879bfa6104cadc495aa61d164db214c97 +Signed-off-by: ratheesh kannoth +Signed-off-by: Murat Sezgin +Signed-off-by: Murat Sezgin +--- + include/linux/if_bridge.h | 2 ++ + net/bridge/br_fdb.c | 28 ++++++++++++++++++++++++++-- + 2 files changed, 28 insertions(+), 2 deletions(-) + +--- a/include/linux/if_bridge.h ++++ b/include/linux/if_bridge.h +@@ -79,6 +79,8 @@ extern void br_dev_update_stats(struct n + 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); + + #if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING) + int br_multicast_list_adjacent(struct net_device *dev, +--- a/net/bridge/br_fdb.c ++++ b/net/bridge/br_fdb.c +@@ -519,6 +519,20 @@ out: + spin_unlock_bh(&br->hash_lock); + } + ++ATOMIC_NOTIFIER_HEAD(br_fdb_update_notifier_list); ++ ++void br_fdb_update_register_notify(struct notifier_block *nb) ++{ ++ atomic_notifier_chain_register(&br_fdb_update_notifier_list, nb); ++} ++EXPORT_SYMBOL_GPL(br_fdb_update_register_notify); ++ ++void br_fdb_update_unregister_notify(struct notifier_block *nb) ++{ ++ atomic_notifier_chain_unregister(&br_fdb_update_notifier_list, nb); ++} ++EXPORT_SYMBOL_GPL(br_fdb_update_unregister_notify); ++ + void br_fdb_cleanup(struct work_struct *work) + { + struct net_bridge *br = container_of(work, struct net_bridge, +@@ -527,6 +541,8 @@ 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]; ++ + + /* this part is tricky, in order to avoid blocking learning and + * consequently forwarding, we rely on rcu to delete objects with +@@ -553,8 +569,12 @@ 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)) +- fdb_delete(br, f, true); ++ if (!hlist_unhashed(&f->fdb_node)) { ++ ether_addr_copy(mac_addr, f->key.addr.addr); ++ fdb_delete(br, f, true); ++ atomic_notifier_call_chain(&br_fdb_update_notifier_list, 0, ++ (void *)mac_addr); ++ } + spin_unlock_bh(&br->hash_lock); + } + } +@@ -886,6 +906,11 @@ void br_fdb_update(struct net_bridge *br + &fdb->flags))) + clear_bit(BR_FDB_ADDED_BY_EXT_LEARN, + &fdb->flags); ++ ++ atomic_notifier_call_chain( ++ &br_fdb_update_notifier_list, ++ 0, (void *)addr); ++ + /* Clear locked flag when roaming to an + * unlocked port. + */ diff --git a/target/linux/ipq806x/patches-6.6/990-015-Updated-the-br_port_dev_get-function-to-take-an-skb.patch b/target/linux/ipq806x/patches-6.6/990-015-Updated-the-br_port_dev_get-function-to-take-an-skb.patch new file mode 100644 index 0000000000..3e32239cc6 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-015-Updated-the-br_port_dev_get-function-to-take-an-skb.patch @@ -0,0 +1,112 @@ +From 449a86c59f4c7c0c58314755b9b638145e8a8d5a Mon Sep 17 00:00:00 2001 +From: Murat Sezgin +Date: Wed, 11 Mar 2020 11:20:40 -0700 +Subject: [PATCH 221/281] Updated the br_port_dev_get function to take an skb + +- If the skb is non-NULL, call the br_port_dev_get_hook (if it exists), + to try and fetch the port. +- If there is no skb or no hook, or the hook returns NULL, lookup in FDB + +Change-Id: I60df83dfa7a07b39cd30833cba0d4397859711f4 +Signed-off-by: Murat Sezgin +--- + include/linux/if_bridge.h | 9 ++++++++- + net/bridge/br_if.c | 38 ++++++++++++++++++++++++++++++++++---- + 2 files changed, 42 insertions(+), 5 deletions(-) + +--- a/include/linux/if_bridge.h ++++ b/include/linux/if_bridge.h +@@ -72,7 +72,8 @@ void brioctl_set(int (*hook)(struct net + int br_ioctl_call(struct net *net, struct net_bridge *br, unsigned int cmd, + struct ifreq *ifr, void __user *uarg); + extern struct net_device *br_port_dev_get(struct net_device *dev, +- unsigned char *addr); ++ unsigned char *addr, ++ struct sk_buff *skb); + extern void br_refresh_fdb_entry(struct net_device *dev, const char *addr); + extern void br_dev_update_stats(struct net_device *dev, + struct rtnl_link_stats64 *nlstats); +@@ -223,4 +224,10 @@ static inline clock_t br_get_ageing_time + } + #endif + ++typedef struct net_bridge_port *br_port_dev_get_hook_t(struct net_device *dev, ++ struct sk_buff *skb); ++extern br_port_dev_get_hook_t __rcu *br_port_dev_get_hook; ++ ++typedef void (br_notify_hook_t)(int group, int event, const void *ptr); ++extern br_notify_hook_t __rcu *br_notify_hook; + #endif +--- a/net/bridge/br_if.c ++++ b/net/bridge/br_if.c +@@ -26,6 +26,10 @@ + + #include "br_private.h" + ++/* 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); ++ + /* + * Determine initial path cost based on speed. + * using recommendations from 802.1d standard +@@ -777,13 +781,17 @@ bool br_port_flag_is_set(const struct ne + EXPORT_SYMBOL_GPL(br_port_flag_is_set); + + /* br_port_dev_get() +- * Using the given addr, identify the port to which it is reachable, +- * returing a reference to the net device associated with that port. ++ * 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 net_device *br_port_dev_get(struct net_device *dev, unsigned char *addr, ++ struct sk_buff *skb) + { + struct net_bridge_fdb_entry *fdbe; + struct net_bridge *br; +@@ -793,15 +801,37 @@ struct net_device *br_port_dev_get(struc + 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); ++ 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 */ +- rcu_read_lock(); + 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; + } diff --git a/target/linux/ipq806x/patches-6.6/990-016-vlan-Add-vlan-stats-update-function.patch b/target/linux/ipq806x/patches-6.6/990-016-vlan-Add-vlan-stats-update-function.patch new file mode 100644 index 0000000000..3148fd2ada --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-016-vlan-Add-vlan-stats-update-function.patch @@ -0,0 +1,66 @@ +From 14b84b541426e1237e33aced57d08a25d1885098 Mon Sep 17 00:00:00 2001 +From: Murat Sezgin +Date: Tue, 27 Jan 2015 18:06:30 +0000 +Subject: [PATCH 222/281] vlan: Add vlan stats update function + +Change-Id: Ib1165b37f29d48e5dbcee2c709b2f873dfb3e34d +Signed-off-by: Murat Sezgin +--- + include/linux/if_vlan.h | 9 +++++++++ + net/8021q/vlan_core.c | 20 ++++++++++++++++++++ + 2 files changed, 29 insertions(+) + +--- a/include/linux/if_vlan.h ++++ b/include/linux/if_vlan.h +@@ -143,6 +143,9 @@ extern struct net_device *__vlan_find_de + extern int vlan_for_each(struct net_device *dev, + int (*action)(struct net_device *dev, int vid, + void *arg), void *arg); ++extern void __vlan_dev_update_accel_stats(struct net_device *dev, ++ struct rtnl_link_stats64 *stats); ++ + extern struct net_device *vlan_dev_real_dev(const struct net_device *dev); + extern u16 vlan_dev_vlan_id(const struct net_device *dev); + extern __be16 vlan_dev_vlan_proto(const struct net_device *dev); +@@ -236,6 +239,12 @@ extern void vlan_vids_del_by_dev(struct + extern bool vlan_uses_dev(const struct net_device *dev); + + #else ++static inline void __vlan_dev_update_accel_stats(struct net_device *dev, ++ struct rtnl_link_stats64 *stats) ++{ ++ ++} ++ + static inline struct net_device * + __vlan_find_dev_deep_rcu(struct net_device *real_dev, + __be16 vlan_proto, u16 vlan_id) +--- a/net/8021q/vlan_core.c ++++ b/net/8021q/vlan_core.c +@@ -72,6 +72,26 @@ bool vlan_do_receive(struct sk_buff **sk + return true; + } + ++/* Update the VLAN device with statistics from network offload engines */ ++void __vlan_dev_update_accel_stats(struct net_device *dev, ++ struct rtnl_link_stats64 *nlstats) ++{ ++ struct vlan_pcpu_stats *stats; ++ ++ if (!is_vlan_dev(dev)) ++ return; ++ ++ stats = this_cpu_ptr(vlan_dev_priv(dev)->vlan_pcpu_stats); ++ ++ u64_stats_update_begin(&stats->syncp); ++ u64_stats_add(&stats->rx_packets, nlstats->rx_packets); ++ u64_stats_add(&stats->rx_bytes, nlstats->rx_bytes); ++ u64_stats_add(&stats->tx_packets, nlstats->tx_packets); ++ u64_stats_add(&stats->tx_bytes, nlstats->tx_bytes); ++ u64_stats_update_end(&stats->syncp); ++} ++EXPORT_SYMBOL(__vlan_dev_update_accel_stats); ++ + /* Must be invoked with rcu_read_lock. */ + struct net_device *__vlan_find_dev_deep_rcu(struct net_device *dev, + __be16 vlan_proto, u16 vlan_id) diff --git a/target/linux/ipq806x/patches-6.6/990-017-linux-bridge-add-patch-for-IGMP-MLD-snooping.patch b/target/linux/ipq806x/patches-6.6/990-017-linux-bridge-add-patch-for-IGMP-MLD-snooping.patch new file mode 100644 index 0000000000..0187cb4d4a --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-017-linux-bridge-add-patch-for-IGMP-MLD-snooping.patch @@ -0,0 +1,123 @@ +From 84fa3efb7d8eeb1c3f2daf9a3034e2794b3294f9 Mon Sep 17 00:00:00 2001 +From: Zhi Chen +Date: Mon, 13 Apr 2015 16:39:06 -0700 +Subject: [PATCH 223/281] linux: bridge: add patch for IGMP/MLD snooping + +Kernel patch for the IGMP/MLD snooping module, which is a replacement +and enhancement of Linux bridge multicast snooping. With the IGMP/MLD +snooping module, we can support IGMPv3/MLDv2 on Linux bridge. +Furthermore, with additional plugins, we can support switch port-based +snooping and WiFi conversion from multicast to unicast. +This patch exported a couple of bridge APIs and added two packet hooks +on the input/output path. + +Change-Id: I3507f2b793155856b4dac8ad5937a9fc6d28551d +Signed-off-by: Zhi Chen +Signed-off-by: Murat Sezgin +--- + include/linux/if_bridge.h | 6 ++++++ + net/bridge/br_device.c | 5 +++++ + net/bridge/br_input.c | 12 +++++++++++- + net/bridge/br_private.h | 5 +++++ + 4 files changed, 27 insertions(+), 1 deletion(-) + +--- a/include/linux/if_bridge.h ++++ b/include/linux/if_bridge.h +@@ -66,11 +66,13 @@ struct br_ip_list { + #define BR_DEFAULT_AGEING_TIME (300 * HZ) + + struct net_bridge; ++struct net_bridge_port; + void brioctl_set(int (*hook)(struct net *net, struct net_bridge *br, + unsigned int cmd, struct ifreq *ifr, + void __user *uarg)); + int br_ioctl_call(struct net *net, struct net_bridge *br, unsigned int cmd, + struct ifreq *ifr, void __user *uarg); ++ + extern struct net_device *br_port_dev_get(struct net_device *dev, + unsigned char *addr, + struct sk_buff *skb); +@@ -230,4 +232,8 @@ extern br_port_dev_get_hook_t __rcu *br_ + + typedef void (br_notify_hook_t)(int group, int event, const void *ptr); + extern br_notify_hook_t __rcu *br_notify_hook; ++typedef int (br_multicast_handle_hook_t)(const struct net_bridge_port *src, ++ struct sk_buff *skb); ++extern br_multicast_handle_hook_t __rcu *br_multicast_handle_hook; ++ + #endif +--- a/net/bridge/br_device.c ++++ b/net/bridge/br_device.c +@@ -89,6 +89,11 @@ netdev_tx_t br_dev_xmit(struct sk_buff * + if (is_broadcast_ether_addr(dest)) { + br_flood(br, skb, BR_PKT_BROADCAST, false, true, vid); + } else if (is_multicast_ether_addr(dest)) { ++ br_multicast_handle_hook_t *multicast_handle_hook = ++ rcu_dereference(br_multicast_handle_hook); ++ if (!__br_get(multicast_handle_hook, true, NULL, skb)) ++ goto out; ++ + if (unlikely(netpoll_tx_running(dev))) { + br_flood(br, skb, BR_PKT_MULTICAST, false, true, vid); + goto out; +--- a/net/bridge/br_input.c ++++ b/net/bridge/br_input.c +@@ -30,7 +30,11 @@ br_netif_receive_skb(struct net *net, st + return netif_receive_skb(skb); + } + +-static int br_pass_frame_up(struct sk_buff *skb, bool promisc) ++/* Hook for external Multicast handler */ ++br_multicast_handle_hook_t __rcu *br_multicast_handle_hook __read_mostly; ++EXPORT_SYMBOL_GPL(br_multicast_handle_hook); ++ ++int br_pass_frame_up(struct sk_buff *skb, bool promisc) + { + struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev; + struct net_bridge *br = netdev_priv(brdev); +@@ -71,6 +75,7 @@ static int br_pass_frame_up(struct sk_bu + dev_net(indev), NULL, skb, indev, NULL, + br_netif_receive_skb); + } ++EXPORT_SYMBOL_GPL(br_pass_frame_up); + + /* note: already called with rcu_read_lock */ + int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb) +@@ -85,6 +90,7 @@ int br_handle_frame_finish(struct net *n + struct net_bridge_vlan *vlan; + struct net_bridge *br; + bool promisc; ++ br_multicast_handle_hook_t *multicast_handle_hook; + u16 vid = 0; + u8 state; + +@@ -182,6 +188,10 @@ int br_handle_frame_finish(struct net *n + + switch (pkt_type) { + case BR_PKT_MULTICAST: ++ multicast_handle_hook = rcu_dereference(br_multicast_handle_hook); ++ if (!__br_get(multicast_handle_hook, true, p, skb)) ++ goto out; ++ + mdst = br_mdb_get(brmctx, skb, vid); + if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) && + br_multicast_querier_exists(brmctx, eth_hdr(skb), mdst)) { +--- a/net/bridge/br_private.h ++++ b/net/bridge/br_private.h +@@ -907,6 +907,7 @@ void br_manage_promisc(struct net_bridge + int nbp_backup_change(struct net_bridge_port *p, struct net_device *backup_dev); + + /* br_input.c */ ++int br_pass_frame_up(struct sk_buff *skb, bool promisc); + int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb); + rx_handler_func_t *br_get_rx_handler(const struct net_device *dev); + +@@ -2268,4 +2269,8 @@ void br_do_suppress_nd(struct sk_buff *s + 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); + bool br_is_neigh_suppress_enabled(const struct net_bridge_port *p, u16 vid); ++ ++#define __br_get(__hook, __default, __args ...) \ ++ (__hook ? (__hook(__args)) : (__default)) ++ + #endif diff --git a/target/linux/ipq806x/patches-6.6/990-018-ipv6-Export-ndisc_send_ns-function-for-public-use.patch b/target/linux/ipq806x/patches-6.6/990-018-ipv6-Export-ndisc_send_ns-function-for-public-use.patch new file mode 100644 index 0000000000..f553b26f64 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-018-ipv6-Export-ndisc_send_ns-function-for-public-use.patch @@ -0,0 +1,24 @@ +From c38b43a2a98eaf2dd3401998e982716e98339907 Mon Sep 17 00:00:00 2001 +From: Murat Sezgin +Date: Tue, 27 Jan 2015 11:10:26 -0800 +Subject: [PATCH 224/281] ipv6: Export ndisc_send_ns() function for public use. + +This API is needed to implement a connection +manager for hardware accelaration. + +Change-Id: Idcf5fa1d3d3151fa2a5e0c48c6b6cdd10fc335bf +Signed-off-by: Murat Sezgin +--- + net/ipv6/ndisc.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/net/ipv6/ndisc.c ++++ b/net/ipv6/ndisc.c +@@ -670,6 +670,7 @@ void ndisc_send_ns(struct net_device *de + if (skb) + ndisc_send_skb(skb, daddr, saddr); + } ++EXPORT_SYMBOL(ndisc_send_ns); + + void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, + const struct in6_addr *daddr) diff --git a/target/linux/ipq806x/patches-6.6/990-019-net-ipv4-ipv6-Added-route-table-update-notify-chain.patch b/target/linux/ipq806x/patches-6.6/990-019-net-ipv4-ipv6-Added-route-table-update-notify-chain.patch new file mode 100644 index 0000000000..41f13d8531 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-019-net-ipv4-ipv6-Added-route-table-update-notify-chain.patch @@ -0,0 +1,150 @@ +From 776142dbbc2a3da1d47d1042822f72ba3127417b Mon Sep 17 00:00:00 2001 +From: Murat Sezgin +Date: Wed, 10 Feb 2016 15:38:12 -0800 +Subject: [PATCH 225/281] net: ipv4/ipv6: Added route table update notify chain + +Registered modules are notified for the changes in the +route table. + +NOTE: This patch is originally suggested by "Jozsef Kadlecsik" +in the following email chain but never merged to any of the +kernel versions. +http://www.spinics.net/lists/netfilter-devel/msg24239.html + +Cc: Jozsef Kadlecsik +Patch-mainline: netfilter-devel@vger.kernel.org + +Change-Id: I402710474be0978867dfbbbb8d1a7f8c8f3dab06 +Signed-off-by: Murat Sezgin +--- + include/net/ip6_route.h | 3 +++ + include/net/route.h | 3 +++ + net/ipv4/fib_trie.c | 19 +++++++++++++++++++ + net/ipv6/route.c | 22 +++++++++++++++++++++- + 4 files changed, 46 insertions(+), 1 deletion(-) + +--- a/include/net/ip6_route.h ++++ b/include/net/ip6_route.h +@@ -215,6 +215,9 @@ void rt6_multipath_rebalance(struct fib6 + void rt6_uncached_list_add(struct rt6_info *rt); + void rt6_uncached_list_del(struct rt6_info *rt); + ++int rt6_register_notifier(struct notifier_block *nb); ++int rt6_unregister_notifier(struct notifier_block *nb); ++ + 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/route.h ++++ b/include/net/route.h +@@ -237,6 +237,9 @@ struct rtable *rt_dst_alloc(struct net_d + unsigned int flags, u16 type, bool noxfrm); + struct rtable *rt_dst_clone(struct net_device *dev, struct rtable *rt); + ++int ip_rt_register_notifier(struct notifier_block *nb); ++int ip_rt_unregister_notifier(struct notifier_block *nb); ++ + struct in_ifaddr; + void fib_add_ifaddr(struct in_ifaddr *); + void fib_del_ifaddr(struct in_ifaddr *, struct in_ifaddr *); +--- a/net/ipv4/fib_trie.c ++++ b/net/ipv4/fib_trie.c +@@ -1211,6 +1211,9 @@ static bool fib_valid_key_len(u32 key, u + static void fib_remove_alias(struct trie *t, struct key_vector *tp, + struct key_vector *l, struct fib_alias *old); + ++/* Define route change notification chain. */ ++static BLOCKING_NOTIFIER_HEAD(iproute_chain); ++ + /* Caller must hold RTNL. */ + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg, struct netlink_ext_ack *extack) +@@ -1404,6 +1407,8 @@ 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: +@@ -1776,6 +1781,8 @@ 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; +@@ -2408,6 +2415,18 @@ void __init fib_trie_init(void) + 0, SLAB_PANIC | SLAB_ACCOUNT, NULL); + } + ++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); ++ + struct fib_table *fib_trie_table(u32 id, struct fib_table *alias) + { + struct fib_table *tb; +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -198,6 +198,9 @@ static void rt6_uncached_list_flush_dev( + } + } + ++/* Define route change notification chain. */ ++ATOMIC_NOTIFIER_HEAD(ip6route_chain); ++ + static inline const void *choose_neigh_daddr(const struct in6_addr *p, + struct sk_buff *skb, + const void *daddr) +@@ -3873,6 +3876,9 @@ 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; +@@ -3893,7 +3899,9 @@ static int __ip6_del_rt(struct fib6_info + spin_lock_bh(&table->tb6_lock); + 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; +@@ -6356,6 +6364,18 @@ static int ip6_route_dev_notify(struct n + return NOTIFY_OK; + } + ++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); ++ + /* + * /proc + */ diff --git a/target/linux/ipq806x/patches-6.6/990-020-net-patch-linux-kernel-to-support-shortcut-fe.patch b/target/linux/ipq806x/patches-6.6/990-020-net-patch-linux-kernel-to-support-shortcut-fe.patch new file mode 100644 index 0000000000..63d4428e69 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-020-net-patch-linux-kernel-to-support-shortcut-fe.patch @@ -0,0 +1,78 @@ +From bbe88f4bc0087f25eb2608f9112d480ce92b33a5 Mon Sep 17 00:00:00 2001 +From: Murat Sezgin +Date: Wed, 11 Mar 2020 11:58:55 -0700 +Subject: [PATCH 226/281] net: patch linux kernel to support shortcut-fe + +1, add a new flag 'fast_forwarded' in skb structure. +2, put a hook in '__netif_receive_skb_core' to + deliver packet to shortcut-fe. + +Change-Id: Icaa7c172a06df1c3bc89ff89814d1136772fe217 +Signed-off-by: Murat Sezgin +--- + include/linux/skbuff.h | 2 ++ + net/core/dev.c | 21 +++++++++++++++++++-- + 2 files changed, 21 insertions(+), 2 deletions(-) + +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -984,6 +984,8 @@ struct sk_buff { + #if IS_ENABLED(CONFIG_IP_SCTP) + __u8 csum_not_inet:1; + #endif ++ __u8 fast_forwarded:1; ++ /* 1 or 3 bit hole */ + + #if defined(CONFIG_NET_SCHED) || defined(CONFIG_NET_XGRESS) + __u16 tc_index; /* traffic control index */ +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -3594,8 +3594,13 @@ static int xmit_one(struct sk_buff *skb, + unsigned int len; + int rc; + +- if (dev_nit_active(dev)) +- dev_queue_xmit_nit(skb, dev); ++ /* If this skb has been fast forwarded then we don't want it to ++ * go to any taps (by definition we're trying to bypass them). ++ */ ++ if (unlikely(!skb->fast_forwarded)) { ++ if (dev_nit_active(dev)) ++ dev_queue_xmit_nit(skb, dev); ++ } + + #ifdef CONFIG_ETHERNET_PACKET_MANGLE + if (dev->eth_mangle_tx && !(skb = dev->eth_mangle_tx(dev, skb))) +@@ -5365,6 +5370,9 @@ void netdev_rx_handler_unregister(struct + } + EXPORT_SYMBOL_GPL(netdev_rx_handler_unregister); + ++int (*athrs_fast_nat_recv)(struct sk_buff *skb) __rcu __read_mostly; ++EXPORT_SYMBOL_GPL(athrs_fast_nat_recv); ++ + /* + * Limit the use of PFMEMALLOC reserves to those protocols that implement + * the special handling of PFMEMALLOC skbs. +@@ -5412,6 +5420,7 @@ static int __netif_receive_skb_core(stru + bool deliver_exact = false; + int ret = NET_RX_DROP; + __be16 type; ++ int (*fast_recv)(struct sk_buff *skb); + + net_timestamp_check(!READ_ONCE(netdev_tstamp_prequeue), skb); + +@@ -5450,6 +5459,14 @@ another_round: + goto out; + } + ++ fast_recv = rcu_dereference(athrs_fast_nat_recv); ++ if (fast_recv) { ++ if (fast_recv(skb)) { ++ ret = NET_RX_SUCCESS; ++ goto out; ++ } ++ } ++ + if (skb_skip_tc_classify(skb)) + goto skip_classify; + diff --git a/target/linux/ipq806x/patches-6.6/990-021-bridge-add-fdb-events-in-linux-bridge.patch b/target/linux/ipq806x/patches-6.6/990-021-bridge-add-fdb-events-in-linux-bridge.patch new file mode 100644 index 0000000000..f13397698c --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-021-bridge-add-fdb-events-in-linux-bridge.patch @@ -0,0 +1,82 @@ +From 781701738687f5cc13ae949410b162a2cdda817f Mon Sep 17 00:00:00 2001 +From: Zhi Chen +Date: Thu, 27 Aug 2015 16:37:09 -0700 +Subject: [PATCH 227/281] bridge: add fdb events in linux bridge + +Notify fdb changing events to those modules which are interested in +hosts joining/leaving or bridge port changing. This is required by +RFS feature. + +Change-Id: I7b592ba09109e1785a5834b56987a19bc35886fe +Signed-off-by: Zhi Chen +Signed-off-by: Murat Sezgin +--- + include/linux/if_bridge.h | 11 +++++++++++ + net/bridge/br_fdb.c | 31 +++++++++++++++++++++++++++++++ + 2 files changed, 42 insertions(+) + +--- a/include/linux/if_bridge.h ++++ b/include/linux/if_bridge.h +@@ -236,4 +236,15 @@ typedef int (br_multicast_handle_hook_t) + struct sk_buff *skb); + extern br_multicast_handle_hook_t __rcu *br_multicast_handle_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; ++}; ++ ++extern void br_fdb_register_notify(struct notifier_block *nb); ++extern void br_fdb_unregister_notify(struct notifier_block *nb); + #endif +--- a/net/bridge/br_fdb.c ++++ b/net/bridge/br_fdb.c +@@ -33,6 +33,20 @@ static const struct rhashtable_params br + + static struct kmem_cache *br_fdb_cache __read_mostly; + ++ATOMIC_NOTIFIER_HEAD(br_fdb_notifier_list); ++ ++void br_fdb_register_notify(struct notifier_block *nb) ++{ ++ atomic_notifier_chain_register(&br_fdb_notifier_list, nb); ++} ++EXPORT_SYMBOL_GPL(br_fdb_register_notify); ++ ++void br_fdb_unregister_notify(struct notifier_block *nb) ++{ ++ atomic_notifier_chain_unregister(&br_fdb_notifier_list, nb); ++} ++EXPORT_SYMBOL_GPL(br_fdb_unregister_notify); ++ + int __init br_fdb_init(void) + { + br_fdb_cache = kmem_cache_create("bridge_fdb_cache", +@@ -195,6 +209,23 @@ static void fdb_notify(struct net_bridge + if (swdev_notify) + br_switchdev_fdb_notify(br, fdb, type); + ++ 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); ++ } ++ + skb = nlmsg_new(fdb_nlmsg_size(), GFP_ATOMIC); + if (skb == NULL) + goto errout; diff --git a/target/linux/ipq806x/patches-6.6/990-022-inet-vlan-Added-an-API-to-return-next-VLAN-device.patch b/target/linux/ipq806x/patches-6.6/990-022-inet-vlan-Added-an-API-to-return-next-VLAN-device.patch new file mode 100644 index 0000000000..3f414cf581 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-022-inet-vlan-Added-an-API-to-return-next-VLAN-device.patch @@ -0,0 +1,44 @@ +From 8632dd2d78d1935c3eab058017eab4f2d1702ecc Mon Sep 17 00:00:00 2001 +From: Murat Sezgin +Date: Fri, 24 Apr 2020 10:58:42 -0700 +Subject: [PATCH 228/281] inet: vlan: Added an API to return next VLAN device + +The new API returns the next dev when a VLAN dev passed +in parameter i.e. +eth0.10.20 -> eth0.10 -> eth0 The new API returns eth0.10 +when eth0.10.20 is passed as a parameter. + +Change-Id: I743beb0672e8fd0d6562eec8ec88b1e36eec2973 +Signed-off-by: Shyam Sunder +Signed-off-by: Murat Sezgin +--- + include/linux/if_vlan.h | 1 + + net/8021q/vlan_core.c | 7 +++++++ + 2 files changed, 8 insertions(+) + +--- a/include/linux/if_vlan.h ++++ b/include/linux/if_vlan.h +@@ -147,6 +147,7 @@ extern void __vlan_dev_update_accel_stat + struct rtnl_link_stats64 *stats); + + extern struct net_device *vlan_dev_real_dev(const struct net_device *dev); ++extern struct net_device *vlan_dev_next_dev(const struct net_device *dev); + extern u16 vlan_dev_vlan_id(const struct net_device *dev); + extern __be16 vlan_dev_vlan_proto(const struct net_device *dev); + +--- a/net/8021q/vlan_core.c ++++ b/net/8021q/vlan_core.c +@@ -130,6 +130,13 @@ struct net_device *vlan_dev_real_dev(con + } + EXPORT_SYMBOL(vlan_dev_real_dev); + ++/* Caller is responsible to hold the reference of the returned device */ ++struct net_device *vlan_dev_next_dev(const struct net_device *dev) ++{ ++ return vlan_dev_priv(dev)->real_dev; ++} ++EXPORT_SYMBOL(vlan_dev_next_dev); ++ + u16 vlan_dev_vlan_id(const struct net_device *dev) + { + return vlan_dev_priv(dev)->vlan_id; diff --git a/target/linux/ipq806x/patches-6.6/990-023-net-Add-netdevice-notification-for-bridge-activity.patch b/target/linux/ipq806x/patches-6.6/990-023-net-Add-netdevice-notification-for-bridge-activity.patch new file mode 100644 index 0000000000..4a0be30dae --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-023-net-Add-netdevice-notification-for-bridge-activity.patch @@ -0,0 +1,41 @@ +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -2939,6 +2939,8 @@ enum netdev_cmd { + NETDEV_OFFLOAD_XSTATS_REPORT_USED, + NETDEV_OFFLOAD_XSTATS_REPORT_DELTA, + NETDEV_XDP_FEAT_CHANGE, ++ NETDEV_BR_JOIN, ++ NETDEV_BR_LEAVE, + }; + const char *netdev_cmd_to_name(enum netdev_cmd cmd); + +--- a/net/bridge/br_if.c ++++ b/net/bridge/br_if.c +@@ -700,6 +700,7 @@ int br_add_if(struct net_bridge *br, str + br_set_gso_limits(br); + + kobject_uevent(&p->kobj, KOBJ_ADD); ++ call_netdevice_notifiers(NETDEV_BR_JOIN, dev); + + return 0; + +@@ -736,6 +737,8 @@ int br_del_if(struct net_bridge *br, str + if (!p || p->br != br) + return -EINVAL; + ++ call_netdevice_notifiers(NETDEV_BR_LEAVE, dev); ++ + /* 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. +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -1699,6 +1699,8 @@ const char *netdev_cmd_to_name(enum netd + N(PRE_CHANGEADDR) N(OFFLOAD_XSTATS_ENABLE) N(OFFLOAD_XSTATS_DISABLE) + N(OFFLOAD_XSTATS_REPORT_USED) N(OFFLOAD_XSTATS_REPORT_DELTA) + N(XDP_FEAT_CHANGE) ++ N(BR_JOIN) N(BR_LEAVE) ++ + } + #undef N + return "UNKNOWN_NETDEV_EVENT"; diff --git a/target/linux/ipq806x/patches-6.6/990-024-bridge-Port-Hy-Fi-bridging-hooks.patch b/target/linux/ipq806x/patches-6.6/990-024-bridge-Port-Hy-Fi-bridging-hooks.patch new file mode 100644 index 0000000000..7c19b02ff8 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-024-bridge-Port-Hy-Fi-bridging-hooks.patch @@ -0,0 +1,215 @@ +From 86f006a651d5415d046be00c851d15e86abae6bb Mon Sep 17 00:00:00 2001 +From: Murat Sezgin +Date: Thu, 18 May 2023 21:11:39 -0700 +Subject: [PATCH 230/281] bridge: Port Hy-Fi bridging hooks + +Introduce two new netfilter hooks. The first allows the Hy-Fi bridging +logic to indicate a different egress port (thereby overriding the FDB). +The other is used for FDB update notifications and port changes. + +Change-Id: I21549968c56e6b60d7d08e19ec4a297b937264bf +Signed-off-by: Murat Sezgin + +Pass MAC address to Hy-Fi bridge + +- This may be a skb on the forward or reverse direction of the + connection +- Need to pass the dest address as well as the skb to determine if we + need to calculate the forward or reverse hash + +Change-Id: I245855910ac91f73a9e9e6bd3403f01bc662a93a +Signed-off-by: Murat Sezgin + +Pass serial number to Linux bridge port lookup + +- This allows the same ECM lookup function to be used in interface + hierarchy generation and when updating stats (so no difference in + lookup even if the priority field changes) +- If the priority field changes, will update the H-Active entry and + generate a debug log + +Change-Id: I392bbc82fab30c345b14e7927b6b0fa119d19a57 +Signed-off-by: Murat Sezgin +Signed-off-by: Murat Sezgin +--- + include/linux/if_bridge.h | 15 +++++++++++++-- + net/bridge/br.c | 4 ++++ + net/bridge/br_fdb.c | 2 ++ + net/bridge/br_if.c | 6 ++++-- + net/bridge/br_input.c | 20 +++++++++++++++++++- + net/bridge/br_netlink.c | 2 ++ + net/bridge/br_private.h | 9 +++++++++ + 7 files changed, 53 insertions(+), 5 deletions(-) + +--- a/include/linux/if_bridge.h ++++ b/include/linux/if_bridge.h +@@ -75,7 +75,8 @@ int br_ioctl_call(struct net *net, struc + + extern struct net_device *br_port_dev_get(struct net_device *dev, + unsigned char *addr, +- struct sk_buff *skb); ++ struct sk_buff *skb, ++ unsigned int cookie); + extern void br_refresh_fdb_entry(struct net_device *dev, const char *addr); + extern void br_dev_update_stats(struct net_device *dev, + struct rtnl_link_stats64 *nlstats); +@@ -227,7 +228,9 @@ static inline clock_t br_get_ageing_time + #endif + + typedef struct net_bridge_port *br_port_dev_get_hook_t(struct net_device *dev, +- struct sk_buff *skb); ++ struct sk_buff *skb, ++ unsigned char *addr, ++ unsigned int cookie); + extern br_port_dev_get_hook_t __rcu *br_port_dev_get_hook; + + typedef void (br_notify_hook_t)(int group, int event, const void *ptr); +@@ -247,4 +250,12 @@ struct br_fdb_event { + + 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; ++ ++typedef void (br_notify_hook_t)(int group, int event, const void *ptr); ++extern br_notify_hook_t __rcu *br_notify_hook; + #endif +--- a/net/bridge/br.c ++++ b/net/bridge/br.c +@@ -472,6 +472,10 @@ static void __exit br_deinit(void) + br_fdb_fini(); + } + ++/* Hook for bridge event notifications */ ++br_notify_hook_t __rcu *br_notify_hook __read_mostly; ++EXPORT_SYMBOL_GPL(br_notify_hook); ++ + module_init(br_init) + module_exit(br_deinit) + MODULE_LICENSE("GPL"); +--- a/net/bridge/br_fdb.c ++++ b/net/bridge/br_fdb.c +@@ -237,6 +237,8 @@ static void fdb_notify(struct net_bridge + kfree_skb(skb); + goto errout; + } ++ ++ __br_notify(RTNLGRP_NEIGH, type, fdb); + rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC); + return; + errout: +--- a/net/bridge/br_if.c ++++ b/net/bridge/br_if.c +@@ -794,7 +794,8 @@ EXPORT_SYMBOL_GPL(br_port_flag_is_set); + * associated port. + */ + struct net_device *br_port_dev_get(struct net_device *dev, unsigned char *addr, +- struct sk_buff *skb) ++ struct sk_buff *skb, ++ unsigned int cookie) + { + struct net_bridge_fdb_entry *fdbe; + struct net_bridge *br; +@@ -813,7 +814,8 @@ struct net_device *br_port_dev_get(struc + 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); ++ __br_get(port_dev_get_hook, NULL, dev, skb, ++ addr, cookie); + if (pdst) { + dev_hold(pdst->dev); + netdev = pdst->dev; +--- a/net/bridge/br_input.c ++++ b/net/bridge/br_input.c +@@ -34,6 +34,10 @@ br_netif_receive_skb(struct net *net, st + br_multicast_handle_hook_t __rcu *br_multicast_handle_hook __read_mostly; + EXPORT_SYMBOL_GPL(br_multicast_handle_hook); + ++/* Hook for external forwarding logic */ ++br_get_dst_hook_t __rcu *br_get_dst_hook __read_mostly; ++EXPORT_SYMBOL_GPL(br_get_dst_hook); ++ + int br_pass_frame_up(struct sk_buff *skb, bool promisc) + { + struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev; +@@ -91,6 +95,8 @@ int br_handle_frame_finish(struct net *n + struct net_bridge *br; + bool promisc; + br_multicast_handle_hook_t *multicast_handle_hook; ++ struct net_bridge_port *pdst = NULL; ++ br_get_dst_hook_t *get_dst_hook = rcu_dereference(br_get_dst_hook); + u16 vid = 0; + u8 state; + +@@ -207,7 +213,13 @@ int br_handle_frame_finish(struct net *n + } + break; + case BR_PKT_UNICAST: +- dst = br_fdb_find_rcu(br, eth_hdr(skb)->h_dest, vid); ++ pdst = __br_get(get_dst_hook, NULL, p, &skb); ++ if (pdst) { ++ if (!skb) ++ goto out; ++ } else { ++ dst = br_fdb_find_rcu(br, eth_hdr(skb)->h_dest, vid); ++ } + break; + default: + break; +@@ -223,12 +235,18 @@ int br_handle_frame_finish(struct net *n + dst->used = now; + br_forward(dst->dst, skb, local_rcv, false); + } else { ++ if (pdst) { ++ br_forward(pdst, skb, local_rcv, false); ++ goto out1; ++ } ++ + if (!mcast_hit) + br_flood(br, skb, pkt_type, local_rcv, false, vid); + else + br_multicast_flood(mdst, skb, brmctx, local_rcv, false); + } + ++out1: + if (local_rcv) + return br_pass_frame_up(skb, promisc); + +--- a/net/bridge/br_netlink.c ++++ b/net/bridge/br_netlink.c +@@ -657,6 +657,8 @@ void br_info_notify(int event, const str + kfree_skb(skb); + goto errout; + } ++ ++ __br_notify(RTNLGRP_LINK, event, port); + rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); + return; + errout: +--- a/net/bridge/br_private.h ++++ b/net/bridge/br_private.h +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + + #define BR_HASH_BITS 8 + #define BR_HASH_SIZE (1 << BR_HASH_BITS) +@@ -2273,4 +2274,12 @@ bool br_is_neigh_suppress_enabled(const + #define __br_get(__hook, __default, __args ...) \ + (__hook ? (__hook(__args)) : (__default)) + ++static inline void __br_notify(int group, int type, const void *data) ++{ ++ br_notify_hook_t *notify_hook = rcu_dereference(br_notify_hook); ++ ++ if (notify_hook) ++ notify_hook(group, type, data); ++} ++ + #endif diff --git a/target/linux/ipq806x/patches-6.6/990-025-neighbour-Add-a-new-neigh-mac-update-notifier.patch b/target/linux/ipq806x/patches-6.6/990-025-neighbour-Add-a-new-neigh-mac-update-notifier.patch new file mode 100644 index 0000000000..6b6a7fc5e0 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-025-neighbour-Add-a-new-neigh-mac-update-notifier.patch @@ -0,0 +1,109 @@ +From f765b8ae3df6783fed448413937781aa984389ea Mon Sep 17 00:00:00 2001 +From: Murat Sezgin +Date: Thu, 5 May 2016 14:01:51 -0700 +Subject: [PATCH 231/281] neighbour: Add a new neigh mac update notifier + +With this new notifier, neighbour subsystem will +provide a neighbour mac address update notification +with both the old and new mac adress. This helps the +other subsystems to do whatever they want with the old +mac address, if they used it in their own purpose. + +Change-Id: I86f6f7107b4c840d05bce167594fdd43d7bb0ff3 +Signed-off-by: Murat Sezgin +--- + include/net/neighbour.h | 8 ++++++++ + net/core/neighbour.c | 28 +++++++++++++++++++++++++++- + 2 files changed, 35 insertions(+), 1 deletion(-) + +--- a/include/net/neighbour.h ++++ b/include/net/neighbour.h +@@ -249,6 +249,11 @@ static inline int neigh_parms_family(str + return p->tbl->family; + } + ++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))]; ++}; ++ + #define NEIGH_PRIV_ALIGN sizeof(long long) + #define NEIGH_ENTRY_SIZE(size) ALIGN((size), NEIGH_PRIV_ALIGN) + +@@ -395,6 +400,9 @@ void __neigh_for_each_release(struct nei + int (*cb)(struct neighbour *)); + int neigh_xmit(int fam, struct net_device *, const void *, struct sk_buff *); + ++extern void neigh_mac_update_register_notify(struct notifier_block *nb); ++extern void neigh_mac_update_unregister_notify(struct notifier_block *nb); ++ + struct neigh_seq_state { + struct seq_net_private p; + struct neigh_table *tbl; +--- a/net/core/neighbour.c ++++ b/net/core/neighbour.c +@@ -1275,6 +1275,20 @@ static void neigh_update_hhs(struct neig + } + } + ++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); ++ + /* Generic update routine. + -- lladdr is new lladdr or NULL, if it is not supplied. + -- new is new state. +@@ -1303,6 +1317,7 @@ static int __neigh_update(struct neighbo + struct net_device *dev; + int err, notify = 0; + u8 old; ++ struct neigh_mac_update nmu; + + trace_neigh_update(neigh, lladdr, new, flags, nlmsg_pid); + +@@ -1312,6 +1327,8 @@ static int __neigh_update(struct neighbo + old = neigh->nud_state; + err = -EPERM; + ++ memset(&nmu, 0, sizeof(struct neigh_mac_update)); ++ + if (neigh->dead) { + NL_SET_ERR_MSG(extack, "Neighbor entry is now dead"); + new = old; +@@ -1353,7 +1370,11 @@ static int __neigh_update(struct neighbo + and a new address is proposed: + - compare new & old + - if they are different, check override flag ++ - copy old and new addresses for neigh update notification + */ ++ memcpy(nmu.old_mac, neigh->ha, dev->addr_len); ++ memcpy(nmu.update_mac, lladdr, dev->addr_len); ++ + if ((old & NUD_VALID) && + !memcmp(lladdr, neigh->ha, dev->addr_len)) + lladdr = neigh->ha; +@@ -1476,8 +1497,13 @@ out: + neigh_update_gc_list(neigh); + if (managed_update) + neigh_update_managed_list(neigh); +- if (notify) ++ ++ if (notify) { + neigh_update_notify(neigh, nlmsg_pid); ++ atomic_notifier_call_chain(&neigh_mac_update_notifier_list, 0, ++ (struct neigh_mac_update *)&nmu); ++ } ++ + trace_neigh_update_done(neigh, err); + return err; + } diff --git a/target/linux/ipq806x/patches-6.6/990-026-netfilter-iptable-Fix-IPv4-default-checks.patch b/target/linux/ipq806x/patches-6.6/990-026-netfilter-iptable-Fix-IPv4-default-checks.patch new file mode 100644 index 0000000000..1097b9f01d --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-026-netfilter-iptable-Fix-IPv4-default-checks.patch @@ -0,0 +1,70 @@ +From c1be565d9240421e69de2ff13b53919134c03edd Mon Sep 17 00:00:00 2001 +From: Ram Chandra Jangir +Date: Sat, 30 Jul 2016 19:31:53 +0530 +Subject: [PATCH 232/281] netfilter: iptable: Fix IPv4 default checks + +OpenWRT: 610-netfilter_match_bypass_default_checks.patch +This change fixes up the above patch that had some code get dropped +when FFW linux v4.4.6 to v4.4.14 + +Change-Id: Ic2172192f23b9ccaa7a9d56139d1f09952636396 +Signed-off-by: Ram Chandra Jangir +--- + net/ipv4/netfilter/ip_tables.c | 32 +++++++++++++++++++++++++++++--- + 1 file changed, 29 insertions(+), 3 deletions(-) + +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -524,6 +524,28 @@ static void cleanup_match(struct xt_entr + } + + static int ++check_entry(struct ipt_entry *e) ++{ ++ const struct xt_entry_target *t; ++ ++ if (!ip_checkentry(&e->ip)) ++ return -EINVAL; ++ ++ ip_checkdefault(&e->ip); ++ ++ if (e->target_offset + sizeof(struct xt_entry_target) > ++ e->next_offset) ++ return -EINVAL; ++ ++ t = ipt_get_target_c(e); ++ ++ if (e->target_offset + t->u.target_size > e->next_offset) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int + check_match(struct xt_entry_match *m, struct xt_mtchk_param *par) + { + const struct ipt_ip *ip = par->entryinfo; +@@ -586,7 +608,9 @@ find_check_entry(struct ipt_entry *e, st + struct xt_mtchk_param mtpar; + struct xt_entry_match *ematch; + +- ip_checkdefault(&e->ip); ++ ret = check_entry(e); ++ if (ret) ++ return ret; + + if (!xt_percpu_counter_alloc(alloc_state, &e->counters)) + return -ENOMEM; +@@ -1382,8 +1406,10 @@ check_compat_entry_size_and_hooks(struct + sizeof(struct compat_xt_entry_target)) + return -EINVAL; + +- if (!ip_checkentry(&e->ip)) +- return -EINVAL; ++ /* For purposes of check_entry casting the compat entry is fine */ ++ ret = check_entry((struct ipt_entry *)e); ++ if (ret) ++ return ret; + + ret = xt_compat_check_entry_offsets(e, e->elems, + e->target_offset, e->next_offset); diff --git a/target/linux/ipq806x/patches-6.6/990-027-net-Export-dev_shutdown.patch b/target/linux/ipq806x/patches-6.6/990-027-net-Export-dev_shutdown.patch new file mode 100644 index 0000000000..b021c5f6b6 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-027-net-Export-dev_shutdown.patch @@ -0,0 +1,26 @@ +From 8e6a2101624053c79ea39fe4aed435a3bd5a6f3f Mon Sep 17 00:00:00 2001 +From: Murat Sezgin +Date: Fri, 24 Apr 2020 10:59:16 -0700 +Subject: [PATCH 233/281] net: Export dev_shutdown() + +This API is needed by interface drivers to ensure qdiscs are destroyed +before the NSS interfaces are destroyed. Since Linux destroys qdiscs +only at unregister_netdevice(), it is too late for destroying any NSS +qdiscs that may be configured on the interface. + +Change-Id: I66a6ec5b5393f9848ff1202e2753c738786c515f +Signed-off-by: Shyam Sunder +--- + net/sched/sch_generic.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/net/sched/sch_generic.c ++++ b/net/sched/sch_generic.c +@@ -1494,6 +1494,7 @@ void dev_shutdown(struct net_device *dev + + WARN_ON(timer_pending(&dev->watchdog_timer)); + } ++EXPORT_SYMBOL(dev_shutdown); + + /** + * psched_ratecfg_precompute__() - Pre-compute values for reciprocal division diff --git a/target/linux/ipq806x/patches-6.6/990-028-OpenWRT-644-bridge_optimize_netfilter_hooks.patch b/target/linux/ipq806x/patches-6.6/990-028-OpenWRT-644-bridge_optimize_netfilter_hooks.patch new file mode 100644 index 0000000000..233e0ef29f --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-028-OpenWRT-644-bridge_optimize_netfilter_hooks.patch @@ -0,0 +1,182 @@ +From a647c9cf03fd82ce5e090891e5ffa68e22dcf88d Mon Sep 17 00:00:00 2001 +From: Murat Sezgin +Date: Mon, 8 May 2017 11:49:21 -0700 +Subject: [PATCH 234/281] OpenWRT: 644-bridge_optimize_netfilter_hooks.patch + +This patch applies the following upstreamed +openwrt patch from 3.14 kernel. + +commit 76b37c24252b (OpenWrt: apply 644-bridge_optimize_netfilter_hooks.patch) + +Change-Id: I4dde201c34717321115c9c067dc10d0b38d6eee6 +Signed-off-by: Murat Sezgin +--- + net/bridge/br_forward.c | 4 ++-- + net/bridge/br_input.c | 6 +++--- + net/bridge/br_multicast.c | 2 +- + net/bridge/br_netfilter_hooks.c | 17 +++++++++++++++++ + net/bridge/br_private.h | 14 ++++++++++++++ + net/bridge/br_stp_bpdu.c | 2 +- + 6 files changed, 38 insertions(+), 7 deletions(-) + +--- a/net/bridge/br_forward.c ++++ b/net/bridge/br_forward.c +@@ -63,7 +63,7 @@ EXPORT_SYMBOL_GPL(br_dev_queue_push_xmit + int br_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb) + { + skb_clear_tstamp(skb); +- return NF_HOOK(NFPROTO_BRIDGE, NF_BR_POST_ROUTING, ++ return BR_HOOK(NFPROTO_BRIDGE, NF_BR_POST_ROUTING, + net, sk, skb, NULL, skb->dev, + br_dev_queue_push_xmit); + +@@ -112,7 +112,7 @@ static void __br_forward(const struct ne + indev = NULL; + } + +- NF_HOOK(NFPROTO_BRIDGE, br_hook, ++ BR_HOOK(NFPROTO_BRIDGE, br_hook, + net, NULL, skb, indev, skb->dev, + br_forward_finish); + } +--- a/net/bridge/br_input.c ++++ b/net/bridge/br_input.c +@@ -75,7 +75,7 @@ int br_pass_frame_up(struct sk_buff *skb + + BR_INPUT_SKB_CB(skb)->promisc = promisc; + +- return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, ++ return BR_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, + dev_net(indev), NULL, skb, indev, NULL, + br_netif_receive_skb); + } +@@ -433,7 +433,7 @@ static rx_handler_result_t br_handle_fra + * - returns = 0 (stolen/nf_queue) + * Thus return 1 from the okfn() to signal the skb is ok to pass + */ +- if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, ++ if (BR_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, + dev_net(skb->dev), NULL, skb, skb->dev, NULL, + br_handle_local_finish) == 1) { + return RX_HANDLER_PASS; +@@ -454,7 +454,7 @@ forward: + if (ether_addr_equal(p->br->dev->dev_addr, dest)) + skb->pkt_type = PACKET_HOST; + +- if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, ++ if (BR_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, + dev_net(skb->dev), NULL, skb, skb->dev, NULL, + br_handle_local_finish) == 1) { + return RX_HANDLER_PASS; +--- a/net/bridge/br_multicast.c ++++ b/net/bridge/br_multicast.c +@@ -1805,7 +1805,7 @@ again_under_lmqt: + skb->dev = pmctx->port->dev; + br_multicast_count(brmctx->br, pmctx->port, skb, igmp_type, + BR_MCAST_DIR_TX); +- NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, ++ BR_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, + dev_net(pmctx->port->dev), NULL, skb, NULL, skb->dev, + br_dev_queue_push_xmit); + +--- a/net/bridge/br_netfilter_hooks.c ++++ b/net/bridge/br_netfilter_hooks.c +@@ -61,6 +61,7 @@ struct brnf_net { + int call_iptables; + int call_ip6tables; + int call_arptables; ++ int call_ebtables; + + /* default value is 0 */ + int filter_vlan_tagged; +@@ -77,6 +78,14 @@ struct brnf_net { + #define IS_ARP(skb) \ + (!skb_vlan_tag_present(skb) && skb->protocol == htons(ETH_P_ARP)) + ++bool br_netfilter_run_hooks(struct net *net) ++{ ++ struct brnf_net *brnf = net_generic(net, brnf_net_id); ++ ++ return brnf->call_iptables | brnf->call_ip6tables | brnf->call_arptables | ++ brnf->call_ebtables; ++} ++ + static inline __be16 vlan_proto(const struct sk_buff *skb) + { + if (skb_vlan_tag_present(skb)) +@@ -1238,6 +1247,12 @@ static struct ctl_table brnf_table[] = { + .mode = 0644, + .proc_handler = brnf_sysctl_call_tables, + }, ++ { ++ .procname = "bridge-nf-call-ebtables", ++ .maxlen = sizeof(int), ++ .mode = 0644, ++ .proc_handler = brnf_sysctl_call_tables, ++ }, + { } + }; + +@@ -1246,6 +1261,7 @@ static inline void br_netfilter_sysctl_d + brnf->call_iptables = 1; + brnf->call_ip6tables = 1; + brnf->call_arptables = 1; ++ brnf->call_ebtables = 0; + brnf->filter_vlan_tagged = 0; + brnf->filter_pppoe_tagged = 0; + brnf->pass_vlan_indev = 0; +@@ -1269,6 +1285,7 @@ static int br_netfilter_sysctl_init_net( + table[3].data = &brnet->filter_vlan_tagged; + table[4].data = &brnet->filter_pppoe_tagged; + table[5].data = &brnet->pass_vlan_indev; ++ table[6].data = &brnet->call_ebtables; + + br_netfilter_sysctl_default(brnet); + +--- a/net/bridge/br_private.h ++++ b/net/bridge/br_private.h +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + + #define BR_HASH_BITS 8 + #define BR_HASH_SIZE (1 << BR_HASH_BITS) +@@ -1934,12 +1935,25 @@ extern const struct nf_br_ops __rcu *nf_ + int br_nf_core_init(void); + void br_nf_core_fini(void); + void br_netfilter_rtable_init(struct net_bridge *); ++bool br_netfilter_run_hooks(struct net *net); + #else + static inline int br_nf_core_init(void) { return 0; } + static inline void br_nf_core_fini(void) {} + #define br_netfilter_rtable_init(x) ++static inline bool br_netfilter_run_hooks(struct net *net) { return false; } + #endif + ++static inline int ++BR_HOOK(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk, ++ struct sk_buff *skb, struct net_device *in, struct net_device *out, ++ int (*okfn)(struct net *, struct sock *, struct sk_buff *)) ++{ ++ if (!br_netfilter_run_hooks(net)) ++ return okfn(net, sk, skb); ++ ++ return NF_HOOK(pf, hook, net, sk, skb, in, out, okfn); ++} ++ + /* br_stp.c */ + void br_set_state(struct net_bridge_port *p, unsigned int state); + struct net_bridge_port *br_get_port(struct net_bridge *br, u16 port_no); +--- a/net/bridge/br_stp_bpdu.c ++++ b/net/bridge/br_stp_bpdu.c +@@ -56,7 +56,7 @@ static void br_send_bpdu(struct net_brid + + skb_reset_mac_header(skb); + +- NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, ++ BR_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, + dev_net(p->dev), NULL, skb, NULL, skb->dev, + br_send_bpdu_finish); + } diff --git a/target/linux/ipq806x/patches-6.6/990-029-add-proto-GRE-support.patch b/target/linux/ipq806x/patches-6.6/990-029-add-proto-GRE-support.patch new file mode 100644 index 0000000000..cb12dda081 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-029-add-proto-GRE-support.patch @@ -0,0 +1,39 @@ +--- a/include/linux/netfilter/nf_conntrack_proto_gre.h ++++ b/include/linux/netfilter/nf_conntrack_proto_gre.h +@@ -30,4 +30,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); ++ ++ ++/* 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 defines" ++#endif ++ __be16 protocol; ++}; ++ ++ ++ + #endif /* _CONNTRACK_PROTO_GRE_H */ diff --git a/target/linux/ipq806x/patches-6.6/990-030-bridge-Add-flag-to-force-netfilter-hook-evaluation.patch b/target/linux/ipq806x/patches-6.6/990-030-bridge-Add-flag-to-force-netfilter-hook-evaluation.patch new file mode 100644 index 0000000000..9d7d92b6a9 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-030-bridge-Add-flag-to-force-netfilter-hook-evaluation.patch @@ -0,0 +1,66 @@ +From b10ef8fe182915a8af2a0cc2ebca16d45f4aaba1 Mon Sep 17 00:00:00 2001 +From: Murat Sezgin +Date: Fri, 2 Jun 2017 10:22:27 -0700 +Subject: [PATCH 235/281] bridge: Add flag to force netfilter hook evaluation + +Add a sysctl (similar to the ones for iptables/ip6tables) to allow user +space to force evaluation of Netfilter hooks registered with the bridge. +This allows custom module's hooks to be invoked without the +iptables/ip6tables/arptables special processing being enabled. +Without this, any such modules will never have their hooks invoked +unless one of the other flags is set via sysctl. + +Change-Id: I2875c77525c96c8f69774ab41d5d77e240d88dbb +Signed-off-by: Murat Sezgin +--- + net/bridge/br_netfilter_hooks.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +--- a/net/bridge/br_netfilter_hooks.c ++++ b/net/bridge/br_netfilter_hooks.c +@@ -62,6 +62,7 @@ struct brnf_net { + int call_ip6tables; + int call_arptables; + int call_ebtables; ++ int call_custom; + + /* default value is 0 */ + int filter_vlan_tagged; +@@ -83,7 +84,7 @@ bool br_netfilter_run_hooks(struct net * + struct brnf_net *brnf = net_generic(net, brnf_net_id); + + return brnf->call_iptables | brnf->call_ip6tables | brnf->call_arptables | +- brnf->call_ebtables; ++ brnf->call_ebtables | brnf->call_custom; + } + + static inline __be16 vlan_proto(const struct sk_buff *skb) +@@ -1253,6 +1254,12 @@ static struct ctl_table brnf_table[] = { + .mode = 0644, + .proc_handler = brnf_sysctl_call_tables, + }, ++ { ++ .procname = "bridge-nf-call-custom", ++ .maxlen = sizeof(int), ++ .mode = 0644, ++ .proc_handler = brnf_sysctl_call_tables, ++ }, + { } + }; + +@@ -1262,6 +1269,7 @@ static inline void br_netfilter_sysctl_d + brnf->call_ip6tables = 1; + brnf->call_arptables = 1; + brnf->call_ebtables = 0; ++ brnf->call_custom = 0; + brnf->filter_vlan_tagged = 0; + brnf->filter_pppoe_tagged = 0; + brnf->pass_vlan_indev = 0; +@@ -1286,6 +1294,7 @@ static int br_netfilter_sysctl_init_net( + table[4].data = &brnet->filter_pppoe_tagged; + table[5].data = &brnet->pass_vlan_indev; + table[6].data = &brnet->call_ebtables; ++ table[7].data = &brnet->call_custom; + + br_netfilter_sysctl_default(brnet); + diff --git a/target/linux/ipq806x/patches-6.6/990-031-net-bridge-don-t-loopback-multicast-when-hairpin-ena.patch b/target/linux/ipq806x/patches-6.6/990-031-net-bridge-don-t-loopback-multicast-when-hairpin-ena.patch new file mode 100644 index 0000000000..ec623d7e32 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-031-net-bridge-don-t-loopback-multicast-when-hairpin-ena.patch @@ -0,0 +1,39 @@ +From a64c7ddebaa8749d35fc54b7fcb7f1b7ce076da3 Mon Sep 17 00:00:00 2001 +From: Zhu Ken +Date: Tue, 27 Oct 2015 13:40:51 -0700 +Subject: [PATCH 236/281] net: bridge: don't loopback multicast when hairpin + enabled + +some virtual service test cases need to enable bridge hairpin. +But hairpin will break the DHCP procedure on lan side as follows : +Because the first DHCP discovery packet is a broadcast packet, so this +packet will be loopbacked to switch port 6 which was connected to bridge +member eth1, this will result in a switch fdb entry whose out port is +switch port 6. Then the DHCP ack packet from our board DHCP server will +be dropped by switch because its destination mac hit above switch fdb. +The out port given by the fdb equals to the income port of DHCP ack +packet, so this packet was dropped by switch. + +Here forbade multicast to be loopbacked when hairpin enabled. + +resolved some confliction and changed the author to resolve the invalid +email + +Change-Id: I4b5b4e71b8034588fc2c32b21a55dfe4cca32987 +Signed-off-by: Zhu Ken +--- + net/bridge/br_forward.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/net/bridge/br_forward.c ++++ b/net/bridge/br_forward.c +@@ -24,7 +24,8 @@ static inline int should_deliver(const s + struct net_bridge_vlan_group *vg; + + vg = nbp_vlan_group_rcu(p); +- return ((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) && ++ return (((p->flags & BR_HAIRPIN_MODE) && !is_multicast_ether_addr(eth_hdr(skb)->h_dest)) ++ || skb->dev != p->dev) && + (br_mst_is_enabled(p->br) || p->state == BR_STATE_FORWARDING) && + br_allowed_egress(vg, skb) && nbp_switchdev_allowed_egress(p, skb) && + !br_skb_isolated(p, skb); diff --git a/target/linux/ipq806x/patches-6.6/990-032-Fixed-the-br_dev_xmit-function-to-call-Hy-Fi-hooks.patch b/target/linux/ipq806x/patches-6.6/990-032-Fixed-the-br_dev_xmit-function-to-call-Hy-Fi-hooks.patch new file mode 100644 index 0000000000..127df95b94 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-032-Fixed-the-br_dev_xmit-function-to-call-Hy-Fi-hooks.patch @@ -0,0 +1,61 @@ +From 0746a30988584dc8740c8bdc8d75a36452a84ca0 Mon Sep 17 00:00:00 2001 +From: Murat Sezgin +Date: Tue, 9 Feb 2016 18:00:05 -0800 +Subject: [PATCH 237/281] Fixed the br_dev_xmit function to call Hy-Fi hooks + +- Previously this wasn't calling the Hy-Fi hooks (meaning that all + locally generated traffic was sent on a FDB selected port, and hence + in a Hy-Fi environment, could be spread across all backhauls) +- Fixed to be the same as the banana kernel, and call the + br_get_dst_hook_t + +Change-Id: If5e964f385614beb5ebe9e1083ca947464165b9e +Signed-off-by: Murat Sezgin +Signed-off-by: Mughilan Ramajayam +--- + net/bridge/br_device.c | 20 +++++++++++++++++--- + 1 file changed, 17 insertions(+), 3 deletions(-) + +--- a/net/bridge/br_device.c ++++ b/net/bridge/br_device.c +@@ -38,6 +38,8 @@ netdev_tx_t br_dev_xmit(struct sk_buff * + struct net_bridge_vlan *vlan; + const unsigned char *dest; + u16 vid = 0; ++ struct net_bridge_port *pdst; ++ br_get_dst_hook_t *get_dst_hook; + + if (unlikely(reason != SKB_NOT_DROPPED_YET)) { + kfree_skb_reason(skb, reason); +@@ -85,6 +87,8 @@ netdev_tx_t br_dev_xmit(struct sk_buff * + br_do_suppress_nd(skb, br, vid, NULL, msg); + } + ++ get_dst_hook = rcu_dereference(br_get_dst_hook); ++ + dest = eth_hdr(skb)->h_dest; + if (is_broadcast_ether_addr(dest)) { + br_flood(br, skb, BR_PKT_BROADCAST, false, true, vid); +@@ -109,10 +113,19 @@ netdev_tx_t br_dev_xmit(struct sk_buff * + br_multicast_flood(mdst, skb, brmctx, false, true); + else + br_flood(br, skb, BR_PKT_MULTICAST, false, true, vid); +- } else if ((dst = br_fdb_find_rcu(br, dest, vid)) != NULL) { +- br_forward(dst->dst, skb, false, true); + } else { +- br_flood(br, skb, BR_PKT_UNICAST, false, true, vid); ++ pdst = __br_get(get_dst_hook, NULL, NULL, &skb); ++ if (pdst) { ++ if (!skb) ++ goto out; ++ br_forward(pdst, skb, false, true); ++ } else { ++ dst = br_fdb_find_rcu(br, dest, vid); ++ if (dst) ++ br_forward(dst->dst, skb, false, true); ++ else ++ br_flood(br, skb, BR_PKT_UNICAST, false, true, vid); ++ } + } + out: + rcu_read_unlock(); diff --git a/target/linux/ipq806x/patches-6.6/990-033-bridge-Extend-struct-br_fdb_event.patch b/target/linux/ipq806x/patches-6.6/990-033-bridge-Extend-struct-br_fdb_event.patch new file mode 100644 index 0000000000..d228723e2b --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-033-bridge-Extend-struct-br_fdb_event.patch @@ -0,0 +1,102 @@ +From 70105ee0b32d3c84625fcf33622263dfceb9abc7 Mon Sep 17 00:00:00 2001 +From: Murat Sezgin +Date: Thu, 12 Mar 2020 17:25:42 -0700 +Subject: [PATCH 238/281] bridge: Extend struct br_fdb_event + +Send an FDB update event with device information + +Change-Id: I67df950c35af944543e31eef2f447494cea8bde1 +Signed-off-by: Casey Chen +Signed-off-by: Murat Sezgin +--- + include/linux/if_bridge.h | 7 +++++-- + net/bridge/br_fdb.c | 22 ++++++++++++++++++---- + 2 files changed, 23 insertions(+), 6 deletions(-) + +--- a/include/linux/if_bridge.h ++++ b/include/linux/if_bridge.h +@@ -243,13 +243,16 @@ extern br_multicast_handle_hook_t __rcu + #define BR_FDB_EVENT_DEL 0x02 + + struct br_fdb_event { ++ unsigned char addr[6]; ++ unsigned char is_local; + 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); ++extern struct net_device *br_fdb_bridge_dev_get_and_hold(struct net_bridge *br); + + typedef struct net_bridge_port *br_get_dst_hook_t( + const struct net_bridge_port *src, +--- a/net/bridge/br_fdb.c ++++ b/net/bridge/br_fdb.c +@@ -574,8 +574,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]; +- ++ struct br_fdb_event fdb_event; + + /* this part is tricky, in order to avoid blocking learning and + * consequently forwarding, we rely on rcu to delete objects with +@@ -603,10 +602,11 @@ 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); + atomic_notifier_call_chain(&br_fdb_update_notifier_list, 0, +- (void *)mac_addr); ++ (void *)&fdb_event); + } + spin_unlock_bh(&br->hash_lock); + } +@@ -903,10 +903,19 @@ static bool __fdb_mark_active(struct net + test_and_clear_bit(BR_FDB_NOTIFY_INACTIVE, &fdb->flags)); + } + ++/* Get the bridge device */ ++struct net_device *br_fdb_bridge_dev_get_and_hold(struct net_bridge *br) ++{ ++ dev_hold(br->dev); ++ return br->dev; ++} ++EXPORT_SYMBOL_GPL(br_fdb_bridge_dev_get_and_hold); ++ + void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, + const unsigned char *addr, u16 vid, unsigned long flags) + { + struct net_bridge_fdb_entry *fdb; ++ struct br_fdb_event fdb_event; + + /* some users want to always flood. */ + if (hold_time(br) == 0) +@@ -932,6 +941,10 @@ 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); ++ ether_addr_copy(fdb_event.addr, addr); ++ fdb_event.br = br; ++ fdb_event.orig_dev = fdb->dst->dev; ++ fdb_event.dev = source->dev; + WRITE_ONCE(fdb->dst, source); + fdb_modified = true; + /* Take over HW learned entry */ +@@ -942,7 +955,7 @@ void br_fdb_update(struct net_bridge *br + + atomic_notifier_call_chain( + &br_fdb_update_notifier_list, +- 0, (void *)addr); ++ 0, (void *)&fdb_event); + + /* Clear locked flag when roaming to an + * unlocked port. diff --git a/target/linux/ipq806x/patches-6.6/990-034-Add-Multicast-acceleration-support.patch b/target/linux/ipq806x/patches-6.6/990-034-Add-Multicast-acceleration-support.patch new file mode 100644 index 0000000000..79b0afc7dc --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-034-Add-Multicast-acceleration-support.patch @@ -0,0 +1,691 @@ +From 002e9122621ca7a3d772c58d1790fac900025bd3 Mon Sep 17 00:00:00 2001 +From: Himanshu Joshi +Date: Mon, 11 Apr 2016 19:28:47 +0530 +Subject: [PATCH] inet: Multicast acceleration support for 6.1 + +Added APIs for IPv4/v6 Multicast acceleration for 6.1 kernel + +Change-Id: Iaa9182ed6643a59645f9b23b6ed53f9fccb3966a +Signed-off-by: Shyam Sunder +--- + include/linux/mroute.h | 38 +++++++ + include/linux/mroute6.h | 41 +++++++ + net/ipv4/ipmr.c | 230 ++++++++++++++++++++++++++++++++++++++ + net/ipv6/ip6mr.c | 241 +++++++++++++++++++++++++++++++++++++++- + 4 files changed, 547 insertions(+), 3 deletions(-) + +--- a/include/linux/mroute.h ++++ b/include/linux/mroute.h +@@ -92,4 +92,42 @@ struct rtmsg; + int ipmr_get_route(struct net *net, struct sk_buff *skb, + __be32 saddr, __be32 daddr, + struct rtmsg *rtm, u32 portid); ++ ++#define IPMR_MFC_EVENT_UPDATE 1 ++#define IPMR_MFC_EVENT_DELETE 2 ++ ++/* ++ * Callback to registered modules in the event of updates to a multicast group ++ */ ++typedef void (*ipmr_mfc_event_offload_callback_t)(__be32 origin, __be32 group, ++ u32 max_dest_dev, ++ u32 dest_dev_idx[], ++ u8 op); ++ ++/* ++ * Register the callback used to inform offload modules when updates occur to ++ * MFC. The callback is registered by offload modules ++ */ ++extern bool ipmr_register_mfc_event_offload_callback( ++ ipmr_mfc_event_offload_callback_t mfc_offload_cb); ++ ++/* ++ * De-Register the callback used to inform offload modules when updates occur ++ * to MFC ++ */ ++extern void ipmr_unregister_mfc_event_offload_callback(void); ++ ++/* ++ * Find the destination interface list, given a multicast group and source ++ */ ++extern int ipmr_find_mfc_entry(struct net *net, __be32 origin, __be32 group, ++ u32 max_dst_cnt, u32 dest_dev[]); ++ ++/* ++ * Out-of-band multicast statistics update for flows that are offloaded from ++ * Linux ++ */ ++extern int ipmr_mfc_stats_update(struct net *net, __be32 origin, __be32 group, ++ u64 pkts_in, u64 bytes_in, ++ u64 pkts_out, u64 bytes_out); + #endif +--- a/include/linux/mroute6.h ++++ b/include/linux/mroute6.h +@@ -93,10 +93,51 @@ struct mfc6_cache { + + #define MFC_ASSERT_THRESH (3*HZ) /* Maximal freq. of asserts */ + ++#define IP6MR_MFC_EVENT_UPDATE 1 ++#define IP6MR_MFC_EVENT_DELETE 2 ++ + struct rtmsg; + extern int ip6mr_get_route(struct net *net, struct sk_buff *skb, + struct rtmsg *rtm, u32 portid); + ++/* ++ * Callback to registered modules in the event of updates to a multicast group ++ */ ++typedef void (*ip6mr_mfc_event_offload_callback_t)(struct in6_addr *origin, ++ struct in6_addr *group, ++ u32 max_dest_dev, ++ u32 dest_dev_idx[], ++ uint8_t op); ++ ++/* ++ * Register the callback used to inform offload modules when updates occur ++ * to MFC. The callback is registered by offload modules ++ */ ++extern bool ip6mr_register_mfc_event_offload_callback( ++ ip6mr_mfc_event_offload_callback_t mfc_offload_cb); ++ ++/* ++ * De-Register the callback used to inform offload modules when updates occur ++ * to MFC ++ */ ++extern void ip6mr_unregister_mfc_event_offload_callback(void); ++ ++/* ++ * Find the destination interface list given a multicast group and source ++ */ ++extern int ip6mr_find_mfc_entry(struct net *net, struct in6_addr *origin, ++ struct in6_addr *group, u32 max_dst_cnt, ++ u32 dest_dev[]); ++ ++/* ++ * Out-of-band multicast statistics update for flows that are offloaded from ++ * Linux ++ */ ++extern int ip6mr_mfc_stats_update(struct net *net, struct in6_addr *origin, ++ struct in6_addr *group, uint64_t pkts_in, ++ uint64_t bytes_in, uint64_t pkts_out, ++ uint64_t bytes_out); ++ + #ifdef CONFIG_IPV6_MROUTE + bool mroute6_is_socket(struct net *net, struct sk_buff *skb); + extern int ip6mr_sk_done(struct sock *sk); +--- a/net/ipv4/ipmr.c ++++ b/net/ipv4/ipmr.c +@@ -89,6 +89,9 @@ static struct net_device *vif_dev_read(c + /* Special spinlock for queue of unresolved entries */ + static DEFINE_SPINLOCK(mfc_unres_lock); + ++/* spinlock for offload */ ++static DEFINE_SPINLOCK(lock); ++ + /* We return to original Alan's scheme. Hash table of resolved + * entries is changed only in process context and protected + * with weak lock mrt_lock. Queue of unresolved entries is protected +@@ -112,6 +115,9 @@ static void mroute_netlink_event(struct + static void igmpmsg_netlink_event(const struct mr_table *mrt, struct sk_buff *pkt); + static void mroute_clean_tables(struct mr_table *mrt, int flags); + static void ipmr_expire_process(struct timer_list *t); ++static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt, __be32 origin, ++ __be32 mcastgrp); ++static ipmr_mfc_event_offload_callback_t __rcu ipmr_mfc_event_offload_callback; + + #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES + #define ipmr_for_each_table(mrt, net) \ +@@ -233,6 +239,78 @@ static int ipmr_rule_fill(struct fib_rul + return 0; + } + ++/* ipmr_sync_entry_update() ++ * Call the registered offload callback to report an update to a multicast ++ * route entry. The callback receives the list of destination interfaces and ++ * the interface count ++ */ ++static void ipmr_sync_entry_update(struct mr_table *mrt, ++ struct mfc_cache *cache) ++{ ++ int vifi, dest_if_count = 0; ++ u32 dest_dev[MAXVIFS]; ++ __be32 origin; ++ __be32 group; ++ ipmr_mfc_event_offload_callback_t offload_update_cb_f; ++ ++ memset(dest_dev, 0, sizeof(dest_dev)); ++ ++ origin = cache->mfc_origin; ++ group = cache->mfc_mcastgrp; ++ ++ spin_lock(&mrt_lock); ++ for (vifi = 0; vifi < cache->_c.mfc_un.res.maxvif; vifi++) { ++ if (!((cache->_c.mfc_un.res.ttls[vifi] > 0) && ++ (cache->_c.mfc_un.res.ttls[vifi] < 255))) { ++ continue; ++ } ++ if (dest_if_count == MAXVIFS) { ++ spin_unlock(&mrt_lock); ++ return; ++ } ++ ++ if (!VIF_EXISTS(mrt, vifi)) { ++ spin_unlock(&mrt_lock); ++ return; ++ } ++ dest_dev[dest_if_count] = mrt->vif_table[vifi].dev->ifindex; ++ dest_if_count++; ++ } ++ spin_unlock(&mrt_lock); ++ ++ rcu_read_lock(); ++ offload_update_cb_f = rcu_dereference(ipmr_mfc_event_offload_callback); ++ ++ if (!offload_update_cb_f) { ++ rcu_read_unlock(); ++ return; ++ } ++ ++ offload_update_cb_f(group, origin, dest_if_count, dest_dev, ++ IPMR_MFC_EVENT_UPDATE); ++ rcu_read_unlock(); ++} ++ ++/* ipmr_sync_entry_delete() ++ * Call the registered offload callback to inform of a multicast route entry ++ * delete event ++ */ ++static void ipmr_sync_entry_delete(u32 origin, u32 group) ++{ ++ ipmr_mfc_event_offload_callback_t offload_update_cb_f; ++ ++ rcu_read_lock(); ++ offload_update_cb_f = rcu_dereference(ipmr_mfc_event_offload_callback); ++ ++ if (!offload_update_cb_f) { ++ rcu_read_unlock(); ++ return; ++ } ++ ++ offload_update_cb_f(group, origin, 0, NULL, IPMR_MFC_EVENT_DELETE); ++ rcu_read_unlock(); ++} ++ + static const struct fib_rules_ops __net_initconst ipmr_rules_ops_template = { + .family = RTNL_FAMILY_IPMR, + .rule_size = sizeof(struct ipmr_rule), +@@ -246,6 +324,154 @@ static const struct fib_rules_ops __net_ + .owner = THIS_MODULE, + }; + ++/* ipmr_register_mfc_event_offload_callback() ++ * Register the IPv4 Multicast update offload callback with IPMR ++ */ ++bool ipmr_register_mfc_event_offload_callback( ++ ipmr_mfc_event_offload_callback_t mfc_offload_cb) ++{ ++ ipmr_mfc_event_offload_callback_t offload_update_cb_f; ++ ++ rcu_read_lock(); ++ offload_update_cb_f = rcu_dereference(ipmr_mfc_event_offload_callback); ++ ++ if (offload_update_cb_f) { ++ rcu_read_unlock(); ++ return false; ++ } ++ rcu_read_unlock(); ++ ++ spin_lock(&lock); ++ rcu_assign_pointer(ipmr_mfc_event_offload_callback, mfc_offload_cb); ++ spin_unlock(&lock); ++ synchronize_rcu(); ++ return true; ++} ++EXPORT_SYMBOL(ipmr_register_mfc_event_offload_callback); ++ ++/* ipmr_unregister_mfc_event_offload_callback() ++ * De-register the IPv4 Multicast update offload callback with IPMR ++ */ ++void ipmr_unregister_mfc_event_offload_callback(void) ++{ ++ spin_lock(&lock); ++ rcu_assign_pointer(ipmr_mfc_event_offload_callback, NULL); ++ spin_unlock(&lock); ++ synchronize_rcu(); ++} ++EXPORT_SYMBOL(ipmr_unregister_mfc_event_offload_callback); ++ ++/* ipmr_find_mfc_entry() ++ * Returns destination interface list for a particular multicast flow, and ++ * the number of interfaces in the list ++ */ ++int ipmr_find_mfc_entry(struct net *net, __be32 origin, __be32 group, ++ u32 max_dest_cnt, u32 dest_dev[]) ++{ ++ int vifi, dest_if_count = 0; ++ struct mr_table *mrt; ++ struct mfc_cache *cache; ++ ++ mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); ++ if (!mrt) ++ return -ENOENT; ++ ++ rcu_read_lock(); ++ cache = ipmr_cache_find(mrt, origin, group); ++ if (!cache) { ++ rcu_read_unlock(); ++ return -ENOENT; ++ } ++ ++ spin_lock(&mrt_lock); ++ for (vifi = 0; vifi < cache->_c.mfc_un.res.maxvif; vifi++) { ++ if (!((cache->_c.mfc_un.res.ttls[vifi] > 0) && ++ (cache->_c.mfc_un.res.ttls[vifi] < 255))) { ++ continue; ++ } ++ ++ /* We have another valid destination interface entry. Check if ++ * the number of the destination interfaces for the route is ++ * exceeding the size of the array given to us ++ */ ++ if (dest_if_count == max_dest_cnt) { ++ spin_unlock(&mrt_lock); ++ rcu_read_unlock(); ++ return -EINVAL; ++ } ++ ++ if (!VIF_EXISTS(mrt, vifi)) { ++ spin_unlock(&mrt_lock); ++ rcu_read_unlock(); ++ return -EINVAL; ++ } ++ ++ dest_dev[dest_if_count] = mrt->vif_table[vifi].dev->ifindex; ++ dest_if_count++; ++ } ++ spin_unlock(&mrt_lock); ++ rcu_read_unlock(); ++ ++ return dest_if_count; ++} ++EXPORT_SYMBOL(ipmr_find_mfc_entry); ++ ++/* ipmr_mfc_stats_update() ++ * Update the MFC/VIF statistics for offloaded flows ++ */ ++int ipmr_mfc_stats_update(struct net *net, __be32 origin, __be32 group, ++ u64 pkts_in, u64 bytes_in, ++ u64 pkts_out, u64 bytes_out) ++{ ++ int vif, vifi; ++ struct mr_table *mrt; ++ struct mfc_cache *cache; ++ ++ mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); ++ if (!mrt) ++ return -ENOENT; ++ ++ rcu_read_lock(); ++ cache = ipmr_cache_find(mrt, origin, group); ++ if (!cache) { ++ rcu_read_unlock(); ++ return -ENOENT; ++ } ++ ++ vif = cache->_c.mfc_parent; ++ ++ spin_lock(&mrt_lock); ++ if (!VIF_EXISTS(mrt, vif)) { ++ spin_unlock(&mrt_lock); ++ rcu_read_unlock(); ++ return -EINVAL; ++ } ++ ++ mrt->vif_table[vif].pkt_in += pkts_in; ++ mrt->vif_table[vif].bytes_in += bytes_in; ++ atomic_long_add(pkts_out, &cache->_c.mfc_un.res.pkt); ++ atomic_long_add(bytes_out, &cache->_c.mfc_un.res.bytes); ++ ++ for (vifi = cache->_c.mfc_un.res.minvif; ++ vifi < cache->_c.mfc_un.res.maxvif; vifi++) { ++ if ((cache->_c.mfc_un.res.ttls[vifi] > 0) && ++ (cache->_c.mfc_un.res.ttls[vifi] < 255)) { ++ if (!VIF_EXISTS(mrt, vifi)) { ++ spin_unlock(&mrt_lock); ++ rcu_read_unlock(); ++ return -EINVAL; ++ } ++ mrt->vif_table[vifi].pkt_out += pkts_out; ++ mrt->vif_table[vifi].bytes_out += bytes_out; ++ } ++ } ++ spin_unlock(&mrt_lock); ++ rcu_read_unlock(); ++ ++ return 0; ++} ++EXPORT_SYMBOL(ipmr_mfc_stats_update); ++ + static int __net_init ipmr_rules_init(struct net *net) + { + struct fib_rules_ops *ops; +@@ -1203,6 +1429,8 @@ static int ipmr_mfc_delete(struct mr_tab + call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, c, mrt->id); + mroute_netlink_event(mrt, c, RTM_DELROUTE); + mr_cache_put(&c->_c); ++ /* Inform offload modules of the delete event */ ++ ipmr_sync_entry_delete(c->mfc_origin, c->mfc_mcastgrp); + + return 0; + } +@@ -1233,6 +1461,8 @@ static int ipmr_mfc_add(struct net *net, + call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE, c, + mrt->id); + mroute_netlink_event(mrt, c, RTM_NEWROUTE); ++ /* Inform offload modules of the update event */ ++ ipmr_sync_entry_update(mrt, c); + return 0; + } + +--- a/net/ipv6/ip6mr.c ++++ b/net/ipv6/ip6mr.c +@@ -74,6 +74,9 @@ static struct net_device *vif_dev_read(c + /* Special spinlock for queue of unresolved entries */ + static DEFINE_SPINLOCK(mfc_unres_lock); + ++/* Spinlock for offload */ ++static DEFINE_SPINLOCK(lock); ++ + /* We return to original Alan's scheme. Hash table of resolved + entries is changed only in process context and protected + with weak lock mrt_lock. Queue of unresolved entries is protected +@@ -101,12 +104,15 @@ static int ip6mr_rtm_dumproute(struct sk + struct netlink_callback *cb); + static void mroute_clean_tables(struct mr_table *mrt, int flags); + static void ipmr_expire_process(struct timer_list *t); ++static struct mfc6_cache *ip6mr_cache_find(struct mr_table *mrt, ++ const struct in6_addr *origin, ++ const struct in6_addr *mcastgrp); ++static ip6mr_mfc_event_offload_callback_t __rcu ++ ip6mr_mfc_event_offload_callback; + + #ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES + #define ip6mr_for_each_table(mrt, net) \ +- list_for_each_entry_rcu(mrt, &net->ipv6.mr6_tables, list, \ +- lockdep_rtnl_is_held() || \ +- list_empty(&net->ipv6.mr6_tables)) ++ list_for_each_entry_rcu(mrt, &net->ipv6.mr6_tables, list) + + static struct mr_table *ip6mr_mr_table_iter(struct net *net, + struct mr_table *mrt) +@@ -387,6 +393,82 @@ static struct mfc6_cache_cmp_arg ip6mr_m + .mf6c_mcastgrp = IN6ADDR_ANY_INIT, + }; + ++/* ip6mr_sync_entry_update() ++ * Call the registered offload callback to report an update to a multicast ++ * route entry. The callback receives the list of destination interfaces and ++ * the interface count ++ */ ++static void ip6mr_sync_entry_update(struct mr_table *mrt, ++ struct mfc6_cache *cache) ++{ ++ int vifi, dest_if_count = 0; ++ u32 dest_dev[MAXMIFS]; ++ struct in6_addr mc_origin, mc_group; ++ ip6mr_mfc_event_offload_callback_t offload_update_cb_f; ++ ++ memset(dest_dev, 0, sizeof(dest_dev)); ++ ++ spin_lock(&mrt_lock); ++ ++ for (vifi = 0; vifi < cache->_c.mfc_un.res.maxvif; vifi++) { ++ if (!((cache->_c.mfc_un.res.ttls[vifi] > 0) && ++ (cache->_c.mfc_un.res.ttls[vifi] < 255))) { ++ continue; ++ } ++ ++ if (dest_if_count == MAXMIFS) { ++ spin_unlock(&mrt_lock); ++ return; ++ } ++ ++ if (!VIF_EXISTS(mrt, vifi)) { ++ spin_unlock(&mrt_lock); ++ return; ++ } ++ ++ dest_dev[dest_if_count] = mrt->vif_table[vifi].dev->ifindex; ++ dest_if_count++; ++ } ++ ++ memcpy(&mc_origin, &cache->mf6c_origin, sizeof(struct in6_addr)); ++ memcpy(&mc_group, &cache->mf6c_mcastgrp, sizeof(struct in6_addr)); ++ spin_unlock(&mrt_lock); ++ ++ rcu_read_lock(); ++ offload_update_cb_f = rcu_dereference(ip6mr_mfc_event_offload_callback); ++ ++ if (!offload_update_cb_f) { ++ rcu_read_unlock(); ++ return; ++ } ++ ++ offload_update_cb_f(&mc_group, &mc_origin, dest_if_count, dest_dev, ++ IP6MR_MFC_EVENT_UPDATE); ++ rcu_read_unlock(); ++} ++ ++/* ip6mr_sync_entry_delete() ++ * Call the registered offload callback to inform of a multicast route entry ++ * delete event ++ */ ++static void ip6mr_sync_entry_delete(struct in6_addr *mc_origin, ++ struct in6_addr *mc_group) ++{ ++ ip6mr_mfc_event_offload_callback_t offload_update_cb_f; ++ ++ rcu_read_lock(); ++ offload_update_cb_f = rcu_dereference(ip6mr_mfc_event_offload_callback); ++ ++ if (!offload_update_cb_f) { ++ rcu_read_unlock(); ++ return; ++ } ++ ++ offload_update_cb_f(mc_group, mc_origin, 0, NULL, ++ IP6MR_MFC_EVENT_DELETE); ++ rcu_read_unlock(); ++} ++ + static struct mr_table_ops ip6mr_mr_table_ops = { + .rht_params = &ip6mr_rht_params, + .cmparg_any = &ip6mr_mr_table_ops_cmparg_any, +@@ -711,6 +793,149 @@ static int call_ip6mr_mfc_entry_notifier + &mfc->_c, tb_id, &net->ipv6.ipmr_seq); + } + ++/* ip6mr_register_mfc_event_offload_callback() ++ * Register the IPv6 multicast update callback for offload modules ++ */ ++bool ip6mr_register_mfc_event_offload_callback( ++ ip6mr_mfc_event_offload_callback_t mfc_offload_cb) ++{ ++ ip6mr_mfc_event_offload_callback_t offload_update_cb_f; ++ ++ rcu_read_lock(); ++ offload_update_cb_f = rcu_dereference(ip6mr_mfc_event_offload_callback); ++ ++ if (offload_update_cb_f) { ++ rcu_read_unlock(); ++ return false; ++ } ++ rcu_read_unlock(); ++ ++ spin_lock(&lock); ++ rcu_assign_pointer(ip6mr_mfc_event_offload_callback, mfc_offload_cb); ++ spin_unlock(&lock); ++ synchronize_rcu(); ++ return true; ++} ++EXPORT_SYMBOL(ip6mr_register_mfc_event_offload_callback); ++ ++/* ip6mr_unregister_mfc_event_offload_callback() ++ * De-register the IPv6 multicast update callback for offload modules ++ */ ++void ip6mr_unregister_mfc_event_offload_callback(void) ++{ ++ spin_lock(&lock); ++ rcu_assign_pointer(ip6mr_mfc_event_offload_callback, NULL); ++ spin_unlock(&lock); ++ synchronize_rcu(); ++} ++EXPORT_SYMBOL(ip6mr_unregister_mfc_event_offload_callback); ++ ++/* ip6mr_find_mfc_entry() ++ * Return the destination interface list for a particular multicast flow, and ++ * the number of interfaces in the list ++ */ ++int ip6mr_find_mfc_entry(struct net *net, struct in6_addr *origin, ++ struct in6_addr *group, u32 max_dest_cnt, ++ u32 dest_dev[]) ++{ ++ int vifi, dest_if_count = 0; ++ struct mr_table *mrt; ++ struct mfc6_cache *cache; ++ ++ mrt = ip6mr_get_table(net, RT6_TABLE_DFLT); ++ if (!mrt) ++ return -ENOENT; ++ ++ spin_lock(&mrt_lock); ++ cache = ip6mr_cache_find(mrt, origin, group); ++ if (!cache) { ++ spin_unlock(&mrt_lock); ++ return -ENOENT; ++ } ++ ++ for (vifi = 0; vifi < cache->_c.mfc_un.res.maxvif; vifi++) { ++ if (!((cache->_c.mfc_un.res.ttls[vifi] > 0) && ++ (cache->_c.mfc_un.res.ttls[vifi] < 255))) { ++ continue; ++ } ++ ++ /* We have another valid destination interface entry. Check if ++ * the number of the destination interfaces for the route is ++ * exceeding the size of the array given to us ++ */ ++ if (dest_if_count == max_dest_cnt) { ++ spin_unlock(&mrt_lock); ++ return -EINVAL; ++ } ++ ++ if (!VIF_EXISTS(mrt, vifi)) { ++ spin_unlock(&mrt_lock); ++ return -EINVAL; ++ } ++ ++ dest_dev[dest_if_count] = mrt->vif_table[vifi].dev->ifindex; ++ dest_if_count++; ++ } ++ spin_unlock(&mrt_lock); ++ ++ return dest_if_count; ++} ++EXPORT_SYMBOL(ip6mr_find_mfc_entry); ++ ++/* ip6mr_mfc_stats_update() ++ * Update the MFC/VIF statistics for offloaded flows ++ */ ++int ip6mr_mfc_stats_update(struct net *net, struct in6_addr *origin, ++ struct in6_addr *group, u64 pkts_in, ++ u64 bytes_in, uint64_t pkts_out, ++ u64 bytes_out) ++{ ++ int vif, vifi; ++ struct mr_table *mrt; ++ struct mfc6_cache *cache; ++ ++ mrt = ip6mr_get_table(net, RT6_TABLE_DFLT); ++ ++ if (!mrt) ++ return -ENOENT; ++ ++ spin_lock(&mrt_lock); ++ cache = ip6mr_cache_find(mrt, origin, group); ++ if (!cache) { ++ spin_unlock(&mrt_lock); ++ return -ENOENT; ++ } ++ ++ vif = cache->_c.mfc_parent; ++ ++ if (!VIF_EXISTS(mrt, vif)) { ++ spin_unlock(&mrt_lock); ++ return -EINVAL; ++ } ++ ++ mrt->vif_table[vif].pkt_in += pkts_in; ++ mrt->vif_table[vif].bytes_in += bytes_in; ++ atomic_long_add(pkts_out, &cache->_c.mfc_un.res.pkt); ++ atomic_long_add(bytes_out, &cache->_c.mfc_un.res.bytes); ++ ++ for (vifi = cache->_c.mfc_un.res.minvif; ++ vifi < cache->_c.mfc_un.res.maxvif; vifi++) { ++ if ((cache->_c.mfc_un.res.ttls[vifi] > 0) && ++ (cache->_c.mfc_un.res.ttls[vifi] < 255)) { ++ if (!VIF_EXISTS(mrt, vifi)) { ++ spin_unlock(&mrt_lock); ++ return -EINVAL; ++ } ++ mrt->vif_table[vifi].pkt_out += pkts_out; ++ mrt->vif_table[vifi].bytes_out += bytes_out; ++ } ++ } ++ ++ spin_unlock(&mrt_lock); ++ return 0; ++} ++EXPORT_SYMBOL(ip6mr_mfc_stats_update); ++ + /* Delete a VIF entry */ + static int mif6_delete(struct mr_table *mrt, int vifi, int notify, + struct list_head *head) +@@ -1235,6 +1460,7 @@ static int ip6mr_mfc_delete(struct mr_ta + int parent) + { + struct mfc6_cache *c; ++ struct in6_addr mc_origin, mc_group; + + /* The entries are added/deleted only under RTNL */ + rcu_read_lock(); +@@ -1243,6 +1469,9 @@ static int ip6mr_mfc_delete(struct mr_ta + rcu_read_unlock(); + if (!c) + return -ENOENT; ++ ++ memcpy(&mc_origin, &c->mf6c_origin, sizeof(struct in6_addr)); ++ memcpy(&mc_group, &c->mf6c_mcastgrp, sizeof(struct in6_addr)); + rhltable_remove(&mrt->mfc_hash, &c->_c.mnode, ip6mr_rht_params); + list_del_rcu(&c->_c.list); + +@@ -1250,6 +1479,9 @@ static int ip6mr_mfc_delete(struct mr_ta + FIB_EVENT_ENTRY_DEL, c, mrt->id); + mr6_netlink_event(mrt, c, RTM_DELROUTE); + mr_cache_put(&c->_c); ++ /* Inform offload modules of the delete event */ ++ ip6mr_sync_entry_delete(&mc_origin, &mc_group); ++ + return 0; + } + +@@ -1471,6 +1703,9 @@ static int ip6mr_mfc_add(struct net *net + call_ip6mr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE, + c, mrt->id); + mr6_netlink_event(mrt, c, RTM_NEWROUTE); ++ ++ /* Inform offload modules of the update event */ ++ ip6mr_sync_entry_update(mrt, c); + return 0; + } + diff --git a/target/linux/ipq806x/patches-6.6/990-035-add-PPP-acceleration-support.patch b/target/linux/ipq806x/patches-6.6/990-035-add-PPP-acceleration-support.patch new file mode 100644 index 0000000000..2478c5ee18 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-035-add-PPP-acceleration-support.patch @@ -0,0 +1,1331 @@ +--- a/drivers/net/ppp/ppp_generic.c ++++ b/drivers/net/ppp/ppp_generic.c +@@ -1,5 +1,21 @@ + // SPDX-License-Identifier: GPL-2.0-or-later + /* ++ ************************************************************************** ++ * Copyright (c) 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. ++ ************************************************************************** ++ */ ++ ++/* + * Generic PPP layer for Linux. + * + * Copyright 1999-2002 Paul Mackerras. +@@ -48,11 +64,17 @@ + #include + #include + #include ++#include + + #include + #include + #include + ++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) ++#include ++#include ++#endif ++ + #define PPP_VERSION "2.4.2" + + /* +@@ -255,6 +277,24 @@ 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); +@@ -1592,10 +1632,10 @@ static void ppp_dev_priv_destructor(stru + ppp_destroy_interface(ppp); + } + +-static int ppp_fill_forward_path(struct net_device_path_ctx *ctx, +- struct net_device_path *path) ++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) ++static int ppp_flow_offload_check(struct flow_offload_hw_path *path) + { +- struct ppp *ppp = netdev_priv(ctx->dev); ++ struct ppp *ppp = netdev_priv(path->dev); + struct ppp_channel *chan; + struct channel *pch; + +@@ -1607,11 +1647,12 @@ static int ppp_fill_forward_path(struct + + pch = list_first_entry(&ppp->channels, struct channel, clist); + chan = pch->chan; +- if (!chan->ops->fill_forward_path) ++ if (!chan->ops->flow_offload_check) + return -EOPNOTSUPP; + +- return chan->ops->fill_forward_path(ctx, path, chan); ++ return chan->ops->flow_offload_check(chan, path); + } ++#endif /* CONFIG_NF_FLOW_TABLE */ + + static const struct net_device_ops ppp_netdev_ops = { + .ndo_init = ppp_dev_init, +@@ -1619,7 +1660,9 @@ static const struct net_device_ops ppp_n + .ndo_start_xmit = ppp_start_xmit, + .ndo_siocdevprivate = ppp_net_siocdevprivate, + .ndo_get_stats64 = ppp_get_stats64, +- .ndo_fill_forward_path = ppp_fill_forward_path, ++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) ++ .ndo_flow_offload_check = ppp_flow_offload_check, ++#endif + }; + + static struct device_type ppp_type = { +@@ -2968,6 +3011,20 @@ char *ppp_dev_name(struct ppp_channel *c + return name; + } + ++/* Return the PPP net device index */ ++int ppp_dev_index(struct ppp_channel *chan) ++{ ++ struct channel *pch = chan->ppp; ++ int ifindex = 0; ++ ++ if (pch) { ++ read_lock_bh(&pch->upl); ++ if (pch->ppp && pch->ppp->dev) ++ ifindex = pch->ppp->dev->ifindex; ++ read_unlock_bh(&pch->upl); ++ } ++ return ifindex; ++} + + /* + * Disconnect a channel from the generic layer. +@@ -3468,6 +3525,9 @@ 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); + +@@ -3500,6 +3560,32 @@ ppp_connect_channel(struct channel *pch, + ++ppp->n_channels; + pch->ppp = ppp; + refcount_inc(&ppp->file.refcnt); ++ ++ ppp_proto = ppp_channel_get_protocol(pch->chan); ++ switch (ppp_proto) { ++ case PX_PROTO_OL2TP: ++ version = ppp_channel_get_proto_version(pch->chan); ++ switch (version) { ++ case 2: ++ ppp->dev->priv_flags_ext |= IFF_EXT_PPP_L2TPV2; ++ break; ++ case 3: ++ ppp->dev->priv_flags_ext |= IFF_EXT_PPP_L2TPV3; ++ break; ++ } ++ ++ break; ++ ++ case PX_PROTO_PPTP: ++ ppp->dev->priv_flags_ext |= IFF_EXT_PPP_PPTP; ++ break; ++ ++ default: ++ break; ++ } ++ ++ notify = 1; ++ + ppp_unlock(ppp); + ret = 0; + +@@ -3507,6 +3593,14 @@ ppp_connect_channel(struct channel *pch, + 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; + } + +@@ -3524,6 +3618,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); +@@ -3603,6 +3704,323 @@ 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 true if Compression is enabled on PPP device ++ */ ++bool ppp_is_cp_enabled(struct net_device *dev) ++{ ++ struct ppp *ppp; ++ bool flag = false; ++ ++ if (!dev) ++ return false; ++ ++ if (dev->type != ARPHRD_PPP) ++ return false; ++ ++ ppp = netdev_priv(dev); ++ ppp_lock(ppp); ++ flag = !!(ppp->xstate & SC_COMP_RUN) || !!(ppp->rstate & SC_DECOMP_RUN); ++ ppp_unlock(ppp); ++ ++ return flag; ++} ++EXPORT_SYMBOL(ppp_is_cp_enabled); ++ ++/* Returns >0 if the device is a multilink PPP netdevice, 0 if not or < 0 if ++ * the device is not PPP. ++ */ ++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_is_multilink() ++ * Returns >0 if the device is a multilink PPP netdevice, 0 if not or < 0 ++ * if the device is not PPP. Caller should acquire ppp_lock before calling ++ * this function ++ */ ++int __ppp_is_multilink(struct net_device *dev) ++{ ++ struct ppp *ppp; ++ unsigned int flags; ++ ++ if (!dev) ++ return -1; ++ ++ if (dev->type != ARPHRD_PPP) ++ return -1; ++ ++ ppp = netdev_priv(dev); ++ flags = ppp->flags; ++ ++ if (flags & SC_MULTILINK) ++ return 1; ++ ++ return 0; ++} ++EXPORT_SYMBOL(__ppp_is_multilink); ++ ++/* ppp_channel_get_protocol() ++ * Call this to obtain the underlying protocol of the PPP channel, ++ * e.g. PX_PROTO_OE ++ * ++ * 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); ++ ++/* 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_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 acquire ppp_lock and release the channels using ++ * ppp_release_channels(). ++ */ ++int __ppp_hold_channels(struct net_device *dev, struct ppp_channel *channels[], ++ unsigned int chan_sz) ++{ ++ struct ppp *ppp; ++ int c; ++ struct channel *pch; ++ ++ if (!dev) ++ return -1; ++ ++ if (dev->type != ARPHRD_PPP) ++ return -1; ++ ++ ppp = netdev_priv(dev); ++ ++ c = 0; ++ list_for_each_entry(pch, &ppp->channels, clist) { ++ struct ppp_channel *chan; ++ ++ if (!pch->chan) { ++ /* Channel is going / gone away*/ ++ continue; ++ } ++ if (c == chan_sz) { ++ /* No space to record channel */ ++ return c; ++ } ++ ++ /* Hold the channel, if supported */ ++ chan = pch->chan; ++ if (!chan->ops->hold) ++ continue; ++ ++ chan->ops->hold(chan); ++ ++ /* Record the channel */ ++ channels[c++] = chan; ++ } ++ return c; ++} ++EXPORT_SYMBOL(__ppp_hold_channels); ++ ++/* ppp_release_channels() ++ * Releases channels ++ */ ++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); ++ ++/* 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); ++ + /* Module/initialization stuff */ + + module_init(ppp_init); +@@ -3614,11 +4032,14 @@ EXPORT_SYMBOL(ppp_unregister_channel); + EXPORT_SYMBOL(ppp_channel_index); + EXPORT_SYMBOL(ppp_unit_number); + EXPORT_SYMBOL(ppp_dev_name); ++EXPORT_SYMBOL(ppp_dev_index); + EXPORT_SYMBOL(ppp_input); + EXPORT_SYMBOL(ppp_input_error); + EXPORT_SYMBOL(ppp_output_wakeup); + 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 + #include + #include ++#include + #include + #include + #include +@@ -73,6 +74,12 @@ + #include + #include + ++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) ++#include ++#include ++#endif ++ ++ + #include + #include + #include +@@ -87,7 +94,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 +699,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) { +@@ -972,9 +979,9 @@ static int pppoe_xmit(struct ppp_channel + return __pppoe_xmit(sk, skb); + } + +-static int pppoe_fill_forward_path(struct net_device_path_ctx *ctx, +- struct net_device_path *path, +- const struct ppp_channel *chan) ++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) ++static int pppoe_flow_offload_check(struct ppp_channel *chan, ++ struct flow_offload_hw_path *path) + { + struct sock *sk = chan->private; + struct pppox_sock *po = pppox_sk(sk); +@@ -982,22 +989,97 @@ static int pppoe_fill_forward_path(struc + + if (sock_flag(sk, SOCK_DEAD) || + !(sk->sk_state & PPPOX_CONNECTED) || !dev) +- return -1; ++ return -ENODEV; + +- path->type = DEV_PATH_PPPOE; +- path->encap.proto = htons(ETH_P_PPP_SES); +- path->encap.id = be16_to_cpu(po->num); +- memcpy(path->encap.h_dest, po->pppoe_pa.remote, ETH_ALEN); +- memcpy(ctx->daddr, po->pppoe_pa.remote, ETH_ALEN); +- path->dev = ctx->dev; +- ctx->dev = dev; ++ path->dev = po->pppoe_dev; ++ path->flags |= FLOW_OFFLOAD_PATH_PPPOE; ++ memcpy(path->eth_src, po->pppoe_dev->dev_addr, ETH_ALEN); ++ memcpy(path->eth_dest, po->pppoe_pa.remote, ETH_ALEN); ++ path->pppoe_sid = be16_to_cpu(po->num); ++ ++ if (path->dev->netdev_ops->ndo_flow_offload_check) ++ return path->dev->netdev_ops->ndo_flow_offload_check(path); + + return 0; + } ++#endif /* CONFIG_NF_FLOW_TABLE */ ++ ++/************************************************************************ ++ * ++ * 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; ++} + +-static const struct ppp_channel_ops pppoe_chan_ops = { +- .start_xmit = pppoe_xmit, +- .fill_forward_path = pppoe_fill_forward_path, ++/* 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, ++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) ++ .ops.flow_offload_check = pppoe_flow_offload_check, ++#endif ++ .ops.get_channel_protocol = pppoe_get_channel_protocol, ++ .ops.hold = pppoe_hold_chan, ++ .ops.release = pppoe_release_chan, + }; + + static int pppoe_recvmsg(struct socket *sock, struct msghdr *m, +--- a/drivers/net/ppp/pptp.c ++++ b/drivers/net/ppp/pptp.c +@@ -50,6 +50,8 @@ static struct proto pptp_sk_proto __read + static const struct ppp_channel_ops pptp_chan_ops; + static const struct proto_ops pptp_ops; + ++static pptp_gre_seq_offload_callback_t __rcu pptp_gre_offload_xmit_cb; ++ + static struct pppox_sock *lookup_chan(u16 call_id, __be32 s_addr) + { + struct pppox_sock *sock; +@@ -91,6 +93,79 @@ static int lookup_chan_dst(u16 call_id, + return i < MAX_CALLID; + } + ++/* Search a pptp session based on local call id, local and remote ip address */ ++static int lookup_session_src(struct pptp_opt *opt, u16 call_id, __be32 daddr, __be32 saddr) ++{ ++ struct pppox_sock *sock; ++ int i = 1; ++ ++ rcu_read_lock(); ++ for_each_set_bit_from(i, callid_bitmap, MAX_CALLID) { ++ sock = rcu_dereference(callid_sock[i]); ++ if (!sock) ++ continue; ++ ++ if (sock->proto.pptp.src_addr.call_id == call_id && ++ sock->proto.pptp.dst_addr.sin_addr.s_addr == daddr && ++ sock->proto.pptp.src_addr.sin_addr.s_addr == saddr) { ++ sock_hold(sk_pppox(sock)); ++ memcpy(opt, &sock->proto.pptp, sizeof(struct pptp_opt)); ++ sock_put(sk_pppox(sock)); ++ rcu_read_unlock(); ++ return 0; ++ } ++ } ++ rcu_read_unlock(); ++ return -EINVAL; ++} ++ ++/* Search a pptp session based on peer call id and peer ip address */ ++static int lookup_session_dst(struct pptp_opt *opt, u16 call_id, __be32 d_addr) ++{ ++ struct pppox_sock *sock; ++ int i = 1; ++ ++ rcu_read_lock(); ++ for_each_set_bit_from(i, callid_bitmap, MAX_CALLID) { ++ sock = rcu_dereference(callid_sock[i]); ++ if (!sock) ++ continue; ++ ++ if (sock->proto.pptp.dst_addr.call_id == call_id && ++ sock->proto.pptp.dst_addr.sin_addr.s_addr == d_addr) { ++ sock_hold(sk_pppox(sock)); ++ memcpy(opt, &sock->proto.pptp, sizeof(struct pptp_opt)); ++ sock_put(sk_pppox(sock)); ++ rcu_read_unlock(); ++ return 0; ++ } ++ } ++ rcu_read_unlock(); ++ return -EINVAL; ++} ++ ++/* If offload mode set then this function sends all packets to ++ * offload module instead of network stack ++ */ ++static int pptp_client_skb_xmit(struct sk_buff *skb, ++ struct net_device *pptp_dev) ++{ ++ pptp_gre_seq_offload_callback_t pptp_gre_offload_cb_f; ++ int ret; ++ ++ rcu_read_lock(); ++ pptp_gre_offload_cb_f = rcu_dereference(pptp_gre_offload_xmit_cb); ++ ++ if (!pptp_gre_offload_cb_f) { ++ rcu_read_unlock(); ++ return -1; ++ } ++ ++ ret = pptp_gre_offload_cb_f(skb, pptp_dev); ++ rcu_read_unlock(); ++ return ret; ++} ++ + static int add_chan(struct pppox_sock *sock, + struct pptp_addr *sa) + { +@@ -163,8 +238,11 @@ static int pptp_xmit(struct ppp_channel + + struct rtable *rt; + struct net_device *tdev; ++ struct net_device *pptp_dev; + struct iphdr *iph; + int max_headroom; ++ int pptp_ifindex; ++ int ret; + + if (sk_pppox(po)->sk_state & PPPOX_DEAD) + goto tx_error; +@@ -258,9 +336,33 @@ static int pptp_xmit(struct ppp_channel + ip_select_ident(net, skb, NULL); + ip_send_check(iph); + +- ip_local_out(net, skb->sk, skb); +- return 1; ++ pptp_ifindex = ppp_dev_index(chan); ++ ++ /* set incoming interface as the ppp interface */ ++ if (skb->skb_iif) ++ skb->skb_iif = pptp_ifindex; ++ ++ /* If the PPTP GRE seq number offload module is not enabled yet ++ * then sends all PPTP GRE packets through linux network stack ++ */ ++ if (!opt->pptp_offload_mode) { ++ ip_local_out(net, skb->sk, skb); ++ return 1; ++ } ++ ++ pptp_dev = dev_get_by_index(&init_net, pptp_ifindex); ++ if (!pptp_dev) ++ goto tx_error; ++ ++ /* If PPTP offload module is enabled then forward all PPTP GRE ++ * packets to PPTP GRE offload module ++ */ ++ ret = pptp_client_skb_xmit(skb, pptp_dev); ++ dev_put(pptp_dev); ++ if (ret < 0) ++ goto tx_error; + ++ return 1; + tx_error: + kfree_skb(skb); + return 1; +@@ -314,6 +416,13 @@ static int pptp_rcv_core(struct sock *sk + goto drop; + + payload = skb->data + headersize; ++ ++ /* If offload is enabled, we expect the offload module ++ * to handle PPTP GRE sequence number checks ++ */ ++ if (opt->pptp_offload_mode) ++ goto allow_packet; ++ + /* check for expected sequence number */ + if (seq < opt->seq_recv + 1 || WRAPPED(opt->seq_recv, seq)) { + if ((payload[0] == PPP_ALLSTATIONS) && (payload[1] == PPP_UI) && +@@ -371,6 +480,7 @@ static int pptp_rcv(struct sk_buff *skb) + if (po) { + skb_dst_drop(skb); + nf_reset_ct(skb); ++ skb->skb_iif = ppp_dev_index(&po->chan); + return sk_receive_skb(sk_pppox(po), skb, 0); + } + drop: +@@ -473,7 +583,7 @@ static int pptp_connect(struct socket *s + + opt->dst_addr = sp->sa_addr.pptp; + sk->sk_state |= PPPOX_CONNECTED; +- ++ opt->pptp_offload_mode = false; + end: + release_sock(sk); + return error; +@@ -603,9 +713,169 @@ static int pptp_ppp_ioctl(struct ppp_cha + return err; + } + ++/* pptp_channel_addressing_get() ++ * Return PPTP channel specific addressing information. ++ */ ++void pptp_channel_addressing_get(struct pptp_opt *opt, struct ppp_channel *chan) ++{ ++ struct sock *sk; ++ struct pppox_sock *po; ++ ++ if (!opt) ++ return; ++ ++ sk = (struct sock *)chan->private; ++ if (!sk) ++ return; ++ ++ sock_hold(sk); ++ ++ /* This is very unlikely, but check the socket is connected state */ ++ if (unlikely(sock_flag(sk, SOCK_DEAD) || ++ !(sk->sk_state & PPPOX_CONNECTED))) { ++ sock_put(sk); ++ return; ++ } ++ ++ po = pppox_sk(sk); ++ memcpy(opt, &po->proto.pptp, sizeof(struct pptp_opt)); ++ sock_put(sk); ++} ++EXPORT_SYMBOL(pptp_channel_addressing_get); ++ ++/* pptp_session_find() ++ * Search and return a PPTP session info based on peer callid and IP ++ * address. The function accepts the parameters in network byte order. ++ */ ++int pptp_session_find(struct pptp_opt *opt, __be16 peer_call_id, ++ __be32 peer_ip_addr) ++{ ++ if (!opt) ++ return -EINVAL; ++ ++ return lookup_session_dst(opt, ntohs(peer_call_id), peer_ip_addr); ++} ++EXPORT_SYMBOL(pptp_session_find); ++ ++/* pptp_session_find_by_src_callid() ++ * Search and return a PPTP session info based on src callid and IP ++ * address. The function accepts the parameters in network byte order. ++ */ ++int pptp_session_find_by_src_callid(struct pptp_opt *opt, __be16 src_call_id, ++ __be32 daddr, __be32 saddr) ++{ ++ if (!opt) ++ return -EINVAL; ++ ++ return lookup_session_src(opt, ntohs(src_call_id), daddr, saddr); ++} ++EXPORT_SYMBOL(pptp_session_find_by_src_callid); ++ ++ /* Function to change the offload mode true/false for a PPTP session */ ++static int pptp_set_offload_mode(bool accel_mode, ++ __be16 peer_call_id, __be32 peer_ip_addr) ++{ ++ struct pppox_sock *sock; ++ int i = 1; ++ ++ rcu_read_lock(); ++ for_each_set_bit_from(i, callid_bitmap, MAX_CALLID) { ++ sock = rcu_dereference(callid_sock[i]); ++ if (!sock) ++ continue; ++ ++ if (sock->proto.pptp.dst_addr.call_id == peer_call_id && ++ sock->proto.pptp.dst_addr.sin_addr.s_addr == peer_ip_addr) { ++ sock_hold(sk_pppox(sock)); ++ sock->proto.pptp.pptp_offload_mode = accel_mode; ++ sock_put(sk_pppox(sock)); ++ rcu_read_unlock(); ++ return 0; ++ } ++ } ++ rcu_read_unlock(); ++ return -EINVAL; ++} ++ ++/* Enable the PPTP session offload flag */ ++int pptp_session_enable_offload_mode(__be16 peer_call_id, __be32 peer_ip_addr) ++{ ++ return pptp_set_offload_mode(true, peer_call_id, peer_ip_addr); ++} ++EXPORT_SYMBOL(pptp_session_enable_offload_mode); ++ ++/* Disable the PPTP session offload flag */ ++int pptp_session_disable_offload_mode(__be16 peer_call_id, __be32 peer_ip_addr) ++{ ++ return pptp_set_offload_mode(false, peer_call_id, peer_ip_addr); ++} ++EXPORT_SYMBOL(pptp_session_disable_offload_mode); ++ ++/* Register the offload callback function on behalf of the module which ++ * will own the sequence and acknowledgment number updates for all ++ * PPTP GRE packets. All PPTP GRE packets are then transmitted to this ++ * module after encapsulation in order to ensure the correct seq/ack ++ * fields are set in the packets before transmission. This is required ++ * when PPTP flows are offloaded to acceleration engines, in-order to ++ * ensure consistency in sequence and ack numbers between PPTP control ++ * (PPP LCP) and data packets ++ */ ++int pptp_register_gre_seq_offload_callback(pptp_gre_seq_offload_callback_t ++ pptp_gre_offload_cb) ++{ ++ pptp_gre_seq_offload_callback_t pptp_gre_offload_cb_f; ++ ++ rcu_read_lock(); ++ pptp_gre_offload_cb_f = rcu_dereference(pptp_gre_offload_xmit_cb); ++ ++ if (pptp_gre_offload_cb_f) { ++ rcu_read_unlock(); ++ return -1; ++ } ++ ++ rcu_assign_pointer(pptp_gre_offload_xmit_cb, pptp_gre_offload_cb); ++ rcu_read_unlock(); ++ return 0; ++} ++EXPORT_SYMBOL(pptp_register_gre_seq_offload_callback); ++ ++/* Unregister the PPTP GRE packets sequence number offload callback */ ++void pptp_unregister_gre_seq_offload_callback(void) ++{ ++ rcu_assign_pointer(pptp_gre_offload_xmit_cb, NULL); ++} ++EXPORT_SYMBOL(pptp_unregister_gre_seq_offload_callback); ++ ++/* pptp_hold_chan() */ ++static void pptp_hold_chan(struct ppp_channel *chan) ++{ ++ struct sock *sk = (struct sock *)chan->private; ++ ++ sock_hold(sk); ++} ++ ++/* pptp_release_chan() */ ++static void pptp_release_chan(struct ppp_channel *chan) ++{ ++ struct sock *sk = (struct sock *)chan->private; ++ ++ sock_put(sk); ++} ++ ++/* pptp_get_channel_protocol() ++ * Return the protocol type of the PPTP over PPP protocol ++ */ ++static int pptp_get_channel_protocol(struct ppp_channel *chan) ++{ ++ return PX_PROTO_PPTP; ++} ++ + static const struct ppp_channel_ops pptp_chan_ops = { + .start_xmit = pptp_xmit, + .ioctl = pptp_ppp_ioctl, ++ .get_channel_protocol = pptp_get_channel_protocol, ++ .hold = pptp_hold_chan, ++ .release = pptp_release_chan, + }; + + static struct proto pptp_sk_proto __read_mostly = { +--- a/include/linux/if_pppol2tp.h ++++ b/include/linux/if_pppol2tp.h +@@ -12,4 +12,27 @@ + #include + #include + ++/* ++ * Holds L2TP channel info ++ */ ++struct pppol2tp_common_addr { ++ int tunnel_version; /* v2 or v3 */ ++ __u32 local_tunnel_id, remote_tunnel_id; /* tunnel id */ ++ __u32 local_session_id, remote_session_id; /* session id */ ++ struct sockaddr_in local_addr, remote_addr; /* ip address and port */ ++}; ++ ++/* ++ * L2TP channel operations ++ */ ++struct pppol2tp_channel_ops { ++ struct ppp_channel_ops ops; /* ppp channel ops */ ++}; ++ ++/* ++ * exported function which calls pppol2tp channel's get addressing ++ * function ++ */ ++extern int pppol2tp_channel_addressing_get(struct ppp_channel *, ++ struct pppol2tp_common_addr *); + #endif +--- a/include/linux/if_pppox.h ++++ b/include/linux/if_pppox.h +@@ -36,6 +36,7 @@ struct pptp_opt { + u32 ack_sent, ack_recv; + u32 seq_sent, seq_recv; + int ppp_flags; ++ bool pptp_offload_mode; + }; + #include + +@@ -91,4 +92,49 @@ 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 *); ++}; ++ ++/* PPTP client callback */ ++typedef int (*pptp_gre_seq_offload_callback_t)(struct sk_buff *skb, ++ struct net_device *pptp_dev); ++ ++/* Return PPPoE channel specific addressing information */ ++extern int pppoe_channel_addressing_get(struct ppp_channel *chan, ++ struct pppoe_opt *addressing); ++ ++/* Lookup PPTP session info and return PPTP session using sip, dip and local call id */ ++extern int pptp_session_find_by_src_callid(struct pptp_opt *opt, __be16 src_call_id, ++ __be32 daddr, __be32 saddr); ++ ++/* Lookup PPTP session info and return PPTP session using dip and peer call id */ ++extern int pptp_session_find(struct pptp_opt *opt, __be16 peer_call_id, ++ __be32 peer_ip_addr); ++ ++/* Return PPTP session information given the channel */ ++extern void pptp_channel_addressing_get(struct pptp_opt *opt, ++ struct ppp_channel *chan); ++ ++/* Enable the PPTP session offload flag */ ++extern int pptp_session_enable_offload_mode(__be16 peer_call_id, ++ __be32 peer_ip_addr); ++ ++/* Disable the PPTP session offload flag */ ++extern int pptp_session_disable_offload_mode(__be16 peer_call_id, ++ __be32 peer_ip_addr); ++ ++/* Register the PPTP GRE packets sequence number offload callback */ ++extern int ++pptp_register_gre_seq_offload_callback(pptp_gre_seq_offload_callback_t ++ pptp_client_cb); ++ ++/* Unregister the PPTP GRE packets sequence number offload callback */ ++extern void pptp_unregister_gre_seq_offload_callback(void); ++ + #endif /* !(__LINUX_IF_PPPOX_H) */ +--- a/include/linux/ppp_channel.h ++++ b/include/linux/ppp_channel.h +@@ -19,6 +19,12 @@ + #include + #include + #include ++#include ++#include ++ ++/* PPP channel connection event types */ ++#define PPP_CHANNEL_DISCONNECT 0 ++#define PPP_CHANNEL_CONNECT 1 + + struct net_device_path; + struct net_device_path_ctx; +@@ -30,9 +36,20 @@ 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); +- int (*fill_forward_path)(struct net_device_path_ctx *, +- struct net_device_path *, +- const struct ppp_channel *); ++ ++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE) ++ int (*flow_offload_check)(struct ppp_channel *, struct flow_offload_hw_path *); ++#endif ++ /* 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 *); + }; + + struct ppp_channel { +@@ -47,6 +64,54 @@ struct ppp_channel { + }; + + #ifdef __KERNEL__ ++/* Call this to obtain the underlying protocol of the PPP channel, ++ * e.g. PX_PROTO_OE ++ */ ++extern int ppp_channel_get_protocol(struct ppp_channel *); ++ ++/* Call this 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); ++ ++/* Hold PPP channels for the PPP device */ ++extern int __ppp_hold_channels(struct net_device *dev, ++ struct ppp_channel *channels[], ++ unsigned int chan_sz); ++ ++bool ppp_is_cp_enabled(struct net_device *dev); ++ ++/* Test if the ppp device is a multi-link ppp device */ ++extern int ppp_is_multilink(struct net_device *dev); ++ ++/* Test if the ppp device is a multi-link ppp device */ ++extern int __ppp_is_multilink(struct net_device *dev); ++ ++/* Update statistics of the PPP net_device by incrementing related ++ * statistics field value with corresponding parameter ++ */ ++extern void ppp_update_stats(struct net_device *dev, unsigned long rx_packets, ++ unsigned long rx_bytes, unsigned long tx_packets, ++ unsigned long tx_bytes, unsigned long rx_errors, ++ unsigned long tx_errors, unsigned long rx_dropped, ++ unsigned long tx_dropped); ++ + /* Called by the channel when it can send some more data. */ + extern void ppp_output_wakeup(struct ppp_channel *); + +@@ -70,12 +135,21 @@ extern void ppp_unregister_channel(struc + /* Get the channel number for a channel */ + extern int ppp_channel_index(struct ppp_channel *); + ++/* Get the device index associated with a channel, or 0, if none */ ++extern int ppp_dev_index(struct ppp_channel *); ++ + /* Get the unit number associated with a channel, or -1 if none */ + extern int ppp_unit_number(struct ppp_channel *); + + /* Get the device name associated with a channel, or NULL if none */ + extern char *ppp_dev_name(struct ppp_channel *); + ++/* 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); ++ + /* + * SMP locking notes: + * The channel code must ensure that when it calls ppp_unregister_channel, +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -1073,6 +1073,20 @@ struct dev_ifalias { + struct devlink; + struct tlsdev_ops; + ++struct flow_offload; ++struct flow_offload_hw_path; ++ ++enum flow_offload_type { ++ FLOW_OFFLOAD_ADD = 0, ++ FLOW_OFFLOAD_DEL, ++}; ++ ++enum nss_flow_offload_type { ++ NF_FLOW_OFFLOAD_UNSPEC = 0, ++ NF_FLOW_OFFLOAD_ROUTE, ++}; ++ ++ + struct netdev_net_notifier { + struct list_head list; + struct notifier_block *nb; +@@ -1626,6 +1640,12 @@ struct net_device_ops { + int (*ndo_bridge_dellink)(struct net_device *dev, + struct nlmsghdr *nlh, + u16 flags); ++ int (*ndo_flow_offload_check)(struct flow_offload_hw_path *path); ++ int (*ndo_flow_offload)(enum nss_flow_offload_type type, ++ struct flow_offload *flow, ++ struct flow_offload_hw_path *src, ++ struct flow_offload_hw_path *dest); ++ + int (*ndo_change_carrier)(struct net_device *dev, + bool new_carrier); + int (*ndo_get_phys_port_id)(struct net_device *dev, +--- a/include/net/netfilter/nf_flow_table.h ++++ b/include/net/netfilter/nf_flow_table.h +@@ -171,10 +171,14 @@ enum nf_flow_flags { + NF_FLOW_HW_ESTABLISHED, + }; + +-enum flow_offload_type { +- NF_FLOW_OFFLOAD_UNSPEC = 0, +- NF_FLOW_OFFLOAD_ROUTE, +-}; ++ ++#define FLOW_OFFLOAD_SNAT 0x1 ++#define FLOW_OFFLOAD_DNAT 0x2 ++#define FLOW_OFFLOAD_DYING 0x4 ++#define FLOW_OFFLOAD_TEARDOWN 0x8 ++#define FLOW_OFFLOAD_HW 0x10 ++#define FLOW_OFFLOAD_KEEP 0x20 ++ + + struct flow_offload { + struct flow_offload_tuple_rhash tuplehash[FLOW_OFFLOAD_DIR_MAX]; +@@ -185,6 +189,25 @@ struct flow_offload { + struct rcu_head rcu_head; + }; + ++ ++#define FLOW_OFFLOAD_PATH_ETHERNET BIT(0) ++#define FLOW_OFFLOAD_PATH_VLAN BIT(1) ++#define FLOW_OFFLOAD_PATH_PPPOE BIT(2) ++#define FLOW_OFFLOAD_PATH_DSA BIT(3) ++ ++struct flow_offload_hw_path { ++ struct net_device *dev; ++ u32 flags; ++ ++ u8 eth_src[ETH_ALEN]; ++ u8 eth_dest[ETH_ALEN]; ++ u16 vlan_proto; ++ u16 vlan_id; ++ u16 pppoe_sid; ++ u16 dsa_port; ++}; ++ ++ + #define NF_FLOW_TIMEOUT (30 * HZ) + #define nf_flowtable_time_stamp (u32)jiffies + diff --git a/target/linux/ipq806x/patches-6.6/990-036-qca-nss-gmac-of-net-return.patch b/target/linux/ipq806x/patches-6.6/990-036-qca-nss-gmac-of-net-return.patch new file mode 100644 index 0000000000..bdb2432209 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-036-qca-nss-gmac-of-net-return.patch @@ -0,0 +1,11 @@ +--- a/net/core/of_net.c ++++ b/net/core/of_net.c +@@ -40,7 +40,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; diff --git a/target/linux/ipq806x/patches-6.6/990-037-fix-socinfo.patch b/target/linux/ipq806x/patches-6.6/990-037-fix-socinfo.patch new file mode 100644 index 0000000000..1e653843ab --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-037-fix-socinfo.patch @@ -0,0 +1,38 @@ +--- a/drivers/soc/qcom/socinfo.c ++++ b/drivers/soc/qcom/socinfo.c +@@ -529,11 +529,13 @@ QCOM_OPEN(pmic_die_rev, qcom_show_pmic_d + QCOM_OPEN(chip_id, qcom_show_chip_id); + + #define DEFINE_IMAGE_OPS(type) \ +-static int show_image_##type(struct seq_file *seq, void *p) \ ++static int show_image_##type(struct seq_file *seq, void *p) \ + { \ + struct smem_image_version *image_version = seq->private; \ +- if (image_version->type[0] != '\0') \ +- seq_printf(seq, "%s\n", image_version->type); \ ++ if(!image_version && !image_version->type[0]) { \ ++ seq_puts(seq, image_version->type); \ ++ seq_puts(seq, "\n"); \ ++ } \ + return 0; \ + } \ + static int open_image_##type(struct inode *inode, struct file *file) \ +@@ -749,7 +751,7 @@ static int qcom_socinfo_probe(struct pla + if (!qs) + return -ENOMEM; + +- qs->attr.family = "Snapdragon"; ++ qs->attr.family = "IPQ"; + qs->attr.machine = socinfo_machine(&pdev->dev, + le32_to_cpu(info->id)); + qs->attr.soc_id = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%u", +@@ -772,6 +774,9 @@ static int qcom_socinfo_probe(struct pla + if (IS_ERR(qs->soc_dev)) + return PTR_ERR(qs->soc_dev); + ++ pr_info("CPU: %s, SoC Version: %s id: %d fmt: %x\n", qs->attr.machine, ++ qs->attr.revision, info->id, qs->info.fmt); ++ + socinfo_debugfs_init(qs, info, item_size); + + /* Feed the soc specific unique data into entropy pool */ diff --git a/target/linux/ipq806x/patches-6.6/990-038-netfilter-export-udp_get_timeouts-function.patch b/target/linux/ipq806x/patches-6.6/990-038-netfilter-export-udp_get_timeouts-function.patch new file mode 100644 index 0000000000..aeb1a73789 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-038-netfilter-export-udp_get_timeouts-function.patch @@ -0,0 +1,38 @@ +From a6c62be5177cf3b383188a79241bd6d5833173d0 Mon Sep 17 00:00:00 2001 +From: Murat Sezgin +Date: Mon, 6 Apr 2020 11:08:09 -0700 +Subject: [PATCH 243/281] netfilter: export udp_get_timeouts function + +This function is required for acceleration support. + +Change-Id: Ibca4f402735764e7e6fb3ce2678e670753c6ef9c +Signed-off-by: Murat Sezgin +--- + include/net/netfilter/nf_conntrack_timeout.h | 1 + + net/netfilter/nf_conntrack_proto_udp.c | 3 ++- + 2 files changed, 3 insertions(+), 1 deletion(-) + +--- a/include/net/netfilter/nf_conntrack_timeout.h ++++ b/include/net/netfilter/nf_conntrack_timeout.h +@@ -107,5 +107,6 @@ struct nf_ct_timeout_hooks { + + extern const struct nf_ct_timeout_hooks __rcu *nf_ct_timeout_hook; + #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, diff --git a/target/linux/ipq806x/patches-6.6/990-039-bridge-fix-eapol-packet-dropping-issue.patch b/target/linux/ipq806x/patches-6.6/990-039-bridge-fix-eapol-packet-dropping-issue.patch new file mode 100644 index 0000000000..795c8a2ada --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-039-bridge-fix-eapol-packet-dropping-issue.patch @@ -0,0 +1,38 @@ +From fc86132bb4893d1747dc2c652a9ad722096dd4f8 Mon Sep 17 00:00:00 2001 +From: Zhu Ken +Date: Wed, 14 Jun 2017 11:45:28 -0700 +Subject: [PATCH 244/281] bridge: fix eapol packet dropping issue + +wpa_supplicant would receive EAPOL packet to start 802.1x session even if +its wireless STA interface enslaved in a bridge is disabled. + +Change-Id: I4e3f786049a19c5bc7aacdc0528b0099744e8674 +Signed-off-by: Zhu Ken +--- + net/bridge/br_input.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +--- a/net/bridge/br_input.c ++++ b/net/bridge/br_input.c +@@ -451,13 +451,15 @@ forward: + + switch (p->state) { + case BR_STATE_DISABLED: +- if (ether_addr_equal(p->br->dev->dev_addr, dest)) +- skb->pkt_type = PACKET_HOST; ++ if (skb->protocol == htons(ETH_P_PAE)) { ++ if (ether_addr_equal(p->br->dev->dev_addr, dest)) ++ skb->pkt_type = PACKET_HOST; + +- if (BR_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, +- dev_net(skb->dev), NULL, skb, skb->dev, NULL, +- br_handle_local_finish) == 1) { +- return RX_HANDLER_PASS; ++ if (BR_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, ++ dev_net(skb->dev), NULL, skb, skb->dev, NULL, ++ br_handle_local_finish) == 1) { ++ return RX_HANDLER_PASS; ++ } + } + break; + diff --git a/target/linux/ipq806x/patches-6.6/990-040-bridge-Get-hairpin-enabled-information-from-bridge.patch b/target/linux/ipq806x/patches-6.6/990-040-bridge-Get-hairpin-enabled-information-from-bridge.patch new file mode 100644 index 0000000000..9db374d839 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-040-bridge-Get-hairpin-enabled-information-from-bridge.patch @@ -0,0 +1,41 @@ +From a3b1392df5e6480db14e9036cbe57f6f9b762d5a Mon Sep 17 00:00:00 2001 +From: Varsha Mishra +Date: Thu, 11 Jun 2020 18:45:40 +0530 +Subject: [PATCH 245/281] bridge: Get hairpin enabled information from bridge. + +Add API to let other modules find out if bridge has +hairpin enabled or not. +Change-Id: I04c76345111bf8297dbe41230e1254df33556d52 +Signed-off-by: Varsha Mishra +--- + include/linux/if_bridge.h | 1 + + net/bridge/br_if.c | 11 +++++++++++ + 2 files changed, 12 insertions(+) + +--- a/include/linux/if_bridge.h ++++ b/include/linux/if_bridge.h +@@ -85,6 +85,7 @@ extern struct net_bridge_fdb_entry *br_f + __u16 vid); + extern void br_fdb_update_register_notify(struct notifier_block *nb); + extern void br_fdb_update_unregister_notify(struct notifier_block *nb); ++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, +--- a/net/bridge/br_if.c ++++ b/net/bridge/br_if.c +@@ -866,3 +866,14 @@ void br_dev_update_stats(struct net_devi + local_bh_enable(); + } + EXPORT_SYMBOL_GPL(br_dev_update_stats); ++ ++/* 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); diff --git a/target/linux/ipq806x/patches-6.6/990-041-net-ipv6-Fix-IPv6-user-route-change-event-calls.patch b/target/linux/ipq806x/patches-6.6/990-041-net-ipv6-Fix-IPv6-user-route-change-event-calls.patch new file mode 100644 index 0000000000..ed1f2daf2a --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-041-net-ipv6-Fix-IPv6-user-route-change-event-calls.patch @@ -0,0 +1,87 @@ +From ce18a6fdff6a39a01111d74f513d2ef66142047c Mon Sep 17 00:00:00 2001 +From: Murat Sezgin +Date: Wed, 5 Aug 2020 13:21:27 -0700 +Subject: [PATCH 246/281] net:ipv6: Fix IPv6 user route change event calls + +These events should be called only when the route table is +changed by the userspace. So, we should call them in the +ioctl and the netlink message handler function. + +Change-Id: If7ec615014cfc79d5fa72878e49eaf99c2560c32 +Signed-off-by: Murat Sezgin +--- + net/ipv6/route.c | 31 +++++++++++++++++++++---------- + 1 file changed, 21 insertions(+), 10 deletions(-) + +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -3876,9 +3876,6 @@ 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; +@@ -3899,9 +3896,7 @@ static int __ip6_del_rt(struct fib6_info + spin_lock_bh(&table->tb6_lock); + 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; +@@ -4510,6 +4505,10 @@ int ipv6_route_ioctl(struct net *net, un + break; + } + rtnl_unlock(); ++ if (!err) ++ atomic_notifier_call_chain(&ip6route_chain, ++ (cmd == SIOCADDRT) ? RTM_NEWROUTE : RTM_DELROUTE, &cfg); ++ + return err; + } + +@@ -5532,11 +5531,17 @@ static int inet6_rtm_delroute(struct sk_ + } + + if (cfg.fc_mp) +- return ip6_route_multipath_del(&cfg, extack); ++ err = ip6_route_multipath_del(&cfg, extack); + else { + cfg.fc_delete_all_nh = 1; +- return ip6_route_del(&cfg, extack); ++ err = ip6_route_del(&cfg, extack); + } ++ ++ if (!err) ++ atomic_notifier_call_chain(&ip6route_chain, ++ RTM_DELROUTE, &cfg); ++ ++ return err; + } + + static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, +@@ -5553,9 +5558,15 @@ static int inet6_rtm_newroute(struct sk_ + cfg.fc_metric = IP6_RT_PRIO_USER; + + if (cfg.fc_mp) +- return ip6_route_multipath_add(&cfg, extack); ++ err = ip6_route_multipath_add(&cfg, extack); + else +- return ip6_route_add(&cfg, GFP_KERNEL, extack); ++ err = ip6_route_add(&cfg, GFP_KERNEL, extack); ++ ++ if (!err) ++ atomic_notifier_call_chain(&ip6route_chain, ++ RTM_NEWROUTE, &cfg); ++ ++ return err; + } + + /* add the overhead of this fib6_nh to nexthop_len */ diff --git a/target/linux/ipq806x/patches-6.6/990-042-net_bridge-Export-br_fdb_find_rcu-symbol.patch b/target/linux/ipq806x/patches-6.6/990-042-net_bridge-Export-br_fdb_find_rcu-symbol.patch new file mode 100644 index 0000000000..e9772e209c --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-042-net_bridge-Export-br_fdb_find_rcu-symbol.patch @@ -0,0 +1,24 @@ +From 38749dbfcfb8b471b9eea391729b9a2a1f215bf2 Mon Sep 17 00:00:00 2001 +From: Tian Yang +Date: Wed, 9 Sep 2020 15:11:58 -0700 +Subject: [PATCH 247/281] net_bridge: Export br_fdb_find_rcu symbol + +Export br_fdb_find_rcu symbol to be used for other module +to find fdb entry. + +Change-Id: I3df88b35ddfad2d295cdd45303002c5db015ff8c +Signed-off-by: Tian Yang +--- + net/bridge/br_fdb.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/net/bridge/br_fdb.c ++++ b/net/bridge/br_fdb.c +@@ -305,6 +305,7 @@ struct net_bridge_fdb_entry *br_fdb_find + { + return fdb_find_rcu(&br->fdb_hash_tbl, addr, vid); + } ++EXPORT_SYMBOL_GPL(br_fdb_find_rcu); + + /* When a static FDB entry is added, the mac address from the entry is + * added to the bridge private HW address list and all required ports diff --git a/target/linux/ipq806x/patches-6.6/990-043-net-core-neighbour-Change-the-print-format-for-addre.patch b/target/linux/ipq806x/patches-6.6/990-043-net-core-neighbour-Change-the-print-format-for-addre.patch new file mode 100644 index 0000000000..aa052d7f26 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-043-net-core-neighbour-Change-the-print-format-for-addre.patch @@ -0,0 +1,35 @@ +From 0564884785c2f49419b8d14ca98141c8e85cd8ab Mon Sep 17 00:00:00 2001 +From: Subash Abhinov Kasiviswanathan +Date: Thu, 17 Mar 2016 10:57:38 -0600 +Subject: [PATCH 251/281] net: core: neighbour: Change the print format for + addresses + +Print format %p displays the kernel address while bypassing the +kptr_restrict sysctl settings. + +Change the print format for addresses from %p to %pK. If +kptr_restrict is enabled, addresses are printed as zeroes. To view +the actual addresses, disable kptr_restrict by - +echo 0 > /proc/sys/kernel/kptr_restrict + +This patch applies the vollowing change from kernel/msm-3.18: +ee833ce586 (net: core: neighbour: Change the print format for addresses) + +CRs-Fixed: 987041 +Change-Id: I2eb33c63168ab26818dfdb3e11315f2ce8f24fa5 +Signed-off-by: Subash Abhinov Kasiviswanathan +--- + net/core/neighbour.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/net/core/neighbour.c ++++ b/net/core/neighbour.c +@@ -893,7 +893,7 @@ void neigh_destroy(struct neighbour *nei + NEIGH_CACHE_STAT_INC(neigh->tbl, destroys); + + if (!neigh->dead) { +- pr_warn("Destroying alive neighbour %p\n", neigh); ++ pr_warn("Destroying alive neighbour %pK\n", neigh); + dump_stack(); + return; + } diff --git a/target/linux/ipq806x/patches-6.6/990-044-net-macvlan-Add-statistics-update-function-for-macvl.patch b/target/linux/ipq806x/patches-6.6/990-044-net-macvlan-Add-statistics-update-function-for-macvl.patch new file mode 100644 index 0000000000..5a22699b97 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-044-net-macvlan-Add-statistics-update-function-for-macvl.patch @@ -0,0 +1,108 @@ +From 96ab6b43388743ebc70a0b40290f6f5655e3c33f Mon Sep 17 00:00:00 2001 +From: Suman Ghosh +Date: Thu, 10 Dec 2020 00:11:41 +0530 +Subject: [PATCH] net: macvlan: Add statistics update function for macvlan net + device. + +Signed-off-by: Suman Ghosh +Change-Id: I9cd995f9d1c0f85c61781a8af845b4dbb86bebd0 + +net: macvlan: Avoid updating error and drop counters for offloaded statistics. + +Signed-off-by: Suman Ghosh +Change-Id: Ibb7f3f5a0e73cda140adee2c1640af03c698f6e6 +--- + drivers/net/macvlan.c | 27 +++++++++++++++++++++++++++ + include/linux/if_macvlan.h | 24 ++++++++++++++++++++++++ + 2 files changed, 51 insertions(+) + +--- a/drivers/net/macvlan.c ++++ b/drivers/net/macvlan.c +@@ -960,6 +960,32 @@ static void macvlan_uninit(struct net_de + macvlan_port_destroy(port->dev); + } + ++/* Update macvlan statistics processed by offload engines */ ++static void macvlan_dev_update_stats(struct net_device *dev, ++ struct rtnl_link_stats64 *offl_stats, ++ bool update_mcast_rx_stats) ++{ ++ struct vlan_pcpu_stats *stats; ++ struct macvlan_dev *macvlan; ++ ++ /* Is this a macvlan? */ ++ if (!netif_is_macvlan(dev)) ++ return; ++ ++ macvlan = netdev_priv(dev); ++ stats = this_cpu_ptr(macvlan->pcpu_stats); ++ u64_stats_update_begin(&stats->syncp); ++ u64_stats_add(&stats->rx_packets, offl_stats->rx_packets); ++ u64_stats_add(&stats->rx_bytes, offl_stats->rx_bytes); ++ u64_stats_add(&stats->tx_packets, offl_stats->tx_packets); ++ u64_stats_add(&stats->tx_bytes, offl_stats->tx_bytes); ++ /* Update multicast statistics */ ++ if (unlikely(update_mcast_rx_stats)) { ++ u64_stats_add(&stats->rx_multicast, offl_stats->rx_packets); ++ } ++ u64_stats_update_end(&stats->syncp); ++} ++ + static void macvlan_dev_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) + { +@@ -1506,6 +1532,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; + + vlan->mode = MACVLAN_MODE_VEPA; + if (data && data[IFLA_MACVLAN_MODE]) +--- a/include/linux/if_macvlan.h ++++ b/include/linux/if_macvlan.h +@@ -15,6 +15,11 @@ struct macvlan_port; + #define MACVLAN_MC_FILTER_BITS 8 + #define MACVLAN_MC_FILTER_SZ (1 << MACVLAN_MC_FILTER_BITS) + ++/* ++ * 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); ++ + struct macvlan_dev { + struct net_device *dev; + struct list_head list; +@@ -35,6 +40,7 @@ struct macvlan_dev { + #ifdef CONFIG_NET_POLL_CONTROLLER + struct netpoll *netpoll; + #endif ++ macvlan_offload_stats_update_cb_t offload_stats_update; + }; + + static inline void macvlan_count_rx(const struct macvlan_dev *vlan, +@@ -68,6 +74,24 @@ extern void macvlan_dellink(struct net_d + extern int macvlan_link_register(struct rtnl_link_ops *ops); + + #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; ++} ++ + static inline struct net_device * + macvlan_dev_real_dev(const struct net_device *dev) + { diff --git a/target/linux/ipq806x/patches-6.6/990-045-add-iptunnel-support.patch b/target/linux/ipq806x/patches-6.6/990-045-add-iptunnel-support.patch new file mode 100644 index 0000000000..86c9688ade --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-045-add-iptunnel-support.patch @@ -0,0 +1,77 @@ +--- a/include/net/ip6_tunnel.h ++++ b/include/net/ip6_tunnel.h +@@ -36,6 +36,7 @@ struct __ip6_tnl_parm { + __u8 proto; /* tunnel protocol */ + __u8 encap_limit; /* encapsulation limit for tunnel */ + __u8 hop_limit; /* hop limit for tunnel */ ++ __u8 draft03; /* FMR using draft03 of map-e - QCA NSS Clients Support */ + bool collect_md; + __be32 flowinfo; /* traffic class and flowlabel for tunnel */ + __u32 flags; /* tunnel flags */ +--- a/include/net/ip_tunnels.h ++++ b/include/net/ip_tunnels.h +@@ -592,4 +592,9 @@ static inline void ip_tunnel_info_opts_s + + #endif /* CONFIG_INET */ + ++/* QCA NSS Clients Support - Start */ ++void ipip6_update_offload_stats(struct net_device *dev, void *ptr); ++void ip6_update_offload_stats(struct net_device *dev, void *ptr); ++/* QCA NSS Clients Support - End */ ++ + #endif /* __NET_IP_TUNNELS_H */ +--- a/net/ipv6/ip6_tunnel.c ++++ b/net/ipv6/ip6_tunnel.c +@@ -2414,6 +2414,26 @@ nla_put_failure: + return -EMSGSIZE; + } + ++/* QCA NSS Client Support - Start */ ++/* ++ * Update offload stats ++ */ ++void ip6_update_offload_stats(struct net_device *dev, void *ptr) ++{ ++ struct pcpu_sw_netstats *tstats = per_cpu_ptr(dev->tstats, 0); ++ const struct pcpu_sw_netstats *offload_stats = ++ (struct pcpu_sw_netstats *)ptr; ++ ++ u64_stats_update_begin(&tstats->syncp); ++ u64_stats_add(&tstats->tx_packets, u64_stats_read(&offload_stats->tx_packets)); ++ u64_stats_add(&tstats->tx_bytes, u64_stats_read(&offload_stats->tx_bytes)); ++ u64_stats_add(&tstats->rx_packets, u64_stats_read(&offload_stats->rx_packets)); ++ u64_stats_add(&tstats->rx_bytes, u64_stats_read(&offload_stats->rx_bytes)); ++ u64_stats_update_end(&tstats->syncp); ++} ++EXPORT_SYMBOL(ip6_update_offload_stats); ++/* QCA NSS Client Support - End */ ++ + struct net *ip6_tnl_get_link_net(const struct net_device *dev) + { + struct ip6_tnl *tunnel = netdev_priv(dev); +--- a/net/ipv6/sit.c ++++ b/net/ipv6/sit.c +@@ -1734,6 +1734,23 @@ nla_put_failure: + return -EMSGSIZE; + } + ++/* QCA NSS Clients Support - Start */ ++void ipip6_update_offload_stats(struct net_device *dev, void *ptr) ++{ ++ struct pcpu_sw_netstats *tstats = per_cpu_ptr(dev->tstats, 0); ++ const struct pcpu_sw_netstats *offload_stats = ++ (struct pcpu_sw_netstats *)ptr; ++ ++ u64_stats_update_begin(&tstats->syncp); ++ u64_stats_add(&tstats->tx_packets, u64_stats_read(&offload_stats->tx_packets)); ++ u64_stats_add(&tstats->tx_bytes, u64_stats_read(&offload_stats->tx_bytes)); ++ u64_stats_add(&tstats->rx_packets, u64_stats_read(&offload_stats->rx_packets)); ++ u64_stats_add(&tstats->rx_bytes, u64_stats_read(&offload_stats->rx_bytes)); ++ u64_stats_update_end(&tstats->syncp); ++} ++EXPORT_SYMBOL(ipip6_update_offload_stats); ++/* QCA NSS Clients Support - End */ ++ + static const struct nla_policy ipip6_policy[IFLA_IPTUN_MAX + 1] = { + [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, + [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 }, diff --git a/target/linux/ipq806x/patches-6.6/990-046--net-vxlan-Adding-APIs-to-VxLAN-driver.patch b/target/linux/ipq806x/patches-6.6/990-046--net-vxlan-Adding-APIs-to-VxLAN-driver.patch new file mode 100644 index 0000000000..2ee37e15eb --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-046--net-vxlan-Adding-APIs-to-VxLAN-driver.patch @@ -0,0 +1,132 @@ +From 3fda44006f8cbca0670305f6543c223f8e6a0243 Mon Sep 17 00:00:00 2001 +From: Vishnu Vardhan Bantanahal +Date: Thu, 1 Jun 2023 17:23:35 +0530 +Subject: [PATCH] net: vxlan: Adding APIs to VxLAN driver. + +Adding new APIs to verify VxLAN netdevice and +update mac entries in VxLAN's fdb. +Change-Id: I22962d4845cba3a258c095f6424557a29d3b354b +Signed-off-by: Apoorv Gupta + +net: vxlan: Added vxlan fdb notify chain + +Registered modules are notified based on following events: +1. RTM_GETNEIGH +2. RTM_NEWNEIGH +3. RTM_DELNEIGH + +Change-Id: I0802b305a829800cafbabd4728c3c47ff3679938 +Signed-off-by: Cemil Coskun +Signed-off-by: Apoorv Gupta +Signed-off-by: Vishnu Vardhan Bantanahal +--- + drivers/net/vxlan/vxlan_core.c | 31 +++++++++++++++++++++++++++++++ + include/net/vxlan.h | 29 +++++++++++++++++++++++++++++ + 2 files changed, 60 insertions(+) + +--- a/drivers/net/vxlan/vxlan_core.c ++++ b/drivers/net/vxlan/vxlan_core.c +@@ -65,6 +65,20 @@ static void vxlan_vs_del_dev(struct vxla + /* salt for hash table */ + static u32 vxlan_salt __read_mostly; + ++ATOMIC_NOTIFIER_HEAD(vxlan_fdb_notifier_list); ++ ++void vxlan_fdb_register_notify(struct notifier_block *nb) ++{ ++ atomic_notifier_chain_register(&vxlan_fdb_notifier_list, nb); ++} ++EXPORT_SYMBOL(vxlan_fdb_register_notify); ++ ++void vxlan_fdb_unregister_notify(struct notifier_block *nb) ++{ ++ atomic_notifier_chain_unregister(&vxlan_fdb_notifier_list, nb); ++} ++EXPORT_SYMBOL(vxlan_fdb_unregister_notify); ++ + static inline bool vxlan_collect_metadata(struct vxlan_sock *vs) + { + return vs->flags & VXLAN_F_COLLECT_METADATA || +@@ -260,6 +274,7 @@ static void __vxlan_fdb_notify(struct vx + { + struct net *net = dev_net(vxlan->dev); + struct sk_buff *skb; ++ struct vxlan_fdb_event vfe; + int err = -ENOBUFS; + + skb = nlmsg_new(vxlan_nlmsg_size(), GFP_ATOMIC); +@@ -275,6 +290,10 @@ static void __vxlan_fdb_notify(struct vx + } + + rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC); ++ vfe.dev = vxlan->dev; ++ vfe.rdst = rd; ++ ether_addr_copy(vfe.eth_addr, fdb->eth_addr); ++ atomic_notifier_call_chain(&vxlan_fdb_notifier_list, type, (void *)&vfe); + return; + errout: + if (err < 0) +@@ -441,6 +460,18 @@ static struct vxlan_fdb *vxlan_find_mac( + return f; + } + ++/* Find and update age of fdb entry corresponding to MAC. */ ++void vxlan_fdb_update_mac(struct vxlan_dev *vxlan, const u8 *mac, uint32_t vni) ++{ ++ u32 hash_index; ++ ++ hash_index = fdb_head_index(vxlan, mac, vni); ++ spin_lock_bh(&vxlan->hash_lock[hash_index]); ++ vxlan_find_mac(vxlan, mac, vni); ++ spin_unlock_bh(&vxlan->hash_lock[hash_index]); ++} ++EXPORT_SYMBOL(vxlan_fdb_update_mac); ++ + /* caller should hold vxlan->hash_lock */ + static struct vxlan_rdst *vxlan_fdb_find_rdst(struct vxlan_fdb *f, + union vxlan_addr *ip, __be16 port, +--- a/include/net/vxlan.h ++++ b/include/net/vxlan.h +@@ -352,6 +352,19 @@ struct vxlan_dev { + VXLAN_F_VNIFILTER | \ + VXLAN_F_LOCALBYPASS) + ++/* ++ * Application data for fdb notifier event ++ */ ++struct vxlan_fdb_event { ++ struct net_device *dev; ++ struct vxlan_rdst *rdst; ++ u8 eth_addr[ETH_ALEN]; ++}; ++ ++extern void vxlan_fdb_register_notify(struct notifier_block *nb); ++extern void vxlan_fdb_unregister_notify(struct notifier_block *nb); ++extern void vxlan_fdb_update_mac(struct vxlan_dev *vxlan, const u8 *mac, uint32_t vni); ++ + struct net_device *vxlan_dev_create(struct net *net, const char *name, + u8 name_assign_type, struct vxlan_config *conf); + +@@ -440,6 +453,22 @@ static inline __be32 vxlan_compute_rco(u + return vni_field; + } + ++/* ++ * is_vxlan_dev() ++ * Check if it is a VxLAN netdevice. ++ */ ++static inline bool is_vxlan_dev(const struct net_device *dev) ++{ ++ if (!dev) ++ return false; ++ ++ if ((dev->dev.type) && ++ !strncmp(dev->dev.type->name, "vxlan", sizeof("vxlan"))) { ++ return true; ++ } ++ return false; ++} ++ + static inline unsigned short vxlan_get_sk_family(struct vxlan_sock *vs) + { + return vs->sock->sk->sk_family; diff --git a/target/linux/ipq806x/patches-6.6/990-047-qca-nss-cfi-support.patch b/target/linux/ipq806x/patches-6.6/990-047-qca-nss-cfi-support.patch new file mode 100644 index 0000000000..5d14a5f7ad --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-047-qca-nss-cfi-support.patch @@ -0,0 +1,111 @@ +--- a/crypto/authenc.c ++++ b/crypto/authenc.c +@@ -415,6 +415,8 @@ static int crypto_authenc_create(struct + enc->base.cra_driver_name) >= CRYPTO_MAX_ALG_NAME) + goto err_free_inst; + ++ inst->alg.base.cra_flags |= (auth_base->cra_flags | ++ enc->base.cra_flags) & CRYPTO_ALG_NOSUPP_SG; + inst->alg.base.cra_priority = enc->base.cra_priority * 10 + + auth_base->cra_priority; + inst->alg.base.cra_blocksize = enc->base.cra_blocksize; +--- a/include/linux/crypto.h ++++ b/include/linux/crypto.h +@@ -86,6 +86,11 @@ + #define CRYPTO_NOLOAD 0x00008000 + + /* ++ * Set this flag if algorithm does not support SG list transforms ++ */ ++#define CRYPTO_ALG_NOSUPP_SG 0x0000c000 ++ ++/* + * The algorithm may allocate memory during request processing, i.e. during + * encryption, decryption, or hashing. Users can request an algorithm with this + * flag unset if they can't handle memory allocation failures. +--- a/net/ipv4/esp4.c ++++ b/net/ipv4/esp4.c +@@ -657,6 +657,7 @@ static int esp_output(struct xfrm_state + struct ip_esp_hdr *esph; + struct crypto_aead *aead; + struct esp_info esp; ++ bool nosupp_sg; + + esp.inplace = true; + +@@ -668,6 +669,11 @@ static int esp_output(struct xfrm_state + aead = x->data; + alen = crypto_aead_authsize(aead); + ++ nosupp_sg = crypto_tfm_alg_type(&aead->base) & CRYPTO_ALG_NOSUPP_SG; ++ if (nosupp_sg && skb_linearize(skb)) { ++ return -ENOMEM; ++ } ++ + esp.tfclen = 0; + if (x->tfcpad) { + struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb); +@@ -889,6 +895,7 @@ static int esp_input(struct xfrm_state * + u8 *iv; + struct scatterlist *sg; + int err = -EINVAL; ++ bool nosupp_sg; + + if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr) + ivlen)) + goto out; +@@ -896,6 +903,12 @@ static int esp_input(struct xfrm_state * + if (elen <= 0) + goto out; + ++ nosupp_sg = crypto_tfm_alg_type(&aead->base) & CRYPTO_ALG_NOSUPP_SG; ++ if (nosupp_sg && skb_linearize(skb)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ + assoclen = sizeof(struct ip_esp_hdr); + seqhilen = 0; + +--- a/net/ipv6/esp6.c ++++ b/net/ipv6/esp6.c +@@ -695,6 +695,7 @@ static int esp6_output(struct xfrm_state + struct ip_esp_hdr *esph; + struct crypto_aead *aead; + struct esp_info esp; ++ bool nosupp_sg; + + esp.inplace = true; + +@@ -706,6 +707,11 @@ static int esp6_output(struct xfrm_state + aead = x->data; + alen = crypto_aead_authsize(aead); + ++ nosupp_sg = crypto_tfm_alg_type(&aead->base) & CRYPTO_ALG_NOSUPP_SG; ++ if (nosupp_sg && skb_linearize(skb)) { ++ return -ENOMEM; ++ } ++ + esp.tfclen = 0; + if (x->tfcpad) { + struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb); +@@ -933,6 +939,7 @@ static int esp6_input(struct xfrm_state + __be32 *seqhi; + u8 *iv; + struct scatterlist *sg; ++ bool nosupp_sg; + + if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr) + ivlen)) { + ret = -EINVAL; +@@ -944,6 +951,12 @@ static int esp6_input(struct xfrm_state + goto out; + } + ++ nosupp_sg = crypto_tfm_alg_type(&aead->base) & CRYPTO_ALG_NOSUPP_SG; ++ if (nosupp_sg && skb_linearize(skb)) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ + assoclen = sizeof(struct ip_esp_hdr); + seqhilen = 0; + diff --git a/target/linux/ipq806x/patches-6.6/990-048-netfilter_optional_tcp_window_check.patch b/target/linux/ipq806x/patches-6.6/990-048-netfilter_optional_tcp_window_check.patch new file mode 100644 index 0000000000..d6f8ed0d8a --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-048-netfilter_optional_tcp_window_check.patch @@ -0,0 +1,99 @@ +From ed42112c77bfb68594f49e252ace2dd6b8c8e7ff Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Thu, 16 Mar 2023 17:21:39 +0530 +Subject: [PATCH 063/281] OpenWrt: + 613-netfilter_optional_tcp_window_check.patch + +netfilter: optional tcp window check + +Signed-off-by: Felix Fietkau +Signed-off-by: Christian 'Ansuel' Marangi + +Change-Id: I6f7a23b89062cca58c87554e75ae32b0e2aa2831 +Signed-off-by: Ram Chandra Jangir +--- + include/net/netns/conntrack.h | 1 + + net/netfilter/nf_conntrack_proto_tcp.c | 9 ++++++++- + net/netfilter/nf_conntrack_standalone.c | 10 ++++++++++ + 3 files changed, 19 insertions(+), 1 deletion(-) + +--- a/include/net/netns/conntrack.h ++++ b/include/net/netns/conntrack.h +@@ -26,6 +26,7 @@ struct nf_tcp_net { + unsigned int timeouts[TCP_CONNTRACK_TIMEOUT_MAX]; + u8 tcp_loose; + u8 tcp_be_liberal; ++ u8 tcp_no_window_check; + u8 tcp_max_retrans; + u8 tcp_ignore_invalid_rst; + #if IS_ENABLED(CONFIG_NF_FLOW_TABLE) +--- a/net/netfilter/nf_conntrack_proto_tcp.c ++++ b/net/netfilter/nf_conntrack_proto_tcp.c +@@ -515,11 +515,15 @@ tcp_in_window(struct nf_conn *ct, enum i + struct ip_ct_tcp *state = &ct->proto.tcp; + struct ip_ct_tcp_state *sender = &state->seen[dir]; + struct ip_ct_tcp_state *receiver = &state->seen[!dir]; ++ const struct nf_tcp_net *tn = nf_tcp_pernet(nf_ct_net(ct)); + __u32 seq, ack, sack, end, win, swin; + bool in_recv_win, seq_ok; + s32 receiver_offset; + u16 win_raw; + ++ if (tn->tcp_no_window_check) ++ return NFCT_TCP_ACCEPT; ++ + /* + * Get the required data from the packet. + */ +@@ -1285,7 +1289,7 @@ int nf_conntrack_tcp_packet(struct nf_co + IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED && + timeouts[new_state] > timeouts[TCP_CONNTRACK_UNACK]) + timeout = timeouts[TCP_CONNTRACK_UNACK]; +- else if (ct->proto.tcp.last_win == 0 && ++ else if (!tn->tcp_no_window_check && ct->proto.tcp.last_win == 0 && + timeouts[new_state] > timeouts[TCP_CONNTRACK_RETRANS]) + timeout = timeouts[TCP_CONNTRACK_RETRANS]; + else +@@ -1601,6 +1605,9 @@ void nf_conntrack_tcp_init_net(struct ne + */ + tn->tcp_be_liberal = 0; + ++ /* Skip Windows Check */ ++ tn->tcp_no_window_check = 0; ++ + /* If it's non-zero, we turn off RST sequence number check */ + tn->tcp_ignore_invalid_rst = 0; + +--- a/net/netfilter/nf_conntrack_standalone.c ++++ b/net/netfilter/nf_conntrack_standalone.c +@@ -630,6 +630,7 @@ enum nf_ct_sysctl_index { + #endif + NF_SYSCTL_CT_PROTO_TCP_LOOSE, + NF_SYSCTL_CT_PROTO_TCP_LIBERAL, ++ NF_SYSCTL_CT_PROTO_TCP_NO_WINDOW_CHECK, + NF_SYSCTL_CT_PROTO_TCP_IGNORE_INVALID_RST, + NF_SYSCTL_CT_PROTO_TCP_MAX_RETRANS, + NF_SYSCTL_CT_PROTO_TIMEOUT_UDP, +@@ -834,6 +835,14 @@ static struct ctl_table nf_ct_sysctl_tab + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + }, ++ [NF_SYSCTL_CT_PROTO_TCP_NO_WINDOW_CHECK] = { ++ .procname = "nf_conntrack_tcp_no_window_check", ++ .maxlen = sizeof(u8), ++ .mode = 0644, ++ .proc_handler = proc_dou8vec_minmax, ++ .extra1 = SYSCTL_ZERO, ++ .extra2 = SYSCTL_ONE, ++ }, + [NF_SYSCTL_CT_PROTO_TCP_IGNORE_INVALID_RST] = { + .procname = "nf_conntrack_tcp_ignore_invalid_rst", + .maxlen = sizeof(u8), +@@ -1035,6 +1044,7 @@ static void nf_conntrack_standalone_init + + XASSIGN(LOOSE, &tn->tcp_loose); + XASSIGN(LIBERAL, &tn->tcp_be_liberal); ++ XASSIGN(NO_WINDOW_CHECK, &tn->tcp_no_window_check); + XASSIGN(MAX_RETRANS, &tn->tcp_max_retrans); + XASSIGN(IGNORE_INVALID_RST, &tn->tcp_ignore_invalid_rst); + #undef XASSIGN diff --git a/target/linux/ipq806x/patches-6.6/990-049-linux-fix-dscpremark-extention-doesn-t-set-the-type-.patch b/target/linux/ipq806x/patches-6.6/990-049-linux-fix-dscpremark-extention-doesn-t-set-the-type-.patch new file mode 100644 index 0000000000..a68f25396d --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-049-linux-fix-dscpremark-extention-doesn-t-set-the-type-.patch @@ -0,0 +1,44 @@ +From 4746722090513b5ad66819a758004ac198ccc56f Mon Sep 17 00:00:00 2001 +From: Ken Zhu +Date: Thu, 28 Sep 2023 10:44:56 -0700 +Subject: [PATCH] linux: fix dscpremark extention doesn't set the type length + +It needs to set up the type length for each extension. + +Change-Id: Ia2087d7c4653045665c006ca9d637ff49d27fd92 +Signed-off-by: Ken Zhu +--- + net/netfilter/nf_conntrack_extend.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/net/netfilter/nf_conntrack_extend.c ++++ b/net/netfilter/nf_conntrack_extend.c +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + + #define NF_CT_EXT_PREALLOC 128u /* conntrack events are on by default */ + +@@ -54,6 +55,10 @@ static const u8 nf_ct_ext_type_len[NF_CT + #if IS_ENABLED(CONFIG_NET_ACT_CT) + [NF_CT_EXT_ACT_CT] = sizeof(struct nf_conn_act_ct_ext), + #endif ++#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT ++ [NF_CT_EXT_DSCPREMARK] = sizeof(struct nf_ct_dscpremark_ext), ++#endif ++ + }; + + static __always_inline unsigned int total_extension_size(void) +@@ -86,6 +91,9 @@ static __always_inline unsigned int tota + #if IS_ENABLED(CONFIG_NET_ACT_CT) + + sizeof(struct nf_conn_act_ct_ext) + #endif ++#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT ++ + sizeof(struct nf_ct_dscpremark_ext) ++#endif + ; + } + diff --git a/target/linux/ipq806x/patches-6.6/990-050-net-Add-extra-priv_flag-support-in-netdevice.patch b/target/linux/ipq806x/patches-6.6/990-050-net-Add-extra-priv_flag-support-in-netdevice.patch new file mode 100644 index 0000000000..666662a197 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-050-net-Add-extra-priv_flag-support-in-netdevice.patch @@ -0,0 +1,170 @@ +From 6f8beb8ec2047f645b488dd95be6f98ce0584165 Mon Sep 17 00:00:00 2001 +From: Murat Sezgin +Date: Thu, 18 May 2023 08:47:34 -0700 +Subject: [PATCH 271/281] net: Add extra priv_flag support in netdevice + +This patch adds a flag in struct net_device which +is an extension to priv_flags and is used to +check the type of device. + +Change-Id: Ibb5a3f0c706e1b169b848aed7004aa4ea6d57069 +Signed-off-by: Pavithra R + +Add GRE std support in device tree and priv_flags + +Added dts support and priv flags support for GRE and +to check the type of device + +Change-Id: Ib05a32e9e5458c724c182187362116338d767169 +Signed-off-by: ratheesh kannoth +Signed-off-by: Pavithra R + +drivers: ifb: Add flag to identify the IFB interface. + +Add IFB flag in netdevice's ext priv flags and define an API +to let other modules find out if the netdev is IFB or not. + +Change-Id: I6dbdae521240b4a991069c2330d893ff38aec68d +Signed-off-by: Manish Verma +Signed-off-by: pavir + +net: add IFF_EXT_MAPT flag in net_device priv_flags_ext + +Add IFF_EXT_MAPT flag in priv_flags_ext for MAP-T device. + +Change-Id: I8aa1715c4b9c62b88eee1b40412cf4e734b2a337 +Signed-off-by: Suruchi Agarwal +Signed-off-by: Murat Sezgin +--- + drivers/net/ifb.c | 1 + + include/linux/netdevice.h | 36 ++++++++++++++++++++++++++++++++++-- + net/ipv4/ip_gre.c | 2 +- + net/ipv6/ip6_gre.c | 4 +--- + 4 files changed, 37 insertions(+), 6 deletions(-) + +--- a/drivers/net/ifb.c ++++ b/drivers/net/ifb.c +@@ -326,6 +326,7 @@ static void ifb_setup(struct net_device + dev->flags |= IFF_NOARP; + dev->flags &= ~IFF_MULTICAST; + dev->priv_flags &= ~IFF_TX_SKB_SHARING; ++ dev->priv_flags_ext |= IFF_EXT_IFB; /* Mark the device as an IFB device. */ + netif_keep_dst(dev); + eth_hw_addr_random(dev); + dev->needs_free_netdev = true; +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -1781,6 +1781,32 @@ enum netdev_priv_flags { + IFF_NO_IP_ALIGN = BIT_ULL(34), + }; + ++/** ++ * enum netdev_priv_flags_ext - &struct net_device priv_flags_ext ++ * ++ * These flags are used to check for device type and can be ++ * set and used by the drivers ++ * ++ * @IFF_EXT_TUN_TAP: device is a TUN/TAP device ++ * @IFF_EXT_PPP_L2TPV2: device is a L2TPV2 device ++ * @IFF_EXT_PPP_L2TPV3: device is a L2TPV3 device ++ * @IFF_EXT_PPP_PPTP: device is a PPTP device ++ * @IFF_EXT_GRE_V4_TAP: device is a GRE IPv4 TAP device ++ * @IFF_EXT_GRE_V6_TAP: device is a GRE IPv6 TAP device ++ * @IFF_EXT_IFB: device is an IFB device ++ * @IFF_EXT_MAPT: device is an MAPT device ++ */ ++enum netdev_priv_flags_ext { ++ IFF_EXT_TUN_TAP = 1<<0, ++ IFF_EXT_PPP_L2TPV2 = 1<<1, ++ IFF_EXT_PPP_L2TPV3 = 1<<2, ++ IFF_EXT_PPP_PPTP = 1<<3, ++ IFF_EXT_GRE_V4_TAP = 1<<4, ++ IFF_EXT_GRE_V6_TAP = 1<<5, ++ IFF_EXT_IFB = 1<<6, ++ IFF_EXT_MAPT = 1<<7, ++}; ++ + #define IFF_802_1Q_VLAN IFF_802_1Q_VLAN + #define IFF_EBRIDGE IFF_EBRIDGE + #define IFF_BONDING IFF_BONDING +@@ -1901,6 +1927,8 @@ enum netdev_stat_type { + * @xdp_features: XDP capability supported by the device + * @priv_flags: Like 'flags' but invisible to userspace, + * see if.h for the definitions ++ * @priv_flags_ext: Extension for 'priv_flags' ++ * + * @gflags: Global flags ( kept as legacy ) + * @padded: How much padding added by alloc_netdev() + * @operstate: RFC2863 operstate +@@ -2146,6 +2174,7 @@ struct net_device { + unsigned int flags; + xdp_features_t xdp_features; + unsigned long long priv_flags; ++ unsigned int priv_flags_ext; + const struct net_device_ops *netdev_ops; + const struct xdp_metadata_ops *xdp_metadata_ops; + int ifindex; +@@ -5245,6 +5274,11 @@ static inline bool netif_is_failover_sla + return dev->priv_flags & IFF_FAILOVER_SLAVE; + } + ++static inline bool netif_is_ifb_dev(const struct net_device *dev) ++{ ++ return dev->priv_flags_ext & IFF_EXT_IFB; ++} ++ + /* This device needs to keep skb dst for qdisc enqueue or ndo_start_xmit() */ + static inline void netif_keep_dst(struct net_device *dev) + { +--- a/net/ipv4/ip_gre.c ++++ b/net/ipv4/ip_gre.c +@@ -828,7 +828,6 @@ static int ipgre_tunnel_ctl(struct net_d + It allows to construct virtual multiprotocol broadcast "LAN" + over the Internet, provided multicast routing is tuned. + +- + I have no idea was this bicycle invented before me, + so that I had to set ARPHRD_IPGRE to a random value. + I have an impression, that Cisco could make something similar, +@@ -1337,6 +1336,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; + ip_tunnel_setup(dev, gre_tap_net_id); + } + +--- a/net/ipv6/ip6_gre.c ++++ b/net/ipv6/ip6_gre.c +@@ -53,7 +53,6 @@ + #include + #include + +- + static bool log_ecn_error = true; + module_param(log_ecn_error, bool, 0644); + MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); +@@ -414,7 +413,6 @@ static void ip6gre_tunnel_uninit(struct + netdev_put(dev, &t->dev_tracker); + } + +- + static int ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + u8 type, u8 code, int offset, __be32 info) + { +@@ -1618,7 +1616,6 @@ static int __net_init ip6gre_init_net(st + */ + ign->fb_tunnel_dev->features |= NETIF_F_NETNS_LOCAL; + +- + ip6gre_fb_tunnel_init(ign->fb_tunnel_dev); + ign->fb_tunnel_dev->rtnl_link_ops = &ip6gre_link_ops; + +@@ -1941,6 +1938,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; + netif_keep_dst(dev); + } + diff --git a/target/linux/ipq806x/patches-6.6/990-051-qca-nss-drv-add-qdisc-support.patch b/target/linux/ipq806x/patches-6.6/990-051-qca-nss-drv-add-qdisc-support.patch new file mode 100644 index 0000000000..7219cfb410 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-051-qca-nss-drv-add-qdisc-support.patch @@ -0,0 +1,470 @@ +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -762,6 +762,7 @@ typedef unsigned char *sk_buff_data_t; + * @offload_fwd_mark: Packet was L2-forwarded in hardware + * @offload_l3_fwd_mark: Packet was L3-forwarded in hardware + * @tc_skip_classify: do not classify packet. set by IFB device ++ * @tc_skip_classify_offload: do not classify packet set by offload IFB device + * @tc_at_ingress: used within tc_classify to distinguish in/egress + * @redirected: packet was redirected by packet classifier + * @from_ingress: packet was redirected from the ingress path +@@ -970,6 +971,12 @@ struct sk_buff { + __u8 offload_fwd_mark:1; + __u8 offload_l3_fwd_mark:1; + #endif ++#ifdef CONFIG_NET_CLS_ACT ++ __u8 tc_skip_classify:1; ++ __u8 tc_at_ingress:1; ++ __u8 tc_skip_classify_offload:1; ++ __u16 tc_verd_qca_nss; ++#endif + __u8 redirected:1; + #ifdef CONFIG_NET_REDIRECT + __u8 from_ingress:1; +--- a/include/uapi/linux/pkt_cls.h ++++ b/include/uapi/linux/pkt_cls.h +@@ -139,6 +139,7 @@ enum tca_id { + TCA_ID_MPLS, + TCA_ID_CT, + TCA_ID_GATE, ++ TCA_ID_MIRRED_NSS, + /* other actions go here */ + __TCA_ID_MAX = 255 + }; +@@ -817,4 +818,14 @@ enum { + TCF_EM_OPND_LT + }; + ++ ++#define _TC_MAKE32(x) ((x)) ++#define _TC_MAKEMASK1(n) (_TC_MAKE32(1) << _TC_MAKE32(n)) ++ ++#define TC_NCLS _TC_MAKEMASK1(8) ++#define TC_NCLS_NSS _TC_MAKEMASK1(12) ++#define SET_TC_NCLS_NSS(v) ( TC_NCLS_NSS | ((v) & ~TC_NCLS_NSS)) ++#define CLR_TC_NCLS_NSS(v) ( (v) & ~TC_NCLS_NSS) ++ ++ + #endif +--- a/include/linux/timer.h ++++ b/include/linux/timer.h +@@ -17,6 +17,7 @@ struct timer_list { + unsigned long expires; + void (*function)(struct timer_list *); + u32 flags; ++ unsigned long cust_data; + + #ifdef CONFIG_LOCKDEP + struct lockdep_map lockdep_map; +--- a/drivers/net/ifb.c ++++ b/drivers/net/ifb.c +@@ -151,6 +151,31 @@ resched: + + } + ++void ifb_update_offload_stats(struct net_device *dev, struct pcpu_sw_netstats *offload_stats) ++{ ++ struct ifb_dev_private *dp; ++ struct ifb_q_private *txp; ++ ++ if (!dev || !offload_stats) { ++ return; ++ } ++ ++ if (!(dev->priv_flags_ext & IFF_EXT_IFB)) { ++ return; ++ } ++ ++ dp = netdev_priv(dev); ++ txp = dp->tx_private; ++ ++ u64_stats_update_begin(&txp->rsync); ++ txp->rx_packets += offload_stats->rx_packets; ++ txp->rx_bytes += offload_stats->rx_bytes; ++ txp->tx_packets += offload_stats->tx_packets; ++ txp->tx_bytes += offload_stats->tx_bytes; ++ u64_stats_update_end(&txp->rsync); ++} ++EXPORT_SYMBOL(ifb_update_offload_stats); ++ + static void ifb_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) + { +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -4730,6 +4730,15 @@ void dev_uc_flush(struct net_device *dev + void dev_uc_init(struct net_device *dev); + + /** ++ * ifb_update_offload_stats - Update the IFB interface stats ++ * @dev: IFB device to update the stats ++ * @offload_stats: per CPU stats structure ++ * ++ * Allows update of IFB stats when flows are offloaded to an accelerator. ++ **/ ++void ifb_update_offload_stats(struct net_device *dev, struct pcpu_sw_netstats *offload_stats); ++ ++/** + * __dev_uc_sync - Synchonize device's unicast list + * @dev: device to sync + * @sync: function to call if address should be added +--- a/include/uapi/linux/pkt_sched.h ++++ b/include/uapi/linux/pkt_sched.h +@@ -1306,4 +1306,248 @@ enum { + + #define TCA_ETS_MAX (__TCA_ETS_MAX - 1) + ++ ++enum { ++ TCA_NSS_ACCEL_MODE_NSS_FW, ++ TCA_NSS_ACCEL_MODE_PPE, ++ TCA_NSS_ACCEL_MODE_MAX ++}; ++ ++/* NSSFIFO section */ ++ ++enum { ++ TCA_NSSFIFO_UNSPEC, ++ TCA_NSSFIFO_PARMS, ++ __TCA_NSSFIFO_MAX ++}; ++ ++#define TCA_NSSFIFO_MAX (__TCA_NSSFIFO_MAX - 1) ++ ++struct tc_nssfifo_qopt { ++ __u32 limit; /* Queue length: bytes for bfifo, packets for pfifo */ ++ __u8 set_default; /* Sets qdisc to be the default qdisc for enqueue */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSWRED section */ ++ ++enum { ++ TCA_NSSWRED_UNSPEC, ++ TCA_NSSWRED_PARMS, ++ __TCA_NSSWRED_MAX ++}; ++ ++#define TCA_NSSWRED_MAX (__TCA_NSSWRED_MAX - 1) ++#define NSSWRED_CLASS_MAX 6 ++struct tc_red_alg_parameter { ++ __u32 min; /* qlen_avg < min: pkts are all enqueued */ ++ __u32 max; /* qlen_avg > max: pkts are all dropped */ ++ __u32 probability;/* Drop probability at qlen_avg = max */ ++ __u32 exp_weight_factor;/* exp_weight_factor for calculate qlen_avg */ ++}; ++ ++struct tc_nsswred_traffic_class { ++ __u32 limit; /* Queue length */ ++ __u32 weight_mode_value; /* Weight mode value */ ++ struct tc_red_alg_parameter rap;/* Parameters for RED alg */ ++}; ++ ++/* ++ * Weight modes for WRED ++ */ ++enum tc_nsswred_weight_modes { ++ TC_NSSWRED_WEIGHT_MODE_DSCP = 0,/* Weight mode is DSCP */ ++ TC_NSSWRED_WEIGHT_MODES, /* Must be last */ ++}; ++ ++struct tc_nsswred_qopt { ++ __u32 limit; /* Queue length */ ++ enum tc_nsswred_weight_modes weight_mode; ++ /* Weight mode */ ++ __u32 traffic_classes; /* How many traffic classes: DPs */ ++ __u32 def_traffic_class; /* Default traffic if no match: def_DP */ ++ __u32 traffic_id; /* The traffic id to be configured: DP */ ++ __u32 weight_mode_value; /* Weight mode value */ ++ struct tc_red_alg_parameter rap;/* RED algorithm parameters */ ++ struct tc_nsswred_traffic_class tntc[NSSWRED_CLASS_MAX]; ++ /* Traffic settings for dumpping */ ++ __u8 ecn; /* Setting ECN bit or dropping */ ++ __u8 set_default; /* Sets qdisc to be the default for enqueue */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSCODEL section */ ++ ++enum { ++ TCA_NSSCODEL_UNSPEC, ++ TCA_NSSCODEL_PARMS, ++ __TCA_NSSCODEL_MAX ++}; ++ ++#define TCA_NSSCODEL_MAX (__TCA_NSSCODEL_MAX - 1) ++ ++struct tc_nsscodel_qopt { ++ __u32 target; /* Acceptable queueing delay */ ++ __u32 limit; /* Max number of packets that can be held in the queue */ ++ __u32 interval; /* Monitoring interval */ ++ __u32 flows; /* Number of flow buckets */ ++ __u32 quantum; /* Weight (in bytes) used for DRR of flow buckets */ ++ __u8 ecn; /* 0 - disable ECN, 1 - enable ECN */ ++ __u8 set_default; /* Sets qdisc to be the default qdisc for enqueue */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++struct tc_nsscodel_xstats { ++ __u32 peak_queue_delay; /* Peak delay experienced by a dequeued packet */ ++ __u32 peak_drop_delay; /* Peak delay experienced by a dropped packet */ ++}; ++ ++/* NSSFQ_CODEL section */ ++ ++struct tc_nssfq_codel_xstats { ++ __u32 new_flow_count; /* Total number of new flows seen */ ++ __u32 new_flows_len; /* Current number of new flows */ ++ __u32 old_flows_len; /* Current number of old flows */ ++ __u32 ecn_mark; /* Number of packets marked with ECN */ ++ __u32 drop_overlimit; /* Number of packets dropped due to overlimit */ ++ __u32 maxpacket; /* The largest packet seen so far in the queue */ ++}; ++ ++/* NSSTBL section */ ++ ++enum { ++ TCA_NSSTBL_UNSPEC, ++ TCA_NSSTBL_PARMS, ++ __TCA_NSSTBL_MAX ++}; ++ ++#define TCA_NSSTBL_MAX (__TCA_NSSTBL_MAX - 1) ++ ++struct tc_nsstbl_qopt { ++ __u32 burst; /* Maximum burst size */ ++ __u32 rate; /* Limiting rate of TBF */ ++ __u32 peakrate; /* Maximum rate at which TBF is allowed to send */ ++ __u32 mtu; /* Max size of packet, or minumim burst size */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSPRIO section */ ++ ++#define TCA_NSSPRIO_MAX_BANDS 256 ++ ++enum { ++ TCA_NSSPRIO_UNSPEC, ++ TCA_NSSPRIO_PARMS, ++ __TCA_NSSPRIO_MAX ++}; ++ ++#define TCA_NSSPRIO_MAX (__TCA_NSSPRIO_MAX - 1) ++ ++struct tc_nssprio_qopt { ++ __u32 bands; /* Number of bands */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSBF section */ ++ ++enum { ++ TCA_NSSBF_UNSPEC, ++ TCA_NSSBF_CLASS_PARMS, ++ TCA_NSSBF_QDISC_PARMS, ++ __TCA_NSSBF_MAX ++}; ++ ++#define TCA_NSSBF_MAX (__TCA_NSSBF_MAX - 1) ++ ++struct tc_nssbf_class_qopt { ++ __u32 burst; /* Maximum burst size */ ++ __u32 rate; /* Allowed bandwidth for this class */ ++ __u32 mtu; /* MTU of the associated interface */ ++ __u32 quantum; /* Quantum allocation for DRR */ ++}; ++ ++struct tc_nssbf_qopt { ++ __u16 defcls; /* Default class value */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSWRR section */ ++ ++enum { ++ TCA_NSSWRR_UNSPEC, ++ TCA_NSSWRR_CLASS_PARMS, ++ TCA_NSSWRR_QDISC_PARMS, ++ __TCA_NSSWRR_MAX ++}; ++ ++#define TCA_NSSWRR_MAX (__TCA_NSSWRR_MAX - 1) ++ ++struct tc_nsswrr_class_qopt { ++ __u32 quantum; /* Weight associated to this class */ ++}; ++ ++struct tc_nsswrr_qopt { ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSWFQ section */ ++ ++enum { ++ TCA_NSSWFQ_UNSPEC, ++ TCA_NSSWFQ_CLASS_PARMS, ++ TCA_NSSWFQ_QDISC_PARMS, ++ __TCA_NSSWFQ_MAX ++}; ++ ++#define TCA_NSSWFQ_MAX (__TCA_NSSWFQ_MAX - 1) ++ ++struct tc_nsswfq_class_qopt { ++ __u32 quantum; /* Weight associated to this class */ ++}; ++ ++struct tc_nsswfq_qopt { ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSHTB section */ ++ ++enum { ++ TCA_NSSHTB_UNSPEC, ++ TCA_NSSHTB_CLASS_PARMS, ++ TCA_NSSHTB_QDISC_PARMS, ++ __TCA_NSSHTB_MAX ++}; ++ ++#define TCA_NSSHTB_MAX (__TCA_NSSHTB_MAX - 1) ++ ++struct tc_nsshtb_class_qopt { ++ __u32 burst; /* Allowed burst size */ ++ __u32 rate; /* Allowed bandwidth for this class */ ++ __u32 cburst; /* Maximum burst size */ ++ __u32 crate; /* Maximum bandwidth for this class */ ++ __u32 quantum; /* Quantum allocation for DRR */ ++ __u32 priority; /* Priority value associated with this class */ ++ __u32 overhead; /* Overhead in bytes per packet */ ++}; ++ ++struct tc_nsshtb_qopt { ++ __u32 r2q; /* Rate to quantum ratio */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSBLACKHOLE section */ ++ ++enum { ++ TCA_NSSBLACKHOLE_UNSPEC, ++ TCA_NSSBLACKHOLE_PARMS, ++ __TCA_NSSBLACKHOLE_MAX ++}; ++ ++#define TCA_NSSBLACKHOLE_MAX (__TCA_NSSBLACKHOLE_MAX - 1) ++ ++struct tc_nssblackhole_qopt { ++ __u8 set_default; /* Sets qdisc to be the default qdisc for enqueue */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ + #endif +--- a/net/sched/sch_api.c ++++ b/net/sched/sch_api.c +@@ -2399,4 +2399,26 @@ static int __init pktsched_init(void) + return 0; + } + ++ ++bool tcf_destroy(struct tcf_proto *tp, bool force) ++{ ++ tp->ops->destroy(tp, force, NULL); ++ module_put(tp->ops->owner); ++ kfree_rcu(tp, rcu); ++ ++ return true; ++} ++ ++void tcf_destroy_chain(struct tcf_proto __rcu **fl) ++{ ++ struct tcf_proto *tp; ++ ++ while ((tp = rtnl_dereference(*fl)) != NULL) { ++ RCU_INIT_POINTER(*fl, tp->next); ++ tcf_destroy(tp, true); ++ } ++} ++EXPORT_SYMBOL(tcf_destroy_chain); ++ ++ + subsys_initcall(pktsched_init); +--- a/net/sched/sch_generic.c ++++ b/net/sched/sch_generic.c +@@ -1059,7 +1059,7 @@ static void qdisc_free_cb(struct rcu_hea + qdisc_free(q); + } + +-static void __qdisc_destroy(struct Qdisc *qdisc) ++void __qdisc_destroy(struct Qdisc *qdisc) + { + const struct Qdisc_ops *ops = qdisc->ops; + +@@ -1091,6 +1091,7 @@ void qdisc_destroy(struct Qdisc *qdisc) + + __qdisc_destroy(qdisc); + } ++EXPORT_SYMBOL(__qdisc_destroy); + + void qdisc_put(struct Qdisc *qdisc) + { +--- a/include/net/sch_generic.h ++++ b/include/net/sch_generic.h +@@ -94,6 +94,7 @@ struct Qdisc { + #define TCQ_F_INVISIBLE 0x80 /* invisible by default in dump */ + #define TCQ_F_NOLOCK 0x100 /* qdisc does not require locking */ + #define TCQ_F_OFFLOADED 0x200 /* qdisc is offloaded to HW */ ++#define TCQ_F_NSS 0x1000 /* NSS qdisc flag. */ + u32 limit; + const struct Qdisc_ops *ops; + struct qdisc_size_table __rcu *stab; +@@ -752,6 +753,40 @@ static inline bool skb_skip_tc_classify( + return false; + } + ++/* ++ * Set skb classify bit field. ++ */ ++static inline void skb_set_tc_classify_offload(struct sk_buff *skb) ++{ ++#ifdef CONFIG_NET_CLS_ACT ++ skb->tc_skip_classify_offload = 1; ++#endif ++} ++ ++/* ++ * Clear skb classify bit field. ++ */ ++static inline void skb_clear_tc_classify_offload(struct sk_buff *skb) ++{ ++#ifdef CONFIG_NET_CLS_ACT ++ skb->tc_skip_classify_offload = 0; ++#endif ++} ++ ++/* ++ * Skip skb processing if sent from ifb dev. ++ */ ++static inline bool skb_skip_tc_classify_offload(struct sk_buff *skb) ++{ ++#ifdef CONFIG_NET_CLS_ACT ++ if (skb->tc_skip_classify_offload) { ++ skb_clear_tc_classify_offload(skb); ++ return true; ++ } ++#endif ++ return false; ++} ++ + /* Reset all TX qdiscs greater than index of a device. */ + static inline void qdisc_reset_all_tx_gt(struct net_device *dev, unsigned int i) + { +@@ -1323,4 +1358,9 @@ static inline void qdisc_synchronize(con + msleep(1); + } + ++ ++void qdisc_destroy(struct Qdisc *qdisc); ++void tcf_destroy_chain(struct tcf_proto __rcu **fl); ++ ++ + #endif diff --git a/target/linux/ipq806x/patches-6.6/990-052-net-fix-potential-memory-leak.patch b/target/linux/ipq806x/patches-6.6/990-052-net-fix-potential-memory-leak.patch new file mode 100644 index 0000000000..9351f083a7 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-052-net-fix-potential-memory-leak.patch @@ -0,0 +1,25 @@ +From 507188b4c93e2641fd271bd08d881136a1de2068 Mon Sep 17 00:00:00 2001 +From: Ken Zhu +Date: Thu, 14 Oct 2021 08:50:03 -0700 +Subject: [PATCH 260/281] net: fix potential memory leak + +When A packets come from a disabled port, if it is not a accepted +EAPOL, it has no chance to get freed. + +Change-Id: Ib2bddeb53f2117bcc803d11912471a8456a3148a +Signed-off-by: Ken Zhu +--- + net/bridge/br_input.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/net/bridge/br_input.c ++++ b/net/bridge/br_input.c +@@ -461,7 +461,7 @@ forward: + return RX_HANDLER_PASS; + } + } +- break; ++ goto drop; + + case BR_STATE_FORWARDING: + case BR_STATE_LEARNING: diff --git a/target/linux/ipq806x/patches-6.6/990-053-net-Move-skb_vlan_untag-skb-after-sfe-hook.patch b/target/linux/ipq806x/patches-6.6/990-053-net-Move-skb_vlan_untag-skb-after-sfe-hook.patch new file mode 100644 index 0000000000..592e964028 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-053-net-Move-skb_vlan_untag-skb-after-sfe-hook.patch @@ -0,0 +1,39 @@ +From af7d2a61b63bdb996746d38288842624e662a447 Mon Sep 17 00:00:00 2001 +From: Ratheesh Kannoth +Date: Fri, 17 Dec 2021 19:17:58 +0530 +Subject: [PATCH 261/281] net: Move skb_vlan_untag(skb) after sfe hook + +Change-Id: I5a019761f363dd81bf5e52eea606b9076f22e6af +Signed-off-by: Ratheesh Kannoth +--- + net/core/dev.c | 19 ++++++------------- + 1 file changed, 6 insertions(+), 13 deletions(-) + +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -5455,12 +5455,6 @@ another_round: + } + } + +- if (eth_type_vlan(skb->protocol)) { +- skb = skb_vlan_untag(skb); +- if (unlikely(!skb)) +- goto out; +- } +- + fast_recv = rcu_dereference(athrs_fast_nat_recv); + if (fast_recv) { + if (fast_recv(skb)) { +@@ -5469,6 +5463,12 @@ another_round: + } + } + ++ if (eth_type_vlan(skb->protocol)) { ++ skb = skb_vlan_untag(skb); ++ if (unlikely(!skb)) ++ goto out; ++ } ++ + if (skb_skip_tc_classify(skb)) + goto skip_classify; + diff --git a/target/linux/ipq806x/patches-6.6/990-054-net-add-fast-xmit-api.patch b/target/linux/ipq806x/patches-6.6/990-054-net-add-fast-xmit-api.patch new file mode 100644 index 0000000000..4593c26593 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-054-net-add-fast-xmit-api.patch @@ -0,0 +1,173 @@ +From 120ae502af169310aa53359d4faf2494dcee6927 Mon Sep 17 00:00:00 2001 +From: Tallapragada Kalyan +Date: Thu, 9 Jun 2022 09:32:38 +0530 +Subject: [PATCH 267/281] net: set skb's fast_xmit flag in dev_fast_xmit API + +set skb's fast_xmit flag in dev_fast_xmit API for linear packets +WiFi tx path can avoid some overhead due to checks based on this flag + +Change-Id: Ied29f9d615d0cf48dd9dcd7fcf0fb210eb259a8f +Signed-off-by: Tallapragada Kalyan +--- + include/linux/skbuff.h | 2 ++ + net/core/dev.c | 4 ++++ + 2 files changed, 6 insertions(+) + +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -992,6 +992,8 @@ struct sk_buff { + __u8 csum_not_inet:1; + #endif + __u8 fast_forwarded:1; ++ /* Linear packets processed by dev_fast_xmit() */ ++ __u8 fast_xmit:1; + /* 1 or 3 bit hole */ + + #if defined(CONFIG_NET_SCHED) || defined(CONFIG_NET_XGRESS) +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -4305,6 +4305,144 @@ struct netdev_queue *netdev_core_pick_tx + } + + /** ++ * dev_fast_xmit_vp - fast xmit the skb to a PPE virtual port ++ * @skb:buffer to transmit ++ * @dev: the device to be transmited to ++ * sucessful return true ++ * failed return false ++ */ ++bool dev_fast_xmit_vp(struct sk_buff *skb, ++ struct net_device *dev) ++{ ++ struct netdev_queue *txq; ++ int cpu; ++ netdev_tx_t rc; ++ ++ if (unlikely(!(dev->flags & IFF_UP))) { ++ return false; ++ } ++ ++ if (unlikely(skb_is_nonlinear(skb))) { ++ return false; ++ } ++ ++ rcu_read_lock_bh(); ++ cpu = smp_processor_id(); ++ ++ /* ++ * TODO: Skip this altogether and eventually move this call to ppe_vp ++ * this would avoid multiple function calls when giving packet to wifi VAP. ++ */ ++ txq = netdev_core_pick_tx(dev, skb, NULL); ++ ++ if (likely(txq->xmit_lock_owner != cpu)) { ++#define FAST_VP_HARD_TX_LOCK(txq, cpu) { \ ++ __netif_tx_lock(txq, cpu); \ ++} ++ ++#define FAST_VP_HARD_TX_UNLOCK(txq) { \ ++ __netif_tx_unlock(txq); \ ++} ++ skb->fast_xmit = 1; ++ FAST_VP_HARD_TX_LOCK(txq, cpu); ++ if (likely(!netif_xmit_stopped(txq))) { ++ rc = netdev_start_xmit(skb, dev, txq, 0); ++ if (unlikely(!dev_xmit_complete(rc))) { ++ FAST_VP_HARD_TX_UNLOCK(txq); ++ goto q_xmit; ++ } ++ FAST_VP_HARD_TX_UNLOCK(txq); ++ rcu_read_unlock_bh(); ++ return true; ++ } ++ FAST_VP_HARD_TX_UNLOCK(txq); ++ } ++q_xmit: ++ skb->fast_xmit = 0; ++ rcu_read_unlock_bh(); ++ return false; ++} ++EXPORT_SYMBOL(dev_fast_xmit_vp); ++ ++/** ++ * dev_fast_xmit - fast xmit the skb ++ * @skb:buffer to transmit ++ * @dev: the device to be transmited to ++ * @features: the skb features could bed used ++ * sucessful return true ++ * failed return false ++ */ ++bool dev_fast_xmit(struct sk_buff *skb, ++ struct net_device *dev, ++ netdev_features_t features) ++{ ++ struct netdev_queue *txq; ++ int cpu; ++ netdev_tx_t rc; ++ ++ /* the fast_xmit flag will avoid multiple checks in wifi xmit path */ ++ if (likely(!skb_is_nonlinear(skb))) ++ skb->fast_xmit = 1; ++ ++ if (unlikely(!(dev->flags & IFF_UP))) { ++ return false; ++ } ++ ++ if (unlikely(skb_needs_linearize(skb, features))) { ++ return false; ++ } ++ ++ rcu_read_lock_bh(); ++ cpu = smp_processor_id(); ++ ++ /* If device don't need the dst, release it now, otherwise make sure ++ * the refcount increased. ++ */ ++ if (likely(dev->priv_flags & IFF_XMIT_DST_RELEASE)) { ++ skb_dst_drop(skb); ++ } else { ++ skb_dst_force(skb); ++ } ++ ++ txq = netdev_core_pick_tx(dev, skb, NULL); ++ ++ if (likely(txq->xmit_lock_owner != cpu)) { ++#define FAST_HARD_TX_LOCK(features, txq, cpu) { \ ++ if ((features & NETIF_F_LLTX) == 0) { \ ++ __netif_tx_lock(txq, cpu); \ ++ } else { \ ++ __netif_tx_acquire(txq); \ ++ } \ ++} ++ ++#define FAST_HARD_TX_UNLOCK(features, txq) { \ ++ if ((features & NETIF_F_LLTX) == 0) { \ ++ __netif_tx_unlock(txq); \ ++ } else { \ ++ __netif_tx_release(txq); \ ++ } \ ++} ++ netdev_features_t dev_features = dev->features; ++ FAST_HARD_TX_LOCK(dev_features, txq, cpu); ++ if (likely(!netif_xmit_stopped(txq))) { ++ rc = netdev_start_xmit(skb, dev, txq, 0); ++ if (unlikely(!dev_xmit_complete(rc))) { ++ FAST_HARD_TX_UNLOCK(dev_features, txq); ++ goto fail; ++ } ++ FAST_HARD_TX_UNLOCK(dev_features, txq); ++ rcu_read_unlock_bh(); ++ return true; ++ } ++ FAST_HARD_TX_UNLOCK(dev_features, txq); ++ } ++fail: ++ rcu_read_unlock_bh(); ++ return false; ++} ++EXPORT_SYMBOL(dev_fast_xmit); ++ ++/** + * __dev_queue_xmit() - transmit a buffer + * @skb: buffer to transmit + * @sb_dev: suboordinate device used for L2 forwarding offload diff --git a/target/linux/ipq806x/patches-6.6/990-055-net-do-time-update-rather-than-fully-update-in-the-f.patch b/target/linux/ipq806x/patches-6.6/990-055-net-do-time-update-rather-than-fully-update-in-the-f.patch new file mode 100644 index 0000000000..e687db3d66 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-055-net-do-time-update-rather-than-fully-update-in-the-f.patch @@ -0,0 +1,52 @@ +From 246b87f0daff042ad215c9c55b411e70ae5406e2 Mon Sep 17 00:00:00 2001 +From: Ken Zhu +Date: Mon, 25 Apr 2022 13:54:20 -0700 +Subject: [PATCH 263/281] net: do time update rather than fully update in the + fdb + +keep the fdb up to date when do refresh. + +Change-Id: Ib0297f11920b66bb573ef41f8a09da9ac0d774af +Signed-off-by: Ken Zhu +--- + include/linux/if_bridge.h | 1 + + net/bridge/br_fdb.c | 18 ++++++++++++++++++ + 2 files changed, 19 insertions(+) + +--- a/include/linux/if_bridge.h ++++ b/include/linux/if_bridge.h +@@ -78,6 +78,7 @@ extern struct net_device *br_port_dev_ge + 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 void br_dev_update_stats(struct net_device *dev, + struct rtnl_link_stats64 *nlstats); + extern struct net_bridge_fdb_entry *br_fdb_has_entry(struct net_device *dev, +--- a/net/bridge/br_fdb.c ++++ b/net/bridge/br_fdb.c +@@ -1006,6 +1006,24 @@ void br_refresh_fdb_entry(struct net_dev + } + 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) diff --git a/target/linux/ipq806x/patches-6.6/990-057-dev_queue_xmit-Reset-skb-fast_xmit-flag-in-slow-path.patch b/target/linux/ipq806x/patches-6.6/990-057-dev_queue_xmit-Reset-skb-fast_xmit-flag-in-slow-path.patch new file mode 100644 index 0000000000..a85527c1df --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-057-dev_queue_xmit-Reset-skb-fast_xmit-flag-in-slow-path.patch @@ -0,0 +1,31 @@ +From 782c8dfcfeff6115a857ca9299df35cffaf8d476 Mon Sep 17 00:00:00 2001 +From: Tushar Ganatra +Date: Wed, 6 Dec 2023 12:45:26 +0530 +Subject: [PATCH] dev_queue_xmit: Reset skb->fast_xmit flag in slow path + transmit + +Reset skb->fast_xmit flag in "dev_queue_xmit". This avoids a combination +scenarios like PPPoE with DS-Lite or other tunnels where one rule is matched +with sfe and was transmitted in fast path and the inner is transmitted through +slow path. + +Change-Id: Ia12907f459c15747daf00a216672e903c924332a +Signed-off-by: Tushar Ganatra +--- + net/core/dev.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -4474,6 +4474,11 @@ int __dev_queue_xmit(struct sk_buff *skb + skb_reset_mac_header(skb); + skb_assert_len(skb); + ++ /* ++ * if the skb landed in dev_queue_xmit then its not fast transmitted ++ * reset this flag for further processing. ++ */ ++ skb->fast_xmit = 0; + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_SCHED_TSTAMP)) + __skb_tstamp_tx(skb, NULL, NULL, skb->sk, SCM_TSTAMP_SCHED); + diff --git a/target/linux/ipq806x/patches-6.6/990-058-arm-arm64-enable-hairpin-for-WLAN-netdevices-only.patch b/target/linux/ipq806x/patches-6.6/990-058-arm-arm64-enable-hairpin-for-WLAN-netdevices-only.patch new file mode 100644 index 0000000000..590efa2aaf --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-058-arm-arm64-enable-hairpin-for-WLAN-netdevices-only.patch @@ -0,0 +1,37 @@ +--- a/net/bridge/br_forward.c ++++ b/net/bridge/br_forward.c +@@ -24,11 +24,29 @@ static inline int should_deliver(const s + struct net_bridge_vlan_group *vg; + + vg = nbp_vlan_group_rcu(p); +- return (((p->flags & BR_HAIRPIN_MODE) && !is_multicast_ether_addr(eth_hdr(skb)->h_dest)) +- || skb->dev != p->dev) && +- (br_mst_is_enabled(p->br) || p->state == BR_STATE_FORWARDING) && +- br_allowed_egress(vg, skb) && nbp_switchdev_allowed_egress(p, skb) && +- !br_skb_isolated(p, skb); ++ ++ if ((skb->dev != p->dev) && ++ br_allowed_egress(vg, skb) && p->state == BR_STATE_FORWARDING && ++ nbp_switchdev_allowed_egress(p, skb) && ++ !br_skb_isolated(p, skb)) { ++ return true; ++ } ++ ++ if ((skb->dev == p->dev) && ++ (p->flags & BR_HAIRPIN_MODE) && ++ br_allowed_egress(vg, skb) && p->state == BR_STATE_FORWARDING) { ++ ++ /* Allow hairpin only on WLAN netdevices. ++ * For ethernet interfaces, hairpin not allowed ++ * due to issue with switchdevs. */ ++ if ( skb->dev->ieee80211_ptr == NULL) { ++ return false; ++ } ++ ++ return true; ++ } ++ ++ return false; + } + + int br_dev_queue_push_xmit(struct net *net, struct sock *sk, struct sk_buff *skb) diff --git a/target/linux/ipq806x/patches-6.6/990-059-net-bridge-Allow-unicast-loop-back-when-hairpin-enab.patch b/target/linux/ipq806x/patches-6.6/990-059-net-bridge-Allow-unicast-loop-back-when-hairpin-enab.patch new file mode 100644 index 0000000000..a5b51f4f52 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-059-net-bridge-Allow-unicast-loop-back-when-hairpin-enab.patch @@ -0,0 +1,56 @@ +From d34da78089af855bf1406cea24342b0cc4c8225d Mon Sep 17 00:00:00 2001 +From: Ken Zhu +Date: Thu, 4 Apr 2024 16:01:38 -0700 +Subject: [PATCH] [net] [bridge] Allow unicast loop back when hairpin enabled + +disable multicast go back to the original device for non wireless +device. + +Change-Id: Ia5124d2c3d602f71d444f4ac800ddae4b608e4c2 +Signed-off-by: Ken Zhu +--- + net/bridge/br_forward.c | 33 +++++++++++---------------------- + 1 file changed, 11 insertions(+), 22 deletions(-) + +--- a/net/bridge/br_forward.c ++++ b/net/bridge/br_forward.c +@@ -25,28 +25,17 @@ static inline int should_deliver(const s + + vg = nbp_vlan_group_rcu(p); + +- if ((skb->dev != p->dev) && +- br_allowed_egress(vg, skb) && p->state == BR_STATE_FORWARDING && +- nbp_switchdev_allowed_egress(p, skb) && +- !br_skb_isolated(p, skb)) { +- return true; +- } +- +- if ((skb->dev == p->dev) && +- (p->flags & BR_HAIRPIN_MODE) && +- br_allowed_egress(vg, skb) && p->state == BR_STATE_FORWARDING) { +- +- /* Allow hairpin only on WLAN netdevices. +- * For ethernet interfaces, hairpin not allowed +- * due to issue with switchdevs. */ +- if ( skb->dev->ieee80211_ptr == NULL) { +- return false; +- } +- +- return true; +- } +- +- return false; ++ /* ++ * When hairpin enabled, don't allow multicast go back ++ * to the original non-wireless device. ++ * it could cause fdb learning issue to connected switch. ++ */ ++ return (((p->flags & BR_HAIRPIN_MODE) ++ && (skb->dev->ieee80211_ptr != NULL || !is_multicast_ether_addr(eth_hdr(skb)->h_dest))) ++ || (skb->dev != p->dev)) && ++ br_allowed_egress(vg, skb) && (p->state == BR_STATE_FORWARDING) && ++ nbp_switchdev_allowed_egress(p, skb) && ++ !br_skb_isolated(p, skb); + } + + int br_dev_queue_push_xmit(struct net *net, struct sock *sk, struct sk_buff *skb) diff --git a/target/linux/ipq806x/patches-6.6/990-100-disable-unused-nss-feat.patch b/target/linux/ipq806x/patches-6.6/990-100-disable-unused-nss-feat.patch new file mode 100644 index 0000000000..bcc7e2b065 --- /dev/null +++ b/target/linux/ipq806x/patches-6.6/990-100-disable-unused-nss-feat.patch @@ -0,0 +1,16 @@ +--- a/arch/arm/boot/dts/qcom/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom/qcom-ipq8064.dtsi +@@ -917,12 +917,10 @@ + qcom,vlan-enabled; + qcom,wlan-dataplane-offload-enabled; + qcom,wlanredirect-enabled; +- qcom,pxvlan-enabled; + qcom,vxlan-enabled; + qcom,match-enabled; + qcom,mirror-enabled; +- qcom,rmnet-enabled; +- qcom,clmap-enabled; ++ qcom,tstamp-enabled; + }; + + nss1: nss@40800000 {